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,
|
reestablish the connection. If multiple linked working trees are moved,
|
||||||
running `repair` from any working tree with each tree's new `<path>` as
|
running `repair` from any working tree with each tree's new `<path>` as
|
||||||
an argument, will reestablish the connection to all the specified paths.
|
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::
|
unlock::
|
||||||
|
|
||||||
|
@ -1052,10 +1052,10 @@ static int repair(int ac, const char **av, const char *prefix)
|
|||||||
int rc = 0;
|
int rc = 0;
|
||||||
|
|
||||||
ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
|
ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
|
||||||
repair_worktrees(report_repair, &rc);
|
|
||||||
p = ac > 0 ? av : self;
|
p = ac > 0 ? av : self;
|
||||||
for (; *p; p++)
|
for (; *p; p++)
|
||||||
repair_worktree_at_path(*p, report_repair, &rc);
|
repair_worktree_at_path(*p, report_repair, &rc);
|
||||||
|
repair_worktrees(report_repair, &rc);
|
||||||
return 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_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_expect_success 'repo not found; .git file broken' '
|
||||||
test_when_finished "rm -rf orig moved && git worktree prune" &&
|
test_when_finished "rm -rf orig moved && git worktree prune" &&
|
||||||
git worktree add --detach orig &&
|
git worktree add --detach orig &&
|
||||||
@ -176,4 +186,20 @@ test_expect_success 'repair multiple gitdir files' '
|
|||||||
test_must_be_empty err
|
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
|
test_done
|
||||||
|
41
worktree.c
41
worktree.c
@ -644,6 +644,42 @@ static int is_main_worktree_path(const char *path)
|
|||||||
return !cmp;
|
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
|
* Repair <repo>/worktrees/<id>/gitdir if missing, corrupt, or not pointing at
|
||||||
* the worktree's path.
|
* the worktree's path.
|
||||||
@ -675,6 +711,11 @@ void repair_worktree_at_path(const char *path,
|
|||||||
if (err == READ_GITFILE_ERR_NOT_A_FILE) {
|
if (err == READ_GITFILE_ERR_NOT_A_FILE) {
|
||||||
fn(1, realdotgit.buf, _("unable to locate repository; .git is not a file"), cb_data);
|
fn(1, realdotgit.buf, _("unable to locate repository; .git is not a file"), cb_data);
|
||||||
goto done;
|
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) {
|
} else if (err) {
|
||||||
fn(1, realdotgit.buf, _("unable to locate repository; .git file broken"), cb_data);
|
fn(1, realdotgit.buf, _("unable to locate repository; .git file broken"), cb_data);
|
||||||
goto done;
|
goto done;
|
||||||
|
Loading…
Reference in New Issue
Block a user