Home / Alt manpages / git-pack-refs(1)

  • git-pack-refs(1)
  • User command
  • linux

Pack Git References Safely and Check What Changed

You will compact a Git repository's references into $GIT_DIR/packed-refs, verify which references moved, and choose a packing scope that does not surprise active branch work. Allow about ten minutes, plus the time needed to inspect a large repository. The examples use Git 2.43.0, matching the installed git-pack-refs(1) manual on this machine.

This is an ordinary repository maintenance command. It normally needs no elevated privileges. Run it as the repository owner, from inside the repository or with Git's usual repository discovery. Do not use sudo: changing ownership or permissions can create a second problem.

1. Check the repository and command version

Start with read-only checks. The working tree may contain uncommitted work, but record its state before maintenance so that a later change is not mistaken for packing:

$ git rev-parse --show-toplevel
/srv/projects/example
$ git --version
git version 2.43.0
$ git status --short
M README.md

A blank status result means the worktree is clean. A non-blank result is not an error for pack-refs, but it is a useful checkpoint. If you are about to maintain a production checkout, stop and agree how to handle concurrent Git operations first.

2. See the current reference layout

References are branch and tag names. Git can store them as individual files below .git/refs, or in the single packed-refs file. The reference name and its commit are what matter; the presence of a loose file is not a reliable way to decide whether a branch exists.

$ git show-ref --heads --tags
84bbab8e1e563f64d069edc15997a8f6ae7bbb81 refs/heads/main
84bbab8e1e563f64d069edc15997a8f6ae7bbb81 refs/tags/v1.0.0
$ test -f "$(git rev-parse --git-path packed-refs)" && echo packed-refs-present
packed-refs-present

Do not edit packed-refs by hand. Git updates it as part of reference operations. If a hook, backup job or another administrator is manipulating refs at the same time, wait until that work has finished.

3. Pack the normal maintenance set

Without options, git pack-refs packs all tags and references that are already packed, while leaving other loose references alone. This default is deliberate: active branch tips are updated often, so packing them usually gives less benefit than packing stable tags.

$ git pack-refs
$ git show-ref --heads --tags
84bbab8e1e563f64d069edc15997a8f6ae7bbb81 refs/heads/main
84bbab8e1e563f64d069edc15997a8f6ae7bbb81 refs/tags/v1.0.0

The command is normally quiet on success. The second command confirms that references remain resolvable; it does not tell you whether each one is loose or packed. To inspect the storage file without changing it:

$ git rev-parse --git-path packed-refs
.git/packed-refs
$ sed -n '1,12p' "$(git rev-parse --git-path packed-refs)"
# pack-refs with: peeled fully-peeled sorted
84bbab8e1e563f64d069edc15997a8f6ae7bbb81 refs/tags/v1.0.0

The object ID in your output will differ. Annotated tags can have an additional peeled line beginning with ^.

4. Pack every eligible reference when there is a reason

Use --all for a repository with many historical branches or other refs that will not be updated regularly:

$ git pack-refs --all
$ git show-ref --heads --tags
84bbab8e1e563f64d069edc15997a8f6ae7bbb81 refs/heads/historical
84bbab8e1e563f64d069edc15997a8f6ae7bbb81 refs/heads/main
84bbab8e1e563f64d069edc15997a8f6ae7bbb81 refs/tags/v1.0.0

Use the actual output from your repository for verification; the shortened object ID above is only illustrative. --all excludes hidden, broken and symbolic refs. A later update to a branch creates a loose ref again. That is normal, and the next ordinary git pack-refs leaves an active branch loose.

Warning

Packing prunes the loose ref files it has successfully packed. It does not delete the branches or tags, but scripts that incorrectly inspect only .git/refs/heads can appear to lose them. Use git show-ref, git for-each-ref or git rev-parse instead.

5. Keep loose refs for an inspection or migration

Use --no-prune if you need the original loose files to remain after packing:

$ git pack-refs --all --no-prune
$ git show-ref --verify refs/heads/main
84bbab8e1e563f64d069edc15997a8f6ae7bbb81 refs/heads/main

This is a storage choice, not a backup. The repository now has both packed data and loose ref files, so it may use more space. Once you have completed the inspection, run ordinary packing if you want Git to remove loose copies that were packed. Keep a real repository backup if recovery matters.

6. Limit packing with include and exclude patterns

Use glob patterns when a full pack is too broad. Repeated --include and --exclude options accumulate. An exclude wins over an include, and an include list means tags are no longer included by default:

$ git pack-refs --include 'refs/heads/archive/*' --exclude 'refs/heads/archive/keep'
$ git show-ref --verify refs/heads/archive/keep
84bbab8e1e563f64d069edc15997a8f6ae7bbb81 refs/heads/archive/keep

Patterns are matched against full ref names such as refs/heads/archive/2023. Quote them so the shell does not expand asterisks against local file names. With --all, only loose refs that do not match an exclusion are packed. An exclusion does not unpack a ref that is already in packed-refs.

7. Recover from a confusing result

If a branch seems missing, query Git directly:

$ git show-ref --verify refs/heads/BRANCH_NAME
fatal: 'refs/heads/BRANCH_NAME' - not a valid ref

Replace BRANCH_NAME with an expected name. If Git cannot resolve it, check the exact spelling and inspect all refs with git for-each-ref. Do not recreate a missing ref by guessing its commit. Restore it from a known-good backup or an explicitly recorded object ID.

There is no separate undo command for packing. Packing does not rewrite commits and does not change branch names. If you used --no-prune, the loose files remain; otherwise, Git can recreate loose storage when a ref is updated. Restore from backup if you need to recover a ref that was deleted before packing.

Done means

  • The Git version and repository path were checked.
  • The intended scope was chosen: ordinary packing, --all, or explicit patterns.
  • git show-ref still resolves the branches and tags you expected.
  • No script relies on loose files below .git/refs to discover refs.
  • Any concurrent operations and any needed backup were handled before pruning loose ref files.