Skip to content

feat(review): add --commit flag to review already-committed changes#40

Merged
Meru143 merged 3 commits intomainfrom
issue/36-post-commit-review
Feb 24, 2026
Merged

feat(review): add --commit flag to review already-committed changes#40
Meru143 merged 3 commits intomainfrom
issue/36-post-commit-review

Conversation

@Meru143
Copy link
Copy Markdown
Owner

@Meru143 Meru143 commented Feb 24, 2026

Summary

Adds flag to review already-committed changes.

Usage

argus review --commit HEAD              # Review last commit
argus review --commit HEAD~3            # Review last 3 commits
argus review --commit HEAD~3..HEAD      # Review range

Closes

#36

Summary by CodeRabbit

  • New Features

    • Added optional --commit flag to the Review command for conducting reviews on specific commits within the repository.
    • Enhanced state management to properly track and record the repository's current state during code reviews.
  • Bug Fixes

    • Improved error handling and diagnostics for repository operations with more meaningful error messages.

Copilot AI review requested due to automatic review settings February 24, 2026 07:33
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Feb 24, 2026

📝 Walkthrough

Walkthrough

A new --commit CLI option is added to the Review command, enabling review of changes between a specified commit and HEAD. When provided, the implementation executes git diff and git rev-parse HEAD to capture the diff and current HEAD state for saving, with enhanced error handling for git subprocess failures.

Changes

Cohort / File(s) Summary
Commit-Based Review Feature
src/main.rs
Added commit: Option<String> field to Review enum variant; implemented git diff and git rev-parse HEAD logic when commit is provided; enhanced error handling for git subprocess invocations; updated state-saving to include current_head with fallback to HEAD resolution.

Sequence Diagram

sequenceDiagram
    participant User
    participant CLI as CLI Handler
    participant Git as Git Process
    participant StateManager as State Manager

    User->>CLI: review --commit <commit_sha>
    CLI->>Git: git diff <commit_sha>
    alt Diff succeeds
        Git-->>CLI: diff output
        CLI->>Git: git rev-parse HEAD
        alt HEAD resolution succeeds
            Git-->>CLI: current_head
            CLI->>StateManager: save state with diff & current_head
            StateManager-->>CLI: state saved
            CLI-->>User: review result
        else HEAD resolution fails
            Git-->>CLI: error
            CLI-->>User: diagnostic error
        end
    else Diff fails
        Git-->>CLI: error
        CLI-->>User: diagnostic error with git output
    end
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related issues

  • feat: Support post-commit review #36: This PR implements the post-commit review feature by adding the commit field and git-based diff handling to support reviewing changes from a specific commit.

Possibly related PRs

Poem

🐰 A new --commit flag hops into sight,
Git diff and HEAD dance in the moonlight,
State saves with care, each change takes flight,
Review commits now—what a delight! 🌙✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(review): add --commit flag to review already-committed changes' accurately and clearly describes the main change: adding a new --commit CLI flag to the review command to support reviewing committed changes.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch issue/36-post-commit-review

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.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds a --commit flag to the argus review command to enable reviewing already-committed changes, addressing issue #36. The feature allows users to specify commit references like HEAD, HEAD~3, or HEAD~3..HEAD to review historical changes. However, the implementation contains a critical bug in how it constructs the git diff command, and the deletion of the GitHub Actions workflow file is unexplained.

Changes:

  • Added --commit flag to the Review command enum with documentation
  • Implemented git diff execution logic for committed changes
  • Deleted .github/workflows/argus-review.yml workflow file

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
src/main.rs Added --commit flag parameter and implementation logic to execute git diff for already-committed changes
.github/workflows/argus-review.yml Completely deleted the Argus code review workflow (reason unexplained)
Comments suppressed due to low confidence (1)

.github/workflows/argus-review.yml:1

  • The workflow file .github/workflows/argus-review.yml is being completely deleted without explanation in the PR description. If this is intentional (perhaps because it's self-referential or could create circular workflows), it should be documented in the PR description. If this deletion is unintentional, it should be restored as it provides automated code review functionality for pull requests.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +1117 to +1122
// Review already-committed changes
let diff_output = std::process::Command::new("git")
.args(["-C", &repo_root.to_string_lossy(), "diff", &commit_ref])
.output()
.into_diagnostic()
.wrap_err(format!("Failed to run git diff {}", commit_ref))?;
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

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

The git diff command is missing the commit parent specification. With the current implementation, git diff HEAD will show differences between HEAD and the working directory, not the changes introduced by HEAD itself. Similarly, git diff HEAD~3 shows differences between HEAD3 and working directory. To review the changes introduced by commits, you should use git diff <commit>^..<commit> or git show <commit> for single commits, or handle ranges appropriately. For the documented usage patterns (HEAD, HEAD3, HEAD~3..HEAD), you'll need different handling: single commits should use <commit>^..<commit> format, and ranges should be passed directly.

Suggested change
// Review already-committed changes
let diff_output = std::process::Command::new("git")
.args(["-C", &repo_root.to_string_lossy(), "diff", &commit_ref])
.output()
.into_diagnostic()
.wrap_err(format!("Failed to run git diff {}", commit_ref))?;
// Review already-committed changes. For a single commit (e.g., HEAD, HEAD~3),
// diff against its parent using `<commit>^..<commit>`. For explicit ranges
// (e.g., HEAD~3..HEAD), pass the range as-is.
let diff_spec = if commit_ref.contains("..") {
commit_ref.clone()
} else {
format!("{}^..{}", commit_ref, commit_ref)
};
let diff_output = std::process::Command::new("git")
.args(["-C", &repo_root.to_string_lossy(), "diff", &diff_spec])
.output()
.into_diagnostic()
.wrap_err(format!("Failed to run git diff {}", diff_spec))?;

Copilot uses AI. Check for mistakes.
Comment on lines +207 to +209
/// Review already-committed changes (e.g., HEAD, HEAD~3, or HEAD~3..HEAD)
#[arg(long)]
commit: Option<String>,
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

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

There's no validation to prevent conflicting flags from being used together. Users could potentially specify multiple input sources (--pr, --file, --commit, --incremental) simultaneously, leading to confusing behavior. Consider adding clap's conflicts_with attribute or explicit validation to ensure only one input source is specified at a time.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/main.rs (1)

1194-1198: ⚠️ Potential issue | 🟡 Minor

Error hint omits --commit after it was added as a valid input source

🔧 Proposed fix
-                    help = "Pipe a diff to argus, e.g.: git diff | argus review --repo .\n       Or use --file <path>, --pr owner/repo#123, or --incremental",
+                    help = "Pipe a diff to argus, e.g.: git diff | argus review --repo .\n       Or use --file <path>, --pr owner/repo#123, --commit HEAD, or --incremental",
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main.rs` around lines 1194 - 1198, The error message produced when both
diff_input.trim().is_empty() and pr.is_none() triggers (the miette::bail! call
in main.rs) is missing the newly supported --commit input; update the help text
inside the miette::miette! invocation to list --commit alongside --file, --pr,
and --incremental so the guidance includes the new valid input source.
🧹 Nitpick comments (1)
src/main.rs (1)

207-209: --commit silently ignored when combined with --pr or --file

The new --commit flag takes third place in the if-else chain (after --pr and --file). If a user accidentally supplies both, --commit is silently dropped with no warning. Consider adding conflicts_with to the clap definition to give an explicit error at arg-parse time:

🔧 Proposed clap annotation
-        /// Review already-committed changes (e.g., HEAD, HEAD~3, or HEAD~3..HEAD)
-        #[arg(long)]
-        commit: Option<String>,
+        /// Review already-committed changes (e.g., HEAD, HEAD~3, or HEAD~3..HEAD)
+        #[arg(long, conflicts_with = "pr", conflicts_with_all = &["pr", "file", "incremental"])]
+        commit: Option<String>,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main.rs` around lines 207 - 209, The --commit flag is silently ignored
when combined with --pr or --file; update the CLI arg definition for the commit
field (the #[arg(...)] on commit: Option<String>) to declare mutual conflicts
with the pr and file flags (e.g., use conflicts_with_all for ["pr","file"] or
equivalent conflicts_with entries) so clap will error at parse-time instead of
silently dropping --commit; ensure the attribute references the exact flag names
"--pr" and "--file".
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main.rs`:
- Around line 1116-1127: The current branch in the else-if handling (the block
beginning with "else if let Some(commit_ref) = commit") incorrectly uses `git
diff <commit>` for single-commit refs which compares the working tree and yields
empty output on a clean repo; change the logic to detect whether `commit_ref`
contains a range ("..") and if so keep using `git diff <range>`, but if it's a
single commit (no "..") run `git show --patch --format="" <commit_ref>` to
produce the exact unified patch for that commit; preserve the existing error
handling around `diff_output`/`stderr` and the wrap_err message when invoking
the command so failures are reported the same way.

---

Outside diff comments:
In `@src/main.rs`:
- Around line 1194-1198: The error message produced when both
diff_input.trim().is_empty() and pr.is_none() triggers (the miette::bail! call
in main.rs) is missing the newly supported --commit input; update the help text
inside the miette::miette! invocation to list --commit alongside --file, --pr,
and --incremental so the guidance includes the new valid input source.

---

Nitpick comments:
In `@src/main.rs`:
- Around line 207-209: The --commit flag is silently ignored when combined with
--pr or --file; update the CLI arg definition for the commit field (the
#[arg(...)] on commit: Option<String>) to declare mutual conflicts with the pr
and file flags (e.g., use conflicts_with_all for ["pr","file"] or equivalent
conflicts_with entries) so clap will error at parse-time instead of silently
dropping --commit; ensure the attribute references the exact flag names "--pr"
and "--file".

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 086e970 and b26d626.

📒 Files selected for processing (2)
  • .github/workflows/argus-review.yml
  • src/main.rs
💤 Files with no reviewable changes (1)
  • .github/workflows/argus-review.yml

Comment on lines +1116 to +1127
} else if let Some(commit_ref) = commit {
// Review already-committed changes
let diff_output = std::process::Command::new("git")
.args(["-C", &repo_root.to_string_lossy(), "diff", &commit_ref])
.output()
.into_diagnostic()
.wrap_err(format!("Failed to run git diff {}", commit_ref))?;

if !diff_output.status.success() {
let stderr = String::from_utf8_lossy(&diff_output.stderr);
miette::bail!("git diff failed: {}", stderr.trim());
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

git diff <single-ref> compares the working tree, not commit-to-parent — --commit HEAD always produces empty output on a clean repo

git diff <commit> compares "working directory to specific commit", and git diff HEAD compares "the Working Tree to the HEAD commit (all local changes)". On a clean working tree (which is the normal state after committing), git diff HEAD produces empty output, so argus review --commit HEAD immediately hits the "Empty diff input" bail at line 1194 — the primary advertised use-case is completely non-functional.

git diff HEAD~3 has a similar problem: it shows the accumulated working-tree diff from HEAD~3, not just the single commit at HEAD~3 as the doc comment implies. Only the range form (HEAD~3..HEAD) works correctly.

The fix is to dispatch on whether the ref is a range or a single commit:

  • Range (.. present): git diff <range> — already correct.
  • Single commit: git show --patch --format="" <commit> to get only the unified patch of that specific commit.
🐛 Proposed fix
-            } else if let Some(commit_ref) = commit {
-                // Review already-committed changes
-                let diff_output = std::process::Command::new("git")
-                    .args(["-C", &repo_root.to_string_lossy(), "diff", &commit_ref])
-                    .output()
-                    .into_diagnostic()
-                    .wrap_err(format!("Failed to run git diff {}", commit_ref))?;
-
-                if !diff_output.status.success() {
-                    let stderr = String::from_utf8_lossy(&diff_output.stderr);
-                    miette::bail!("git diff failed: {}", stderr.trim());
-                }
+            } else if let Some(commit_ref) = commit {
+                // Review already-committed changes.
+                // For a range (e.g. HEAD~3..HEAD) use `git diff`; for a single commit
+                // use `git show --patch --format=` to get the diff the commit introduced.
+                let is_range = commit_ref.contains("..");
+                let (cmd_args, cmd_name): (Vec<&str>, &str) = if is_range {
+                    (vec!["-C", &repo_root.to_string_lossy(), "diff", commit_ref], "git diff")
+                } else {
+                    (vec!["-C", &repo_root.to_string_lossy(), "show", "--patch", "--format=", commit_ref], "git show")
+                };
+
+                let diff_output = std::process::Command::new("git")
+                    .args(&cmd_args)
+                    .output()
+                    .into_diagnostic()
+                    .wrap_err(format!("Failed to run {} {}", cmd_name, commit_ref))?;
+
+                if !diff_output.status.success() {
+                    let stderr = String::from_utf8_lossy(&diff_output.stderr);
+                    miette::bail!("{} failed: {}", cmd_name, stderr.trim());
+                }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main.rs` around lines 1116 - 1127, The current branch in the else-if
handling (the block beginning with "else if let Some(commit_ref) = commit")
incorrectly uses `git diff <commit>` for single-commit refs which compares the
working tree and yields empty output on a clean repo; change the logic to detect
whether `commit_ref` contains a range ("..") and if so keep using `git diff
<range>`, but if it's a single commit (no "..") run `git show --patch
--format="" <commit_ref>` to produce the exact unified patch for that commit;
preserve the existing error handling around `diff_output`/`stderr` and the
wrap_err message when invoking the command so failures are reported the same
way.

@Meru143 Meru143 force-pushed the issue/36-post-commit-review branch from b26d626 to 270723c Compare February 24, 2026 07:58
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
src/main.rs (1)

1168-1199: ⚠️ Potential issue | 🔴 Critical

git diff <single-ref> breaks --commit HEAD on clean repos.

git diff <commit> compares the working tree to the commit, so --commit HEAD returns empty diff after a clean commit. Branch on range vs single commit and use git show --patch --format= for single refs.

🐛 Proposed fix
-            } else if let Some(commit_ref) = commit {
-                // Review already-committed changes
-                let diff_output = std::process::Command::new("git")
-                    .args(["-C", &repo_root.to_string_lossy(), "diff", commit_ref])
-                    .output()
-                    .into_diagnostic()
-                    .wrap_err(format!("Failed to run git diff {}", commit_ref))?;
+            } else if let Some(commit_ref) = commit {
+                // Review already-committed changes.
+                // Range refs (..): git diff <range>; single refs: git show --patch --format=
+                let is_range = commit_ref.contains("..");
+                let (cmd_args, cmd_name): (Vec<&str>, &str) = if is_range {
+                    (vec!["-C", &repo_root.to_string_lossy(), "diff", commit_ref], "git diff")
+                } else {
+                    (
+                        vec![
+                            "-C",
+                            &repo_root.to_string_lossy(),
+                            "show",
+                            "--patch",
+                            "--format=",
+                            commit_ref,
+                        ],
+                        "git show",
+                    )
+                };
+
+                let diff_output = std::process::Command::new("git")
+                    .args(&cmd_args)
+                    .output()
+                    .into_diagnostic()
+                    .wrap_err(format!("Failed to run {} {}", cmd_name, commit_ref))?;
 
                 if !diff_output.status.success() {
                     let stderr = String::from_utf8_lossy(&diff_output.stderr);
-                    miette::bail!("git diff failed: {}", stderr.trim());
+                    miette::bail!("{} failed: {}", cmd_name, stderr.trim());
                 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main.rs` around lines 1168 - 1199, The current use of git diff with a
single commit ref (variable commit_ref) produces empty output for cases like
--commit HEAD on a clean repo; change the logic where diff_output is created to
branch on whether commit_ref is a range vs a single ref (inspect commit_ref for
range syntax like ".." or other range indicators) and, for single refs, run git
show --patch --format= <commit_ref> instead of git diff <commit_ref>, preserving
the same error handling (wrap_err and status checks) and keeping the
current_head logic intact so other code using diff_output and current_head (the
variables diff_output and current_head) continues to work.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/main.rs`:
- Around line 1168-1199: The current use of git diff with a single commit ref
(variable commit_ref) produces empty output for cases like --commit HEAD on a
clean repo; change the logic where diff_output is created to branch on whether
commit_ref is a range vs a single ref (inspect commit_ref for range syntax like
".." or other range indicators) and, for single refs, run git show --patch
--format= <commit_ref> instead of git diff <commit_ref>, preserving the same
error handling (wrap_err and status checks) and keeping the current_head logic
intact so other code using diff_output and current_head (the variables
diff_output and current_head) continues to work.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b26d626 and 48ba5b0.

📒 Files selected for processing (1)
  • src/main.rs

@Meru143 Meru143 merged commit 6dccd1e into main Feb 24, 2026
2 checks passed
@Meru143 Meru143 deleted the issue/36-post-commit-review branch February 24, 2026 08:05
@Meru143 Meru143 mentioned this pull request Feb 24, 2026
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