[GH-ISSUE #366] git-ai stats returns 0 AI lines with shallow clones #133

Open
opened 2026-03-02 04:12:07 +03:00 by kerem · 0 comments
Owner

Originally created by @satyamtg on GitHub (Jan 17, 2026).
Original GitHub issue: https://github.com/git-ai-project/git-ai/issues/366

Problem

git-ai stats <range> incorrectly reports 0 AI lines when used with shallow clones, even though commits have valid AI attribution.

Affects:

  • Shallow clones (git clone --depth=N)
  • CI/CD pipelines (GitHub Actions, GitLab CI)
  • Repos after force-push/rebase (most critical - unshallow won't help)
  • Partial clones with --single-branch

Example

# Shallow clone a repo
git clone --depth=10 https://github.com/user/repo.git
cd repo

# Fetch AI notes
git fetch origin refs/notes/ai:refs/notes/ai

# Try to get stats
git-ai stats HEAD~5..HEAD --json

Result:

{
  "prompts": 0,
  "ai_accepted": 0   WRONG!
}

Expected:

{
  "prompts": 1,
  "ai_accepted": 30   CORRECT
}

Root Cause

When you fetch git notes, you get all notes but may not have all commits they reference. The code tries to run git log --no-walk on commits that don't exist locally, causing the command to fail silently.

Example:

  • Git notes reference 4 commits: A, B, C, D
  • Shallow clone only has: A, B
  • Code runs: git log --no-walk A B C D
  • Git fails because C and D don't exist
  • Result: 0 prompts loaded

Special Case: Force Push

The most critical case is after a force-push/rebase. Even git fetch --unshallow won't help because the old commits no longer exist on the remote.

Example:

# Someone force-pushed, rewriting history
git fetch origin
git fetch origin refs/notes/ai:refs/notes/ai

# Git notes still reference OLD commit SHAs (before rebase)
# Those commits don't exist locally OR on remote anymore
# git fetch --unshallow won't retrieve them

Only solution: Manual fetch if you know the old commit IDs:

# If you have the orphaned commit IDs
git fetch origin 63580b31a3fa4db0  # Fetch specific commits

But typically in CI/CD, you don't know these IDs, so the fix filters them out automatically.

Proposed fix

Filter out non-existent commits before passing to git log:

// Check which commits actually exist
let existing_shas: Vec<String> = sha_vec
    .into_iter()
    .filter(|sha| {
        // Use git cat-file -e to verify existence
        let mut args = repo.global_args_for_exec();
        args.push("cat-file".to_string());
        args.push("-e".to_string());
        args.push(sha.clone());
        exec_git(&args).is_ok()
    })
    .collect();

// Only pass existing commits to git log
git log --no-walk <existing_commits_only>

File: src/git/refs.rs
Function: grep_ai_notes()

Impact

  • Fixes CI/CD pipelines using shallow clones
  • Works with GitHub Actions/GitLab CI default fetch behavior
  • Handles repos with rewritten history
  • No breaking changes - purely additive safety check

Workarounds (Before Fix)

For Shallow Clones

# Fetch full history (not ideal for large repos)
git fetch --unshallow

For Force-Push Scenarios

# If you know the orphaned commit IDs
git fetch origin <commit-sha-1> <commit-sha-2>

# Or fetch all refs (may not help if commits are gone)
git fetch --all

Note: After force-push, old commits may be unreachable even on remote. The fix handles this gracefully by skipping non-existent commits.

Originally created by @satyamtg on GitHub (Jan 17, 2026). Original GitHub issue: https://github.com/git-ai-project/git-ai/issues/366 ## Problem `git-ai stats <range>` incorrectly reports **0 AI lines** when used with shallow clones, even though commits have valid AI attribution. **Affects:** - Shallow clones (`git clone --depth=N`) - CI/CD pipelines (GitHub Actions, GitLab CI) - Repos after force-push/rebase (most critical - unshallow won't help) - Partial clones with `--single-branch` ## Example ```bash # Shallow clone a repo git clone --depth=10 https://github.com/user/repo.git cd repo # Fetch AI notes git fetch origin refs/notes/ai:refs/notes/ai # Try to get stats git-ai stats HEAD~5..HEAD --json ``` **Result:** ```json { "prompts": 0, "ai_accepted": 0 ← WRONG! } ``` **Expected:** ```json { "prompts": 1, "ai_accepted": 30 ← CORRECT } ``` ## Root Cause When you fetch git notes, you get **all notes** but may not have **all commits** they reference. The code tries to run `git log --no-walk` on commits that don't exist locally, causing the command to fail silently. **Example:** - Git notes reference 4 commits: `A`, `B`, `C`, `D` - Shallow clone only has: `A`, `B` - Code runs: `git log --no-walk A B C D` - Git fails because `C` and `D` don't exist - Result: 0 prompts loaded ### Special Case: Force Push The most critical case is **after a force-push/rebase**. Even `git fetch --unshallow` won't help because the old commits no longer exist on the remote. **Example:** ```bash # Someone force-pushed, rewriting history git fetch origin git fetch origin refs/notes/ai:refs/notes/ai # Git notes still reference OLD commit SHAs (before rebase) # Those commits don't exist locally OR on remote anymore # git fetch --unshallow won't retrieve them ``` **Only solution:** Manual fetch if you know the old commit IDs: ```bash # If you have the orphaned commit IDs git fetch origin 63580b31a3fa4db0 # Fetch specific commits ``` But typically in CI/CD, you don't know these IDs, so the fix filters them out automatically. ## Proposed fix Filter out non-existent commits before passing to `git log`: ```rust // Check which commits actually exist let existing_shas: Vec<String> = sha_vec .into_iter() .filter(|sha| { // Use git cat-file -e to verify existence let mut args = repo.global_args_for_exec(); args.push("cat-file".to_string()); args.push("-e".to_string()); args.push(sha.clone()); exec_git(&args).is_ok() }) .collect(); // Only pass existing commits to git log git log --no-walk <existing_commits_only> ``` **File:** `src/git/refs.rs` **Function:** `grep_ai_notes()` ## Impact - ✅ Fixes CI/CD pipelines using shallow clones - ✅ Works with GitHub Actions/GitLab CI default fetch behavior - ✅ Handles repos with rewritten history - ✅ No breaking changes - purely additive safety check ## Workarounds (Before Fix) ### For Shallow Clones ```bash # Fetch full history (not ideal for large repos) git fetch --unshallow ``` ### For Force-Push Scenarios ```bash # If you know the orphaned commit IDs git fetch origin <commit-sha-1> <commit-sha-2> # Or fetch all refs (may not help if commits are gone) git fetch --all ``` **Note:** After force-push, old commits may be unreachable even on remote. The fix handles this gracefully by skipping non-existent commits.
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#133
No description provided.