--- title: What would VCS look like if SSD wear was the primary constraint? date: 2026-04-20 layout: post --- Git: 1819 inodes, 59 MB to track a 36.59 MB repo pre-GC. GC collected 1514 inodes. Packing reclaimed 6 MB. Can we do better? PoC implements status, add, commit, log, show, diff. Supports symlinks, binary files. Optimized for SSD longevity — sequential reads/writes, reduced TBW/WA. Architecture: sorted index tracks files. Staging copies object to staging area, records path, mtime, size, SHA-1: ``` my $p = $wrk_entry->{path}; my $current_hash = hash_file_content($p); my $stg_path = File::Spec->catfile(TMP_DIR, $p); make_path(dirname($stg_path)); (-l $p) ? symlink(readlink($p), $stg_path) : copy($p, $stg_path); printf $out "%-40s\t%-40s\t%-40s\t%-12d\t%-10d\t%s\n", $current_hash, $idx_entry->{c_hash}, $idx_entry->{b_hash}, $wrk_entry->{mtime}, $wrk_entry->{size}, $p; ``` No CAS, no delta chains. One base file per tracked object. Revisions are patches against the base. When the patch outgrows the file, snapshot and rebase. Commits record directory structure and patchset. Patches stored in a tarball—one inode per commit. Tarballs >512 bytes gzipped. Trees deduplicated by content hash. Diff/show: look up revision, find tree and patchset, apply patch. O(1) checkout. Single corrupt patch can't poison history. External tools: sort, diff, patch, tar, gzip—all base system. Pipes and streams throughout. MEM_LIMIT falls back to disk for large repos: ``` use constant MEM_LIMIT => 64 * 1024 * 1024; use constant CHUNK_LEN => 8192; use constant IO_LAYER => ":raw:perlio(layer=" . CHUNK_LEN . ")"; if (!$use_disk) { @buf = sort @buf; return sub { my $line = shift @buf; return unless $line; chomp $line; my ($p, $m, $s) = split(/\t/, $line); return { path => $p, mtime => $m, size => $s }; }; } else { $flush->() if @buf; close $tmp_fh; open(my $sort_fh, "-|", "sort", "-t", "\t", "-k1,1", $tmp_path) or die $!; return sub { my $line = <$sort_fh>; unless ($line) { close $sort_fh; return; } chomp $line; my ($p, $s, $m) = split(/\t/, $line); return { path => $p, mtime => $m, size => $s }; }; } ``` Index and tree processing: O(N) two-finger walk. Streamed line-by-line, calibrated buffers. No mmap. ## Benchmarks Performed on T490 (i7-10510U, OpenBSD 7.8) against git v2.51.0: ### Impact of repository size Small repo:
=============================================================
 BENCHMARK: 200 files @ 20 depth
=============================================================

ACTION: Status
-------------------------------------------------------------
METRIC          | URN                  | GIT                 
----------------+----------------------+---------------------
Time            |                0.10s |                0.00s
Max RSS         |              0.02 MB |              0.00 MB
Page faults     |        Maj:0 / Min:0 |        Maj:0 / Min:0
Inodes          |                    6 |                   27
Repo size       |                20 KB |               116 KB
-------------------------------------------------------------

ACTION: Add
-------------------------------------------------------------
METRIC          | URN                  | GIT                 
----------------+----------------------+---------------------
Time            |                0.16s |                0.17s
Max RSS         |              0.02 MB |              0.00 MB
Page faults     |        Maj:0 / Min:0 |        Maj:0 / Min:0
Inodes          |                  225 |                  360
Repo size       |              3700 KB |              3348 KB
-------------------------------------------------------------

ACTION: Commit
-------------------------------------------------------------
METRIC          | URN                  | GIT                 
----------------+----------------------+---------------------
Time            |                0.17s |                0.04s
Max RSS         |              0.02 MB |              0.01 MB
Page faults     |        Maj:0 / Min:0 |        Maj:0 / Min:0
Inodes          |                  347 |                  397
Repo size       |              4212 KB |              3496 KB
-------------------------------------------------------------

ACTION: Status(Clean)
-------------------------------------------------------------
METRIC          | URN                  | GIT                 
----------------+----------------------+---------------------
Time            |                0.10s |                0.01s
Max RSS         |              0.02 MB |              0.00 MB
Page faults     |        Maj:0 / Min:0 |        Maj:0 / Min:0
Inodes          |                  347 |                  397
Repo size       |              4212 KB |              3496 KB
-------------------------------------------------------------
Larger repo:
=============================================================
 BENCHMARK: 5000 files @ 50 depth
=============================================================

ACTION: Status
-------------------------------------------------------------
METRIC          | URN                  | GIT                 
----------------+----------------------+---------------------
Time            |                0.26s |                0.00s
Max RSS         |              0.02 MB |              0.00 MB
Page faults     |        Maj:0 / Min:0 |        Maj:0 / Min:0
Inodes          |                    6 |                   27
Repo size       |                20 KB |               116 KB
-------------------------------------------------------------

ACTION: Add
-------------------------------------------------------------
METRIC          | URN                  | GIT                 
----------------+----------------------+---------------------
Time            |                2.82s |                4.62s
Max RSS         |              0.02 MB |              0.01 MB
Page faults     |        Maj:0 / Min:0 |        Maj:0 / Min:0
Inodes          |                 5055 |                 5284
Repo size       |             89444 KB |             70360 KB
-------------------------------------------------------------

ACTION: Commit
-------------------------------------------------------------
METRIC          | URN                  | GIT                 
----------------+----------------------+---------------------
Time            |                1.18s |                0.93s
Max RSS         |              0.03 MB |              0.01 MB
Page faults     |        Maj:0 / Min:0 |        Maj:0 / Min:0
Inodes          |                 5264 |                 5342
Repo size       |             91620 KB |             70592 KB
-------------------------------------------------------------

ACTION: Status (Clean)
-------------------------------------------------------------
METRIC          | URN                  | GIT                 
----------------+----------------------+---------------------
Time            |                0.34s |                0.10s
Max RSS         |              0.02 MB |              0.01 MB
Page faults     |        Maj:0 / Min:0 |        Maj:0 / Min:0
Inodes          |                 5264 |                 5342
Repo size       |             91620 KB |             70592 KB
-------------------------------------------------------------
### Impact of commits over time
=============================================================
 REBASE BENCHMARK: 1000 files (100 commits)
 CONDITIONS: Depth=2, Files Mod=5%, Change=50%
 INITIAL RAW DATA SIZE: 16976 KB
=============================================================

SNAPSHOT: Commit #20
-------------------------------------------------------------
METRIC          | URN                  | GIT                 
----------------+----------------------+---------------------
Time            |                0.29s |                0.05s
Max RSS         |              0.02 MB |              0.01 MB
Page faults     |        Maj:0 / Min:0 |        Maj:0 / Min:0
Inodes          |                 1578 |                 2334
Repo size       |             20404 KB |             19380 KB
-------------------------------------------------------------

SNAPSHOT: Commit #40
-------------------------------------------------------------
METRIC          | URN                  | GIT                 
----------------+----------------------+---------------------
Time            |                0.54s |                0.05s
Max RSS         |              0.02 MB |              0.01 MB
Page faults     |        Maj:0 / Min:0 |        Maj:0 / Min:0
Inodes          |                 1607 |                 3374
Repo size       |             20520 KB |             23788 KB
-------------------------------------------------------------

SNAPSHOT: Commit #60
-------------------------------------------------------------
METRIC          | URN                  | GIT                 
----------------+----------------------+---------------------
Time            |                0.31s |                0.05s
Max RSS         |              0.02 MB |              0.01 MB
Page faults     |        Maj:0 / Min:0 |        Maj:0 / Min:0
Inodes          |                 1635 |                 4414
Repo size       |             20632 KB |             28196 KB
-------------------------------------------------------------

SNAPSHOT: Commit #80
-------------------------------------------------------------
METRIC          | URN                  | GIT                 
----------------+----------------------+---------------------
Time            |                0.29s |                0.05s
Max RSS         |              0.02 MB |              0.01 MB
Page faults     |        Maj:0 / Min:0 |        Maj:0 / Min:0
Inodes          |                 1664 |                 5454
Repo size       |             20748 KB |             32596 KB
-------------------------------------------------------------

SNAPSHOT: Commit #100
-------------------------------------------------------------
METRIC          | URN                  | GIT                 
----------------+----------------------+---------------------
Time            |                0.54s |                0.10s
Max RSS         |              0.02 MB |              0.01 MB
Page faults     |        Maj:0 / Min:0 |        Maj:0 / Min:0
Inodes          |                 1693 |                 6495
Repo size       |             20864 KB |             37008 KB
-------------------------------------------------------------

TOTAL URN REBASES: 273
Git wins on speed and memory. Cold start is the exception — urn's add + commit beats git there. Git's zlib compression wins on initial disk usage. Over time the picture flips. 80 commits: git wrote 17 MB, urn wrote 0.5 MB. Git's inode count went from 2,334 to 6,495. Urn's went from 1,578 to 1,693. The thing it was built to do, it does. Commit: 49ae774