tummychow/git-absorb
git commit --fixup, but automatic
Healthy across the board
weakest axisPermissive license, no critical CVEs, actively maintained — safe to depend on.
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
No critical CVEs, sane security posture — runnable as-is.
- ✓Last commit 3mo ago
- ✓10 active contributors
- ✓Distributed ownership (top contributor 49% of recent commits)
Show all 6 evidence items →Show less
- ✓BSD-3-Clause licensed
- ✓CI configured
- ✓Tests present
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Healthy" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/tummychow/git-absorb)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/tummychow/git-absorb on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: tummychow/git-absorb
Generated by RepoPilot · 2026-05-09 · Source
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/tummychow/git-absorb shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
🎯Verdict
GO — Healthy across the board
- Last commit 3mo ago
- 10 active contributors
- Distributed ownership (top contributor 49% of recent commits)
- BSD-3-Clause licensed
- CI configured
- Tests present
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live tummychow/git-absorb
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/tummychow/git-absorb.
What it runs against: a local clone of tummychow/git-absorb — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in tummychow/git-absorb | Confirms the artifact applies here, not a fork |
| 2 | License is still BSD-3-Clause | Catches relicense before you depend on it |
| 3 | Default branch master exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 113 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of tummychow/git-absorb. If you don't
# have one yet, run these first:
#
# git clone https://github.com/tummychow/git-absorb.git
# cd git-absorb
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of tummychow/git-absorb and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "tummychow/git-absorb(\\.git)?\\b" \\
&& ok "origin remote is tummychow/git-absorb" \\
|| miss "origin remote is not tummychow/git-absorb (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(BSD-3-Clause)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"BSD-3-Clause\"" package.json 2>/dev/null) \\
&& ok "license is BSD-3-Clause" \\
|| miss "license drift — was BSD-3-Clause at generation time"
# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
&& ok "default branch master exists" \\
|| miss "default branch master no longer exists"
# 4. Critical files exist
test -f "src/main.rs" \\
&& ok "src/main.rs" \\
|| miss "missing critical file: src/main.rs"
test -f "src/lib.rs" \\
&& ok "src/lib.rs" \\
|| miss "missing critical file: src/lib.rs"
test -f "src/stack.rs" \\
&& ok "src/stack.rs" \\
|| miss "missing critical file: src/stack.rs"
test -f "src/commute.rs" \\
&& ok "src/commute.rs" \\
|| miss "missing critical file: src/commute.rs"
test -f "src/config.rs" \\
&& ok "src/config.rs" \\
|| miss "missing critical file: src/config.rs"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 113 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~83d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/tummychow/git-absorb"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
⚡TL;DR
git-absorb is a Rust CLI tool that automates the creation of fixup commits by analyzing staged changes and automatically identifying which commits in your current branch modified the same lines. It's a Git reimplementation of Mercurial's hg absorb feature, eliminating manual git commit --fixup or interactive rebase workflows. The --and-rebase flag can automatically fold these fixup commits back into their targets using git's autosquash mechanism. Single Rust binary architecture. Core logic in src/lib.rs exports high-level functions; src/main.rs handles CLI argument parsing with clap (derive macros in Cargo.toml). Domain-specific modules: src/commute.rs (detecting safe commit reordering), src/stack.rs (managing commit stacks), src/config.rs (configuration), src/owned.rs (memory management utilities). Tests located in src/tests/ (log_utils.rs, repo_utils.rs) and integration tests in test/create.sh.
👥Who it's for
Git developers and teams practicing atomic commit discipline who want to apply code review feedback without manually running git commit --fixup or interactive rebases. Specifically useful for feature branch workflows where reviewers suggest fixes that should logically belong in earlier commits rather than new squashed commits.
🌱Maturity & risk
Actively maintained and production-ready. At v0.9.0 with a clear release pipeline (GitHub Actions CI for build.yml and release.yml), comprehensive shell-based integration tests in test/create.sh, and published artifacts across Windows/macOS/Linux platforms. The repository shows active maintenance with packaged distributions available in multiple system package managers (per repology badge).
Low risk for a single-maintainer project. Dependency surface is minimal (git2 for libgit2 bindings, clap for CLI, slog for logging, anyhow for error handling) with few direct dependencies. The Rust edition 2021 requirement (MSRV 1.74.1) is relatively recent but stable. Primary risk is single maintainer (tummychow) and no visible issue tracker data, though the v0.9.0 version suggests stability.
Active areas of work
Unable to determine from provided data — no recent commit timestamps or PR list visible. However, the presence of shell completion generation (clap_complete, clap_complete_nushell dependencies) and active CI workflows suggests ongoing development focus on usability (shell integrations) and release automation.
🚀Get running
git clone https://github.com/tummychow/git-absorb.git
cd git-absorb
cargo build --release
./target/release/git-absorb --help
For testing: cargo test (runs Rust unit tests + integration test harness in test/create.sh).
Daily commands:
This is a CLI tool, not a server. After building with cargo build --release, invoke directly: git-absorb --and-rebase (in a Git repo with staged changes) or use git integration: git absorb --help (if installed as git subcommand). Shell completions available via clap_complete for bash/zsh/fish/nushell.
🗺️Map of the codebase
src/main.rs— Entry point and CLI argument parsing; defines the absorb command interface and workflow orchestration.src/lib.rs— Core public API and main absorb algorithm; entry point for all core logic that matches hunks to commits.src/stack.rs— Commit stack representation and manipulation; handles the list of commits being rebased and their patch application.src/commute.rs— Hunk commutation logic; determines whether hunks can be reordered to apply cleanly to their target commits.src/config.rs— Configuration loading and CLI option handling; controls absorb behavior (merge strategy, filters, logging).Cargo.toml— Project manifest defining Rust version, dependencies (git2, clap, slog), and release profile with LTO.Documentation/git-absorb.adoc— User-facing documentation detailing command-line options, behavior, and examples for the absorb workflow.
🧩Components & responsibilities
- CLI (main.rs) (clap, anyhow) — Parse arguments, validate paths, call absorb library, print results and exit codes
- Failure mode: Argument validation failure halts before absorb; invalid repo path fails early with clear error
- Absorb Engine (lib.rs) (git2, memchr for diff parsing) — Orchestrate hunk extraction, matching to commits, and safety checks; main algorithm
- Failure mode: Partial absorb on conflict; returns error with list of hunks that could not be absorbed
- CommitStack (stack.rs) (git2 Tree/Index API) — Maintain ordered list of commits, apply hunks via git2 patch, track state
- Failure mode: Hunk apply fails if target lines changed or index corrupted; error returned to absorb engine
- Commute Logic (commute.rs) (git2 diff and patch simulation) — Test whether a hunk can be applied in a different order; rebase feasibility check
- Failure mode: Conservative: marks hunk non-commutable if test fails; may reject valid commutations
- Config (config.rs) (slog configuration) — Store and apply user preferences (merge strategy, filters, log level)
- Failure mode: Invalid config enum value rejected by clap; default fallback for optional fields
🔀Data flow
User terminal→CLI (main.rs)— Command-line arguments and git repository contextCLI→Config— Parsed clap arguments converted to Config structConfig→Absorb Engine— Settings (merge strategy, filters, verbosity) passed to absorb()Absorb Engine→git2 Repository— Read repo state, extract hunks from working directory, query commit graphgit2 Repository→CommitStack— Commits and index state passed for hunk application simulationCommitStack→Commute Logic— Hunk and commit pair tested for commutation feasibilityCommute Logic→Absorb Engine— Boolean result (can commute?) feeds hunk-to-commit matching decision
🛠️How to make changes
Add a new absorb filter or merge strategy
- Define filter logic in src/lib.rs absorb() function where hunks are matched to commits (
src/lib.rs) - Add CLI option flag in src/config.rs Config struct (
src/config.rs) - Parse the new flag in src/main.rs clap derive struct (
src/main.rs) - Document the new option in Documentation/git-absorb.adoc (
Documentation/git-absorb.adoc)
Improve hunk-to-commit matching logic
- Study the current matching algorithm in src/lib.rs (main absorb loop) (
src/lib.rs) - Understand hunk commutation rules in src/commute.rs (
src/commute.rs) - Modify the matching heuristic or CommitStack::apply_hunk() in src/stack.rs (
src/stack.rs) - Add test cases using repo_utils in src/tests/repo_utils.rs (
src/tests/repo_utils.rs)
Add a new git operation or safety check
- Use git2 bindings (already imported in src/lib.rs) to implement the git operation (
src/lib.rs) - If git2 objects need manual lifetime handling, wrap them in src/owned.rs (
src/owned.rs) - Integrate the check into the absorb() function flow (
src/lib.rs)
🔧Why these technologies
- git2 — Direct access to libgit2 for repository inspection, hunk diffing, and commit manipulation without spawning git subprocess
- clap with derive macros — Declarative CLI argument parsing with automatic help generation, validation, and shell completion support
- slog for structured logging — Debug-level tracing of hunk matching, commutation failures, and commit amendment for troubleshooting absorb logic
- Rust with LTO — Memory safety for git operations; LTO release profile reduces binary size and improves performance on large repos
⚖️Trade-offs already made
-
Hunk-level granularity instead of file-level absorb
- Why: Facebook's hg absorb matches by modified lines; more precise than file-level matching
- Consequence: Higher accuracy but slower on large changesets with many hunks; requires robust commutation logic
-
Fail partial absorb rather than auto-resolve conflicts
- Why: Prevents silent data loss or incorrect rebasing; user retains full control
- Consequence: User must manually resolve conflicts; reduces automation but maintains safety and predictability
-
Single-threaded hunk matching loop
- Why: Simpler implementation; hunk processing is typically I/O-bound on git2, not CPU-bound
- Consequence: Linear scaling with hunk count; fine for typical workflows, may be slow on 10k+ hunk patches
🚫Non-goals (don't propose these)
- Does not handle merge conflicts automatically; absorb halts and leaves failed hunks uncommitted
- Does not support public/published commits; only absorbs into draft (uncommitted-ancestor) commits
- Does not provide an interactive mode to select which hunks absorb to which commits
- Does not support non-linear histories (side branches); assumes a linear commit stack
🪤Traps & gotchas
The --and-rebase flag modifies your working branch history in place; if rebase fails mid-operation, you may need git reflog recovery. Requires a valid Git repository with at least one commit (empty repos will fail). The tool uses git2's default backends, so unusual Git configurations (custom merge drivers, filter attributes) may not be respected. Staged changes with merge conflicts during commute analysis remain unstaged; the tool does not auto-resolve.
🏗️Architecture
💡Concepts to learn
- Commit commutation (history rewriting) — The core algorithm in src/commute.rs must detect when two commits can swap positions in history without changing file content — essential for identifying safe targets for absorbing changes without conflicts
- Line-to-commit attribution (blame/hunk detection) — git-absorb must map each staged change (at the line level) back to the specific commit that originally introduced it; this requires efficient hunk parsing and diff matching across the commit DAG
- Fixup commits and autosquash — The tool generates commits with 'fixup!' prefixes that Git's --autosquash rebase mode recognizes for automatic folding; understanding this protocol is key to how --and-rebase works
- libgit2 object model (commits, trees, blobs) — The git2 crate wraps libgit2 C bindings; understanding commit ownership, ref updates, and tree construction is needed for modifying the repository safely in src/lib.rs and src/stack.rs
- DAG (directed acyclic graph) traversal — git-absorb must traverse the commit graph backward from HEAD to find all commits reachable in the current branch, filtering mutable (not public) ancestors — critical logic in src/stack.rs
- Staging area (index) manipulation — The tool reads staged hunks from the Git index and must accurately parse unified diff format to extract line numbers and content, done via git2's diff API in src/lib.rs
- Structured logging with slog — The codebase uses slog for debug output; understanding its macro-based API and log level filtering (max_level_debug in Cargo.toml) helps you trace algorithm execution without recompiling
🔗Related repos
facebook/sapling— Sapling is Facebook's modern version-control system that evolved from Mercurial and includes hg absorb, the original inspiration for this toolmozilla/moz-phab— Mozilla's Phabricator integration tool used in conjunction with absorb-like workflows for handling review feedback on feature branchesgit-ecosystem/git-interactive-rebase-tool— Alternative to --and-rebase: provides a TUI for interactive rebasing, solving the same problem of commit reorganization but with manual controldandavison/delta— Popular Git diff pager often used alongside git-absorb to review staged changes before absorptionextrawurst/gitui— Rust TUI Git client that could integrate git-absorb as a builtin workflow command
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add integration tests for commute.rs edge cases
The src/commute.rs module handles the critical logic of determining whether changes can be reordered without conflicts. Currently, src/tests/ has repo_utils.rs and log_utils.rs but no dedicated test file for commute.rs. This module is central to git-absorb's core functionality (deciding which hunks to absorb into which commits). Adding comprehensive integration tests for edge cases like overlapping line ranges, bidirectional commute attempts, and conflicting patches would catch regressions early.
- [ ] Create src/tests/commute_test.rs
- [ ] Add test cases for: hunks that commute cleanly, hunks that don't commute, empty diffs, hunks at file boundaries
- [ ] Test bidirectional commute scenarios (A→B and B→A)
- [ ] Use repo_utils.rs helpers to set up test repositories with specific commit histories
Add pre-commit hook and shellcheck validation to GitHub Actions
The repo contains shell scripts (test/create.sh) but has no CI validation for them. The .github/workflows/build.yml and release.yml exist but there's no workflow checking shell script syntax or running shellcheck. This catches shell errors early and ensures test scripts are valid before they're relied upon.
- [ ] Create .github/workflows/lint.yml with shellcheck step for test/create.sh
- [ ] Add shell syntax validation using 'bash -n'
- [ ] Configure shellcheck to fail on SC2086 (unquoted variables) and similar common issues
- [ ] Document any shellcheck exclusions in a .shellcheckrc file if needed
Add verbose logging integration tests in src/tests/
The codebase uses slog for structured logging (slog, slog-term, slog-extlog dependencies), but there are no tests verifying that log output is correct at different verbosity levels. Currently test infrastructure has repo_utils.rs and log_utils.rs, but log_utils.rs isn't used in any visible tests. Add tests that verify git-absorb produces expected log messages when absorbing commits, which helps catch silent failures and improves debuggability for users.
- [ ] Create src/tests/logging_test.rs that captures slog output during absorb operations
- [ ] Use slog-extlog (already in dev-dependencies) to capture log events
- [ ] Add assertions for: successful absorb operations log 'absorbed X hunks', conflicting hunks are logged with file paths, debug mode provides extra detail
- [ ] Test across slog log levels (info, debug, warn)
🌿Good first issues
- Add documentation for the src/commute.rs line-matching algorithm in src/commute.rs itself — currently no inline comments explaining the heuristic for detecting 'same lines' across commits, making it hard for contributors to understand or modify the core matching logic
- Extend src/tests/repo_utils.rs with a helper function to create merge conflicts during test setup, then add unit tests to src/commute.rs validating behavior when commuting changes that genuinely conflict (currently unclear if all conflict cases are covered)
- Add a --dry-run flag to src/main.rs that outputs the proposed fixup commits as JSON (use serde_json which is already in dev-dependencies) without actually writing them, improving user confidence before running --and-rebase
⭐Top contributors
Click to expand
Top contributors
- @blairconrad — 49 commits
- @tummychow — 32 commits
- @arielf212 — 9 commits
- @sh-at-cs — 4 commits
- @p1k0chu — 1 commits
📝Recent commits
Click to expand
Recent commits
debdcd2— bump to 0.9.0 (tummychow)5cef2e4— Merge pull request #208 from blairconrad/restore-verbose-logging (tummychow)aa056a1— Enable debug-level log messages (blairconrad)ddda0b7— Merge pull request #204 from p1k0chu/118-root-option (tummychow)35e0dd7— Add --no-limit option (p1k0chu)a1de60b— Merge pull request #203 from radiosilence/ci-darwin-arm64 (tummychow)5d738cb— ci: add darwin arm64 (aarch64-apple-darwin) build target (radiosilence)3a1148e— Merge pull request #200 from blairconrad/alternative-output (tummychow)8ef9791— Announce commit header in human friendly format (blairconrad)b4a0c38— Characterize header message format (blairconrad)
🔒Security observations
The codebase demonstrates generally good security practices with no critical vulnerabilities detected. The project is a Rust CLI tool with minimal external attack surface. Key observations: (1) No hardcoded credentials or secrets found; (2) No SQL injection or XSS risks (CLI tool, not web-based); (3) Dependencies are from reputable sources with reasonable versions; (4) No Docker configuration exposed, suggesting this is a compiled binary tool. Primary concerns are around library feature configuration (git2 default-features disabled) and debug logging in production. The moderate security score reflects these lower-severity configuration concerns rather than exploitable vulnerabilities. Overall security posture is solid for a CLI development tool.
- Medium · git2 library with default-features disabled —
Cargo.toml - [dependencies.git2]. The git2 dependency (v0.20) has default-features explicitly disabled. While this can be intentional for reducing attack surface, it may disable important security features or SSL/TLS verification. The version 0.20 is relatively recent but should be verified for known CVEs. Fix: Review why default features are disabled. Ensure SSL/TLS verification is explicitly enabled if needed for secure git operations. Consider upgrading to the latest git2 version and re-evaluate feature flags. - Low · Outdated Rust MSRV —
Cargo.toml - rust-version field. The rust-version specified is 1.74.1, which is relatively old (released November 2023). While not a direct vulnerability, using older Rust versions may miss critical compiler-level security improvements and bug fixes. Fix: Consider updating rust-version to a more recent stable release (1.75+) to ensure access to latest security patches and compiler improvements. - Low · slog max_level_debug in production —
Cargo.toml - [dependencies.slog]. The slog dependency uses 'max_level_debug' feature, which may enable debug-level logging in release builds. This could potentially expose sensitive information like file paths, git object IDs, or command line arguments in logs. Fix: Consider using 'max_level_info' or 'max_level_warn' for release builds. Use conditional compilation to enable debug logging only in development.
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.