Merge pull request #1359 from MuskanPaliwal/fix-submodule-gitdir-worktree-id · Entire
Merge pull request #1359 from MuskanPaliwal/fix-submodule-gitdir-worktree-id
aa03f22→main·
peyton-alt·2d ago·2 files·+68 added/-11 removed
fix(paths): handle submodule gitdirs in worktree IDs
Changes
2
cmd/entire/cli/paths
Mworktree.go+24/-11
Mworktree_test.go+44
35 unmodified lines
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
43
44
64
46
47
48
65
66
67
68
51
52
53
54
55
69
57
70
71
35 unmodified lines
}
gitdir := strings.TrimPrefix(line, "gitdir: ")
if worktreeID, found := parseWorktreeID(gitdir); found {
return worktreeID, nil
}
return "", fmt.Errorf("unexpected gitdir format (no worktrees): %s", gitdir)
}
func parseWorktreeID(gitdir string) (string, bool) {
gitdir = strings.TrimSuffix(strings.ReplaceAll(gitdir, "\\", "/"), "/")
// Submodule gitdirs live under .git/modules/<path>. If that submodule
// repository has its own linked worktree, the gitdir ends with
// .git/modules/<path>/worktrees/<id>. A /worktrees/ segment before the
// final /modules/ belongs to the superproject's worktree, not the submodule.
if modulesIndex := strings.LastIndex(gitdir, "/modules/"); modulesIndex >= 0 {
afterModules := gitdir[modulesIndex+len("/modules/") :]
if _, worktreeID, found := strings.Cut(afterModules, "/worktrees/"); found {
return strings.TrimSuffix(worktreeID, "/"), true
}
return "", true
}
// Extract worktree name from path like /repo/.git/worktrees/<name>
// or /repo/.bare/worktrees/<name> (bare repo + worktree layout).
// The path after the marker is the worktree identifier.
var worktreeID string
var found bool
for _, marker := range []string{ ".git/worktrees/", ".bare/worktrees/" } {
_, worktreeID, found = strings.Cut(gitdir, marker)
if found {
break
}
}
if !found {
return "", fmt.Errorf("unexpected gitdir format (no worktrees): %s", gitdir)
}
// Remove trailing slashes if any
worktreeID = strings.TrimSuffix(worktreeID, "/")
return worktreeID, nil
}
return "", false
}
Mcmd/entire/cli/paths/worktree.go+24/-11
7 unmodified lines
8
9
10
11
12
13
14
15
8 unmodified lines
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
55 unmodified lines
125
126
127
128
129
130
131
132
7 unmodified lines
)
func TestGetWorktreeID(t *testing.T) {
t.Parallel()
tests := []struct {
name string
setupFunc func(dir string) error
8 unmodified lines
},
wantID: "",
},
{
name: "ordinary submodule relative gitdir",
setupFunc: func(dir string) error {
content := "gitdir: ../../.git/modules/deps/go-git\n"
return os.WriteFile(filepath.Join(dir, ".git"), []byte(content), 0o644)
},
wantID: "",
},
{
name: "ordinary submodule absolute gitdir",
setupFunc: func(dir string) error {
content := "gitdir: /repo/.git/modules/deps/go-git\n"
return os.WriteFile(filepath.Join(dir, ".git"), []byte(content), 0o644)
},
wantID: "",
},
{
name: "nested ordinary submodule gitdir",
setupFunc: func(dir string) error {
content := "gitdir: /repo/.git/modules/libs/go-git/modules/vendor/crypto\n"
return os.WriteFile(filepath.Join(dir, ".git"), []byte(content), 0o644)
},
wantID: "",
},
{
name: "linked worktree of submodule",
setupFunc: func(dir string) error {
content := "gitdir: /repo/.git/modules/deps/go-git/worktrees/sub-linked\n"
return os.WriteFile(filepath.Join(dir, ".git"), []byte(content), 0o644)
},
wantID: "sub-linked",
},
{
name: "ordinary submodule inside linked superproject worktree",
setupFunc: func(dir string) error {
content := "gitdir: /repo/.git/worktrees/super-linked/modules/deps/go-git\n"
return os.WriteFile(filepath.Join(dir, ".git"), []byte(content), 0o644)
},
wantID: "",
},
{
name: "linked worktree simple name",
setupFunc: func(dir string) error {
55 unmodified lines
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
dir := t.TempDir()
if err := tt.setupFunc(dir); err != nil {
t.Fatalf("setup failed: %v", err)