Merge branch 'cw/worktree-relative'
An extra worktree attached to a repository points at each other to allow finding the repository from the worktree and vice versa possible. Turn this linkage to relative paths. * cw/worktree-relative: worktree: add test for path handling in linked worktrees worktree: link worktrees with relative paths worktree: refactor infer_backlink() to use *strbuf worktree: repair copied repository and linked worktreesmaint
commit
8e08668322
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -414,7 +414,8 @@ static int add_worktree(const char *path, const char *refname,
|
|||
const struct add_opts *opts)
|
||||
{
|
||||
struct strbuf sb_git = STRBUF_INIT, sb_repo = STRBUF_INIT;
|
||||
struct strbuf sb = STRBUF_INIT, realpath = STRBUF_INIT;
|
||||
struct strbuf sb = STRBUF_INIT, sb_tmp = STRBUF_INIT;
|
||||
struct strbuf sb_path_realpath = STRBUF_INIT, sb_repo_realpath = STRBUF_INIT;
|
||||
const char *name;
|
||||
struct strvec child_env = STRVEC_INIT;
|
||||
unsigned int counter = 0;
|
||||
|
|
@ -490,11 +491,10 @@ static int add_worktree(const char *path, const char *refname,
|
|||
|
||||
strbuf_reset(&sb);
|
||||
strbuf_addf(&sb, "%s/gitdir", sb_repo.buf);
|
||||
strbuf_realpath(&realpath, sb_git.buf, 1);
|
||||
write_file(sb.buf, "%s", realpath.buf);
|
||||
strbuf_realpath(&realpath, repo_get_common_dir(the_repository), 1);
|
||||
write_file(sb_git.buf, "gitdir: %s/worktrees/%s",
|
||||
realpath.buf, name);
|
||||
strbuf_realpath(&sb_path_realpath, path, 1);
|
||||
strbuf_realpath(&sb_repo_realpath, sb_repo.buf, 1);
|
||||
write_file(sb.buf, "%s/.git", relative_path(sb_path_realpath.buf, sb_repo_realpath.buf, &sb_tmp));
|
||||
write_file(sb_git.buf, "gitdir: %s", relative_path(sb_repo_realpath.buf, sb_path_realpath.buf, &sb_tmp));
|
||||
strbuf_reset(&sb);
|
||||
strbuf_addf(&sb, "%s/commondir", sb_repo.buf);
|
||||
write_file(sb.buf, "../..");
|
||||
|
|
@ -578,11 +578,13 @@ done:
|
|||
|
||||
strvec_clear(&child_env);
|
||||
strbuf_release(&sb);
|
||||
strbuf_release(&sb_tmp);
|
||||
strbuf_release(&symref);
|
||||
strbuf_release(&sb_repo);
|
||||
strbuf_release(&sb_repo_realpath);
|
||||
strbuf_release(&sb_git);
|
||||
strbuf_release(&sb_path_realpath);
|
||||
strbuf_release(&sb_name);
|
||||
strbuf_release(&realpath);
|
||||
free_worktree(wt);
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
2
setup.c
2
setup.c
|
|
@ -2420,7 +2420,7 @@ static void separate_git_dir(const char *git_dir, const char *git_link)
|
|||
|
||||
if (rename(src, git_dir))
|
||||
die_errno(_("unable to move %s to %s"), src, git_dir);
|
||||
repair_worktrees(NULL, NULL);
|
||||
repair_worktrees_after_gitdir_move(src);
|
||||
}
|
||||
|
||||
write_file(git_link, "gitdir: %s", git_dir);
|
||||
|
|
|
|||
|
|
@ -120,4 +120,23 @@ test_expect_success 'prune duplicate (main/linked)' '
|
|||
! test -d .git/worktrees/wt
|
||||
'
|
||||
|
||||
test_expect_success 'not prune proper worktrees when run inside linked worktree' '
|
||||
test_when_finished rm -rf repo wt_ext &&
|
||||
git init repo &&
|
||||
(
|
||||
cd repo &&
|
||||
echo content >file &&
|
||||
git add file &&
|
||||
git commit -m msg &&
|
||||
git worktree add ../wt_ext &&
|
||||
git worktree add wt_int &&
|
||||
cd wt_int &&
|
||||
git worktree prune -v >out &&
|
||||
test_must_be_empty out &&
|
||||
cd ../../wt_ext &&
|
||||
git worktree prune -v >out &&
|
||||
test_must_be_empty out
|
||||
)
|
||||
'
|
||||
|
||||
test_done
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
#!/bin/sh
|
||||
|
||||
test_description='test worktrees linked with relative paths'
|
||||
|
||||
TEST_PASSES_SANITIZE_LEAK=true
|
||||
. ./test-lib.sh
|
||||
|
||||
test_expect_success 'links worktrees with relative paths' '
|
||||
test_when_finished rm -rf repo &&
|
||||
git init repo &&
|
||||
(
|
||||
cd repo &&
|
||||
test_commit initial &&
|
||||
git worktree add wt1 &&
|
||||
echo "../../../wt1/.git" >expected_gitdir &&
|
||||
cat .git/worktrees/wt1/gitdir >actual_gitdir &&
|
||||
echo "gitdir: ../.git/worktrees/wt1" >expected_git &&
|
||||
cat wt1/.git >actual_git &&
|
||||
test_cmp expected_gitdir actual_gitdir &&
|
||||
test_cmp expected_git actual_git
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'move repo without breaking relative internal links' '
|
||||
test_when_finished rm -rf repo moved &&
|
||||
git init repo &&
|
||||
(
|
||||
cd repo &&
|
||||
test_commit initial &&
|
||||
git worktree add wt1 &&
|
||||
cd .. &&
|
||||
mv repo moved &&
|
||||
cd moved/wt1 &&
|
||||
git status >out 2>err &&
|
||||
test_must_be_empty err
|
||||
)
|
||||
'
|
||||
|
||||
test_done
|
||||
269
worktree.c
269
worktree.c
|
|
@ -110,6 +110,12 @@ struct worktree *get_linked_worktree(const char *id,
|
|||
strbuf_rtrim(&worktree_path);
|
||||
strbuf_strip_suffix(&worktree_path, "/.git");
|
||||
|
||||
if (!is_absolute_path(worktree_path.buf)) {
|
||||
strbuf_strip_suffix(&path, "gitdir");
|
||||
strbuf_addbuf(&path, &worktree_path);
|
||||
strbuf_realpath_forgiving(&worktree_path, path.buf, 0);
|
||||
}
|
||||
|
||||
CALLOC_ARRAY(worktree, 1);
|
||||
worktree->repo = the_repository;
|
||||
worktree->path = strbuf_detach(&worktree_path, NULL);
|
||||
|
|
@ -373,18 +379,29 @@ done:
|
|||
void update_worktree_location(struct worktree *wt, const char *path_)
|
||||
{
|
||||
struct strbuf path = STRBUF_INIT;
|
||||
struct strbuf repo = STRBUF_INIT;
|
||||
struct strbuf file = STRBUF_INIT;
|
||||
struct strbuf tmp = STRBUF_INIT;
|
||||
|
||||
if (is_main_worktree(wt))
|
||||
BUG("can't relocate main worktree");
|
||||
|
||||
strbuf_realpath(&repo, git_common_path("worktrees/%s", wt->id), 1);
|
||||
strbuf_realpath(&path, path_, 1);
|
||||
if (fspathcmp(wt->path, path.buf)) {
|
||||
write_file(git_common_path("worktrees/%s/gitdir", wt->id),
|
||||
"%s/.git", path.buf);
|
||||
strbuf_addf(&file, "%s/gitdir", repo.buf);
|
||||
write_file(file.buf, "%s/.git", relative_path(path.buf, repo.buf, &tmp));
|
||||
strbuf_reset(&file);
|
||||
strbuf_addf(&file, "%s/.git", path.buf);
|
||||
write_file(file.buf, "gitdir: %s", relative_path(repo.buf, path.buf, &tmp));
|
||||
|
||||
free(wt->path);
|
||||
wt->path = strbuf_detach(&path, NULL);
|
||||
}
|
||||
strbuf_release(&path);
|
||||
strbuf_release(&repo);
|
||||
strbuf_release(&file);
|
||||
strbuf_release(&tmp);
|
||||
}
|
||||
|
||||
int is_worktree_being_rebased(const struct worktree *wt,
|
||||
|
|
@ -564,38 +581,52 @@ static void repair_gitfile(struct worktree *wt,
|
|||
{
|
||||
struct strbuf dotgit = STRBUF_INIT;
|
||||
struct strbuf repo = STRBUF_INIT;
|
||||
char *backlink;
|
||||
struct strbuf backlink = STRBUF_INIT;
|
||||
struct strbuf tmp = STRBUF_INIT;
|
||||
char *dotgit_contents = NULL;
|
||||
const char *repair = NULL;
|
||||
int err;
|
||||
|
||||
/* missing worktree can't be repaired */
|
||||
if (!file_exists(wt->path))
|
||||
return;
|
||||
goto done;
|
||||
|
||||
if (!is_directory(wt->path)) {
|
||||
fn(1, wt->path, _("not a directory"), cb_data);
|
||||
return;
|
||||
goto done;
|
||||
}
|
||||
|
||||
strbuf_realpath(&repo, git_common_path("worktrees/%s", wt->id), 1);
|
||||
strbuf_addf(&dotgit, "%s/.git", wt->path);
|
||||
backlink = xstrdup_or_null(read_gitfile_gently(dotgit.buf, &err));
|
||||
dotgit_contents = xstrdup_or_null(read_gitfile_gently(dotgit.buf, &err));
|
||||
|
||||
if (dotgit_contents) {
|
||||
if (is_absolute_path(dotgit_contents)) {
|
||||
strbuf_addstr(&backlink, dotgit_contents);
|
||||
} else {
|
||||
strbuf_addf(&backlink, "%s/%s", wt->path, dotgit_contents);
|
||||
strbuf_realpath_forgiving(&backlink, backlink.buf, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (err == READ_GITFILE_ERR_NOT_A_FILE)
|
||||
fn(1, wt->path, _(".git is not a file"), cb_data);
|
||||
else if (err)
|
||||
repair = _(".git file broken");
|
||||
else if (fspathcmp(backlink, repo.buf))
|
||||
else if (fspathcmp(backlink.buf, repo.buf))
|
||||
repair = _(".git file incorrect");
|
||||
|
||||
if (repair) {
|
||||
fn(0, wt->path, repair, cb_data);
|
||||
write_file(dotgit.buf, "gitdir: %s", repo.buf);
|
||||
write_file(dotgit.buf, "gitdir: %s", relative_path(repo.buf, wt->path, &tmp));
|
||||
}
|
||||
|
||||
free(backlink);
|
||||
done:
|
||||
free(dotgit_contents);
|
||||
strbuf_release(&repo);
|
||||
strbuf_release(&dotgit);
|
||||
strbuf_release(&backlink);
|
||||
strbuf_release(&tmp);
|
||||
}
|
||||
|
||||
static void repair_noop(int iserr UNUSED,
|
||||
|
|
@ -618,6 +649,59 @@ void repair_worktrees(worktree_repair_fn fn, void *cb_data)
|
|||
free_worktrees(worktrees);
|
||||
}
|
||||
|
||||
void repair_worktree_after_gitdir_move(struct worktree *wt, const char *old_path)
|
||||
{
|
||||
struct strbuf path = STRBUF_INIT;
|
||||
struct strbuf repo = STRBUF_INIT;
|
||||
struct strbuf gitdir = STRBUF_INIT;
|
||||
struct strbuf dotgit = STRBUF_INIT;
|
||||
struct strbuf olddotgit = STRBUF_INIT;
|
||||
struct strbuf tmp = STRBUF_INIT;
|
||||
|
||||
if (is_main_worktree(wt))
|
||||
goto done;
|
||||
|
||||
strbuf_realpath(&repo, git_common_path("worktrees/%s", wt->id), 1);
|
||||
strbuf_addf(&gitdir, "%s/gitdir", repo.buf);
|
||||
|
||||
if (strbuf_read_file(&olddotgit, gitdir.buf, 0) < 0)
|
||||
goto done;
|
||||
|
||||
strbuf_rtrim(&olddotgit);
|
||||
if (is_absolute_path(olddotgit.buf)) {
|
||||
strbuf_addbuf(&dotgit, &olddotgit);
|
||||
} else {
|
||||
strbuf_addf(&dotgit, "%s/worktrees/%s/%s", old_path, wt->id, olddotgit.buf);
|
||||
strbuf_realpath_forgiving(&dotgit, dotgit.buf, 0);
|
||||
}
|
||||
|
||||
if (!file_exists(dotgit.buf))
|
||||
goto done;
|
||||
|
||||
strbuf_addbuf(&path, &dotgit);
|
||||
strbuf_strip_suffix(&path, "/.git");
|
||||
|
||||
write_file(dotgit.buf, "gitdir: %s", relative_path(repo.buf, path.buf, &tmp));
|
||||
write_file(gitdir.buf, "%s", relative_path(dotgit.buf, repo.buf, &tmp));
|
||||
done:
|
||||
strbuf_release(&path);
|
||||
strbuf_release(&repo);
|
||||
strbuf_release(&gitdir);
|
||||
strbuf_release(&dotgit);
|
||||
strbuf_release(&olddotgit);
|
||||
strbuf_release(&tmp);
|
||||
}
|
||||
|
||||
void repair_worktrees_after_gitdir_move(const char *old_path)
|
||||
{
|
||||
struct worktree **worktrees = get_worktrees_internal(1);
|
||||
struct worktree **wt = worktrees + 1; /* +1 skips main worktree */
|
||||
|
||||
for (; *wt; wt++)
|
||||
repair_worktree_after_gitdir_move(*wt, old_path);
|
||||
free_worktrees(worktrees);
|
||||
}
|
||||
|
||||
static int is_main_worktree_path(const char *path)
|
||||
{
|
||||
struct strbuf target = STRBUF_INIT;
|
||||
|
|
@ -642,10 +726,9 @@ static int is_main_worktree_path(const char *path)
|
|||
* 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)
|
||||
static int infer_backlink(const char *gitfile, struct strbuf *inferred)
|
||||
{
|
||||
struct strbuf actual = STRBUF_INIT;
|
||||
struct strbuf inferred = STRBUF_INIT;
|
||||
const char *id;
|
||||
|
||||
if (strbuf_read_file(&actual, gitfile, 0) < 0)
|
||||
|
|
@ -658,17 +741,18 @@ static char *infer_backlink(const char *gitfile)
|
|||
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))
|
||||
strbuf_reset(inferred);
|
||||
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);
|
||||
return 1;
|
||||
|
||||
error:
|
||||
strbuf_release(&actual);
|
||||
strbuf_release(&inferred);
|
||||
return NULL;
|
||||
strbuf_reset(inferred); /* clear invalid path */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -680,9 +764,13 @@ void repair_worktree_at_path(const char *path,
|
|||
{
|
||||
struct strbuf dotgit = STRBUF_INIT;
|
||||
struct strbuf realdotgit = STRBUF_INIT;
|
||||
struct strbuf backlink = STRBUF_INIT;
|
||||
struct strbuf inferred_backlink = STRBUF_INIT;
|
||||
struct strbuf gitdir = STRBUF_INIT;
|
||||
struct strbuf olddotgit = STRBUF_INIT;
|
||||
char *backlink = NULL;
|
||||
struct strbuf realolddotgit = STRBUF_INIT;
|
||||
struct strbuf tmp = STRBUF_INIT;
|
||||
char *dotgit_contents = NULL;
|
||||
const char *repair = NULL;
|
||||
int err;
|
||||
|
||||
|
|
@ -698,107 +786,178 @@ void repair_worktree_at_path(const char *path,
|
|||
goto done;
|
||||
}
|
||||
|
||||
backlink = xstrdup_or_null(read_gitfile_gently(realdotgit.buf, &err));
|
||||
if (err == READ_GITFILE_ERR_NOT_A_FILE) {
|
||||
infer_backlink(realdotgit.buf, &inferred_backlink);
|
||||
strbuf_realpath_forgiving(&inferred_backlink, inferred_backlink.buf, 0);
|
||||
dotgit_contents = xstrdup_or_null(read_gitfile_gently(realdotgit.buf, &err));
|
||||
if (dotgit_contents) {
|
||||
if (is_absolute_path(dotgit_contents)) {
|
||||
strbuf_addstr(&backlink, dotgit_contents);
|
||||
} else {
|
||||
strbuf_addbuf(&backlink, &realdotgit);
|
||||
strbuf_strip_suffix(&backlink, ".git");
|
||||
strbuf_addstr(&backlink, dotgit_contents);
|
||||
strbuf_realpath_forgiving(&backlink, backlink.buf, 0);
|
||||
}
|
||||
} else 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.len) {
|
||||
/*
|
||||
* 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>.
|
||||
*/
|
||||
strbuf_swap(&backlink, &inferred_backlink);
|
||||
} else {
|
||||
fn(1, realdotgit.buf, _("unable to locate repository; .git file does not reference a repository"), cb_data);
|
||||
goto done;
|
||||
}
|
||||
} else if (err) {
|
||||
} else {
|
||||
fn(1, realdotgit.buf, _("unable to locate repository; .git file broken"), cb_data);
|
||||
goto done;
|
||||
}
|
||||
|
||||
strbuf_addf(&gitdir, "%s/gitdir", backlink);
|
||||
/*
|
||||
* 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.len && fspathcmp(backlink.buf, inferred_backlink.buf)) {
|
||||
strbuf_swap(&backlink, &inferred_backlink);
|
||||
}
|
||||
|
||||
strbuf_addf(&gitdir, "%s/gitdir", backlink.buf);
|
||||
if (strbuf_read_file(&olddotgit, gitdir.buf, 0) < 0)
|
||||
repair = _("gitdir unreadable");
|
||||
else {
|
||||
strbuf_rtrim(&olddotgit);
|
||||
if (fspathcmp(olddotgit.buf, realdotgit.buf))
|
||||
if (is_absolute_path(olddotgit.buf)) {
|
||||
strbuf_addbuf(&realolddotgit, &olddotgit);
|
||||
} else {
|
||||
strbuf_addf(&realolddotgit, "%s/%s", backlink.buf, olddotgit.buf);
|
||||
strbuf_realpath_forgiving(&realolddotgit, realolddotgit.buf, 0);
|
||||
}
|
||||
if (fspathcmp(realolddotgit.buf, realdotgit.buf))
|
||||
repair = _("gitdir incorrect");
|
||||
}
|
||||
|
||||
if (repair) {
|
||||
fn(0, gitdir.buf, repair, cb_data);
|
||||
write_file(gitdir.buf, "%s", realdotgit.buf);
|
||||
write_file(gitdir.buf, "%s", relative_path(realdotgit.buf, backlink.buf, &tmp));
|
||||
}
|
||||
done:
|
||||
free(backlink);
|
||||
free(dotgit_contents);
|
||||
strbuf_release(&olddotgit);
|
||||
strbuf_release(&realolddotgit);
|
||||
strbuf_release(&backlink);
|
||||
strbuf_release(&inferred_backlink);
|
||||
strbuf_release(&gitdir);
|
||||
strbuf_release(&realdotgit);
|
||||
strbuf_release(&dotgit);
|
||||
strbuf_release(&tmp);
|
||||
}
|
||||
|
||||
int should_prune_worktree(const char *id, struct strbuf *reason, char **wtpath, timestamp_t expire)
|
||||
{
|
||||
struct stat st;
|
||||
char *path;
|
||||
struct strbuf dotgit = STRBUF_INIT;
|
||||
struct strbuf gitdir = STRBUF_INIT;
|
||||
struct strbuf repo = STRBUF_INIT;
|
||||
struct strbuf file = STRBUF_INIT;
|
||||
char *path = NULL;
|
||||
int rc = 0;
|
||||
int fd;
|
||||
size_t len;
|
||||
ssize_t read_result;
|
||||
|
||||
*wtpath = NULL;
|
||||
if (!is_directory(git_path("worktrees/%s", id))) {
|
||||
strbuf_realpath(&repo, git_common_path("worktrees/%s", id), 1);
|
||||
strbuf_addf(&gitdir, "%s/gitdir", repo.buf);
|
||||
if (!is_directory(repo.buf)) {
|
||||
strbuf_addstr(reason, _("not a valid directory"));
|
||||
return 1;
|
||||
rc = 1;
|
||||
goto done;
|
||||
}
|
||||
if (file_exists(git_path("worktrees/%s/locked", id)))
|
||||
return 0;
|
||||
if (stat(git_path("worktrees/%s/gitdir", id), &st)) {
|
||||
strbuf_addf(&file, "%s/locked", repo.buf);
|
||||
if (file_exists(file.buf)) {
|
||||
goto done;
|
||||
}
|
||||
if (stat(gitdir.buf, &st)) {
|
||||
strbuf_addstr(reason, _("gitdir file does not exist"));
|
||||
return 1;
|
||||
rc = 1;
|
||||
goto done;
|
||||
}
|
||||
fd = open(git_path("worktrees/%s/gitdir", id), O_RDONLY);
|
||||
fd = open(gitdir.buf, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
strbuf_addf(reason, _("unable to read gitdir file (%s)"),
|
||||
strerror(errno));
|
||||
return 1;
|
||||
rc = 1;
|
||||
goto done;
|
||||
}
|
||||
len = xsize_t(st.st_size);
|
||||
path = xmallocz(len);
|
||||
|
||||
read_result = read_in_full(fd, path, len);
|
||||
close(fd);
|
||||
if (read_result < 0) {
|
||||
strbuf_addf(reason, _("unable to read gitdir file (%s)"),
|
||||
strerror(errno));
|
||||
close(fd);
|
||||
free(path);
|
||||
return 1;
|
||||
}
|
||||
close(fd);
|
||||
|
||||
if (read_result != len) {
|
||||
rc = 1;
|
||||
goto done;
|
||||
} else if (read_result != len) {
|
||||
strbuf_addf(reason,
|
||||
_("short read (expected %"PRIuMAX" bytes, read %"PRIuMAX")"),
|
||||
(uintmax_t)len, (uintmax_t)read_result);
|
||||
free(path);
|
||||
return 1;
|
||||
rc = 1;
|
||||
goto done;
|
||||
}
|
||||
while (len && (path[len - 1] == '\n' || path[len - 1] == '\r'))
|
||||
len--;
|
||||
if (!len) {
|
||||
strbuf_addstr(reason, _("invalid gitdir file"));
|
||||
free(path);
|
||||
return 1;
|
||||
rc = 1;
|
||||
goto done;
|
||||
}
|
||||
path[len] = '\0';
|
||||
if (!file_exists(path)) {
|
||||
if (stat(git_path("worktrees/%s/index", id), &st) ||
|
||||
st.st_mtime <= expire) {
|
||||
if (is_absolute_path(path)) {
|
||||
strbuf_addstr(&dotgit, path);
|
||||
} else {
|
||||
strbuf_addf(&dotgit, "%s/%s", repo.buf, path);
|
||||
strbuf_realpath_forgiving(&dotgit, dotgit.buf, 0);
|
||||
}
|
||||
if (!file_exists(dotgit.buf)) {
|
||||
strbuf_reset(&file);
|
||||
strbuf_addf(&file, "%s/index", repo.buf);
|
||||
if (stat(file.buf, &st) || st.st_mtime <= expire) {
|
||||
strbuf_addstr(reason, _("gitdir file points to non-existent location"));
|
||||
free(path);
|
||||
return 1;
|
||||
} else {
|
||||
*wtpath = path;
|
||||
return 0;
|
||||
rc = 1;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
*wtpath = path;
|
||||
return 0;
|
||||
*wtpath = strbuf_detach(&dotgit, NULL);
|
||||
done:
|
||||
free(path);
|
||||
strbuf_release(&dotgit);
|
||||
strbuf_release(&gitdir);
|
||||
strbuf_release(&repo);
|
||||
strbuf_release(&file);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int move_config_setting(const char *key, const char *value,
|
||||
|
|
|
|||
10
worktree.h
10
worktree.h
|
|
@ -131,6 +131,16 @@ typedef void (* worktree_repair_fn)(int iserr, const char *path,
|
|||
*/
|
||||
void repair_worktrees(worktree_repair_fn, void *cb_data);
|
||||
|
||||
/*
|
||||
* Repair the linked worktrees after the gitdir has been moved.
|
||||
*/
|
||||
void repair_worktrees_after_gitdir_move(const char *old_path);
|
||||
|
||||
/*
|
||||
* Repair the linked worktree after the gitdir has been moved.
|
||||
*/
|
||||
void repair_worktree_after_gitdir_move(struct worktree *wt, const char *old_path);
|
||||
|
||||
/*
|
||||
* Repair administrative files corresponding to the worktree at the given path.
|
||||
* The worktree's .git file pointing at the repository must be intact for the
|
||||
|
|
|
|||
Loading…
Reference in New Issue