@@ -47,17 +47,20 @@ We use Git's storage model (hierarchical trees, content-addressable) but with sn
4747└─────────────────────────────────────────────────────────┘
4848 ↓
4949 ┌────────────────────────────────┐
50- │ Filesystem Storage │
51- │ .snapshots/ │
52- │ ├── objects/ │
53- │ │ ├── ab/ │
54- │ │ │ └── cdef123... │
55- │ │ └── 12/ │
56- │ │ └── 3456... │
57- │ ├── snapshots/ │
58- │ │ ├── snap-uuid-1.json │
59- │ │ └── snap-uuid-2.json │
60- │ └── HEAD │
50+ │ Filesystem Storage │
51+ │ .snapshots/ │
52+ │ ├── objects/ │
53+ │ │ ├── ab/ │
54+ │ │ │ └── cdef123... │
55+ │ │ └── 12/ │
56+ │ │ └── 3456... │
57+ │ ├── snapshots/ │
58+ │ │ ├── snap-uuid-1.json │
59+ │ │ └── snap-uuid-2.json │
60+ │ └── refs/ │
61+ │ └── tags/ │
62+ │ ├── v1.0.0 │
63+ │ └── stable │
6164 └────────────────────────────────┘
6265```
6366
@@ -127,8 +130,30 @@ objects/
127130 └── 887766... ← Tree: root directory structure (JSON)
128131
129132snapshots/snap-uuid.json → points to root tree (998877...)
133+
134+ refs/tags/v1.0.0 → contains: snap-uuid (plain text file)
135+ refs/tags/stable → contains: snap-uuid
130136```
131137
138+ ** Tag System (Git-Style):**
139+
140+ Tags are stored as plain text files in ` refs/tags/ ` :
141+
142+ ```
143+ refs/tags/
144+ ├── v1.0.0 ← Contains: "snap-abc-123-def"
145+ ├── stable ← Contains: "snap-xyz-789"
146+ └── pre-deploy ← Contains: "snap-uvw-456"
147+ ```
148+
149+ ** Tag properties:**
150+ - ** Immutable** : Once created, a tag cannot be changed (like Git tags)
151+ - ** O(1) lookup** : Finding snapshots by tag requires reading one small file
152+ - ** Stored in both places** : Tag names are in snapshot JSON * and* tag ref files
153+ - Snapshot file: Contains list of tags for fast delete
154+ - Tag refs: Enable O(1) tag → snapshot lookup
155+ - ** Automatically cleaned up** : Deleting a snapshot removes its tag refs
156+
132157---
133158
134159## Core API
@@ -159,18 +184,20 @@ impl SnapshotManager {
159184 ) -> Result <()>;
160185
161186 /// List all snapshots (optionally filtered by tags)
187+ /// Filter by tags uses O(1) tag ref lookup
162188 pub async fn list_snapshots (
163189 & self ,
164- filter_tags : Option <Vec <String >>,
190+ tags : Option <Vec <String >>, // OR filter: any matching tag
165191 ) -> Result <Vec <SnapshotInfo >>;
166192
167- /// Find snapshots by tag
168- pub async fn find_by_tag (& self , tag : & str ) -> Result <Vec <SnapshotInfo >>;
193+ /// Find snapshot by tag (O(1) lookup via tag ref)
194+ /// Returns single snapshot since tags are immutable
195+ pub async fn find_by_tag (& self , tag : & str ) -> Result <Option <SnapshotInfo >>;
169196
170197 /// Get snapshot by ID
171198 pub async fn get_snapshot (& self , id : & str ) -> Result <Snapshot >;
172199
173- /// Delete snapshot
200+ /// Delete snapshot (also removes tag refs)
174201 pub async fn delete_snapshot (& self , id : & str ) -> Result <()>;
175202}
176203```
@@ -247,8 +274,15 @@ async fn main() -> Result<()> {
247274 println! (" {}: {} (tags: {:?})" , snap . id, snap . message, snap . tags);
248275 }
249276
250- // Find snapshots by tag
251- let pre_task_snapshots = manager . find_by_tag (" pre-task" ). await ? ;
277+ // Find snapshot by tag (O(1) lookup)
278+ if let Some (snapshot ) = manager . find_by_tag (" pre-task" ). await ? {
279+ println! (" Found: {}" , snapshot . id);
280+ }
281+
282+ // Filter snapshots by tags
283+ let filtered = manager . list_snapshots (
284+ Some (vec! [" pre-task" . to_string (), " important" . to_string ()])
285+ ). await ? ; // Returns snapshots with "pre-task" OR "important"
252286
253287 // Get specific snapshot details
254288 let snapshot = manager . get_snapshot (& snapshot_id ). await ? ;
0 commit comments