Skip to content

feat: add Git-AI trailer with UUID to commits and authorship notes#622

Closed
svarlamov wants to merge 13 commits intomainfrom
devin/1772489310-git-ai-trailer-uuid
Closed

feat: add Git-AI trailer with UUID to commits and authorship notes#622
svarlamov wants to merge 13 commits intomainfrom
devin/1772489310-git-ai-trailer-uuid

Conversation

@svarlamov
Copy link
Copy Markdown
Member

@svarlamov svarlamov commented Mar 2, 2026

feat: add Git-AI trailer with UUID to commits and authorship notes

Summary

Adds a Git-AI: <uuid> trailer to every commit message and stores the same UUID as a new id field in the authorship note metadata, creating a link between the two. Also ensures the id is preserved or generated across all rewrite flows (rebase, cherry-pick, CI squash/rebase).

How it works (commit flow):

  1. Pre-commit: Generate UUID v4, write to .git/ai/pending_note_id temp file via RepoStorage
  2. Wrapper mode: Inject --trailer "Git-AI: <uuid>" into the git commit args forwarded to real git (inserted before any -- pathspec separator). A -c trailer.Git-AI.ifExists=replace global arg ensures --amend replaces the original trailer rather than appending a duplicate.
  3. Hooks mode: In prepare-commit-msg, use git interpret-trailers --in-place --if-exists replace to append (or replace) the trailer in the commit message file. The needs_prepare_commit_msg_handling() gate was updated to also trigger when a pending_note_id file exists. A cherry-pick guard prevents stale UUIDs from aborted commits leaking into cherry-picked messages.
  4. Post-commit / amend: Read the pending UUID from the temp file, set authorship_log.metadata.id, then clean up the temp file

How it works (rewrite flows):

  • Rebase (rewrite_authorship_after_rebase_v2): On the full recompute path, reads the original commit's note id via get_reference_as_authorship_log_v3 and carries it to the rewritten commit's note. Uses .and_then() chaining to always reset id each loop iteration, preventing stale id leakage when a source note lookup fails. The fast-path remap already preserved id since it only touches base_commit_sha.
  • Cherry-pick (rewrite_authorship_after_cherry_pick): Same pattern — source commit's id is read and set on the cherry-picked commit's note during recomputation. Fresh authorship_log per iteration means no cross-iteration leak risk.
  • CI squash/rebase (rewrite_authorship_after_squash_or_rebase): Generates a new UUID v4 for the merge note since there's no single source commit to inherit from.
  • Metadata-only notes (build_metadata_only_authorship_log_from_source_notes): Also generates a new UUID v4 for notes that carry only prompt metadata without file attestations.

The id field on AuthorshipMetadata is Option<String> with #[serde(default, skip_serializing_if = "Option::is_none")] for backward compatibility with existing notes.

Worktree support: pending_note_id_exists() delegates to the canonical worktree_storage_ai_dir() helper (made pub(crate)) for path resolution. A new resolve_git_common_dir() reads git's commondir file from linked worktree git directories, matching how git itself resolves the common directory — no manual path reconstruction needed.

Review & Testing Checklist for Human

  • Verify --amend produces exactly one Git-AI trailer: Run git commit --amend --no-edit (and repeated amends) and confirm only one Git-AI trailer is present via git log --format='%(trailers:key=Git-AI)'. The fix relies on trailer.Git-AI.ifExists=replace (wrapper) and --if-exists replace (hooks) — verify these work with the project's minimum supported git version (flags added in git 2.15).
  • Verify rebase id preservation on the recompute path: The rebase tests mostly exercise the fast-path remap (which trivially preserves id). To test the full recompute path, rebase commits that modify AI-touched files with content conflicts, then verify note id via git notes --ref=refs/notes/ai show <new-commit>. Pay particular attention to the .and_then() reset pattern at rebase_authorship.rs:729-735 — this prevents inter-iteration id leakage when a source note lookup fails.
  • Verify cherry-pick id preservation uses fresh authorship_log per iteration: The cherry-pick path still uses nested if let (not .and_then()), relying on a fresh authorship_log being created each loop iteration. Confirm this assumption holds by reviewing the loop structure at rebase_authorship.rs:947-955.
  • Verify the cherry-pick guard in prepare-commit-msg: The new !is_cherry_pick_in_progress(&repo) guard at git_hook_handlers.rs:2455 prevents stale pending_note_id files (from aborted commits) from leaking into cherry-picked messages. This relies on CHERRY_PICK_HEAD existing when prepare-commit-msg fires during cherry-pick — standard git behavior, but worth manual verification. Note: there is no automated test for the abort→cherry-pick stale trailer scenario; only the guard itself was added.
  • Confirm CI squash/rebase UUID generation semantics: Merge commits get a new UUID rather than inheriting from any source commit. Verify this is desired behavior — the merge note id will NOT match source commits.
  • Test plan: Run task test in both wrapper (default) and hooks mode (GIT_AI_TEST_GIT_MODE=hooks). All 27 tests pass locally (16 original trailer tests + 11 new rewrite tests). Manually test with git-ai installed: make commits, amend (multiple times), rebase, cherry-pick, and CI-style merges in both main and linked worktrees. Verify trailers in git log and note id matches via git notes --ref=refs/notes/ai show <commit>. For rebase/cherry-pick, confirm id preservation across rewrites. Test the cherry-pick guard by aborting a commit (close editor) then cherry-picking — verify no stale trailer appears.

Notes

  • Tests added: 16 original trailer UUID tests (8 base + 8 worktree variants) + 11 new rewrite tests (covering rebase, cherry-pick, CI squash paths, including worktree variants) = 27 total, all passing in both wrapper and hooks mode
  • Insta snapshots: 3 snapshots updated to include the new id: None field
  • Existing tests: commit_hooks_comprehensive.rs tests updated for the &mut signature change on commit_pre_command_hook
  • Git-compat whitelist: Expanded significantly to cover trailer-induced commit message format changes (38 new entries across 9 test files)
  • Missing from original spec: test_dry_run_commit_no_trailer (test 8 of 9) was not implemented. Dry-run commits correctly skip trailer injection but lack a dedicated test.
  • Signature change: commit_pre_command_hook signature changed to &mut ParsedGitInvocation to allow in-place modification of args for trailer injection
  • Git version requirement: The --if-exists flag for git interpret-trailers and -c trailer.<key>.ifExists config were added in git 2.15 (Oct 2017). Verify this aligns with the project's minimum supported git version.

Updates since last revision:

  • Added id preservation during rebase rewrites (rewrite_authorship_after_rebase_v2)
  • Added id preservation during cherry-pick rewrites (rewrite_authorship_after_cherry_pick)
  • Added UUID generation for CI squash/rebase merges (rewrite_authorship_after_squash_or_rebase, build_metadata_only_authorship_log_from_source_notes)
  • Added 11 comprehensive tests in tests/rewrite_trailer_uuid.rs covering all rewrite flows using TDD methodology
  • Fixed rebase loop id leak bug: Changed from nested if let to .and_then() chain to ensure current_authorship_log.metadata.id always resets when source note lookup fails, preventing inter-iteration leakage
  • Fixed stale UUID leak in cherry-pick: Added !is_cherry_pick_in_progress(&repo) guard to prepare-commit-msg trailer injection to prevent stale pending_note_id from aborted commits leaking into cherry-picked messages
  • Fixed duplicate Git-AI trailers on --amend: Both wrapper mode (via -c trailer.Git-AI.ifExists=replace) and hooks mode (via --if-exists replace) now replace existing trailers instead of appending duplicates, preventing accumulation of stale trailers on repeated amends
  • Applied cargo fmt formatting

Link to Devin Session: https://app.devin.ai/sessions/ecd79d3689984605abd6bfd6b01280ad
Requested by: @svarlamov


Open with Devin

- Add id field to AuthorshipMetadata for storing per-note UUID
- Generate UUID v4 in pre-commit hook, store via RepoStorage
- Inject Git-AI trailer in wrapper mode via --trailer flag
- Inject Git-AI trailer in hooks mode via git interpret-trailers
- Read pending UUID in post_commit and amend flows, set on metadata
- Add 8 tests (+ 8 worktree variants) covering trailer/UUID behavior
- Update snapshot tests for new id field

Co-Authored-By: Sasha Varlamov <sasha@sashavarlamov.com>
@devin-ai-integration
Copy link
Copy Markdown
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

@git-ai-cloud-dev
Copy link
Copy Markdown

git-ai-cloud-dev Bot commented Mar 2, 2026

Stats powered by Git AI

🧠 you    ░░░░░░░░░░░░░░░░░░░░  0%
🤖 ai     ████████████████████  100%
More stats
  • 1.0 lines generated for every 1 accepted
  • 0 seconds waiting for AI
  • Top model: devin::unknown (1195 accepted lines, 1195 generated lines)

AI code tracked with git-ai

@git-ai-cloud
Copy link
Copy Markdown

git-ai-cloud Bot commented Mar 2, 2026

Stats powered by Git AI

🧠 you    ███████████████████░  93%
🤖 ai     ░░░░░░░░░░░░░░░░░░░█  7%
More stats
  • 1.1 lines generated for every 1 accepted
  • 0 seconds waiting for AI
  • Top model: codex::gpt-5.3-codex (55 accepted lines, 61 generated lines)

AI code tracked with git-ai

@CLAassistant
Copy link
Copy Markdown

CLAassistant commented Mar 2, 2026

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
1 out of 2 committers have signed the CLA.

✅ svarlamov
❌ devin-ai-integration[bot]
You have signed the CLA already but the status is still pending? Let us recheck it.

devin-ai-integration[bot]

This comment was marked as resolved.

…for hooks mode, update git-compat whitelist

- Insert --trailer before any '--' pathspec separator to avoid git treating it as a file path
- Update needs_prepare_commit_msg_handling() to also return true when pending_note_id exists, fixing hooks mode trailer injection
- Update git-compat whitelist to include new trailer-induced test failures

Co-Authored-By: Sasha Varlamov <sasha@sashavarlamov.com>
devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration Bot and others added 6 commits March 2, 2026 22:46
…s mode

The pending_note_id file for linked worktrees is stored at
<common_dir>/ai/worktrees/<name>/pending_note_id, but the previous check
only looked at <git_dir>/ai/pending_note_id. Extract a pending_note_id_exists
helper that checks both the non-worktree and worktree paths.

Co-Authored-By: Sasha Varlamov <sasha@sashavarlamov.com>
Co-Authored-By: Sasha Varlamov <sasha@sashavarlamov.com>
Co-Authored-By: Sasha Varlamov <sasha@sashavarlamov.com>
Co-Authored-By: Sasha Varlamov <sasha@sashavarlamov.com>
… resolution

Replace manual worktree path construction in pending_note_id_exists() with
the canonical worktree_storage_ai_dir() helper. Add resolve_git_common_dir()
to read git's commondir file for linked worktrees, matching how git itself
resolves the common directory.

Co-Authored-By: Sasha Varlamov <sasha@sashavarlamov.com>
…rebase

- Rebase (rewrite_authorship_after_rebase_v2): preserve source commit note id on recompute path
- Cherry-pick (rewrite_authorship_after_cherry_pick): preserve source commit note id on recompute path
- CI squash/rebase (rewrite_authorship_after_squash_or_rebase): generate new UUID for merge notes
- Metadata-only notes (build_metadata_only_authorship_log_from_source_notes): generate new UUID
- Add 11 TDD tests covering all three rewrite flows including worktree variants

Co-Authored-By: Sasha Varlamov <sasha@sashavarlamov.com>
devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration Bot and others added 2 commits March 3, 2026 06:26
…us commit's id

Co-Authored-By: Sasha Varlamov <sasha@sashavarlamov.com>
Co-Authored-By: Sasha Varlamov <sasha@sashavarlamov.com>
devin-ai-integration[bot]

This comment was marked as resolved.

…o prevent stale UUID leak

Co-Authored-By: Sasha Varlamov <sasha@sashavarlamov.com>
@devin-ai-integration
Copy link
Copy Markdown
Contributor

Addressed the stale pending_note_id issue from Devin Review Comment 17 in commit 688e886 — added !is_cherry_pick_in_progress(&repo) guard to the prepare-commit-msg trailer injection. This prevents a stale UUID from an aborted commit leaking into cherry-picked commits.

Full code review summary:

All 5 Devin Review issues have been addressed:

  1. prepare-commit-msg skipped for normal commits in hooks mode (fixed in 9fe1c6a)
  2. --trailer args after -- separator (fixed in 9fe1c6a)
  3. Wrong worktree path in pending_note_id check (fixed in a99732a, refactored in 098db92)
  4. Rebase loop id leak (fixed in 0810637)
  5. Stale pending_note_id during cherry-pick (fixed in 688e886)

Additional review findings (no issues):

  • Insert order for --trailer args is correct
  • No double-trailer risk (hooks mode discards wrapper args)
  • No race condition risk (git index.lock prevents concurrent commits; worktrees have separate storage)
  • Cherry-pick id preservation uses fresh authorship_log per iteration — no leak risk
  • .and_then() chain in rebase correctly resets id each iteration
  • resolve_git_common_dir correctly reads git's commondir file for linked worktrees

devin-ai-integration[bot]

This comment was marked as resolved.

@git-ai-bot-svarlamov-dev
Copy link
Copy Markdown

Stats powered by Git AI

🧠 you    ███████████████████░  95%
🤖 ai     ░░░░░░░░░░░░░░░░░░░█  5%
More stats
  • 1.0 lines generated for every 1 accepted
  • 0 seconds waiting for AI
  • Top model: codex::gpt-5.3-codex (61 accepted lines, 61 generated lines)

AI code tracked with git-ai

@svarlamov svarlamov closed this Mar 11, 2026
@svarlamov svarlamov deleted the devin/1772489310-git-ai-trailer-uuid branch April 4, 2026 20:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants