Skip to content

Conversation

@Lautron
Copy link

@Lautron Lautron commented Dec 3, 2025

Why did I implement this feature?

I recently realized how useful it would be to see Git Blame information right in the repomix output when reviewing my own contributions across different repositories. I figured others might find this helpful too, so I created this PR to share it!

Description

This change simply adds the ability to include Git Blame details (author and date) for every line in the packed output. This makes it much easier to understand the history and context of the code directly within the generated file.

Changes:

  • Added --output-show-git-blame CLI flag.
  • Added output.git.showBlame configuration option.
  • Implemented getGitBlame core logic to format output as [<Author> <Date>] <Code>.
  • Updated fileProcessContent to integrate blame data when enabled.
  • Added unit tests for new git commands and handlers.
  • Updated README.md with new flag and config option

Tests added

  • tests/core/git/gitBlameHandle.test.ts

    • Verifies that getGitBlame returns correctly formatted author and date strings for valid git repositories.
    • Ensures getGitBlame returns null and skips processing when the directory is not a git repository.
    • Checks that getGitBlame returns null for files producing empty blame output, such as untracked files.
    • Confirms that getGitBlame handles execution errors gracefully by returning null and logging the error.
  • tests/core/git/gitCommand.test.ts

    • Tests that execGitBlame correctly invokes the git binary with arguments for short dates and ignoring whitespace.
    • Verifies that execGitBlame returns an empty string and logs a trace if the git command fails.
  • tests/core/file/fileProcessContent.test.ts

    • Ensures the file processing pipeline replaces raw content with blame information when showBlame is enabled.
    • Verifies that the pipeline correctly falls back to the original file content if git blame retrieval fails.

How to try it

npm run repomix -- --output-show-git-blame --include "src/index.ts"

Example output

<file path="src/index.ts">
[yamadashy 2025-02-17] // ---------------------------------------------------------------------------------------------------------------------
[yamadashy 2025-02-17] // Core
[yamadashy 2025-02-17] // ---------------------------------------------------------------------------------------------------------------------
[yamadashy 2025-02-17] export { pack } from './core/packager.js';
[Kazuki Yamada 2025-04-19] export type { PackResult } from './core/packager.js';
[Kazuki Yamada 2025-04-19] 
[Kazuki Yamada 2025-04-19] // File
[Kazuki Yamada 2025-04-19] export { collectFiles } from './core/file/fileCollect.js';
[Kazuki Yamada 2025-04-19] export { sortPaths } from './core/file/filePathSort.js';
[Riqwan Thamir 2025-04-21] export { processFiles } from './core/file/fileProcess.js';
[Riqwan Thamir 2025-04-21] export { searchFiles } from './core/file/fileSearch.js';
[Riqwan Thamir 2025-04-21] export type { FileSearchResult } from './core/file/fileSearch.js';
[Kazuki Yamada 2025-05-25] export { generateFileTree, generateTreeString, treeToString, type TreeNode } from './core/file/fileTreeGenerate.js';
[Kazuki Yamada 2025-04-19] 
[Kazuki Yamada 2025-05-18] // Git
[Kazuki Yamada 2025-05-18] export { isValidRemoteValue, isValidShorthand, parseRemoteValue } from './core/git/gitRemoteParse.js';
[Kazuki Yamada 2025-05-18] 
[Kazuki Yamada 2025-04-19] // Security
[Kazuki Yamada 2025-04-19] export { runSecurityCheck } from './core/security/securityCheck.js';
[Kazuki Yamada 2025-04-19] export type { SuspiciousFileResult } from './core/security/securityCheck.js';
[Kazuki Yamada 2025-04-19] 
[Kazuki Yamada 2025-04-19] // Token Count
[Kazuki Yamada 2025-04-20] export { TokenCounter } from './core/metrics/TokenCounter.js';
[Kazuki Yamada 2025-04-19] 
[Kazuki Yamada 2025-04-19] // Tree-sitter
[Kazuki Yamada 2025-04-19] export { parseFile } from './core/treeSitter/parseFile.js';
[Kazuki Yamada 2025-01-03] 
[yamadashy 2025-02-17] // ---------------------------------------------------------------------------------------------------------------------
[Kazuki Yamada 2025-01-11] // Config
[yamadashy 2025-02-17] // ---------------------------------------------------------------------------------------------------------------------
[Kazuki Yamada 2025-04-19] export { loadFileConfig, mergeConfigs } from './config/configLoad.js';
[Riqwan Thamir 2025-04-21] export type { RepomixConfigFile as RepomixConfig } from './config/configSchema.js';
[Kazuki Yamada 2025-10-11] export { defineConfig } from './config/configSchema.js';
[Kazuki Yamada 2025-04-19] export { defaultIgnoreList } from './config/defaultIgnore.js';
[Kazuki Yamada 2025-01-11] 
[yamadashy 2025-02-17] // ---------------------------------------------------------------------------------------------------------------------
[yamadashy 2025-02-17] // Shard
[yamadashy 2025-02-17] // ---------------------------------------------------------------------------------------------------------------------
[yamadashy 2025-02-17] export { setLogLevel } from './shared/logger.js';
[Kazuki Yamada 2025-04-19] export type { RepomixProgressCallback } from './shared/types.js';
[yamadashy 2025-02-17] 
[yamadashy 2025-02-17] // ---------------------------------------------------------------------------------------------------------------------
[yamadashy 2025-02-17] // CLI
[yamadashy 2025-02-17] // ---------------------------------------------------------------------------------------------------------------------
[yamadashy 2025-02-17] export { run as cli } from './cli/cliRun.js';
[yamadashy 2025-02-17] export type { CliOptions } from './cli/types.js';
[yamadashy 2025-02-17] 
[yamadashy 2025-02-17] // Run CLI Repomix
[yamadashy 2025-02-17] export { runCli } from './cli/cliRun.js';
[Yamada Dev 2025-02-14] 
[Kazuki Yamada 2025-01-11] // Init action
[Kazuki Yamada 2025-01-03] export { runInitAction } from './cli/actions/initAction.js';
[Kazuki Yamada 2025-01-11] 
[Kazuki Yamada 2025-01-11] // Default action
[Kazuki Yamada 2025-05-06] export { runDefaultAction, buildCliConfig } from './cli/actions/defaultAction.js';
[Kazuki Yamada 2025-01-11] 
[Kazuki Yamada 2025-01-11] // Remote action
[Kazuki Yamada 2025-05-18] export { runRemoteAction } from './cli/actions/remoteAction.js';
</file>

Checklist

  • Run npm run test
  • Run npm run lint

@Lautron Lautron requested a review from yamadashy as a code owner December 3, 2025 21:57
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 3, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

This PR adds support for displaying Git blame information in Repomix output. It introduces a new CLI flag --output-show-git-blame and corresponding configuration property, along with new functions to execute Git blame commands, format their output, and inject blame data into processed file content when enabled.

Changes

Cohort / File(s) Summary
CLI Configuration & Types
src/cli/cliRun.ts, src/cli/types.ts
Adds new CLI option --output-show-git-blame (boolean flag) to Repomix Output Options and introduces corresponding outputShowGitBlame?: boolean field to CliOptions interface
CLI Action Handler
src/cli/actions/defaultAction.ts
Extends buildCliConfig to merge options.outputShowGitBlame into config.output.git.showBlame when the flag is provided
Configuration Schema
src/config/configSchema.ts
Adds optional showBlame: boolean field to git configuration in both base and default config schemas (defaults to false)
Core Git Blame Logic
src/core/git/gitBlameHandle.ts, src/core/git/gitCommand.ts
Introduces new modules: execGitBlame (executes git blame command via git blame --date=short -w) and getGitBlame (wraps blame execution with repo validation, formatting, error handling, and dependency injection support)
File Processing Integration
src/core/file/fileProcessContent.ts
Integrates git blame retrieval into content processing pipeline: when config.output.git.showBlame is enabled, fetches blame via getGitBlame and uses formatted blame content if available
Documentation
README.md
Documents new CLI flag and configuration property
CLI Action Tests
tests/cli/actions/defaultAction.test.ts, tests/cli/actions/workers/defaultActionWorker.test.ts
Adds test case for CLI config merging and updates mock config to include showBlame field
Config Schema Tests
tests/config/configSchema.test.ts
Updates test config fixtures to include showBlame field in git configuration
File Processing Tests
tests/core/file/fileProcessContent.test.ts
Adds test cases for blame content injection: verifies getGitBlame is called when enabled and content is replaced when blame is available
Git Blame Unit Tests
tests/core/git/gitBlameHandle.test.ts, tests/core/git/gitCommand.test.ts
Adds comprehensive test suites for getGitBlame and execGitBlame covering success paths, repo validation, error handling, and empty output scenarios
Metrics & Output Tests
tests/core/metrics/calculateGitDiffMetrics.test.ts, tests/core/metrics/calculateGitLogMetrics.test.ts, tests/core/output/flagFullDirectoryStructure.test.ts, tests/core/output/outputStyles/jsonStyle.test.ts
Updates mock configurations to include showBlame field in git section

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant CLI as CLI Parser
    participant Config as Config Builder
    participant FileProcessor as File Processor
    participant GitOps as Git Operations
    participant GitCmd as Git Command

    User->>CLI: --output-show-git-blame flag
    CLI->>CLI: Parse option to outputShowGitBlame
    CLI->>Config: buildCliConfig(options)
    Config->>Config: Merge outputShowGitBlame → config.output.git.showBlame
    
    rect rgb(200, 220, 240)
    Note over FileProcessor,GitOps: File Processing Phase
    FileProcessor->>FileProcessor: Check config.output.git.showBlame
    alt showBlame enabled
        FileProcessor->>GitOps: getGitBlame(cwd, filePath)
        GitOps->>GitOps: isGitRepository(cwd)
        alt Is git repo
            GitOps->>GitCmd: execGitBlame(cwd, filePath)
            GitCmd->>GitCmd: Execute: git blame --date=short -w
            GitCmd-->>GitOps: Raw blame output (stdout)
            GitOps->>GitOps: formatGitBlame(output) → [Author Date] Code
            GitOps-->>FileProcessor: Formatted blame content
            FileProcessor->>FileProcessor: Replace processedContent with blame
        else Not a git repo
            GitOps-->>FileProcessor: Return null
            FileProcessor->>FileProcessor: Use original file content
        end
    else showBlame disabled
        FileProcessor->>FileProcessor: Use original file content
    end
    end
    
    FileProcessor-->>User: Output with optional blame lines
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

  • Git blame logic: Review the formatting logic in gitBlameHandle.ts and error handling in both blame-related functions to ensure robustness across edge cases (non-repo directories, untracked files, command failures)
  • Config integration: Verify the CLI option is correctly threaded through the config builder without side effects
  • File processing hook: Ensure the blame content injection in fileProcessContent.ts occurs at the right point in the processing pipeline and handles null returns gracefully
  • Test coverage: Confirm test mocks and fixtures comprehensively cover both enabled and disabled scenarios, and that dependency injection in git functions works as intended

Possibly related PRs

  • feat(cli): Add quiet mode option #347: Modifies the same CLI code paths (src/cli/actions/defaultAction.ts, src/cli/types.ts, buildCliConfig, CliOptions interface), indicating related CLI surface changes

Suggested reviewers

  • yamadashy

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: Add support for Git Blame information' accurately and clearly describes the main feature added in the changeset.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Description check ✅ Passed The pull request description is comprehensive and well-structured, including why the feature was implemented, detailed description of changes, specific files modified, test coverage details, usage instructions, and example output.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @Lautron, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request enhances the repomix tool by adding a new capability to include Git Blame details directly within the generated output. This feature aims to provide immediate historical context for each line of code, making it significantly easier for users to understand code origins and review contributions without needing to consult git logs separately. It integrates seamlessly into the existing output generation process via new CLI and configuration options.

Highlights

  • New Feature: Git Blame Information: Introduced the ability to display Git Blame information (author and date) for each line in the repomix output.
  • CLI Flag and Configuration Option: Added a new CLI flag --output-show-git-blame and a corresponding configuration option output.git.showBlame to enable this feature.
  • Core Logic Implementation: Implemented getGitBlame to execute the git blame command, parse its output, and format it as [<Author> <Date>] <Code>.
  • Integration into File Processing: Updated fileProcessContent to integrate the blame data into the processed file content when the feature is enabled.
  • Comprehensive Testing: Added unit tests for the new git blame command execution and handling logic, as well as integration tests for the file processing and CLI configuration.
  • Documentation Update: The README.md has been updated to reflect the new CLI flag and configuration option.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a useful feature to display git blame information in the output. The implementation is well-structured, with new logic for fetching and formatting blame data, along with corresponding configuration options, CLI flags, and tests.

However, I've identified a significant issue in src/core/file/fileProcessContent.ts where enabling showBlame conflicts with other output formatting options like --compress and --output-show-line-numbers. The current processing order causes these features to either fail silently or produce incorrect/redundant output. I've left a detailed comment explaining the problem. Addressing this conflict is crucial for the feature to work correctly with other options.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (6)
README.md (1)

621-621: Git-blame docs align with implementation; optional perf note

The new CLI flag --output-show-git-blame and config option output.git.showBlame are clearly documented and match the wiring in CliOptions and buildCliConfig. You might optionally add a short note that enabling blame can be slower on large repos (since it runs git blame per file), but the current wording is already accurate and complete.

Also applies to: 1211-1211

tests/cli/actions/defaultAction.test.ts (1)

307-314: Test correctly verifies CLI → config mapping for git blame

This test cleanly asserts that outputShowGitBlame results in config.output.git.showBlame === true, matching the logic in buildCliConfig. Consider adding a follow-up test that combines this flag with other git options (e.g., includeDiffs / includeLogs) to ensure all git settings are preserved when merged, but not required for correctness here.

src/cli/actions/defaultAction.ts (1)

283-291: Git blame flag wiring is consistent and preserves existing git options

The outputShowGitBlame handling correctly sets output.git.showBlame = true while spreading cliConfig.output?.git, so other git-related flags (includeDiffs, includeLogs, etc.) remain intact. This mirrors the existing patterns for git options and looks solid. If this area grows further, you might consider consolidating all git-related CLI handling into a small helper to keep buildCliConfig easier to scan, but it’s not necessary right now.

src/core/file/fileProcessContent.ts (1)

28-33: Clarify how showBlame should interact with compress and other transforms

Right now, when config.output.git?.showBlame is true you replace processedContent with blame output, then still run truncate/removeComments/removeEmptyLines and, crucially, the compress/parseFile step on that blame-formatted text. If a user enables both --compress and --output-show-git-blame, parseFile is likely to see non‑valid source and throw, which makes the combination surprising or unusable.

It would be good to define and enforce explicit semantics here, for example:

  • Either treat blame as final content and skip the compress branch (and possibly other structural transforms) when showBlame is on, or
  • Prefer structural compression and skip blame when compress is enabled, or
  • Make the two options mutually exclusive at the CLI/config layer.

Whichever behavior you choose, I’d recommend adding a small test to lock it in.

tests/core/git/gitCommand.test.ts (1)

3-4: execGitBlame tests exercise both success and failure paths; consider flattening describe hierarchy

The new tests correctly verify the git arguments, returned stdout, and the trace log on error for execGitBlame, which tightly matches the implementation.

For readability, you might move describe('execGitBlame', ...) to be a top‑level sibling of the other command describes instead of nesting it inside describe('execLsRemote', ...), but that’s purely structural.

Also applies to: 408-432

src/core/git/gitBlameHandle.ts (1)

20-20: Consider using non-greedy matching for the author capture group.

The regex uses greedy (.+) to capture the author name. While this works due to backtracking, using non-greedy (.+?) would more explicitly convey the intent to match "up to" the date pattern. This improves regex clarity and predictability.

Additionally, [\^]? can be simplified to \^? (the character class is unnecessary for a single escaped character).

Apply this diff for improved regex clarity:

-  const blameLineRegex = /^[\^]?[a-f0-9]+\s*\((.+)\s+(\d{4}-\d{2}-\d{2})\s+\d+\)(.*)$/i;
+  const blameLineRegex = /^\^?[a-f0-9]+\s*\((.+?)\s+(\d{4}-\d{2}-\d{2})\s+\d+\)(.*)$/i;
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8751404 and b9f1197.

📒 Files selected for processing (18)
  • README.md (2 hunks)
  • src/cli/actions/defaultAction.ts (1 hunks)
  • src/cli/cliRun.ts (1 hunks)
  • src/cli/types.ts (1 hunks)
  • src/config/configSchema.ts (2 hunks)
  • src/core/file/fileProcessContent.ts (2 hunks)
  • src/core/git/gitBlameHandle.ts (1 hunks)
  • src/core/git/gitCommand.ts (1 hunks)
  • tests/cli/actions/defaultAction.test.ts (1 hunks)
  • tests/cli/actions/workers/defaultActionWorker.test.ts (1 hunks)
  • tests/config/configSchema.test.ts (2 hunks)
  • tests/core/file/fileProcessContent.test.ts (3 hunks)
  • tests/core/git/gitBlameHandle.test.ts (1 hunks)
  • tests/core/git/gitCommand.test.ts (2 hunks)
  • tests/core/metrics/calculateGitDiffMetrics.test.ts (1 hunks)
  • tests/core/metrics/calculateGitLogMetrics.test.ts (1 hunks)
  • tests/core/output/flagFullDirectoryStructure.test.ts (1 hunks)
  • tests/core/output/outputStyles/jsonStyle.test.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (7)
tests/cli/actions/defaultAction.test.ts (1)
src/cli/actions/defaultAction.ts (1)
  • buildCliConfig (114-306)
src/core/git/gitCommand.ts (1)
src/shared/logger.ts (1)
  • error (34-38)
src/core/file/fileProcessContent.ts (1)
src/core/git/gitBlameHandle.ts (1)
  • getGitBlame (48-73)
tests/core/git/gitBlameHandle.test.ts (1)
src/core/git/gitBlameHandle.ts (1)
  • getGitBlame (48-73)
src/core/git/gitBlameHandle.ts (1)
src/shared/logger.ts (1)
  • error (34-38)
tests/core/file/fileProcessContent.test.ts (3)
src/core/git/gitBlameHandle.ts (1)
  • getGitBlame (48-73)
src/config/configSchema.ts (1)
  • RepomixConfigMerged (159-159)
src/core/file/fileProcessContent.ts (1)
  • processContent (22-73)
tests/core/git/gitCommand.test.ts (1)
src/core/git/gitCommand.ts (1)
  • execGitBlame (213-228)
🔇 Additional comments (15)
tests/core/output/flagFullDirectoryStructure.test.ts (1)

34-35: Mock config updated to new git.showBlame field

Adding showBlame: false to the mock git config keeps this fixture aligned with the expanded RepomixConfigMerged schema without affecting test behavior.

tests/core/metrics/calculateGitDiffMetrics.test.ts (1)

52-53: Config fixture correctly extended with git.showBlame

The mockConfig.output.git object now includes showBlame: false, matching the updated config schema while leaving the diff-metrics behavior unchanged.

src/cli/types.ts (1)

30-31: CliOptions extended cleanly with outputShowGitBlame

The new outputShowGitBlame?: boolean field matches the CLI flag name and is consumed correctly in buildCliConfig; no issues here.

tests/cli/actions/workers/defaultActionWorker.test.ts (1)

62-63: Worker config fixture aligned with new git.showBlame field

Including showBlame: false in the mock git config keeps this test’s RepomixConfigMerged shape up to date with the schema while not affecting the worker behavior being asserted.

tests/config/configSchema.test.ts (1)

122-125: Schema tests correctly cover new git.showBlame field

Adding showBlame: false to the valid configs for both repomixConfigDefaultSchema and repomixConfigMergedSchema ensures the updated git config shape is explicitly exercised and preserved through parsing. This is a good, minimal extension of the existing tests.

Also applies to: 228-231

tests/core/file/fileProcessContent.test.ts (2)

6-25: Git blame mocking setup is consistent and isolated

Importing and mocking getGitBlame, then defaulting it to resolve null in beforeEach, keeps existing tests unaffected while allowing per-test overrides. This is a clean pattern for the new dependency.


182-225: New git blame tests cover happy path and fallback correctly

Both cases—blame present and blame null—are exercised with proper cwd and output.git.showBlame setup, and you assert both getGitBlame usage and the fallback to original content.

tests/core/output/outputStyles/jsonStyle.test.ts (1)

28-35: Mock config update for showBlame matches schema expectations

Adding showBlame: false to the git block keeps this test fixture in sync with the config schema defaults without altering existing assertions.

src/cli/cliRun.ts (1)

140-140: New --output-show-git-blame option is clearly named and placed

The flag name and description are consistent with existing output options, and its placement under "Repomix Output Options" matches its purpose.

src/core/git/gitCommand.ts (1)

206-228: execGitBlame implementation is safe and matches optional-blame semantics

Executing git blame via execFileAsync with argument arrays avoids shell injection, and logging then returning '' on failure aligns with treating blame as best‑effort data that shouldn’t break processing.

src/config/configSchema.ts (1)

44-52: showBlame is wired consistently into base and default git config schemas

Adding showBlame as optional in the base schema and defaulting it to false in the default schema keeps configuration parsing consistent and ensures existing configs remain valid.

Also applies to: 105-112

tests/core/metrics/calculateGitLogMetrics.test.ts (1)

46-53: Metrics test git config kept in sync via showBlame: false

Including showBlame: false in the mock git config mirrors the real schema and avoids drift between tests and configuration without altering test logic.

tests/core/git/gitBlameHandle.test.ts (1)

1-71: LGTM! Comprehensive test coverage.

The test suite thoroughly covers all main scenarios:

  • Happy path with proper blame output formatting
  • Non-git repository early exit optimization
  • Empty blame output handling (untracked files)
  • Error handling with appropriate logging

The use of dependency injection via the deps parameter enables clean mocking and all assertions are appropriate.

src/core/git/gitBlameHandle.ts (2)

48-73: LGTM! Well-structured error handling and dependency injection.

The function correctly:

  • Uses dependency injection for testability
  • Checks if the directory is a git repository before executing blame
  • Handles errors gracefully by returning null and logging at trace level
  • Maintains consistent behavior across different failure scenarios

20-20: Verify git blame date format compatibility between regex pattern and execGitBlame output.

The regex pattern expects YYYY-MM-DD without time and timezone, but standard git blame outputs YYYY-MM-DD HH:MM:SS +ZONE. Confirm that execGitBlame in gitCommand.ts applies date formatting flags (e.g., --date=short) to match the expected pattern, or update the regex if the command produces full datetime output.

removeEmptyLines.

If the user uses them simultaneously it shows a warning.
@Lautron
Copy link
Author

Lautron commented Dec 4, 2025

I've fixed Gemini comment about interaction with --compress and other flags that could cause problems when paired with --output-show-git-blame.

/gemini review

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a valuable feature for displaying Git Blame information in the output. The implementation is well-rounded, including updates to the CLI, configuration, core logic, and comprehensive tests. I appreciate the thoughtful handling of incompatible options, which improves the user experience by providing clear warnings.

My primary feedback focuses on enhancing the robustness of the Git Blame output parsing. The current approach uses regular expressions on human-readable output, which can be brittle. I've provided suggestions to switch to the machine-readable --porcelain format. This change will make the feature more reliable and resilient to edge cases like unconventional author names.

Comment on lines +10 to +39
function formatGitBlame(blameOutput: string): string {
// Regex breakdown:
// ^[a-f0-9]+ - Matches the commit hash at the start of the line (e.g., a2d3c5fed)
// \s* - Matches optional whitespace
// \((.+) - Group 1: Matches the AUTHOR part (e.g., yamadashy, Kazuki Yamada)
// \s+ - Matches one or more spaces
// (\d{4}-\d{2}-\d{2}) - Group 2: Matches the DATE part (e.g., 2025-02-17)
// \s+ - Matches one or more spaces
// \d+\) - Matches the line number inside the parenthesis (e.g., 1)
// (.*) - Group 3: Matches the CODE LINE part, including leading space
const blameLineRegex = /^[\^]?[a-f0-9]+\s*\((.+)\s+(\d{4}-\d{2}-\d{2})\s+\d+\)(.*)$/i;

const lines = blameOutput.split('\n');
const formattedLines: string[] = [];

for (const line of lines) {
const match = line.match(blameLineRegex);
if (match) {
const [, rawAuthor, date, codeLineRaw] = match;
const author = rawAuthor.trim();
const codeLine = codeLineRaw.startsWith(' ') ? codeLineRaw.substring(1) : codeLineRaw;

formattedLines.push(`[${author} ${date}] ${codeLine}`);
} else if (line.trim().length > 0) {
formattedLines.push(`[N/A N/A] ${line.trim()}`);
}
}

return formattedLines.join('\n');
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The current implementation uses a regular expression to parse the human-readable git blame output. This approach is fragile and may fail if author names contain unexpected characters or formats that conflict with the regex pattern.

To make parsing more robust and reliable, it's best practice to use the --porcelain format, which is specifically designed for machine consumption. I've provided a new implementation of formatGitBlame below that parses this format. It's more resilient and correctly handles various edge cases, such as multi-line blame groups.

This change should be paired with an update to execGitBlame to use the --porcelain flag instead of --date=short.

function formatGitBlame(blameOutput: string): string {
  const lines = blameOutput.split('\n');
  const formattedLines: string[] = [];
  let currentAuthor = 'N/A';
  let currentDate = 'N/A';

  for (const line of lines) {
    if (!line) continue;

    // Porcelain format provides metadata blocks.
    // A line with a 40-char hash is a header. Metadata may follow.
    if (/^[a-f0-9]{40}/.test(line)) {
      // This is a header line. We don't need to parse it, but it signals a new block might start.
      // The state (author/date) will be updated if new metadata is found.
      continue;
    }

    if (line.startsWith('author ')) {
      currentAuthor = line.substring('author '.length);
      continue;
    }
    if (line.startsWith('author-time ')) {
      const timestamp = parseInt(line.substring('author-time '.length), 10);
      if (!isNaN(timestamp)) {
        currentDate = new Date(timestamp * 1000).toISOString().split('T')[0];
      }
      continue;
    }

    // A line starting with a tab is the actual code content.
    if (line.startsWith('\t')) {
      const codeLine = line.substring(1);
      // Avoid a trailing space for empty lines.
      const formattedLine = codeLine ? `[${currentAuthor} ${currentDate}] ${codeLine}` : `[${currentAuthor} ${currentDate}]`;
      formattedLines.push(formattedLine);
    }
    // Any other metadata lines (committer, summary, etc.) are ignored.
  }

  return formattedLines.join('\n');
}

},
): Promise<string> => {
try {
const result = await deps.execFileAsync('git', ['-C', directory, 'blame', '--date=short', '-w', filePath]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

To ensure robust parsing of the git blame output, it's better to use the --porcelain format, which is designed for machine consumption. This format is more stable and less ambiguous than the human-readable default, especially with complex author names.

The --date=short option is not compatible with --porcelain and is no longer needed, as the porcelain format provides a UNIX timestamp that can be formatted as required.

Suggested change
const result = await deps.execFileAsync('git', ['-C', directory, 'blame', '--date=short', '-w', filePath]);
const result = await deps.execFileAsync('git', ['-C', directory, 'blame', '--porcelain', '-w', filePath]);

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.

1 participant