git-copilot-commit 0.5.5__py3-none-any.whl → 0.5.7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- git_copilot_commit/cli.py +56 -25
- git_copilot_commit/git.py +47 -6
- {git_copilot_commit-0.5.5.dist-info → git_copilot_commit-0.5.7.dist-info}/METADATA +1 -1
- {git_copilot_commit-0.5.5.dist-info → git_copilot_commit-0.5.7.dist-info}/RECORD +7 -7
- {git_copilot_commit-0.5.5.dist-info → git_copilot_commit-0.5.7.dist-info}/WHEEL +0 -0
- {git_copilot_commit-0.5.5.dist-info → git_copilot_commit-0.5.7.dist-info}/entry_points.txt +0 -0
- {git_copilot_commit-0.5.5.dist-info → git_copilot_commit-0.5.7.dist-info}/licenses/LICENSE +0 -0
git_copilot_commit/cli.py
CHANGED
|
@@ -95,6 +95,14 @@ class PreparedSplitCommit:
|
|
|
95
95
|
patch_units: tuple[PatchUnit, ...]
|
|
96
96
|
|
|
97
97
|
|
|
98
|
+
@dataclass(frozen=True, slots=True)
|
|
99
|
+
class SplitCommitExecutionState:
|
|
100
|
+
"""Original HEAD state used to roll back partial split-commit execution."""
|
|
101
|
+
|
|
102
|
+
original_head_sha: str | None
|
|
103
|
+
original_head_ref: str | None
|
|
104
|
+
|
|
105
|
+
|
|
98
106
|
CORE_CHANGE_COMMIT_TYPES = frozenset({"feat", "fix", "perf", "refactor", "revert"})
|
|
99
107
|
FOLLOW_UP_COMMIT_TYPE_PRIORITY = {
|
|
100
108
|
"test": 2,
|
|
@@ -757,39 +765,62 @@ def execute_split_commit_plan(
|
|
|
757
765
|
console.print("Invalid choice. Commit cancelled.")
|
|
758
766
|
raise typer.Exit()
|
|
759
767
|
|
|
768
|
+
execution_state = SplitCommitExecutionState(
|
|
769
|
+
original_head_sha=repo.get_head_sha() if repo.has_commit("HEAD") else None,
|
|
770
|
+
original_head_ref=repo.get_symbolic_head_ref(),
|
|
771
|
+
)
|
|
760
772
|
commit_shas: list[str] = []
|
|
761
773
|
total_commits = len(prepared_commits)
|
|
762
774
|
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
775
|
+
try:
|
|
776
|
+
for index, prepared_commit in enumerate(prepared_commits, start=1):
|
|
777
|
+
console.print(
|
|
778
|
+
f"[cyan]Creating commit {index}/{total_commits}:[/cyan] {prepared_commit.message}"
|
|
779
|
+
)
|
|
767
780
|
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
781
|
+
with repo.temporary_alternate_index() as alternate_index:
|
|
782
|
+
try:
|
|
783
|
+
for patch_unit in prepared_commit.patch_units:
|
|
784
|
+
repo.check_patch_for_alternate_index(
|
|
785
|
+
patch_unit.patch,
|
|
786
|
+
index=alternate_index,
|
|
787
|
+
)
|
|
788
|
+
repo.apply_patch_to_alternate_index(
|
|
789
|
+
patch_unit.patch,
|
|
790
|
+
index=alternate_index,
|
|
791
|
+
)
|
|
792
|
+
except GitError as exc:
|
|
793
|
+
console.print(
|
|
794
|
+
f"[red]Failed to apply the planned changes for commit {index}: {exc}[/red]"
|
|
774
795
|
)
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
796
|
+
raise typer.Exit(1)
|
|
797
|
+
|
|
798
|
+
commit_shas.append(
|
|
799
|
+
commit_with_retry_no_verify(
|
|
800
|
+
repo,
|
|
801
|
+
prepared_commit.message,
|
|
802
|
+
use_editor=use_editor,
|
|
803
|
+
env=alternate_index.env,
|
|
778
804
|
)
|
|
779
|
-
except GitError as exc:
|
|
780
|
-
console.print(
|
|
781
|
-
f"[red]Failed to apply the planned changes for commit {index}: {exc}[/red]"
|
|
782
|
-
)
|
|
783
|
-
raise typer.Exit(1)
|
|
784
|
-
|
|
785
|
-
commit_shas.append(
|
|
786
|
-
commit_with_retry_no_verify(
|
|
787
|
-
repo,
|
|
788
|
-
prepared_commit.message,
|
|
789
|
-
use_editor=use_editor,
|
|
790
|
-
env=alternate_index.env,
|
|
791
805
|
)
|
|
806
|
+
except BaseException:
|
|
807
|
+
try:
|
|
808
|
+
if execution_state.original_head_sha is not None:
|
|
809
|
+
repo.soft_reset(execution_state.original_head_sha)
|
|
810
|
+
elif execution_state.original_head_ref is not None and repo.has_commit(
|
|
811
|
+
"HEAD"
|
|
812
|
+
):
|
|
813
|
+
repo.delete_ref(execution_state.original_head_ref)
|
|
814
|
+
except GitError as exc:
|
|
815
|
+
console.print(
|
|
816
|
+
"[red]Failed to restore the original staged changes after split commit creation stopped early: "
|
|
817
|
+
f"{exc}[/red]"
|
|
818
|
+
)
|
|
819
|
+
else:
|
|
820
|
+
console.print(
|
|
821
|
+
"[yellow]Split commit creation did not complete; restored the original staged changes.[/yellow]"
|
|
792
822
|
)
|
|
823
|
+
raise
|
|
793
824
|
|
|
794
825
|
return commit_shas
|
|
795
826
|
|
git_copilot_commit/git.py
CHANGED
|
@@ -139,7 +139,9 @@ class GitRepository:
|
|
|
139
139
|
except subprocess.CalledProcessError:
|
|
140
140
|
raise NotAGitRepositoryError(f"{self.cwd} is not a git repository")
|
|
141
141
|
except subprocess.TimeoutExpired:
|
|
142
|
-
raise GitCommandError(
|
|
142
|
+
raise GitCommandError(
|
|
143
|
+
"Git command timed out: git rev-parse --show-toplevel"
|
|
144
|
+
)
|
|
143
145
|
|
|
144
146
|
repo_root = result.stdout.strip()
|
|
145
147
|
if not repo_root:
|
|
@@ -264,10 +266,27 @@ class GitRepository:
|
|
|
264
266
|
result = self._run_git_command(["rev-parse", ref])
|
|
265
267
|
return result.stdout.strip()
|
|
266
268
|
|
|
269
|
+
def has_commit(self, ref: str = "HEAD") -> bool:
|
|
270
|
+
"""Return whether the provided ref resolves to a commit."""
|
|
271
|
+
result = self._run_git_command(
|
|
272
|
+
["rev-parse", "--verify", "--quiet", f"{ref}^{{commit}}"],
|
|
273
|
+
check=False,
|
|
274
|
+
)
|
|
275
|
+
return result.returncode == 0
|
|
276
|
+
|
|
277
|
+
def get_symbolic_head_ref(self) -> str | None:
|
|
278
|
+
"""Return the symbolic ref for HEAD when attached to a branch."""
|
|
279
|
+
result = self._run_git_command(["symbolic-ref", "-q", "HEAD"], check=False)
|
|
280
|
+
if result.returncode != 0:
|
|
281
|
+
return None
|
|
282
|
+
|
|
283
|
+
ref = result.stdout.strip()
|
|
284
|
+
return ref or None
|
|
285
|
+
|
|
267
286
|
def _parse_status_output(self, status_output: str) -> list[GitFile]:
|
|
268
287
|
"""Parse git status --porcelain output into GitFile objects."""
|
|
269
288
|
files = []
|
|
270
|
-
for line in status_output.
|
|
289
|
+
for line in status_output.splitlines():
|
|
271
290
|
if not line:
|
|
272
291
|
continue
|
|
273
292
|
|
|
@@ -316,14 +335,32 @@ class GitRepository:
|
|
|
316
335
|
else:
|
|
317
336
|
self._run_git_command(["reset", "HEAD"] + self._normalize_paths(paths))
|
|
318
337
|
|
|
338
|
+
def soft_reset(self, ref: str) -> None:
|
|
339
|
+
"""Move HEAD to ref while preserving the working tree and index."""
|
|
340
|
+
self._run_git_command(["reset", "--soft", ref])
|
|
341
|
+
|
|
342
|
+
def delete_ref(self, ref: str, *, missing_ok: bool = False) -> None:
|
|
343
|
+
"""Delete a ref, optionally ignoring missing refs."""
|
|
344
|
+
result = self._run_git_command(["update-ref", "-d", ref], check=False)
|
|
345
|
+
if result.returncode == 0 or missing_ok:
|
|
346
|
+
return
|
|
347
|
+
|
|
348
|
+
error_output = result.stderr or result.stdout or ""
|
|
349
|
+
if error_output:
|
|
350
|
+
raise GitCommandError(
|
|
351
|
+
f"Git command failed: git update-ref -d {ref}\n{error_output}"
|
|
352
|
+
)
|
|
353
|
+
raise GitCommandError(f"Git command failed: git update-ref -d {ref}")
|
|
354
|
+
|
|
319
355
|
def create_alternate_index(self, from_ref: str = "HEAD") -> AlternateGitIndex:
|
|
320
356
|
"""Create a temporary git index initialized from the provided ref."""
|
|
321
|
-
fd, index_path = tempfile.mkstemp(
|
|
322
|
-
prefix="git-copilot-commit-", suffix=".index"
|
|
323
|
-
)
|
|
357
|
+
fd, index_path = tempfile.mkstemp(prefix="git-copilot-commit-", suffix=".index")
|
|
324
358
|
os.close(fd)
|
|
325
359
|
alternate_index = AlternateGitIndex(Path(index_path))
|
|
326
|
-
self.
|
|
360
|
+
if from_ref == "HEAD" and not self.has_commit(from_ref):
|
|
361
|
+
self.read_empty_tree(index=alternate_index)
|
|
362
|
+
else:
|
|
363
|
+
self.read_tree(from_ref, index=alternate_index)
|
|
327
364
|
return alternate_index
|
|
328
365
|
|
|
329
366
|
@contextmanager
|
|
@@ -341,6 +378,10 @@ class GitRepository:
|
|
|
341
378
|
"""Populate an alternate index from the provided ref."""
|
|
342
379
|
self._run_git_command(["read-tree", ref], env=index.env)
|
|
343
380
|
|
|
381
|
+
def read_empty_tree(self, *, index: AlternateGitIndex) -> None:
|
|
382
|
+
"""Initialize an alternate index with an empty tree."""
|
|
383
|
+
self._run_git_command(["read-tree", "--empty"], env=index.env)
|
|
384
|
+
|
|
344
385
|
def apply_patch(
|
|
345
386
|
self,
|
|
346
387
|
patch: str,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
git_copilot_commit/__init__.py,sha256=v3x5oBkxwKJEZLv62QqSmP3iqNKLtZgrWZfH8eFzlQg,60
|
|
2
|
-
git_copilot_commit/cli.py,sha256=
|
|
3
|
-
git_copilot_commit/git.py,sha256=
|
|
2
|
+
git_copilot_commit/cli.py,sha256=FynjjH0cnMdyjsvNayNhwmDfMaoJyxu8OD5O7pv_Kvg,35221
|
|
3
|
+
git_copilot_commit/git.py,sha256=dEsyazWfD2TIVCpDObwu6TDWRBkzxEjdIKHrQ59h7I0,16697
|
|
4
4
|
git_copilot_commit/github_copilot.py,sha256=2SKVVFmQ2yETAySjSRYKQTujW-1vZEW4rFKSr--dtLs,50427
|
|
5
5
|
git_copilot_commit/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
6
|
git_copilot_commit/settings.py,sha256=WrM10_J3F7QBfOVmPDWpNZrNHhmZSeN-9FqQZxgdWvQ,3730
|
|
@@ -8,8 +8,8 @@ git_copilot_commit/split_commits.py,sha256=rHyuVJggjmYjbva7BVqsM3aZRxUgOKkuZtxxv
|
|
|
8
8
|
git_copilot_commit/version.py,sha256=AieHOUX52g6N67HL0iLWtDKrgOYyulxwHWViu26Jrd4,105
|
|
9
9
|
git_copilot_commit/prompts/commit-message-generator-prompt.md,sha256=EHAS6w15vLQ-kgT1N7nPG2nWqdeTmlHje_kN9yZIoZQ,2378
|
|
10
10
|
git_copilot_commit/prompts/split-commit-planner-prompt.md,sha256=JActWTKMbdR9eaJyCbQ6UwzyRu5jJi8Iec7H7qWAmAA,1418
|
|
11
|
-
git_copilot_commit-0.5.
|
|
12
|
-
git_copilot_commit-0.5.
|
|
13
|
-
git_copilot_commit-0.5.
|
|
14
|
-
git_copilot_commit-0.5.
|
|
15
|
-
git_copilot_commit-0.5.
|
|
11
|
+
git_copilot_commit-0.5.7.dist-info/METADATA,sha256=8Dw3F46Ybp1PmkPQZmAbCmSrlJNBg_puowP3v2xxMVQ,6649
|
|
12
|
+
git_copilot_commit-0.5.7.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
13
|
+
git_copilot_commit-0.5.7.dist-info/entry_points.txt,sha256=-D4bQqiuSPwQJG2zx--vJbZD1iqB5coUfoJ_gmC3rSg,66
|
|
14
|
+
git_copilot_commit-0.5.7.dist-info/licenses/LICENSE,sha256=14lNZAoKJPI1U7eGpletjN_PFm1JwP1vT_0jFKY6eWg,1065
|
|
15
|
+
git_copilot_commit-0.5.7.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|