Home / Alt manpages / git-unpack-objects(1)

  • git-unpack-objects(1)
  • User command
  • linux

Recover Git Objects from a Pack with git unpack-objects

git unpack-objects takes a stray .pack file, maybe from a broken push or a backup, and expands its objects into a repository you control. Then you can verify what actually arrived. The examples use Git 2.43.0 from Debian package git-man 1:2.43.0-1ubuntu7.3.

Allow about fifteen minutes. You need a shell, Git, a readable pack file, and a target repository. Work on a copy of anything valuable first, because this command reads the pack from standard input; it does not create branches, move HEAD, populate a working tree, or install the pack as a normal indexed pack. No command below needs sudo.

1. Confirm the installed command

Check the version and the local option syntax before you go near the pack:

$ git --version
git version 2.43.0
$ git unpack-objects -h
usage: git unpack-objects [-n] [-q] [-r] [--strict]

The installed manual also documents --max-input-size=<size>, which caps the size of input accepted; the short help above is not a full substitute for it. Keep the input path and target repository explicit while you work.

Checkpoint

Identify the exact pack and confirm it really is one:

$ ls -lh /path/to/incoming.pack
$ file /path/to/incoming.pack
/path/to/incoming.pack: Git pack, version 2, 6 objects

Wording from file varies. A missing file, an unreadable file or an ordinary archive is an input problem: sort that out before reaching for a more privileged command.

2. Select the target repository

Run the command from, or point it at, the repository that should receive the objects. A bare repository is fine. For an ordinary checkout, verify it before you change anything:

$ git -C /path/to/target rev-parse --git-dir
/path/to/target/.git
$ git -C /path/to/target status --short

Changes printed here? Stop and record them before you start recovery work. git unpack-objects writes into the object database, so keep a backup or snapshot of the target if it matters. The operation does not normally touch tracked files, the index or refs, but the new objects do become part of the repository's stored data.

3. Dry-run the pack

Use -n first. It checks the pack without unpacking anything:

$ git -C /path/to/target unpack-objects -n < /path/to/incoming.pack
$ printf 'dry-run status: %s\n' "$?"
dry-run status: 0

Silence and status 0 mean this check passed. The command reads standard input, so the redirection is part of the example, not optional decoration. Seeing a pack-format or checksum error? Keep the original pack and get it again from whoever produced it. Do not reach for -r just to suppress an error: that asks Git to push past corruption and make a best-effort recovery, which is a different decision entirely.

4. Limit a potentially untrusted input

A pack can be far larger than expected. Set an input limit when the source is untrusted or storage is tight:

$ git -C /path/to/target unpack-objects --max-input-size=500m < /path/to/incoming.pack
fatal: pack exceeds maximum allowed size

That error is expected only when the pack genuinely exceeds your chosen limit, so pick a number that fits the job rather than copying this one blindly. This check does not make a malformed pack safe, and it does not cap every resource used while processing a pack that gets accepted. Use normal filesystem quotas and operational isolation for anything genuinely hostile.

5. Unpack into the repository

Once the dry-run passes, send the pack to the target for real, without -n:

$ git -C /path/to/target unpack-objects -q < /path/to/incoming.pack
$ printf 'unpack status: %s\n' "$?"
unpack status: 0

-q suppresses the normal percentage progress; leave it off if you want to watch a large pack move. Objects already present in the target are skipped, so feeding a pack that already belongs there can look like nothing happened. That is normal, not proof the input was silently ignored.

Git may print notices if the target has no refs yet, such as an empty repository with an unborn HEAD. Those describe the target's refs, which this command does not create, so do not read them as failure.

6. Verify objects, then find the missing ref

Run Git's object check against the target:

$ git -C /path/to/target fsck --full --no-progress
Checking object directories: 100% (256/256), done.

Exact progress and diagnostic lines depend on the repository. What matters is exit status 0 and no corruption error. You can also count the objects directly:

$ find /path/to/target/.git/objects -mindepth 2 -maxdepth 2 -type f | wc -l
6

git log will not show a recovered commit until a ref points to it. Know the commit ID? Inspect it directly:

$ git -C /path/to/target cat-file -t COMMIT_ID
commit
$ git -C /path/to/target show --stat COMMIT_ID

Only after verifying the object should you restore a branch or tag, using the ref name and object ID from your own recovery record. git -C /path/to/target update-ref refs/heads/recovered COMMIT_ID changes repository state and deserves review as its own deliberate step. Pointed the wrong ref at it? Remove that exact ref with git -C /path/to/target update-ref -d refs/heads/recovered.

7. Handle corruption and security boundaries

By default, a corrupt pack stops at the first corruption. -r keeps going and tries to recover as many objects as it can. Treat that result as partial: run git fsck --full, compare the recovered object IDs against an independent copy, and do not build a release or restore a branch from it until the gaps are understood.

--strict refuses to write objects with broken content or links. Use it when you specifically need object validity guaranteed. Neither -r nor --strict repairs a damaged source or proves a commit trustworthy: a pack can contain perfectly valid objects from an unwanted or compromised history, so check its provenance before making any recovered ref visible to anyone else.

Warning

Do not overwrite the only copy of the pack, delete the original repository, or run recovery as root just to bypass an access error. Check ownership and permissions first. If you need to undo the object write, restore the target from the snapshot or disposable clone you took before step 5; deleting individual loose objects by hand is unsafe because other refs or objects may still depend on them.

Done means

  • The Git version, pack path and target repository were confirmed before anything ran.
  • A dry-run completed successfully before any objects were written.
  • The pack was unpacked through standard input, with a deliberate input-size limit where it mattered.
  • git fsck --full completed without corruption errors.
  • You understand unpacking creates objects, not refs, branches or working-tree files.
  • The original pack and a recovery snapshot remain available until the result is verified.