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.
Git · Command
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
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.
git archive [--format=<fmt>] [--list] [--prefix=<prefix>/] [<extra>] [-o <file> | --output=<file>] [--worktree-attributes] [--remote=<repo> [--exec=<git-upload-archive>]] <tree-ish> [<path>...]| Name | Type | Description |
|---|---|---|
--format=<fmt> | string | Archive 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, --list | flag | Lists every format you can use. |
-v, --verbose | flag | Prints progress to stderr. |
--prefix=<prefix>/ | string | Adds <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> | path | Writes the archive to a file instead of stdout. |
--add-file=<file> | path | Puts 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> | string | Adds a regular file with the given path and inline content. Can be repeated. |
--worktree-attributes | flag | Also reads .gitattributes files from the working tree. |
--mtime=<time> | date | Sets the modification time stored on every archive entry. |
<extra> | backend options | Options for the archiver backend, such as compression level (-0 to -9 for zip, default -6). |
--remote=<repo> | repository | Gets the archive from a remote repository instead of building it locally. |
--exec=<git-upload-archive> | path | Used with --remote: the path to git-upload-archive on the remote side. |
Writes the archive bytes to stdout, or to the file given with -o/--output.
#!/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 --listThe 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.
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.
Send a clean snapshot of a commit, with no .git directory, to a server or into a Docker build context.
Package only HEAD:Documentation/ as a zip for publishing docs separately.
Use --remote to pull a tag's tree as a tarball without cloning the whole repository.
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.
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.
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-idgit upload-archivegitattributesgit bundlegit config