All notable changes to tiny-git will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
A full audit of the codebase against the invariants the documentation already claimed. Every item below is covered by a regression test that was verified to fail against the pre-fix code.
- Branch names are validated everywhere they are used to build a path. An
absolute or traversal-containing branch name (
/tmp/pwned,a/../../escape) previously wrote a ref file outside the repository; committing on a malicious initial branch could overwrite an external file.init -bis covered too. - Tree entries are validated before checkout, at both construction and parse
time. A crafted tree could previously plant
.tinygit/hooks/post-checkout. Entry names containing a separator, traversal, a metadata directory, or an empty name are now rejected asInvalidObjectError. is_ignored()no longer treats every.git-prefixed name as metadata..gitignore,.github/, and.gitattributeswere silently excluded fromadd ..- Filenames that the index format cannot represent are rejected at staging time rather than producing a tree that cannot be read back.
checkoutof the current branch no longer destroys work. It reverted unstaged edits and deleted newly staged files; it now preserves both.- An unreadable tree is no longer treated as an empty snapshot. A checkout
targeting a missing tree deleted every tracked file and still moved
HEAD. rmrefuses to discard uncommitted edits unless--forceis given.- The merge index is rebuilt from the merge result. A clean 3-way merge deleted files from disk while keeping them in the resulting commit.
- Conflict stages are correct. Conflicted paths had a spurious stage 0 and
zero hashes were written for nonexistent sides, which broke
diffandfsckon a perfectly valid add/add conflict. - Arbitrary bytes survive a merge. Non-UTF-8 content was rewritten with replacement characters even when the merge reported success; undecodable content is now treated as binary.
- Merge-base selection is ancestry-based, not distance-based. The old heuristic returned a non-maximal common ancestor in 34 of 394 randomly generated DAGs; multiple merge bases are now detected and handled explicitly.
- Executable-bit changes are real changes. Mode-only edits are now reported
by
statusanddiff, carried throughmergeandcheckout, andrestoreclears an exec bit instead of leaving it set. - Paths given on the command line resolve relative to the caller's
directory, so
tiny-git add filefrom a subdirectory stages that file. fsckvalidates far more: referent object types, index/stage consistency,HEADcommit types, malformed and newerrepositoryformatversionvalues, and unsafe names anywhere in a flattened tree path.
- Operation locking.
FileLockwith stale-lock recovery serialises mutating commands, and ref updates are compare-and-swap, so concurrent writers cannot lose commits. - Recoverable failures. Mutating operations validate their whole plan before
touching disk, propagate filesystem errors instead of swallowing them, and
fsyncparent directories after metadata renames. tiny-git checkout --forcere-materialises a revision's tree, which is the recovery path after an interrupted checkout or merge..tinygitignorewith documented gitignore-lite semantics, plus explicit tracked-file behaviour and anadd --forceoverride.tiny-git reflog [-n N]backed by.tinygit/logs/HEAD.tiny-git status --shortand--porcelain.- Merge lifecycle enforcement: while a merge is in progress,
checkoutand a secondmergeare refused until the merge is committed or aborted.
- Development status is
4 - Beta, not5 - Production/Stable. The classifier was aspirational; this audit found reachable data-loss and repository-corruption bugs in released code. - Crash-safety claims are narrowed.
atomic_write_filestill guarantees a per-file atomic replacement, and now fsyncs the containing directory as well. There is no journal, so a command interrupted between writes is not recoverable automatically.docs/failure-model.mdstates the boundary explicitly.
First tagged release. The 16-command V1 surface is frozen — see the Roadmap for what is planned next.
A note on this entry. Earlier drafts of this file split the work below across
1.0.0and1.1.0entries dated a day apart. Neither version was ever tagged, released on GitHub, or published to PyPI, and every feature listed under both is present in the shipped code. They have been consolidated here so that this file,pyproject.toml, and thev1.0.0tag all agree.1.1.0remains reserved for the reference-extension work in the roadmap.
- Core CLI Application:
tiny-gitcommand with the full subcommand suite (init,add,commit,status,log,branch,checkout,merge,diff,config). - Configuration Engine:
.tinygit/configINI parser foruser.nameanduser.email.
- Content-Addressable Object Storage: SHA-1 hashed
Blob,Tree, andCommitmodels stored under.tinygit/objects/xx/yyyy...with deduplication. - SHA-1 Extension Point:
HashAlgorithmenum andget_hasher()abstraction decoupling the hashing algorithm from object storage, so a future SHA-256 mode needs no storage rewrite. - Symlink & Byte-Oriented Storage Policy: symlinks are explicitly rejected in
validate_path()to prevent filesystem-traversal escapes; blobs are stored byte-for-byte so binary files and arbitrary encodings round-trip safely.
- Staging Area / Index: structured
.tinygit/indexJSON format supporting addition, modification, and staged deletions. - Symbolic References: managed
.tinygit/HEADand.tinygit/refs/heads/<branch>with detached HEAD support. - Revision Resolver (
RevisionResolver): unified engine supporting branch names, short SHA prefixes, and ancestry syntax (~N,^N,HEAD~1,HEAD^2).
- Working Tree Safety Engine: validation preventing checkouts or merges from overwriting uncommitted user changes.
- Working-Tree Completeness:
tiny-git rm <files>... [--cached]: remove files from index and working tree (or--cachedfor index only).tiny-git restore <files>... [--staged]: restore working-tree files from the index, or unstage index modifications fromHEAD.
- Unified Diff Engine: standard-library diff formatting (
--- a/file/+++ b/file) for unstaged (diff) and staged (diff --cached) changes, with binary detection. - Three-Way Merge Engine: Lowest Common Ancestor (LCA) BFS search, fast-forward detection, line-level 3-way merge, Git conflict markers (
<<<<<<< HEAD), multi-parent merge commits, andmerge --abortrestoring exact pre-merge state.
- Repository Integrity Checker (
tiny-git fsck): comprehensive verification of.tinygit/objects,refs,HEAD,index, trees, commits, and blobs for corruption, missing references, and topological cycles. - Developer Inspection Commands:
tiny-git cat-file <object> [-t | -s | -p]: inspect object type, size, or pretty-printed payload.tiny-git show [<rev>]: show commit metadata and unified diff against its first parent.tiny-git rev-parse <rev>: resolve arbitrary revision expressions to full 40-character SHA-1 hashes.
- Atomic File Writes (
atomic_write_file): temporary-file write,os.fsync(), and atomic rename acrossHEAD,refs,index,config, andobjects/to guarantee crash consistency. - Repository Format Versioning: enforced
core.repositoryformatversion = 1in.tinygit/config, raisingUnsupportedRepositoryFormatErroron newer formats.
- Test Suite: unit, property-invariant, crash-consistency, merge-matrix, state-machine fuzzing, and CLI-contract tests across 31 modules — see
docs/testing.md. - Documentation & Architecture: educational documentation in
docs/andexamples/.