Skip to content

feat(git): update --include-logs to optionally pack git commit history with graphs, metadata, and diffs/patches - #968

Open
ahundt wants to merge 20 commits into
yamadashy:mainfrom
ahundt:feat/git-commit-history-squashed
Open

feat(git): update --include-logs to optionally pack git commit history with graphs, metadata, and diffs/patches#968
ahundt wants to merge 20 commits into
yamadashy:mainfrom
ahundt:feat/git-commit-history-squashed

Conversation

@ahundt

@ahundt ahundt commented Nov 25, 2025

Copy link
Copy Markdown

Choose which to pack: commit graphs, metadata, and diffs to analyze changes

Enhances --include-logs to include structured git commit data with optional diff/patch content, using orthogonal flags that mirror git log's own options (prefixed with --git- to avoid namespace conflicts).

Why

To evaluate repository history for cleanup, review, or AI analysis, info is needed beyond commit messages:

  1. Diffs — diff content shows exactly what lines were added/removed
  2. Commit Dependencies — parent hashes reveal commit relationships
  3. How work was integrated — branch/merge topology shows integration points
  4. Where releases are — tag mappings identify version boundaries

This adds these capabilities through flags that match git log's parameter structure.

Features

Graph Visualization (--git-graph) — NEW

  • ASCII graph: same format as git log --graph --oneline --all
  • Mermaid diagram: renders in GitHub, GitLab, documentation sites
  • Shows branch connections within the specified commit range

Commit Metadata

With --include-logs (existing): date, message, files

Add any output verbosity/graph option (--git-graph/--git-stat/--git-patch/--git-summary/etc.) for NEW fields:

  • Hashes: full SHA and abbreviated for referencing
  • Parents: merge commits show 2+ parents
  • Author: name, email, timestamp — who wrote the code
  • Committer: name, email, timestamp — may differ in cherry-picks, rebases
  • Body: full commit message body

Patch/Diff Content (--git-patch, --git-stat, etc.) — NEW

Include actual code changes — see exactly what lines were added, removed, or modified per commit. Choose detail level with diff format options.

Tag Mapping (--git-graph) — NEW

Maps git tags to commit hashes for identifying release boundaries. Included with graph visualization.

CLI Options

repomix --include-logs [--commit-range <range>] [--git-stat|--git-patch|...] [--git-graph] [path/to/repo]

Diff Format Options (mutually exclusive, mirrors git log):

Option Git Equivalent What it does Why you'd use it
--git-name-only --name-only Filenames only Default — see what files changed
--git-stat --stat Per-file change counts See scope without full diffs
--git-patch --patch Line-by-line diffs Evaluate actual code changes
--git-numstat --numstat Machine-readable +/- Programmatic analysis
--git-shortstat --shortstat One-line summary Quick overview
--git-dirstat --dirstat Directory percentages See which areas changed most
--git-name-status --name-status Filenames with A/M/D/R Track adds, mods, deletes, renames
--git-raw --raw Low-level with blob SHAs Detailed analysis

Output Verbosity & Graph Options (combinable with diff format):

Option Git Equivalent What it does Default
--git-graph --graph --all ASCII + Mermaid graphs off
--git-summary --summary File operations (create/delete/rename) off
--commit-range <range> <range> Specify commit range HEAD~50..HEAD
--include-logs-count <n> -n Limit commits 50

Usage Examples

# Full diffs for code review
repomix --include-logs --git-patch

# Analyze release range with graph
repomix --include-logs --commit-range v1.0..v2.0 --git-stat --git-graph

# Review feature branch
repomix --include-logs --commit-range main..feature --git-stat --git-graph

# Quick scope overview
repomix --include-logs --git-shortstat

Performance

Measured over 10 runs each on repomix repo (median with min-max range, lower duration is better):

Test Suite Comparison:

Metric Main (a63546b) This PR (586d3ae) Change Notes
Total Duration 3.58s (3.57-3.73s) 3.62s (3.57-3.71s) ✅ +1.1% Within variance
Test Files 96 97 ✅ +1 New gitHistory.test.ts
Test Count 852 884 ✅ +32 +3.8% coverage

gitLogHandle.test.ts (10 runs each, tests the changed code, lower duration is better):

Metric Main (a63546b) This PR (586d3ae) Change
Median 344ms 255ms 🚀 -26%
Min 278ms 249ms 🚀 -10%
Max 896ms 296ms 🚀 -67%
Range 618ms 47ms 🚀 -92%

Repomix Execution (repomix --include-logs on repomix repo, 10 runs each, lower duration is better):

Metric Main (a63546b) This PR (586d3ae) Change Notes
Execution time 10.83s (10.69-10.99s) 10.39s (10.00-10.75s) 🚀 -4% Faster with more features

Backwards Compatibility

Preserved:

  • All existing CLI arguments (--include-logs, --include-logs-count, --include-diffs) remain unchanged;
  • Core XML structure: <git_logs>, <git_log_commit>, <date>, <message>, <files>
  • Default behavior with --include-logs alone produces equivalent output
  • New fields are optional and only appear when new flags are used

Changed (Minor):

  • Date format: 2025-11-20 23:12:33 +09002025-11-26T04:30:33-05:00 (strict ISO 8601)
    • More parseable format using git's %aI instead of %ad with --date=iso
    • Both are ISO 8601 variants; the new format includes T separator and colon in timezone
    • This is a minor format change that improves parseability for automated tools
  • All new flags (--git-stat, --git-patch, --git-graph, --git-summary, --commit-range, etc.) are purely additive.

New optional sections (only when using enhancement flags):

  • <hash>, <author>, <committer>, <parents>, <body>, <patch> per commit
  • <summary> section with commit counts and range info
  • <commit_graph> section with ASCII and Mermaid visualizations

Output Structure

GitLogCommit with progressive fields:

interface GitLogCommit {
  date: string;
  message: string;
  files: string[];
  // Extended (with --git-graph, --git-stat, --git-patch, --git-summary, etc.):
  hash?: string;
  abbreviatedHash?: string;
  author?: { name: string; email: string; date: string };
  committer?: { name: string; email: string; date: string };
  parents?: string[];
  body?: string;
  patch?: string;
}

Checklist

  • npm run test — 1136 tests (1134 pass, 2 pre-existing failures in packageJsonParse.test.ts also fail on main at 72d53a4a)
  • npm run lint — 0 errors, 0 warnings
  • Documentation updated
  • Backward compatible (--include-logs behavior preserved)

@ahundt
ahundt requested a review from yamadashy as a code owner November 25, 2025 04:39
@coderabbitai

coderabbitai Bot commented Nov 25, 2025

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: dff129d1-93fc-4431-a8cc-1dd5ca8a2e2f

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR adds comprehensive Git commit history analysis capabilities to Repomix, introducing CLI flags (--patch, --stat, --graph, --summary, --commit-range) and configuration options to retrieve, parse, and render structured commit metadata, patches, ASCII/Mermaid graphs, and tags. The implementation spans CLI parsing, config schema, core git parsing utilities, output generation, and documentation.

Changes

Cohort / File(s) Summary
CLI Options & Configuration
src/cli/types.ts, src/cli/cliRun.ts, src/cli/actions/defaultAction.ts
Adds new CliOptions fields (commitRange, stat, patch, numstat, shortstat, dirstat, nameOnly, nameStatus, raw, graph, summary); registers CLI flags with grouping (Git Log Diff Format Options, Git Log Output Verbosity & Graph Options); wires flags into cliConfig with mutual-exclusion validation and commitPatchDetail derivation.
Config Schema
src/config/configSchema.ts
Extends git output config with commitRange (default 'HEAD~50..HEAD'), commitPatchDetail (default 'name-only'), includeCommitGraph, includeSummary, includeGitTags, includeCommitPatches fields in both base and default schemas.
Git History Core
src/core/git/gitCommand.ts, src/core/git/gitHistory.ts, src/core/git/gitLogHandle.ts
Introduces structured git output parsing layer with NUL delimiters; adds CommitMetadata, CommitGraph, ParsedCommitRange interfaces; implements parseCommitRange, getCommitMetadata, getCommitGraph, getTags, getCommitPatch, generateMermaidGraph functions; refactors git log handling to two-pass structured parsing with optional patches and graph.
Output Generation
src/core/output/outputGeneratorTypes.ts, src/core/output/outputGenerate.ts, src/core/output/outputStyles/markdownStyle.ts, src/core/output/outputStyles/xmlStyle.ts
Extends RenderContext with gitCommitHistorySummary and gitCommitGraph; updates markdown/XML output templates to render commit history sections (graph, summary, commits with metadata, patches, tags).
Integration
src/core/packager.ts, src/core/metrics/calculateGitLogMetrics.ts, src/core/security/securityCheck.ts
Updates GitLogCommit shape to include optional hash, abbreviatedHash, author, committer, parents, body, patch fields; adjusts metrics calculation and security checks to consume new logCommits structure instead of logContent.
Tests — Config & Metrics
tests/config/configSchema.test.ts, tests/core/metrics/calculateGitDiffMetrics.test.ts, tests/core/metrics/calculateGitLogMetrics.test.ts
Extends mock RepomixConfig with new git fields; updates GitLogResult shape expectations to use logCommits array instead of logContent/commits.
Tests — Git & Output
tests/core/git/gitHistory.test.ts, tests/core/git/gitLogHandle.test.ts, tests/core/output/outputGenerate.test.ts, tests/core/output/outputGenerateDiffs.test.ts, tests/core/output/outputGenerateMarkdownStyle.test.ts, tests/core/output/outputGenerateXmlStyle.test.ts
Adds comprehensive test suite for gitHistory functions (parseCommitRange, getCommitMetadata, getTags, getCommitPatch, getCommitGraph); refactors gitLogHandle tests to mock structured output and NUL-separated parsing; extends output tests to verify new git history rendering across markdown/XML/JSON.
Tests — Utilities
tests/cli/actions/workers/defaultActionWorker.test.ts, tests/core/output/flagFullDirectoryStructure.test.ts, tests/core/output/outputStyles/jsonStyle.test.ts
Updates mock RepomixConfigMerged.git with new fields (commitRange, commitPatchDetail, includeCommitGraph, includeSummary, includeGitTags, includeCommitPatches).
Documentation — English
README.md, llms-install.md, website/client/src/en/guide/command-line-options.md, website/client/src/en/guide/configuration.md, website/client/src/en/guide/output.md, website/client/src/en/guide/usage.md, website/client/src/en/guide/tips/git-commit-history.md
Adds sections documenting Git Commit History Analysis feature: CLI options (diff format flags, output detail/graph options, commit-range syntax), configuration defaults, output components (metadata, ASCII/Mermaid graphs, tags, patches), and usage examples/workflows.
Documentation — Translations
website/client/src/*/guide/command-line-options.md (de, es, fr, hi, id, ja, ko, pt-br, vi, zh-cn, zh-tw)
Adds translated Git Commit History Options sections describing orthogonal diff flags and output options; consistent content across languages with some duplication within files.
Documentation — Navigation & Other
website/client/.vitepress/config/configEnUs.ts, .gitignore
Adds sidebar navigation entry for Git Commit History Analysis guide; adds .claude to gitignore.

Sequence Diagram(s)

sequenceDiagram
    participant CLI as CLI<br/>(cliRun.ts)
    participant Action as Action<br/>(defaultAction.ts)
    participant GitCmd as Git Commands<br/>(gitCommand.ts)
    participant GitHist as Git History<br/>(gitHistory.ts)
    participant GitLog as Git Log Handle<br/>(gitLogHandle.ts)
    participant Output as Output Generate<br/>(outputGenerate.ts)
    participant Render as Render Styles<br/>(markdown/xml/json)

    CLI->>Action: Parse flags (--patch, --graph, --summary, --commit-range)
    Action->>Action: Validate mutual exclusion, derive commitPatchDetail
    Action->>Action: Augment cliConfig with git options
    
    Action->>GitLog: getGitLogs(config, deps)
    
    GitLog->>GitCmd: execGitLogStructured(range, maxCommits)
    GitCmd-->>GitLog: NUL-delimited structured metadata
    
    GitLog->>GitHist: parseCommitRange(range)
    GitHist-->>GitLog: { from, to, raw }
    
    alt includeCommitPatches
        GitLog->>GitCmd: execGitLogTextBlob(range, patchDetail)
        GitCmd-->>GitLog: patch/stat/diff text blobs
    end
    
    alt includeCommitGraph
        GitLog->>GitCmd: execGitGraph(range)
        GitCmd-->>GitLog: git log --graph output
        GitLog->>GitHist: generateMermaidGraph(commits, tags)
        GitHist-->>GitLog: Mermaid diagram string
    end
    
    GitLog->>GitHist: getTags(directory)
    GitHist-->>GitLog: tag→hash map
    
    GitLog-->>Action: GitLogResult { logCommits[], graph?, summary? }
    
    Action->>Output: generateOutput(context, gitLogResult)
    Output->>Output: Build RenderContext with gitCommitHistorySummary, gitCommitGraph
    Output->>Render: Render git history section
    Render->>Render: Format commits, patches, graph, tags per style
    Render-->>Output: Rendered markdown/XML/JSON
    Output-->>Action: Final output string
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • gitCommand.ts & gitHistory.ts (~25 min): Multi-pass git command architecture with structured NUL-delimited parsing requires careful review of delimiter handling, regex patterns, and error paths. Note: gitCommand.ts contains apparent duplicate declarations of NUL, METADATA_FORMAT, GitLogStructuredOptions, GitLogTextBlobOptions, execGitLogStructured, execGitLogTextBlob, execGitGraph—confirm if intentional or merge artifact.
  • gitLogHandle.ts refactor (~20 min): Significant restructuring from simple text parsing to two-pass structured approach; parseStructuredOutput helper regex patterns and Mermaid graph generation logic need verification for correctness and edge cases.
  • CLI integration & config wiring (~10 min): Multiple touch points (cliRun.ts, defaultAction.ts, configSchema.ts) require tracing flag→config→output flow to ensure mutual-exclusion validation and commitPatchDetail derivation work correctly.
  • Output generation complexity (~10 min): RenderContext changes, optional field rendering across markdown/XML/JSON styles, and new data shape (logCommits vs. logContent) must be validated for consistency.
  • Test coverage (~5 min): Extensive test additions are largely straightforward mock-based tests, though gitLogHandle.test.ts refactor adds structured parsing expectations that should be spot-checked.

Areas requiring extra attention:

  • Duplicate code block in src/core/git/gitCommand.ts (lines near end of file repeating NUL constant, METADATA_FORMAT, and all three exec* functions)
  • NUL delimiter parsing robustness for edge cases (empty output, incomplete records, special characters in commit messages)
  • Mermaid graph generation for complex merge scenarios and tag escaping
  • Integration with existing packager/metrics/security flows to ensure logCommits structure properly replaces logContent
  • Semantic consistency between CliOptions (snake_case: nameOnly, nameStatus) and command-line flags (kebab-case: --name-only, --name-status)

Possibly related issues

Possibly related PRs

Suggested labels

enhancement, git, feature

Suggested reviewers

  • yamadashy
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically summarizes the main change: enhancing --include-logs to pack git commit history with graphs, metadata, and diffs/patches. It is concise, related to the primary objective, and conveys meaningful information.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description check ✅ Passed The PR description is comprehensive and well-structured, covering motivation, features, CLI options, usage examples, performance data, backwards compatibility, and output structure.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
📝 Coding Plan
  • Generate coding plan for human review comments

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.

Tip

You can disable sequence diagrams in the walkthrough.

Disable the reviews.sequence_diagrams setting to disable sequence diagrams in the walkthrough.

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello @ahundt, 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 significantly enhances the repomix tool by introducing a robust Git commit history analysis feature. This allows AI systems to gain a deeper understanding of code evolution, development patterns, and contextual information beyond just current file contents. By providing detailed commit metadata, visual graphs, and configurable patch levels, the tool now offers richer insights crucial for advanced AI-driven code analysis and understanding.

Highlights

  • Comprehensive Git Commit History Analysis: Introduced a new feature to analyze and include detailed Git commit history in the output, providing rich metadata, visual graphs (ASCII and Mermaid), and configurable patch details for AI systems.
  • New CLI Options and Configuration: Added six new CLI options (--include-commit-history, --commit-range, --commit-patch-detail, --no-commit-graph, --no-git-tags, --no-commit-patches) and corresponding configuration schema updates to give users fine-grained control over Git history extraction.
  • Enhanced Output Formats: Integrated the new Git commit history data into both Markdown and XML output styles, ensuring structured and comprehensive presentation for downstream AI analysis.
  • New Core Modules for Git Operations: Developed dedicated core modules (src/core/git/gitHistory.ts and src/core/git/gitHistoryHandle.ts) to manage Git command execution, parse commit data, and orchestrate the history collection process.
  • Extensive Documentation and Testing: Updated README.md, llms-install.md, and the project website with new guides and examples for the Git commit history feature, complemented by 871 new unit and integration tests to ensure reliability.
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.

coderabbitai[bot]

This comment was marked as resolved.

gemini-code-assist[bot]

This comment was marked as resolved.

@ahundt

ahundt commented Nov 25, 2025

Copy link
Copy Markdown
Author

Hey, I addressed the ai-generated review feedback in the latest commit:

Fixed:

  • Body/file parsing now uses %x00 null byte delimiter instead of the fragile heuristic. Null bytes can't appear in commit messages or file paths, so this reliably separates them.
  • Mermaid gitGraph syntax corrected. Was using invalid merge <hash>, now uses proper commit id: "..." tag: "..." type: HIGHLIGHT.
  • Added git_history section to parsable XML output to match JSON structure.
  • Added includeCommitPatches to configuration docs.
  • Added files list to XML (<files>) and Markdown (**Files Changed**) templates.

Kept as-is:

  • --all flag stays. It's git's standard flag for showing branch topology within the specified range. Documented this behavior.

Out of scope:

  • CDATA wrapping: The existing XML templates don't use it, so adding it just for commit history would be inconsistent. Parsable XML uses XMLBuilder which escapes automatically.
  • Parallel patch fetching: Overkill for this functionality. Sequential is simpler and avoids spawning many git processes.

894 tests passing, lint clean. Let me know if you'd like any changes! Thanks for the useful tool!

@ahundt
ahundt force-pushed the feat/git-commit-history-squashed branch from dfda264 to 67ff18a Compare November 25, 2025 20:07
@ahundt ahundt changed the title feat(git): add git commit history analysis to explore and find high quality commits feat(git): enhance --include-logs with Output Verbosity, Diff, & Graph Options Nov 26, 2025
@ahundt ahundt changed the title feat(git): enhance --include-logs with Output Verbosity, Diff, & Graph Options feat(git): enhance --include-logs with Output Verbosity, Metadata, Diff, & Graph Options Nov 26, 2025
@ahundt ahundt changed the title feat(git): enhance --include-logs with Output Verbosity, Metadata, Diff, & Graph Options feat(git): update --include-logs to optionally pack git commit history with graphs, metadata, and diffs/patches Nov 26, 2025
@ahundt
ahundt force-pushed the feat/git-commit-history-squashed branch 4 times, most recently from e2b824d to 3b8412a Compare November 26, 2025 11:05
@ahundt

ahundt commented Nov 26, 2025

Copy link
Copy Markdown
Author

@yamadashy this seems to be in reasonably good shape now if you're up for giving it a look! The summary at the very top should be up to date now.

/gemini review

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Nov 26, 2025

Copy link
Copy Markdown
Contributor

@ahundt, thank you for the comprehensive updates and for addressing the previous feedback! I can see you've made thoughtful improvements:

Robust parsing with null byte delimiters
Correct Mermaid syntax for git graphs
Complete XML/JSON parity with the new git_history section
Enhanced documentation and template coverage
Strong test coverage (894 tests passing, lint clean)

Your decisions on keeping --all and deferring CDATA/parallel fetching are reasonable given the context and consistency with the existing codebase.

I'll perform a full review of the entire PR now to provide comprehensive feedback on the implementation.

✅ Actions performed

Full review triggered.

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Hello @ahundt,

Thank you for the detailed update and for addressing the feedback from the previous review. I appreciate the thoroughness of your changes and the clear explanations for the decisions made.

I've reviewed the latest commit, and the fixes you've implemented are excellent:

  • Null Byte Delimiter: The adoption of %x00 for robust parsing of commit metadata and file lists is a significant improvement. This eliminates the fragility of previous heuristics and ensures reliable data extraction, even with unusual commit messages or file paths.
  • Mermaid GitGraph Syntax: The correction to use commit id: "..." tag: "..." type: HIGHLIGHT is spot on. This ensures compatibility with Mermaid and correctly visualizes merge commits and tags.
  • XML Output Structure: The git_history section has been correctly integrated into the parsable XML output, aligning it with the JSON structure and providing a consistent, rich data representation.
  • Configuration Documentation: includeCommitPatches is now properly documented in llms-install.md and website/client/src/en/guide/configuration.md.
  • Files List in Templates: The inclusion of the files list in both XML and Markdown templates for commit history is a valuable addition, providing a complete picture of each commit's changes.

Your decision to keep the --all flag for git log and to document its behavior is reasonable, as it aligns with standard Git functionality for showing branch topology. Similarly, your rationale for not implementing CDATA wrapping (due to existing XML templates and automatic escaping) and parallel patch fetching (for simplicity and avoiding process overhead) is well-justified.

The new two-pass architecture for Git log parsing, along with the dedicated modules for Git operations (gitCommand.ts, gitHistory.ts, gitLogHandle.ts), significantly enhances the robustness, maintainability, and flexibility of the Git history feature. The comprehensive test suite, especially gitHistory.test.ts, demonstrates a strong commitment to quality and covers various scenarios and edge cases.

Overall, this is a very well-executed enhancement. Great work!

I will now proceed with a full review as requested by /gemini review.

coderabbitai[bot]

This comment was marked as resolved.

@ahundt
ahundt force-pushed the feat/git-commit-history-squashed branch from 3b8412a to e0ff030 Compare November 26, 2025 12:50
@yamadashy

Copy link
Copy Markdown
Owner

Hi @ahundt!
Thank you for this PR!

I've been thinking about extending the git history-related features, so the direction looks good to me.

I'd like to take some time to think about the option design. I don't have time to review the code in detail right now, but I'll take a closer look later!

@ahundt

ahundt commented Nov 29, 2025

Copy link
Copy Markdown
Author

Ok I stuck to your existing pattern and it is essentially a pass through of the existing git commands which I view as one of the the likeliest paths users and tools like ais will be able to run with existing knowledge, and thus a reasonable choice. It is dramatically simplified from the initial commit.

…ta extraction

Summary: Enable detailed git commit history in repository output for AI systems to understand code evolution, including ASCII/Mermaid graph visualization, commit metadata extraction (hash/author/committer/parents/files), and configurable patch detail levels.

Previous behavior: Repomix supported basic git log output via --include-logs flag, which only displayed simple chronological commit messages without graph topology, detailed metadata (author/committer/parents), diff patches, or branch/merge visualization.

What changed:
- src/cli/cliRun.ts: add 6 CLI options (--include-commit-history, --commit-range, --commit-patch-detail, --no-commit-graph, --no-git-tags, --no-commit-patches) with semantic suggestion mappings
- src/cli/actions/defaultAction.ts: add buildCliConfig() logic to process commit history options and merge into config.output.git
- src/cli/types.ts: add CliOptions fields for commit history (includeCommitHistory, commitRange, commitPatchDetail, commitGraph, gitTags, commitPatches)
- src/config/configSchema.ts: add repomixConfigBaseSchema and repomixConfigDefaultSchema fields for commit history config with defaults (commitRange: 'HEAD~50..HEAD', commitPatchDetail: 'stat', includeCommitGraph: true, includeGitTags: true, includeCommitPatches: true)
- src/core/git/gitHistory.ts: create parseCommitRange(), getCommitMetadata(), getCommitGraph(), getCommitPatch(), getTags() functions to execute git log/show commands, parse commit data including full author/committer metadata, generate ASCII/Mermaid graphs from topology, and retrieve diffs at configurable detail levels
- src/core/git/gitHistoryHandle.ts: create getGitHistory() to orchestrate git history collection with configurable patch detail levels (full/stat/files/metadata)
- src/core/output/outputGenerate.ts: integrate getGitHistory() and add gitCommitHistory* fields to template data
- src/core/output/outputGeneratorTypes.ts: add GeneratedOutputData fields for gitCommitHistory, gitCommitGraph, gitCommitHistoryItems
- src/core/output/outputStyles/markdownStyle.ts: add Git Commit History section with Summary (total commits/merge commits/range/detail level), Commit Graph (ASCII/Mermaid/Tags), and per-commit details (hash/author/committer/date/message/body/files/patch)
- src/core/output/outputStyles/xmlStyle.ts: add <git_history> with <summary>, <commit_graph>, and <commit> elements including full author/committer metadata
- src/core/packager.ts: add gitHistory parameter to packDirectory()
- tests/core/git/gitHistory.test.ts: add tests for parseCommitRange, getCommitMetadata, getCommitGraph, getCommitPatch, getTags functions (19 tests, 282 lines)
- tests/*: add integration tests and update existing tests for git history functionality
- website/client/src/en/guide/tips/git-commit-history.md: create guide explaining feature usage, patch detail levels, and configuration options
- website/client/src/en/guide/*.md: update command-line-options.md, configuration.md, output.md, usage.md with git commit history documentation
- website/client/.vitepress/config/configEnUs.ts: add sidebar link to git-commit-history guide
- README.md: add git commit history feature to main feature list with examples
- llms-install.md: document includeCommitHistory, commitRange, commitPatchDetail options for MCP tools

Why: AI systems analyzing repositories need to understand code evolution patterns beyond individual file contents. Commit history reveals development patterns, identifies code stability risks, and provides context about why changes were made. Graph visualization shows branch/merge topology for understanding parallel development. Raw author/committer email metadata allows consumers to identify development patterns (e.g., commits from AI assistants like claude@ or copilot@ vs human developers) without pre-classifying commits. This enables more context-aware AI analysis of repository health and development practices.

Files affected:
- 2 new core files: src/core/git/gitHistory.ts (372 lines), src/core/git/gitHistoryHandle.ts (149 lines)
- 8 modified source files: CLI (cliRun.ts, defaultAction.ts, types.ts), config (configSchema.ts), output (outputGenerate.ts, outputGeneratorTypes.ts, markdownStyle.ts, xmlStyle.ts)
- 1 modified packager: src/core/packager.ts
- 10 test files added/modified: comprehensive unit tests for all git history functions (871 tests pass)
- 5 documentation files: new guide + updates to existing guides
- 2 project files: README.md, llms-install.md

Testable:
```bash
# Basic commit history with graph
npx repomix --include-commit-history --output output.xml

# Analyze last 20 commits
npx repomix --include-commit-history --commit-range HEAD~20..HEAD

# Full diffs for specific range
npx repomix --include-commit-history --commit-range v1.0..v2.0 --commit-patch-detail full

# Metadata only (no patches, no graph)
npx repomix --include-commit-history --commit-patch-detail metadata --no-commit-graph

# Verify author/committer metadata is visible
npx repomix --include-commit-history -o output.xml
grep '<email>' output.xml

# Run tests
npm run test  # 871 tests pass
npm run lint  # 0 errors
```
…syntax, tests

Summary: Fix commit history parsing by replacing fragile body/file heuristic with
null byte delimiter, add missing git_history section to parsable XML output, fix
invalid Mermaid gitGraph syntax, add files list to XML/Markdown templates.

Previous behavior:
- getCommitMetadata() in gitHistory.ts used heuristic loop assuming body lines
  start with spaces or are empty, which misclassified body text like
  "This line has no leading space" as a file path
- generateMermaidGraph() produced invalid syntax: `merge abc1234 tag: "message"`
  and separate `commit tag: "v1.0"` lines which Mermaid parsers reject
- generateParsableXmlOutput() in outputGenerate.ts had no git_history section,
  while generateParsableJsonOutput() had complete gitCommitHistory structure
- xmlStyle.ts and markdownStyle.ts templates rendered commits without files list
  even though metadata.files was available

What changed:
- src/core/git/gitHistory.ts:
  - Add NULL_BYTE constant and %x00 to git log format string after %b
  - Split stdout on NULL_BYTE: `const [formatOutput, fileListOutput] = result.stdout.split(NULL_BYTE)`
  - Body parsing: `lines.slice(10).join('\n').trim()` instead of heuristic loop
  - Files parsing: `fileListOutput.split('\n').filter(Boolean)` instead of heuristic
  - Add escapeMermaidString(): replaces " with ' and \n with space
  - Fix Mermaid syntax: `commit id: "abc1234: (merge) message" tag: "v1.0" type: HIGHLIGHT`
    instead of separate merge/tag lines
- src/core/output/outputGenerate.ts:
  - Add git_history block to generateParsableXmlOutput() matching JSON structure:
    summary (total_commits, merge_commits, range, detail_level),
    commit_graph (ascii_graph, mermaid_graph, merge_commits, tags),
    commits array with @_hash/@_abbreviated_hash attributes, author/committer/parents/message/body/files/patch
- src/core/output/outputStyles/xmlStyle.ts:
  - Add `{{#if this.metadata.files.length}}<files>{{#each}}<file>{{{this}}}</file>{{/each}}</files>{{/if}}`
- src/core/output/outputStyles/markdownStyle.ts:
  - Add `{{#if this.metadata.files.length}}**Files Changed**:\n{{#each}}- \`{{{this}}}\`{{/each}}{{/if}}`
- tests/core/git/gitHistory.test.ts:
  - Add NULL_BYTE constant matching implementation
  - Update existing tests to use NULL_BYTE instead of empty line after body
  - Add test: body text without leading spaces parsed correctly (edge case fix verification)
  - Add test: empty body with NULL_BYTE handled
  - Add test: files with special characters (parentheses, brackets, hyphens) in paths
  - Add test: commit with no files changed returns empty array
- tests/core/git/gitHistoryHandle.test.ts (new file, 264 lines):
  - Test: returns undefined when includeCommitHistory is false
  - Test: returns undefined when directory is not a git repository
  - Test: returns git history with default options (range HEAD~50..HEAD, detailLevel stat)
  - Test: uses custom commitRange from config
  - Test: uses custom commitPatchDetail from config
  - Test: excludes graph when includeCommitGraph is false
  - Test: excludes tags when includeGitTags is false (returns empty object)
  - Test: excludes patches when includeCommitPatches is false
  - Test: counts merge commits correctly (commits with 2+ parents)
  - Test: throws RepomixError on git command failure
  - Test: uses cwd when rootDirs is empty
- tests/core/output/outputGenerate.test.ts:
  - Add test: markdown output includes Files Changed section with bullet list
  - Add test: parsable XML output includes git_history with summary/commit_graph/commits
  - Add test: XML template output includes <files>/<file> elements
  - Add test: parsable XML commit_graph includes merge_commits field
- website/client/src/en/guide/configuration.md:
  - Add includeCommitPatches option to table: "Whether to include code patches (diffs) for each commit"
  - Add complete example config JSON with all commit history options
  - Add "Commit History Analysis" section explaining each option
- website/client/src/en/guide/output.md:
  - Update XML example: hash="abc123def456789" instead of "abc123"
  - Add <parents><parent>def456abc789012</parent></parents> element
  - Add <body>Extended commit message body...</body> element
  - Add <files><file>src/feature.ts</file><file>tests/feature.test.ts</file></files> element
- website/client/src/en/guide/tips/git-commit-history.md:
  - Clarify --all flag: "shows how branches connect within the specified range"

Why: Null bytes (\0) cannot appear in git commit messages or file paths, making
%x00 a reliable delimiter vs heuristics that fail on body text without indentation.
Mermaid gitGraph requires specific syntax (commit id: "..." tag: "..." type: ...).
XML/JSON output parity ensures consistent data regardless of format choice.

Testable: npm run test (894 pass), npm run lint (pass).
Generate: repomix --include-commit-history --style xml --parsable-style
Verify: XML contains <git_history> with <summary>, <commit_graph>, <commits>
…ional parameter

Previous behavior:
- CLI had 6 separate flags: --include-commit-history, --commit-range, --commit-patch-detail, --no-commit-graph, --no-git-tags, --no-commit-patches
- Patch detail levels used git-incompatible names: 'full', 'files'
- Documentation used vague terms like "complete diffs", "change statistics"
- Config file and CLI both exposed all fine-grained controls

What changed:
- Simplified CLI to 2 flags with optional parameter syntax
- Renamed patch levels to match git log parameters: 'full'→'patch', 'files'→'name-only'
- Made patch detail level an optional parameter: --include-commit-history [level]
- Config file retains all advanced options (includeCommitGraph, includeGitTags, includeCommitPatches)
- Updated all descriptions to use concrete terms with examples
- Added git parameter references throughout documentation

Why:
This change follows the principle of optimizing for the 95% use case. Most users want simple commit history analysis without fine-tuning every component. The simplified CLI makes common tasks easier (just --include-commit-history patch) while advanced users can still use config files for edge cases. Parameter names now match git's conventions for familiarity.

Files changed:
- src/cli/cliRun.ts: Simplified from 6 flags to 2, added optional parameter with validation
- src/cli/types.ts: Changed includeCommitHistory to boolean | string type, removed 3 boolean flags
- src/cli/actions/defaultAction.ts: Streamlined config building logic, handle string vs boolean type
- src/config/configSchema.ts: Updated enum from ['full', 'stat', 'files', 'metadata'] to ['patch', 'stat', 'name-only', 'metadata']
- src/core/git/gitHistory.ts: Updated PatchDetailLevel type and switch cases for 'patch' and 'name-only'
- tests/core/git/gitHistory.test.ts: Updated test cases to use new parameter names
- tests/core/git/gitHistoryHandle.test.ts: Changed 'full' to 'patch' in test expectations
- tests/core/output/outputGenerate.test.ts: Updated detailLevel from 'full' to 'patch'
- README.md: Replaced vague terms with concrete examples, simplified CLI examples
- llms-install.md: Added git log parameter references to commitPatchDetail descriptions
- website/client/src/en/guide/command-line-options.md: Condensed 6 options into 2 with git param mapping
- website/client/src/en/guide/configuration.md: Updated table and detailed explanations with concrete terms
- website/client/src/en/guide/tips/git-commit-history.md: Added Git Param column to table, made examples concise
- website/client/src/en/guide/output.md: Added git param references to patch detail levels
- website/client/src/en/guide/usage.md: Updated examples to use simplified syntax with git params

Technical details:
- Commander.js optional parameter syntax: --include-commit-history [level]
- Validation function checks valid levels: ['patch', 'stat', 'name-only', 'metadata']
- Type narrowing in defaultAction.ts: typeof options.includeCommitHistory === 'string'
- Zod enum validation updated in both base and default schemas
- Git commands mapped: patch→--patch, stat→--stat, name-only→--name-only, metadata→--no-patch

Testable:
- Run: npm run lint (passes: 0 errors, 0 warnings)
- Run: npm test (passes: 894/894 tests)
- Run: repomix --include-commit-history (uses stat level by default)
- Run: repomix --include-commit-history patch (shows line-by-line diffs)
- Run: repomix --include-commit-history name-only (shows filenames only)
- Run: repomix --include-commit-history metadata (no diffs, metadata only)
- Run: repomix --include-commit-history invalid (shows error with valid options)
…git log

Replace --include-commit-history with orthogonal CLI flags that directly
map to git log parameters, enabling precise control over diff output format
and enhancement features.

Previous behavior:
- Single --include-commit-history flag enabled comprehensive history
- gitHistoryHandle.ts separate from gitLogHandle.ts
- includeCommitHistory config field controlled all history features
- Limited to 4 diff formats (patch, stat, name-only, name-status)
- No separate control over graph visualization vs summary output

What changed:
- Removed --include-commit-history CLI flag
- Removed includeCommitHistory config field
- Deleted src/core/git/gitHistoryHandle.ts (merged into gitLogHandle.ts)
- Added 8 diff format flags matching git log: --stat, --patch, --numstat,
  --shortstat, --dirstat, --name-only, --name-status, --raw
- Added 2 enhancement flags: --graph (commit graph visualization),
  --summary (file operations like creates, renames, mode changes)
- Extended commitPatchDetail enum to support all 8 formats
- Added includeSummary config field for granular control
- Implemented mutual exclusion validation for diff format flags
- Unified getGitLogs() function returns GitLogResult | GitHistoryResult
- Automatic routing between simple mode (includeLogs only) and
  comprehensive mode (includeCommitGraph or includeSummary enabled)
- Support both .. and ... commit range syntax in all documentation

Why:
Enable users to select exact git log output format needed for their use case
without enabling unwanted features. Orthogonal flags match git log's design,
making the interface intuitive for git users. Consolidating handlers reduces
code duplication and maintenance burden.

Files affected:
Core implementation:
- src/config/configSchema.ts: extended commitPatchDetail enum to 8 formats,
  added includeSummary field, removed includeCommitHistory
- src/core/git/gitHistory.ts: extended PatchDetailLevel type with 5 new
  formats (numstat, shortstat, dirstat, name-status, raw), added
  includeSummary parameter to getCommitPatch()
- src/core/git/gitLogHandle.ts: merged all gitHistoryHandle.ts
  functionality, added getComprehensiveGitHistory() and getSimpleGitLogs()
  helpers, unified getGitLogs() with routing logic
- src/core/git/gitHistoryHandle.ts: deleted (consolidated into
  gitLogHandle.ts)
- src/core/output/outputGenerate.ts: removed gitHistoryResult parameter
  from generateOutput() and buildOutputGeneratorContext() (8→6 params,
  7→6 params), updated createRenderContext() to detect and extract from
  GitLogResult | GitHistoryResult union type
- src/core/packager.ts: replaced separate getGitHistory() call with single
  unified getGitLogs() call

CLI:
- src/cli/types.ts: removed includeCommitHistory field, added 8 orthogonal
  diff format flags (stat, patch, numstat, shortstat, dirstat, nameOnly,
  nameStatus, raw) and 2 enhancement flags (graph, summary)
- src/cli/cliRun.ts: removed --include-commit-history option, added 8 diff
  format option flags and 2 enhancement option flags with descriptions
- src/cli/actions/defaultAction.ts: implemented mutual exclusion validation
  for diff format flags (lines 287-302), mapped each CLI flag to
  appropriate config field (lines 305-340), fixed linter error by removing
  duplicate else-if block

Tests (fixed compilation errors):
- tests/core/output/diffsInOutput.test.ts: updated generateOutput() calls
  from 8 to 6 parameters
- tests/core/output/flagFullDirectoryStructure.test.ts: removed
  includeCommitHistory, added includeSummary, updated
  buildOutputGeneratorContext() calls from 7 to 6 parameters
- tests/core/output/outputGenerate.test.ts: replaced includeCommitHistory
  with includeCommitGraph + includeCommitPatches in mock structures
- tests/core/output/outputGenerateDiffs.test.ts: updated generateOutput()
  calls from 8 to 6 parameters
- tests/core/output/outputStyles/jsonStyle.test.ts: removed
  includeCommitHistory, added includeSummary
- tests/core/metrics/calculateGitDiffMetrics.test.ts: removed
  includeCommitHistory, added includeSummary
- tests/core/metrics/calculateGitLogMetrics.test.ts: removed
  includeCommitHistory, added includeSummary
- tests/core/git/gitLogHandle.test.ts: added missing mock dependencies
  (isGitRepository, getCommitGraph, getCommitPatch), added type casts for
  union type access

Documentation (with concrete examples for both .. and ... syntax):
- README.md: replaced --include-commit-history examples with orthogonal
  flags, listed all 8 diff format flags and 2 enhancement flags
- website/client/src/en/guide/tips/git-commit-history.md: complete rewrite
  with orthogonal parameter design, detailed tables for diff format and
  enhancement flags, workflow examples
- website/client/src/en/guide/usage.md: updated Commit History Analysis
  section with all flags and examples
- website/client/src/en/guide/command-line-options.md: reorganized Git
  Commit History Options section with detailed flag descriptions
- website/client/src/en/guide/output.md: updated to reflect new flag
  structure and all diff formats

Testable:
- Verify mutual exclusion: npx repomix --stat --patch (should error)
- Test diff format flag: npx repomix --name-only --commit-range HEAD~10..HEAD
- Test enhancement flags: npx repomix --name-status --graph --summary
- Test both range syntaxes: npx repomix --stat --commit-range main..feature
  vs npx repomix --stat --commit-range main...feature
- Verify simple mode: npx repomix --include-logs (no graph/summary)
- Verify comprehensive mode: npx repomix --include-logs --graph
…t codebase

Update all functions that accept git log results to handle the new union
type (GitLogResult | GitHistoryResult) returned by the unified getGitLogs()
function.

Previous behavior:
- Functions expected only GitLogResult type
- GitHistoryResult couldn't be passed to security checks or metrics
- Test mocks used old includeCommitHistory field
- Test mocks called functions with wrong parameter counts
- Boolean expression returned non-boolean type

What changed:
Type signature updates:
- src/core/metrics/calculateMetrics.ts: updated gitLogResult parameter
  type from GitLogResult | undefined to GitLogResult | GitHistoryResult |
  undefined
- src/core/metrics/calculateGitLogMetrics.ts: same type update, added
  logic to extract content from either logContent (simple) or graph.graph
  (comprehensive)
- src/core/security/validateFileSafety.ts: same type update for
  gitLogResult parameter
- src/core/security/securityCheck.ts: same type update, added logic to
  check both logContent and graph.graph fields for security scanning

Union type handling:
- src/core/output/outputGenerate.ts:36: added !! boolean coercion to fix
  "boolean | undefined" assignment error
- src/core/metrics/calculateGitLogMetrics.ts:24-30: added type
  discrimination logic using 'logContent' in gitLogResult vs 'graph' in
  gitLogResult
- src/core/security/securityCheck.ts:50-64: same discrimination logic
  for security checks

Test fixes:
- tests/config/configSchema.test.ts: replaced includeCommitHistory with
  includeSummary (2 occurrences)
- tests/core/packager.test.ts:90-97: updated generateOutput() mock call
  from 7 to 6 parameters (removed extra undefined)
- tests/core/git/gitHistory.test.ts:314,334,354: added includeSummary
  parameter (false) to getCommitPatch() calls, replaced "metadata" test
  with "includeSummary" test
- tests/core/git/gitHistoryHandle.test.ts: deleted file (consolidation
  into gitLogHandle.ts completed)

Why:
The consolidated getGitLogs() function returns a union type to support
both simple logs and comprehensive history. All consuming functions must
handle both types correctly to avoid TypeScript compilation errors and
runtime failures. Tests needed updates to match new function signatures
and remove references to deleted functionality.

Files affected:
- src/core/metrics/calculateGitLogMetrics.ts: type update, union
  discrimination
- src/core/metrics/calculateMetrics.ts: type signature update
- src/core/output/outputGenerate.ts: boolean coercion fix
- src/core/security/securityCheck.ts: type update, union discrimination
- src/core/security/validateFileSafety.ts: type signature update
- tests/config/configSchema.test.ts: replaced includeCommitHistory
- tests/core/packager.test.ts: fixed parameter count
- tests/core/git/gitHistory.test.ts: added includeSummary parameter,
  updated test
- tests/core/git/gitHistoryHandle.test.ts: deleted obsolete file

Testable:
- npm run build (passes without TypeScript errors)
- npm test (all 883 tests pass across 97 test files)
- Verify union type handling: Git operations work with both simple and
  comprehensive modes
- Verify security checks: Both logContent and graph fields scanned
- Verify metrics: Token counts calculated for both result types
…ult into GitLogResult eliminating union type

Summary: Replace union type (GitLogResult | GitHistoryResult) with single GitLogResult interface using optional fields for simple/comprehensive modes, and add conditional checks to prevent git_logs/gitLogs output when comprehensive mode is active.

Previous behavior (from git diff --staged):
- getGitLogs() in gitLogHandle.ts:300 returned GitLogResult | GitHistoryResult union type
- createRenderContext() in outputGenerate.ts:33 used 'summary' in result checks for type discrimination
- generateParsableXmlOutput() line 119: git_logs block output when renderContext.gitLogEnabled (no check for comprehensive mode)
- generateParsableJsonOutput() line 210: gitLogs field output when renderContext.gitLogEnabled (no check for comprehensive mode)
- Result: empty <git_logs></git_logs> tag appeared alongside <git_history> in XML
- Result: gitLogs: undefined appeared alongside gitCommitHistory in JSON
- Tests expected old {date, message, files} structure instead of {metadata: {...}, patch} structure

What changed:
Type consolidation (src/core/git/gitLogHandle.ts):
- Line 56: Renamed GitHistoryResult interface to GitLogResult with optional fields
- Line 27-29: Made patch field optional in HistoryCommitResult (patch?: string)
- Line 56-66: GitLogResult structure: commits (required), graph/summary/logContent (optional)
- Lines 156-176: getSimpleGitLogs() converts {date, message, files} to {metadata: CommitMetadata, patch: undefined}
- Line 199: getComprehensiveGitHistory() return type changed from GitHistoryResult to GitLogResult
- Line 300: getGitLogs() return type changed from union to single GitLogResult | undefined

Conditional output logic (src/core/output/outputGenerate.ts):
- Line 8: Removed GitHistoryResult import, kept GitLogCommit and GitLogResult
- Lines 36-50: Added extractSimpleCommits() function to convert HistoryCommitResult[] to GitLogCommit[] for templates
- Line 53: Changed isComprehensiveHistory from config check to data check: !!(gitLogResult?.graph || gitLogResult?.summary)
- Lines 77-82: Access gitLogResult fields directly instead of separate gitHistoryResult parameter
- Line 119: Added && !renderContext.gitCommitHistoryEnabled to git_logs condition (prevents dual output in XML)
- Line 210: Added && !renderContext.gitCommitHistoryEnabled to gitLogs condition (prevents dual output in JSON)

Type signature updates (removed union type):
- src/core/output/outputGeneratorTypes.ts:13: gitLogResult from GitLogResult | GitHistoryResult | undefined to GitLogResult | undefined
- src/core/metrics/calculateMetrics.ts:29: gitLogResult parameter type
- src/core/metrics/calculateGitLogMetrics.ts:8-26: gitLogResult parameter, extract content from logContent || graph?.graph
- src/core/security/securityCheck.ts:10: gitLogResult parameter type
- src/core/security/validateFileSafety.ts:17: gitLogResult parameter type

Test structure updates:
- tests/core/git/gitLogHandle.test.ts:89-134: Changed 3 test expectations from {date, message, files} to {metadata: {hash, abbreviatedHash, parents, author: {name, email, date}, committer: {name, email, date}, message, body, files}, patch: undefined}
- tests/core/git/gitLogHandle.test.ts:3-9: Added import type GitLogResult on separate line (TypeScript best practice)
- tests/core/git/gitLogHandle.test.ts:84-239: Added missing deps (isGitRepository, getCommitGraph, getCommitPatch) to all getGitLogs test calls
- tests/core/output/outputGenerate.test.ts:67-92: Updated mock gitLogResult to use metadata wrapper structure
- tests/cli/actions/workers/defaultActionWorker.test.ts:62: Removed includeCommitHistory field (no longer exists)
- tests/cli/actions/workers/defaultActionWorker.test.ts:65: Added includeSummary field (required by schema)
- tests/core/git/gitHistory.test.ts:392: Added missing includeSummary parameter to getCommitPatch call

Why: Union types require type discrimination checks ('summary' in result) scattered across 9 files. Single interface with optional fields:
1. Removes all type discrimination logic from outputGenerate.ts, calculateGitLogMetrics.ts
2. Single function signature GitLogResult | undefined instead of GitLogResult | GitHistoryResult | undefined
3. Backward compatibility via optional logContent field for simple mode
4. Fixes XML/JSON template substitution bug where git_logs appeared empty alongside git_history
5. Data-driven mode detection (graph || summary present) instead of config flag check

Files affected:
- src/core/git/gitLogHandle.ts (102 insertions): Type consolidation, metadata wrapper conversion
- src/core/output/outputGenerate.ts (50 modifications): Removed type discrimination, added dual-output prevention
- src/core/output/outputGeneratorTypes.ts (10 modifications): Removed union type from interface
- src/core/metrics/calculateGitLogMetrics.ts (15 modifications): Extract content from unified type
- src/core/metrics/calculateMetrics.ts (4 modifications): Updated parameter type
- src/core/security/securityCheck.ts (8 modifications): Updated parameter type
- src/core/security/validateFileSafety.ts (4 modifications): Updated parameter type
- tests/core/git/gitLogHandle.test.ts (106 insertions): Metadata wrapper expectations, fixed deps, proper imports
- tests/core/output/outputGenerate.test.ts (22 insertions): Metadata wrapper mock data
- tests/cli/actions/workers/defaultActionWorker.test.ts (2 modifications): Fixed config schema compliance
- tests/core/git/gitHistory.test.ts (1 modification): Fixed getCommitPatch parameter count

Testable:
- Run: node bin/repomix.cjs --style xml --commit-range HEAD~3..HEAD --stat --parsable-style --output test.xml
  - Verify: grep "<git_history>" test.xml shows git_history block present
  - Verify: grep "<git_logs>" test.xml | wc -l returns 0 (no git_logs when comprehensive mode active)
- Run: node bin/repomix.cjs --style json --commit-range HEAD~3..HEAD --stat --output test.json
  - Verify: jq '.gitCommitHistory' test.json shows object with summary, commits
  - Verify: jq '.gitLogs' test.json shows null (not present when comprehensive mode active)
- Run: npm run lint
  - Verify: 0 errors, 0 warnings
- Run: npm test
  - Verify: all 883 tests pass
…CommitPatches default

**Summary**: Consolidates all git commit output into single git_logs block across XML/Markdown/JSON formats while maintaining backward compatibility, and fixes includeCommitPatches default that broke backward compat.

**Previous behavior**:
- XML/Markdown had separate <git_history> and <git_logs> blocks
- includeCommitPatches defaulted to true, forcing --include-logs to use graph path
- When --include-logs used alone, tried to fetch git log --graph which failed on repos with <50 commits
- Tests checked for "Git Commit History" heading in markdown
- JSON output included both logCommits and commits with empty metadata in backward compat mode

**What changed**:
- src/config/configSchema.ts:125: Change includeCommitPatches default from true to false
- src/core/git/gitLogHandle.ts:43-56: Update GitLogResult interface docs to describe additive optional fields
- src/core/git/gitLogHandle.ts:129-227: Remove getSimpleGitLogs/getComprehensiveGitLogs split, use single getGitLogs function
- src/core/output/outputGenerate.ts:36-76: Remove gitCommitHistoryEnabled field and hasGraphOrSummary variable
- src/core/output/outputGeneratorTypes.ts:37: Remove gitCommitHistoryEnabled field from RenderContext
- src/core/output/outputStyles/xmlStyle.ts:67-152: Consolidate git_history into git_logs block with additive optional fields
- src/core/output/outputStyles/markdownStyle.ts:64-150: Consolidate "Git Commit History" section into "Git Logs" section
- src/core/output/outputGenerate.ts:114-165,213-256: Consolidate parsable XML and JSON output to single gitLogs structure
- src/core/output/outputGenerate.ts:142,237: Add !renderContext.gitLogCommits condition to prevent outputting commits with empty metadata in backward compat mode
- tests/core/output/outputGenerate.test.ts:330,457: Add includeLogs: true to test configs
- tests/core/output/outputGenerate.test.ts:374: Update test expectation from "Git Commit History" to "# Git Logs"

**Why**: Consolidating into one naturally backward and forward compatible structure eliminates special case handling and makes output format cleaner. Fixing includeCommitPatches default ensures --include-logs alone uses backward compatible git log command, not git log --graph.

**Testable**:
- Backward compat: `repomix --include-logs --style xml` outputs only git_log_commit entries
- Full features: `repomix --include-logs --commit-range HEAD~1..HEAD --stat --graph --summary --style xml` outputs summary, commit_graph, and commits in git_logs block
- All 883 tests passing
- All linting passing (0 errors, 0 warnings)

**Files affected**:
- src/config/configSchema.ts: Fix includeCommitPatches default
- src/core/git/gitLogHandle.ts: Update docs, simplify function
- src/core/output/outputGenerate.ts: Remove mode flag, consolidate output
- src/core/output/outputGeneratorTypes.ts: Remove gitCommitHistoryEnabled field
- src/core/output/outputStyles/xmlStyle.ts: Consolidate into git_logs block
- src/core/output/outputStyles/markdownStyle.ts: Consolidate into Git Logs section
- tests/core/output/outputGenerate.test.ts: Update test configs and expectations
…le GitLogCommit structure

Summary: Eliminate duplicate commit data structures (logCommits vs commits) by enhancing GitLogCommit interface with progressive optional fields, following DRY principles while maintaining backward compatibility with main branch.

Previous behavior (based on git diff):
- GitLogResult had two separate arrays: logCommits (core fields) and commits (full metadata with HistoryCommitResult wrapper)
- Output generation used extractBasicCommits() to transform commits → logCommits
- Templates had conditional logic: render logCommits OR commits, never both
- Token counting used logContent string field
- Security checking used logContent string field

What changed:
- src/core/git/gitLogHandle.ts:
  * Removed HistoryCommitResult interface (lines 24-29)
  * Enhanced GitLogCommit interface with optional fields: hash, abbreviatedHash, author, committer, parents, body, patch (lines 58-97)
  * Updated GitLogResult to use only logCommits array, removed logContent and commits fields (lines 42-54)
  * Full features path populates all fields including extended metadata (lines 193-215)
  * Backward compat path returns only core fields (lines 236-240)

- src/core/output/outputGenerate.ts:
  * Removed extractBasicCommits() transformation function (lines 36-46)
  * Simplified createRenderContext to return only gitLogCommits (lines 55-62)
  * Consolidated XML output to single git_log_commit mapping with progressive optional fields (lines 117-145)
  * Consolidated JSON output to single logCommits array with progressive optional fields (lines 209-225)

- src/core/output/outputGeneratorTypes.ts:
  * Removed gitLogContent and gitCommitHistoryItems fields from RenderContext (lines 34-37)

- src/core/output/outputStyles/xmlStyle.ts:
  * Moved summary and commit_graph sections before commits for overview-first presentation (lines 98-143)
  * Removed separate gitCommitHistoryItems loop
  * Single gitLogCommits loop with conditional rendering of optional fields

- src/core/output/outputStyles/markdownStyle.ts:
  * Removed separate gitCommitHistoryItems loop (lines 100-138)
  * Single gitLogCommits loop with progressive disclosure of extended fields

- src/core/metrics/calculateGitLogMetrics.ts:
  * Changed from logContent string to JSON.stringify(logCommits) for token counting (lines 22-33)
  * Maintains graph content in token count calculation

- src/core/security/securityCheck.ts:
  * Changed from logContent string to JSON.stringify(logCommits) for security scanning (lines 49-56)

- tests/core/git/gitLogHandle.test.ts:
  * Updated expectations from commits array with metadata wrapper to logCommits with core fields only (lines 93-105)

- tests/core/metrics/calculateGitLogMetrics.test.ts:
  * Updated mock data from logContent string to logCommits array (multiple test cases)
  * Validators expect JSON.stringify(logCommits) for token counting

- tests/core/output/outputGenerate.test.ts:
  * Updated mock GitLogResult objects to use logCommits with flattened structure (lines 232-238, 331-343)
  * Changed XML assertions from commits['@_hash'] to git_log_commit.hash (lines 424-431)
  * Updated files format expectations to match plain-text rendering

Why:
- Violates DRY: Two arrays (logCommits, commits) representing same concept
- Unnecessary complexity: Conditional output logic to prevent rendering both
- Template duplication: Separate iteration loops for same data
- Data transformation overhead: extractBasicCommits() converting between formats
- Single source of truth: One canonical structure eliminates inconsistencies

Implementation approach:
- Progressive disclosure pattern: Core fields (date, message, files) always present, extended fields (hash, author, committer, parents, body, patch) optional
- Backward compatible: Old parsers ignore unknown optional fields per JSON/XML specs
- Forward compatible: Extended fields populate when graph/summary/patches enabled
- Additive evolution: Existing gitLogCommits field grows capabilities rather than being replaced

Files affected:
- src/core/git/gitLogHandle.ts: Enhanced GitLogCommit interface, removed HistoryCommitResult, updated GitLogResult
- src/core/output/outputGenerate.ts: Removed extractBasicCommits(), simplified render context
- src/core/output/outputGeneratorTypes.ts: Cleaned up RenderContext interface
- src/core/output/outputStyles/xmlStyle.ts: Unified template with progressive optional fields
- src/core/output/outputStyles/markdownStyle.ts: Unified template with progressive optional fields
- src/core/metrics/calculateGitLogMetrics.ts: JSON serialization for token counting
- src/core/security/securityCheck.ts: JSON serialization for security scanning
- tests/core/git/gitLogHandle.test.ts: Updated expectations for unified structure
- tests/core/metrics/calculateGitLogMetrics.test.ts: Updated mock data and assertions
- tests/core/output/outputGenerate.test.ts: Updated mock data and XML/JSON expectations

Testable outcomes:
- Run: npm run test → All 883 tests pass
- Run: repomix --include-logs --style xml → Outputs only core fields (date, message, files)
- Run: repomix --include-logs --commit-range HEAD~1..HEAD --graph --stat --summary --style xml → Outputs all extended fields (hash, author, committer, parents, body, patch) plus summary and graph sections
- Verify: XML/JSON output structure matches main branch baseline for backward compatibility
- Verify: Progressive disclosure works - optional fields only present when features enabled

Backward compatibility: Maintained with main branch - gitLogCommits field existed in main with same plain-text files format, core fields structure unchanged

Implementation note: Token counting and security scanning now use JSON.stringify(logCommits) instead of raw logContent string (removed field), providing consistent serialization for internal metrics
…patch flag mapping

Summary: Eliminate dual simple/enhanced code paths by implementing single git log execution that combines metadata, files, patches, and graph in one subprocess call using git's parameter stacking capability, and fix CLI bug where --stat/--patch flags failed to enable patch fetching.

Previous behavior (based on git diff):
- getGitLogs() had if/else branching (lines 299-362): simple path vs enhanced path
- Enhanced path called getCommitGraph() which looped calling git log -1 per commit for metadata (gitHistory.ts:229)
- Simple path called execGitLog() once for basic date|message + files
- CLI set commitPatchDetail when --stat/--patch specified but left includeCommitPatches as false
- Result: patches never fetched despite user requesting them via --stat/--patch flags

What changed:
- src/cli/actions/defaultAction.ts:330-333:
  * Change commitPatchDetail mapping from single field to object with includeCommitPatches: true
  * When user specifies --stat/--patch/--numstat/etc, both commitPatchDetail and includeCommitPatches are set

- src/core/git/gitCommand.ts:7,181-281:
  * Import PatchDetailLevel type
  * Add GitLogCompleteOptions interface (directory, range, maxCommits, includeGraph, patchDetail)
  * Add execGitLogComplete() function
  * Build git log args array conditionally: base format with metadata fields, add --stat/--patch/etc if patchDetail specified, add --graph --all if includeGraph true, add range or -n maxCommits
  * Use separators: \x1E (record separator) between commits, \x1F (field separator) between metadata fields, \x00 (null byte) before file list
  * Return raw git log stdout for parsing

- src/core/git/gitHistory.ts:368:
  * Export generateMermaidGraph() (was const, now export const for reuse)

- src/core/git/gitLogHandle.ts:4,8-11:
  * Import execGitLogComplete, GitLogCompleteOptions from gitCommand
  * Import generateMermaidGraph, getTags from gitHistory
  * Update deps parameter in getGitLogs() to use new functions

- src/core/git/gitLogHandle.ts:125-245:
  * Add ParsedCommitComplete interface with hash, abbreviatedHash, parents, author, committer, message, body, files, optional patch, optional graphPrefix
  * Add parseGitLogComplete() function
  * Split records on \x1E separator
  * Extract graph prefix lines (before first field separator) if hasGraph is true
  * Split metadata section from files/patch section on null byte
  * Parse metadata by splitting on field separator and stripping graph prefixes from each field
  * Split files and patch sections on blank lines (\n\n+)
  * Normalize line endings with split(/\r?\n/) to handle CRLF

- src/core/git/gitLogHandle.ts:297-397:
  * Remove entire if/else block (enhanced mode vs simple mode branching deleted)
  * Calculate needsEnhanced based on includeCommitGraph || includeCommitPatches || includeSummary
  * Call execGitLogComplete() once with conditional parameters (range if enhanced, maxCommits otherwise)
  * Call parseGitLogComplete() to extract all data from single output
  * Map to GitLogCommit array with core fields always present, extended fields only when needsEnhanced
  * Build CommitGraph from parsed data by reconstructing ASCII from graphPrefix, building metadata array, generating Mermaid
  * Build summary from parsed data (no separate git calls)

- tests/core/git/gitLogHandle.test.ts:67-355:
  * Replace getGitLog/getCommitGraph mocks with execGitLogComplete/getTags mocks
  * Build mock output using \x1E, \x1F, \x00 separators matching execGitLogComplete format
  * Update expected function calls to match GitLogCompleteOptions structure
  * Update all assertions to expect {logCommits, graph, summary} return structure
  * Add test data with \r\n line endings to verify CRLF handling

Why:
- Dual code paths violated DRY principle with separate implementations for simple vs enhanced
- Enhanced mode made multiple separate git subprocess calls instead of combining parameters
- Git supports combining --pretty + --name-only + --stat + --graph in single command
- Parsing interleaved output once is more efficient than multiple subprocess calls
- CLI bug prevented user-requested patches from appearing in output

Implementation approach:
- Conditional parameter building: start with base args, add flags based on config
- Intelligent parsing: use separators to reliably split commits, fields, files, patches
- Progressive disclosure: parse all data, conditionally include fields in GitLogCommit based on needsEnhanced
- Graph reconstruction: preserve graph prefix lines during parsing, rejoin for ASCII graph
- Single source of truth: one git call, one parse, multiple uses of parsed data

Files affected:
- src/cli/actions/defaultAction.ts: Fix CLI param mapping for patch flags
- src/core/git/gitCommand.ts: Add execGitLogComplete() with conditional arg building
- src/core/git/gitHistory.ts: Export generateMermaidGraph
- src/core/git/gitLogHandle.ts: Add parseGitLogComplete(), consolidate getGitLogs()
- tests/core/git/gitLogHandle.test.ts: Update mocks for consolidated API

Testable outcomes:
- Run: npm run test → All 883 tests pass
- Run: repomix --include-logs → Outputs only core fields (date, message, files)
- Run: repomix --include-logs --graph --stat → Outputs extended fields (hash, author, committer, parents) and patches
- Run: repomix --stat → Patches appear in output (CLI bug fixed)
- Verify: git log called once per execution instead of multiple times
- Verify: Progressive disclosure works - extended fields only when enhancement flags used

Performance: Consolidated multiple git subprocess calls into single git log invocation by using git's parameter stacking capability (--pretty + --name-only + --stat + --graph)
…th implementation

Summary: Update MCP and user documentation to accurately describe consolidated git logs implementation where all modes use single optimized code path with progressive field disclosure.

Previous behavior (documentation):
- configuration.md line 111 referenced removed includeCommitHistory flag
- configuration.md had incorrect default values for includeCommitGraph (true → false) and includeCommitPatches (true → false)
- llms-install.md documented includeCommitHistory instead of orthogonal flags (includeLogs, includeCommitGraph, includeSummary, includeCommitPatches)
- output.md showed old <git_history>/<commits> XML structure from previous implementation

What changed:
- llms-install.md:64-69:
  * Remove includeCommitHistory parameter documentation
  * Add includeLogs, includeCommitGraph, includeSummary, includeCommitPatches parameters
  * Update commitPatchDetail to list all format options: patch, stat, numstat, shortstat, dirstat, name-only, name-status, raw

- llms-install.md:114-119:
  * Same updates for pack_remote_repository tool

- website/client/src/en/guide/configuration.md:111-116:
  * Remove includeCommitHistory row from configuration table
  * Add includeSummary row
  * Correct defaults: includeCommitGraph false (was true), includeCommitPatches false (was true)
  * Update commitPatchDetail description with all format options

- website/client/src/en/guide/configuration.md:173-185:
  * Remove includeCommitHistory from example config
  * Add includeSummary with correct default false
  * Update all git config defaults to match implementation

- website/client/src/en/guide/configuration.md:349-372:
  * Replace "Commit History Analysis" section with "Commit History Enhancement"
  * Remove includeCommitHistory description
  * Document that enhancement flags (commitRange, includeCommitGraph, includeSummary, includeCommitPatches) provide structured metadata when combined with includeLogs
  * Update example config to use includeLogs + enhancement flags instead of includeCommitHistory

- website/client/src/en/guide/output.md:246-305:
  * Update XML structure from <git_history>/<commits> to <git_logs>/<git_log_commit>
  * Show summary and commit_graph elements with correct structure
  * Show git_log_commit elements with all fields (date, message, files, hash, abbreviated_hash, author, committer, parents, body, patch)
  * Files rendered as plain text (backward compatible with main branch)
  * Update heading from "Commit History Output" context

Why:
- Documentation must accurately reflect actual API and configuration schema
- Users need correct parameter names for MCP tools (pack_codebase, pack_remote_repository)
- Configuration examples must show valid flag combinations that match implementation
- Default values in docs must match configSchema.ts defaults
- XML structure examples must match actual template output

Files affected:
- llms-install.md: MCP tool parameters for both pack_codebase and pack_remote_repository
- website/client/src/en/guide/configuration.md: Config table, defaults, examples, and descriptions
- website/client/src/en/guide/output.md: XML structure example for git logs

Testable outcomes:
- Configuration table defaults match src/config/configSchema.ts default values
- Example configs use valid flag names (includeLogs, includeCommitGraph, includeSummary, includeCommitPatches)
- XML output structure matches src/core/output/outputStyles/xmlStyle.ts template
- MCP parameter names match src/mcp/tools/ implementation
… chars in commits

Previous behavior: Parser used ASCII control characters \x1E (Record Separator)
and \x1F (Unit Separator) as delimiters. These characters CAN appear in git
commit messages, breaking parsing when encountered.

What changed:
- gitCommand.ts: Replace execGitLogComplete with two-pass architecture
  - execGitLogStructured: Metadata + files via -z --raw (NUL-terminated)
  - execGitLogTextBlob: Patch/stat content with double-NUL separator
  - execGitGraph: Separate call avoids prefix interleaving
  - All delimiters now use NUL (\x00) which git rejects in commit content

- gitLogHandle.ts: Rewrite parser for robustness and performance
  - Incremental parsing by detecting 40-char hex hashes as commit boundaries
  - Verify commit boundary by checking abbreviated hash shares prefix
  - Handle filenames that look like git hashes (after raw entry lines)
  - Pre-compile regexes and add length checks for ~40% performance gain

- gitLogHandle.test.ts: Update test infrastructure
  - Use proper 40-char hex hashes in mocks
  - Add edge case test for hash-like filenames
  - Remove DOUBLE_NUL constant (parser no longer relies on it)

Why: Git rejects commits containing NUL bytes ("a NUL byte in commit log
message not allowed"), making NUL 100% safe as delimiter. ASCII control
characters \x1E/\x1F can appear in messages (rare but possible).

Files affected:
- src/core/git/gitCommand.ts: New two-pass git log functions
- src/core/git/gitLogHandle.ts: Rewritten parser with hash-based boundaries
- tests/core/git/gitLogHandle.test.ts: Updated mocks and added edge case test

Testable: npm run test (884 tests pass), parser handles commits with \x1E/\x1F
…tions'

Previous behavior: CLI and docs used "Enhancement Flags" and "Range Options"
as category names for git log related flags.

What changed: Renamed to "Output Verbosity & Graph Options" across all
documentation and source files:
- src/cli/cliRun.ts: .optionsGroup('Git Log Output Verbosity & Graph Options')
- src/cli/types.ts: comment updated to match
- README.md: section header updated
- website/client/src/en/guide/*: All English docs updated

Why: "Enhancement Flags" was vague and non-descriptive. "Output Verbosity &
Graph Options" clearly describes that these options control the level of
detail (verbosity) in git log output and graph visualization. The commit
range is a form of verbosity since it restricts the amount of output.

Files affected:
- src/cli/cliRun.ts:157-158 - CLI option group name
- src/cli/types.ts:42 - Type comment
- README.md:669 - Section header
- website/client/src/en/guide/command-line-options.md - Merged sections
- website/client/src/en/guide/configuration.md - Updated references
- website/client/src/en/guide/output.md - Updated references
- website/client/src/en/guide/tips/git-commit-history.md - Section header
- website/client/src/en/guide/usage.md - Section header
Previous behavior: All translation files had the Git Commit History Options
section in English only.

What changed: Properly translated the Git Commit History Options section
into each respective language:
- de (German): Git-Commit-Verlaufsoptionen
- es (Spanish): Opciones del Historial de Commits de Git
- fr (French): Options de l'Historique des Commits Git
- hi (Hindi): Git कमिट इतिहास विकल्प
- id (Indonesian): Opsi Riwayat Commit Git
- ja (Japanese): Gitコミット履歴オプション
- ko (Korean): Git 커밋 히스토리 옵션
- pt-br (Portuguese-BR): Opções de Histórico de Commits do Git
- vi (Vietnamese): Tùy chọn Lịch sử Commit Git
- zh-cn (Chinese Simplified): Git提交历史选项
- zh-tw (Chinese Traditional): Git提交歷史選項

Each translation includes:
- Section header translated to native language
- Introductory paragraph about orthogonal flags
- "Diff Format Flags" subheader with mutually exclusive note
- All 8 diff format option descriptions
- "Output Verbosity & Graph Options" subheader with combinable note
- All 3 verbosity/graph option descriptions

Why: Proper i18n requires content to be in the user's native language,
not just the section headers. Technical terms like git log flags remain
in English as they are command-line parameters.

Files affected:
- website/client/src/de/guide/command-line-options.md
- website/client/src/es/guide/command-line-options.md
- website/client/src/fr/guide/command-line-options.md
- website/client/src/hi/guide/command-line-options.md
- website/client/src/id/guide/command-line-options.md
- website/client/src/ja/guide/command-line-options.md
- website/client/src/ko/guide/command-line-options.md
- website/client/src/pt-br/guide/command-line-options.md
- website/client/src/vi/guide/command-line-options.md
- website/client/src/zh-cn/guide/command-line-options.md
- website/client/src/zh-tw/guide/command-line-options.md
…name-only'

Previous behavior: Documentation and TypeScript types claimed commitPatchDetail
defaulted to 'stat', but configSchema.ts:121 actually defaults to 'name-only'.
Additionally, GitLogTextBlobOptions.patchDetail union type was missing three
valid values ('name-only', 'name-status', 'raw'), and patchTypes array omitted
'raw', preventing commitPatchDetail: 'raw' from fetching patch content.

What changed:
- llms-install.md: Fix default from 'stat' to 'name-only' (2 occurrences)
- src/core/git/gitCommand.ts:208: Add 'name-only' | 'name-status' | 'raw' to
  GitLogTextBlobOptions.patchDetail union type
- src/core/git/gitLogHandle.ts:241: Add 'raw' to patchTypes array so
  commitPatchDetail: 'raw' triggers patch fetching
- src/core/git/gitLogHandle.ts:251-259: Update type cast to match expanded union
- website/client/src/en/guide/configuration.md: Fix default from 'stat' to
  'name-only' (3 occurrences: table, JSON example, prose)

Why: CodeRabbit review identified documentation/type mismatches with the actual
config schema. Users configuring commitPatchDetail: 'raw' would silently get no
patch output because 'raw' wasn't in the patchTypes check array.

Testable: npm run lint && npm test (884 tests pass)
@yamadashy
yamadashy force-pushed the feat/git-commit-history-squashed branch from 6d34ebc to 414ab2d Compare November 29, 2025 15:08
@yamadashy

yamadashy commented Nov 30, 2025

Copy link
Copy Markdown
Owner

@ahundt
I've thought about the option design. The idea of mirroring git log options is great - it makes the CLI intuitive for users familiar with git.

Here are my thoughts:

Option naming

  • I'd prefer --git- prefix (e.g., --git-stat, --git-patch)
  • It may seem redundant since they're used with --include-logs, but for example, if we want to add other graph-related features in the future, --include-logs --graph could conflict
  • Adding the prefix now avoids namespace issues later

Design concerns

  • --patch fails with stdout maxBuffer length exceeded on this repository
  • includeLogsCount seems ignored when using --patch, --graph, etc.
  • Relationship between commitRange and includeLogsCount is unclear

Scope

  • Many options are added at once. Starting with a minimal set (e.g., --git-patch, --git-stat) might be easier to review and iterate on

What do you think?

@ahundt

ahundt commented Dec 8, 2025

Copy link
Copy Markdown
Author

I'm actually using all the params, they are helpful for inspecting and diagnosing good commits and bad commits in a messy history.

Also if it were instead set up as commands and/or ordered params the ambiguity would go away.

like how there is git rm, not git --rm; and then git rm can itself have params/flags, and those params/flags can also (potentially) work for git log, for example.

even if the --include-logs was kept, there could still be ordering to disambiguate. But I don't care that much if you want --git-patch, it is a bit redundant but not the end of the world it would get the job done.

@ahundt

ahundt commented Dec 17, 2025

Copy link
Copy Markdown
Author

Just following up!

Merge 632 upstream commits (v1.9.1 → v1.12.0) into the git commit
history feature branch.

Conflicts resolved:
- src/core/output/outputGenerate.ts: accept upstream's template cache,
  calculateFileLineCounts, export createRenderContext, filePathsByRoot
  parameter alongside branch's git log field changes and expanded
  XML/JSON output sections
- tests/core/output/outputGenerateDiffs.test.ts: add filePathsByRoot
  undefined parameter to all generateOutput call sites

Post-merge fixes (upstream tests using old GitLogResult interface):
- tests/core/output/outputSplit.test.ts: logContent/commits → logCommits
- tests/core/packager/produceOutput.test.ts: logContent/commits → logCommits
- tests/core/skill/skillSectionGenerators.test.ts: gitLogContent →
  gitCommitHistorySummary/gitCommitGraph in RenderContext mock

Build passes, lint passes (0 errors), 1116/1118 tests pass (2 pre-existing
upstream failures in packageJsonParse.test.ts unrelated to this merge).
devin-ai-integration[bot]

This comment was marked as resolved.

…ee-dot ranges, and range handling

Summary: Address all PR yamadashy#968 review feedback — rename 10 CLI flags to --git- prefix
per maintainer request, fix 4 bugs (maxBuffer crash, three-dot range parsing, missing
patchTypes entries, --commit-range silently ignored), and allow range+maxCommits together.

Previous behavior:
- CLI flags --stat, --patch, --graph, etc. had no namespace prefix, risking conflicts
- execFileAsync used default 1MB maxBuffer, crashing on large repos with --patch
- parseCommitRange split on '..' first, corrupting three-dot ranges like main...feature
- patchTypes array omitted 'name-only' and 'name-status', silently dropping those flags
- --commit-range without --graph/--patch/--summary was silently ignored
- range and maxCommits were mutually exclusive (if/else if)

What changed:
- src/cli/types.ts: rename 10 CliOptions fields (stat→gitStat, graph→gitGraph, etc.)
- src/cli/cliRun.ts: rename 10 CLI flag definitions (--stat→--git-stat, etc.) and
  update semanticSuggestionMap alias (--graph→--git-graph)
- src/cli/actions/defaultAction.ts: update all flag references to use new gitStat/etc.
  field names, update error message to show --git- prefixed flag names
- src/core/git/gitCommand.ts: add GIT_LOG_MAX_BUFFER (50MB) to all 3 exec functions,
  change range vs maxCommits from if/else-if to two independent if blocks
- src/core/git/gitHistory.ts: add separator field to ParsedCommitRange, rewrite
  parseCommitRange to check for '...' before '..', add GIT_LOG_MAX_BUFFER to all
  4 execFileAsync calls, use separator in range reconstruction
- src/core/git/gitLogHandle.ts: add 'name-only' and 'name-status' to patchTypes,
  add hasExplicitRange check so --commit-range works standalone

Tests added/updated:
- tests/cli/actions/defaultAction.buildCliConfig.test.ts: 7 new tests for renamed flags
- tests/core/git/gitCommand.test.ts: 6 new tests for maxBuffer and range+maxCommits
- tests/core/git/gitHistory.test.ts: update 4 assertions for maxBuffer, add 2 three-dot
  range tests with separator field, add separator to all existing parseCommitRange expects
- tests/core/git/gitLogHandle.test.ts: 3 new tests for name-only, name-status patchTypes
  and explicit commitRange without other flags

Documentation (17 files):
- README.md, website EN (4 files), website i18n (11 files): rename all repomix CLI flag
  references from --stat/--patch/etc. to --git-stat/--git-patch/etc., preserving git
  parameter references in descriptions

Why: Maintainer (yamadashy) requested --git- prefix to avoid namespace conflicts.
Bug fixes address Devin AI code review findings and maxBuffer crash report.

Testable: bun run test (1134/1136 pass, 2 pre-existing failures in packageJsonParse),
bun run lint (0 errors), bun run build (compiles clean)

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Devin Review found 3 new potential issues.

View 10 additional findings in Devin Review.

Open in Devin Review

Comment thread src/core/git/gitLogHandle.ts Outdated
Comment on lines +1 to +402
import { execFile } from 'node:child_process';
import { promisify } from 'node:util';
import { RepomixError } from '../../shared/errorHandle.js';
import { logger } from '../../shared/logger.js';

const execFileAsync = promisify(execFile);

/**
* Commit metadata with full information
*/
export interface CommitMetadata {
hash: string;
abbreviatedHash: string;
parents: string[];
author: {
name: string;
email: string;
date: string;
};
committer: {
name: string;
email: string;
date: string;
};
message: string;
body: string;
files: string[];
}

/**
* Commit graph with topology information
*/
export interface CommitGraph {
commits: CommitMetadata[];
graph: string; // ASCII art graph
mermaidGraph: string; // Mermaid diagram
mergeCommits: string[];
tags: Record<string, string>; // tag name -> commit hash
}

/**
* Parsed commit range
*/
export interface ParsedCommitRange {
from: string;
to: string;
raw: string;
separator: '..' | '...';
}

/**
* Detail level for patches (matching git log parameters)
*/
export type PatchDetailLevel =
| 'patch' // git log --patch: line-by-line diffs
| 'stat' // git log --stat: diffstat histogram
| 'numstat' // git log --numstat: numeric additions/deletions
| 'shortstat' // git log --shortstat: one-line summary
| 'dirstat' // git log --dirstat: directory distribution
| 'name-only' // git log --name-only: filenames only
| 'name-status' // git log --name-status: filenames with status
| 'raw'; // git log --raw: low-level format

/**
* Parse and validate a commit range
* Supports: HEAD~10..HEAD, tag1..tag2, branch1..branch2, commit1..commit2
*/
export const parseCommitRange = (range: string): ParsedCommitRange => {
if (!range || typeof range !== 'string') {
throw new RepomixError('Commit range must be a non-empty string');
}

// Handle single commit (treated as commit^..commit)
if (!range.includes('..')) {
return { from: `${range}^`, to: range, raw: range, separator: '..' };
}

// Three-dot (...) must be checked before two-dot (..) to avoid mis-split
const tripleIdx = range.indexOf('...');
if (tripleIdx !== -1) {
const from = range.slice(0, tripleIdx).trim();
const to = range.slice(tripleIdx + 3).trim();
if (!from || !to) {
throw new RepomixError(`Invalid commit range format: '${range}'. Expected format: 'from...to'`);
}
return { from, to, raw: range, separator: '...' };
}

const [from, to] = range.split('..');
if (!from || !to) {
throw new RepomixError(`Invalid commit range format: '${range}'. Expected format: 'from..to'`);
}

return { from: from.trim(), to: to.trim(), raw: range, separator: '..' };
};

// Null byte delimiter (via git's %x00) to separate format output from --name-only file list
// Null bytes cannot appear in commit messages or file paths, making this a reliable delimiter
const NULL_BYTE = '\0';
const GIT_LOG_MAX_BUFFER = 50 * 1024 * 1024; // 50MB — git log --patch on large repos

/**
* Get full metadata for a specific commit
*/
export const getCommitMetadata = async (
directory: string,
hash: string,
deps = {
execFileAsync,
},
): Promise<CommitMetadata> => {
try {
// Get commit metadata with fuller format showing author and committer
// Use %x00 (null byte) as delimiter - it cannot appear in commit messages or file paths
const formatString = [
'%H', // Full hash
'%h', // Abbreviated hash
'%P', // Parent hashes (space-separated)
'%an', // Author name
'%ae', // Author email
'%aI', // Author date (ISO 8601)
'%cn', // Committer name
'%ce', // Committer email
'%cI', // Committer date (ISO 8601)
'%s', // Subject (first line of message)
'%b', // Body (rest of message)
'%x00', // Null byte delimiter before file list (git format specifier)
].join('%n');

const result = await deps.execFileAsync(
'git',
['-C', directory, 'log', '-1', `--pretty=format:${formatString}`, '--name-only', hash],
{ maxBuffer: GIT_LOG_MAX_BUFFER },
);

// Split on null byte to separate format output from file list
const [formatOutput, fileListOutput] = result.stdout.split(NULL_BYTE);

const lines = formatOutput.split('\n');

if (lines.length < 10) {
throw new RepomixError(`Invalid git log output for commit ${hash}`);
}

const [
fullHash,
abbrevHash,
parents,
authorName,
authorEmail,
authorDate,
committerName,
committerEmail,
committerDate,
subject,
] = lines.slice(0, 10);

// Lines after subject (index 10+) are the body
const body = lines.slice(10).join('\n').trim();

// Files come after the delimiter
const files = fileListOutput ? fileListOutput.split('\n').filter(Boolean) : [];

return {
hash: fullHash,
abbreviatedHash: abbrevHash,
parents: parents ? parents.split(' ').filter(Boolean) : [],
author: {
name: authorName,
email: authorEmail,
date: authorDate,
},
committer: {
name: committerName,
email: committerEmail,
date: committerDate,
},
message: subject,
body,
files,
};
} catch (error) {
logger.trace('Failed to get commit metadata:', (error as Error).message);
throw new RepomixError(`Failed to get commit metadata for ${hash}: ${(error as Error).message}`);
}
};

/**
* Get commit graph with topology
*/
export const getCommitGraph = async (
directory: string,
range: string,
deps = {
execFileAsync,
parseCommitRange,
getCommitMetadata,
getTags,
},
): Promise<CommitGraph> => {
try {
const parsedRange = deps.parseCommitRange(range);

// Get ASCII graph
const graphResult = await deps.execFileAsync(
'git',
[
'-C',
directory,
'log',
'--graph',
'--oneline',
'--decorate',
'--all',
`${parsedRange.from}${parsedRange.separator}${parsedRange.to}`,
],
{ maxBuffer: GIT_LOG_MAX_BUFFER },
);

// Get list of commit hashes in range
const hashesResult = await deps.execFileAsync(
'git',
['-C', directory, 'log', '--pretty=format:%H', `${parsedRange.from}${parsedRange.separator}${parsedRange.to}`],
{ maxBuffer: GIT_LOG_MAX_BUFFER },
);

const hashes = hashesResult.stdout.split('\n').filter(Boolean);

// Get metadata for each commit
const commits = await Promise.all(hashes.map((hash) => deps.getCommitMetadata(directory, hash)));

// Identify merge commits (commits with multiple parents)
const mergeCommits = commits.filter((c) => c.parents.length > 1).map((c) => c.hash);

// Get tags
const tags = await deps.getTags(directory);

// Generate Mermaid graph
const mermaidGraph = generateMermaidGraph(commits, tags);

return {
commits,
graph: graphResult.stdout,
mermaidGraph,
mergeCommits,
tags,
};
} catch (error) {
logger.trace('Failed to get commit graph:', (error as Error).message);
throw new RepomixError(`Failed to get commit graph: ${(error as Error).message}`);
}
};

/**
* Get git tags mapping
*/
export const getTags = async (
directory: string,
deps = {
execFileAsync,
},
): Promise<Record<string, string>> => {
try {
const result = await deps.execFileAsync('git', [
'-C',
directory,
'tag',
'-l',
'--format=%(refname:short) %(objectname)',
]);

const tags: Record<string, string> = {};
const lines = result.stdout.split('\n').filter(Boolean);

for (const line of lines) {
const [tag, hash] = line.split(' ');
if (tag && hash) {
tags[tag] = hash;
}
}

return tags;
} catch (error) {
logger.trace('Failed to get tags:', (error as Error).message);
return {};
}
};

/**
* Get patch for a commit with configurable detail level
*/
export const getCommitPatch = async (
directory: string,
hash: string,
detailLevel: PatchDetailLevel = 'stat',
includeSummary = false,
deps = {
execFileAsync,
},
): Promise<string> => {
try {
const args = ['-C', directory, 'show', '--no-color'];

switch (detailLevel) {
case 'patch':
// Full patch with line-by-line diffs (git log --patch)
args.push('--patch');
break;
case 'stat':
// Diffstat histogram (git log --stat)
args.push('--stat');
break;
case 'numstat':
// Numeric additions/deletions per file (git log --numstat)
args.push('--numstat');
break;
case 'shortstat':
// One-line summary of changes (git log --shortstat)
args.push('--shortstat');
break;
case 'dirstat':
// Directory change distribution (git log --dirstat)
args.push('--dirstat');
break;
case 'name-only':
// Filenames only (git log --name-only)
args.push('--name-only');
break;
case 'name-status':
// Filenames with A/M/D/R status (git log --name-status)
args.push('--name-status');
break;
case 'raw':
// Low-level format with SHA hashes and modes (git log --raw)
args.push('--raw');
break;
default:
throw new RepomixError(`Invalid detail level: ${detailLevel}`);
}

// Add --summary flag if requested (shows file operations like creates, renames, mode changes)
if (includeSummary) {
args.push('--summary');
}

args.push(hash);

const result = await deps.execFileAsync('git', args, { maxBuffer: GIT_LOG_MAX_BUFFER });
return result.stdout;
} catch (error) {
logger.trace('Failed to get commit patch:', (error as Error).message);
throw new RepomixError(`Failed to get patch for commit ${hash}: ${(error as Error).message}`);
}
};

/**
* Escape special characters for Mermaid string literals
*/
const escapeMermaidString = (str: string): string => {
return str.replace(/"/g, "'").replace(/\n/g, ' ');
};

/**
* Generate Mermaid diagram from commits
* Note: Mermaid gitGraph has limited syntax - it doesn't support arbitrary
* commit histories with merge visualization without branch context.
* We generate a simplified linear view with merge indicators.
*/
export const generateMermaidGraph = (commits: CommitMetadata[], tags: Record<string, string>): string => {
const lines: string[] = [];
lines.push('gitGraph');

// Reverse to show oldest first
const reversed = [...commits].reverse();

for (const commit of reversed) {
const shortHash = commit.abbreviatedHash;
// Escape quotes and limit message length
const shortMessage = escapeMermaidString(commit.message.substring(0, 40));

// Check if this commit has a tag
const tagForCommit = Object.entries(tags).find(([, hash]) => hash === commit.hash)?.[0];

// Build commit line with optional tag
// Mermaid syntax: commit id: "..." [tag: "..."] [type: ...]
const isMerge = commit.parents.length > 1;
const messageWithMerge = isMerge ? `(merge) ${shortMessage}` : shortMessage;
const commitId = `${shortHash}: ${messageWithMerge}`;

let commitLine = ` commit id: "${commitId}"`;
if (tagForCommit) {
commitLine += ` tag: "${escapeMermaidString(tagForCommit)}"`;
}
if (isMerge) {
commitLine += ' type: HIGHLIGHT';
}

lines.push(commitLine);
}

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 New file gitHistory.ts exceeds 250-line limit from AGENTS.md

The new file src/core/git/gitHistory.ts is 402 lines, far exceeding the 250-line limit specified in AGENTS.md: "If a file exceeds 250 lines, then the assistant shall split it into multiple files based on functionality." The file contains several distinct functional areas (commit range parsing, commit metadata fetching, commit graph generation, tag fetching, patch retrieval, and Mermaid graph generation) that could be split into separate modules.

Prompt for agents
Split src/core/git/gitHistory.ts (402 lines) into smaller files to comply with the 250-line limit in AGENTS.md. Suggested split:

1. src/core/git/gitCommitRange.ts - parseCommitRange function and ParsedCommitRange interface (~30 lines)
2. src/core/git/gitCommitMetadata.ts - getCommitMetadata, CommitMetadata interface (~90 lines)
3. src/core/git/gitCommitGraph.ts - getCommitGraph, CommitGraph interface, generateMermaidGraph, escapeMermaidString (~120 lines)
4. src/core/git/gitTags.ts - getTags function (~30 lines)
5. src/core/git/gitCommitPatch.ts - getCommitPatch, PatchDetailLevel type (~70 lines)

Each file should export its public types and functions, and the current gitHistory.ts can become a barrel file re-exporting everything for backward compatibility.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +62 to 197
// ===== Two-Pass Parser =====
//
// Architecture: Separates structured data from text blobs for robust parsing
// - Pass 1 (execGitLogStructured): NUL-terminated metadata + raw file entries
// - Pass 2 (execGitLogTextBlob): Patch/stat content matched by array index
// - Pass 3 (execGitGraph): Separate call avoids graph prefix interleaving
//
// NUL (\x00) is used as the ONLY delimiter because git REJECTS commits containing
// NUL bytes. This makes parsing 100% robust - NUL cannot appear in commit messages,
// author names, emails, or any other commit content.
//
// With -z --raw, git uses double-NUL (\x00\x00) between commits, providing a safe
// record separator that cannot be forged by commit content.

interface ParsedCommit {
hash: string;
abbrevHash: string;
parents: string[];
author: { name: string; email: string; date: string };
committer: { name: string; email: string; date: string };
message: string;
body: string;
files: string[];
patch?: string;
}

const parseGitLog = (rawLogOutput: string, recordSeparator = GIT_LOG_RECORD_SEPARATOR): GitLogCommit[] => {
if (!rawLogOutput.trim()) {
return [];
}
// Pre-compiled regexes for performance
const HASH_REGEX = /^[0-9a-f]{40}$/i;
const ABBREV_HASH_REGEX = /^[0-9a-f]{4,12}$/i;

/** Fast check if string could be a 40-char hex hash (length + first char check before regex) */
const isHash = (s: string): boolean => s.length === 40 && HASH_REGEX.test(s);

/** Fast check if string looks like abbreviated hash sharing prefix with full hash */
const isAbbrevOf = (abbrev: string, full: string): boolean =>
abbrev.length >= 4 && abbrev.length <= 12 && ABBREV_HASH_REGEX.test(abbrev) && full.startsWith(abbrev);

const commits: GitLogCommit[] = [];
// Split by record separator used in git log output
// This is more robust than splitting by double newlines, as commit messages may contain newlines
const logEntries = rawLogOutput.split(recordSeparator).filter(Boolean);
/**
* Parse output from execGitLogStructured with optional patch content from execGitLogTextBlob
*
* Input format from -z --raw (all fields NUL-separated):
* - Fields 0-10: hash, abbrevHash, parents, authorName, authorEmail, authorDate,
* committerName, committerEmail, committerDate, subject, body
* - Fields 11+: Raw file entries ":mode mode blob blob STATUS" followed by filename
* - Next commit starts when we see another 40-char hex hash
*
* NUL is 100% safe because git rejects commits containing NUL bytes in any content.
* We parse incrementally rather than splitting on double-NUL since empty fields
* also produce double-NUL sequences.
*/
const parseStructuredOutput = (output: string, patchOutput?: string): ParsedCommit[] => {
if (!output) return [];

// Split on single NUL - all fields are NUL-separated
const parts = output.split('\x00');
const patches = patchOutput ? patchOutput.split('\x00\x00').filter(Boolean) : [];
const commits: ParsedCommit[] = [];
let i = 0;

while (i < parts.length) {
// Find start of commit (40-char hex hash) - use length check first for speed
const hashRaw = parts[i];
if (!hashRaw) {
i++;
continue;
}
const hash = hashRaw.trim();
if (!isHash(hash)) {
i++;
continue;
}

for (const entry of logEntries) {
// Split on both \n and \r\n to handle different line ending formats across platforms
const lines = entry.split(/\r?\n/).filter((line) => line.trim() !== '');
if (lines.length === 0) continue;
// Need at least 11 fields for metadata
if (i + 10 >= parts.length) break;

// First line contains date and message separated by |
const firstLine = lines[0];
const separatorIndex = firstLine.indexOf('|');
if (separatorIndex === -1) continue;
// Extract metadata fields - trim once and store
const abbrev = parts[i + 1]?.trim() || '';
const parents = parts[i + 2]?.trim() || '';
const aName = parts[i + 3]?.trim() || '';
const aEmail = parts[i + 4]?.trim() || '';
const aDate = parts[i + 5]?.trim() || '';
const cName = parts[i + 6]?.trim() || '';
const cEmail = parts[i + 7]?.trim() || '';
const cDate = parts[i + 8]?.trim() || '';
const subject = parts[i + 9]?.trim() || '';
const body = parts[i + 10]?.trim() || '';

const date = firstLine.substring(0, separatorIndex);
const message = firstLine.substring(separatorIndex + 1);
i += 11; // Move past metadata fields

// Remaining lines are file paths
const files = lines.slice(1).filter((line) => line.trim() !== '');
// Parse raw file entries until we hit the next commit or end
// Raw entry format: ":mode mode blob blob STATUS" followed by filename
const files: string[] = [];
while (i < parts.length) {
const partRaw = parts[i];
if (!partRaw) {
i++;
continue;
}

// Check for raw entry (starts with ':') - most common case in file section
const firstChar = partRaw[0];
if (firstChar === ':' || (firstChar === '\n' && partRaw[1] === ':')) {
i++;
const filename = parts[i]?.trim();
if (filename) files.push(filename);
i++;
continue;
}

// Check for next commit boundary - only if length matches hash (40 chars)
const part = partRaw.trim();
if (part.length === 40 && isHash(part)) {
const nextPart = parts[i + 1]?.trim();
if (nextPart && isAbbrevOf(nextPart, part)) {
break; // This is definitely a new commit
}
}

i++;
}

commits.push({
date,
message,
hash,
abbrevHash: abbrev,
parents: parents.split(' ').filter(Boolean),
author: { name: aName, email: aEmail, date: aDate },
committer: { name: cName, email: cEmail, date: cDate },
message: subject,
body,
files,
...(patches[commits.length] && { patch: patches[commits.length].trim() }),
});
}

return commits;
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Modified file gitLogHandle.ts exceeds 250-line limit from AGENTS.md

The file src/core/git/gitLogHandle.ts grew from 113 lines to 322 lines in this PR, exceeding the 250-line limit specified in AGENTS.md: "If a file exceeds 250 lines, then the assistant shall split it into multiple files based on functionality." The file contains the two-pass parser (parseStructuredOutput with helper functions), type definitions, and the public API (getGitLog, getGitLogs) which could be split.

Prompt for agents
Split src/core/git/gitLogHandle.ts (322 lines) into smaller files to comply with the 250-line limit in AGENTS.md. Suggested split:

1. src/core/git/gitLogParser.ts - Move the parseStructuredOutput function along with ParsedCommit interface, HASH_REGEX, ABBREV_HASH_REGEX, isHash, isAbbrevOf helper functions (lines 76-197, ~120 lines)
2. src/core/git/gitLogHandle.ts - Keep the type definitions (GitLogResult, GitLogCommit, HistorySummary), public API functions (getGitLog, getGitLogs), and constants (~130 lines)

The parser module would export parseStructuredOutput which gitLogHandle.ts imports.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@ahundt

ahundt commented Mar 12, 2026

Copy link
Copy Markdown
Author

@yamadashy Thanks for the feedback on the option design! I've pushed a commit addressing all the points you raised, plus the bugs found by Devin and CodeRabbit.

PR #968 Issue Resolution Summary

yamadashy (maintainer) feedback, all 3 addressed:

Issue Status Evidence
Rename CLI flags to --git- prefix Fixed 10 flags renamed in types.ts, cliRun.ts, defaultAction.ts, + 17 doc files
--patch crashes with stdout maxBuffer exceeded Fixed GIT_LOG_MAX_BUFFER (50MB) added to all 7 execFileAsync calls
includeLogsCount ignored with --patch/--graph Fixed Changed if/else if to two independent if blocks in 3 functions

Devin AI review bugs, all 3 addressed:

Issue Status Evidence
Three-dot range parsing (main...feature broken) Fixed parseCommitRange checks ... before .., preserves separator field
Missing name-only/name-status in patchTypes Fixed Array now has all 8 entries at gitLogHandle.ts:242
--commit-range silently ignored without other flags Fixed hasExplicitRange check at gitLogHandle.ts:239

CodeRabbit review, all addressed:

Issue Status Evidence
GitLogTextBlobOptions.patchDetail enum incomplete Fixed Now includes all 8 values at gitCommand.ts:217
commitPatchDetail default wrong in llms-install.md Already fixed Shows 'name-only' matching schema
Missing patchTypes entries Fixed Same as Devin BUG_0002

Capability Regression Check, no regressions:

  • All exported functions/interfaces in gitHistory.ts, gitCommand.ts, gitLogHandle.ts preserved
  • All 8 config schema git fields preserved (configSchema.ts:50-59, 118-127)
  • All output templates (markdownStyle, xmlStyle, outputGenerate) unchanged
  • All 10 CLI flags preserved (renamed with --git- prefix)
  • --commit-range preserved as-is (already namespaced)
  • 1134/1136 tests pass. The 2 failing tests (tests/core/file/packageJsonParse.test.ts: getVersion and getVersion should handle missing version) are a pre-existing upstream issue that also fails on main at commit 72d53a4a with vi.mocked(url.fileURLToPath).mockReturnValue is not a function. This branch does not modify packageJsonParse.test.ts or its source file.

Happy to make any further adjustments. Thanks again for the great tool!

…h message

logger.info('Fetched N commits') at gitLogHandle.ts:265 was the only
logger.info call in src/core/git/ (29 other calls all use logger.trace).
This polluted normal CLI output since INFO is the default log level.
Changed to logger.trace so it only appears in --verbose mode, matching
the codebase convention. Also removed emoji from the log message.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
devin-ai-integration[bot]

This comment was marked as resolved.

…flag injection

git log --output=/tmp/evil HEAD~5..HEAD writes to an arbitrary file because
execFile passes the value directly as a git arg. execFile prevents shell
injection but git itself interprets leading '--' as flags, not revision ranges.
Verified: git log --output=/tmp/test and git log --pretty=INJECTED both work.

Adds validateCommitRange() in gitCommand.ts (mirrors existing validateGitUrl
pattern) and calls it in execGitLogStructured, execGitLogTextBlob, execGitGraph
before building the args array, so execFileAsync is never called with a malicious
range. Also adds the same guard in parseCommitRange() in gitHistory.ts, and adds
a z.string().refine() check in configSchema.ts to reject injections from config
files as well.

What changed:
- src/core/git/gitCommand.ts: add validateCommitRange(), call it in all 3 exec fns
- src/core/git/gitHistory.ts: reject range.startsWith('-') in parseCommitRange()
- src/config/configSchema.ts: add .refine() to both commitRange schema entries

Tests added (11 new, 1147 total, 1145 pass):
- tests/core/git/gitCommand.test.ts: validateCommitRange unit tests + injection
  tests for all 3 exec functions confirming execFileAsync is never reached
- tests/core/git/gitHistory.test.ts: parseCommitRange throws for --output=,
  --pretty=, and -n injection attempts
devin-ai-integration[bot]

This comment was marked as resolved.

…parser

git log -z --raw outputs two NUL-separated filenames for rename (R) and
copy (C) entries: old-name\0new-name. The parser only consumed the first,
dropping the destination filename from the files list.

Fix: detect R/C status from the raw entry header line, then consume the
extra destination filename part when present. Add TDD tests for both
rename and copy entries verifying both filenames appear in the result.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Devin Review found 2 new potential issues.

⚠️ 1 issue in files not directly in the diff

⚠️ Plain text template not updated with new git commit history fields (src/core/output/outputStyles/plainStyle.ts:80-96)

The plain text output template (src/core/output/outputStyles/plainStyle.ts) was not updated to render any of the new git commit history fields (hash, author, committer, parents, body, patch, graph, summary). Both the XML template (xmlStyle.ts) and Markdown template (markdownStyle.ts) were updated with these new fields, but the plain text template still only renders the basic date, message, and files fields. This means users selecting --style plain with --git-graph, --git-patch, etc. will silently lose the enhanced data they requested.

View 11 additional findings in Devin Review.

Open in Devin Review

directory,
'tag',
'-l',
'--format=%(refname:short) %(objectname)',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Annotated git tags never match commits due to %(objectname) returning tag object hash instead of commit hash

The getTags function at src/core/git/gitHistory.ts:275 uses %(objectname) in the --format string. For lightweight tags, %(objectname) correctly returns the commit hash. However, for annotated tags (created with git tag -a), %(objectname) returns the tag object hash, not the underlying commit hash. This means that when generateMermaidGraph at src/core/git/gitHistory.ts:388 tries to match tags to commits via hash === commit.hash, annotated tags will never match because the tag object hash differs from the commit hash. Annotated tags are very common (e.g., GitHub release tags are annotated). The result is that annotated tags won't appear as markers on the Mermaid graph, and the tags section in the output will list incorrect hashes (tag object hashes instead of commit hashes).

The fix is to use %(*objectname) (the dereferenced/peeled hash) for annotated tags, falling back to %(objectname) for lightweight tags.

Suggested change
'--format=%(refname:short) %(objectname)',
'--format=%(refname:short) %(if)%(*objectname)%(then)%(*objectname)%(else)%(objectname)%(end)',
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

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