store

package
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 15, 2026 License: MIT Imports: 14 Imported by: 0

Documentation

Overview

Package store implements undo-anything's on-disk, content-addressed object store. It is the trust anchor of the whole tool: file contents are stored once per unique SHA-256 (deduplicated and compressed), and snapshots are immutable manifests that reference those blobs. Everything here is written atomically (temp file + rename) so an interrupted snapshot can never corrupt existing history.

Index

Constants

View Source
const (
	TriggerManual     = "manual"      // `ua snapshot`
	TriggerWatch      = "watch"       // file change detected by the daemon
	TriggerPreRestore = "pre-restore" // automatic safety snapshot before a restore
	TriggerInit       = "init"        // initial snapshot at `ua init`
)

Trigger describes what caused a snapshot to be taken.

View Source
const DirName = ".undo"

DirName is the per-folder store directory, analogous to ".git".

View Source
const MaxBlobBytes int64 = 2 << 30 // 2 GiB

MaxBlobBytes is a hard ceiling on how many bytes a single blob may decompress to. It guards against a "decompression bomb" — a tiny crafted object in an untrusted .undo store that would otherwise expand to gigabytes and exhaust memory. Legitimate blobs never approach this (they are bounded by the much smaller max_file_size setting at snapshot time).

Variables

View Source
var ErrNotInitialized = errors.New("no undo-anything store found (run `ua init` first)")

ErrNotInitialized is returned when no store is found for a path.

Functions

func CompressedLen

func CompressedLen(data []byte) int

CompressedLen returns the number of bytes data occupies after zlib compression — i.e. its physical footprint in the object store.

func ComputeID

func ComputeID(files []FileEntry) string

ComputeID derives a snapshot's stable short ID from its file manifest. Two snapshots with identical file sets (path+hash+mode) collapse to one ID, which is what makes "snapshot on every save" cheap and idempotent.

func HashBytes

func HashBytes(data []byte) string

HashBytes returns the hex SHA-256 of data. This is the object key.

Types

type FileEntry

type FileEntry struct {
	Path    string    `json:"path"` // relative, slash-separated
	Hash    string    `json:"hash"`
	Size    int64     `json:"size"`
	Mode    uint32    `json:"mode"`
	ModTime time.Time `json:"modtime"`
}

FileEntry records a single tracked file within a snapshot. Hash is the SHA-256 (hex) of the file's uncompressed contents, which is also its key in the object store.

type GCResult

type GCResult struct {
	SnapshotsRemoved int
	BlobsRemoved     int
	BytesReclaimed   int64
}

GCResult reports what a garbage-collection pass removed.

type IndexEntry

type IndexEntry struct {
	ID       string    `json:"id"`
	Time     time.Time `json:"time"`
	Trigger  string    `json:"trigger"`
	Label    string    `json:"label,omitempty"`
	Parent   string    `json:"parent,omitempty"`
	Files    int       `json:"files"`
	Size     int64     `json:"size"`
	NewBlobs int       `json:"new_blobs"`
	NewBytes int64     `json:"new_bytes"`
}

IndexEntry is the compact, append-only timeline record for a snapshot. The timeline log is a stream of these, newest last, used for fast listing.

type Snapshot

type Snapshot struct {
	ID      string      `json:"id"`
	Time    time.Time   `json:"time"`
	Trigger string      `json:"trigger"`
	Label   string      `json:"label,omitempty"`
	Parent  string      `json:"parent,omitempty"`
	Files   []FileEntry `json:"files"`
	Stats   Stats       `json:"stats"`
}

Snapshot is a complete, content-addressed manifest of a watched tree at a point in time. The ID is the short hash of the canonical manifest, so two identical trees collapse to one snapshot.

type Stats

type Stats struct {
	Files     int   `json:"files"`
	TotalSize int64 `json:"total_size"`
	NewBlobs  int   `json:"new_blobs"` // blobs written for the first time by this snapshot
	NewBytes  int64 `json:"new_bytes"` // physical bytes those new blobs occupy (compressed)
}

Stats summarizes a snapshot for quick display without rehydrating files.

type Store

type Store struct {
	// Root is the absolute path of the watched working directory.
	Root string
	// Dir is the absolute path of the .undo directory.
	Dir string
}

Store is a handle to a single folder's history.

func Find

func Find(start string) (*Store, error)

Find walks up from start looking for an existing store, mirroring how git discovers its repository root.

func Init

func Init(root string) (*Store, error)

Init creates a new store rooted at the given working directory. It is idempotent: re-initializing an existing store is a no-op that returns the existing handle.

func Open

func Open(root string) (*Store, error)

Open returns a handle to the store at root, erroring if it is not initialized.

func (*Store) GC

func (s *Store) GC(keep map[string]bool) (GCResult, error)

GC removes every snapshot whose ID is not in keep, then deletes any blob no longer referenced by a surviving snapshot. It is safe to run at any time; an empty keep set is rejected by callers (prune always keeps at least one).

func (*Store) GetBlob

func (s *Store) GetBlob(hash string) ([]byte, error)

GetBlob returns the decompressed contents of the blob with the given hash.

func (*Store) HasBlob

func (s *Store) HasBlob(hash string) bool

HasBlob reports whether a blob with the given hash already exists.

func (*Store) Index

func (s *Store) Index() ([]IndexEntry, error)

Index returns all timeline entries in chronological order (oldest first).

func (*Store) IndexDesc

func (s *Store) IndexDesc() ([]IndexEntry, error)

IndexDesc returns timeline entries newest first.

func (*Store) Latest

func (s *Store) Latest() (*IndexEntry, error)

Latest returns the most recent timeline entry, or nil if the store is empty.

func (*Store) PutBlob

func (s *Store) PutBlob(data []byte) (hash string, written bool, err error)

PutBlob stores data, returning its hash and whether it was newly written (false means it was already present — a dedup hit). Storage is atomic.

func (*Store) ReadSnapshot

func (s *Store) ReadSnapshot(id string) (*Snapshot, error)

ReadSnapshot loads a full snapshot manifest by ID or unique prefix.

func (*Store) ResolveID

func (s *Store) ResolveID(prefix string) (string, error)

ResolveID expands a unique snapshot ID prefix to its full ID. The literal alias "latest" resolves to the newest snapshot.

func (*Store) Usage

func (s *Store) Usage() (Usage, error)

Usage computes store statistics by walking the objects directory and reading the timeline.

func (*Store) WriteSnapshot

func (s *Store) WriteSnapshot(snap *Snapshot) (created bool, err error)

WriteSnapshot persists a snapshot manifest and appends it to the timeline. It returns created=false if a snapshot with the same ID already exists (an identical-state no-op), in which case nothing is written.

type Usage

type Usage struct {
	Snapshots   int   // number of timeline entries
	UniqueSnaps int   // distinct snapshot manifests on disk
	Objects     int   // number of stored blobs
	ObjectBytes int64 // physical bytes on disk (compressed)
	LogicalSize int64 // total size of files in the latest snapshot (uncompressed)
}

Usage summarizes physical and logical disk usage for `ua status`.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL