git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH v3 00/12] builtin/maintenance: fix ref lock races when detaching

From
Patrick Steinhardt <ps@pks.im>
Date
Jun 2, 2025, 07:17 UTC
Message-ID
<20250602-b4-pks-maintenance-ref-lock-race-v3-0-587d44252dcb@pks.im>
In-Reply-To
<20250527-b4-pks-maintenance-ref-lock-race-v1-0-e1ceb2dea66e@pks.im>
Hi,

this patch series fixes races around locking the "packed-refs" file when auto-maintenance decides to repack it. This issue has been reported e.g. via [1] and [2].

The root cause is that git-gc(1) used to know to detach _after_ having repacked references. As such, callers wouldn't continue with their thing until we have already packed refs, and thus the race does not exist there. git-maintenance(1) didn't have the same split though, so this patch series retrofits that logic.

The series is structured as follows:
  - Patches 1 and 2 do some light refactorings.
  - Patches 3 to 5 refactor how we set up the list of tasks to not rely
    on globals anymore. Instead, we now have a single source of truth
    for which tasks exactly will be run.
  - The remaining patches introduce the split of before/after-detach
    tasks and wire them up for "pack-refs", "reflog-expire" and "gc"
    tasks.
Changes in v2:
  - A couple of commit message improvements.
  - Introduce `die(NULL)` to die with the correct exit code but no error
    message. This gets rid of some magic numbers.
  - Introduce an enum to discern the phases before and after detach.
  - Link to v1: https://lore.kernel.org/r/20250527-b4-pks-maintenance-ref-lock-race-v1-0-e1ceb2dea66e@pks.im
Changes in v3:
  - Rework logic to talk about foreground/background tasks instead of
    before/after detach.
  - Link to v2: https://lore.kernel.org/r/20250530-b4-pks-maintenance-ref-lock-race-v2-0-d04e2f93e51f@pks.im
Thanks!
Patrick

[1]: <CAJR-fbZ4X1+gN75m2dUvocR6NkowLOZ9F26cjBy8w1qd181OoQ@mail.gmail.com> [2]: <CANi7bVAkNc+gY1NoXfJuDRjxjZLTgL8Lfn8_ZmWsvLAoiLPkNg@mail.gmail.com>

---
Patrick Steinhardt (12):
      builtin/gc: use designated field initializers for maintenance tasks
      builtin/gc: drop redundant local variable
      builtin/maintenance: centralize configuration of explicit tasks
      builtin/maintenance: mark "--task=" and "--schedule=" as incompatible
      builtin/maintenance: stop modifying global array of tasks
      builtin/maintenance: extract function to run tasks
      builtin/maintenance: fix typedef for function pointers
      builtin/maintenance: split into foreground and background tasks
      builtin/maintenance: fix locking race when packing refs and reflogs
      usage: allow dying without writing an error message
      builtin/gc: avoid global state in `gc_before_repack()`
      builtin/maintenance: fix locking race when handling "gc" task
 builtin/am.c                |   4 +-
 builtin/checkout.c          |   4 +-
 builtin/fetch.c             |   2 +-
 builtin/gc.c                | 410 +++++++++++++++++++++++++-------------------
 builtin/submodule--helper.c |  12 +-
 t/t7900-maintenance.sh      |  19 +-
 usage.c                     |   2 +
 7 files changed, 263 insertions(+), 190 deletions(-)
Range-diff versus v2:
 1:  2c90fadd640 =  1:  00fa348fb9c builtin/gc: use designated field initializers for maintenance tasks
 2:  ebd37a92895 =  2:  d7a2072da28 builtin/gc: drop redundant local variable
 3:  90863ae9957 =  3:  2eef35388e1 builtin/maintenance: centralize configuration of explicit tasks
 4:  f0c05004ea3 =  4:  372efcf7fec builtin/maintenance: mark "--task=" and "--schedule=" as incompatible
 5:  85c8273d4f4 =  5:  979f27b19ce builtin/maintenance: stop modifying global array of tasks
 6:  a9d49980f8a =  6:  b89c8d237ed builtin/maintenance: extract function to run tasks
 7:  35a78300572 =  7:  96554899fd9 builtin/maintenance: fix typedef for function pointers
 8:  b5fe34348aa !  8:  b79befc82be builtin/maintenance: let tasks do maintenance before and after detach
    @@ Metadata
     Author: Patrick Steinhardt <ps@pks.im>
     
      ## Commit message ##
    -    builtin/maintenance: let tasks do maintenance before and after detach
    +    builtin/maintenance: split into foreground and background tasks
     
         Both git-gc(1) and git-maintenance(1) have logic to daemonize so that
         the maintenance tasks are performed in the background. git-gc(1) has
    @@ Commit message
         Signed-off-by: Patrick Steinhardt <ps@pks.im>
     
      ## builtin/gc.c ##
    -@@ builtin/gc.c: typedef int (*maintenance_auto_fn)(struct gc_config *cfg);
    +@@ builtin/gc.c: static int maintenance_task_incremental_repack(struct maintenance_run_opts *opts
    + 
    + typedef int (*maintenance_task_fn)(struct maintenance_run_opts *opts,
    + 				   struct gc_config *cfg);
    +-
    +-/*
    +- * An auto condition function returns 1 if the task should run
    +- * and 0 if the task should NOT run. See needs_to_gc() for an
    +- * example.
    +- */
    + typedef int (*maintenance_auto_fn)(struct gc_config *cfg);
      
      struct maintenance_task {
      	const char *name;
     -	maintenance_task_fn fn;
    -+	maintenance_task_fn before_detach;
    -+	maintenance_task_fn after_detach;
    ++
    ++	/*
    ++	 * Work that will be executed before detaching. This should not include
    ++	 * tasks that may run for an extended amount of time as it does cause
    ++	 * auto-maintenance to block until foreground tasks have been run.
    ++	 */
    ++	maintenance_task_fn foreground;
    ++
    ++	/*
    ++	 * Work that will be executed after detaching. When not detaching the
    ++	 * work will be run in the foreground, as well.
    ++	 */
    ++	maintenance_task_fn background;
    ++
    ++	/*
    ++	 * An auto condition function returns 1 if the task should run and 0 if
    ++	 * the task should NOT run. See needs_to_gc() for an example.
    ++	 */
      	maintenance_auto_fn auto_condition;
      };
      
    @@ builtin/gc.c: typedef int (*maintenance_auto_fn)(struct gc_config *cfg);
      	[TASK_PREFETCH] = {
      		.name = "prefetch",
     -		.fn = maintenance_task_prefetch,
    -+		.after_detach = maintenance_task_prefetch,
    ++		.background = maintenance_task_prefetch,
      	},
      	[TASK_LOOSE_OBJECTS] = {
      		.name = "loose-objects",
     -		.fn = maintenance_task_loose_objects,
    -+		.after_detach = maintenance_task_loose_objects,
    ++		.background = maintenance_task_loose_objects,
      		.auto_condition = loose_object_auto_condition,
      	},
      	[TASK_INCREMENTAL_REPACK] = {
      		.name = "incremental-repack",
     -		.fn = maintenance_task_incremental_repack,
    -+		.after_detach = maintenance_task_incremental_repack,
    ++		.background = maintenance_task_incremental_repack,
      		.auto_condition = incremental_repack_auto_condition,
      	},
      	[TASK_GC] = {
      		.name = "gc",
     -		.fn = maintenance_task_gc,
    -+		.after_detach = maintenance_task_gc,
    ++		.background = maintenance_task_gc,
      		.auto_condition = need_to_gc,
      	},
      	[TASK_COMMIT_GRAPH] = {
      		.name = "commit-graph",
     -		.fn = maintenance_task_commit_graph,
    -+		.after_detach = maintenance_task_commit_graph,
    ++		.background = maintenance_task_commit_graph,
      		.auto_condition = should_write_commit_graph,
      	},
      	[TASK_PACK_REFS] = {
      		.name = "pack-refs",
     -		.fn = maintenance_task_pack_refs,
    -+		.after_detach = maintenance_task_pack_refs,
    ++		.background = maintenance_task_pack_refs,
      		.auto_condition = pack_refs_condition,
      	},
      	[TASK_REFLOG_EXPIRE] = {
      		.name = "reflog-expire",
     -		.fn = maintenance_task_reflog_expire,
    -+		.after_detach = maintenance_task_reflog_expire,
    ++		.background = maintenance_task_reflog_expire,
      		.auto_condition = reflog_expire_condition,
      	},
      	[TASK_WORKTREE_PRUNE] = {
      		.name = "worktree-prune",
     -		.fn = maintenance_task_worktree_prune,
    -+		.after_detach = maintenance_task_worktree_prune,
    ++		.background = maintenance_task_worktree_prune,
      		.auto_condition = worktree_prune_condition,
      	},
      	[TASK_RERERE_GC] = {
      		.name = "rerere-gc",
     -		.fn = maintenance_task_rerere_gc,
    -+		.after_detach = maintenance_task_rerere_gc,
    ++		.background = maintenance_task_rerere_gc,
      		.auto_condition = rerere_gc_condition,
      	},
      };
      
     +enum task_phase {
    -+	TASK_PHASE_BEFORE_DETACH,
    -+	TASK_PHASE_AFTER_DETACH,
    ++	TASK_PHASE_FOREGROUND,
    ++	TASK_PHASE_BACKGROUND,
     +};
     +
      static int maybe_run_task(const struct maintenance_task *task,
    @@ builtin/gc.c: typedef int (*maintenance_auto_fn)(struct gc_config *cfg);
     +			  struct gc_config *cfg,
     +			  enum task_phase phase)
      {
    -+	int before = (phase == TASK_PHASE_BEFORE_DETACH);
    -+	maintenance_task_fn fn = before ? task->before_detach : task->after_detach;
    -+	const char *region = before ? "maintenance before" : "maintenance";
    ++	int foreground = (phase == TASK_PHASE_FOREGROUND);
    ++	maintenance_task_fn fn = foreground ? task->foreground : task->background;
    ++	const char *region = foreground ? "maintenance foreground" : "maintenance";
      	int ret = 0;
      
     +	if (!fn)
    @@ builtin/gc.c: static int maintenance_run_tasks(struct maintenance_run_opts *opts
      
     +	for (size_t i = 0; i < opts->tasks_nr; i++)
     +		if (maybe_run_task(&tasks[opts->tasks[i]], r, opts, cfg,
    -+				   TASK_PHASE_BEFORE_DETACH))
    ++				   TASK_PHASE_FOREGROUND))
     +			result = 1;
     +
      	/* Failure to daemonize is ok, we'll continue in foreground. */
    @@ builtin/gc.c: static int maintenance_run_tasks(struct maintenance_run_opts *opts
      	for (size_t i = 0; i < opts->tasks_nr; i++)
     -		if (maybe_run_task(&tasks[opts->tasks[i]], r, opts, cfg))
     +		if (maybe_run_task(&tasks[opts->tasks[i]], r, opts, cfg,
    -+				   TASK_PHASE_AFTER_DETACH))
    ++				   TASK_PHASE_BACKGROUND))
      			result = 1;
      
      	rollback_lock_file(&lk);
 9:  79766b0afa0 <  -:  ----------- builtin/maintenance: fix locking race when packing refs and reflogs
 -:  ----------- >  9:  8b3f82b871d builtin/maintenance: fix locking race when packing refs and reflogs
10:  d74a6a3fe7f = 10:  7c61970f4aa usage: allow dying without writing an error message
11:  f8e77819f6e = 11:  39a1aa9dcb0 builtin/gc: avoid global state in `gc_before_repack()`
12:  31007c209cb ! 12:  8b263d0fc2b builtin/maintenance: fix locking race when handling "gc" task
    @@ Commit message
         for the "pack-refs" and "reflog-expire" tasks in preceding commits. Fix
         this by splitting up the logic of the "gc" task:
     
    -      - Before detaching we execute `gc_before_repack()`, which contains the
    -        logic that git-gc(1) itself would execute before detaching.
    +      - We execute `gc_before_repack()` in the foreground, which contains
    +        the logic that git-gc(1) itself would execute in the foreground, as
    +        well.
     
    -      - After detaching we spawn git-gc(1), but with a new hidden flag that
    +      - We spawn git-gc(1) after detaching, but with a new hidden flag that
             suppresses calling `gc_before_repack()`.
     
         Like this we have roughly the same logic as git-gc(1) itself and know to
         repack refs and reflogs before detaching, thus fixing the race.
     
    -    Note that `gc_before_repack()` is renamed to `gc_before_detach()` to
    +    Note that `gc_before_repack()` is renamed to `gc_foreground_tasks()` to
         better reflect what this function does.
     
         Signed-off-by: Patrick Steinhardt <ps@pks.im>
    @@ builtin/gc.c: static int report_last_gc_error(void)
      }
      
     -static int gc_before_repack(struct maintenance_run_opts *opts,
    -+static int gc_before_detach(struct maintenance_run_opts *opts,
    - 			    struct gc_config *cfg)
    +-			    struct gc_config *cfg)
    ++static int gc_foreground_tasks(struct maintenance_run_opts *opts,
    ++			       struct gc_config *cfg)
      {
      	if (cfg->pack_refs && maintenance_task_pack_refs(opts, cfg))
    + 		return error(FAILED_RUN, "pack-refs");
     @@ builtin/gc.c: int cmd_gc(int argc,
      	pid_t pid;
      	int daemonized = 0;
      	int keep_largest_pack = -1;
    -+	int skip_maintenance_before_detach = 0;
    ++	int skip_foreground_tasks = 0;
      	timestamp_t dummy;
      	struct maintenance_run_opts opts = MAINTENANCE_RUN_OPTS_INIT;
      	struct gc_config cfg = GC_CONFIG_INIT;
    @@ builtin/gc.c: int cmd_gc(int argc,
      			 N_("repack all other packs except the largest pack")),
      		OPT_STRING(0, "expire-to", &cfg.repack_expire_to, N_("dir"),
      			   N_("pack prefix to store a pack containing pruned objects")),
    -+		OPT_HIDDEN_BOOL(0, "skip-maintenance-before-detach", &skip_maintenance_before_detach,
    -+			   N_("skip maintenance steps typically done before detaching")),
    ++		OPT_HIDDEN_BOOL(0, "skip-foreground-tasks", &skip_foreground_tasks,
    ++			   N_("skip maintenance tasks typically done in the foreground")),
      		OPT_END()
      	};
      
    @@ builtin/gc.c: int cmd_gc(int argc,
     -			ret = 0;
     -			goto out;
     -		}
    -+		if (!skip_maintenance_before_detach) {
    ++		if (!skip_foreground_tasks) {
     +			if (lock_repo_for_gc(force, &pid)) {
     +				ret = 0;
     +				goto out;
    @@ builtin/gc.c: int cmd_gc(int argc,
     -		if (gc_before_repack(&opts, &cfg) < 0)
     -			die(NULL);
     -		delete_tempfile(&pidfile);
    -+			if (gc_before_detach(&opts, &cfg) < 0)
    ++			if (gc_foreground_tasks(&opts, &cfg) < 0)
     +				die(NULL);
     +			delete_tempfile(&pidfile);
     +		}
    @@ builtin/gc.c: int cmd_gc(int argc,
      
     -	if (opts.detach <= 0)
     -		gc_before_repack(&opts, &cfg);
    -+	if (opts.detach <= 0 && !skip_maintenance_before_detach)
    -+		gc_before_detach(&opts, &cfg);
    ++	if (opts.detach <= 0 && !skip_foreground_tasks)
    ++		gc_foreground_tasks(&opts, &cfg);
      
      	if (!repository_format_precious_objects) {
      		struct child_process repack_cmd = CHILD_PROCESS_INIT;
    @@ builtin/gc.c: static int maintenance_task_prefetch(struct maintenance_run_opts *
      
     -static int maintenance_task_gc(struct maintenance_run_opts *opts,
     -			       struct gc_config *cfg UNUSED)
    -+static int maintenance_task_gc_before_detach(struct maintenance_run_opts *opts,
    -+					     struct gc_config *cfg)
    ++static int maintenance_task_gc_foreground(struct maintenance_run_opts *opts,
    ++					  struct gc_config *cfg)
     +{
    -+	return gc_before_detach(opts, cfg);
    ++	return gc_foreground_tasks(opts, cfg);
     +}
     +
    -+static int maintenance_task_gc_after_detach(struct maintenance_run_opts *opts,
    -+					    struct gc_config *cfg UNUSED)
    ++static int maintenance_task_gc_background(struct maintenance_run_opts *opts,
    ++					  struct gc_config *cfg UNUSED)
      {
      	struct child_process child = CHILD_PROCESS_INIT;
      
    @@ builtin/gc.c: static int maintenance_task_gc(struct maintenance_run_opts *opts,
      	else
      		strvec_push(&child.args, "--no-quiet");
      	strvec_push(&child.args, "--no-detach");
    -+	strvec_push(&child.args, "--skip-maintenance-before-detach");
    ++	strvec_push(&child.args, "--skip-foreground-tasks");
      
      	return run_command(&child);
      }
    @@ builtin/gc.c: static const struct maintenance_task tasks[] = {
      	},
      	[TASK_GC] = {
      		.name = "gc",
    --		.after_detach = maintenance_task_gc,
    -+		.before_detach = maintenance_task_gc_before_detach,
    -+		.after_detach = maintenance_task_gc_after_detach,
    +-		.background = maintenance_task_gc,
    ++		.foreground = maintenance_task_gc_foreground,
    ++		.background = maintenance_task_gc_background,
      		.auto_condition = need_to_gc,
      	},
      	[TASK_COMMIT_GRAPH] = {
    @@ t/t7900-maintenance.sh: test_expect_success 'run [--auto|--quiet]' '
     -	test_subcommand git gc --quiet --no-detach <run-no-auto.txt &&
     -	test_subcommand ! git gc --auto --quiet --no-detach <run-auto.txt &&
     -	test_subcommand git gc --no-quiet --no-detach <run-no-quiet.txt
    -+	test_subcommand git gc --quiet --no-detach --skip-maintenance-before-detach <run-no-auto.txt &&
    -+	test_subcommand ! git gc --auto --quiet --no-detach --skip-maintenance-before-detach <run-auto.txt &&
    -+	test_subcommand git gc --no-quiet --no-detach --skip-maintenance-before-detach <run-no-quiet.txt
    ++	test_subcommand git gc --quiet --no-detach --skip-foreground-tasks <run-no-auto.txt &&
    ++	test_subcommand ! git gc --auto --quiet --no-detach --skip-foreground-tasks <run-auto.txt &&
    ++	test_subcommand git gc --no-quiet --no-detach --skip-foreground-tasks <run-no-quiet.txt
      '
      
      test_expect_success 'maintenance.auto config option' '
    @@ t/t7900-maintenance.sh: test_expect_success 'run --task=<task>' '
     -	test_subcommand ! git gc --quiet --no-detach <run-commit-graph.txt &&
     -	test_subcommand git gc --quiet --no-detach <run-gc.txt &&
     -	test_subcommand git gc --quiet --no-detach <run-both.txt &&
    -+	test_subcommand ! git gc --quiet --no-detach --skip-maintenance-before-detach <run-commit-graph.txt &&
    -+	test_subcommand git gc --quiet --no-detach --skip-maintenance-before-detach <run-gc.txt &&
    -+	test_subcommand git gc --quiet --no-detach --skip-maintenance-before-detach <run-both.txt &&
    ++	test_subcommand ! git gc --quiet --no-detach --skip-foreground-tasks <run-commit-graph.txt &&
    ++	test_subcommand git gc --quiet --no-detach --skip-foreground-tasks <run-gc.txt &&
    ++	test_subcommand git gc --quiet --no-detach --skip-foreground-tasks <run-both.txt &&
      	test_subcommand git commit-graph write --split --reachable --no-progress <run-commit-graph.txt &&
      	test_subcommand ! git commit-graph write --split --reachable --no-progress <run-gc.txt &&
      	test_subcommand git commit-graph write --split --reachable --no-progress <run-both.txt

--- base-commit: 845c48a16a7f7b2c44d8cb137b16a4a1f0140229 change-id: 20250527-b4-pks-maintenance-ref-lock-race-11ae5d68e06f

Previous: Patrick SteinhardtNext: Patrick Steinhardt
Message 30 of 71 in “builtin/maintenance: fix ref lock races when detaching”
  1. 00/11 builtin/maintenance: fix ref lock races when detachingPatrick Steinhardt, May 27, 2025
  2. 01/11 builtin/gc: use designated field initializers for maintenance tasksPatrick Steinhardt, May 27, 2025
  3. 02/11 builtin/gc: drop redundant local variablePatrick Steinhardt, May 27, 2025
  4. 03/11 builtin/maintenance: centralize configuration of explicit tasksPatrick Steinhardt, May 27, 2025
  5. 04/11 builtin/maintenance: mark "--task=" and "--schedule=" as incompatiblePatrick Steinhardt, May 27, 2025
  6. Ramsay JonesMay 27, 2025
  7. Patrick SteinhardtMay 28, 2025
  8. 05/11 builtin/maintenance: stop modifying global array of tasksPatrick Steinhardt, May 27, 2025
  9. 06/11 builtin/maintenance: extract function to run tasksPatrick Steinhardt, May 27, 2025
  10. 07/11 builtin/maintenance: fix typedef for function pointersPatrick Steinhardt, May 27, 2025
  11. 08/11 builtin/maintenance: let tasks do maintenance before and after detachPatrick Steinhardt, May 27, 2025
  12. Ramsay JonesMay 27, 2025
  13. Patrick SteinhardtMay 28, 2025
  14. 09/11 builtin/maintenance: fix locking race when packing refs and reflogsPatrick Steinhardt, May 27, 2025
  15. 11/11 builtin/maintenance: fix locking race when handling "gc" taskPatrick Steinhardt, May 27, 2025
  16. 10/11 builtin/gc: avoid global state in `gc_before_repack()`Patrick Steinhardt, May 27, 2025
  17. 00/12 builtin/maintenance: fix ref lock races when detachingPatrick Steinhardt, May 30, 2025
  18. 01/12 builtin/gc: use designated field initializers for maintenance tasksPatrick Steinhardt, May 30, 2025
  19. 02/12 builtin/gc: drop redundant local variablePatrick Steinhardt, May 30, 2025
  20. 04/12 builtin/maintenance: mark "--task=" and "--schedule=" as incompatiblePatrick Steinhardt, May 30, 2025
  21. 03/12 builtin/maintenance: centralize configuration of explicit tasksPatrick Steinhardt, May 30, 2025
  22. 05/12 builtin/maintenance: stop modifying global array of tasksPatrick Steinhardt, May 30, 2025
  23. 07/12 builtin/maintenance: fix typedef for function pointersPatrick Steinhardt, May 30, 2025
  24. 06/12 builtin/maintenance: extract function to run tasksPatrick Steinhardt, May 30, 2025
  25. 08/12 builtin/maintenance: let tasks do maintenance before and after detachPatrick Steinhardt, May 30, 2025
  26. 09/12 builtin/maintenance: fix locking race when packing refs and reflogsPatrick Steinhardt, May 30, 2025
  27. 10/12 usage: allow dying without writing an error messagePatrick Steinhardt, May 30, 2025
  28. 11/12 builtin/gc: avoid global state in `gc_before_repack()`Patrick Steinhardt, May 30, 2025
  29. 12/12 builtin/maintenance: fix locking race when handling "gc" taskPatrick Steinhardt, May 30, 2025
  30. 00/12 builtin/maintenance: fix ref lock races when detachingPatrick Steinhardt, Jun 2, 2025
  31. 01/12 builtin/gc: use designated field initializers for maintenance tasksPatrick Steinhardt, Jun 2, 2025
  32. Karthik NayakJun 2, 2025
  33. 02/12 builtin/gc: drop redundant local variablePatrick Steinhardt, Jun 2, 2025
  34. 03/12 builtin/maintenance: centralize configuration of explicit tasksPatrick Steinhardt, Jun 2, 2025
  35. 04/12 builtin/maintenance: mark "--task=" and "--schedule=" as incompatiblePatrick Steinhardt, Jun 2, 2025
  36. 05/12 builtin/maintenance: stop modifying global array of tasksPatrick Steinhardt, Jun 2, 2025
  37. 06/12 builtin/maintenance: extract function to run tasksPatrick Steinhardt, Jun 2, 2025
  38. 07/12 builtin/maintenance: fix typedef for function pointersPatrick Steinhardt, Jun 2, 2025
  39. 08/12 builtin/maintenance: split into foreground and background tasksPatrick Steinhardt, Jun 2, 2025
  40. 09/12 builtin/maintenance: fix locking race when packing refs and reflogsPatrick Steinhardt, Jun 2, 2025
  41. Kristoffer HaugsbakkJun 2, 2025
  42. Patrick SteinhardtJun 3, 2025
  43. Kristoffer HaugsbakkJun 3, 2025
  44. 10/12 usage: allow dying without writing an error messagePatrick Steinhardt, Jun 2, 2025
  45. Karthik NayakJun 3, 2025
  46. Kristoffer HaugsbakkJun 3, 2025
  47. Karthik NayakJun 3, 2025
  48. Patrick SteinhardtJun 3, 2025
  49. Kristoffer HaugsbakkJun 3, 2025
  50. Junio C HamanoJun 3, 2025
  51. Patrick SteinhardtJun 5, 2025
  52. 11/12 builtin/gc: avoid global state in `gc_before_repack()`Patrick Steinhardt, Jun 2, 2025
  53. 12/12 builtin/maintenance: fix locking race when handling "gc" taskPatrick Steinhardt, Jun 2, 2025
  54. 00/12 builtin/maintenance: fix ref lock races when detachingPatrick Steinhardt, Jun 3, 2025
  55. 01/12 builtin/gc: use designated field initializers for maintenance tasksPatrick Steinhardt, Jun 3, 2025
  56. 02/12 builtin/gc: drop redundant local variablePatrick Steinhardt, Jun 3, 2025
  57. 03/12 builtin/maintenance: centralize configuration of explicit tasksPatrick Steinhardt, Jun 3, 2025
  58. 05/12 builtin/maintenance: stop modifying global array of tasksPatrick Steinhardt, Jun 3, 2025
  59. 04/12 builtin/maintenance: mark "--task=" and "--schedule=" as incompatiblePatrick Steinhardt, Jun 3, 2025
  60. Derrick StoleeJun 5, 2025
  61. 06/12 builtin/maintenance: extract function to run tasksPatrick Steinhardt, Jun 3, 2025
  62. 07/12 builtin/maintenance: fix typedef for function pointersPatrick Steinhardt, Jun 3, 2025
  63. 08/12 builtin/maintenance: split into foreground and background tasksPatrick Steinhardt, Jun 3, 2025
  64. 09/12 builtin/maintenance: fix locking race with refs and reflogs tasksPatrick Steinhardt, Jun 3, 2025
  65. 11/12 builtin/gc: avoid global state in `gc_before_repack()`Patrick Steinhardt, Jun 3, 2025
  66. 10/12 usage: allow dying without writing an error messagePatrick Steinhardt, Jun 3, 2025
  67. 12/12 builtin/maintenance: fix locking race when handling "gc" taskPatrick Steinhardt, Jun 3, 2025
  68. Kristoffer HaugsbakkJun 3, 2025
  69. Ben KnobleJun 4, 2025
  70. Derrick StoleeJun 5, 2025
  71. Patrick SteinhardtJun 6, 2025

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.