Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions cmd/roborev/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,12 @@ Examples:
roborev enqueue abc123 def456 # Review range from abc123 to def456 (inclusive)
`,
RunE: func(cmd *cobra.Command, args []string) error {
// In quiet mode, suppress cobra's error output (hook uses &, so exit code doesn't matter)
if quiet {
cmd.SilenceErrors = true
cmd.SilenceUsage = true
}

// Default to current directory
if repoPath == "" {
repoPath = "."
Expand All @@ -397,22 +403,22 @@ Examples:
root, err := git.GetRepoRoot(repoPath)
if err != nil {
if quiet {
return nil // Silent exit in quiet mode
return nil // Not a repo - silent exit for hooks
}
return fmt.Errorf("not a git repository: %w", err)
}

// Skip during rebase to avoid reviewing every replayed commit
if git.IsRebaseInProgress(root) {
return nil // Silent exit
if !quiet {
fmt.Println("Skipping: rebase in progress")
}
return nil // Intentional skip, exit 0
}

// Ensure daemon is running
if err := ensureDaemon(); err != nil {
if quiet {
return nil
}
return err
return err // Return error (quiet mode silences output, not exit code)
}

var gitRef string
Expand Down
108 changes: 108 additions & 0 deletions internal/git/git_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
// Reset for other tests
cmd = exec.Command("git", "config", "--unset", "core.hooksPath")
cmd.Dir = tmpDir
cmd.Run() // ignore error if not set

Check failure on line 73 in internal/git/git_test.go

View workflow job for this annotation

GitHub Actions / lint

Error return value of `cmd.Run` is not checked (errcheck)
})

t.Run("custom core.hooksPath relative", func(t *testing.T) {
Expand Down Expand Up @@ -98,3 +98,111 @@
}
})
}

func TestIsRebaseInProgress(t *testing.T) {
// Create a temp git repo
tmpDir := t.TempDir()

cmd := exec.Command("git", "init")
cmd.Dir = tmpDir
if out, err := cmd.CombinedOutput(); err != nil {
t.Fatalf("git init failed: %v\n%s", err, out)
}

// Configure git user for commits
exec.Command("git", "-C", tmpDir, "config", "user.email", "test@test.com").Run()

Check failure on line 113 in internal/git/git_test.go

View workflow job for this annotation

GitHub Actions / lint

Error return value of `(*os/exec.Cmd).Run` is not checked (errcheck)
exec.Command("git", "-C", tmpDir, "config", "user.name", "Test").Run()

Check failure on line 114 in internal/git/git_test.go

View workflow job for this annotation

GitHub Actions / lint

Error return value of `(*os/exec.Cmd).Run` is not checked (errcheck)

t.Run("no rebase", func(t *testing.T) {
if IsRebaseInProgress(tmpDir) {
t.Error("expected no rebase in progress")
}
})

t.Run("rebase-merge directory", func(t *testing.T) {
// Simulate interactive rebase by creating rebase-merge directory
rebaseMerge := filepath.Join(tmpDir, ".git", "rebase-merge")
if err := os.MkdirAll(rebaseMerge, 0755); err != nil {
t.Fatal(err)
}
defer os.RemoveAll(rebaseMerge)

if !IsRebaseInProgress(tmpDir) {
t.Error("expected rebase in progress with rebase-merge")
}
})

t.Run("rebase-apply directory", func(t *testing.T) {
// Simulate git am / regular rebase by creating rebase-apply directory
rebaseApply := filepath.Join(tmpDir, ".git", "rebase-apply")
if err := os.MkdirAll(rebaseApply, 0755); err != nil {
t.Fatal(err)
}
defer os.RemoveAll(rebaseApply)

if !IsRebaseInProgress(tmpDir) {
t.Error("expected rebase in progress with rebase-apply")
}
})

t.Run("non-repo returns false", func(t *testing.T) {
nonRepo := t.TempDir()
if IsRebaseInProgress(nonRepo) {
t.Error("expected false for non-repo")
}
})

t.Run("worktree with rebase", func(t *testing.T) {
// Create initial commit so we can create a worktree
if err := os.WriteFile(filepath.Join(tmpDir, "file.txt"), []byte("content"), 0644); err != nil {
t.Fatal(err)
}
exec.Command("git", "-C", tmpDir, "add", ".").Run()

Check failure on line 160 in internal/git/git_test.go

View workflow job for this annotation

GitHub Actions / lint

Error return value of `(*os/exec.Cmd).Run` is not checked (errcheck)
exec.Command("git", "-C", tmpDir, "commit", "-m", "initial").Run()

// Create a worktree
worktreeDir := t.TempDir()
cmd := exec.Command("git", "-C", tmpDir, "worktree", "add", worktreeDir, "-b", "test-branch")
if out, err := cmd.CombinedOutput(); err != nil {
t.Fatalf("git worktree add failed: %v\n%s", err, out)
}
defer exec.Command("git", "-C", tmpDir, "worktree", "remove", worktreeDir).Run()

// Verify worktree has .git file (not directory)
gitPath := filepath.Join(worktreeDir, ".git")
info, err := os.Stat(gitPath)
if err != nil {
t.Fatalf("worktree .git not found: %v", err)
}
if info.IsDir() {
t.Skip("worktree has .git directory instead of file - older git version")
}

// No rebase in worktree
if IsRebaseInProgress(worktreeDir) {
t.Error("expected no rebase in worktree")
}

// Get the actual gitdir for the worktree to simulate rebase
gitDirCmd := exec.Command("git", "-C", worktreeDir, "rev-parse", "--git-dir")
gitDirOut, err := gitDirCmd.Output()
if err != nil {
t.Fatalf("git rev-parse --git-dir failed: %v", err)
}
worktreeGitDir := strings.TrimSpace(string(gitDirOut))
if !filepath.IsAbs(worktreeGitDir) {
worktreeGitDir = filepath.Join(worktreeDir, worktreeGitDir)
}

// Simulate rebase in worktree
rebaseMerge := filepath.Join(worktreeGitDir, "rebase-merge")
if err := os.MkdirAll(rebaseMerge, 0755); err != nil {
t.Fatal(err)
}
defer os.RemoveAll(rebaseMerge)

if !IsRebaseInProgress(worktreeDir) {
t.Error("expected rebase in progress in worktree")
}
})
}
Loading