-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Fix Merge With AI Bug - Execute Git Merge After Semantic Analysis #1867
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
ed6a005
e74c53b
38b0f1d
544b241
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -743,21 +743,99 @@ def _try_smart_merge_inner( | |
| "preview": preview, | ||
| } | ||
|
|
||
| # All conflicts can be auto-merged or no conflicts | ||
| print(muted(" All changes compatible, proceeding with merge...")) | ||
| # All conflicts can be auto-merged or no conflicts - execute git merge | ||
| spec_branch = f"auto-claude/{spec_name}" | ||
| print(muted(f" All changes compatible, merging {spec_branch}...")) | ||
|
|
||
| # Always use --no-commit so we can inspect staged files with git diff --cached. | ||
| # If the caller did NOT request no_commit, we commit explicitly after inspection. | ||
| merge_cmd = ["merge", "--no-commit", "--no-ff", spec_branch] | ||
|
|
||
| # Execute git merge | ||
| merge_result = run_git( | ||
| merge_cmd, | ||
| cwd=project_dir, | ||
| ) | ||
|
|
||
| # 'Already up to date' is returned with exit code 0, but check both | ||
| # stdout locations defensively in case git behaviour varies. | ||
| stdout_lower = merge_result.stdout.lower() if merge_result.stdout else "" | ||
| if "already up to date" in stdout_lower: | ||
| # Already up to date - treat as success | ||
| if progress_callback is not None: | ||
| progress_callback( | ||
| MergeProgressStage.COMPLETE, | ||
| 100, | ||
| "Merge complete (already up to date)", | ||
| ) | ||
| return { | ||
| "success": True, | ||
| "resolved_files": [], | ||
| "stats": { | ||
| "files_merged": 0, | ||
| "conflicts_resolved": 0, | ||
| "ai_assisted": 0, | ||
| "auto_merged": 0, | ||
| "git_merge": True, | ||
| }, | ||
| } | ||
|
|
||
| if merge_result.returncode != 0: | ||
| # Merge failed - abort to restore clean state and return failure | ||
| abort_result = run_git(["merge", "--abort"], cwd=project_dir) | ||
| if abort_result.returncode != 0: | ||
| debug_error( | ||
| MODULE, | ||
| "Failed to abort merge - repo may be in inconsistent state", | ||
| stderr=abort_result.stderr, | ||
| ) | ||
| return None # Trigger fallback to avoid operating on inconsistent state | ||
| return { | ||
| "success": False, | ||
| "error": f"Git merge failed: {merge_result.stderr}", | ||
| "conflicts": [], | ||
| } | ||
|
|
||
| # Get list of files that were actually merged (staged but not yet committed) | ||
| diff_result = run_git( | ||
| ["diff", "--cached", "--name-only"], | ||
| cwd=project_dir, | ||
| ) | ||
| merged_files = [ | ||
|
Comment on lines
+801
to
+804
This comment was marked as outdated.
Sorry, something went wrong. |
||
| f.strip() | ||
| for f in diff_result.stdout.splitlines() | ||
| if f.strip() and not _is_auto_claude_file(f.strip()) | ||
| ] | ||
|
|
||
| # If caller wants a commit, create it now | ||
| if not no_commit: | ||
| commit_result = run_git( | ||
| ["commit", "--no-edit"], | ||
| cwd=project_dir, | ||
| ) | ||
| if commit_result.returncode != 0: | ||
| debug_warning( | ||
| MODULE, | ||
| "Failed to commit merge", | ||
| stderr=commit_result.stderr, | ||
| ) | ||
|
Comment on lines
+811
to
+821
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Commit failure is silently swallowed — callers will believe the merge succeeded. When At minimum, surface the failure in the returned dict (e.g., set a Proposed fix — propagate commit failure # If caller wants a commit, create it now
if not no_commit:
commit_result = run_git(
["commit", "--no-edit"],
cwd=project_dir,
)
if commit_result.returncode != 0:
- debug_warning(
+ debug_error(
MODULE,
"Failed to commit merge",
stderr=commit_result.stderr,
)
+ # Abort the merge to leave the repo in a clean state
+ run_git(["merge", "--abort"], cwd=project_dir)
+ return {
+ "success": False,
+ "error": f"Merge staged successfully but commit failed: {commit_result.stderr}",
+ "conflicts": [],
+ }🤖 Prompt for AI Agents |
||
|
|
||
| if progress_callback is not None: | ||
| progress_callback( | ||
| MergeProgressStage.COMPLETE, | ||
| 100, | ||
| f"Analysis complete ({files_to_merge} files compatible)", | ||
| f"Merge complete ({len(merged_files)} files merged)", | ||
| ) | ||
|
|
||
| return { | ||
| "success": True, | ||
| "resolved_files": merged_files, | ||
| "stats": { | ||
| "files_merged": files_to_merge, | ||
| "auto_resolved": auto_mergeable, | ||
| "files_merged": len(merged_files), | ||
| "conflicts_resolved": 0, | ||
| "ai_assisted": 0, | ||
| "auto_merged": len(merged_files), | ||
| "git_merge": True, | ||
| }, | ||
|
Comment on lines
+836
to
839
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bug: A failed git commit is not propagated as an error, causing the function to return a success status while files remain staged but uncommitted. Suggested FixAfter the Prompt for AI Agent |
||
| } | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
rg -n -B 5 -A 15 'def merge_existing_build' apps/backend/core/workspace.pyRepository: AndyMik90/Auto-Claude
Length of output: 731
🏁 Script executed:
rg -n -B 2 -A 25 'smart_result is not None' apps/backend/core/workspace.pyRepository: AndyMik90/Auto-Claude
Length of output: 1766
🏁 Script executed:
Repository: AndyMik90/Auto-Claude
Length of output: 2914
🏁 Script executed:
Repository: AndyMik90/Auto-Claude
Length of output: 4381
🏁 Script executed:
rg -n -B 5 -A 30 'def _try_smart_merge' apps/backend/core/workspace.pyRepository: AndyMik90/Auto-Claude
Length of output: 2503
🏁 Script executed:
Repository: AndyMik90/Auto-Claude
Length of output: 650
🏁 Script executed:
rg -n -B 10 'return None.*Trigger fallback' apps/backend/core/workspace.pyRepository: AndyMik90/Auto-Claude
Length of output: 1290
🏁 Script executed:
rg -n -B 2 -A 20 'Fall back to standard git merge' apps/backend/core/workspace.pyRepository: AndyMik90/Auto-Claude
Length of output: 1078
Return structured error instead of
Nonewhen merge abort fails.When
git merge --abortfails (lines 786–792), the function returnsNoneto "trigger fallback to avoid operating on inconsistent state." However, this fallback at line 340 callsmanager.merge_worktree()on the already-inconsistent repo, contradicting the stated intent. The second merge attempt will likely fail identically or worse.Instead, return a structured error dict with an explicit "repo may be in an inconsistent state" message (matching the pattern at lines 793–797). This allows the caller to handle the error gracefully within the
if smart_result is not None:block (line 265) rather than triggering a problematic retry.The same issue exists at line 715.
🤖 Prompt for AI Agents