β¨ A Rust flake to automatically log al Git commits into Obsidian. β¨
Rusty Commit Saver captures each commitβs:
- Timestamp
- Commit message
- Repository URL
- Branch name
- Commit hash
and appends it to a dated diary entry in your Wiki directory.
Table of Contents:
- π Features
- π¦ Installation
- π Usage
- π οΈ Configuration
- π Roadmap & Improvements
- π Contributing
- Automatic diary entry creation with YAML frontmatter and table header
- Timestamped commit rows formatted for Obsidian
- Customizable storage path under
π Diaries/0. Commits/YYYY/MM-MMMM/ - A
git logbackstop (--reconcile) that journals commits made on a machine with no vault mounted, so a row is late rather than lost β for the historyHEADreaches - Preconfigured hooks (via Nix + pre-commit) to ensure code quality
-
Clone the repository
git clone https://github.com/chess-seventh/rusty-commit-saver.git cd rusty-commit-saver -
Enter the Nix development shell
devenv shell
-
Build the project
cargo build --release
-
(Optional) Install the binary to your PATH
cargo install --path .
devenv shell -- cargo test # the whole suite
devenv shell -- cargo clippy --all-targets
devenv shell -- pre-check # linters + tests + build
devenv.nixneeds nothing outside the repository. It enriches the shell with the shared modules from thedevenv_sharedcheckout when the machine has one β at~/src/claude-src/repos/devenv_shared, or at the older~/devenv_sharedβ and evaluates fine without it, which is what a fresh box and a CI runner get. Everything the commands above need (the Rust toolchain,cargo-nextest/cargo-shear/cargo-llvm-cov,treefmtand its formatters) is declared indevenv.nixitself, so this gate is reachable anywhere.The repository's own flake is self-contained too, so
nix develop --command cargo testalso works on any machine with Nix. Note that itsrustfmtdefaults to a different style edition β format withcargo fmt -- --style-edition=2024there, or it will reflow files it should leave alone.
Simply commit as usual. The hooks will:
- Run linters (
clippy,rustfmt, etc.) inside the Nix shell β pre-commit - Invoke Rusty Commit Saver to log the commit β post-commit, once the commit exists, which is why nothing this tool does can cost you a commit
If you prefer manual invocation:
rusty-commit-saverYour commit will be appended to, where Obsidian should be:
~/Documents/Wiki/π
Diaries/0. Commits/YYYY/MM-MMMM/YYYY-MM-DD.md
The hook can only journal on a machine that mounts the vault. Everywhere else the row is lost, and once the config is present on those machines it is lost silently β a journal that quietly stops looks exactly like a quiet week.
--reconcile is the other half: it reads the history each checkout's HEAD
can reach and appends whatever row the day note is missing. It needs no network
and no broker, so it works on the machine where the vault actually lives,
whenever you run it.
HEAD-reachable is narrower than "the repository", and the difference bites. Commits sitting on a branch that is not the checkout's currentHEADare not journalled, and if that branch is later squash-merged they never become reachable at all. If you work in git worktrees, point--reconcileat each worktree rather than only at the main clone.
# everything each of these checkouts can reach from its own HEAD
rusty-commit-saver --reconcile ~/src/one --reconcile ~/src/two
# just the recent past, for a scheduled run
rusty-commit-saver --reconcile ~/src/one --since 2026-08-01Each repository gets one line on stdout, so a scheduled run that appended nothing still says so:
rusty-commit-saver: one: 412 scanned, 3 appended, 409 already present
rusty-commit-saver: claude-src: excluded
What it will and will not do:
- It only appends. A row already in a note is never rewritten, reordered or reformatted. Your day notes are yours.
- A row goes in the note for the commit's own date, never today's β so a first backfill spreads across the months it actually happened in.
- A row the hook already wrote is recognised, by the commit hash in the last column, and not written twice. Running the pass a second time appends nothing and leaves every note byte-identical.
- The
[exclude]list applies here too. An excluded repository is not even walked. - A repository it cannot open is reported on stderr and skipped, so one broken clone does not stop the other repositories from catching up.
- It ignores the directory you started it in. Unlike the hook, it journals only the repositories you named.
--since takes a date as YYYY-MM-DD. A value it cannot read stops the run and
names both what it wanted and what it got, rather than quietly backfilling years
of history from a typo in a timer unit.
rust-toolchain.tomlpins Rust 1.89.0devenv.nixprovisions Rust, Clippy, rustfmt, and Git hooks.pre-commit-config.yamldefines all pre-commit checkstreefmt.tomlconfigurestreefmtand formatters
Runtime settings live in an INI file at
~/.config/rusty-commit-saver/rusty-commit-saver.ini:
[obsidian]
root_path_dir = ~/Documents/Obsidian
commit_path = Diaries/Commits
[templates]
commit_date_path = %Y/%m-%B/%F.md
commit_datetime = %H:%M:%S
# Optional: repositories to skip, by canonical repo name (comma-separated).
# A commit made in one of these repos writes nothing to the diary.
[exclude]
repos = claude-srcThe [exclude] section is optional. Each entry is matched, case-sensitively,
against the committing repository's canonical name β taken from its origin
remote URL (β¦/claude-src.git β claude-src), falling back to the
working-directory name for a repo with no usable origin. Because the origin is
the same from every checkout, one entry covers the main clone and every git
worktree of that repo, from any subdirectory.
[obsidian] and [templates] are required; a config missing either one is
fatal. Any other section is ignored, with a line on stderr naming it, never
fatal. One INI file is shared by every checkout on the machine, so a section
written for a newer release must not break a binary that predates it β which is
exactly what adding [exclude] did to every checkout older than 4.17.0.
The stderr line matters: a misspelt section ([excludes]) is ignored too, so
without it your exclusions would silently stop applying.
Keys work the same way, for the same reason:
-
A key this binary does not understand is ignored and named on stderr (
ignoring unrecognised config keys [templates] commit_datetimes). It used to be ignored in complete silence, so a typo applied nothing and said nothing. -
The four keys in
[obsidian]and[templates]are required, and so is a non-empty value for each βcommit_path =counts as missing. Without them there is no destination to write to, and a hook that quietly journals nothing looks exactly like a quiet day, so this one stays fatal. ([exclude] reposis optional, like its section.) -
The two
[templates]values must be formatschronocan actually render, and that is checked when the config is read. A bad specifier used to surface from inside the writer asa formatting trait implementation returned an error, naming nothing, after an empty diary file had already been created. -
The fatal message names the config file, the key and its section, plus any unrecognised key in that same section, since a misspelt
commit_pathsis the usual reasoncommit_pathis missing:rusty-commit-saver: /home/you/.config/rusty-commit-saver/rusty-commit-saver.ini: missing required key 'commit_path' in section [obsidian]; unrecognised in [obsidian]: commit_paths
None of this can cost you a commit: the tool runs as a post-commit hook, and git ignores that hook's exit status. A config fault costs you the diary entry and prints on stderr; the commit itself always stands.
tests/hook-gate.sh drives a real commit through a real post-commit hook, in a
throwaway repo and vault, and prints what a human would see:
cargo build
./tests/hook-gate.sh good # journals, says nothing
./tests/hook-gate.sh unknown-key # journals, names the key on stderr
./tests/hook-gate.sh missing-key # journals nothing, names file + key
./tests/hook-gate.sh blank-key # same, for a key with an empty value
./tests/hook-gate.sh bad-format # same, for a format chrono cannot render
./tests/hook-gate.sh unknown-section # journals, names the sectionThere are many enhancements planned:
- Configurable Obsidian path
- Configurable year/day/month on where to save the commit
- Interactive CLI flags and richer metadata (author, files changed)
- Improved error handling and user feedback
- Unit tests and CI pipeline for automated releases
Contributions welcome! Feel free to open issues or submit PRs.
- Fork the repo
- Create a feature branch
- Write tests and update
README.md - Submit a pull request
MIT Β© 2026 Chess7th