This is a re-roll of [1] which addresses a limitation of `git worktree repair` in which it is unable to repair the two-way links between the repository and its secondary worktrees if both the repository and the worktrees have been moved manually. The primary change in v2 is that the commit message has been expanded quite a bit to explain how the new repair mechanism works, rather than expecting the reader to glean the necessary information from the patch itself, as well as to express the reasoning behind a couple changes which might not be obvious at first glance. The patch itself is unchanged with the exception of two minor alterations to one of the two added tests. First, anchoring has been added to a couple `sed` expressions. Second, the rename of the main and secondary worktrees is now done with two distinct `mv` commands rather than a single "clever" `mv` so as to avoid giving the reader the false impression that the cleverness implies something about how the repair mechanism works (it doesn't imply anything, it was just done as a convenience in v1). [1]: https://lore.kernel.org/git/20201208173705.5770-1-sunshine@xxxxxxxxxxxxxx/ Eric Sunshine (1): worktree: teach `repair` to fix multi-directional breakage Documentation/git-worktree.txt | 5 +++++ builtin/worktree.c | 2 +- t/t2406-worktree-repair.sh | 26 +++++++++++++++++++++ worktree.c | 41 ++++++++++++++++++++++++++++++++++ 4 files changed, 73 insertions(+), 1 deletion(-) Range-diff against v1: 1: 956e61bbc8 ! 1: cd38528672 worktree: teach `repair` to fix multi-directional breakage @@ Commit message Fix these shortcomings by teaching `repair` to attempt to infer the new location of the <repo>/worktrees/<id>/gitdir file when the location recorded in the worktree's gitfile has become stale but the file is - otherwise well-formed. + otherwise well-formed. The inference is intentionally simple-minded. + For each worktree path specified as an argument, `git worktree repair` + manually reads the ".git" gitfile at that location and, if it is + well-formed, extracts the <id>. It then searches for a corresponding + <id> in <repo>/worktrees/ and, if found, concludes that there is a + reasonable match and updates <repo>/worktrees/<id>/gitdir to point at + the specified worktree path. In order for <repo> to be known, `git + worktree repair` must be run in the main worktree or bare repository. + + `git worktree repair` first attempts to repair each incoming + /path/to/worktree/.git gitfile to point at the repository, and then + attempts to repair outgoing <repo>/worktrees/<id>/gitdir files to point + at the worktrees. This sequence was chosen arbitrarily when originally + implemented since the order of fixes is immaterial as long as one side + of the two-way link between the repository and a worktree is sound. + However, for this new repair technique to work, the order must be + reversed. This is because the new inference mechanism, when it is + successful, allows the outgoing <repo>/worktrees/<id>/gitdir file to be + repaired, thus fixing one side of the two-way link. Once that side is + fixed, the other side can be fixed by the existing repair mechanism, + hence the order of repairs is now significant. + + Two safeguards are employed to avoid hijacking a worktree from a + different repository if the user accidentally specifies a foreign + worktree as an argument. The first, as described above, is that it + requires an <id> match between the repository and the worktree. That + itself is not foolproof for preventing hijack, so the second safeguard + is that the inference will only kick in if the worktree's + /path/to/worktree/.git gitfile does not point at a repository. Signed-off-by: Eric Sunshine <sunshine@xxxxxxxxxxxxxx> @@ t/t2406-worktree-repair.sh: test_expect_success 'repair multiple gitdir files' ' ' +test_expect_success 'repair moved main and linked worktrees' ' -+ test_when_finished "rm -rf orig moved" && -+ test_create_repo orig/main && -+ test_commit -C orig/main init && -+ git -C orig/main worktree add --detach ../side && -+ sed s,orig/side/\.git,moved/side/.git, \ -+ orig/main/.git/worktrees/side/gitdir >expect-gitdir && -+ sed s,orig/main/.git/worktrees/side,moved/main/.git/worktrees/side, \ -+ orig/side/.git >expect-gitfile && -+ mv orig moved && -+ git -C moved/main worktree repair ../side && -+ test_cmp expect-gitdir moved/main/.git/worktrees/side/gitdir && -+ test_cmp expect-gitfile moved/side/.git ++ test_when_finished "rm -rf main side mainmoved sidemoved" && ++ test_create_repo main && ++ test_commit -C main init && ++ git -C main worktree add --detach ../side && ++ sed "s,side/\.git$,sidemoved/.git," \ ++ main/.git/worktrees/side/gitdir >expect-gitdir && ++ sed "s,main/.git/worktrees/side$,mainmoved/.git/worktrees/side," \ ++ side/.git >expect-gitfile && ++ mv main mainmoved && ++ mv side sidemoved && ++ git -C mainmoved worktree repair ../sidemoved && ++ test_cmp expect-gitdir mainmoved/.git/worktrees/side/gitdir && ++ test_cmp expect-gitfile sidemoved/.git +' + test_done -- 2.30.0.rc1.243.g5298b911bd