diff mbox series

[v2,2/6] worktree: teach worktree to lazy-load "prunable" reason

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

Commit Message

Rafael Silva Jan. 17, 2021, 11:42 p.m. UTC
Add worktree_prune_reason() to allow a caller to discover whether a
worktree is prunable and the reason that it is, much like
worktree_lock_reason() indicates whether a worktree is locked and the
reason for the lock. As with worktree_lock_reason(), retrieve the
prunable reason lazily and cache it in the `worktree` structure.

Helped-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Rafael Silva <rafaeloliveira.cs@gmail.com>
---
 worktree.c | 20 ++++++++++++++++++++
 worktree.h |  9 +++++++++
 2 files changed, 29 insertions(+)

Comments

Eric Sunshine Jan. 18, 2021, 2:57 a.m. UTC | #1
On Sun, Jan 17, 2021 at 6:43 PM Rafael Silva
<rafaeloliveira.cs@gmail.com> wrote:
> Add worktree_prune_reason() to allow a caller to discover whether a
> worktree is prunable and the reason that it is, much like
> worktree_lock_reason() indicates whether a worktree is locked and the
> reason for the lock. As with worktree_lock_reason(), retrieve the
> prunable reason lazily and cache it in the `worktree` structure.
>
> Signed-off-by: Rafael Silva <rafaeloliveira.cs@gmail.com>
> ---
> diff --git a/worktree.c b/worktree.c
> @@ -245,6 +246,25 @@ const char *worktree_lock_reason(struct worktree *wt)
> +const char *worktree_prune_reason(struct worktree *wt, timestamp_t expire)
> +{
> +       struct strbuf reason = STRBUF_INIT;
> +       char *path;

The `path` variable is uninitialized...

> +       if (should_prune_worktree(wt->id, &reason, &path, expire))
> +               wt->prune_reason = strbuf_detach(&reason, NULL);

...but the very first thing should_prune_worktree() does is
unconditionally set it to NULL, so it's either NULL or an allocated
string regardless of the value returned by should_prune_worktree()...

> +       strbuf_release(&reason);
> +       free(path);

...which makes the behavior of `free(path)` deterministic. Good.

I'm on the fence about whether or not we should initialize `path` to NULL:

    char *path = NULL;

just to make it easier for the next person to reason about it without
having to dig into the implementation of should_prune_worktree(). This
is a really minor point, though, not worth a re-roll.
Rafael Silva Jan. 19, 2021, 7:57 a.m. UTC | #2
Eric Sunshine writes:

> On Sun, Jan 17, 2021 at 6:43 PM Rafael Silva
> <rafaeloliveira.cs@gmail.com> wrote:
>> Add worktree_prune_reason() to allow a caller to discover whether a
>> worktree is prunable and the reason that it is, much like
>> worktree_lock_reason() indicates whether a worktree is locked and the
>> reason for the lock. As with worktree_lock_reason(), retrieve the
>> prunable reason lazily and cache it in the `worktree` structure.
>>
>> Signed-off-by: Rafael Silva <rafaeloliveira.cs@gmail.com>
>> ---
>> diff --git a/worktree.c b/worktree.c
>> @@ -245,6 +246,25 @@ const char *worktree_lock_reason(struct worktree *wt)
>> +const char *worktree_prune_reason(struct worktree *wt, timestamp_t expire)
>> +{
>> +       struct strbuf reason = STRBUF_INIT;
>> +       char *path;
>
> The `path` variable is uninitialized...
>
>> +       if (should_prune_worktree(wt->id, &reason, &path, expire))
>> +               wt->prune_reason = strbuf_detach(&reason, NULL);
>
> ...but the very first thing should_prune_worktree() does is
> unconditionally set it to NULL, so it's either NULL or an allocated
> string regardless of the value returned by should_prune_worktree()...
>
>> +       strbuf_release(&reason);
>> +       free(path);
>
> ...which makes the behavior of `free(path)` deterministic. Good.
>
> I'm on the fence about whether or not we should initialize `path` to NULL:
>
>     char *path = NULL;
>
> just to make it easier for the next person to reason about it without
> having to dig into the implementation of should_prune_worktree(). This
> is a really minor point, though, not worth a re-roll.

This is a good point and totally agreed. I'm going to include this
change in the next revision.
diff mbox series

Patch

diff --git a/worktree.c b/worktree.c
index 8ae019af79..474ed46562 100644
--- a/worktree.c
+++ b/worktree.c
@@ -15,6 +15,7 @@  void free_worktrees(struct worktree **worktrees)
 		free(worktrees[i]->id);
 		free(worktrees[i]->head_ref);
 		free(worktrees[i]->lock_reason);
+		free(worktrees[i]->prune_reason);
 		free(worktrees[i]);
 	}
 	free (worktrees);
@@ -245,6 +246,25 @@  const char *worktree_lock_reason(struct worktree *wt)
 	return wt->lock_reason;
 }
 
+const char *worktree_prune_reason(struct worktree *wt, timestamp_t expire)
+{
+	struct strbuf reason = STRBUF_INIT;
+	char *path;
+
+	if (is_main_worktree(wt))
+		return NULL;
+	if (wt->prune_reason_valid)
+		return wt->prune_reason;
+
+	if (should_prune_worktree(wt->id, &reason, &path, expire))
+		wt->prune_reason = strbuf_detach(&reason, NULL);
+	wt->prune_reason_valid = 1;
+
+	strbuf_release(&reason);
+	free(path);
+	return wt->prune_reason;
+}
+
 /* convenient wrapper to deal with NULL strbuf */
 static void strbuf_addf_gently(struct strbuf *buf, const char *fmt, ...)
 {
diff --git a/worktree.h b/worktree.h
index 818e1491c7..8b7c408132 100644
--- a/worktree.h
+++ b/worktree.h
@@ -11,11 +11,13 @@  struct worktree {
 	char *id;
 	char *head_ref;		/* NULL if HEAD is broken or detached */
 	char *lock_reason;	/* private - use worktree_lock_reason */
+	char *prune_reason;     /* private - use worktree_prune_reason */
 	struct object_id head_oid;
 	int is_detached;
 	int is_bare;
 	int is_current;
 	int lock_reason_valid; /* private */
+	int prune_reason_valid; /* private */
 };
 
 /*
@@ -73,6 +75,13 @@  int is_main_worktree(const struct worktree *wt);
  */
 const char *worktree_lock_reason(struct worktree *wt);
 
+/*
+ * Return the reason string if the given worktree should be pruned, otherwise
+ * NULL if it should not be pruned. `expire` defines a grace period to prune
+ * the worktree when its path does not exist.
+ */
+const char *worktree_prune_reason(struct worktree *wt, timestamp_t expire);
+
 /*
  * Return true if worktree entry should be pruned, along with the reason for
  * pruning. Otherwise, return false and the worktree's path in `wtpath`, or