Skip to content
masterclass Buddy

Git · Command

git archive

git archive packs the files of one commit, tag or tree into a tar or zip archive. It works straight from repository objects and does not need a checkout, so it is the usual way to build source release tarballs.

Last updated

How it works

You give it a tree-ish (a branch, tag, commit, tree, or rev:path). Git walks that tree and streams an archive to stdout, or to a file if you pass -o. Built in, it can write tar, zip, tar.gz and tgz. Other formats work by piping tar output through a shell command set in tar.<format>.command. Optional paths cut the archive down to parts of the tree, and --prefix adds a leading directory to every entry. Before writing, Git checks the export-ignore and export-subst attributes. With --remote, the archive is built on the server by git-upload-archive and sent back to you.

Syntax

git archive [--format=<fmt>] [--list] [--prefix=<prefix>/] [<extra>] [-o <file> | --output=<file>] [--worktree-attributes] [--remote=<repo> [--exec=<git-upload-archive>]] <tree-ish> [<path>...]

Parameters

NameTypeDescription
--format=<fmt>stringArchive format: tar, zip, tar.gz, tgz, or a custom format set up with tar.<format>.command. Defaults to tar unless the output filename suggests something else.
-l, --listflagLists every format you can use.
-v, --verboseflagPrints progress to stderr.
--prefix=<prefix>/stringAdds <prefix>/ to the start of each path in the archive. You can repeat it; tracked files get the last value given.
-o <file>, --output=<file>pathWrites the archive to a file instead of stdout.
--add-file=<file>pathPuts an untracked file in the archive, named with the last preceding --prefix plus the file's basename. Can be repeated.
--add-virtual-file=<path>:<content>stringAdds a regular file with the given path and inline content. Can be repeated.
--worktree-attributesflagAlso reads .gitattributes files from the working tree.
--mtime=<time>dateSets the modification time stored on every archive entry.
<extra>backend optionsOptions for the archiver backend, such as compression level (-0 to -9 for zip, default -6).
--remote=<repo>repositoryGets the archive from a remote repository instead of building it locally.
--exec=<git-upload-archive>pathUsed with --remote: the path to git-upload-archive on the remote side.

Returns

Writes the archive bytes to stdout, or to the file given with -o/--output.

Key points

  • If you skip --format but give -o, Git picks the format from the file extension, so -o latest.zip produces a zip. With neither, you get tar.
  • Entry timestamps depend on what you pass. A commit or tag gives every file the commit time, so the timestamps are the same on every run. A bare tree ID gives the current time instead.
  • When you archive a commit as tar, Git records the commit ID in a pax header, and git get-tar-commit-id can read it back. In zip files the ID is stored as the file comment.
  • Mark files with the export-ignore attribute in .gitattributes to keep them out of archives. Typical examples are CI configs and test fixtures.
  • The export-subst attribute tells Git to fill in placeholders inside a file as it is archived, for example to stamp a version or commit hash into it.
  • You can add build outputs that aren't committed, such as a generated configure script, with --add-file. Its archive path comes from the last --prefix given before it.
  • For zip, a single-digit option sets the compression level. Higher numbers take longer but make smaller files.
  • tar.umask sets the permission bits stored in tar entries. The default 0002 removes the world-write bit.

Common mistakes

  • Git does not clean up --prefix. Values containing ../ or a leading slash go into the archive exactly as typed, and some extractors will warn about them or rewrite them.
  • With --remote, the server may reject some revision expressions, such as a raw SHA or master^. Refs and ref:path usually work.
  • Attributes are read from the commit being archived, not from your working tree. To fix a missing export-ignore after the fact, edit .gitattributes in the working tree and pass --worktree-attributes.
  • --add-virtual-file ignores --prefix, so you must write the full path inside the archive yourself.

Example: Build a release tarball and a docs zip from a tag

#!/usr/bin/env bash
set -euo pipefail

TAG=v1.4.0
NAME=myproj-${TAG#v}

# Keep dev-only files out of release archives
printf '.github/ export-ignore\ntests/ export-ignore\n' >> .gitattributes

# Gzipped tarball; format inferred from extension
git archive --prefix="$NAME/" -o "$NAME.tar.gz" "$TAG"

# Only the docs/ subtree, as zip at max compression
git archive --format=zip -9 --prefix=docs/ -o "$NAME-docs.zip" "$TAG:docs/"

# Read back the embedded commit ID
gzip -dc "$NAME.tar.gz" | git get-tar-commit-id

# List available formats
git archive --list

The script builds a prefixed .tar.gz of a tag and a zip of just its docs subtree, then reads back the commit ID stored in the tarball. Note that export-ignore only takes effect once .gitattributes is committed in the tagged tree, or if you pass --worktree-attributes.

When to use it

Source release tarballs

Make myproj-1.2.0.tar.gz from tag v1.2.0, with every file under myproj-1.2.0/, to attach to a release.

Lightweight deploy artifacts

Send a clean snapshot of a commit, with no .git directory, to a server or into a Docker build context.

Shipping a subdirectory

Package only HEAD:Documentation/ as a zip for publishing docs separately.

Fetching a snapshot from a server

Use --remote to pull a tag's tree as a tarball without cloning the whole repository.

Interview questions

QHow is git archive different from cloning a repo and zipping the folder?

git archive reads the tree straight from the object database, so there's no .git directory, no untracked files and no local edits in the output. It applies export-ignore and export-subst, can add a top-level prefix directory, and when given a commit it stamps every entry with the commit time.

Follow-up: Why are two archives of the same commit usually byte-identical, but archives of the same tree ID aren't? Because a commit or tag gives every entry its commit timestamp, while a bare tree ID has no commit, so the current time is used.

QHow would you keep CI configuration and tests out of release tarballs?

Mark those paths with export-ignore in .gitattributes and commit the change, because attributes come from the tree being archived. For a commit that is already tagged, edit the working-tree .gitattributes and run git archive with --worktree-attributes, or put the rules in $GIT_DIR/info/attributes.

Follow-up: How can you embed the commit hash in a file inside the archive? Set export-subst on that file, and Git replaces its placeholders (as described in gitattributes) when it writes the file into the archive.

QWhat happens when you run git archive --remote=origin master^?

The remote's git-upload-archive will usually refuse it. To protect unreachable objects, it only accepts refs and ref:path forms, not relative expressions or raw SHAs. Use a ref such as a tag or branch name instead.

Follow-up: How can a server admin relax that rule? Set uploadArchive.allowUnreachable=true on the server, which lets clients use any SHA-1 expression.

  • git get-tar-commit-id
  • git upload-archive
  • gitattributes
  • git bundle
  • git config

Source: Git - git-archive Documentation. Checked 2026-11-02.