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

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)