mirror of
https://github.com/stablyai/orca.git
synced 2026-09-25 00:02:35 +00:00
fix: validate gitdir marker targets are directories
When a .git marker points to a missing or non-directory path, that's unverifiable—not the same as an absent .git file (bare repo). Validate accessibility before reading commondir to catch these errors clearly.
This commit is contained in:
@@ -61,12 +61,31 @@ afterEach(() => {
|
||||
describe('describeCreatedWorktree when Git and the repo disagree', () => {
|
||||
it('reports nothing when the witness proves a different object store', async () => {
|
||||
// A real `.git` file pointing somewhere else: the worktree genuinely is not this repo's.
|
||||
writeFileSync(join(repoPath, '.git'), `gitdir: ${join(scratchDir, 'other-repo', '.git')}\n`)
|
||||
const otherGitDir = join(scratchDir, 'other-repo', '.git')
|
||||
mkdirSync(otherGitDir, { recursive: true })
|
||||
writeFileSync(join(repoPath, '.git'), `gitdir: ${otherGitDir}\n`)
|
||||
await expect(
|
||||
describeCreatedWorktree(repoPath, worktreePath, 'feature')
|
||||
).resolves.toBeUndefined()
|
||||
})
|
||||
|
||||
it('throws when the .git marker points at a path that does not exist', async () => {
|
||||
// Nothing is there to prove a store either way: a fabricated candidate would decide the create.
|
||||
writeFileSync(join(repoPath, '.git'), `gitdir: ${join(scratchDir, 'gone', '.git')}\n`)
|
||||
await expect(describeCreatedWorktree(repoPath, worktreePath, 'feature')).rejects.toMatchObject({
|
||||
message: expect.stringContaining('gitdir marker target unreadable')
|
||||
})
|
||||
})
|
||||
|
||||
it('throws when the .git marker points at a file', async () => {
|
||||
const notAGitDir = join(scratchDir, 'not-a-git-dir')
|
||||
writeFileSync(notAGitDir, 'not a git dir\n')
|
||||
writeFileSync(join(repoPath, '.git'), `gitdir: ${notAGitDir}\n`)
|
||||
await expect(describeCreatedWorktree(repoPath, worktreePath, 'feature')).rejects.toMatchObject({
|
||||
message: expect.stringContaining('gitdir marker target is not a directory')
|
||||
})
|
||||
})
|
||||
|
||||
it('reports nothing for a bare repo, whose missing .git is a real answer', async () => {
|
||||
// No `.git` at all is definitive absence, not an unreadable witness.
|
||||
await expect(
|
||||
|
||||
@@ -188,11 +188,30 @@ async function resolveRepoCommonDirFromDisk(
|
||||
return undefined
|
||||
}
|
||||
gitDir = resolveGitMetadataPath(repoPath, pointer) ?? dotGit
|
||||
await assertGitDirIsDirectory(gitDir)
|
||||
}
|
||||
|
||||
return readCommonDirMarker(gitDir)
|
||||
}
|
||||
|
||||
/**
|
||||
* A marker target that is missing or is not a directory is unverifiable, not an absent `.git`:
|
||||
* without this, `commondir`'s own ENOENT/ENOTDIR would pass as absence and hand the caller the
|
||||
* pointer target as a common dir it never proved exists.
|
||||
*/
|
||||
async function assertGitDirIsDirectory(gitDir: string): Promise<void> {
|
||||
let gitDirStats
|
||||
try {
|
||||
gitDirStats = await stat(gitDir)
|
||||
} catch (error) {
|
||||
// Rewrapped so the outer absence check cannot read this errno as a bare repo's missing `.git`.
|
||||
throw new Error(`gitdir marker target unreadable: ${gitDir}`, { cause: error })
|
||||
}
|
||||
if (!gitDirStats.isDirectory()) {
|
||||
throw new Error(`gitdir marker target is not a directory: ${gitDir}`)
|
||||
}
|
||||
}
|
||||
|
||||
async function readCommonDirMarker(gitDir: string): Promise<string> {
|
||||
try {
|
||||
const pointer = await readFile(join(gitDir, 'commondir'), 'utf8')
|
||||
|
||||
Reference in New Issue
Block a user