Last modified: September 13, 2025
This article is written in: 🇺🇸
git archive
is your clean-room packager. It snapshots exactly what Git tracks at a commit—no .git
folder, no stray build junk, no temp files. This means you can hand someone a tidy source bundle or ship code to a server without dragging history along.
Think of it as a camera: you point it at a commit, click, and get a tar/zip of only the files that belong in that picture. It’s great for releases, vendor handoffs, monorepo subtrees, and “just give me the code” moments.
Because the archive is created from the repository’s tree, not your working directory, it’s consistent and repeatable, which makes ops and audits much happier.
+------------------------+ +------------------------+
| YOUR GIT REPOSITORY | | project.tar |
| | git archive | |
| .git/ src/ | ========> | src/ README |
| README docs/ | | docs/ |
| | | |
+------------------------+ +------------------------+
The left box is your repo with history + working files; the right box is the clean export—no .git
, only tracked content as of the chosen commit.
You pick a point in time (HEAD, a branch, a tag, a SHA), and you get a bundle. This is the “just give me today’s code” path.
# Zip of whatever you're on now
git archive -o project.zip HEAD
# Tar.gz from a release tag, with a friendly top-level folder name
git archive --format=tar --prefix=project-1.4.2/ v1.4.2 | gzip > project-1.4.2.tar.gz
# Peek inside without extracting
tar -tzf project-1.4.2.tar.gz | head
# output (example):
# project-1.4.2/
# project-1.4.2/README.md
# project-1.4.2/src/main.py
# project-1.4.2/src/utils/__init__.py
A tiny mental model:
commit (tree) ──► reproducible source package
^
└── HEAD / tag / SHA you chose
You don’t have to ship the whole repo. Target a file, a folder, or a pattern and keep the archive lean.
# One file from HEAD
git archive -o config.zip HEAD config.yml
# One directory
git archive -o api.zip HEAD services/api/
# Glob patterns (quote so your shell doesn’t expand it)
git archive -o py-src.zip HEAD ':(glob)src/**/*.py'
Visualization:
+------------------------+ git archive +-----------------------+
| BRANCH HEAD | ============> | config.zip |
| config.yml | | config.yml |
| src/ | | |
+------------------------+ +-----------------------+
A prefix puts everything under a single folder inside the archive—super handy for releases and avoiding file collisions when someone extracts.
git archive --prefix=project-name/ -o project.zip HEAD
+------------------------+ +------------------------------+
| YOUR GIT REPOSITORY | | project.zip |
| | git archive | |
| src/ README | ========> | project-name/src/ |
| docs/ | | project-name/README |
| | | |
+------------------------+ +------------------------------+
Because git archive
writes to stdout by default, you can compress and ship in one go. Great for deployments or quick backups.
archive_name="project-$(date +%F).tar.gz"
git archive main | gzip | ssh user@host "cat > /deploy/$archive_name"
# output (local):
# (no local file created; archive landed on remote host)
Remote server view:
[repo @ dev box] --stdout--> [gzip] --ssh--> [/deploy/project-2025-09-13.tar.gz]
1) Release tarball from a tag, nice folder name inside
git archive --format=tar --prefix=myapp-2.0.0/ v2.0.0 | gzip > myapp-2.0.0.tar.gz
2) Monorepo: ship just one service
git archive -o payments.zip HEAD services/payments/
3) Vendor handoff: exclude dev-only stuff using .gitattributes
Create rules once, every archive stays clean.
# .gitattributes (commit this)
docs/private/** export-ignore
*.psd export-ignore
scripts/dev/** export-ignore
VERSION export-subst
And if VERSION
contains a placeholder, it gets filled when you archive:
# VERSION file in repo
MyApp $Format:%h$ ($Format:%ci$)
git archive -o clean.zip HEAD
# After extracting clean.zip, VERSION might read:
# MyApp 9f3a1c2 (2025-09-13 09:45:12 +0000)
4) Zero-clone archive from a remote (server does the packing) Useful when you don’t want a local checkout.
git archive --remote=ssh://git@yourserver/opt/git/myrepo.git v1.4.2 | gzip > myrepo-1.4.2.tar.gz
# note: the remote must allow git-upload-archive over SSH
tar
→ project.tar
(uncompressed, fast on server-to-server)tar.gz
→ project.tar.gz
(common for releases)zip
→ project.zip
(nice for Windows users)Pick explicitly, or let -o
’s extension do the talking:
git archive --format=zip -o src.zip HEAD
git archive --format=tar HEAD > project.tar
You want the latest main
on a server, clean and quick.
# I. create tar from main and compress
git archive --format=tar main | gzip > project.tar.gz
# II. ship it
scp project.tar.gz user@server:/deployments
# III. unpack on the server
ssh user@server 'mkdir -p /apps/project && tar -xzf /deployments/project.tar.gz -C /apps/project'
Result: server gets only the tracked sources, with the exact tree from main
.
# List contents of a tar.gz without extracting
tar -tzf project.tar.gz | head -10
git-upload-archive
over SSH; most self-hosted setups do, some hosted platforms disable it.