git maintenance

Updated

October 30, 2026

git maintenance

Git commands that add data (add, commit, fetch) optimize for a snappy UI. They do not spend time reshaping the whole object database. git maintenance is the operator tool that runs background (or manual) optimization tasks: commit-graph, prefetch, loose-object packing, multi-pack-index repacks, and more — without turning every git status into a full gc.

Mental model

foreground Git  ──► writes packs / loose objects / refs  (fast path)
        │
        ▼
git maintenance run / start
        │
        ├── commit-graph      (faster reachability / log)
        ├── prefetch          (objects ready before real fetch)
        ├── loose-objects     (loose → pack, carefully)
        ├── incremental-repack (multi-pack-index dance)
        └── pack-refs, …      (ref iteration speed)

git maintenance start registers the repo and installs a scheduler (systemd user timers on modern Linux, else cron; launchctl / schtasks on macOS / Windows). register alone updates config; start = register + schedule.

Tasks that matter at scale

Task What it does Default under incremental strategy
commit-graph Incremental commit-graph write + verify; safe beside concurrent Git hourly
prefetch git fetch into refs/prefetch/ (no tag update; does not move remote-tracking branches) hourly
loose-objects Delete loose objects already packed; pack a batch of remaining loose objects daily
incremental-repack multi-pack-index expire then repack small packs into a larger one daily
pack-refs Pack loose refs into a single file weekly (when enabled by strategy)
gc Full garbage collection / aggressive repack — expensive; not on the incremental schedule manual / other strategies

Prefetch makes the next interactive git fetch mostly a tip update when objects were already downloaded. Exclude a remote with remote.<name>.skipFetchAll if that remote should not be prefetched.

incremental-repack uses the multi-pack-index (MIDX): expire unreferenced packs, then repack a batch of small packs and retarget MIDX entries. Default batch size 0 means “try to fold into one pack.”

Worked examples

Case 1: One-shot tasks on a lab clone

cd /path/to/large-repo

# Inspect whether auto thresholds say work is due
git maintenance is-needed --auto; echo "exit=$?"

# Run specific safe tasks explicitly
git maintenance run --task=commit-graph
git maintenance run --task=loose-objects
git maintenance run --task=incremental-repack

# Optional: see commit-graph files appear
ls -la .git/objects/info/commit-graph 2>/dev/null || ls -la .git/objects/info/commit-graphs/ 2>/dev/null || true

Use --task when you want a known order. Without --task, Git runs tasks with maintenance.<task>.enabled=true (by default that is mainly gc unless you set a strategy).

Case 2: Enable incremental strategy and register

cd /path/to/large-repo

git maintenance register
# Sets maintenance.strategy=incremental if unset, adds repo to global maintenance.repo,
# enables recommended schedules, sets maintenance.auto=false in this repo

git config --get maintenance.strategy
git config --get-all --global maintenance.repo | tail -n 5
git config --get maintenance.auto

Expected shape: incremental, a path entry under maintenance.repo, and maintenance.auto=false so foreground commands do not also kick auto-maintenance while the scheduler owns the job.

Case 3: git maintenance start on Linux

cd /path/to/large-repo
git maintenance start --scheduler=auto
# On Linux with user systemd: systemd-timer; otherwise crontab

systemctl --user list-timers 'git-maintenance*' 2>/dev/null || true
# Or: crontab -l | sed -n '/GIT MAINTENANCE/,/END GIT MAINTENANCE/p'

Timers run git for-each-repo --config=maintenance.repo maintenance run --schedule=hourly|daily|weekly. Stop with git maintenance stop (leaves the repo registered). unregister removes the repo from the global list (--force if unsure).

Case 4: Prefetch vs a normal fetch

# After maintenance has run prefetch on a registered remote:
git show-ref | grep prefetch | head
git fetch origin
# Ideal case: tips move with little or no object transfer

Prefetch refs live under refs/prefetch/ so your origin/main does not jump until you fetch.

Case 5: Manual vs scheduled, and locks

# Manual full gc via maintenance (preferred over bare git gc when using the framework)
git maintenance run --task=gc

# Auto mode: only if thresholds say so (loose object count, pack count, …)
git maintenance run --auto

Each git maintenance run takes an object database lock. Overlapping hourly runs on huge multi-repo schedules can skip a cycle — shrink task cost or stagger repos if windows exceed an hour. Do not mix bare git gc with concurrent git maintenance run; prefer git maintenance run --task=gc.

Strategy cheat sheet

maintenance.strategy Intent
none Scheduled maintenance runs nothing by default
gc Run the gc task
geometric Geometric pack repack + auxiliary cleanup; strong default for manual maintenance on large repos (Git 2.50+ docs)
incremental Small non-deleting tasks on a schedule; manual path still uses gc

Tune with maintenance.<task>.enabled and maintenance.<task>.schedule (hourly / daily / weekly). Threshold knobs include maintenance.commit-graph.auto, maintenance.loose-objects.auto, maintenance.incremental-repack.auto.

Safety

  • Prefetch contacts remotes on a timer — ensure credentials/agents work non-interactively or exclude remotes.
  • gc deletes stale data; incremental tasks prefer not to. Know which strategy you enabled before leaving a laptop on a huge monorepo overnight.
  • Customizing systemd units: use drop-ins under ~/.config/systemd/user/git-maintenance@.service.d/; start overwrites the main unit files.
  • maintenance.auto=false after register is intentional; re-enable only if you understand double scheduling.

Hands-on checklist

  1. On a disposable clone, run commit-graph and incremental-repack with --task.
  2. git maintenance register and confirm maintenance.strategy / maintenance.repo.
  3. git maintenance start; list timers or crontab region.
  4. Trigger prefetch; inspect refs/prefetch/; run a normal git fetch and note transfer size.
  5. git maintenance stop and unregister when the lab is done.

Pitfalls

Symptom Likely cause Fix
Hourly tasks sometimes skip Overlap on object DB lock Fewer repos per user, cheaper tasks, or longer spacing
Remote-tracking branches “randomly” move Not prefetch — something else fetching Prefetch should only touch refs/prefetch/
Disk grows after incremental mode No full gc; MIDX keeps more packs briefly Periodic manual gc / geometric strategy if policy allows
Scheduler missing on Linux No user systemd / lingering loginctl enable-linger $USER or use --scheduler=crontab
unregister errors Repo not in list git maintenance unregister --force

Sources