Merge branch 'es/worktree-repair-both-moved'
"git worktree repair" learned to deal with the case where both the repository and the worktree moved. * es/worktree-repair-both-moved: worktree: teach `repair` to fix multi-directional breakage
This commit is contained in:
commit
8664fcb83b
@ -143,6 +143,11 @@ locate it. Running `repair` within the recently-moved working tree will
|
||||
reestablish the connection. If multiple linked working trees are moved,
|
||||
running `repair` from any working tree with each tree's new `<path>` as
|
||||
an argument, will reestablish the connection to all the specified paths.
|
||||
+
|
||||
If both the main working tree and linked working trees have been moved
|
||||
manually, then running `repair` in the main working tree and specifying the
|
||||
new `<path>` of each linked working tree will reestablish all connections
|
||||
in both directions.
|
||||
|
||||
unlock::
|
||||
|
||||
|
@ -1052,10 +1052,10 @@ static int repair(int ac, const char **av, const char *prefix)
|
||||
int rc = 0;
|
||||
|
||||
ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
|
||||
repair_worktrees(report_repair, &rc);
|
||||
p = ac > 0 ? av : self;
|
||||
for (; *p; p++)
|
||||
repair_worktree_at_path(*p, report_repair, &rc);
|
||||
repair_worktrees(report_repair, &rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -104,6 +104,16 @@ test_expect_success 'repo not found; .git not file' '
|
||||
test_i18ngrep ".git is not a file" err
|
||||
'
|
||||
|
||||
test_expect_success 'repo not found; .git not referencing repo' '
|
||||
test_when_finished "rm -rf side not-a-repo && git worktree prune" &&
|
||||
git worktree add --detach side &&
|
||||
sed s,\.git/worktrees/side$,not-a-repo, side/.git >side/.newgit &&
|
||||
mv side/.newgit side/.git &&
|
||||
mkdir not-a-repo &&
|
||||
test_must_fail git worktree repair side 2>err &&
|
||||
test_i18ngrep ".git file does not reference a repository" err
|
||||
'
|
||||
|
||||
test_expect_success 'repo not found; .git file broken' '
|
||||
test_when_finished "rm -rf orig moved && git worktree prune" &&
|
||||
git worktree add --detach orig &&
|
||||
@ -176,4 +186,20 @@ test_expect_success 'repair multiple gitdir files' '
|
||||
test_must_be_empty err
|
||||
'
|
||||
|
||||
test_expect_success 'repair moved main and linked worktrees' '
|
||||
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
|
||||
|
41
worktree.c
41
worktree.c
@ -644,6 +644,42 @@ static int is_main_worktree_path(const char *path)
|
||||
return !cmp;
|
||||
}
|
||||
|
||||
/*
|
||||
* If both the main worktree and linked worktree have been moved, then the
|
||||
* gitfile /path/to/worktree/.git won't point into the repository, thus we
|
||||
* won't know which <repo>/worktrees/<id>/gitdir to repair. However, we may
|
||||
* be able to infer the gitdir by manually reading /path/to/worktree/.git,
|
||||
* extracting the <id>, and checking if <repo>/worktrees/<id> exists.
|
||||
*/
|
||||
static char *infer_backlink(const char *gitfile)
|
||||
{
|
||||
struct strbuf actual = STRBUF_INIT;
|
||||
struct strbuf inferred = STRBUF_INIT;
|
||||
const char *id;
|
||||
|
||||
if (strbuf_read_file(&actual, gitfile, 0) < 0)
|
||||
goto error;
|
||||
if (!starts_with(actual.buf, "gitdir:"))
|
||||
goto error;
|
||||
if (!(id = find_last_dir_sep(actual.buf)))
|
||||
goto error;
|
||||
strbuf_trim(&actual);
|
||||
id++; /* advance past '/' to point at <id> */
|
||||
if (!*id)
|
||||
goto error;
|
||||
strbuf_git_common_path(&inferred, the_repository, "worktrees/%s", id);
|
||||
if (!is_directory(inferred.buf))
|
||||
goto error;
|
||||
|
||||
strbuf_release(&actual);
|
||||
return strbuf_detach(&inferred, NULL);
|
||||
|
||||
error:
|
||||
strbuf_release(&actual);
|
||||
strbuf_release(&inferred);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Repair <repo>/worktrees/<id>/gitdir if missing, corrupt, or not pointing at
|
||||
* the worktree's path.
|
||||
@ -675,6 +711,11 @@ void repair_worktree_at_path(const char *path,
|
||||
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))) {
|
||||
fn(1, realdotgit.buf, _("unable to locate repository; .git file does not reference a repository"), cb_data);
|
||||
goto done;
|
||||
}
|
||||
} else if (err) {
|
||||
fn(1, realdotgit.buf, _("unable to locate repository; .git file broken"), cb_data);
|
||||
goto done;
|
||||
|
Loading…
Reference in New Issue
Block a user