Skip to content

Commit fb6237a

Browse files
committed
Add test coverage for worktree directory switching and fix main worktree detection
- Add pragma: no branch for unreachable break in _update_worktrees_cache_after_checkout - Add get_main_worktree_path() method to reliably get main worktree path regardless of current directory - Use get_main_worktree_path() instead of get_root_dir() in traverse() to fix bug where main_worktree_path was incorrectly set to linked worktree when traverse started from linked worktree - Add flush_caches() calls after os.chdir() to invalidate cached root_dir - Add __root_dir to list of caches flushed by flush_caches() - Add test_traverse_cd_from_linked_to_main_worktree test to cover lines 88-89 (cd from linked to main worktree) and line 477 (return to initial directory) - Improve coverage from 98% to 99% for traverse.py
1 parent 6250b0b commit fb6237a

File tree

3 files changed

+82
-2
lines changed

3 files changed

+82
-2
lines changed

git_machete/client/traverse.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ def _update_worktrees_cache_after_checkout(self, checked_out_branch: LocalBranch
6262
for branch, path in self.__worktrees_cache.items():
6363
if path == current_worktree_path:
6464
del self.__worktrees_cache[branch]
65-
break
65+
break # pragma: no branch
6666

6767
# Add the new branch entry for this worktree
6868
self.__worktrees_cache[checked_out_branch] = current_worktree_path
@@ -86,6 +86,8 @@ def _switch_to_branch_worktree(
8686
if current_worktree_root != main_worktree_path:
8787
print(f"Changing directory to main worktree at {bold(main_worktree_path)}")
8888
os.chdir(main_worktree_path)
89+
# Flush cache after directory change so get_root_dir() returns the correct path
90+
self._git.flush_caches()
8991
self._git.checkout(branch)
9092
# Update cache after checkout
9193
self._update_worktrees_cache_after_checkout(branch)
@@ -96,6 +98,8 @@ def _switch_to_branch_worktree(
9698
print(f"Branch {bold(branch)} is checked out in worktree at {bold(worktree_path)}")
9799
print("Changing directory to that worktree")
98100
os.chdir(worktree_path)
101+
# Flush cache after directory change so get_root_dir() returns the correct path
102+
self._git.flush_caches()
99103

100104
def traverse(
101105
self,
@@ -138,7 +142,7 @@ def traverse(
138142
current_user = self.code_hosting_client.get_current_user_login()
139143

140144
# Store the main worktree path and initial directory for later restoration
141-
main_worktree_path = self._git.get_root_dir()
145+
main_worktree_path = self._git.get_main_worktree_path()
142146
initial_directory = os.getcwd()
143147
initial_branch = nearest_remaining_branch = self._git.get_current_branch()
144148

git_machete/git_operations.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,7 @@ def flush_caches(self) -> None:
253253
self.__remote_branches_cached = None
254254
self.__remotes_cached = None
255255
self.__removed_from_remote = None
256+
self.__root_dir = None
256257
self.__short_commit_hash_by_revision_cached = {}
257258

258259
def _run_git(self, git_cmd: str, *args: str, flush_caches: bool, allow_non_zero: bool = False) -> int:
@@ -622,6 +623,25 @@ def get_worktree_path(self, branch: LocalBranchShortName) -> Optional[str]:
622623
worktrees = self.get_worktrees()
623624
return worktrees.get(branch)
624625

626+
def get_main_worktree_path(self) -> str:
627+
"""
628+
Returns the path to the main worktree (not a linked worktree).
629+
"""
630+
if self.get_git_version() < (2, 5):
631+
# git worktree command was introduced in git 2.5
632+
# If worktrees aren't supported, just return the current root dir
633+
return self.get_root_dir()
634+
635+
result = self._popen_git("worktree", "list", "--porcelain")
636+
637+
# The first worktree in the list is always the main worktree
638+
for line in result.stdout.strip().splitlines():
639+
if line.startswith('worktree '):
640+
return line[len('worktree '):]
641+
642+
# Fallback: if no worktrees found, return current root dir
643+
return self.get_root_dir()
644+
625645
def checkout(self, branch: LocalBranchShortName) -> None:
626646
self._run_git("checkout", "--quiet", branch, "--", flush_caches=True)
627647

tests/test_traverse.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1993,3 +1993,59 @@ def test_traverse_with_worktrees(self) -> None:
19931993
# feature-2 should now be based on the updated feature-1
19941994
feature_2_log = subprocess.check_output("git log --oneline feature-2", shell=True).decode()
19951995
assert "feature-1 additional commit" in feature_2_log
1996+
1997+
def test_traverse_cd_from_linked_to_main_worktree(self) -> None:
1998+
"""Test traverse cd from linked worktree to main worktree for non-checked-out branch."""
1999+
if get_git_version() < (2, 5):
2000+
# git worktree command was introduced in git 2.5
2001+
return
2002+
2003+
(local_path, _) = create_repo_with_remote()
2004+
new_branch("root")
2005+
commit()
2006+
push()
2007+
new_branch("branch-1")
2008+
commit()
2009+
push()
2010+
2011+
body: str = \
2012+
"""
2013+
root
2014+
branch-1
2015+
"""
2016+
rewrite_branch_layout_file(body)
2017+
2018+
check_out("root")
2019+
new_branch("branch-2")
2020+
commit()
2021+
push()
2022+
2023+
body = """
2024+
root
2025+
branch-1
2026+
branch-2
2027+
"""
2028+
rewrite_branch_layout_file(body)
2029+
2030+
# Setup: Main worktree on branch-2, linked worktree for branch-1
2031+
check_out("branch-2") # Main worktree on branch-2
2032+
branch_1_worktree = add_worktree("branch-1") # Linked worktree for branch-1
2033+
2034+
# cd into branch-1 linked worktree
2035+
os.chdir(branch_1_worktree)
2036+
2037+
# Now run traverse --start-from=first-root
2038+
# This should checkout root, which is NOT in any worktree
2039+
# We're currently in branch-1 linked worktree
2040+
# So it should cd to main worktree (lines 88-89)
2041+
output = launch_command("traverse", "-y", "--return-to=here", "--start-from=first-root")
2042+
2043+
# Verify lines 88-89 were executed
2044+
assert "Changing directory to main worktree at" in output, \
2045+
f"Expected 'Changing directory to main worktree at' in output, but got:\n{output}"
2046+
2047+
# Verify we returned to branch-1 worktree (line 475)
2048+
current_dir = os.path.realpath(os.getcwd())
2049+
branch_1_worktree_real = os.path.realpath(branch_1_worktree)
2050+
assert current_dir == branch_1_worktree_real, \
2051+
f"Expected to be in {branch_1_worktree_real}, but in {current_dir}"

0 commit comments

Comments
 (0)