Skip to content

Commit 71a1472

Browse files
committed
Skip redundant restore fetch when all patches already applied
In the default (all-patches) case review-patch was fetching three times: once for clean upstream, once to apply all patches, and once more in the finally block to "restore" — even though the working tree was already fully patched after the second fetch. Track worktree_fully_patched after the non-interactive update call and skip the finally fetch when the working tree is already at patch_count=-1. The --count N and --interactive paths still re-fetch because the final working-tree state differs from fully patched. Update tests and the synthetic demo cast accordingly. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017zY8BoH65KBX6cz7Pm8aeF
1 parent 244185b commit 71a1472

3 files changed

Lines changed: 19 additions & 31 deletions

File tree

‎dfetch/commands/review_patch.py‎

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ def _ignored() -> list[str]:
160160
assert isinstance(superproject, GitSuperProject)
161161
superproject.add_path(subproject.local_path)
162162

163+
worktree_fully_patched = False
163164
try:
164165
if interactive:
165166
_step_tui(
@@ -177,6 +178,7 @@ def _ignored() -> list[str]:
177178
patch_count=chosen_count,
178179
eol_preferences_callback=superproject.eol_preferences,
179180
)
181+
worktree_fully_patched = chosen_count == -1
180182
patch_label = (
181183
str(total_patches) if chosen_count == -1 else str(chosen_count)
182184
)
@@ -188,12 +190,13 @@ def _ignored() -> list[str]:
188190
if is_tty():
189191
input("Press Enter to restore...")
190192
finally:
191-
subproject.update(
192-
force=True,
193-
ignored_files_callback=_ignored,
194-
patch_count=-1,
195-
eol_preferences_callback=superproject.eol_preferences,
196-
)
193+
if not worktree_fully_patched:
194+
subproject.update(
195+
force=True,
196+
ignored_files_callback=_ignored,
197+
patch_count=-1,
198+
eol_preferences_callback=superproject.eol_preferences,
199+
)
197200
if is_git:
198201
assert isinstance(superproject, GitSuperProject)
199202
superproject.restore_staged(subproject.local_path)

‎doc/asciicasts/review-patch.cast‎

Lines changed: 7 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -14,45 +14,32 @@
1414
[7.45, "o", "\u001b[1;34mDfetch (0.14.0)\u001b[0m\r\n"]
1515
[7.49, "o", " \u001b[1;92mcpputest:\u001b[0m\r\n"]
1616
[7.5, "o", "\u001b[?25l"]
17-
[7.58, "o", "\u001b[32m⠋\u001b[0m \u001b[1;94m> Fetching v3.4\u001b[0m"]
17+
[7.58, "o", "\u001b[32m⠇\u001b[0m \u001b[1;94m> Fetching v3.4\u001b[0m"]
1818
[7.66, "o", "\r\u001b[2K\u001b[32m⠙\u001b[0m \u001b[1;94m> Fetching v3.4\u001b[0m"]
1919
[7.74, "o", "\r\u001b[2K\u001b[32m⠹\u001b[0m \u001b[1;94m> Fetching v3.4\u001b[0m"]
2020
[7.82, "o", "\r\u001b[2K\u001b[32m⠸\u001b[0m \u001b[1;94m> Fetching v3.4\u001b[0m"]
2121
[7.9, "o", "\r\u001b[2K\u001b[32m⠼\u001b[0m \u001b[1;94m> Fetching v3.4\u001b[0m"]
2222
[7.98, "o", "\r\u001b[2K\u001b[32m⠴\u001b[0m \u001b[1;94m> Fetching v3.4\u001b[0m"]
2323
[8.06, "o", "\r\u001b[2K\u001b[32m⠦\u001b[0m \u001b[1;94m> Fetching v3.4\u001b[0m"]
24-
[8.14, "o", "\r\u001b[2K\u001b[32m⠇\u001b[0m \u001b[1;94m> Fetching v3.4\u001b[0m"]
24+
[8.14, "o", "\r\u001b[2K\u001b[32m⠧\u001b[0m \u001b[1;94m> Fetching v3.4\u001b[0m"]
2525
[8.22, "o", "\r\u001b[2K\u001b[32m⠏\u001b[0m \u001b[1;94m> Fetching v3.4\u001b[0m\r\n\u001b[?25h\r\u001b[1A\u001b[2K"]
2626
[8.23, "o", " \u001b[1;34m> Fetched v3.4\u001b[0m\r\n"]
2727
[8.3, "o", "\u001b[?25l"]
28-
[8.38, "o", "\u001b[32m⠋\u001b[0m \u001b[1;94m> Fetching v3.4\u001b[0m"]
28+
[8.38, "o", "\u001b[32m⠇\u001b[0m \u001b[1;94m> Fetching v3.4\u001b[0m"]
2929
[8.46, "o", "\r\u001b[2K\u001b[32m⠙\u001b[0m \u001b[1;94m> Fetching v3.4\u001b[0m"]
3030
[8.54, "o", "\r\u001b[2K\u001b[32m⠹\u001b[0m \u001b[1;94m> Fetching v3.4\u001b[0m"]
3131
[8.62, "o", "\r\u001b[2K\u001b[32m⠸\u001b[0m \u001b[1;94m> Fetching v3.4\u001b[0m"]
3232
[8.7, "o", "\r\u001b[2K\u001b[32m⠼\u001b[0m \u001b[1;94m> Fetching v3.4\u001b[0m"]
3333
[8.78, "o", "\r\u001b[2K\u001b[32m⠴\u001b[0m \u001b[1;94m> Fetching v3.4\u001b[0m"]
3434
[8.86, "o", "\r\u001b[2K\u001b[32m⠦\u001b[0m \u001b[1;94m> Fetching v3.4\u001b[0m"]
35-
[8.94, "o", "\r\u001b[2K\u001b[32m⠇\u001b[0m \u001b[1;94m> Fetching v3.4\u001b[0m"]
35+
[8.94, "o", "\r\u001b[2K\u001b[32m⠧\u001b[0m \u001b[1;94m> Fetching v3.4\u001b[0m"]
3636
[9.02, "o", "\r\u001b[2K\u001b[32m⠏\u001b[0m \u001b[1;94m> Fetching v3.4\u001b[0m\r\n\u001b[?25h\r\u001b[1A\u001b[2K"]
3737
[9.03, "o", " \u001b[1;34m> Fetched v3.4\u001b[0m\r\n"]
3838
[9.04, "o", " \u001b[1;34m> Applying patch \"patches/cpputest.patch\"\u001b[0m\r\n"]
3939
[9.09, "o", " \u001b[34msuccessfully patched 1/1: \u001b[0m\u001b[34m \u001b[0m \r\n\u001b[34mb'README.md'\u001b[0m \r\n"]
4040
[9.15, "o", " \u001b[1;34m> stage = upstream, working tree = 1 patch(es) applied — open your editor and run `git diff` to inspect\u001b[0m\r\n"]
4141
[9.2, "o", "Press Enter to restore..."]
4242
[11.2, "o", "\r\n"]
43-
[11.3, "o", "\u001b[?25l"]
44-
[11.38, "o", "\u001b[32m⠋\u001b[0m \u001b[1;94m> Fetching v3.4\u001b[0m"]
45-
[11.46, "o", "\r\u001b[2K\u001b[32m⠙\u001b[0m \u001b[1;94m> Fetching v3.4\u001b[0m"]
46-
[11.54, "o", "\r\u001b[2K\u001b[32m⠹\u001b[0m \u001b[1;94m> Fetching v3.4\u001b[0m"]
47-
[11.62, "o", "\r\u001b[2K\u001b[32m⠸\u001b[0m \u001b[1;94m> Fetching v3.4\u001b[0m"]
48-
[11.7, "o", "\r\u001b[2K\u001b[32m⠼\u001b[0m \u001b[1;94m> Fetching v3.4\u001b[0m"]
49-
[11.78, "o", "\r\u001b[2K\u001b[32m⠴\u001b[0m \u001b[1;94m> Fetching v3.4\u001b[0m"]
50-
[11.86, "o", "\r\u001b[2K\u001b[32m⠦\u001b[0m \u001b[1;94m> Fetching v3.4\u001b[0m"]
51-
[11.94, "o", "\r\u001b[2K\u001b[32m⠇\u001b[0m \u001b[1;94m> Fetching v3.4\u001b[0m"]
52-
[12.02, "o", "\r\u001b[2K\u001b[32m⠏\u001b[0m \u001b[1;94m> Fetching v3.4\u001b[0m\r\n\u001b[?25h\r\u001b[1A\u001b[2K"]
53-
[12.03, "o", " \u001b[1;34m> Fetched v3.4\u001b[0m\r\n"]
54-
[12.04, "o", " \u001b[1;34m> Applying patch \"patches/cpputest.patch\"\u001b[0m\r\n"]
55-
[12.09, "o", " \u001b[34msuccessfully patched 1/1: \u001b[0m\u001b[34m \u001b[0m \r\n\u001b[34mb'README.md'\u001b[0m \r\n"]
56-
[12.15, "o", " \u001b[1;34m> restored\u001b[0m\r\n"]
57-
[12.2, "o", "$ "]
58-
[15.2, "o", ""]
43+
[11.25, "o", " \u001b[1;34m> restored\u001b[0m\r\n"]
44+
[11.3, "o", "$ "]
45+
[14.3, "o", ""]

‎tests/test_review_patch.py‎

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,16 +63,14 @@ def test_review_all_patches_calls_update_add_path_update():
6363
cmd(_make_args())
6464

6565
update_calls = fake_sub.update.call_args_list
66+
assert len(update_calls) == 2, "all-patches case must only fetch twice (no redundant restore fetch)"
6667
assert update_calls[0] == call(
6768
force=True, ignored_files_callback=ANY, patch_count=0, eol_preferences_callback=ANY
6869
), "first call must fetch clean upstream"
6970
fake_super.add_path.assert_called_once_with("my_project")
7071
assert update_calls[1] == call(
7172
force=True, ignored_files_callback=ANY, patch_count=-1, eol_preferences_callback=ANY
72-
), "second call (inside try) applies all patches"
73-
assert update_calls[2] == call(
74-
force=True, ignored_files_callback=ANY, patch_count=-1, eol_preferences_callback=ANY
75-
), "third call (finally) restores all patches"
73+
), "second call applies all patches; working tree is already restored so no third fetch"
7674
fake_super.restore_staged.assert_called_once_with("my_project")
7775

7876

@@ -116,7 +114,7 @@ def test_svn_superproject_warns_and_skips_staging():
116114
mock_log.warning.assert_called_once()
117115
fake_super.add_path.assert_not_called()
118116
fake_super.restore_staged.assert_not_called()
119-
assert fake_sub.update.call_count == 3
117+
assert fake_sub.update.call_count == 2
120118

121119

122120
# ---------------------------------------------------------------------------

0 commit comments

Comments
 (0)