Skip to content

feat(cmd):Add a method to initiate OCR based on a single branch and its corresponding patch - #1063

Open
Strke wants to merge 18 commits into
alibaba:mainfrom
Strke:fix-contextual-mismatch
Open

feat(cmd):Add a method to initiate OCR based on a single branch and its corresponding patch#1063
Strke wants to merge 18 commits into
alibaba:mainfrom
Strke:fix-contextual-mismatch

Conversation

@Strke

@Strke Strke commented Aug 25, 2026

Copy link
Copy Markdown

Description

By adding the --repo, --branch, --patch, and --apply-patch methods, the data preparation space required for testing OCR review has been reduced.

Running ocr review with --from and --to requires downloading the complete code of the target repository, which can lead to long download times or significant local storage usage when testing a large codebase. Therefore, a new method for running ocr review has been added, which only requires using the repository's git shallow clone code and the patch in the pr to adapt the ocr review backend running program for execution. Local testing has shown that the storage space required to test 50 repositories has been reduced from 25.31GB to 4.87GB (using the code-review-benchmark dataset)

--repo: directory address of the code repository
--branch: The target branch of the code repository
--patch: Directory address of the .patch file
--apply-patch: Whether to apply the patch to the current branch

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Refactoring (no functional changes)
  • Documentation update
  • CI / Build / Tooling

How Has This Been Tested?

  • make test passes locally
  • Manual testing (describe below)
截屏2026-08-26 01 02 43 截屏2026-08-26 01 04 56

Checklist

  • My code follows the project's coding style (go fmt, go vet)
  • I have performed a self-review of my code
  • I have added tests that prove my fix is effective or my feature works
  • New and existing unit tests pass locally with my changes
  • I have updated the documentation accordingly (if applicable)
  • I have signed the CLA

Related Issues

@Strke

Strke commented Aug 25, 2026

Copy link
Copy Markdown
Author

Based on the fact that it seems inconvenient to resolve conflicts on the main branch, I have submitted a new pull request

@github-actions

github-actions Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

🔍 OpenCodeReview found 7 issue(s) in this PR.

  • ✅ Successfully posted inline: 6 comment(s)
  • ❌ Failed to post inline: 1 comment(s)

documentation · low

📄 internal/agent/agent.go (L905-L908)

⚠️ GitHub could not post this as an inline comment: Lines 905-908 could not be resolved (outside PR diff hunks)

Stale comment: the function no longer derives purely from From/To/Commit — it now also checks DiffDir and ReviewMode. Consider updating the doc comment to reflect the new patch-mode derivation so future maintainers aren't misled.

Comment on lines +136 to +139
head := diff.NewCommitProvider(cc.RepoDir, ref, cc.GitRunner).ResolveInput(ctx).ResolvedHead
if head == "" {
return fmt.Errorf("resolve patch post-image ref %q in --repo", ref)
}

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.

bug · medium
The error message here loses all diagnostic context about why resolution failed. ResolveInput silently returns an empty string when git rev-parse fails (e.g., unborn repo, corrupt ref, permission error), so the user sees only resolve patch post-image ref "HEAD" in --repo with no underlying cause.

The equivalent code in internal/agent/identity.go:resolveInputBeforeDiff calls resolveCommitHead which at least returns a wrappable error. Consider either:

  1. Using resolveCommitHead-style logic that preserves the git stderr output, or
  2. Running git rev-parse directly here to capture the actual failure reason.

This matters because HEAD resolution can fail for non-obvious reasons (unborn repository, detached HEAD edge cases, corrupted refs), and the current message gives the user no actionable information.

// so a provider that changed via config file or environment stays implicit —
// which is the transition this check exists to reject.
func validateResumeIdentity(ctx context.Context, cc *commonContext, opts reviewOptions, rt *llmRuntime, state *session.ResumeState) (*agent.SealedInput, error) {
func validateResumeIdentity(ctx context.Context, cc *commonContext, opts reviewOptions, rt *llmRuntime, state *session.ResumeState, patchInputs ...*diff.InputResolution) (*agent.SealedInput, error) {

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.

maintainability · low
Using a variadic parameter here to accommodate existing test callers that omit this argument obscures the production contract: exactly one *diff.InputResolution is expected, and passing zero or multiple values silently does the wrong thing (nil or ignored extras). Consider making this a regular pointer parameter and updating the test call sites to pass nil explicitly — this makes the API self-documenting and prevents future callers from accidentally passing multiple values.

Comment thread internal/agent/agent.go
patchRef := a.args.PatchRef
ref = diff.NewCommitProvider(a.args.RepoDir, patchRef, a.args.GitRunner).ResolveInput(ctx).ResolvedHead
if ref == "" {
return fmt.Errorf("resolve patch post-image ref %q", patchRef)

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.

maintainability · low
The error message reads as a phrase fragment ("resolve patch post-image ref ...") rather than describing what went wrong. Unlike the equivalent path in identity.go which wraps an underlying error with %w, here ResolveInput returns an empty string with no error to wrap. Consider adding a verb to make the failure actionable, e.g. "cannot resolve patch post-image ref %q".

Comment thread internal/diff/patch.go
Comment on lines +132 to +135
if err := os.Remove(indexPath); err != nil {
return "", fmt.Errorf("prepare temporary patch index: %w", err)
}
defer os.Remove(indexPath)

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.

bug · medium
If os.Remove(indexPath) fails here, the function returns without the deferred cleanup (which is set up on line 135, after this check), leaking the temporary file. Move the defer os.Remove(indexPath) before this removal attempt, or add an explicit os.Remove(indexPath) in this error branch.

Suggestion:

Suggested change
if err := os.Remove(indexPath); err != nil {
return "", fmt.Errorf("prepare temporary patch index: %w", err)
}
defer os.Remove(indexPath)
defer os.Remove(indexPath)
if err := os.Remove(indexPath); err != nil {
return "", fmt.Errorf("prepare temporary patch index: %w", err)
}

Comment thread internal/diff/patch.go
Comment on lines +145 to +146
cmd.Stdin = bytes.NewReader(input)
return cmd.CombinedOutput()

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.

bug · high
Bug: The fallback path uses cmd.CombinedOutput() which merges stdout and stderr. For write-tree and commit-tree, the returned bytes are parsed as a git object hash via strings.TrimSpace. If git emits any stderr warnings or diagnostics alongside the hash, the trimmed result will contain non-hash text, causing downstream failures (e.g., commit-tree receiving an invalid tree hash, or the returned commit hash being malformed).

The runner path (OutputWithInputEnv) correctly returns stdout only. The fallback should match this behavior by separating stdout and stderr.

Suggestion:

Suggested change
cmd.Stdin = bytes.NewReader(input)
return cmd.CombinedOutput()
cmd.Stdin = bytes.NewReader(input)
var stderr bytes.Buffer
cmd.Stderr = &stderr
out, err := cmd.Output()
if err != nil && stderr.Len() > 0 {
return nil, fmt.Errorf("%w: %s", err, stderr.String())
}
return out, err

Comment thread internal/gitcmd/runner.go
Comment on lines +88 to +91
if err != nil && stderr.Len() > 0 {
return nil, fmt.Errorf("%w: %s", err, stderr.String())
}
return out, err

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.

bug · medium
When the command fails and stderr is non-empty, this returns nil for stdout. However, callers (e.g., MaterializePatchCommit in internal/diff/patch.go) use the returned out in error messages (strings.TrimSpace(string(out))) expecting diagnostic content even on failure. With this code, those error messages will display empty strings whenever stderr has content.

Additionally, Go's cmd.Output() does return stdout bytes alongside an *exec.ExitError when the process exits non-zero. Consider returning out along with the wrapped error so callers retain access to any stdout produced before the failure.

Suggestion:

Suggested change
if err != nil && stderr.Len() > 0 {
return nil, fmt.Errorf("%w: %s", err, stderr.String())
}
return out, err
if err != nil && stderr.Len() > 0 {
return out, fmt.Errorf("%w: %s", err, stderr.String())
}
return out, err

@Strke

Strke commented Aug 25, 2026

Copy link
Copy Markdown
Author

#1052 (review)

These issues have been corrected

@Strke

Strke commented Aug 25, 2026

Copy link
Copy Markdown
Author

#1052 (review)


1. NewFileContent semantic mismatch (want to confirm your intended workflow)

In my understanding, NewFileContent should be the complete file content of a modified file after the changes are completed.

Currently, I've outlined my logic and modified the code, which involves two execution logics:

With the --apply-patch flag: In this case, the code will apply the patch to the latest commit on the branches specified by --repo and --branch, forming a new commit. This new commit is the context that the larger model can see.

Without the --apply-patch flag: In this case, we need to ensure that the latest commit on the branches specified by --repo and --branch is the commit that applied the patch, so that the larger model can see the context corresponding to that patch.


2. Missing upfront validation for --diff path

An advance check for --patch has now been added.


3. Should --diff require --repo?

The --patch option can be used alone:
​​ocr review --patch /path/to/patches
In this case, --repo defaults to the top-level directory of the Git repository where the current working directory is located; --branch defaults to the HEAD of that repository.


4. PatchProvider.ResolveInput returns empty InputResolution
Previously, commits were only parsed when explicitly passed --branch or --apply-patch. Now, whenever --patch is used, the base branch is determined first:

  • If --branch is present: parse the branch
  • If --branch is not present: parse the current HEAD

Then it is fixed as the commit SHA and passed to PatchProvider. This allows ResolveInput() to write the SHA to the manifest's ResolvedHead, preventing it from being empty.


5. Flag naming: --diff → --patch?

Now diff has been replaced with patch.

@lizhengfeng101 lizhengfeng101 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.

Two minor suggestions:

  1. validateResumeIdentity variadic: Nit: the variadic patchInputs ...*diff.InputResolution feels like a workaround to avoid touching callers — just make it an explicit param, it's cleaner and only two call sites need updating.

  2. review_cmd.go patch logic: The patch-specific branches scattered through executeReviewContext are getting hard to follow — would you mind pulling them into a helper like resolvePatchInput(ctx, cc, opts) so the main flow reads top-to-bottom again?

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