Skip to content

Commit 33474d4

Browse files
committed
During traverse, correctly detect a failed rebase in a worktree
1 parent fa153b2 commit 33474d4

5 files changed

Lines changed: 106 additions & 13 deletions

File tree

RELEASE_NOTES.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
## New in git-machete 3.39.3
44

5+
- fixed: when `git machete traverse` failed on a rebase within a worktree,
6+
in certain cases the warning message about changed directory didn't show up (reported by @lsierant)
7+
58
## New in git-machete 3.39.2
69

710
- improved: interactive `git machete go` displays a branch layout as in `status`

git_machete/git_operations.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,8 +262,9 @@ def chdir(self, path: str) -> None:
262262
self.__flush_current_worktree_caches()
263263

264264
def __flush_current_worktree_caches(self) -> None:
265+
# No point in clearing main worktree paths as they are not affected by chdir between worktrees
265266
self.__current_worktree_root_dir = None
266-
self.__main_worktree_root_dir = None
267+
self.__current_worktree_git_dir = None
267268

268269
def _run_git(self, git_cmd: str, *args: str, flush_caches: bool, allow_non_zero: bool = False) -> int:
269270
exit_code = utils.run_cmd(*GIT_EXEC, git_cmd, *args)

tests/mockers.py

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@
99
redirect_stderr, redirect_stdout)
1010
from typing import Any, Callable, Iterable, Iterator, Optional, Tuple, Type
1111

12-
import pytest
13-
1412
from git_machete import cli, utils
1513
from git_machete.utils import MacheteException, PopenResult
1614

@@ -61,27 +59,41 @@ def launch_command(*cmd_and_args: str) -> str:
6159
return output
6260

6361

62+
def strip_trailing_spaces(text: str) -> str:
63+
return re.sub(" +$", "", text, flags=re.MULTILINE)
64+
65+
6466
def assert_success(cmd_and_args: Iterable[str], expected_result: str) -> None:
6567
if expected_result.startswith("\n"):
6668
# removeprefix is only available since Python 3.9
6769
expected_result = expected_result[1:]
6870
expected_result = textwrap.dedent(expected_result)
69-
actual_result = re.sub(" +$", "", textwrap.dedent(launch_command(*cmd_and_args)), flags=re.MULTILINE)
71+
actual_result = strip_trailing_spaces(textwrap.dedent(launch_command(*cmd_and_args)))
7072
assert actual_result == expected_result
7173

7274

73-
def assert_failure(cmd_and_args: Iterable[str], expected_message: str, expected_type: Type[BaseException] = MacheteException) -> None:
75+
def assert_failure(cmd_and_args: Iterable[str], expected_message: str, expected_type: Type[BaseException] = MacheteException,
76+
expected_output: Optional[str] = None) -> None:
7477
if expected_message.startswith("\n"):
7578
# removeprefix is only available since Python 3.9
7679
expected_message = expected_message[1:]
7780
expected_message = textwrap.dedent(expected_message)
7881

79-
with pytest.raises(expected_type) as ei:
80-
launch_command(*cmd_and_args)
81-
error_message = ei.value.msg # type: ignore[attr-defined]
82-
error_message = re.sub(" +$", "", error_message, flags=re.MULTILINE)
82+
if expected_output is not None:
83+
if expected_output.startswith("\n"):
84+
expected_output = expected_output[1:]
85+
expected_output = textwrap.dedent(expected_output)
86+
87+
output, e = launch_command_capturing_output_and_exception(*cmd_and_args)
88+
assert e is not None, f"Expected {expected_type.__name__} but no exception was raised"
89+
assert isinstance(e, expected_type), f"Expected {expected_type.__name__} but got {type(e).__name__}: {e}"
90+
error_message = strip_trailing_spaces(e.msg) # type: ignore[attr-defined]
8391
assert error_message == expected_message
8492

93+
if expected_output is not None:
94+
actual_output = strip_trailing_spaces(textwrap.dedent(output or ""))
95+
assert actual_output == expected_output
96+
8597

8698
def read_branch_layout_file() -> str:
8799
with open(".git/machete") as def_file:

tests/test_traverse.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import os
22

3+
import pytest
34
from pytest_mock import MockerFixture
45

56
from git_machete.utils import UnderlyingGitException
@@ -1922,3 +1923,28 @@ def test_traverse_yellow_edges(self, mocker: MockerFixture) -> None:
19221923
Returned to the initial branch feature-2
19231924
"""
19241925
)
1926+
1927+
# The expected error message includes `--empty=drop` which is only passed on git >= 2.26.0.
1928+
@pytest.mark.skipif(get_git_version() < (2, 26, 0), reason="--empty=drop is only passed to git rebase since git 2.26.0")
1929+
def test_traverse_rebase_conflict(self) -> None:
1930+
create_repo()
1931+
with fixed_author_and_committer_date_in_past():
1932+
new_branch("master")
1933+
add_file_and_commit("file.txt", "base content\n", "Base commit")
1934+
new_branch("feature")
1935+
add_file_and_commit("file.txt", "feature content\n", "Feature commit")
1936+
check_out("master")
1937+
add_file_and_commit("file.txt", "master content\n", "Master commit")
1938+
check_out("feature")
1939+
1940+
rewrite_branch_layout_file("master\n\tfeature")
1941+
1942+
assert_failure(
1943+
["traverse", "-y"],
1944+
"git -c log.showSignature=false rebase --empty=drop"
1945+
" --onto refs/heads/master 77b81e64de792099dad58d67756b66cda9e80aa7 feature returned 1",
1946+
expected_type=UnderlyingGitException,
1947+
expected_output="""
1948+
Rebasing feature onto master...
1949+
"""
1950+
)

tests/test_traverse_worktrees.py

Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@
44
import pytest
55
from pytest_mock import MockerFixture
66

7-
from git_machete.utils import abspath_posix
7+
from git_machete.utils import UnderlyingGitException, abspath_posix
88

99
from .base_test import BaseTest
10-
from .mockers import (assert_success, launch_command, mock_input_returning,
11-
rewrite_branch_layout_file)
12-
from .mockers_git_repository import (add_worktree, check_out, commit,
10+
from .mockers import (assert_failure, assert_success,
11+
fixed_author_and_committer_date_in_past, launch_command,
12+
mock_input_returning, rewrite_branch_layout_file)
13+
from .mockers_git_repository import (add_file_and_commit, add_worktree,
14+
check_out, commit,
1315
create_repo_with_remote, get_git_version,
1416
new_branch, push, set_git_config_key)
1517

@@ -517,3 +519,52 @@ def test_traverse_stay_in_same_worktree_when_branch_not_checked_out(self, mocker
517519
cd {normalized_branch_2_worktree}
518520
"""
519521
)
522+
523+
# The expected error message includes `--empty=drop` which is only passed on git >= 2.26.0.
524+
@pytest.mark.skipif(get_git_version() < (2, 26, 0), reason="--empty=drop is only passed to git rebase since git 2.26.0")
525+
def test_traverse_rebase_conflict_in_worktree(self) -> None:
526+
create_repo_with_remote()
527+
with fixed_author_and_committer_date_in_past():
528+
new_branch("base")
529+
add_file_and_commit("file.txt", "base content\n", "Base commit")
530+
push()
531+
new_branch("feature")
532+
add_file_and_commit("file.txt", "feature content\n", "Feature commit")
533+
push()
534+
535+
body: str = \
536+
"""
537+
base
538+
feature
539+
"""
540+
rewrite_branch_layout_file(body)
541+
542+
# Create a worktree for feature
543+
check_out("base")
544+
feature_worktree = add_worktree("feature")
545+
normalized_feature_worktree = abspath_posix(feature_worktree)
546+
547+
# Make a conflicting change on base
548+
check_out("base")
549+
with fixed_author_and_committer_date_in_past():
550+
add_file_and_commit("file.txt", "conflicting base content\n", "Conflicting base commit")
551+
push()
552+
553+
assert_failure(
554+
["traverse", "-y"],
555+
"git -c log.showSignature=false rebase --empty=drop"
556+
" --onto refs/heads/base 77b81e64de792099dad58d67756b66cda9e80aa7 feature returned 1",
557+
expected_type=UnderlyingGitException,
558+
expected_output=f"""
559+
Changing directory to {normalized_feature_worktree} worktree where feature is checked out
560+
561+
base
562+
|
563+
x-feature *
564+
565+
Rebasing feature onto base...
566+
Warn: branch feature is checked out in worktree at {normalized_feature_worktree}
567+
You may want to change directory with:
568+
cd {normalized_feature_worktree}
569+
"""
570+
)

0 commit comments

Comments
 (0)