firnpack 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
firnpack-1.0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Fabrizio Hernandez
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,192 @@
1
+ Metadata-Version: 2.4
2
+ Name: firnpack
3
+ Version: 1.0.0
4
+ Summary: a glacier for your files — every commit a single packed layer of history.
5
+ Author: Fabrizio Hernandez
6
+ License-Expression: MIT
7
+ Keywords: version control,vcs,backup,single-user,archive
8
+ Classifier: Development Status :: 5 - Production/Stable
9
+ Classifier: Environment :: Console
10
+ Classifier: Intended Audience :: End Users/Desktop
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Programming Language :: Python :: 3 :: Only
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Classifier: Programming Language :: Python :: 3.14
15
+ Classifier: Programming Language :: Python :: 3.15
16
+ Classifier: Topic :: System :: Archiving :: Backup
17
+ Classifier: Topic :: Software Development :: Version Control
18
+ Requires-Python: >=3.13.16
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Provides-Extra: dev
22
+ Requires-Dist: pytest>=8; extra == "dev"
23
+ Requires-Dist: ruff>=0.8; extra == "dev"
24
+ Dynamic: license-file
25
+
26
+ # firnpack
27
+
28
+ > a glacier for your files — every commit a single packed layer of history.
29
+
30
+ [![ci](https://github.com/fabzter/firnpack/actions/workflows/ci.yml/badge.svg)](https://github.com/fabzter/firnpack/actions/workflows/ci.yml)
31
+ ![python](https://img.shields.io/badge/python-3.13.16%20%E2%80%93%203.15-blue)
32
+
33
+ **firnpack** is version control for people who don't want version control
34
+ machinery. If you work on small projects, on one computer, and you've ever
35
+ wished you could just *save* a version and get back to it later — without
36
+ staging areas, remotes, or a `.git` folder that grows thousands of tiny files
37
+ on a tired hard drive — firnpack is for you.
38
+
39
+ Every commit is one file: a compressed, checksummed, immutable archive of your
40
+ whole project, stacked on the ones before it. A repository with 100 commits
41
+ holds 102 files. That's the whole design.
42
+
43
+ ## Install
44
+
45
+ Requires Python 3.13.16 or newer. From a clone:
46
+
47
+ ```
48
+ pip install .
49
+ ```
50
+
51
+ which registers both `firnpack` and the shorter `firn` command. (Or run the
52
+ single module directly: `python firnpack.py …` — the whole engine is one
53
+ file.)
54
+
55
+ ## Quickstart
56
+
57
+ ```
58
+ cd my-project
59
+ firnpack init
60
+ firnpack commit # packs everything — that's it
61
+ # ...work...
62
+ firnpack commit # a new layer
63
+ firnpack status # what changed since the last pack
64
+ firnpack history # every saved version, newest first
65
+ firnpack diff # what exactly changed, line by line
66
+ ```
67
+
68
+ That's the whole daily loop: five commands, no staging, no jargon you have
69
+ to look up. Misspelled a commit message? `firnpack amend -m "fixed"` replaces
70
+ the head layer without adding a new one.
71
+
72
+ ## Commands
73
+
74
+ | Command | What it does |
75
+ |---|---|
76
+ | `firnpack init [PATH]` | create a repository in this folder (or PATH) |
77
+ | `firnpack commit [-m MSG]` | pack the working tree into a new layer |
78
+ | `firnpack status [--full] [--ignored]` | what changed since the last pack |
79
+ | `firnpack history [-n N] [--all] [BRANCH]` | saved versions, newest first |
80
+ | `firnpack diff` | line-by-line changes vs the last pack |
81
+ | `firnpack branch NAME [FROM]` | create a branch (at HEAD, or at FROM) |
82
+ | `firnpack switch REF` | move to a branch, or pin to a layer id |
83
+ | `firnpack list-branches` | branches and their heads |
84
+ | `firnpack rename-branch OLD NEW` | rename a branch |
85
+ | `firnpack delete-branch NAME` | delete a branch (warns if heads orphan) |
86
+ | `firnpack merge BRANCH [--abort]` | merge a branch in; abort a pending one |
87
+ | `firnpack amend [-m MSG]` | replace the head layer (same parents) |
88
+ | `firnpack restore REF [--to DIR]` | bring a layer back over the worktree |
89
+ | `firnpack export REF --to DIR` | extract a layer to a fresh directory |
90
+ | `firnpack verify` | re-hash every layer; check database consistency |
91
+ | `firnpack compact --keep N [--dry-run] [--yes] [--archive DIR]` | squash old layers, reclaim disk |
92
+
93
+ Every command accepts `--json` for machine-readable output — firnpack is
94
+ designed to be operated by people *and* by agents; see
95
+ [`docs/agent-contract.md`](docs/agent-contract.md) for the normative
96
+ contract.
97
+
98
+ ## Branching and merging
99
+
100
+ Branches are names for heads; nothing else. `branch` creates one (staying
101
+ where you are), `switch` moves you — to a branch, or to a pinned layer id
102
+ when you hand it one. `merge` is a plain 3-way merge: clean trees merge into
103
+ one layer that remembers both parents; conflicting trees leave git-style
104
+ markers in the files and a durable "merge in progress" state that `status`
105
+ reports and `merge --abort` clears. A merge never clobbers uncommitted work:
106
+ with drift in the worktree it refuses instead.
107
+
108
+ ## Undo, rescue, explore
109
+
110
+ - **`amend`** replaces the head layer — fix a message or a file without
111
+ stacking another version on top. The replaced layer stays on disk until a
112
+ `compact` collects it.
113
+ - **`restore REF`** brings any layer back over the worktree. If you have
114
+ uncommitted work, firnpack **rescues it first** as its own layer and tells
115
+ you — restore never destroys drift.
116
+ - **`export REF --to DIR`** extracts a layer into a fresh directory and
117
+ touches nothing else: the zero-risk way to look at an old version.
118
+
119
+ ## Checking the glacier
120
+
121
+ `firnpack verify` re-hashes every layer against its own filename, cross-checks
122
+ the database, and reports dangling references or orphan files. It is the
123
+ answer to "is my history still sound?" after a power cut, a disk scare, or a
124
+ copy between machines. If the database itself is lost or corrupt,
125
+ `verify --rebuild-db` reconstructs it from the layers themselves — and says
126
+ plainly which branch names could not be recovered.
127
+
128
+ ## History management: compact
129
+
130
+ Whole-tree layers are the honest price of a glacier you can verify one file
131
+ at a time. When history grows past comfort, `compact --keep N` squashes
132
+ everything below the newest N layers into one foundation layer and reclaims
133
+ the disk.
134
+
135
+ It is **never automatic and never silent**:
136
+
137
+ - `--keep N` is required every time — there is no default to trip over;
138
+ - `--dry-run` prints the full plan (what drops, what rewrites, what gets
139
+ collected, peak disk needed) and changes nothing;
140
+ - real runs require `--yes`;
141
+ - `--archive DIR` exports every layer whose content is about to disappear
142
+ into a zip before anything is deleted;
143
+ - branches that point into the squashed range refuse the compact by name;
144
+ branches that sit on rewritten layers follow the rewrite, and the plan and
145
+ the output say exactly which branch moved where.
146
+
147
+ A reminder line after `commit` tells you when the glacier crosses 50 layers.
148
+ Nothing is ever compacted behind your back.
149
+
150
+ ## Ignoring junk
151
+
152
+ firnpack reads the standard `.gitignore` file at your project root, so the
153
+ ignore rules you already have keep working. If you have none, `init` seeds
154
+ one line for you: `~$*` — the hidden lock files office tools leave behind.
155
+ Excluded files are never silently hidden: `status` counts them, and
156
+ `status --ignored` lists every one.
157
+
158
+ ## Safety, in plain words
159
+
160
+ - A commit either happens completely or not at all. A power cut mid-pack
161
+ leaves your repository exactly as it was.
162
+ - Every layer carries its own checksum, and the layer's *name* is that
163
+ checksum — tampering or disk rot is detectable by re-reading.
164
+ - Extraction of a layer is guarded: a corrupt or hostile archive can never
165
+ write outside your project folder, no matter what paths it claims to
166
+ contain.
167
+ - No command destroys uncommitted work: merge, switch and restore all refuse
168
+ on drift, or rescue it first and say so.
169
+ - History rewrites (compact) require explicit permission, report everything
170
+ they move, and archive what they delete.
171
+
172
+ ## Documentation
173
+
174
+ - [`docs/usage.md`](docs/usage.md) — every command: examples, exit codes,
175
+ JSON shapes
176
+ - [`docs/agent-contract.md`](docs/agent-contract.md) — the normative contract
177
+ for automated operators
178
+ - [`docs/storage.md`](docs/storage.md) — layer format, database schema,
179
+ recovery playbook
180
+ - [`docs/decisions.md`](docs/decisions.md) — the decision log
181
+ - [`SPEC.md`](SPEC.md) — the full specification; [`research/`](research/) —
182
+ the research behind it
183
+
184
+ ## Status
185
+
186
+ v1.0 candidate: the full command surface above is implemented and tested
187
+ (unit, property and end-to-end CLI suites, CI across Linux, Windows and
188
+ macOS). PyPI publication follows the v1.0.0 tag.
189
+
190
+ ## License
191
+
192
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,167 @@
1
+ # firnpack
2
+
3
+ > a glacier for your files — every commit a single packed layer of history.
4
+
5
+ [![ci](https://github.com/fabzter/firnpack/actions/workflows/ci.yml/badge.svg)](https://github.com/fabzter/firnpack/actions/workflows/ci.yml)
6
+ ![python](https://img.shields.io/badge/python-3.13.16%20%E2%80%93%203.15-blue)
7
+
8
+ **firnpack** is version control for people who don't want version control
9
+ machinery. If you work on small projects, on one computer, and you've ever
10
+ wished you could just *save* a version and get back to it later — without
11
+ staging areas, remotes, or a `.git` folder that grows thousands of tiny files
12
+ on a tired hard drive — firnpack is for you.
13
+
14
+ Every commit is one file: a compressed, checksummed, immutable archive of your
15
+ whole project, stacked on the ones before it. A repository with 100 commits
16
+ holds 102 files. That's the whole design.
17
+
18
+ ## Install
19
+
20
+ Requires Python 3.13.16 or newer. From a clone:
21
+
22
+ ```
23
+ pip install .
24
+ ```
25
+
26
+ which registers both `firnpack` and the shorter `firn` command. (Or run the
27
+ single module directly: `python firnpack.py …` — the whole engine is one
28
+ file.)
29
+
30
+ ## Quickstart
31
+
32
+ ```
33
+ cd my-project
34
+ firnpack init
35
+ firnpack commit # packs everything — that's it
36
+ # ...work...
37
+ firnpack commit # a new layer
38
+ firnpack status # what changed since the last pack
39
+ firnpack history # every saved version, newest first
40
+ firnpack diff # what exactly changed, line by line
41
+ ```
42
+
43
+ That's the whole daily loop: five commands, no staging, no jargon you have
44
+ to look up. Misspelled a commit message? `firnpack amend -m "fixed"` replaces
45
+ the head layer without adding a new one.
46
+
47
+ ## Commands
48
+
49
+ | Command | What it does |
50
+ |---|---|
51
+ | `firnpack init [PATH]` | create a repository in this folder (or PATH) |
52
+ | `firnpack commit [-m MSG]` | pack the working tree into a new layer |
53
+ | `firnpack status [--full] [--ignored]` | what changed since the last pack |
54
+ | `firnpack history [-n N] [--all] [BRANCH]` | saved versions, newest first |
55
+ | `firnpack diff` | line-by-line changes vs the last pack |
56
+ | `firnpack branch NAME [FROM]` | create a branch (at HEAD, or at FROM) |
57
+ | `firnpack switch REF` | move to a branch, or pin to a layer id |
58
+ | `firnpack list-branches` | branches and their heads |
59
+ | `firnpack rename-branch OLD NEW` | rename a branch |
60
+ | `firnpack delete-branch NAME` | delete a branch (warns if heads orphan) |
61
+ | `firnpack merge BRANCH [--abort]` | merge a branch in; abort a pending one |
62
+ | `firnpack amend [-m MSG]` | replace the head layer (same parents) |
63
+ | `firnpack restore REF [--to DIR]` | bring a layer back over the worktree |
64
+ | `firnpack export REF --to DIR` | extract a layer to a fresh directory |
65
+ | `firnpack verify` | re-hash every layer; check database consistency |
66
+ | `firnpack compact --keep N [--dry-run] [--yes] [--archive DIR]` | squash old layers, reclaim disk |
67
+
68
+ Every command accepts `--json` for machine-readable output — firnpack is
69
+ designed to be operated by people *and* by agents; see
70
+ [`docs/agent-contract.md`](docs/agent-contract.md) for the normative
71
+ contract.
72
+
73
+ ## Branching and merging
74
+
75
+ Branches are names for heads; nothing else. `branch` creates one (staying
76
+ where you are), `switch` moves you — to a branch, or to a pinned layer id
77
+ when you hand it one. `merge` is a plain 3-way merge: clean trees merge into
78
+ one layer that remembers both parents; conflicting trees leave git-style
79
+ markers in the files and a durable "merge in progress" state that `status`
80
+ reports and `merge --abort` clears. A merge never clobbers uncommitted work:
81
+ with drift in the worktree it refuses instead.
82
+
83
+ ## Undo, rescue, explore
84
+
85
+ - **`amend`** replaces the head layer — fix a message or a file without
86
+ stacking another version on top. The replaced layer stays on disk until a
87
+ `compact` collects it.
88
+ - **`restore REF`** brings any layer back over the worktree. If you have
89
+ uncommitted work, firnpack **rescues it first** as its own layer and tells
90
+ you — restore never destroys drift.
91
+ - **`export REF --to DIR`** extracts a layer into a fresh directory and
92
+ touches nothing else: the zero-risk way to look at an old version.
93
+
94
+ ## Checking the glacier
95
+
96
+ `firnpack verify` re-hashes every layer against its own filename, cross-checks
97
+ the database, and reports dangling references or orphan files. It is the
98
+ answer to "is my history still sound?" after a power cut, a disk scare, or a
99
+ copy between machines. If the database itself is lost or corrupt,
100
+ `verify --rebuild-db` reconstructs it from the layers themselves — and says
101
+ plainly which branch names could not be recovered.
102
+
103
+ ## History management: compact
104
+
105
+ Whole-tree layers are the honest price of a glacier you can verify one file
106
+ at a time. When history grows past comfort, `compact --keep N` squashes
107
+ everything below the newest N layers into one foundation layer and reclaims
108
+ the disk.
109
+
110
+ It is **never automatic and never silent**:
111
+
112
+ - `--keep N` is required every time — there is no default to trip over;
113
+ - `--dry-run` prints the full plan (what drops, what rewrites, what gets
114
+ collected, peak disk needed) and changes nothing;
115
+ - real runs require `--yes`;
116
+ - `--archive DIR` exports every layer whose content is about to disappear
117
+ into a zip before anything is deleted;
118
+ - branches that point into the squashed range refuse the compact by name;
119
+ branches that sit on rewritten layers follow the rewrite, and the plan and
120
+ the output say exactly which branch moved where.
121
+
122
+ A reminder line after `commit` tells you when the glacier crosses 50 layers.
123
+ Nothing is ever compacted behind your back.
124
+
125
+ ## Ignoring junk
126
+
127
+ firnpack reads the standard `.gitignore` file at your project root, so the
128
+ ignore rules you already have keep working. If you have none, `init` seeds
129
+ one line for you: `~$*` — the hidden lock files office tools leave behind.
130
+ Excluded files are never silently hidden: `status` counts them, and
131
+ `status --ignored` lists every one.
132
+
133
+ ## Safety, in plain words
134
+
135
+ - A commit either happens completely or not at all. A power cut mid-pack
136
+ leaves your repository exactly as it was.
137
+ - Every layer carries its own checksum, and the layer's *name* is that
138
+ checksum — tampering or disk rot is detectable by re-reading.
139
+ - Extraction of a layer is guarded: a corrupt or hostile archive can never
140
+ write outside your project folder, no matter what paths it claims to
141
+ contain.
142
+ - No command destroys uncommitted work: merge, switch and restore all refuse
143
+ on drift, or rescue it first and say so.
144
+ - History rewrites (compact) require explicit permission, report everything
145
+ they move, and archive what they delete.
146
+
147
+ ## Documentation
148
+
149
+ - [`docs/usage.md`](docs/usage.md) — every command: examples, exit codes,
150
+ JSON shapes
151
+ - [`docs/agent-contract.md`](docs/agent-contract.md) — the normative contract
152
+ for automated operators
153
+ - [`docs/storage.md`](docs/storage.md) — layer format, database schema,
154
+ recovery playbook
155
+ - [`docs/decisions.md`](docs/decisions.md) — the decision log
156
+ - [`SPEC.md`](SPEC.md) — the full specification; [`research/`](research/) —
157
+ the research behind it
158
+
159
+ ## Status
160
+
161
+ v1.0 candidate: the full command surface above is implemented and tested
162
+ (unit, property and end-to-end CLI suites, CI across Linux, Windows and
163
+ macOS). PyPI publication follows the v1.0.0 tag.
164
+
165
+ ## License
166
+
167
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,192 @@
1
+ Metadata-Version: 2.4
2
+ Name: firnpack
3
+ Version: 1.0.0
4
+ Summary: a glacier for your files — every commit a single packed layer of history.
5
+ Author: Fabrizio Hernandez
6
+ License-Expression: MIT
7
+ Keywords: version control,vcs,backup,single-user,archive
8
+ Classifier: Development Status :: 5 - Production/Stable
9
+ Classifier: Environment :: Console
10
+ Classifier: Intended Audience :: End Users/Desktop
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Programming Language :: Python :: 3 :: Only
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Classifier: Programming Language :: Python :: 3.14
15
+ Classifier: Programming Language :: Python :: 3.15
16
+ Classifier: Topic :: System :: Archiving :: Backup
17
+ Classifier: Topic :: Software Development :: Version Control
18
+ Requires-Python: >=3.13.16
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Provides-Extra: dev
22
+ Requires-Dist: pytest>=8; extra == "dev"
23
+ Requires-Dist: ruff>=0.8; extra == "dev"
24
+ Dynamic: license-file
25
+
26
+ # firnpack
27
+
28
+ > a glacier for your files — every commit a single packed layer of history.
29
+
30
+ [![ci](https://github.com/fabzter/firnpack/actions/workflows/ci.yml/badge.svg)](https://github.com/fabzter/firnpack/actions/workflows/ci.yml)
31
+ ![python](https://img.shields.io/badge/python-3.13.16%20%E2%80%93%203.15-blue)
32
+
33
+ **firnpack** is version control for people who don't want version control
34
+ machinery. If you work on small projects, on one computer, and you've ever
35
+ wished you could just *save* a version and get back to it later — without
36
+ staging areas, remotes, or a `.git` folder that grows thousands of tiny files
37
+ on a tired hard drive — firnpack is for you.
38
+
39
+ Every commit is one file: a compressed, checksummed, immutable archive of your
40
+ whole project, stacked on the ones before it. A repository with 100 commits
41
+ holds 102 files. That's the whole design.
42
+
43
+ ## Install
44
+
45
+ Requires Python 3.13.16 or newer. From a clone:
46
+
47
+ ```
48
+ pip install .
49
+ ```
50
+
51
+ which registers both `firnpack` and the shorter `firn` command. (Or run the
52
+ single module directly: `python firnpack.py …` — the whole engine is one
53
+ file.)
54
+
55
+ ## Quickstart
56
+
57
+ ```
58
+ cd my-project
59
+ firnpack init
60
+ firnpack commit # packs everything — that's it
61
+ # ...work...
62
+ firnpack commit # a new layer
63
+ firnpack status # what changed since the last pack
64
+ firnpack history # every saved version, newest first
65
+ firnpack diff # what exactly changed, line by line
66
+ ```
67
+
68
+ That's the whole daily loop: five commands, no staging, no jargon you have
69
+ to look up. Misspelled a commit message? `firnpack amend -m "fixed"` replaces
70
+ the head layer without adding a new one.
71
+
72
+ ## Commands
73
+
74
+ | Command | What it does |
75
+ |---|---|
76
+ | `firnpack init [PATH]` | create a repository in this folder (or PATH) |
77
+ | `firnpack commit [-m MSG]` | pack the working tree into a new layer |
78
+ | `firnpack status [--full] [--ignored]` | what changed since the last pack |
79
+ | `firnpack history [-n N] [--all] [BRANCH]` | saved versions, newest first |
80
+ | `firnpack diff` | line-by-line changes vs the last pack |
81
+ | `firnpack branch NAME [FROM]` | create a branch (at HEAD, or at FROM) |
82
+ | `firnpack switch REF` | move to a branch, or pin to a layer id |
83
+ | `firnpack list-branches` | branches and their heads |
84
+ | `firnpack rename-branch OLD NEW` | rename a branch |
85
+ | `firnpack delete-branch NAME` | delete a branch (warns if heads orphan) |
86
+ | `firnpack merge BRANCH [--abort]` | merge a branch in; abort a pending one |
87
+ | `firnpack amend [-m MSG]` | replace the head layer (same parents) |
88
+ | `firnpack restore REF [--to DIR]` | bring a layer back over the worktree |
89
+ | `firnpack export REF --to DIR` | extract a layer to a fresh directory |
90
+ | `firnpack verify` | re-hash every layer; check database consistency |
91
+ | `firnpack compact --keep N [--dry-run] [--yes] [--archive DIR]` | squash old layers, reclaim disk |
92
+
93
+ Every command accepts `--json` for machine-readable output — firnpack is
94
+ designed to be operated by people *and* by agents; see
95
+ [`docs/agent-contract.md`](docs/agent-contract.md) for the normative
96
+ contract.
97
+
98
+ ## Branching and merging
99
+
100
+ Branches are names for heads; nothing else. `branch` creates one (staying
101
+ where you are), `switch` moves you — to a branch, or to a pinned layer id
102
+ when you hand it one. `merge` is a plain 3-way merge: clean trees merge into
103
+ one layer that remembers both parents; conflicting trees leave git-style
104
+ markers in the files and a durable "merge in progress" state that `status`
105
+ reports and `merge --abort` clears. A merge never clobbers uncommitted work:
106
+ with drift in the worktree it refuses instead.
107
+
108
+ ## Undo, rescue, explore
109
+
110
+ - **`amend`** replaces the head layer — fix a message or a file without
111
+ stacking another version on top. The replaced layer stays on disk until a
112
+ `compact` collects it.
113
+ - **`restore REF`** brings any layer back over the worktree. If you have
114
+ uncommitted work, firnpack **rescues it first** as its own layer and tells
115
+ you — restore never destroys drift.
116
+ - **`export REF --to DIR`** extracts a layer into a fresh directory and
117
+ touches nothing else: the zero-risk way to look at an old version.
118
+
119
+ ## Checking the glacier
120
+
121
+ `firnpack verify` re-hashes every layer against its own filename, cross-checks
122
+ the database, and reports dangling references or orphan files. It is the
123
+ answer to "is my history still sound?" after a power cut, a disk scare, or a
124
+ copy between machines. If the database itself is lost or corrupt,
125
+ `verify --rebuild-db` reconstructs it from the layers themselves — and says
126
+ plainly which branch names could not be recovered.
127
+
128
+ ## History management: compact
129
+
130
+ Whole-tree layers are the honest price of a glacier you can verify one file
131
+ at a time. When history grows past comfort, `compact --keep N` squashes
132
+ everything below the newest N layers into one foundation layer and reclaims
133
+ the disk.
134
+
135
+ It is **never automatic and never silent**:
136
+
137
+ - `--keep N` is required every time — there is no default to trip over;
138
+ - `--dry-run` prints the full plan (what drops, what rewrites, what gets
139
+ collected, peak disk needed) and changes nothing;
140
+ - real runs require `--yes`;
141
+ - `--archive DIR` exports every layer whose content is about to disappear
142
+ into a zip before anything is deleted;
143
+ - branches that point into the squashed range refuse the compact by name;
144
+ branches that sit on rewritten layers follow the rewrite, and the plan and
145
+ the output say exactly which branch moved where.
146
+
147
+ A reminder line after `commit` tells you when the glacier crosses 50 layers.
148
+ Nothing is ever compacted behind your back.
149
+
150
+ ## Ignoring junk
151
+
152
+ firnpack reads the standard `.gitignore` file at your project root, so the
153
+ ignore rules you already have keep working. If you have none, `init` seeds
154
+ one line for you: `~$*` — the hidden lock files office tools leave behind.
155
+ Excluded files are never silently hidden: `status` counts them, and
156
+ `status --ignored` lists every one.
157
+
158
+ ## Safety, in plain words
159
+
160
+ - A commit either happens completely or not at all. A power cut mid-pack
161
+ leaves your repository exactly as it was.
162
+ - Every layer carries its own checksum, and the layer's *name* is that
163
+ checksum — tampering or disk rot is detectable by re-reading.
164
+ - Extraction of a layer is guarded: a corrupt or hostile archive can never
165
+ write outside your project folder, no matter what paths it claims to
166
+ contain.
167
+ - No command destroys uncommitted work: merge, switch and restore all refuse
168
+ on drift, or rescue it first and say so.
169
+ - History rewrites (compact) require explicit permission, report everything
170
+ they move, and archive what they delete.
171
+
172
+ ## Documentation
173
+
174
+ - [`docs/usage.md`](docs/usage.md) — every command: examples, exit codes,
175
+ JSON shapes
176
+ - [`docs/agent-contract.md`](docs/agent-contract.md) — the normative contract
177
+ for automated operators
178
+ - [`docs/storage.md`](docs/storage.md) — layer format, database schema,
179
+ recovery playbook
180
+ - [`docs/decisions.md`](docs/decisions.md) — the decision log
181
+ - [`SPEC.md`](SPEC.md) — the full specification; [`research/`](research/) —
182
+ the research behind it
183
+
184
+ ## Status
185
+
186
+ v1.0 candidate: the full command surface above is implemented and tested
187
+ (unit, property and end-to-end CLI suites, CI across Linux, Windows and
188
+ macOS). PyPI publication follows the v1.0.0 tag.
189
+
190
+ ## License
191
+
192
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,19 @@
1
+ LICENSE
2
+ README.md
3
+ firnpack.py
4
+ pyproject.toml
5
+ firnpack.egg-info/PKG-INFO
6
+ firnpack.egg-info/SOURCES.txt
7
+ firnpack.egg-info/dependency_links.txt
8
+ firnpack.egg-info/entry_points.txt
9
+ firnpack.egg-info/requires.txt
10
+ firnpack.egg-info/top_level.txt
11
+ tests/test_docs.py
12
+ tests/test_e2e.py
13
+ tests/test_fuzz_compact.py
14
+ tests/test_guards.py
15
+ tests/test_m1.py
16
+ tests/test_m2_branch.py
17
+ tests/test_m2_merge.py
18
+ tests/test_m2_safety.py
19
+ tests/test_regressions.py
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ firn = firnpack:main
3
+ firnpack = firnpack:main
@@ -0,0 +1,4 @@
1
+
2
+ [dev]
3
+ pytest>=8
4
+ ruff>=0.8
@@ -0,0 +1 @@
1
+ firnpack