Git development
 help / color / mirror / Atom feed
From: Patrick Steinhardt <ps@pks.im>
To: Thomas Bachem via GitGitGadget <gitgitgadget@gmail.com>
Cc: git@vger.kernel.org, Phillip Wood <phillip.wood@dunelm.org.uk>,
	Junio C Hamano <gitster@pobox.com>,
	Phillip Wood <phillip.wood123@gmail.com>,
	Thomas Bachem <mail@thomasbachem.com>
Subject: Re: [PATCH v6 2/3] rerere: add "gc --skip-locked" for auto maintenance
Date: Fri, 9 Oct 2026 11:06:08 +0200	[thread overview]
Message-ID: <asiugInq7YTj4Qbe@pks.im> (raw)
In-Reply-To: <2ef141410a1508477976f9e57cec05f1a7603264.1790939492.git.gitgitgadget@gmail.com>

On Fri, Oct 02, 2026 at 11:11:31AM +0000, Thomas Bachem via GitGitGadget wrote:
> From: Thomas Bachem <mail@thomasbachem.com>
> 
> Since the previous commit, "git rerere gc" waits for MERGE_RR.lock
> like every other command that takes it, and fails only if the wait
> times out. That suits a user who runs it by hand and wants to know
> when nothing was pruned.

Two nits:

  - We already took the lock before the preceding commit, the only
    difference is that we now have a timeout. Your message sounds as if
    the whole lock were new.

  - The user don't necessarily care that nothing was pruned, but they do
    care that pruning has failed.

> But the user did not ask for the gc that auto
> maintenance starts after a commit, and the next commit starts another
> one.

And this reads quite awkward, too. How about:

  Starting with the preceding commit, processes that want to acquire
  the rerere cache's MERGE_RR.lock by default know to wait up to one
  second until that lock has been released. This is a sensible default
  for many commands that happen to write rerere entries, as we would
  otherwise die immediately when the lock is taken by another process.

  But for repository maintenance it's a bit more complicated, as there
  are two cases that we have to care about. When the user explicitly
  asks us to garbage collect rerere entries via `git rerere gc` they
  probably want us to try our best to perform this operation. It's thus
  sensible to wait for the lock and then die if we weren't able to
  acquire it.

  But we also prune rerere entries as part of auto-maintenance, which is
  only executed on a best-effort basis anyway. Delaying the whole
  operation to acquire the lock is somewhat heavy-handed, and neither
  does it make sense to die in case we haven't been able to garbage
  collect rerere entries as that would impede other housekeeping tasks.
  Furthermore, it's totally fine to skip the operation when the rerere
  cache is locked already, as we will retry during the next run anyway.

  But we do not have an easy way to tell `git rerere gc` to skip the
  operation in case the cache is locked already. Add a new
  "--skip-locked" flag to plug that gap and have auto-maintenance pass
  that flag.

> diff --git a/builtin/gc.c b/builtin/gc.c
> index 57a3520263..7ad3987b71 100644
> --- a/builtin/gc.c
> +++ b/builtin/gc.c
> @@ -385,12 +385,14 @@ out:
>  	return should_prune;
>  }
>  
> -static int maintenance_task_rerere_gc(struct maintenance_run_opts *opts UNUSED,
> +static int maintenance_task_rerere_gc(struct maintenance_run_opts *opts,
>  				      struct gc_config *cfg UNUSED)
>  {
>  	struct child_process rerere_cmd = CHILD_PROCESS_INIT;
>  	rerere_cmd.git_cmd = 1;
>  	strvec_pushl(&rerere_cmd.args, "rerere", "gc", NULL);
> +	if (opts->auto_flag)
> +		strvec_push(&rerere_cmd.args, "--skip-locked");
>  	return run_command(&rerere_cmd);
>  }

Makes sense, as this is what drives both `git gc --auto` and `git
maintenance run --auto`.

> diff --git a/rerere.c b/rerere.c
> index 64fac07c71..43c8eb04db 100644
> --- a/rerere.c
> +++ b/rerere.c
> @@ -887,18 +887,30 @@ int setup_rerere(struct repository *r, struct string_list *merge_rr, int flags)
>  
>  	if (flags & (RERERE_AUTOUPDATE|RERERE_NOAUTOUPDATE))
>  		rerere_autoupdate = !!(flags & RERERE_AUTOUPDATE);
> +	if ((flags & RERERE_READONLY) && (flags & RERERE_NOWAIT))
> +		BUG("RERERE_NOWAIT does not apply with RERERE_READONLY");
>  	if (flags & RERERE_READONLY) {
>  		fd = 0;
>  	} else {
> +		int lock_flags = LOCK_DIE_ON_ERROR;
> +		int timeout_ms = rerere_lock_timeout_ms;
> +
>  		/*
>  		 * Another process may hold the lock for a while, e.g.
>  		 * "git rerere gc" while it prunes rr-cache, so wait for
> -		 * it instead of dying right away.
> +		 * it instead of dying right away.  The gc of an automatic
> +		 * maintenance run does not wait, since skipping one of
> +		 * its runs costs nothing.
>  		 */

This comment is basically a layering violation, as you now assume who
passes `RERERE_NOWAIT`. It's a generic mechanism though, so I'd just
drop that part.

> +		if (flags & RERERE_NOWAIT) {
> +			lock_flags = 0;
> +			timeout_ms = 0;
> +		}
>  		fd = repo_hold_lock_file_for_update_timeout(r, &write_lock,
>  							    git_path_merge_rr(r),
> -							    LOCK_DIE_ON_ERROR,
> -							    rerere_lock_timeout_ms);
> +							    lock_flags, timeout_ms);
> +		if (fd < 0)
> +			return -1;

It's a tiny bit fishy that we return an error in the case where we have
been asked to skip locking and we indeed weren't able to acquire the
lock. To me it doesn't really indicate an error, as it matches the
intent of the caller. But I guess that's debatable.

> diff --git a/rerere.h b/rerere.h
> index feeb0e2c9f..d54c53d0d4 100644
> --- a/rerere.h
> +++ b/rerere.h
> @@ -10,6 +10,8 @@ struct repository;
>  #define RERERE_AUTOUPDATE   01
>  #define RERERE_NOAUTOUPDATE 02
>  #define RERERE_READONLY     04
> +/* Take MERGE_RR.lock only if it is free, and return quietly otherwise */
> +#define RERERE_NOWAIT       010

"free" is a bit unusual for a term for a lock.

> @@ -37,7 +39,7 @@ const char *rerere_path(struct strbuf *buf, const struct rerere_id *,
>  int rerere_forget(struct repository *, struct pathspec *);
>  int rerere_remaining(struct repository *, struct string_list *);
>  void rerere_clear(struct repository *, struct string_list *);
> -void rerere_gc(struct repository *, struct string_list *);
> +void rerere_gc(struct repository *, struct string_list *, int);

Given that these flags are new now, and given that none of the other
flags apply to `rerere_gc`, shouldn't we instead have a separate list of
flags specific to this function?

    enum rerere_gc_flags {
        /* Skip the operation in case the MERGE_RR.lock is already taken. */
        RERERE_GC_NOWAIT = (1 << 0),
    };

    void rerere_gc(struct repository *, struct string_list *,
                   enum rerere_gc_flags flags);

> diff --git a/t/t4200-rerere.sh b/t/t4200-rerere.sh
> index 7bd92235dc..28152bf456 100755
> --- a/t/t4200-rerere.sh
> +++ b/t/t4200-rerere.sh
> @@ -242,6 +242,27 @@ test_expect_success 'old records rest in peace' '
>  	test_path_is_missing $rr2/preimage
>  '
>  
> +test_expect_success 'gc --skip-locked does nothing while MERGE_RR is locked' '
> +	mkdir -p $rr2 &&
> +	echo Hello >$rr2/preimage &&
> +	test-tool chmtime =$just_over_15_days_ago $rr2/preimage &&
> +
> +	test_when_finished "rm -f .git/MERGE_RR.lock" &&
> +	>.git/MERGE_RR.lock &&
> +	git rerere gc --skip-locked 2>err &&
> +	test_must_be_empty err &&
> +	test_path_is_file $rr2/preimage &&
> +
> +	rm .git/MERGE_RR.lock &&
> +	git rerere gc --skip-locked &&
> +	test_path_is_missing $rr2/preimage
> +'
> +
> +test_expect_success '--skip-locked is only accepted by gc' '
> +	test_must_fail git rerere --skip-locked clear 2>err &&
> +	test_grep "option .--skip-locked. requires .gc." err
> +'

You verify that --skip-locked skips when locked, but you don't verify
that it doesn't skip when unlocked.

Patrick

  reply	other threads:[~2026-10-09  9:06 UTC|newest]

Thread overview: 44+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-02  8:31 [PATCH] rerere: keep a background gc from killing a rebase Thomas Bachem via GitGitGadget
2026-09-02 13:27 ` Phillip Wood
2026-09-02 15:07   ` Thomas Bachem
2026-09-03 13:50     ` Phillip Wood
2026-09-03  7:40 ` Patrick Steinhardt
2026-09-03  8:11   ` Thomas Bachem
2026-09-03  8:32     ` Patrick Steinhardt
2026-09-03 12:12       ` Thomas Bachem
2026-09-03 13:50       ` Phillip Wood
2026-09-04  7:44 ` [PATCH v2] " Thomas Bachem via GitGitGadget
2026-09-04 15:21   ` Phillip Wood
2026-09-04 15:55     ` Thomas Bachem
2026-09-07 10:07       ` Phillip Wood
2026-09-04 17:06     ` Junio C Hamano
2026-09-04 18:17       ` Thomas Bachem
2026-09-04 15:51 ` [PATCH v3] " Thomas Bachem via GitGitGadget
2026-09-04 19:08   ` Junio C Hamano
2026-09-05  5:41     ` Thomas Bachem
2026-09-05 16:10       ` Junio C Hamano
2026-09-06 10:29         ` Thomas Bachem
2026-09-07  7:41   ` Patrick Steinhardt
2026-09-14  8:04 ` [PATCH v4 0/2] rerere: wait for MERGE_RR.lock, and go on at a conflict Thomas Bachem via GitGitGadget
2026-09-14  8:04   ` [PATCH v4 1/2] rerere: wait for MERGE_RR.lock, and let the gc skip it Thomas Bachem via GitGitGadget
2026-09-28  8:18     ` Patrick Steinhardt
2026-09-14  8:04   ` [PATCH v4 2/2] rerere: go on at a conflict when the lock stays busy Thomas Bachem via GitGitGadget
2026-09-28  8:18     ` Patrick Steinhardt
2026-09-28 11:58 ` [PATCH v5 0/3] rerere: wait for MERGE_RR.lock, and go on at a conflict Thomas Bachem via GitGitGadget
2026-09-28 11:58   ` [PATCH v5 1/3] rerere: wait for MERGE_RR.lock before giving up Thomas Bachem via GitGitGadget
2026-09-28 11:58   ` [PATCH v5 2/3] rerere: add "gc --auto" that skips a held lock Thomas Bachem via GitGitGadget
2026-09-30 15:00     ` Patrick Steinhardt
2026-10-01  8:08       ` Thomas Bachem
2026-10-01 11:19         ` Patrick Steinhardt
2026-09-28 11:58   ` [PATCH v5 3/3] rerere: go on at a conflict when the lock stays busy Thomas Bachem via GitGitGadget
2026-10-02 11:11 ` [PATCH v6 0/3] rerere: wait for MERGE_RR.lock, and go on at a conflict Thomas Bachem via GitGitGadget
2026-10-02 11:11   ` [PATCH v6 1/3] rerere: wait for MERGE_RR.lock before giving up Thomas Bachem via GitGitGadget
2026-10-02 11:11   ` [PATCH v6 2/3] rerere: add "gc --skip-locked" for auto maintenance Thomas Bachem via GitGitGadget
2026-10-09  9:06     ` Patrick Steinhardt [this message]
2026-10-10  9:09       ` Thomas Bachem
2026-10-02 11:11   ` [PATCH v6 3/3] rerere: go on at a conflict when the lock stays busy Thomas Bachem via GitGitGadget
2026-10-09  9:06     ` Patrick Steinhardt
2026-10-10  9:09       ` Thomas Bachem
2026-10-10 10:13 ` [PATCH v7 0/2] rerere: wait for MERGE_RR.lock, but not in auto maintenance Thomas Bachem via GitGitGadget
2026-10-10 10:13   ` [PATCH v7 1/2] rerere: wait for MERGE_RR.lock before giving up Thomas Bachem via GitGitGadget
2026-10-10 10:13   ` [PATCH v7 2/2] rerere: add "gc --skip-locked" for auto maintenance Thomas Bachem via GitGitGadget

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=asiugInq7YTj4Qbe@pks.im \
    --to=ps@pks.im \
    --cc=git@vger.kernel.org \
    --cc=gitgitgadget@gmail.com \
    --cc=gitster@pobox.com \
    --cc=mail@thomasbachem.com \
    --cc=phillip.wood123@gmail.com \
    --cc=phillip.wood@dunelm.org.uk \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox