Skip to content

feat: parse Go-style diffs#704

Merged
jdx merged 7 commits intomainfrom
jcannon/go-style-diff
Feb 25, 2026
Merged

feat: parse Go-style diffs#704
jdx merged 7 commits intomainfrom
jcannon/go-style-diff

Conversation

@jdx
Copy link
Copy Markdown
Owner

@jdx jdx commented Feb 25, 2026

Summary

  • Go tools (like gofmt -d) output diffs with .orig suffix on --- lines (e.g., --- file.go.orig instead of --- file.go)
  • Adds strip_orig_suffix() to handle this by stripping .orig from --- lines when the corresponding +++ line doesn't have it
  • Correctly preserves .orig when the file itself has a .orig extension

Supersedes #640 (rebased onto current main).

Test plan

  • New bats test: check_diff handles diffs with .orig suffix on --- line
  • New bats test: check_diff works if the file has .orig suffix
  • All existing apply_check_diff.bats tests pass

Co-authored-by: Joshua Cannon josh.cannon@airbnb.com


Note

Low Risk
Scoped change to diff parsing/apply that normalizes --- *.orig headers; main risk is incorrectly rewriting paths in edge-case diffs, mitigated by added tests.

Overview
Adds support for Go-style unified diffs (e.g., gofmt -d) where only the --- header uses a .orig suffix by introducing strip_orig_suffix() and running it before both file extraction (filter_files_from_check_diff) and git apply (apply_diff_output).

Extends apply_check_diff.bats with coverage for .orig-suffixed --- lines and ensures files that actually end with .orig continue to work, plus small test cleanup/renames.

Written by Cursor Bugbot for commit f86f004. This will update automatically on new commits. Configure here.

@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

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

This pull request enhances the diff parsing capabilities to correctly interpret and apply diffs generated by Go tools like gofmt -d. By introducing a dedicated function to handle the .orig suffix often present in the 'before' file line of such diffs, it improves the robustness and compatibility of the system with various formatting tools. This change ensures that automated fixes and checks can proceed smoothly even when encountering these specific diff formats.

Highlights

  • Go-style Diffs Support: Implemented support for parsing Go-style diffs, which often include a .orig suffix on the --- (original file) line.
  • New strip_orig_suffix function: Added a utility function strip_orig_suffix() to correctly remove the .orig suffix from --- lines when the corresponding +++ line does not have it, ensuring proper diff application.
  • Preservation of .orig extension: Ensured that the .orig suffix is correctly preserved when the file itself genuinely has a .orig extension.
  • Test Coverage: Added new bats tests to validate the handling of .orig suffixes in diffs and confirmed all existing apply_check_diff.bats tests still pass.
Changelog
  • src/step/check_parsing.rs
    • Imported strip_orig_suffix function.
    • Applied strip_orig_suffix to the stdout content before parsing diffs to extract file names.
  • src/step/diff.rs
    • Imported strip_orig_suffix function.
    • Updated documentation for the apply_diff method to mention handling Go-style diffs with .orig suffixes.
    • Applied strip_orig_suffix to the stdout content before applying the diff.
  • src/step/mod.rs
    • Added a new public function strip_orig_suffix which processes a diff string to remove .orig from --- lines if the subsequent +++ line does not contain it.
  • test/apply_check_diff.bats
    • Removed redundant git add . and git commit -m "initial" commands from several test cases.
    • Updated the test name "check_diff falls back to fixer when git apply fails" to "check_diff falls back to fixer when apply fails".
    • Added a new test case "check_diff handles diffs with .orig suffix on --- line" to verify correct parsing of Go-style diffs.
    • Added a new test case "check_diff works if the file has .orig suffix" to ensure .orig is preserved when it's part of the actual filename.
Activity
  • No specific activity (comments, reviews, etc.) was found in the provided context.
Using Gemini Code Assist

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

Invoking Gemini

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

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

Customization

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

Limitations & Feedback

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

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

Footnotes

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

Copy link
Copy Markdown
Contributor

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

Choose a reason for hiding this comment

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

Code Review

This pull request introduces support for Go-style diffs by stripping the .orig suffix from --- lines. The implementation is sound, but I've identified a potential issue with the use of replace, which could cause problems with filenames containing .orig not as a suffix. I've suggested a more robust alternative using rsplit_once. The addition of new tests to cover this functionality, including edge cases, is excellent. The refactoring in the test suite to remove unnecessary git commands also improves test simplicity and speed.

Comment thread src/step/mod.rs Outdated
if line.starts_with("--- ") && line.contains(".orig") {
if let Some(next) = lines.peek() {
if next.starts_with("+++ ") && !next.contains(".orig") {
result.push(line.replace(".orig", ""));
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.

high

Using replace is too broad and could lead to incorrect behavior if a file or directory name contains .orig as part of its name, not as a suffix. For example, a path a/my.orig.file.go.orig would become a/my.file.go, which is incorrect. Using rsplit_once to split on the last occurrence of .orig is more robust and correctly handles this case, as well as filenames with timestamps.

if let Some((before, after)) = line.rsplit_once(".orig") {
    result.push(format!("{}{}", before, after));
} else {
    // This branch is technically unreachable due to the `line.contains(".orig")` check,
    // but is kept for robustness.
    result.push(line.to_string());
}

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Feb 25, 2026

Greptile Summary

This PR adds support for parsing Go-style diffs where tools like gofmt -d append .orig to the --- line (e.g., --- file.go.orig instead of --- file.go). The implementation adds a strip_orig_suffix() function that intelligently strips .orig only when the corresponding +++ line doesn't have it, preserving correct behavior for files that actually have .orig extensions.

Key changes:

  • Added strip_orig_suffix() function in src/step/mod.rs to handle Go-style diff format
  • Applied stripping in both apply_diff_output() (for git apply) and filter_files_from_check_diff() (for parsing file names)
  • Added two comprehensive bats tests covering both the Go-style .orig suffix case and files with actual .orig extensions
  • Cleaned up test suite by removing unnecessary git operations from several tests

Issues found:

  • The replace(".orig", "") call will incorrectly strip all occurrences of .orig (including from directory names), which could break paths like path.orig/file.go.orig
  • Minor inconsistency in diff.rs where prefix detection uses original input instead of the stripped version

Confidence Score: 3/5

  • This PR has a logical bug that will cause incorrect behavior in edge cases with paths containing multiple .orig occurrences
  • The core logic bug in strip_orig_suffix using replace instead of strip_suffix could cause incorrect path handling in production. While the common case (single .orig at end of filename) works correctly and is well-tested, edge cases like path.orig/file.go.orig will fail. The bug is in critical path-handling code.
  • Pay close attention to src/step/mod.rs - the replace call needs to be changed to strip_suffix to fix the logic bug

Important Files Changed

Filename Overview
src/step/mod.rs Added strip_orig_suffix function to handle Go-style diffs, but has bug with replace that affects paths with multiple .orig occurrences
src/step/diff.rs Applied strip_orig_suffix to handle Go-style diffs before git apply, minor inconsistency in prefix detection
src/step/check_parsing.rs Applied strip_orig_suffix before parsing diff output to extract file names, implementation is correct
test/apply_check_diff.bats Added comprehensive tests for .orig suffix handling and cleaned up unnecessary git operations from existing tests

Last reviewed commit: 08a2df8

Copy link
Copy Markdown

@greptile-apps greptile-apps Bot left a comment

Choose a reason for hiding this comment

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

4 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

Comment thread src/step/mod.rs Outdated
if line.starts_with("--- ") && line.contains(".orig") {
if let Some(next) = lines.peek() {
if next.starts_with("+++ ") && !next.contains(".orig") {
result.push(line.replace(".orig", ""));
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

replace replaces all occurrences of .orig, which will incorrectly strip .orig from directory names or multiple occurrences. For --- path.orig/file.go.orig, this produces --- path/file.go instead of --- path.orig/file.go

Suggested change
result.push(line.replace(".orig", ""));
result.push(line.strip_suffix(".orig").unwrap_or(line).to_string());

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Feb 25, 2026

Additional Comments (1)

src/step/diff.rs
prefix detection uses original stdout instead of diff_content (after .orig stripping), creating inconsistency

        for line in diff_content.lines() {

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the [Cursor dashboard](https://www.cursor.com/dashboard?tab=bugbot).

Comment thread src/step/diff.rs
Comment thread src/step/mod.rs Outdated
Address review feedback:
- Use strip_suffix instead of replace to only remove trailing .orig,
  avoiding incorrect stripping from directory names or multiple occurrences
- Handle tab-separated timestamps in --- lines
- Fix prefix detection in diff.rs to use stripped diff_content instead
  of original stdout

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Feb 25, 2026

Greptile Summary

Adds support for parsing Go-style unified diffs where tools like gofmt -d append .orig suffix only to the --- line.

  • Introduces strip_orig_suffix() helper that normalizes diffs by removing .orig from --- lines when the corresponding +++ line lacks it
  • Correctly preserves .orig when both lines contain it (i.e., when the file itself has .orig extension)
  • Applies normalization in two critical places: before parsing file paths from check_diff output and before piping diffs to git apply
  • New bats tests validate both the .orig stripping behavior and the preservation of actual .orig files
  • Test cleanup: removes unnecessary git add and git commit commands from existing tests

Confidence Score: 5/5

  • Safe to merge - well-scoped change with comprehensive test coverage
  • Implementation is clean and focused, using strip_suffix() which correctly handles edge cases. The logic only strips .orig when the +++ line doesn't have it, preventing incorrect stripping of files that actually have .orig extensions. Comprehensive test coverage validates both the new functionality and edge cases.
  • No files require special attention

Important Files Changed

Filename Overview
src/step/mod.rs adds strip_orig_suffix() helper to normalize Go-style diffs by removing .orig from --- lines when +++ line doesn't have it
src/step/check_parsing.rs applies .orig normalization before parsing file paths from diff output
src/step/diff.rs applies .orig normalization before feeding diff to git apply
test/apply_check_diff.bats adds tests for .orig suffix handling and removes unnecessary git commits from existing tests

Last reviewed commit: d0b591c

Use strip_prefix and collapse nested if statements per clippy's
manual_strip and collapsible_if lints.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Feb 25, 2026

Greptile Summary

Adds support for Go-style unified diffs where --- lines include a .orig suffix (e.g., gofmt -d) by introducing strip_orig_suffix() function that normalizes these diffs before processing.

Key Changes:

  • New strip_orig_suffix() function in src/step/mod.rs detects and strips .orig from --- lines when the corresponding +++ line lacks it
  • Integration points: called before file extraction (filter_files_from_check_diff) and diff application (apply_diff_output)
  • Correctly handles edge cases: preserves .orig in directory paths, handles tab-separated timestamps, works with files actually named *.orig
  • Test coverage includes both Go-style .orig suffix and actual .orig files
  • Test cleanup: removes unnecessary git commit commands from several tests

Minor Issue:

  • Function unconditionally adds trailing newline to output (line 80); git apply is typically tolerant of this, but exact input preservation would be more correct

Confidence Score: 4/5

  • Safe to merge with very low risk; addresses a legitimate compatibility issue with Go tooling
  • Implementation correctly handles the primary use case and edge cases (directory paths with .orig, actual .orig files, timestamps). Only minor style issue is the unconditional trailing newline addition, which git apply tolerates. Good test coverage validates both the new feature and existing functionality.
  • No files require special attention

Important Files Changed

Filename Overview
src/step/mod.rs Adds strip_orig_suffix() function to normalize Go-style diffs; implementation is mostly correct but unconditionally adds trailing newline
src/step/diff.rs Integrates strip_orig_suffix() before applying diffs; changes are minimal and safe
src/step/check_parsing.rs Integrates strip_orig_suffix() before parsing file names from diff; changes are minimal and safe
test/apply_check_diff.bats Adds tests for .orig suffix handling and removes unnecessary git commits from existing tests; good test coverage

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[check_diff command output] --> B{Contains .orig suffix?}
    B -->|Yes| C[strip_orig_suffix]
    B -->|No| D[Use as-is]
    C --> E[Normalized diff]
    D --> E
    E --> F[filter_files_from_check_diff]
    E --> G[apply_diff_output]
    F --> H[Extract file paths]
    G --> I{Has a/b prefixes?}
    I -->|Yes| J[git apply -p1]
    I -->|No| K[git apply -p0]
    J --> L{Success?}
    K --> L
    L -->|Yes| M[Changes applied]
    L -->|No| N[Fall back to fixer]
    
    C1[--- file.go.orig] --> C2{+++ has .orig?}
    C2 -->|No| C3[Strip .orig from ---]
    C2 -->|Yes| C4[Keep as-is]
    C3 --> C5[--- file.go]
    
    style C fill:#e1f5ff
    style C1 fill:#fff4e1
    style C5 fill:#e8f5e9
Loading

Last reviewed commit: f86f004

Copy link
Copy Markdown

@greptile-apps greptile-apps Bot left a comment

Choose a reason for hiding this comment

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

4 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Comment thread src/step/mod.rs
}
result.push(line.to_string());
}
result.join("\n") + "\n"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

unconditionally adds trailing newline even if input lacks one; git apply is typically tolerant, but consider preserving exact input structure

Suggested change
result.join("\n") + "\n"
if diff.ends_with('\n') {
result.join("\n") + "\n"
} else {
result.join("\n")
}

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@jdx jdx merged commit dc8be17 into main Feb 25, 2026
21 checks passed
@jdx jdx deleted the jcannon/go-style-diff branch February 25, 2026 11:42
@jdx jdx mentioned this pull request Feb 25, 2026
tmeijn pushed a commit to tmeijn/dotfiles that referenced this pull request Mar 11, 2026
This MR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [hk](https://github.com/jdx/hk) | minor | `1.36.0` → `1.38.0` |

MR created with the help of [el-capitano/tools/renovate-bot](https://gitlab.com/el-capitano/tools/renovate-bot).

**Proposed changes to behavior should be submitted there as MRs.**

---

### Release Notes

<details>
<summary>jdx/hk (hk)</summary>

### [`v1.38.0`](https://github.com/jdx/hk/blob/HEAD/CHANGELOG.md#1380---2026-03-06)

[Compare Source](jdx/hk@v1.37.0...v1.38.0)

##### 🚀 Features

- **(hook)** add `fail_on_fix` option by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;725](jdx/hk#725)

##### 🐛 Bug Fixes

- **(builtins)** remove redundant check/check\_diff from builtins by [@&#8203;nkakouros](https://github.com/nkakouros) in [#&#8203;726](jdx/hk#726)

##### 📦️ Dependency Updates

- update anthropics/claude-code-action digest to [`26ec041`](jdx/hk@26ec041) by [@&#8203;renovate\[bot\]](https://github.com/renovate\[bot]) in [#&#8203;720](jdx/hk#720)
- update jdx/mise-action digest to [`e79ddf6`](jdx/hk@e79ddf6) by [@&#8203;renovate\[bot\]](https://github.com/renovate\[bot]) in [#&#8203;721](jdx/hk#721)
- update actions-rust-lang/setup-rust-toolchain digest to [`a0b538f`](jdx/hk@a0b538f) by [@&#8203;renovate\[bot\]](https://github.com/renovate\[bot]) in [#&#8203;719](jdx/hk#719)
- update rust crate tokio to v1.50.0 by [@&#8203;renovate\[bot\]](https://github.com/renovate\[bot]) in [#&#8203;722](jdx/hk#722)

### [`v1.37.0`](https://github.com/jdx/hk/blob/HEAD/CHANGELOG.md#1370---2026-03-03)

[Compare Source](jdx/hk@v1.36.0...v1.37.0)

##### 🚀 Features

- **(hook)** add env support to hooks by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;709](jdx/hk#709)
- parse Go-style diffs by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;704](jdx/hk#704)

##### 🐛 Bug Fixes

- **(builtins)** strip extra trailing newlines in end-of-file-fixer by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;708](jdx/hk#708)
- **(docs)** correctly document what --all is about by [@&#8203;nkakouros](https://github.com/nkakouros) in [#&#8203;715](jdx/hk#715)
- **(git)** exclude untracked files from unstaged\_files set by [@&#8203;nkakouros](https://github.com/nkakouros) in [#&#8203;716](jdx/hk#716)
- **(hkrc)** config format and load order by [@&#8203;ivy](https://github.com/ivy) in [#&#8203;710](jdx/hk#710)
- **(release)** write release notes to file instead of capturing stdout by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;688](jdx/hk#688)
- **(release)** make release notes editorialization non-blocking by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;690](jdx/hk#690)
- **(step)** gate check\_diff forced check\_first on Fix mode only by [@&#8203;nkakouros](https://github.com/nkakouros) in [#&#8203;717](jdx/hk#717)

##### 📚 Documentation

- **(shanty)** add audio player with sea shanty recording by [@&#8203;jdx](https://github.com/jdx) in [67a25ad](jdx/hk@67a25ad)
- document config file search paths by [@&#8203;ivy](https://github.com/ivy) in [#&#8203;701](jdx/hk#701)
- require AI disclosure on GitHub comments by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;703](jdx/hk#703)

##### 🔍 Other Changes

- replace gen-release-notes script with communique by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;700](jdx/hk#700)
- add autofix.ci workflow by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;705](jdx/hk#705)

##### 📦️ Dependency Updates

- lock file maintenance by [@&#8203;renovate\[bot\]](https://github.com/renovate\[bot]) in [#&#8203;686](jdx/hk#686)
- update taiki-e/upload-rust-binary-action digest to [`f391289`](jdx/hk@f391289) by [@&#8203;renovate\[bot\]](https://github.com/renovate\[bot]) in [#&#8203;692](jdx/hk#692)
- update anthropics/claude-code-action digest to [`c22f7c3`](jdx/hk@c22f7c3) by [@&#8203;renovate\[bot\]](https://github.com/renovate\[bot]) in [#&#8203;691](jdx/hk#691)
- update rust crate libc to v0.2.181 by [@&#8203;renovate\[bot\]](https://github.com/renovate\[bot]) in [#&#8203;694](jdx/hk#694)
- update rust crate clap to v4.5.58 by [@&#8203;renovate\[bot\]](https://github.com/renovate\[bot]) in [#&#8203;693](jdx/hk#693)
- lock file maintenance by [@&#8203;renovate\[bot\]](https://github.com/renovate\[bot]) in [#&#8203;695](jdx/hk#695)
- update anthropics/claude-code-action digest to [`edd85d6`](jdx/hk@edd85d6) by [@&#8203;renovate\[bot\]](https://github.com/renovate\[bot]) in [#&#8203;698](jdx/hk#698)
- update rust crate clap to v4.5.60 by [@&#8203;renovate\[bot\]](https://github.com/renovate\[bot]) in [#&#8203;699](jdx/hk#699)
- lock file maintenance by [@&#8203;renovate\[bot\]](https://github.com/renovate\[bot]) in [#&#8203;702](jdx/hk#702)
- lock file maintenance by [@&#8203;renovate\[bot\]](https://github.com/renovate\[bot]) in [#&#8203;711](jdx/hk#711)

##### New Contributors

- [@&#8203;ivy](https://github.com/ivy) made their first contribution in [#&#8203;710](jdx/hk#710)
- [@&#8203;nkakouros](https://github.com/nkakouros) made their first contribution in [#&#8203;715](jdx/hk#715)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever MR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this MR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this MR, check this box

---

This MR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My40OS4wIiwidXBkYXRlZEluVmVyIjoiNDMuNTcuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiUmVub3ZhdGUgQm90IiwiYXV0b21hdGlvbjpib3QtYXV0aG9yZWQiLCJkZXBlbmRlbmN5LXR5cGU6Om1pbm9yIl19-->
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