Atomic commit validator for Go. Ensures staged files form a self-contained commit where every symbol used is defined in the staged changeset or in already-committed code - never in unstaged or untracked files.
The name comes from Lithuanian darna, meaning harmony - the quality of a codebase where every commit is complete and coherent.
Non-atomic commits break git bisect, block cherry-picks, and make code review painful. A commit that references a function defined only in a later commit (or worse, never committed at all) cannot compile when checked out in isolation.
This problem intensifies in automatic programming workflows where developers and AI coding agents collaborate on large features. Automatic programming requires maintaining vision and control throughout development - but that control depends on being able to commit working subsets of code at any point. When building complex features incrementally, both human developers and autonomous agents need to safely checkpoint progress without accidentally creating broken commits.
Darna was inspired by Vladislav Shpilevoy's FOSDEM 2026 talk "An Efficient Git Workflow For High-Stakes Projects", which presents "Atomic Flow" - a methodology for maintaining clean commit histories through multi-commit patchsets in mission-critical systems.
Common causes of non-atomic commits:
- Forgetting to stage a newly created file.
- Staging part of a refactor while the rest sits in the working tree.
- Partial staging (
git add -p) that splits a dependency across staged and unstaged hunks. - AI agents generating code across multiple files without tracking dependencies.
Darna catches these at commit time. It loads Go packages with full type information, builds a symbol dependency graph, and reports exactly which unstaged files are missing and why.
The --committable mode is designed specifically for automatic programming workflows - it identifies which files can be committed independently, enabling both developers and autonomous agents to build multi-commit patchsets incrementally without human intervention.
go install dario.cat/darna/cmd/darna@latestOr build from source:
make build # produces bin/darnaRequires Go 1.24+ and git on PATH.
darnaReturns exit code 0 if the commit is atomic, 1 if violations are found.
| Flag | Description |
|---|---|
-v |
Verbose - prints confirmation on success |
-dir <path> |
Set working directory (default: .) |
--committable |
Find the next file that can be committed atomically |
--select |
Alias for --committable |
--dependants |
Include direct dependants when using --committable |
--commit-msg <agent> |
Generate commit message using LLM agent (claude, codex, mistral, opencode) |
--prompt-file <path> |
Custom prompt file for --commit-msg (default: built-in Conventional Commits prompt) |
--committable returns the first independent file (sorted lexicographically by path) that has no dependencies on other uncommitted changes.
while [ -n "$(darna --committable)" ]; do
git add $(darna --committable)
git commit -m "feat: add $(darna --committable)"
done--committable --dependants returns the first independent file plus direct dependants that only depend on that file and committed code.
# Commit in larger atomic sets when possible.
while [ -n "$(darna --committable --dependants)" ]; do
FILES=$(darna --committable --dependants)
git add $FILES
git commit -m "feat: add $FILES"
doneThis mode enables building multi-commit patchsets more efficiently by grouping related changes together while maintaining atomicity.
The --commit-msg flag generates Conventional Commits format messages from staged changes using local LLM agents.
# Generate message for currently staged files
darna --commit-msg=claude
# Use in commit workflow
git add $(darna --committable)
git commit -m "$(darna --commit-msg=claude)"claude- Claude Code CLI (claude -p)codex- OpenAI Codex CLI (codex exec)mistral- Mistral CLI (mistral -p)opencode- OpenCode CLI (opencode run)
Agents must be installed separately and available in PATH.
Override the default Conventional Commits prompt with --prompt-file:
# Create custom prompt
cat > .darna-commit-prompt.txt <<'EOF'
Generate a concise commit message for this diff.
Use imperative mood, lowercase, no period.
Max 50 characters.
EOF
# Use custom prompt
darna --commit-msg=claude --prompt-file .darna-commit-prompt.txtThe built-in prompt generates messages following Conventional Commits:
<type>[optional scope]: <description>
Types: feat, fix, refactor, docs, test, chore, ci, perf, style, build
Scope: optional, in parentheses if present
Description: imperative mood, lowercase, no period, max 72 chars after prefix
Only the first line (summary) is generated. Commit bodies are not included because atomic commits are inherently small and focused.
Combine --committable and --commit-msg for fully automated atomic commits:
while [ -n "$(darna --committable)" ]; do
git add $(darna --committable)
git commit -m "$(darna --commit-msg=claude)"
doneThis is particularly useful for autonomous coding agents that need to checkpoint progress incrementally.
- No staged changes: Returns error "no staged changes (stage files with git add first)"
- Agent not installed: Returns error "agent not found: is not installed"
- Agent timeout: 30 second default timeout for LLM generation
Files are sorted lexicographically by path. The first file that is independent (has no dependencies on other unstaged files) is selected as the base file. When --dependants is used, direct dependants are added to the set - files that:
- Depend on the base file
- Are in the changeset (unstaged or untracked)
- Have NO dependencies on OTHER changeset files (only the base file + committed code)
Transitive dependants (dependants of dependants) are excluded to maintain atomicity.
#!/bin/sh
darnaDarna exits non-zero on violations, blocking the commit.
- Git status - categorize files as staged, unstaged, or untracked using
git status --porcelain -z. - Package loading - load all Go packages with full type information via
golang.org/x/tools/go/packages. For partially staged files (statusMM), an overlay withgit show :pathcontent ensures analysis reflects what will actually be committed. - Dependency graph - walk
types.Info.Defsandtypes.Info.Usesto build a bidirectional symbol dependency graph with transitive reachability. - Violation detection - for each symbol in a staged file, check whether its dependencies are all satisfied by staged or committed files. Report any that require unstaged files.
- Commit message generation (optional) - when
--commit-msgis used, invoke the specified LLM agent withgit diff --cachedcontent and the prompt (default or custom via--prompt-file), extract the first line of output, and return as the commit message.
cmd/darna/ CLI entry point
internal/agent/ LLM agent integrations for commit message generation
internal/analyzer/ Go package loading and symbol extraction
internal/git/ Git command wrappers (staged files, content, status)
internal/graph/ Symbol dependency graph construction and traversal
internal/validator/ Validation orchestration and committable file selection
docs/decisions/ Architecture decision records
make all # lint + test + build
make lint # golangci-lint
make test # tests with race detector and coverage
make clean # remove artifactsFor design rationale and implementation details of commit message generation, see ADR-002: LLM-powered commit message generation.
See LICENSE.