diff mbox series

[v3,6/7] worktree: teach `list` to annotate prunable worktree

Message ID 20210119212739.77882-7-rafaeloliveira.cs@gmail.com (mailing list archive)
State New, archived
Headers show
Series teach `worktree list` verbose mode and prunable annotations | expand

Commit Message

Rafael Silva Jan. 19, 2021, 9:27 p.m. UTC
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 <sunshine@sunshineco.com>
Signed-off-by: Rafael Silva <rafaeloliveira.cs@gmail.com>
---
 Documentation/git-worktree.txt | 26 ++++++++++++++++++++++++--
 builtin/worktree.c             | 10 ++++++++++
 builtin/worktree.cc            |  0
 t/t2402-worktree-list.sh       | 32 ++++++++++++++++++++++++++++++++
 4 files changed, 66 insertions(+), 2 deletions(-)
 create mode 100644 builtin/worktree.cc

Comments

Junio C Hamano Jan. 21, 2021, 3:28 a.m. UTC | #1
Rafael Silva <rafaeloliveira.cs@gmail.com> writes:

>  Documentation/git-worktree.txt | 26 ++++++++++++++++++++++++--
>  builtin/worktree.c             | 10 ++++++++++
>  builtin/worktree.cc            |  0
>  t/t2402-worktree-list.sh       | 32 ++++++++++++++++++++++++++++++++
>  4 files changed, 66 insertions(+), 2 deletions(-)
>  create mode 100644 builtin/worktree.cc

What's the h*ck is that .cc file doing ;-)
Rafael Silva Jan. 21, 2021, 3:09 p.m. UTC | #2
Junio C Hamano writes:

> Rafael Silva <rafaeloliveira.cs@gmail.com> writes:
>
>>  Documentation/git-worktree.txt | 26 ++++++++++++++++++++++++--
>>  builtin/worktree.c             | 10 ++++++++++
>>  builtin/worktree.cc            |  0
>>  t/t2402-worktree-list.sh       | 32 ++++++++++++++++++++++++++++++++
>>  4 files changed, 66 insertions(+), 2 deletions(-)
>>  create mode 100644 builtin/worktree.cc
>
> What's the h*ck is that .cc file doing ;-)

Oops. I accidentally created and committed this file.

Re-rolling ...

Side note (joke): I wasn't trying to add C++ into Git codebase :)
Junio C Hamano Jan. 21, 2021, 10:18 p.m. UTC | #3
Rafael Silva <rafaeloliveira.cs@gmail.com> writes:

> Junio C Hamano writes:
>
>> Rafael Silva <rafaeloliveira.cs@gmail.com> writes:
>>
>>>  Documentation/git-worktree.txt | 26 ++++++++++++++++++++++++--
>>>  builtin/worktree.c             | 10 ++++++++++
>>>  builtin/worktree.cc            |  0
>>>  t/t2402-worktree-list.sh       | 32 ++++++++++++++++++++++++++++++++
>>>  4 files changed, 66 insertions(+), 2 deletions(-)
>>>  create mode 100644 builtin/worktree.cc
>>
>> What's the h*ck is that .cc file doing ;-)
>
> Oops. I accidentally created and committed this file.
>
> Re-rolling ...
>
> Side note (joke): I wasn't trying to add C++ into Git codebase :)

Yeah, I know ;-)  I've already removed the empty file in my copy,
but it seems you are getting other useful input, so it probably is a
good idea to remove it on your end, too.

Thanks.
diff mbox series

Patch

diff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt
index 7cb8124f28..e0dd291e30 100644
--- a/Documentation/git-worktree.txt
+++ b/Documentation/git-worktree.txt
@@ -97,8 +97,9 @@  list::
 List details of each working tree.  The main working tree is listed first,
 followed by each of the linked working trees.  The output details include
 whether the working tree is bare, the revision currently checked out, the
-branch currently checked out (or "detached HEAD" if none), and "locked" if
-the worktree is locked.
+branch currently checked out (or "detached HEAD" if none), "locked" if
+the worktree is locked, "prunable" if the worktree can be pruned by `prune`
+command.
 
 lock::
 
@@ -234,6 +235,9 @@  This can also be set up as the default behaviour by using the
 
 --expire <time>::
 	With `prune`, only expire unused working trees older than `<time>`.
++
+With `list`, annotate missing working trees as prunable if they are
+older than `<time>`.
 
 --reason <string>::
 	With `lock`, an explanation why the working tree is locked.
@@ -372,6 +376,19 @@  $ git worktree list
 /path/to/other-linked-worktree  1234abc  (detached HEAD)
 ------------
 
+The command also shows annotations for each working tree, according to its state.
+These annotations are:
+
+ * `locked`, if the working tree is locked.
+ * `prunable`, if the working tree can be pruned via `git worktree prune`.
+
+------------
+$ git worktree list
+/path/to/linked-worktree    abcd1234 [master]
+/path/to/locked-worktreee   acbd5678 (brancha) locked
+/path/to/prunable-worktree  5678abc  (detached HEAD) prunable
+------------
+
 Porcelain Format
 ~~~~~~~~~~~~~~~~
 The porcelain format has a line per attribute.  Attributes are listed with a
@@ -405,6 +422,11 @@  HEAD 3456def3456def3456def3456def3456def3456b
 branch refs/heads/locked-with-reason
 locked reason why is locked
 
+worktree /path/to/linked-worktree-prunable
+HEAD 1233def1234def1234def1234def1234def1234b
+detached
+prunable gitdir file points to non-existent location
+
 ------------
 
 EXAMPLES
diff --git a/builtin/worktree.c b/builtin/worktree.c
index 98177f91d4..20944c266e 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -592,6 +592,10 @@  static void show_worktree_porcelain(struct worktree *wt)
 	} else if (reason)
 		printf("locked\n");
 
+	reason = worktree_prune_reason(wt, expire);
+	if (reason)
+		printf("prunable %s\n", reason);
+
 	printf("\n");
 }
 
@@ -620,6 +624,9 @@  static void show_worktree(struct worktree *wt, int path_maxlen, int abbrev_len)
 	if (worktree_lock_reason(wt))
 		strbuf_addstr(&sb, " locked");
 
+	if (worktree_prune_reason(wt, expire))
+		strbuf_addstr(&sb, " prunable");
+
 	printf("%s\n", sb.buf);
 	strbuf_release(&sb);
 }
@@ -663,9 +670,12 @@  static int list(int ac, const char **av, const char *prefix)
 
 	struct option options[] = {
 		OPT_BOOL(0, "porcelain", &porcelain, N_("machine-readable output")),
+		OPT_EXPIRY_DATE(0, "expire", &expire,
+				N_("add 'prunable' annotation to worktrees 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);
diff --git a/builtin/worktree.cc b/builtin/worktree.cc
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/t/t2402-worktree-list.sh b/t/t2402-worktree-list.sh
index 1fe53c3309..9e342d6c66 100755
--- a/t/t2402-worktree-list.sh
+++ b/t/t2402-worktree-list.sh
@@ -102,6 +102,38 @@  test_expect_success '"list" all worktrees --porcelain with locked reason newline
 	test_cmp expect actual
 '
 
+test_expect_success '"list" all worktrees with prunable annotation' '
+	test_when_finished "rm -rf prunable unprunable out && git worktree prune" &&
+	git worktree add --detach prunable &&
+	git worktree add --detach unprunable &&
+	rm -rf prunable &&
+	git worktree list >out &&
+	grep "/prunable  *[0-9a-f].* prunable$" out &&
+	! grep "/unprunable  *[0-9a-f].* prunable$"
+'
+
+test_expect_success '"list" all worktrees --porcelain with prunable' '
+	test_when_finished "rm -rf prunable out && git worktree prune" &&
+	git worktree add --detach prunable &&
+	rm -rf prunable &&
+	git worktree list --porcelain >out &&
+	sed -n "/^worktree .*\/prunable$/,/^$/p" <out >only_prunable &&
+	test_i18ngrep "^prunable gitdir file points to non-existent location$" only_prunable
+'
+
+test_expect_success '"list" all worktrees with prunable consistent with "prune"' '
+	test_when_finished "rm -rf prunable unprunable out && git worktree prune" &&
+	git worktree add --detach prunable &&
+	git worktree add --detach unprunable &&
+	rm -rf prunable &&
+	git worktree list >out &&
+	grep "/prunable  *[0-9a-f].* prunable$" out &&
+	! grep "/unprunable  *[0-9a-f].* unprunable$" out &&
+	git worktree prune --verbose >out &&
+	test_i18ngrep "^Removing worktrees/prunable" out &&
+	test_i18ngrep ! "^Removing worktrees/unprunable" out
+'
+
 test_expect_success 'bare repo setup' '
 	git init --bare bare1 &&
 	echo "data" >file1 &&