Skip to content

Source Code Map en

Won-Kyu Park edited this page Aug 28, 2026 · 3 revisions

Source Code Map

Package layout

simple_rcs/
  simple_rcs.py          the core class (2,344 lines) — basically the library
  codec.py                format primitives (binary encoding, hashing, escaping)
  matchers.py             diff backend registry (resolved at import, with fallback)
  gitpatch.py             git-compatible binary patch output (export only)
  pydifflib.py             the production diff engine (StreamSequenceMatcher)
  pybsdiff.py              binary deltas (BSDIFF40-compatible)
  myersdiff.py             pure-Python Myers algorithm (reference/benchmark)
  myersdiff_ses.py          Myers SES variant
  myersdiff_dmp.py          Myers, diff-match-patch-style offset variant
  _myersdiff_ses.pyx       Cython port of the SES variant above
  _myersdiff_dmp.pyx        Cython port of the DMP variant above
  simple_rcs_gpg.py         GPG signing/verification callbacks
  adapters.py               stream adapter for psycopg2 large objects

tools/
  srcs_commit.py     CLI to commit a file
  srcs_log.py         print history / signature listing
  srcs_diff.py        unified diff between versions, or between engines
  srcs_blame.py       per-line author/version attribution
  srcs_verify.py      verify the hash chain and GPG signatures
  srcs_sign_head.py    add a GPG signature to the current HEAD
  bench_diff.py        diff-engine benchmark (time + memory)
  compare_memory_usage.py  memory-usage comparison utility

tests/unit_tests/    pytest suite (186 tests)
docs/                 design/benchmark notes
scripts/              one-off experimental benchmark scripts (e.g. wiki backend)

simple_rcs.py — the core

One class, SimpleRCS, does essentially all the work. Its public surface looks like this:

Method What it does
commit(content, author, log, ...) store a new version (text or binary, optional snapshot)
checkout(ver_num=None) restore a specific version (defaults to HEAD)
log(limit=None, reverse=False) list history metadata
diff(ver_a, ver_b) unified diff between two versions
blame(depth=None) which version/author each line of HEAD came from
sign_head(signer_callbacks) add a GPG signature to HEAD
verify(verifier_callbacks=None) verify the whole hash chain plus signatures
verify_block_signature(...) verify a single block's signature
get_content() return the whole stream as a string
get_bytes() return the whole stream verbatim as bytes (use this when raw/base85 blocks are present)

The constructor also takes durable (default True) — whether commits on the file-path backend are fsynced.

Internally, the part that matters most is the backward scan from the end of the stream (_load_head, _get_prev_block). It never loads the whole file into memory — it only reads the blocks it needs — so memory usage doesn't grow with history length. Block boundaries are located by the ver @ marker and then decided by @ parity (Storage Format).

The write side branches in _rewrite_head. A file-path store goes through _atomic_rewrite_head (copy the prefix, build a temp file, os.replace); memory and caller-owned streams go through _rewrite_head_in_place. Both commit paths (commit/sign_head) funnel through it, so the destructive write exists in exactly one place.

gitpatch.py — git-compatible binary patches

Our storage format (BSDIFF inside blocks) is unreadable to any other tool. This module is the export path: it reconstructs both revisions and wraps them the way git diff --binary does — a GIT binary patch with a literal block, zlib-compressed and framed in git's base85 — so git apply can consume it. tools/srcs_diff.py --binary is the only caller.

codec.py — stateless format primitives

Pure functions that don't depend on a SimpleRCS instance, so they can be tested in isolation. Binary payload encoding/decoding (encode_binary/decode_binary, supports base64/base85/raw), escaping @ inside @...@ values, and computing the v2 block hash all live here. SimpleRCS passes its own config (hash_algo, encoding) into these as arguments.

The diff modules — why so many

  • StreamSequenceMatcher in pydifflib.py — the only engine actually used on the commit path. A hybrid: greedy hash-based matching, then standard difflib refinement on the replace blocks. Doesn't guarantee the shortest edit distance, but it's fast.
  • myersdiff*.py — pure-Python implementations of the classic Myers O(ND) algorithm. Two variants: an SES (shortest edit script) version and a diff-match-patch-style offset-based version. Both guarantee minimal edit distance, but their performance diverges depending on the input characteristics.
  • _myersdiff_{ses,dmp}.pyx — straight Cython ports of the two above. 15–26x faster than the pure-Python versions, but not wired into the commit path today — they're exercised through tools/bench_diff.py only.

Why there are this many, and what you'd actually reach for, is covered in more depth in Diff Engines.

pybsdiff.py — binary deltas

Used whenever you commit non-text content (bytes). Built to be compatible with the BSDIFF40 format, so patches can also be created and applied with the native bsdiff/bspatch tools if you have them installed.

simple_rcs_gpg.py — signing and verification

Shells out to the gpg binary to create and verify signatures. The SRCS_GPGSIGN_PATH environment variable lets you point at a different gpg executable. SimpleRCS.sign_head()/verify() take these functions in as callbacks, so if you wanted a different signing scheme entirely, you could plug in a replacement with the same callback signature.

adapters.py — plugging directly into a database

SimpleRCS expects a BinaryIO, and something like psycopg2's large object — which doesn't inherit from BinaryIO — can't be handed over directly. PsycopgLargeObjectAdapter bridges that gap, so a PostgreSQL large object can stand in for a .srcs file as the backing store. Why this particular adapter is a good fit is discussed in Wiki Backend Design (Korean only).

tools/ — command-line helpers

Each script is a thin CLI wrapper around the SimpleRCS library. Run them from the repo root with uv run tools/<name>.py. Library code is supposed to log through logging and never print, but the tools/ scripts are the exception — printing to stdout is their whole job. See CLI Tools for what each one does.

Build

[build-system] in pyproject.toml uses setuptools + Cython, and ext-modules registers the two Cython extensions (_myersdiff_ses/_myersdiff_dmp). They get built as part of uv sync. If you edit a .pyx file, you need to rebuild before it takes effect.

Clone this wiki locally