Skip to content

Commit 9edf715

Browse files
refactor: Complete Phase 5 performance improvements
- Optimize SortWorktrees: precompute os.Stat calls to reduce O(n log n) to O(n) - Add merge status cache in ListWorktreesDetailed to avoid redundant git merge-base calls
1 parent 7e18e64 commit 9edf715

1 file changed

Lines changed: 28 additions & 6 deletions

File tree

internal/git/worktree.go

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -127,18 +127,30 @@ func ListWorktreesDetailed(barePath, currentWorktreePath, defaultBranch string)
127127

128128
currentWorktreePathEval, _ := filepath.EvalSymlinks(currentWorktreePath)
129129

130+
mergeStatusCache := make(map[string]bool)
131+
130132
for i := range worktrees {
131133
wt := &worktrees[i]
132134
wt.IsMain = wt.Branch == defaultBranch
133135
wtPathEval, _ := filepath.EvalSymlinks(wt.Path)
134136
wt.IsCurrent = wtPathEval == currentWorktreePathEval
135137
if wt.Branch != defaultBranch {
136-
featureInDefault, err := IsMerged(barePath, wt.Branch, defaultBranch)
138+
cacheKey1 := wt.Branch + "->" + defaultBranch
139+
featureInDefault, ok := mergeStatusCache[cacheKey1]
140+
if !ok {
141+
featureInDefault, err = IsMerged(barePath, wt.Branch, defaultBranch)
142+
mergeStatusCache[cacheKey1] = featureInDefault
143+
}
137144
if err != nil {
138145
wt.IsMerged = false
139146
continue
140147
}
141-
defaultInFeature, _ := IsMerged(barePath, defaultBranch, wt.Branch)
148+
cacheKey2 := defaultBranch + "->" + wt.Branch
149+
defaultInFeature, ok := mergeStatusCache[cacheKey2]
150+
if !ok {
151+
defaultInFeature, err = IsMerged(barePath, defaultBranch, wt.Branch)
152+
mergeStatusCache[cacheKey2] = defaultInFeature
153+
}
142154
wt.IsMerged = featureInDefault && !defaultInFeature
143155
}
144156
}
@@ -151,18 +163,28 @@ func SortWorktrees(worktrees []Worktree, by string, reverse bool) []Worktree {
151163
sorted := make([]Worktree, len(worktrees))
152164
copy(sorted, worktrees)
153165

166+
var modTimeMap map[string]int64
167+
if by == "created" {
168+
modTimeMap = make(map[string]int64, len(sorted))
169+
for _, wt := range sorted {
170+
if info, err := os.Stat(wt.Path); err == nil {
171+
modTimeMap[wt.Path] = info.ModTime().UnixNano()
172+
}
173+
}
174+
}
175+
154176
sort.Slice(sorted, func(i, j int) bool {
155177
var cmp int
156178
switch by {
157179
case "branch":
158180
cmp = strings.Compare(sorted[i].Branch, sorted[j].Branch)
159181
case "created":
160-
infoI, errI := os.Stat(sorted[i].Path)
161-
infoJ, errJ := os.Stat(sorted[j].Path)
162-
if errI != nil || errJ != nil {
182+
timeI := modTimeMap[sorted[i].Path]
183+
timeJ := modTimeMap[sorted[j].Path]
184+
if timeI == 0 || timeJ == 0 {
163185
cmp = strings.Compare(sorted[i].Path, sorted[j].Path)
164186
} else {
165-
cmp = int(infoI.ModTime().Sub(infoJ.ModTime()).Nanoseconds())
187+
cmp = int(timeI - timeJ)
166188
}
167189
default: // "name"
168190
nameI := filepath.Base(sorted[i].Path)

0 commit comments

Comments
 (0)