Skip to content

Commit 076b444

Browse files
raffsgitster
authored andcommitted
worktree: teach list verbose mode
"git worktree list" annotates each worktree according to its state such as "prunable" or "locked", however it is not immediately obvious why these worktrees are being annotated. For prunable worktrees a reason is available that is returned by should_prune_worktree() and for locked worktrees a reason might be available provided by the user via `lock` command. Let's teach "git worktree list" a --verbose mode that outputs the reason why the worktrees are being annotated. The reason is a text that can take virtually any size and appending the text on the default columned format will make it difficult to extend the command with other annotations and not fit nicely on the screen. In order to address this shortcoming the annotation is then moved to the next line indented followed by the reason If the reason is not available the annotation stays on the same line as the worktree itself. The output of "git worktree list" with verbose becomes like so: $ git worktree list --verbose ... /path/to/locked-no-reason acb124 [branch-a] locked /path/to/locked-with-reason acc125 [branch-b] locked: worktree with a locked reason /path/to/prunable-reason ace127 [branch-d] 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 9b19a58 commit 076b444

File tree

3 files changed

+63
-2
lines changed

3 files changed

+63
-2
lines changed

Documentation/git-worktree.txt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,8 @@ This can also be set up as the default behaviour by using the
232232
-v::
233233
--verbose::
234234
With `prune`, report all removals.
235+
+
236+
With `list`, output additional information about worktrees (see below).
235237

236238
--expire <time>::
237239
With `prune`, only expire unused working trees older than `<time>`.
@@ -389,6 +391,24 @@ $ git worktree list
389391
/path/to/prunable-worktree 5678abc (detached HEAD) prunable
390392
------------
391393

394+
For these annotations, a reason might also be available and this can be
395+
seen using the verbose mode. The annotation is then moved to the next line
396+
indented followed by the additional information.
397+
398+
------------
399+
$ git worktree list --verbose
400+
/path/to/linked-worktree abcd1234 [master]
401+
/path/to/locked-worktree-no-reason abcd5678 (detached HEAD) locked
402+
/path/to/locked-worktree-with-reason 1234abcd (brancha)
403+
locked: working tree path is mounted on a portable device
404+
/path/to/prunable-worktree 5678abc1 (detached HEAD)
405+
prunable: gitdir file points to non-existent location
406+
------------
407+
408+
Note that the annotation is moved to the next line if the additional
409+
information is available, otherwise it stays on the same line as the
410+
working tree itself.
411+
392412
Porcelain Format
393413
~~~~~~~~~~~~~~~~
394414
The porcelain format has a line per attribute. Attributes are listed with a

builtin/worktree.c

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,7 @@ static void show_worktree(struct worktree *wt, int path_maxlen, int abbrev_len)
604604
struct strbuf sb = STRBUF_INIT;
605605
int cur_path_len = strlen(wt->path);
606606
int path_adj = cur_path_len - utf8_strwidth(wt->path);
607+
const char *reason;
607608

608609
strbuf_addf(&sb, "%-*s ", 1 + path_maxlen + path_adj, wt->path);
609610
if (wt->is_bare)
@@ -621,10 +622,16 @@ static void show_worktree(struct worktree *wt, int path_maxlen, int abbrev_len)
621622
strbuf_addstr(&sb, "(error)");
622623
}
623624

624-
if (worktree_lock_reason(wt))
625+
reason = worktree_lock_reason(wt);
626+
if (verbose && reason && *reason)
627+
strbuf_addf(&sb, "\n\tlocked: %s", reason);
628+
else if (reason)
625629
strbuf_addstr(&sb, " locked");
626630

627-
if (worktree_prune_reason(wt, expire))
631+
reason = worktree_prune_reason(wt, expire);
632+
if (verbose && reason)
633+
strbuf_addf(&sb, "\n\tprunable: %s", reason);
634+
else if (reason)
628635
strbuf_addstr(&sb, " prunable");
629636

630637
printf("%s\n", sb.buf);
@@ -670,6 +677,7 @@ static int list(int ac, const char **av, const char *prefix)
670677

671678
struct option options[] = {
672679
OPT_BOOL(0, "porcelain", &porcelain, N_("machine-readable output")),
680+
OPT__VERBOSE(&verbose, N_("show extended annotations and reasons, if available")),
673681
OPT_EXPIRY_DATE(0, "expire", &expire,
674682
N_("add 'prunable' annotation to worktrees older than <time>")),
675683
OPT_END()
@@ -679,6 +687,8 @@ static int list(int ac, const char **av, const char *prefix)
679687
ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
680688
if (ac)
681689
usage_with_options(worktree_usage, options);
690+
else if (verbose && porcelain)
691+
die(_("--verbose and --porcelain are mutually exclusive"));
682692
else {
683693
struct worktree **worktrees = get_worktrees();
684694
int path_maxlen = 0, abbrev = DEFAULT_ABBREV, i;

t/t2402-worktree-list.sh

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,37 @@ test_expect_success '"list" all worktrees with prunable consistent with "prune"'
136136
test_i18ngrep ! "^Removing worktrees/unprunable" out
137137
'
138138

139+
test_expect_success '"list" --verbose and --porcelain mutually exclusive' '
140+
test_must_fail git worktree list --verbose --porcelain
141+
'
142+
143+
test_expect_success '"list" all worktrees --verbose with locked' '
144+
test_when_finished "rm -rf locked1 locked2 out actual expect && git worktree prune" &&
145+
git worktree add locked1 --detach &&
146+
git worktree add locked2 --detach &&
147+
git worktree lock locked1 &&
148+
test_when_finished "git worktree unlock locked1" &&
149+
git worktree lock locked2 --reason "with reason" &&
150+
test_when_finished "git worktree unlock locked2" &&
151+
echo "$(git -C locked2 rev-parse --show-toplevel) $(git rev-parse --short HEAD) (detached HEAD)" >expect &&
152+
printf "\tlocked: with reason\n" >>expect &&
153+
git worktree list --verbose >out &&
154+
grep "/locked1 *[0-9a-f].* locked$" out &&
155+
sed -n "s/ */ /g;/\/locked2 *[0-9a-f].*$/,/locked: .*$/p" <out >actual &&
156+
test_cmp actual expect
157+
'
158+
159+
test_expect_success '"list" all worktrees --verbose with prunable' '
160+
test_when_finished "rm -rf prunable out actual expect && git worktree prune" &&
161+
git worktree add prunable --detach &&
162+
echo "$(git -C prunable rev-parse --show-toplevel) $(git rev-parse --short HEAD) (detached HEAD)" >expect &&
163+
printf "\tprunable: gitdir file points to non-existent location\n" >>expect &&
164+
rm -rf prunable &&
165+
git worktree list --verbose >out &&
166+
sed -n "s/ */ /g;/\/prunable *[0-9a-f].*$/,/prunable: .*$/p" <out >actual &&
167+
test_i18ncmp actual expect
168+
'
169+
139170
test_expect_success 'bare repo setup' '
140171
git init --bare bare1 &&
141172
echo "data" >file1 &&

0 commit comments

Comments
 (0)