Archiving a repository without losing history Jump to heading
A repository that is finished with is rarely deleted deliberately — it is abandoned, which is worse. Nobody pushes to it, nobody watches it, and two years later someone pushes to it by accident and the work sits there unnoticed for a month. Meanwhile the knowledge of why it existed decays along with the ability to find it. Archiving is the deliberate version: read-only, discoverable, with evidence of its final state. This recipe covers it, within repository migration and consolidation.
When to use this approach Jump to heading
- A service has been decommissioned but its history may be needed.
- Code has moved to another repository and the old one should stop accepting work.
- A project is finished and you want it findable rather than deleted.
- Compliance requires retention of source history for a fixed period.
- If the repository is still receiving changes, it is not ready to archive — freeze it first and see whether anyone notices.
Step 1 — Record what exists before freezing Jump to heading
# Open branches and their last activity, so nothing in flight is lost silently
git for-each-ref --sort=-committerdate --format='%(committerdate:short) %(refname:short) %(authorname)' \
refs/remotes/origin | head -30 # Unmerged work, which is the only thing that would actually be lost
git fetch --prune origin
git branch -r --no-merged origin/main | sed 's|origin/||' | while read -r b; do
printf '%4s commits %s\n' "$(git rev-list --count "origin/main..origin/$b")" "$b"
done | sort -rn | head # Verification: anything unmerged has been acknowledged
git branch -r --no-merged origin/main | wc -l Unmerged branches are the decision point. Either they are merged, archived as tags, or explicitly abandoned — and abandoning them should be a sentence someone wrote, not an omission.
Step 2 — Leave a pointer that answers the obvious question Jump to heading
The first thing anyone finding an archived repository wants to know is where the code went.
cat > ARCHIVED.md <<'DOC'
# Archived
This repository is read-only as of 2026-09-18.
- The service it built was decommissioned on 2026-08-30.
- Its remaining code now lives in acme/platform under `services/legacy-billing/`.
- History here is retained for reference and audit; nothing here is deployed.
- Questions: the platform team.
DOC
git add ARCHIVED.md && git commit -m 'docs: mark the repository archived'
git push origin main # Verification: the note is the first thing visible
head -3 ARCHIVED.md Step 3 — Fix the final state with a signed tag Jump to heading
A signed annotated tag is evidence: it records the exact tree and commit, and who attested to it.
git tag -s archive/final -m 'Final state before archiving. Service decommissioned 2026-08-30.'
git push origin archive/final # Verification: the tag is annotated, signed, and verifies
git cat-file -t archive/final # expect: tag
git tag -v archive/final # Record the state in a form that does not depend on Git at all
git archive --format=tar archive/final | sha256sum > /tmp/final-tree.sha256
git rev-parse archive/final^{commit} >> /tmp/final-tree.sha256
cat /tmp/final-tree.sha256 The signing mechanics, including which key to use for something intended to be verifiable years later, are in signing and verifying release tags.
Step 4 — Store a mirror somewhere independent Jump to heading
An archive that exists only on the host you are archiving away from is not an archive.
git clone --mirror https://github.com/acme/legacy-billing.git legacy-billing.git
tar -czf legacy-billing-$(date +%Y%m%d).tar.gz legacy-billing.git
sha256sum legacy-billing-*.tar.gz > legacy-billing.sha256 # Verification: the archive restores to a working repository
mkdir -p /tmp/restore && tar -xzf legacy-billing-*.tar.gz -C /tmp/restore
git clone /tmp/restore/legacy-billing.git /tmp/restore/check
git -C /tmp/restore/check log --oneline -3
git -C /tmp/restore/check tag -v archive/final SAFETY WARNING — an untested archive is a guess. The restore check above is the difference between having a backup and believing you have one, and it takes a minute. Repeat it whenever the storage medium or the archive format changes, because the failure mode is discovering at the moment of need that the file has been unreadable for three years.
Step 5 — Make the original read-only Jump to heading
gh api -X PATCH repos/acme/legacy-billing -F archived=true # Verification: pushes are refused and reads still work
git push https://github.com/acme/legacy-billing.git HEAD:refs/heads/probe 2>&1 \
| grep -qi 'archiv\|read-only\|denied' && echo "read-only confirmed"
git ls-remote https://github.com/acme/legacy-billing.git | head -3 # Clean up integrations that will now fail noisily for no reason
gh api repos/acme/legacy-billing/hooks --jq '.[].id' | while read -r id; do
gh api -X DELETE "repos/acme/legacy-billing/hooks/$id"
done Removing webhooks and scheduled pipelines matters more than it seems: an archived repository whose nightly job keeps failing generates alerts that train people to ignore alerts.
Validation checklist Jump to heading
Frequently Asked Questions Jump to heading
Why keep it at all if the code moved? Jump to heading
Because history answers questions the current code cannot: why a decision was made, when a behaviour changed, who to ask. Those questions arrive unpredictably — during an incident, an audit, or a rewrite of the successor system — and the cost of retention is close to zero.
Is archiving on the forge enough? Jump to heading
It covers the read-only requirement and nothing else. Your account, your organisation and your provider are all single points of failure, and none of them guarantee a repository will be readable in five years. The independent mirror is what makes the archive an archive.
What about repositories containing secrets in their history? Jump to heading
Deal with that before archiving, not after — an archived repository is harder to rewrite and more likely to be copied around. The procedure is in removing a leaked secret from git history, and the credentials themselves must be rotated regardless of what the history says.
Related Jump to heading
- Repository Migration & Consolidation — the parent topic and the four shapes.
- Moving a Repository Between Hosting Providers — where archiving is the final step of a move.
- Signing and Verifying Release Tags — making the final-state tag verifiable years later.