GIT-PACK-REFS(1)

πŸ’– git-pack-refs(1) πŸ’–

okay so bestie, welcome to the git-pack-refs page β€” that's section 1 of the Git manual, and yes we're gonna get through it together, hand in hand, because it's actually not that deep and it's kinda iconic. πŸŽ€


-->

✨ NAME ✨

git-pack-refs β€” packing up heads and tags so your repo stops being slow, no cap πŸ’…

✨ SYNOPSIS ✨

git pack-refs [--all] [--no-prune] [--auto] [--include <pattern>] [--exclude <pattern>]

🌸 DESCRIPTION 🌸

So back in the day β€” like, traditionally β€” the tips of branches and tags (we call them refs collectively, iykyk) were stored one file per ref, sitting in a (sub)directory under the $GIT_DIR/refs directory. Cute in theory!! But here's the tea: lots of branch tips get updated constantly, while most tags and some branch tips literally never change. So when a repo has hundreds or thousands of tags, that one-file-per-ref situation is giving storage waste and performance pain. 😭

This command exists to fix that exact storage-and-performance problem by storing refs in a single file, $GIT_DIR/packed-refs. And when a ref is missing from the traditional $GIT_DIR/refs directory hierarchy, it gets looked up in this file, and used if it's found. slay. ✨

Subsequent updates to branches always create new files under the $GIT_DIR/refs directory hierarchy.

Now, the recommended practice (write this down πŸ–ŠοΈ) for a repo with way too many refs is: pack its refs with --all once, then occasionally run git pack-refs. Why? Because tags are by definition stationary and not expected to change. Branch heads get packed with that initial pack-refs --all, but only the currently active branch heads become unpacked β€” and the next pack-refs (without --all) will leave them unpacked. periodt. πŸ’…

πŸ‘‘ OPTIONS πŸ‘‘

  • --all β€” bestie listen. By default this command packs all tags and refs that are already packed, and leaves other refs alone. Why? Because branches are expected to be actively developed, and packing their tips doesn't help performance. This option makes all refs get packed too β€” with the exception of hidden refs, broken refs, and symbolic refs. Super useful for a repo with lots of branches of historical interest. πŸ¦‹
  • --no-prune β€” normally the command removes loose refs under the $GIT_DIR/refs hierarchy after packing them. This option tells it not to. that's it, that's the whole vibe. πŸ™ˆ
  • --auto β€” pack refs as needed, depending on the current state of the ref database. The behavior depends on the ref format your repo uses and may change in the future. Here's the breakdown:
    • "files": loose references get packed into the packed-refs file based on the ratio of loose references to the size of the packed-refs file. The bigger the packed-refs file, the more loose references need to exist before we repack.
    • "reftable": tables are compacted so they form a geometric sequence. For two tables N and N+1, where N+1 is newer, this maintains the property that N is at least twice as big as N+1. Only tables that violate this property get compacted.
  • --include <pattern> β€” pack refs based on a glob(7) pattern. ✨ Repetitions of this option accumulate inclusion patterns. If a ref is both included in --include and --exclude, --exclude takes precedence (exclude is the main character here, sorry πŸ’€). Using --include will preclude all tags from being included by default. Symbolic refs and broken refs will never be packed. When used with --all, it will be a noop. Use --no-include to clear and reset the list of patterns.
  • --exclude <pattern> β€” do not pack refs matching the given glob(7) pattern. Repetitions of this option accumulate exclusion patterns. Use --no-exclude to clear and reset the list of patterns. If a ref is already packed, including it with --exclude will not unpack it.

When used with --all, pack only loose refs which do not match any of the provided --exclude patterns.

When used with --include, refs provided to --include, minus refs that are provided to --exclude, will be packed. math! 🧚

πŸ’€ BUGS πŸ’€

Older documentation written before the packed-refs mechanism was introduced may still say things like ".git/refs/heads/<branch> file exists" when what it actually means is "branch <branch> exists". so if you see that, don't come for me, it's just old lore 🫢

🌈 GIT 🌈

Part of the git(1) suite.


and that's the whole page bestie, you ate that πŸ“ this was Git 2.56.0, dated 2026-09-27 β€” catch you on the next man page, mwah πŸ’œπŸŽ€

πŸ“¦ where this page comes from