Skip to content

Commit 01c08f0

Browse files
authored
Merge pull request #23 from BHFock/stash_bug
Fix: Safe directory handling during git stash operations
2 parents a979f3e + 11331c0 commit 01c08f0

File tree

1 file changed

+69
-6
lines changed

1 file changed

+69
-6
lines changed

git-cl

Lines changed: 69 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ Features:
5454
This tool is intended for use within Git repositories.
5555
"""
5656

57-
__version__ = "0.3.3"
57+
__version__ = "0.3.4"
5858

5959
import argparse
6060
import json
@@ -808,6 +808,63 @@ def clutil_get_current_branch() -> Optional[str]:
808808
return None
809809

810810

811+
@contextmanager
812+
def clutil_safe_stash_context(git_root: Path,
813+
files_for_git: list[str],
814+
untracked_files_for_git: list[str]):
815+
"""
816+
Context manager to handle directory changes during stash operations.
817+
Recalculates file paths relative to git root.
818+
819+
Args:
820+
git_root: Path to git repository root
821+
files_for_git: Original file paths relative to current directory
822+
untracked_files_for_git: Original untracked file paths relative to current directory
823+
824+
Yields:
825+
tuple[list[str], list[str]]: (files_for_git, untracked_files_for_git) relative to git root
826+
"""
827+
original_cwd = Path.cwd()
828+
829+
def convert_to_git_root_relative(file_paths: list[str]) -> list[str]:
830+
"""Convert file paths to be relative to git root."""
831+
result = []
832+
for file_path in file_paths:
833+
abs_path = (original_cwd / file_path).resolve()
834+
try:
835+
git_root_relative = abs_path.relative_to(git_root)
836+
result.append(git_root_relative.as_posix())
837+
except ValueError:
838+
# Path is outside git root - should not happen with validation
839+
result.append(file_path)
840+
return result
841+
842+
# Convert both file lists
843+
safe_files = convert_to_git_root_relative(files_for_git)
844+
safe_untracked_files = convert_to_git_root_relative(untracked_files_for_git)
845+
846+
# Change to git root
847+
os.chdir(git_root)
848+
849+
try:
850+
yield safe_files, safe_untracked_files
851+
finally:
852+
# Try to restore original directory
853+
if original_cwd.exists():
854+
os.chdir(original_cwd)
855+
else:
856+
# Directory was deleted - warn user and stay in git root
857+
try:
858+
rel_path = os.path.relpath(original_cwd, git_root)
859+
print(f"Note: Directory '{rel_path}' was removed by git stash.")
860+
print("Your shell is still pointing to the deleted directory.")
861+
print("Please run: cd ..")
862+
except (ValueError, OSError):
863+
print("Note: Your working directory was removed by git stash.")
864+
print("Your shell is still pointing to the deleted directory.")
865+
print("Please run: cd ..")
866+
867+
811868
def clutil_get_stash_source_branch(stash_data: dict) -> Optional[str]:
812869
"""
813870
Extract the branch name from stash metadata if stored.
@@ -928,6 +985,7 @@ def clutil_rollback_stash(stash_ref: str, changelist_name: str) -> bool:
928985
f"'{changelist_name}'")
929986
return False
930987

988+
931989
def clutil_stash_all_changelists(changelists: dict[str, list[str]], quiet: bool = False) -> None:
932990
"""
933991
Helper function to stash all active changelists.
@@ -2664,10 +2722,12 @@ def cl_stash(args: argparse.Namespace, quiet: bool = False) -> None:
26642722
stashable_files, file_categories, git_root, quiet=quiet
26652723
)
26662724

2667-
# Execute git stash
2725+
# Execute git stash with safe directory handling
26682726
try:
2669-
stash_ref = clutil_execute_git_stash(name, files_for_git,
2670-
untracked_files_for_git)
2727+
with clutil_safe_stash_context(
2728+
git_root, files_for_git, untracked_files_for_git) as (safe_files,
2729+
safe_untracked_files):
2730+
stash_ref = clutil_execute_git_stash(name, safe_files, safe_untracked_files)
26712731
except (subprocess.CalledProcessError, ValueError):
26722732
return
26732733

@@ -2689,6 +2749,7 @@ def cl_stash(args: argparse.Namespace, quiet: bool = False) -> None:
26892749
clutil_handle_stash_failure(error, name, stash_ref,
26902750
original_changelists, stashes)
26912751

2752+
26922753
def cl_unstash(args: argparse.Namespace, quiet: bool = False) -> None:
26932754
"""
26942755
Restore a stashed changelist to the working directory.
@@ -2727,7 +2788,8 @@ def cl_unstash(args: argparse.Namespace, quiet: bool = False) -> None:
27272788

27282789
# Check for conflicts (suppress verbose output in quiet mode)
27292790
if not clutil_check_and_report_conflicts(files, git_root, base_name,
2730-
getattr(args, 'force', False), quiet=quiet):
2791+
getattr(args, 'force', False),
2792+
quiet=quiet):
27312793
return
27322794

27332795
# Verify stash still exists and update reference
@@ -2839,7 +2901,8 @@ def cl_branch(args: argparse.Namespace) -> None:
28392901

28402902
except Exception as error:
28412903
print(f"Error unstashing changelist: {error}")
2842-
print(f"Branch '{branch_name}' was created but changelist restore failed.")
2904+
print(f"Branch '{branch_name}' was created but"
2905+
"changelist restore failed.")
28432906
print(f"Try manually: git cl unstash {changelist_name}")
28442907

28452908

0 commit comments

Comments
 (0)