Skip to content

Commit 58d8805

Browse files
committed
Merge branch 'es/worktree-repair-copied' into cw/worktrees-relative
* es/worktree-repair-copied: worktree: repair copied repository and linked worktrees
2 parents 777489f + 992f7a4 commit 58d8805

File tree

3 files changed

+59
-2
lines changed

3 files changed

+59
-2
lines changed

Documentation/git-worktree.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ will reestablish the connection. If multiple linked worktrees are moved,
157157
running `repair` from any worktree with each tree's new `<path>` as an
158158
argument, will reestablish the connection to all the specified paths.
159159
+
160-
If both the main worktree and linked worktrees have been moved manually,
160+
If both the main worktree and linked worktrees have been moved or copied manually,
161161
then running `repair` in the main worktree and specifying the new `<path>`
162162
of each linked worktree will reestablish all connections in both
163163
directions.

t/t2406-worktree-repair.sh

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,4 +197,23 @@ test_expect_success 'repair moved main and linked worktrees' '
197197
test_cmp expect-gitfile sidemoved/.git
198198
'
199199

200+
test_expect_success 'repair copied main and linked worktrees' '
201+
test_when_finished "rm -rf orig dup" &&
202+
mkdir -p orig &&
203+
git -C orig init main &&
204+
test_commit -C orig/main nothing &&
205+
git -C orig/main worktree add ../linked &&
206+
cp orig/main/.git/worktrees/linked/gitdir orig/main.expect &&
207+
cp orig/linked/.git orig/linked.expect &&
208+
cp -R orig dup &&
209+
sed "s,orig/linked/\.git$,dup/linked/.git," orig/main.expect >dup/main.expect &&
210+
sed "s,orig/main/\.git/worktrees/linked$,dup/main/.git/worktrees/linked," \
211+
orig/linked.expect >dup/linked.expect &&
212+
git -C dup/main worktree repair ../linked &&
213+
test_cmp orig/main.expect orig/main/.git/worktrees/linked/gitdir &&
214+
test_cmp orig/linked.expect orig/linked/.git &&
215+
test_cmp dup/main.expect dup/main/.git/worktrees/linked/gitdir &&
216+
test_cmp dup/linked.expect dup/linked/.git
217+
'
218+
200219
test_done

worktree.c

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -683,6 +683,7 @@ void repair_worktree_at_path(const char *path,
683683
struct strbuf gitdir = STRBUF_INIT;
684684
struct strbuf olddotgit = STRBUF_INIT;
685685
char *backlink = NULL;
686+
char *inferred_backlink = NULL;
686687
const char *repair = NULL;
687688
int err;
688689

@@ -698,12 +699,24 @@ void repair_worktree_at_path(const char *path,
698699
goto done;
699700
}
700701

702+
inferred_backlink = infer_backlink(realdotgit.buf);
701703
backlink = xstrdup_or_null(read_gitfile_gently(realdotgit.buf, &err));
702704
if (err == READ_GITFILE_ERR_NOT_A_FILE) {
703705
fn(1, realdotgit.buf, _("unable to locate repository; .git is not a file"), cb_data);
704706
goto done;
705707
} else if (err == READ_GITFILE_ERR_NOT_A_REPO) {
706-
if (!(backlink = infer_backlink(realdotgit.buf))) {
708+
if (inferred_backlink) {
709+
/*
710+
* Worktree's .git file does not point at a repository
711+
* but we found a .git/worktrees/<id> in this
712+
* repository with the same <id> as recorded in the
713+
* worktree's .git file so make the worktree point at
714+
* the discovered .git/worktrees/<id>. (Note: backlink
715+
* is already NULL, so no need to free it first.)
716+
*/
717+
backlink = inferred_backlink;
718+
inferred_backlink = NULL;
719+
} else {
707720
fn(1, realdotgit.buf, _("unable to locate repository; .git file does not reference a repository"), cb_data);
708721
goto done;
709722
}
@@ -712,6 +725,30 @@ void repair_worktree_at_path(const char *path,
712725
goto done;
713726
}
714727

728+
/*
729+
* If we got this far, either the worktree's .git file pointed at a
730+
* valid repository (i.e. read_gitfile_gently() returned success) or
731+
* the .git file did not point at a repository but we were able to
732+
* infer a suitable new value for the .git file by locating a
733+
* .git/worktrees/<id> in *this* repository corresponding to the <id>
734+
* recorded in the worktree's .git file.
735+
*
736+
* However, if, at this point, inferred_backlink is non-NULL (i.e. we
737+
* found a suitable .git/worktrees/<id> in *this* repository) *and* the
738+
* worktree's .git file points at a valid repository *and* those two
739+
* paths differ, then that indicates that the user probably *copied*
740+
* the main and linked worktrees to a new location as a unit rather
741+
* than *moving* them. Thus, the copied worktree's .git file actually
742+
* points at the .git/worktrees/<id> in the *original* repository, not
743+
* in the "copy" repository. In this case, point the "copy" worktree's
744+
* .git file at the "copy" repository.
745+
*/
746+
if (inferred_backlink && fspathcmp(backlink, inferred_backlink)) {
747+
free(backlink);
748+
backlink = inferred_backlink;
749+
inferred_backlink = NULL;
750+
}
751+
715752
strbuf_addf(&gitdir, "%s/gitdir", backlink);
716753
if (strbuf_read_file(&olddotgit, gitdir.buf, 0) < 0)
717754
repair = _("gitdir unreadable");
@@ -727,6 +764,7 @@ void repair_worktree_at_path(const char *path,
727764
}
728765
done:
729766
free(backlink);
767+
free(inferred_backlink);
730768
strbuf_release(&olddotgit);
731769
strbuf_release(&gitdir);
732770
strbuf_release(&realdotgit);

0 commit comments

Comments
 (0)