Skip to content

Commit 2062c50

Browse files
committed
Fix Git path normalization
1 parent 2b3e4b6 commit 2062c50

File tree

3 files changed

+115
-4
lines changed

3 files changed

+115
-4
lines changed

internal/git/file_reader.go

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ func NewGitRefFileReader(ref string, dir string) *GitRefFileReader {
2323

2424
// ReadFile reads a file from the git ref
2525
func (r *GitRefFileReader) ReadFile(path string) ([]byte, error) {
26-
// Normalize path - remove leading slash if present
27-
path = strings.TrimPrefix(path, "/")
26+
// Normalize path - make it relative to the repository root
27+
path = r.normalizePathForGit(path)
2828

2929
// Use git show to read the file from the ref
3030
output, err := r.executor.execute("git", "show", fmt.Sprintf("%s:%s", r.ref, path))
@@ -36,10 +36,24 @@ func (r *GitRefFileReader) ReadFile(path string) ([]byte, error) {
3636

3737
// PathExists checks if a file exists in the git ref
3838
func (r *GitRefFileReader) PathExists(path string) bool {
39-
// Normalize path - remove leading slash if present
40-
path = strings.TrimPrefix(path, "/")
39+
// Normalize path - make it relative to the repository root
40+
path = r.normalizePathForGit(path)
4141

4242
// Use git cat-file to check if the file exists
4343
_, err := r.executor.execute("git", "cat-file", "-e", fmt.Sprintf("%s:%s", r.ref, path))
4444
return err == nil
4545
}
46+
47+
// normalizePathForGit converts an absolute filesystem path to a path relative to the repository root
48+
func (r *GitRefFileReader) normalizePathForGit(path string) string {
49+
// We want to remove the path to the dir Root
50+
dir_prefix := r.dir
51+
if !strings.HasSuffix(dir_prefix, "/") {
52+
dir_prefix = dir_prefix + "/"
53+
}
54+
path = strings.TrimPrefix(path, dir_prefix)
55+
if strings.HasPrefix(path, "/") {
56+
return path[1:]
57+
}
58+
return path
59+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package git
2+
3+
import (
4+
"testing"
5+
)
6+
7+
// TestGitRefFileReader_PathNormalization tests that absolute paths are correctly
8+
// normalized to be relative to the repository root
9+
func TestGitRefFileReader_PathNormalization(t *testing.T) {
10+
tt := []struct {
11+
name string
12+
repoDir string
13+
input string
14+
expected string
15+
}{
16+
{
17+
name: "relative path unchanged",
18+
repoDir: "/repo",
19+
input: ".codeowners",
20+
expected: ".codeowners",
21+
},
22+
{
23+
name: "absolute path stripped",
24+
repoDir: "/repo",
25+
input: "/repo/.codeowners",
26+
expected: ".codeowners",
27+
},
28+
{
29+
name: "nested path with repo prefix",
30+
repoDir: "/repo",
31+
input: "/repo/internal/app/.codeowners",
32+
expected: "internal/app/.codeowners",
33+
},
34+
{
35+
name: "current directory repo with relative path",
36+
repoDir: ".",
37+
input: "./.codeowners",
38+
expected: ".codeowners",
39+
},
40+
{
41+
name: "github workspace path",
42+
repoDir: "/github/workspace",
43+
input: "/github/workspace/.codeowners",
44+
expected: ".codeowners",
45+
},
46+
{
47+
name: "github workspace nested path",
48+
repoDir: "/github/workspace",
49+
input: "/github/workspace/internal/app/.codeowners",
50+
expected: "internal/app/.codeowners",
51+
},
52+
{
53+
name: "path not under repo dir",
54+
repoDir: "/repo",
55+
input: "/other/.codeowners",
56+
expected: "other/.codeowners",
57+
},
58+
{
59+
name: "repo dir without trailing slash",
60+
repoDir: "/repo/",
61+
input: "/repo/subdir/.codeowners",
62+
expected: "subdir/.codeowners",
63+
},
64+
}
65+
66+
for _, tc := range tt {
67+
t.Run(tc.name, func(t *testing.T) {
68+
reader := &GitRefFileReader{
69+
ref: "test-ref",
70+
dir: tc.repoDir,
71+
}
72+
73+
result := reader.normalizePathForGit(tc.input)
74+
if result != tc.expected {
75+
t.Errorf("normalizePathForGit(%q) with dir=%q = %q, want %q",
76+
tc.input, tc.repoDir, result, tc.expected)
77+
}
78+
})
79+
}
80+
}

internal/git/file_reader_test.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,18 @@ func TestGitRefFileReader_ReadFile(t *testing.T) {
6868
expected: "* @owner1\n",
6969
expectError: false,
7070
},
71+
{
72+
name: "read with absolute path including repo dir",
73+
path: "/repo/.codeowners",
74+
expected: "* @owner1\n",
75+
expectError: false,
76+
},
77+
{
78+
name: "read subdirectory with absolute path",
79+
path: "/repo/subdir/.codeowners",
80+
expected: "*.js @owner2\n",
81+
expectError: false,
82+
},
7183
}
7284

7385
for _, tc := range tt {
@@ -129,6 +141,11 @@ func TestGitRefFileReader_PathExists(t *testing.T) {
129141
path: "/.codeowners",
130142
expected: true,
131143
},
144+
{
145+
name: "existing file with absolute path",
146+
path: "/repo/.codeowners",
147+
expected: true,
148+
},
132149
}
133150

134151
for _, tc := range tt {

0 commit comments

Comments
 (0)