diff --git a/platform/git.go b/platform/git.go index a5f3d232..5cae56bb 100644 --- a/platform/git.go +++ b/platform/git.go @@ -82,6 +82,15 @@ func GitRevisions(cwd string) []string { return reverse(GitLog(cwd, "%H", 0)) } +// GitRoot returns absolute path of repo root +func GitRoot(cwd string, logdir string) (string, error) { + stdout, _, err := gitRun(cwd, []string{"rev-parse", "--show-toplevel"}, logdir) + if err != nil { + return "", err + } + return strings.TrimSpace(stdout), nil +} + // GitRemoteUrl returns the remote url of the git repository. func GitRemoteUrl(cwd string, logdir string) (string, error) { stdout, _, err := gitRun(cwd, []string{"remote", "get-url", "origin"}, logdir) diff --git a/platform/git_changes.go b/platform/git_changes.go index c7e9a886..cf56151a 100644 --- a/platform/git_changes.go +++ b/platform/git_changes.go @@ -70,15 +70,24 @@ func GitDiffNameOnly(cwd string, diffStart string, diffEnd string, logdir string } func GitChangedFiles(cwd string, diffStart string, diffEnd string, logdir string) (ChangedFiles, error) { + cwd, err := filepath.EvalSymlinks(cwd) + if err != nil { + return ChangedFiles{}, err + } + repoRoot, err := GitRoot(cwd, logdir) + if err != nil { + return ChangedFiles{}, err + } + stdout, _, err := gitRun(cwd, []string{"diff", diffStart, diffEnd, "--unified=0", "--no-renames"}, logdir) if err != nil { return ChangedFiles{}, err } - return parseDiff(stdout) + return parseDiff(stdout, repoRoot, cwd) } // parseDiff parses the git diff output and extracts changes -func parseDiff(diff string) (ChangedFiles, error) { +func parseDiff(diff string, repoRoot string, cwd string) (ChangedFiles, error) { var changes []HunkChange scanner := bufio.NewScanner(strings.NewReader(diff)) @@ -135,11 +144,14 @@ func parseDiff(diff string) (ChangedFiles, error) { fileName = file.ToPath } } - files = append(files, &ChangedFile{ - Path: fileName, - Added: file.Added, - Deleted: file.Deleted, - }) + path := filepath.Join(repoRoot, fileName) + if strings.HasPrefix(path, cwd) { // take changes only inside project + files = append(files, &ChangedFile{ + Path: path, + Added: file.Added, + Deleted: file.Deleted, + }) + } } sort.Slice(files, func(i, j int) bool { diff --git a/platform/git_changes_test.go b/platform/git_changes_test.go index 8d85b58b..7131708c 100644 --- a/platform/git_changes_test.go +++ b/platform/git_changes_test.go @@ -30,8 +30,9 @@ import ( type TestConfig struct { initialContent string modifiedContent string - action string // Either "create" or "delete" + action string result string + projDir string } func TestChangesCalculation(t *testing.T) { @@ -174,6 +175,57 @@ func TestChangesCalculation(t *testing.T) { "deleted": [] } ] +}`, + }, + { + initialContent: "Hello, New File!\nThis file is newly created.\n", + modifiedContent: "", + action: "subfolder_move", + result: ` +{ + "files": [ + { + "path": "file.txt", + "added": [], + "deleted": [ + { + "firstLine": 1, + "count": 2 + } + ] + }, + { + "path": "subfolder/file2.txt", + "added": [ + { + "firstLine": 1, + "count": 2 + } + ], + "deleted": [] + } + ] +}`, + }, + { + initialContent: "Hello, New File!\nThis file is newly created.\n", + modifiedContent: "", + action: "subfolder_move", + projDir: "subfolder", + result: ` +{ + "files": [ + { + "path": "subfolder/file2.txt", + "added": [ + { + "firstLine": 1, + "count": 2 + } + ], + "deleted": [] + } + ] }`, }, } @@ -182,13 +234,19 @@ func TestChangesCalculation(t *testing.T) { t.Run(tc.action, func(t *testing.T) { temp, _ := os.MkdirTemp("", "") repo := createRepo(t, tc) + defer func(path string) { _ = os.RemoveAll(path) }(repo) - commits, err := GitChangedFiles(repo, "HEAD~1", "HEAD", temp) + repo, err := filepath.EvalSymlinks(repo) + assert.NoError(t, err) + projDir := filepath.Join(repo, tc.projDir) + commits, err := GitChangedFiles(projDir, "HEAD~1", "HEAD", temp) + for _, file := range commits.Files { - file.Path = filepath.Base(file.Path) + relPath, _ := filepath.Rel(repo, file.Path) + file.Path = filepath.Clean(relPath) } assert.NoError(t, err) jsonCommits, err := json.MarshalIndent(commits, "", " ") @@ -214,6 +272,12 @@ func createRepo(t *testing.T, tc TestConfig) string { absolutePath := filepath.Join(repoDir, fileName) absolutePath2 := filepath.Join(repoDir, fileName2) + if tc.action == "subfolder_move" { + err = os.MkdirAll(filepath.Join(repoDir, "subfolder"), 0755) + assert.NoError(t, err) + absolutePath2 = filepath.Join(repoDir, "subfolder", fileName2) + } + // Step 3: Create the first file and commit it if initial content is not empty initialFileName := fileName if tc.initialContent == "" { @@ -242,6 +306,9 @@ func createRepo(t *testing.T, tc TestConfig) string { case "move": cmd = exec.Command("git", "mv", absolutePath, absolutePath2) runGit(t, cmd, repoDir) + case "subfolder_move": + cmd = exec.Command("git", "mv", absolutePath, absolutePath2) + runGit(t, cmd, repoDir) case "delete": err = os.Remove(absolutePath) assert.NoError(t, err)