Hell Version Control

A lightweight, content-addressed version control system for solo developers. Snapshots are stored as SHA-256 objects, compressed with a custom LZ codec and delta encoding, and kept in a plain-text layout under .hvc/.

Note: HVC targets a single user on a single machine. No remote push / pull, no authentication layer.

Installation

One-line install on Linux. Downloads the latest release binary.

$ curl -fsSL https://hvc.example.com/install.sh | sh

Build from source

Download the source archive and compile with Cargo.

$ curl -fsSL https://hvc.example.com/hvc-source.zip -o hvc-source.zip
$ unzip hvc-source.zip && cd hvc-source
$ cargo build --release

Initialize a repository

Run init at the root of the project you want to version.

$ hvc init

This creates a .hvc/ directory containing:

.hvc/
├── active       # branch name or detached hash
├── staged       # index
├── ignore       # ignore patterns
├── history      # reflog of HEAD moves
├── branches/    # one file per branch
└── data/        # loose objects and packfiles

Quick workflow

save stages every change and commits in one step. undo moves HEAD back one commit, keeping your files on disk untouched.

$ hvc save "tweak renderer"
Saved 3 change(s) [a1b2c3d]

$ hvc undo
Undid last save. Files on disk remain untouched.

save respects .hvc/ignore and always skips .hvc/ itself. If nothing changed, it exits without creating a commit.

Staged workflow

For precise control, stage files with add, then commit.

Stage

$ hvc add src/main.rs src/lib.rs
added 2 file(s)

$ hvc add "src/**/*.rs"  # glob pathspecs
$ hvc add .              # everything under cwd

Unstage

$ hvc unstage src/lib.rs
unstaged 1 file(s)

Commit

The string after -m is a free-form commit message.

$ hvc commit -m "your message"
[a1b2c3d] your message
Difference from save: commit only records what is in the index. It never scans the working tree.

Status

Compares HEAD, the index, and the working directory. Prints clean when nothing differs.

  • staged: differences between HEAD and the index.
  • modified: tracked files whose content or mode differs from the index.
  • deleted: tracked files missing from disk.
  • untracked: files on disk that are neither tracked nor ignored.
$ hvc status
staged:
  new src/tests.rs
modified:
  src/main.rs
untracked:
  notes.md

Log

Walk history from HEAD, following the first parent.

$ hvc log
commit 3f8a2c1e...
date   1733241600

    tweak renderer
FlagEffect
--onelineShort hash plus first line of message.
-pPrint a textual patch against the first parent.

Diff

Compares the index against the working directory. Use --staged to see what commit would record.

$ hvc diff
modified: src/main.rs
  fn main() {
-     println!("hi");
+     println!("hello");
  }

$ hvc diff --staged

Uses Hirschberg LCS: O(n·m) time, O(min(n,m)) memory. Binary files print (binary).

Undo

Moves HEAD to HEAD~1 and rebuilds the index. Working files are not touched.

$ hvc undo
Undid last save. Files on disk remain untouched.

After undo, status will show the previous commit's content as modified, since the working tree still holds the newer files.

Rollback

Moves HEAD to an arbitrary revision. Default is soft (index only); --hard also overwrites working files.

$ hvc rollback a1b2c3d         # soft reset
HEAD is now at a1b2c3d

$ hvc rollback a1b2c3d --hard  # discard working changes
$ hvc rollback --last          # same as HEAD~1
Warning: --hard overwrites tracked files and deletes tracked files not in the target tree. Untracked files are left alone.

Restore

Bring files back from the index or from any commit.

$ hvc restore -- src/main.rs           # from index
$ hvc restore HEAD~2 -- src/main.rs    # from a commit
$ hvc restore a1b2c3d -- README.md

The -- separator is required. Before it: revision. After it: path.

Branch

List branches or create one at the current HEAD.

$ hvc branch
* main
  experiment

$ hvc branch experiment
created branch experiment

Branch names map 1:1 to files under .hvc/branches/. Current branch is recorded in .hvc/active.

Switch

Change the active branch, or check out a raw commit (detached HEAD).

$ hvc switch experiment
switched to experiment

$ hvc switch a1b2c3d
switched to a1b2c3d
Safety check: refuses to run if the index differs from HEAD, or if a tracked file has uncommitted edits that would be overwritten. Commit or rollback --hard first.

Merge

Three-way merge using the best common ancestor. Fast-forwards when possible.

$ hvc merge experiment
fast-forward to 9e4f1c0

Conflict handling

Conflicting files are written with markers and listed in .hvc/IN_PROGRESS:

<<<<<<< current
local version
=======
other version
>>>>>>> target

Resolve, then:

$ hvc merge --continue  # finalize
$ hvc merge --abort     # discard

--continue refuses to proceed while any conflicted file still contains <<<<<<< .

Garbage collection

Marks reachable objects, deletes the rest, and packs loose objects into .pack + .idx pairs.

$ hvc gc
packing 87 loose object(s)...
packed 87 object(s)
gc: removed 12 unreachable object(s)

Packing uses a greedy delta search over recent objects of the same type, preferring deltas smaller than half the target size.

Help

Print the built-in command summary. Running hvc with no arguments does the same.

$ hvc help

Command reference

CommandDescription
initCreate a new .hvc/ repository.
save [msg]Auto-stage all changes and commit.
add <paths…>Stage files, directories, or glob pathspecs.
unstage <paths…>Remove entries from the index.
commit -m <msg>Commit the current index.
undoMove HEAD to the previous commit, keeping files.
rollback <rev> [--hard]Reset HEAD (and optionally the working tree).
statusShow staged / modified / deleted / untracked.
log [--oneline] [-p]Walk commit history from HEAD.
diff [--staged]Changes vs. index, or index vs. HEAD.
restore [rev] -- <paths…>Restore files from index or a commit.
branchList branches (current marked with *).
branch <name>Create a branch at HEAD.
switch <target>Switch to a branch or detached commit.
merge <branch>Merge into the current branch.
merge --continueFinalize an in-progress merge.
merge --abortAbort an in-progress merge.
gcPrune unreachable objects and pack loose ones.
helpShow command summary.

Revision syntax

SyntaxMeaning
HEAD / @Current commit.
mainTip of the named branch.
<hash>Full SHA-256 or any unique prefix (≥ 4 chars).
rev~nWalk n first parents back (default 1).
rev^ / rev^nFirst parent / n-th parent.
$ hvc log --oneline HEAD~3
$ hvc restore main~1 -- src/lib.rs

Architecture

Three object types, two storage formats, and a handful of text files for refs and locks.

Object model

Every object is type_byte || payload, hashed with SHA-256.

TypeBytePayload
blobbRaw content (or symlink target).
treetLines of mode hash path.
commitcHeader block, blank line, message.

Storage layout

  • Loose objects at data/aa/bb/cccc….
  • Packfiles at data/pack/*.pack with matching .idx.
  • Index is staged: one entry per line, mode hash path.
  • Reflog is history: one line per HEAD move.

Compression

Loose objects use an LZ77 codec (32 KiB window, 64-probe hash chain), prefixed with 0xFF. If compression doesn't win, the raw payload is stored.

Packfile format

flags (1) | type (1) | hash (32) | [base_off u32, len u32, delta]  if DELTA_FLAG
                                 | [len u32, encoded_data]            otherwise

The companion .idx is a sorted hash table with a 256-entry fanout prefix.

Delta encoding

Copy (0x00–0x7F + u32 offset) and insert (0x80–0xFE + bytes) commands, terminated by 0xFF. Only used if smaller than half the target.

Locking

All writes take a path.lock file with the holder's PID. Stale after 60s if the PID is gone. Writes go through write_atomic: temp file, fsync, rename.

Ignore patterns

.hvc/ignore supports # comments, ! negation, trailing / for dirs, leading / for anchoring. Matches any path suffix, so *.log catches build/output.log.

License

HVC is free software, released under the GNU General Public License v3.0.

You may redistribute and modify it under the terms of the GPL. It is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose.

Full text: GNU General Public License v3.0.

Developer

Designed and maintained by Winexero.