Skip to content

Commit 916133e

Browse files
sunshinecogitster
authored andcommitted
worktree: prune linked worktree referencing main worktree path
"git worktree prune" detects when multiple entries are associated with the same path and prunes the duplicates, however, it does not detect when a linked worktree points at the path of the main worktree. Although "git worktree add" disallows creating a new worktree with the same path as the main worktree, such a case can arise outside the control of Git even without the user mucking with .git/worktree/<id>/ administrative files. For instance: $ git clone foo.git $ git -C foo worktree add ../bar $ rm -rf bar $ mv foo bar $ git -C bar worktree list .../bar deadfeeb [master] .../bar deadfeeb [bar] Help the user recover from such corruption by extending "git worktree prune" to also detect when a linked worktree is associated with the path of the main worktree. Reported-by: Jonathan Müller <[email protected]> Signed-off-by: Eric Sunshine <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 4a3ce47 commit 916133e

File tree

2 files changed

+27
-0
lines changed

2 files changed

+27
-0
lines changed

builtin/worktree.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,16 @@ static int prune_cmp(const void *a, const void *b)
156156

157157
if ((c = fspathcmp(x->string, y->string)))
158158
return c;
159+
/*
160+
* paths same; prune_dupes() removes all but the first worktree entry
161+
* having the same path, so sort main worktree ('util' is NULL) above
162+
* linked worktrees ('util' not NULL) since main worktree can't be
163+
* removed
164+
*/
165+
if (!x->util)
166+
return -1;
167+
if (!y->util)
168+
return 1;
159169
/* paths same; sort by .git/worktrees/<id> */
160170
return strcmp(x->util, y->util);
161171
}
@@ -174,6 +184,7 @@ static void prune_dups(struct string_list *l)
174184
static void prune_worktrees(void)
175185
{
176186
struct strbuf reason = STRBUF_INIT;
187+
struct strbuf main_path = STRBUF_INIT;
177188
struct string_list kept = STRING_LIST_INIT_NODUP;
178189
DIR *dir = opendir(git_path("worktrees"));
179190
struct dirent *d;
@@ -191,6 +202,10 @@ static void prune_worktrees(void)
191202
}
192203
closedir(dir);
193204

205+
strbuf_add_absolute_path(&main_path, get_git_common_dir());
206+
/* massage main worktree absolute path to match 'gitdir' content */
207+
strbuf_strip_suffix(&main_path, "/.");
208+
string_list_append(&kept, strbuf_detach(&main_path, NULL));
194209
prune_dups(&kept);
195210
string_list_clear(&kept, 1);
196211

t/t2401-worktree-prune.sh

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,4 +104,16 @@ test_expect_success 'prune duplicate (linked/linked)' '
104104
! test -d .git/worktrees/w2
105105
'
106106

107+
test_expect_success 'prune duplicate (main/linked)' '
108+
test_when_finished rm -fr repo wt &&
109+
test_create_repo repo &&
110+
test_commit -C repo x &&
111+
git -C repo worktree add --detach ../wt &&
112+
rm -fr wt &&
113+
mv repo wt &&
114+
git -C wt worktree prune --verbose >actual &&
115+
test_i18ngrep "duplicate entry" actual &&
116+
! test -d .git/worktrees/wt
117+
'
118+
107119
test_done

0 commit comments

Comments
 (0)