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. It is not clear whether a worktree, if any, is prunable. The "prune" command will remove a worktree in case is a prunable candidate unless --dry-run option is specified. This could lead to a worktree being removed without the user realizing before is to 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 "accidently" on the worse case scenario. Let's teach "git worktree list" to show when a worktree is prunable by appending "prunable" text to its output by default and show a prunable label for the porcelain format followed by the reason, if the avaiable. While at it, let's do the same for the "locked" annotation. Also, the worktree_prune_reason() stores the reason why git is selecting the worktree to be pruned, so let's leverage that and also display this information to the user. However, the reason is human readable text that can take virtually any size which might make harder to extend the "list" command with additional annotations and not fit nicely on the screen. In order to address this shortcoming, let's teach "git worktree list" to take a verbose option that will output the prune reason on the next line indented, if the reason is available, otherwise the annotation is kept on the same line. While at it, let's do the same for the "locked" annotation. The output of "git worktree list" with verbose becomes like so: $ git worktree list --verbose /path/to/main aba123 [main] /path/to/locked acb124 [branch-a] locked /path/to/locked-reason acc125 [branch-b] locked: worktree with locked reason /path/to/prunable acd126 [branch-c] prunable /path/to/prunable-reason ace127 [branch-d] prunable: gitdir file points to non-existent location And the output of the "git worktree list --porcelain" becomes like so: $ git worktree list --porcelain worktree /path/to/main HEAD abc1234abc1234abc1234abc1234abc1234abc12 branch refs/heads/main worktree /path/to/linked HEAD 123abc456def789abc1234def5678abc9123abce branch refs/heads/linked worktree /path/to/locked HEAD 123abcdea123abcd123acbd123acbda123abcd12 detached locked lock reason worktree /path/to/prunable HEAD def1234def1234def1234def1234def1234def1a detached prunable prunable reason Signed-off-by: Rafael Silva <rafaeloliveira.cs@xxxxxxxxx> --- builtin/worktree.c | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/builtin/worktree.c b/builtin/worktree.c index eeb3ffaed0..dedd4089e5 100644 --- a/builtin/worktree.c +++ b/builtin/worktree.c @@ -578,6 +578,20 @@ static void show_worktree_porcelain(struct worktree *wt) printf("detached\n"); else if (wt->head_ref) printf("branch %s\n", wt->head_ref); + + if (worktree_lock_reason(wt)) { + if (*wt->lock_reason) + printf("locked %s\n", wt->lock_reason); + else + printf("locked\n"); + } + + if (worktree_prune_reason(wt, expire)) { + if (*wt->prune_reason) + printf("prunable %s\n", wt->prune_reason); + else + printf("prunable\n"); + } } printf("\n"); } @@ -604,8 +618,19 @@ static void show_worktree(struct worktree *wt, int path_maxlen, int abbrev_len) strbuf_addstr(&sb, "(error)"); } - if (!is_main_worktree(wt) && worktree_lock_reason(wt)) - strbuf_addstr(&sb, " locked"); + if (worktree_lock_reason(wt)) { + if (verbose && *wt->lock_reason) + strbuf_addf(&sb, "\n\tlocked: %s", wt->lock_reason); + else + strbuf_addstr(&sb, " locked"); + } + + if (worktree_prune_reason(wt, expire)) { + if (verbose && *wt->prune_reason) + strbuf_addf(&sb, "\n\tprunable: %s", wt->prune_reason); + else + strbuf_addstr(&sb, " prunable"); + } printf("%s\n", sb.buf); strbuf_release(&sb); @@ -650,12 +675,18 @@ static int list(int ac, const char **av, const char *prefix) struct option options[] = { OPT_BOOL(0, "porcelain", &porcelain, N_("machine-readable output")), + OPT__VERBOSE(&verbose, N_("show extended annotations and reasons, if available")), + OPT_EXPIRY_DATE(0, "expire", &expire, + N_("show working trees that is candidate to be pruned older than <time>")), OPT_END() }; + expire = TIME_MAX; ac = parse_options(ac, av, prefix, options, worktree_usage, 0); if (ac) usage_with_options(worktree_usage, options); + else if (verbose && porcelain) + die(_("--verbose and --porcelain are mutually exclusive")); else { struct worktree **worktrees = get_worktrees(); int path_maxlen = 0, abbrev = DEFAULT_ABBREV, i; -- 2.30.0.391.g469bf2a980