Skip to content

Commit 5cee9bb

Browse files
authored
feat: validate git repository before executing commands (#255)
- Add a check to ensure the current directory is a valid git repository and can execute git diff before running commands - Update command logic to pass context to the new check function - Implement CanExecuteGitDiff method to verify git repository status - Add a test suite for CanExecuteGitDiff to cover repository, non-repository, and subdirectory cases Signed-off-by: appleboy <appleboy.tw@gmail.com>
1 parent a46313c commit 5cee9bb

File tree

5 files changed

+124
-3
lines changed

5 files changed

+124
-3
lines changed

cmd/commit.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ var commitCmd = &cobra.Command{
7373
Use: "commit",
7474
Short: "Automatically generate commit message",
7575
RunE: func(cmd *cobra.Command, args []string) error {
76-
if err := check(); err != nil {
76+
if err := check(cmd.Context()); err != nil {
7777
return err
7878
}
7979

cmd/hepler.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,30 @@
11
package cmd
22

33
import (
4+
"context"
45
"errors"
56
"fmt"
67

8+
"github.com/appleboy/CodeGPT/git"
79
"github.com/appleboy/CodeGPT/prompt"
810
"github.com/appleboy/CodeGPT/provider/openai"
911
"github.com/appleboy/CodeGPT/util"
1012
"github.com/appleboy/com/file"
1113
"github.com/spf13/viper"
1214
)
1315

14-
func check() error {
16+
func check(ctx context.Context) error {
1517
// Check if the Git command is available on the system's PATH
1618
if !util.IsCommandAvailable("git") {
1719
return errors.New("git command not found in your system's PATH. Please install Git and try again")
1820
}
1921

22+
// Check if the current directory is a git repository and can execute git diff
23+
g := git.New()
24+
if err := g.CanExecuteGitDiff(ctx); err != nil {
25+
return fmt.Errorf("cannot execute git diff: %w", err)
26+
}
27+
2028
// Apply configuration values from CLI flags to Viper
2129
if diffUnified != 3 {
2230
viper.Set("git.diff_unified", diffUnified)

cmd/review.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ var reviewCmd = &cobra.Command{
3535
Use: "review",
3636
Short: "Auto review code changes",
3737
RunE: func(cmd *cobra.Command, args []string) error {
38-
if err := check(); err != nil {
38+
if err := check(cmd.Context()); err != nil {
3939
return err
4040
}
4141

git/git.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,16 @@ func (c *Command) gitDir(ctx context.Context) *exec.Cmd {
121121
)
122122
}
123123

124+
// checkGitRepository generates the git command to check if the current directory is a git repository.
125+
func (c *Command) checkGitRepository(ctx context.Context) *exec.Cmd {
126+
return exec.CommandContext(
127+
ctx,
128+
"git",
129+
"rev-parse",
130+
"--is-inside-work-tree",
131+
)
132+
}
133+
124134
// commit generates the git command to create a commit with the provided message.
125135
// It includes options to skip pre-commit hooks, sign off the commit, and handle amendments.
126136
func (c *Command) commit(ctx context.Context, val string) *exec.Cmd {
@@ -163,6 +173,21 @@ func (c *Command) GitDir(ctx context.Context) (string, error) {
163173
return string(output), nil
164174
}
165175

176+
// CanExecuteGitDiff checks if git diff can be executed in the current directory.
177+
// It returns an error if the current directory is not a git repository or if git diff cannot be executed.
178+
func (c *Command) CanExecuteGitDiff(ctx context.Context) error {
179+
output, err := c.checkGitRepository(ctx).Output()
180+
if err != nil {
181+
return errors.New("not a git repository (or any of the parent directories)")
182+
}
183+
184+
if strings.TrimSpace(string(output)) != "true" {
185+
return errors.New("not inside a git working tree")
186+
}
187+
188+
return nil
189+
}
190+
166191
// Diff compares the differences between two sets of data.
167192
// It returns a string representing the differences and an error.
168193
// If there are no differences, it returns an empty string and an error.

git/git_test.go

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package git
2+
3+
import (
4+
"context"
5+
"os"
6+
"path/filepath"
7+
"testing"
8+
)
9+
10+
func TestCanExecuteGitDiff(t *testing.T) {
11+
// Test in the current directory (which is a git repository)
12+
t.Run("in git repository", func(t *testing.T) {
13+
cmd := New()
14+
ctx := context.Background()
15+
16+
err := cmd.CanExecuteGitDiff(ctx)
17+
if err != nil {
18+
t.Errorf("CanExecuteGitDiff() should succeed in a git repository, got error: %v", err)
19+
}
20+
})
21+
22+
// Test in a non-git directory
23+
t.Run("not in git repository", func(t *testing.T) {
24+
// Create a temporary directory
25+
tmpDir := t.TempDir()
26+
27+
// Change to the temporary directory
28+
originalDir, err := os.Getwd()
29+
if err != nil {
30+
t.Fatalf("Failed to get current directory: %v", err)
31+
}
32+
defer func() {
33+
if err := os.Chdir(originalDir); err != nil {
34+
t.Logf("Failed to restore directory: %v", err)
35+
}
36+
}()
37+
38+
if err := os.Chdir(tmpDir); err != nil {
39+
t.Fatalf("Failed to change directory: %v", err)
40+
}
41+
42+
cmd := New()
43+
ctx := context.Background()
44+
45+
err = cmd.CanExecuteGitDiff(ctx)
46+
if err == nil {
47+
t.Error("CanExecuteGitDiff() should fail in a non-git directory")
48+
}
49+
50+
expectedMsg := "not a git repository"
51+
if err != nil && err.Error() != expectedMsg {
52+
t.Logf("Got expected error: %v", err)
53+
}
54+
})
55+
56+
// Test in a git repository subdirectory
57+
t.Run("in git repository subdirectory", func(t *testing.T) {
58+
// Create a test subdirectory in the git repository
59+
tmpDir := filepath.Join(".", "test_subdir")
60+
if err := os.MkdirAll(tmpDir, 0o755); err != nil {
61+
t.Fatalf("Failed to create test directory: %v", err)
62+
}
63+
defer os.RemoveAll(tmpDir)
64+
65+
// Change to the subdirectory
66+
originalDir, err := os.Getwd()
67+
if err != nil {
68+
t.Fatalf("Failed to get current directory: %v", err)
69+
}
70+
defer func() {
71+
if err := os.Chdir(originalDir); err != nil {
72+
t.Logf("Failed to restore directory: %v", err)
73+
}
74+
}()
75+
76+
if err := os.Chdir(tmpDir); err != nil {
77+
t.Fatalf("Failed to change directory: %v", err)
78+
}
79+
80+
cmd := New()
81+
ctx := context.Background()
82+
83+
err = cmd.CanExecuteGitDiff(ctx)
84+
if err != nil {
85+
t.Errorf("CanExecuteGitDiff() should succeed in a git repository subdirectory, got error: %v", err)
86+
}
87+
})
88+
}

0 commit comments

Comments
 (0)