aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJunio C Hamano <gitster@pobox.com>2024-10-08 11:49:13 -0700
committerJunio C Hamano <gitster@pobox.com>2024-10-08 11:49:13 -0700
commit58d8805de246352b41ca7bfd6cd70078b94ff1f1 (patch)
tree746687410532de23d6182c39012518a396b68369
parent777489f9e09c8d0dd6b12f9d90de6376330577a2 (diff)
parent992f7a4fdbadda9f30ff3a8995e966f0562bc73a (diff)
downloadgit-58d8805de246352b41ca7bfd6cd70078b94ff1f1.tar.gz
Merge branch 'es/worktree-repair-copied' into cw/worktrees-relative
* es/worktree-repair-copied: worktree: repair copied repository and linked worktrees
-rw-r--r--Documentation/git-worktree.txt2
-rwxr-xr-xt/t2406-worktree-repair.sh19
-rw-r--r--worktree.c40
3 files changed, 59 insertions, 2 deletions
diff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt
index 2a240f53ba..70437c815f 100644
--- a/Documentation/git-worktree.txt
+++ b/Documentation/git-worktree.txt
@@ -157,7 +157,7 @@ will reestablish the connection. If multiple linked worktrees are moved,
running `repair` from any worktree with each tree's new `<path>` as an
argument, will reestablish the connection to all the specified paths.
+
-If both the main worktree and linked worktrees have been moved manually,
+If both the main worktree and linked worktrees have been moved or copied manually,
then running `repair` in the main worktree and specifying the new `<path>`
of each linked worktree will reestablish all connections in both
directions.
diff --git a/t/t2406-worktree-repair.sh b/t/t2406-worktree-repair.sh
index edbf502ec5..7686e60f6a 100755
--- a/t/t2406-worktree-repair.sh
+++ b/t/t2406-worktree-repair.sh
@@ -197,4 +197,23 @@ test_expect_success 'repair moved main and linked worktrees' '
test_cmp expect-gitfile sidemoved/.git
'
+test_expect_success 'repair copied main and linked worktrees' '
+ test_when_finished "rm -rf orig dup" &&
+ mkdir -p orig &&
+ git -C orig init main &&
+ test_commit -C orig/main nothing &&
+ git -C orig/main worktree add ../linked &&
+ cp orig/main/.git/worktrees/linked/gitdir orig/main.expect &&
+ cp orig/linked/.git orig/linked.expect &&
+ cp -R orig dup &&
+ sed "s,orig/linked/\.git$,dup/linked/.git," orig/main.expect >dup/main.expect &&
+ sed "s,orig/main/\.git/worktrees/linked$,dup/main/.git/worktrees/linked," \
+ orig/linked.expect >dup/linked.expect &&
+ git -C dup/main worktree repair ../linked &&
+ test_cmp orig/main.expect orig/main/.git/worktrees/linked/gitdir &&
+ test_cmp orig/linked.expect orig/linked/.git &&
+ test_cmp dup/main.expect dup/main/.git/worktrees/linked/gitdir &&
+ test_cmp dup/linked.expect dup/linked/.git
+'
+
test_done
diff --git a/worktree.c b/worktree.c
index 0f032ccedf..ec95ea2986 100644
--- a/worktree.c
+++ b/worktree.c
@@ -683,6 +683,7 @@ void repair_worktree_at_path(const char *path,
struct strbuf gitdir = STRBUF_INIT;
struct strbuf olddotgit = STRBUF_INIT;
char *backlink = NULL;
+ char *inferred_backlink = NULL;
const char *repair = NULL;
int err;
@@ -698,12 +699,24 @@ void repair_worktree_at_path(const char *path,
goto done;
}
+ inferred_backlink = infer_backlink(realdotgit.buf);
backlink = xstrdup_or_null(read_gitfile_gently(realdotgit.buf, &err));
if (err == READ_GITFILE_ERR_NOT_A_FILE) {
fn(1, realdotgit.buf, _("unable to locate repository; .git is not a file"), cb_data);
goto done;
} else if (err == READ_GITFILE_ERR_NOT_A_REPO) {
- if (!(backlink = infer_backlink(realdotgit.buf))) {
+ if (inferred_backlink) {
+ /*
+ * Worktree's .git file does not point at a repository
+ * but we found a .git/worktrees/<id> in this
+ * repository with the same <id> as recorded in the
+ * worktree's .git file so make the worktree point at
+ * the discovered .git/worktrees/<id>. (Note: backlink
+ * is already NULL, so no need to free it first.)
+ */
+ backlink = inferred_backlink;
+ inferred_backlink = NULL;
+ } else {
fn(1, realdotgit.buf, _("unable to locate repository; .git file does not reference a repository"), cb_data);
goto done;
}
@@ -712,6 +725,30 @@ void repair_worktree_at_path(const char *path,
goto done;
}
+ /*
+ * If we got this far, either the worktree's .git file pointed at a
+ * valid repository (i.e. read_gitfile_gently() returned success) or
+ * the .git file did not point at a repository but we were able to
+ * infer a suitable new value for the .git file by locating a
+ * .git/worktrees/<id> in *this* repository corresponding to the <id>
+ * recorded in the worktree's .git file.
+ *
+ * However, if, at this point, inferred_backlink is non-NULL (i.e. we
+ * found a suitable .git/worktrees/<id> in *this* repository) *and* the
+ * worktree's .git file points at a valid repository *and* those two
+ * paths differ, then that indicates that the user probably *copied*
+ * the main and linked worktrees to a new location as a unit rather
+ * than *moving* them. Thus, the copied worktree's .git file actually
+ * points at the .git/worktrees/<id> in the *original* repository, not
+ * in the "copy" repository. In this case, point the "copy" worktree's
+ * .git file at the "copy" repository.
+ */
+ if (inferred_backlink && fspathcmp(backlink, inferred_backlink)) {
+ free(backlink);
+ backlink = inferred_backlink;
+ inferred_backlink = NULL;
+ }
+
strbuf_addf(&gitdir, "%s/gitdir", backlink);
if (strbuf_read_file(&olddotgit, gitdir.buf, 0) < 0)
repair = _("gitdir unreadable");
@@ -727,6 +764,7 @@ void repair_worktree_at_path(const char *path,
}
done:
free(backlink);
+ free(inferred_backlink);
strbuf_release(&olddotgit);
strbuf_release(&gitdir);
strbuf_release(&realdotgit);