Skip to content

Commit 6aefec8

Browse files
mohamedashrraf222Saga4
authored andcommitted
Updated the fix to handle both error cases:
1- "not a git repository" error - Now caught when trying to open the repository (git.InvalidGitRepositoryError) and also when git commands fail. Falls back to shutil.rmtree directly. 2- Permission denied error - Retries once with a 0.5s delay, then falls back to shutil.rmtree.
1 parent 524c0d3 commit 6aefec8

1 file changed

Lines changed: 54 additions & 2 deletions

File tree

codeflash/code_utils/git_worktree_utils.py

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
from __future__ import annotations
22

33
import configparser
4+
import shutil
45
import subprocess
56
import tempfile
67
import time
78
from pathlib import Path
89
from typing import Optional
910

1011
import git
12+
from git.exc import GitCommandError
1113

1214
from codeflash.cli_cmds.console import logger
1315
from codeflash.code_utils.compat import codeflash_cache_dir
@@ -95,12 +97,62 @@ def create_detached_worktree(module_root: Path) -> Optional[Path]:
9597
return worktree_dir
9698

9799

100+
def _fallback_remove_worktree(worktree_dir: Path) -> None:
101+
"""Fallback worktree removal using shutil.rmtree when git commands fail."""
102+
if worktree_dir.exists():
103+
shutil.rmtree(worktree_dir, ignore_errors=True)
104+
logger.debug(f"Removed worktree directory using fallback method: {worktree_dir}")
105+
106+
98107
def remove_worktree(worktree_dir: Path) -> None:
108+
"""Remove a git worktree, with retry logic for Windows permission errors."""
109+
# Try to get repository reference
99110
try:
100111
repository = git.Repo(worktree_dir, search_parent_directories=True)
101-
repository.git.worktree("remove", "--force", worktree_dir)
112+
except git.InvalidGitRepositoryError:
113+
# Worktree is not a valid git repository (corrupted or partially created)
114+
logger.debug(f"Worktree is not a valid git repository, using fallback deletion: {worktree_dir}")
115+
_fallback_remove_worktree(worktree_dir)
116+
return
102117
except Exception:
103-
logger.exception(f"Failed to remove worktree: {worktree_dir}")
118+
logger.exception(f"Failed to open worktree repository: {worktree_dir}")
119+
_fallback_remove_worktree(worktree_dir)
120+
return
121+
122+
# Try git worktree remove first
123+
for attempt in range(2):
124+
try:
125+
repository.git.worktree("remove", "--force", worktree_dir)
126+
logger.debug(f"Successfully removed worktree: {worktree_dir}")
127+
return
128+
except GitCommandError as e:
129+
error_msg = str(e).lower()
130+
# Check if it's a permission error or not a git repository error
131+
if "permission denied" in error_msg or "failed to delete" in error_msg:
132+
if attempt == 0:
133+
# Retry once with a small delay to allow file handles to close
134+
logger.debug(f"Permission error removing worktree (attempt {attempt + 1}), retrying after delay: {worktree_dir}")
135+
time.sleep(0.5)
136+
continue
137+
elif "not a git repository" in error_msg:
138+
# Worktree reference is broken, just delete the directory
139+
logger.debug(f"Worktree git reference is broken, using fallback deletion: {worktree_dir}")
140+
_fallback_remove_worktree(worktree_dir)
141+
return
142+
143+
# Fallback to shutil.rmtree for any persistent error
144+
logger.warning(f"Git worktree remove failed, using fallback deletion: {worktree_dir}")
145+
_fallback_remove_worktree(worktree_dir)
146+
# Try to prune stale worktree references
147+
try:
148+
repository.git.worktree("prune")
149+
except Exception:
150+
pass
151+
return
152+
except Exception:
153+
logger.exception(f"Failed to remove worktree: {worktree_dir}")
154+
_fallback_remove_worktree(worktree_dir)
155+
return
104156

105157

106158
def create_diff_patch_from_worktree(

0 commit comments

Comments
 (0)