[PR #558] migrate initial attributions during rebase #565

Closed
opened 2026-03-02 04:13:59 +03:00 by kerem · 0 comments
Owner

Original Pull Request: https://github.com/git-ai-project/git-ai/pull/558

State: closed
Merged: No


Fix: Migrate INITIAL attributions during rebase

Problem

When a rebase rewrites commit SHAs, uncommitted AI attributions stored in the working log's INITIAL file are orphaned on the old (pre-rebase) SHA. Working logs are keyed by commit SHA under .git/ai/working_logs/<sha>/, so after a rebase changes HEAD from abc123 to def456, the INITIAL attributions remain stranded at .git/ai/working_logs/abc123/INITIAL while the developer is now working on def456.

When the developer eventually commits, git-ai reads the working log for the current HEAD — which has no INITIAL — and the AI-authored lines are silently attributed to the human author instead.

This affects any workflow that triggers a rebase:

  • gt sync (fetches trunk + rebases feature branches)
  • gt restack (rebases branches to maintain stack ordering)
  • git rebase (direct rebase)

Fix

migrate_working_log_after_rebase() — called from rewrite_authorship_if_needed after RebaseComplete events, migrates the working log from the pre-rebase HEAD to the post-rebase HEAD:

  • Only old working log exists: renames the entire directory (preserving INITIAL, checkpoints, and blobs) via the existing rename_working_log method.
  • Both old and new working logs exist: merges only INITIAL attributions into the new directory. Checkpoints from the old directory are intentionally dropped since the new directory's checkpoints already reflect the post-rebase state. The old working log is then cleaned up.
  • No old working log: no-op.

has_working_log() on RepoStorage — checks for directory existence without creating it (unlike working_log_for_base_commit which auto-creates via create_dir_all).

VirtualAttributions::from_full_state() — fixes a related issue where the final_state file contents map only included files from the checkpoint VA, dropping files tracked exclusively via blame/notes. Now unions both VAs' file contents so committed AI work on disjoint files isn't lost when merging with INITIAL.

Tests

Four new unit tests in rebase_authorship::tests:

Test Scenario
rebase_complete_migrates_initial_to_new_head Single-file INITIAL is migrated after rebase (happy path)
rebase_complete_no_initial_is_noop No working log on old HEAD — no-op, no crash
rebase_complete_migrates_multi_file_initial Multi-file INITIAL with multiple prompt records migrated correctly
rebase_complete_merges_initial_when_both_working_logs_exist Old INITIAL merged into new HEAD that already has checkpoints; old working log cleaned up

Reproduction script

scripts/reproduce_gitai_orphan.py — end-to-end reproduction using real git-ai and gt commands. Walks through the full lifecycle: checkpoint, commit, amend, rebase, and validates attribution at each phase.

**Original Pull Request:** https://github.com/git-ai-project/git-ai/pull/558 **State:** closed **Merged:** No --- ## Fix: Migrate INITIAL attributions during rebase ### Problem When a rebase rewrites commit SHAs, uncommitted AI attributions stored in the working log's `INITIAL` file are orphaned on the old (pre-rebase) SHA. Working logs are keyed by commit SHA under `.git/ai/working_logs/<sha>/`, so after a rebase changes HEAD from `abc123` to `def456`, the INITIAL attributions remain stranded at `.git/ai/working_logs/abc123/INITIAL` while the developer is now working on `def456`. When the developer eventually commits, git-ai reads the working log for the current HEAD — which has no INITIAL — and the AI-authored lines are silently attributed to the human author instead. This affects any workflow that triggers a rebase: - `gt sync` (fetches trunk + rebases feature branches) - `gt restack` (rebases branches to maintain stack ordering) - `git rebase` (direct rebase) ### Fix **`migrate_working_log_after_rebase()`** — called from `rewrite_authorship_if_needed` after `RebaseComplete` events, migrates the working log from the pre-rebase HEAD to the post-rebase HEAD: - **Only old working log exists**: renames the entire directory (preserving INITIAL, checkpoints, and blobs) via the existing `rename_working_log` method. - **Both old and new working logs exist**: merges only INITIAL attributions into the new directory. Checkpoints from the old directory are intentionally dropped since the new directory's checkpoints already reflect the post-rebase state. The old working log is then cleaned up. - **No old working log**: no-op. **`has_working_log()`** on `RepoStorage` — checks for directory existence without creating it (unlike `working_log_for_base_commit` which auto-creates via `create_dir_all`). **`VirtualAttributions::from_full_state()`** — fixes a related issue where the `final_state` file contents map only included files from the checkpoint VA, dropping files tracked exclusively via blame/notes. Now unions both VAs' file contents so committed AI work on disjoint files isn't lost when merging with INITIAL. ### Tests Four new unit tests in `rebase_authorship::tests`: | Test | Scenario | |------|----------| | `rebase_complete_migrates_initial_to_new_head` | Single-file INITIAL is migrated after rebase (happy path) | | `rebase_complete_no_initial_is_noop` | No working log on old HEAD — no-op, no crash | | `rebase_complete_migrates_multi_file_initial` | Multi-file INITIAL with multiple prompt records migrated correctly | | `rebase_complete_merges_initial_when_both_working_logs_exist` | Old INITIAL merged into new HEAD that already has checkpoints; old working log cleaned up | ### Reproduction script `scripts/reproduce_gitai_orphan.py` — end-to-end reproduction using real `git-ai` and `gt` commands. Walks through the full lifecycle: checkpoint, commit, amend, rebase, and validates attribution at each phase.
kerem 2026-03-02 04:13:59 +03:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/git-ai#565
No description provided.