kin: auto-commit after pipeline
This commit is contained in:
parent
977176f004
commit
d7f7193ad7
4 changed files with 352 additions and 52 deletions
|
|
@ -10,6 +10,7 @@ All functions are defensive: never raise, always log warnings on error.
|
|||
import logging
|
||||
import shutil
|
||||
import subprocess
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
_logger = logging.getLogger("kin.worktree")
|
||||
|
|
@ -59,12 +60,15 @@ def create_worktree(project_path: str, task_id: str, step_name: str = "step") ->
|
|||
return None
|
||||
|
||||
|
||||
def merge_worktree(worktree_path: str, project_path: str) -> dict:
|
||||
def merge_worktree(worktree_path: str, project_path: str, max_retries: int = 0, retry_delay_s: int = 15) -> dict:
|
||||
"""Merge the worktree branch back into current HEAD of project_path.
|
||||
|
||||
Branch name is derived from the worktree directory name.
|
||||
On conflict: aborts merge and returns success=False with conflict list.
|
||||
|
||||
max_retries: number of retry attempts after the first failure (default 0 = no retry).
|
||||
retry_delay_s: seconds to wait between retry attempts.
|
||||
|
||||
Returns {success: bool, conflicts: list[str], merged_files: list[str]}
|
||||
"""
|
||||
git = _git(project_path)
|
||||
|
|
@ -88,50 +92,60 @@ def merge_worktree(worktree_path: str, project_path: str) -> dict:
|
|||
)
|
||||
commit_had_changes = commit_result.returncode == 0
|
||||
|
||||
merge_result = subprocess.run(
|
||||
[git, "merge", "--no-ff", branch_name],
|
||||
cwd=project_path,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=60,
|
||||
)
|
||||
if merge_result.returncode == 0:
|
||||
diff_result = subprocess.run(
|
||||
[git, "diff", "HEAD~1", "HEAD", "--name-only"],
|
||||
for attempt in range(max_retries + 1):
|
||||
merge_result = subprocess.run(
|
||||
[git, "merge", "--no-ff", branch_name],
|
||||
cwd=project_path,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=60,
|
||||
)
|
||||
if merge_result.returncode == 0:
|
||||
diff_result = subprocess.run(
|
||||
[git, "diff", "HEAD~1", "HEAD", "--name-only"],
|
||||
cwd=project_path,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=10,
|
||||
)
|
||||
merged_files = [
|
||||
f.strip() for f in diff_result.stdout.splitlines() if f.strip()
|
||||
]
|
||||
_logger.info("Merged worktree %s: %d files", branch_name, len(merged_files))
|
||||
return {"success": True, "conflicts": [], "merged_files": merged_files}
|
||||
|
||||
# Merge failed — if commit was also empty (nothing to commit), treat as success
|
||||
if not commit_had_changes:
|
||||
_logger.info("Worktree %s: nothing to commit, skipping merge", branch_name)
|
||||
return {"success": True, "conflicts": [], "merged_files": []}
|
||||
|
||||
# Merge failed — collect conflicts and abort
|
||||
conflict_result = subprocess.run(
|
||||
[git, "diff", "--name-only", "--diff-filter=U"],
|
||||
cwd=project_path,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=10,
|
||||
)
|
||||
merged_files = [
|
||||
f.strip() for f in diff_result.stdout.splitlines() if f.strip()
|
||||
]
|
||||
_logger.info("Merged worktree %s: %d files", branch_name, len(merged_files))
|
||||
return {"success": True, "conflicts": [], "merged_files": merged_files}
|
||||
conflicts = [f.strip() for f in conflict_result.stdout.splitlines() if f.strip()]
|
||||
|
||||
# Merge failed — if commit was also empty (nothing to commit), treat as success
|
||||
if not commit_had_changes:
|
||||
_logger.info("Worktree %s: nothing to commit, skipping merge", branch_name)
|
||||
return {"success": True, "conflicts": [], "merged_files": []}
|
||||
subprocess.run(
|
||||
[git, "merge", "--abort"],
|
||||
cwd=project_path,
|
||||
capture_output=True,
|
||||
timeout=10,
|
||||
)
|
||||
|
||||
# Merge failed — collect conflicts and abort
|
||||
conflict_result = subprocess.run(
|
||||
[git, "diff", "--name-only", "--diff-filter=U"],
|
||||
cwd=project_path,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=10,
|
||||
)
|
||||
conflicts = [f.strip() for f in conflict_result.stdout.splitlines() if f.strip()]
|
||||
if attempt < max_retries:
|
||||
_logger.warning(
|
||||
"KIN-139: merge conflict in %s (attempt %d/%d), retrying in %ds",
|
||||
branch_name, attempt + 1, max_retries + 1, retry_delay_s,
|
||||
)
|
||||
time.sleep(retry_delay_s)
|
||||
continue
|
||||
|
||||
subprocess.run(
|
||||
[git, "merge", "--abort"],
|
||||
cwd=project_path,
|
||||
capture_output=True,
|
||||
timeout=10,
|
||||
)
|
||||
_logger.warning("Merge conflict in worktree %s: %s", branch_name, conflicts)
|
||||
return {"success": False, "conflicts": conflicts, "merged_files": []}
|
||||
_logger.warning("Merge conflict in worktree %s: %s", branch_name, conflicts)
|
||||
return {"success": False, "conflicts": conflicts, "merged_files": []}
|
||||
|
||||
except Exception as exc:
|
||||
_logger.warning("merge_worktree error for %s: %s", branch_name, exc)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue