Skip to content

Commit 0d089b1

Browse files
committed
fix: address review feedback on nested git repo handling
Improvements based on reviewer bot feedback: 1. Enhanced safety check (HIGH priority): - Now uses 'git ls-files -s --cached' to detect mode 160000 entries - Catches newly added/removed gitlinks, not just pointer changes - Added check for .gitmodules changes with warning log 2. Improved pathspec exclusion (MEDIUM priority): - Added 'git rm --cached' to remove existing gitlinks before staging - Prevents staging of gitlink entries already in the index - Ensures complete exclusion of nested repo changes 3. Better error logging (LOW priority): - Added warning logs for git command failures in findNestedRepos() - Helps debugging while avoiding feature breakage - Distinguishes expected errors (no .gitmodules) from real issues All tests continue to pass with these enhancements.
1 parent b02bf48 commit 0d089b1

File tree

1 file changed

+44
-10
lines changed

1 file changed

+44
-10
lines changed

src/services/checkpoints/ShadowCheckpointService.ts

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,17 @@ export abstract class ShadowCheckpointService extends EventEmitter {
146146
)
147147
}
148148

149+
// Remove any existing gitlinks from the index before staging
150+
for (const repoPath of nestedRepos) {
151+
try {
152+
await git.raw(["rm", "--cached", "--ignore-unmatch", "-r", repoPath])
153+
} catch (error) {
154+
this.log(
155+
`[${this.constructor.name}#stageAll] failed to remove cached gitlink ${repoPath}: ${error instanceof Error ? error.message : String(error)}`,
156+
)
157+
}
158+
}
159+
149160
// Build add command with pathspec excludes
150161
const addArgs: string[] = ["-A", ":/"]
151162
for (const repoPath of nestedRepos) {
@@ -155,10 +166,24 @@ export abstract class ShadowCheckpointService extends EventEmitter {
155166
// Stage files
156167
await git.add(addArgs)
157168

158-
// Safety check: ensure no submodule changes staged
159-
const diffSummary = await git.raw(["diff", "--cached", "--summary"])
160-
if (diffSummary.includes("Submodule")) {
161-
throw new Error("Submodule changes detected in staging area - this should not happen")
169+
// Enhanced safety check: verify no mode 160000 entries in staging area
170+
const stagedFiles = await git.raw(["ls-files", "-s", "--cached"])
171+
const gitlinkEntries = stagedFiles
172+
.split("\n")
173+
.filter((line) => line.startsWith("160000"))
174+
.map((line) => line.split(/\s+/)[3])
175+
.filter(Boolean)
176+
177+
if (gitlinkEntries.length > 0) {
178+
throw new Error(
179+
`Gitlink entries detected in staging area: ${gitlinkEntries.join(", ")} - this should not happen`,
180+
)
181+
}
182+
183+
// Additional check for .gitmodules changes
184+
const diffSummary = await git.raw(["diff", "--cached", "--name-only"])
185+
if (diffSummary.includes(".gitmodules")) {
186+
this.log(`[${this.constructor.name}#stageAll] warning: .gitmodules changes detected in staging area`)
162187
}
163188
} catch (error) {
164189
this.log(
@@ -178,8 +203,13 @@ export abstract class ShadowCheckpointService extends EventEmitter {
178203
const match = line.match(/submodule\..*\.path\s+(.+)/)
179204
if (match) nestedRepos.add(match[1])
180205
}
181-
} catch {
182-
// No .gitmodules file or error reading it
206+
} catch (error) {
207+
// No .gitmodules file is expected in most cases, only log if it's a real error
208+
if (error instanceof Error && !error.message.includes("exit code 1")) {
209+
this.log(
210+
`[${this.constructor.name}#findNestedRepos] warning: failed to read .gitmodules: ${error.message}`,
211+
)
212+
}
183213
}
184214

185215
// 2. From index (gitlinks with mode 160000)
@@ -191,8 +221,10 @@ export abstract class ShadowCheckpointService extends EventEmitter {
191221
if (parts[3]) nestedRepos.add(parts[3])
192222
}
193223
}
194-
} catch {
195-
// Ignore errors
224+
} catch (error) {
225+
this.log(
226+
`[${this.constructor.name}#findNestedRepos] warning: failed to list files from index: ${error instanceof Error ? error.message : String(error)}`,
227+
)
196228
}
197229

198230
// 3. From filesystem (any nested .git directory or worktree)
@@ -244,8 +276,10 @@ export abstract class ShadowCheckpointService extends EventEmitter {
244276
const repoDir = path.dirname(result.path)
245277
nestedRepos.add(repoDir)
246278
}
247-
} catch {
248-
// Ignore errors reading .git file
279+
} catch (error) {
280+
this.log(
281+
`[${this.constructor.name}#findNestedRepos] warning: failed to read .git file at ${result.path}: ${error instanceof Error ? error.message : String(error)}`,
282+
)
249283
}
250284
}
251285
}

0 commit comments

Comments
 (0)