Skip to content

Commit 9b19a58

Browse files
raffsgitster
authored andcommitted
worktree: teach list to annotate prunable worktree
The "git worktree list" command shows the absolute path to the worktree, the commit that is checked out, the name of the branch, and a "locked" annotation if the worktree is locked, however, it does not indicate whether the worktree is prunable. The "prune" command will remove a worktree if it is prunable unless `--dry-run` option is specified. This could lead to a worktree being removed without the user realizing before it is too late, in case the user forgets to pass --dry-run for instance. If the "list" command shows which worktree is prunable, the user could verify before running "git worktree prune" and hopefully prevents the working tree to be removed "accidentally" on the worse case scenario. Let's teach "git worktree list" to show when a worktree is a prunable candidate for both default and porcelain format. In the default format a "prunable" text is appended: $ git worktree list /path/to/main aba123 [main] /path/to/linked 123abc [branch-a] /path/to/prunable ace127 (detached HEAD) prunable In the --porcelain format a prunable label is added followed by its reason: $ git worktree list --porcelain ... worktree /path/to/prunable HEAD abc1234abc1234abc1234abc1234abc1234abc12 detached prunable gitdir file points to non-existent location ... Helped-by: Eric Sunshine <[email protected]> Signed-off-by: Rafael Silva <[email protected]> Reviewed-by: Eric Sunshine <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 862c723 commit 9b19a58

File tree

3 files changed

+66
-2
lines changed

3 files changed

+66
-2
lines changed

Documentation/git-worktree.txt

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,9 @@ list::
9797
List details of each working tree. The main working tree is listed first,
9898
followed by each of the linked working trees. The output details include
9999
whether the working tree is bare, the revision currently checked out, the
100-
branch currently checked out (or "detached HEAD" if none), and "locked" if
101-
the worktree is locked.
100+
branch currently checked out (or "detached HEAD" if none), "locked" if
101+
the worktree is locked, "prunable" if the worktree can be pruned by `prune`
102+
command.
102103

103104
lock::
104105

@@ -234,6 +235,9 @@ This can also be set up as the default behaviour by using the
234235

235236
--expire <time>::
236237
With `prune`, only expire unused working trees older than `<time>`.
238+
+
239+
With `list`, annotate missing working trees as prunable if they are
240+
older than `<time>`.
237241

238242
--reason <string>::
239243
With `lock`, an explanation why the working tree is locked.
@@ -372,6 +376,19 @@ $ git worktree list
372376
/path/to/other-linked-worktree 1234abc (detached HEAD)
373377
------------
374378

379+
The command also shows annotations for each working tree, according to its state.
380+
These annotations are:
381+
382+
* `locked`, if the working tree is locked.
383+
* `prunable`, if the working tree can be pruned via `git worktree prune`.
384+
385+
------------
386+
$ git worktree list
387+
/path/to/linked-worktree abcd1234 [master]
388+
/path/to/locked-worktreee acbd5678 (brancha) locked
389+
/path/to/prunable-worktree 5678abc (detached HEAD) prunable
390+
------------
391+
375392
Porcelain Format
376393
~~~~~~~~~~~~~~~~
377394
The porcelain format has a line per attribute. Attributes are listed with a
@@ -405,6 +422,11 @@ HEAD 3456def3456def3456def3456def3456def3456b
405422
branch refs/heads/locked-with-reason
406423
locked reason why is locked
407424

425+
worktree /path/to/linked-worktree-prunable
426+
HEAD 1233def1234def1234def1234def1234def1234b
427+
detached
428+
prunable gitdir file points to non-existent location
429+
408430
------------
409431

410432
If the lock reason contains "unusual" characters such as newline, they

builtin/worktree.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -592,6 +592,10 @@ static void show_worktree_porcelain(struct worktree *wt)
592592
} else if (reason)
593593
printf("locked\n");
594594

595+
reason = worktree_prune_reason(wt, expire);
596+
if (reason)
597+
printf("prunable %s\n", reason);
598+
595599
printf("\n");
596600
}
597601

@@ -620,6 +624,9 @@ static void show_worktree(struct worktree *wt, int path_maxlen, int abbrev_len)
620624
if (worktree_lock_reason(wt))
621625
strbuf_addstr(&sb, " locked");
622626

627+
if (worktree_prune_reason(wt, expire))
628+
strbuf_addstr(&sb, " prunable");
629+
623630
printf("%s\n", sb.buf);
624631
strbuf_release(&sb);
625632
}
@@ -663,9 +670,12 @@ static int list(int ac, const char **av, const char *prefix)
663670

664671
struct option options[] = {
665672
OPT_BOOL(0, "porcelain", &porcelain, N_("machine-readable output")),
673+
OPT_EXPIRY_DATE(0, "expire", &expire,
674+
N_("add 'prunable' annotation to worktrees older than <time>")),
666675
OPT_END()
667676
};
668677

678+
expire = TIME_MAX;
669679
ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
670680
if (ac)
671681
usage_with_options(worktree_usage, options);

t/t2402-worktree-list.sh

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,38 @@ test_expect_success '"list" all worktrees --porcelain with locked reason newline
104104
test_cmp expect actual
105105
'
106106

107+
test_expect_success '"list" all worktrees with prunable annotation' '
108+
test_when_finished "rm -rf prunable unprunable out && git worktree prune" &&
109+
git worktree add --detach prunable &&
110+
git worktree add --detach unprunable &&
111+
rm -rf prunable &&
112+
git worktree list >out &&
113+
grep "/prunable *[0-9a-f].* prunable$" out &&
114+
! grep "/unprunable *[0-9a-f].* prunable$"
115+
'
116+
117+
test_expect_success '"list" all worktrees --porcelain with prunable' '
118+
test_when_finished "rm -rf prunable out && git worktree prune" &&
119+
git worktree add --detach prunable &&
120+
rm -rf prunable &&
121+
git worktree list --porcelain >out &&
122+
sed -n "/^worktree .*\/prunable$/,/^$/p" <out >only_prunable &&
123+
test_i18ngrep "^prunable gitdir file points to non-existent location$" only_prunable
124+
'
125+
126+
test_expect_success '"list" all worktrees with prunable consistent with "prune"' '
127+
test_when_finished "rm -rf prunable unprunable out && git worktree prune" &&
128+
git worktree add --detach prunable &&
129+
git worktree add --detach unprunable &&
130+
rm -rf prunable &&
131+
git worktree list >out &&
132+
grep "/prunable *[0-9a-f].* prunable$" out &&
133+
! grep "/unprunable *[0-9a-f].* unprunable$" out &&
134+
git worktree prune --verbose >out &&
135+
test_i18ngrep "^Removing worktrees/prunable" out &&
136+
test_i18ngrep ! "^Removing worktrees/unprunable" out
137+
'
138+
107139
test_expect_success 'bare repo setup' '
108140
git init --bare bare1 &&
109141
echo "data" >file1 &&

0 commit comments

Comments
 (0)