Skip to main content

firnpack

a glacier for your files — every commit a single packed layer of history.

ci python

firnpack is version control for people who don't want version control machinery. If you work on small projects, on one computer, and you've ever wished you could just save a version and get back to it later — without staging areas, remotes, or a .git folder that grows thousands of tiny files on a tired hard drive — firnpack is for you.

Every commit is one file: a compressed, checksummed, immutable archive of your whole project, stacked on the ones before it. A repository with 100 commits holds 102 files. That's the whole design.

Install

Requires Python 3.13.16 or newer. From a clone:

pip install .

which registers both firnpack and the shorter firn command. (Or run the single module directly: python firnpack.py … — the whole engine is one file.)

Quickstart

cd my-project
firnpack init
firnpack commit              # packs everything — that's it
# ...work...
firnpack commit              # a new layer
firnpack status              # what changed since the last pack
firnpack history             # every saved version, newest first
firnpack diff                # what exactly changed, line by line

That's the whole daily loop: five commands, no staging, no jargon you have to look up. Misspelled a commit message? firnpack amend -m "fixed" replaces the head layer without adding a new one.

Commands

Command What it does
firnpack init [PATH] create a repository in this folder (or PATH)
firnpack commit [-m MSG] pack the working tree into a new layer
firnpack status [--full] [--ignored] what changed since the last pack
firnpack history [-n N] [--all] [BRANCH] saved versions, newest first
firnpack diff line-by-line changes vs the last pack
firnpack branch NAME [FROM] create a branch (at HEAD, or at FROM)
firnpack switch REF move to a branch, or pin to a layer id
firnpack list-branches branches and their heads
firnpack rename-branch OLD NEW rename a branch
firnpack delete-branch NAME delete a branch (warns if heads orphan)
firnpack merge BRANCH [--abort] merge a branch in; abort a pending one
firnpack amend [-m MSG] replace the head layer (same parents)
firnpack restore REF [--to DIR] bring a layer back over the worktree
firnpack export REF --to DIR extract a layer to a fresh directory
firnpack verify re-hash every layer; check database consistency
firnpack compact --keep N [--dry-run] [--yes] [--archive DIR] squash old layers, reclaim disk

Every command accepts --json for machine-readable output — firnpack is designed to be operated by people and by agents; see docs/agent-contract.md for the normative contract.

Branching and merging

Branches are names for heads; nothing else. branch creates one (staying where you are), switch moves you — to a branch, or to a pinned layer id when you hand it one. merge is a plain 3-way merge: clean trees merge into one layer that remembers both parents; conflicting trees leave git-style markers in the files and a durable "merge in progress" state that status reports and merge --abort clears. A merge never clobbers uncommitted work: with drift in the worktree it refuses instead.

Undo, rescue, explore

  • amend replaces the head layer — fix a message or a file without stacking another version on top. The replaced layer stays on disk until a compact collects it.
  • restore REF brings any layer back over the worktree. If you have uncommitted work, firnpack rescues it first as its own layer and tells you — restore never destroys drift.
  • export REF --to DIR extracts a layer into a fresh directory and touches nothing else: the zero-risk way to look at an old version.

Checking the glacier

firnpack verify re-hashes every layer against its own filename, cross-checks the database, and reports dangling references or orphan files. It is the answer to "is my history still sound?" after a power cut, a disk scare, or a copy between machines. If the database itself is lost or corrupt, verify --rebuild-db reconstructs it from the layers themselves — and says plainly which branch names could not be recovered.

History management: compact

Whole-tree layers are the honest price of a glacier you can verify one file at a time. When history grows past comfort, compact --keep N squashes everything below the newest N layers into one foundation layer and reclaims the disk.

It is never automatic and never silent:

  • --keep N is required every time — there is no default to trip over;
  • --dry-run prints the full plan (what drops, what rewrites, what gets collected, peak disk needed) and changes nothing;
  • real runs require --yes;
  • --archive DIR exports every layer whose content is about to disappear into a zip before anything is deleted;
  • branches that point into the squashed range refuse the compact by name; branches that sit on rewritten layers follow the rewrite, and the plan and the output say exactly which branch moved where.

A reminder line after commit tells you when the glacier crosses 50 layers. Nothing is ever compacted behind your back.

Ignoring junk

firnpack reads the standard .gitignore file at your project root, so the ignore rules you already have keep working. If you have none, init seeds one line for you: ~$* — the hidden lock files office tools leave behind. Excluded files are never silently hidden: status counts them, and status --ignored lists every one.

Safety, in plain words

  • A commit either happens completely or not at all. A power cut mid-pack leaves your repository exactly as it was.
  • Every layer carries its own checksum, and the layer's name is that checksum — tampering or disk rot is detectable by re-reading.
  • Extraction of a layer is guarded: a corrupt or hostile archive can never write outside your project folder, no matter what paths it claims to contain.
  • No command destroys uncommitted work: merge, switch and restore all refuse on drift, or rescue it first and say so.
  • History rewrites (compact) require explicit permission, report everything they move, and archive what they delete.

Documentation

Status

v1.0 candidate: the full command surface above is implemented and tested (unit, property and end-to-end CLI suites, CI across Linux, Windows and macOS). PyPI publication follows the v1.0.0 tag.

License

MIT — see LICENSE.

Metadata

Release files for firnpack 1.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for firnpack 1.0.0
File Size Uploaded Provenance
firnpack-1.0.0.tar.gz 64.3 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for firnpack 1.0.0
File Interpreter ABI Platform
firnpack-1.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 100.1 kB

Release files / firnpack-1.0.0.tar.gz

Download URL firnpack-1.0.0.tar.gz
Size 64.3 kB
Tags Source
SHA-256 checksum
How to use checksums
7428eef82d8340e3e2dc6d6c6d238133d18f6e67aee9f1e3a43092abbcaeabef
BLAKE2b-256 checksum
How to use checksums
12f7a8f15be289aa89277ed027bae45c116cee72d23744d3fe62460b5c92bc0a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.

Transparency log

Release files / firnpack-1.0.0-py3-none-any.whl

Download URL firnpack-1.0.0-py3-none-any.whl
Size 35.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e7662b5d0f7ccca30c0e90dfdc3e7128c0c1c63f5d331fccc7c7d0dda1d2d4e2
BLAKE2b-256 checksum
How to use checksums
9018cc289c23028bf25f3a2b62c5df1a8729536200f4554705fd105065ef56e5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.

Transparency log

Security for firnpack 1.0.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for firnpack-1.0.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Release history Release notifications | RSS feed

This release

1.0.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page