mirror of
https://github.com/stablyai/orca.git
synced 2026-09-22 00:02:31 +00:00
fix(relay): treat staging paths as literals (#8358)
* fix(relay): treat staging paths as literals * docs(relay): document pathspec collision fixture * docs(relay): document staging mutation contracts * chore(relay): remove redundant staging comments Co-authored-by: Orca <help@stably.ai> --------- Co-authored-by: Siddharth Ahire <siddharth@Siddharths-MacBook-Air.local> Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com> Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
co-authored by
Orca
Siddharth Ahire
Jinwoo-H
parent
8abe093e86
commit
deb8152c9a
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Tests for GitHandler commit and bulk-staging operations.
|
||||
* Tests for GitHandler commit and staging operations.
|
||||
*
|
||||
* Why: split from git-handler.test.ts to stay under the oxlint max-lines (300) limit.
|
||||
*/
|
||||
@@ -19,6 +19,32 @@ import {
|
||||
type RelayDispatcher
|
||||
} from './git-handler-test-setup'
|
||||
|
||||
const PATHSPEC_SELECTED_FILE = '[k]eep.log'
|
||||
const PATHSPEC_MATCHING_FILE = 'keep.log'
|
||||
const PATHSPEC_MUTATION_CASES = [
|
||||
{
|
||||
mode: 'single-file',
|
||||
stageMethod: 'git.stage',
|
||||
unstageMethod: 'git.unstage',
|
||||
selection: { filePath: PATHSPEC_SELECTED_FILE }
|
||||
},
|
||||
{
|
||||
mode: 'bulk',
|
||||
stageMethod: 'git.bulkStage',
|
||||
unstageMethod: 'git.bulkUnstage',
|
||||
selection: { filePaths: [PATHSPEC_SELECTED_FILE] }
|
||||
}
|
||||
] as const
|
||||
|
||||
function createPathspecCollisionChanges(dir: string): void {
|
||||
gitInit(dir)
|
||||
writeFileSync(path.join(dir, PATHSPEC_SELECTED_FILE), 'selected')
|
||||
writeFileSync(path.join(dir, PATHSPEC_MATCHING_FILE), 'matching')
|
||||
gitCommit(dir, 'initial')
|
||||
writeFileSync(path.join(dir, PATHSPEC_SELECTED_FILE), 'selected modified')
|
||||
writeFileSync(path.join(dir, PATHSPEC_MATCHING_FILE), 'matching modified')
|
||||
}
|
||||
|
||||
describe('GitHandler — commit & staging', () => {
|
||||
let dispatcher: MockDispatcher
|
||||
let tmpDir: string
|
||||
@@ -77,7 +103,38 @@ describe('GitHandler — commit & staging', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('bulkStage and bulkUnstage', () => {
|
||||
describe('stage and unstage', () => {
|
||||
it.each(PATHSPEC_MUTATION_CASES)(
|
||||
'treats $mode stage paths with Git glob characters as literals',
|
||||
async ({ stageMethod, selection }) => {
|
||||
createPathspecCollisionChanges(tmpDir)
|
||||
|
||||
await dispatcher.callRequest(stageMethod, { worktreePath: tmpDir, ...selection })
|
||||
|
||||
const output = execFileSync('git', ['diff', '--cached', '--name-only'], {
|
||||
cwd: tmpDir,
|
||||
encoding: 'utf-8'
|
||||
})
|
||||
expect(output.trim()).toBe(PATHSPEC_SELECTED_FILE)
|
||||
}
|
||||
)
|
||||
|
||||
it.each(PATHSPEC_MUTATION_CASES)(
|
||||
'treats $mode unstage paths with Git glob characters as literals',
|
||||
async ({ unstageMethod, selection }) => {
|
||||
createPathspecCollisionChanges(tmpDir)
|
||||
execFileSync('git', ['add', '.'], { cwd: tmpDir, stdio: 'pipe' })
|
||||
|
||||
await dispatcher.callRequest(unstageMethod, { worktreePath: tmpDir, ...selection })
|
||||
|
||||
const output = execFileSync('git', ['diff', '--cached', '--name-only'], {
|
||||
cwd: tmpDir,
|
||||
encoding: 'utf-8'
|
||||
})
|
||||
expect(output.trim()).toBe(PATHSPEC_MATCHING_FILE)
|
||||
}
|
||||
)
|
||||
|
||||
it('stages multiple files', async () => {
|
||||
gitInit(tmpDir)
|
||||
writeFileSync(path.join(tmpDir, 'a.txt'), 'a')
|
||||
|
||||
@@ -1192,7 +1192,7 @@ describe('GitHandler', () => {
|
||||
await Promise.all([first, second])
|
||||
|
||||
expect(gitBufferSpy).toHaveBeenCalledTimes(2)
|
||||
expect(gitSpy).toHaveBeenCalledWith(['add', '--', 'src/file.ts'], tmpDir)
|
||||
expect(gitSpy).toHaveBeenCalledWith(['add', '--', ':(literal)src/file.ts'], tmpDir)
|
||||
const submodulePathReads = gitSpy.mock.calls.filter(
|
||||
([args]) => args[0] === 'config' && args.includes('.gitmodules')
|
||||
)
|
||||
|
||||
@@ -488,7 +488,7 @@ export class GitHandler {
|
||||
const worktreePath = params.worktreePath as string
|
||||
const filePath = params.filePath as string
|
||||
try {
|
||||
await this.git(['add', '--', filePath], worktreePath)
|
||||
await this.git(['add', '--', this.literalPathspec(filePath)], worktreePath)
|
||||
} finally {
|
||||
this.clearGitMutationReadCaches()
|
||||
}
|
||||
@@ -512,7 +512,7 @@ export class GitHandler {
|
||||
const worktreePath = params.worktreePath as string
|
||||
const filePath = params.filePath as string
|
||||
try {
|
||||
await this.git(['restore', '--staged', '--', filePath], worktreePath)
|
||||
await this.git(['restore', '--staged', '--', this.literalPathspec(filePath)], worktreePath)
|
||||
} finally {
|
||||
this.clearGitMutationReadCaches()
|
||||
}
|
||||
@@ -525,7 +525,10 @@ export class GitHandler {
|
||||
try {
|
||||
for (let i = 0; i < filePaths.length; i += BULK_CHUNK_SIZE) {
|
||||
const chunk = filePaths.slice(i, i + BULK_CHUNK_SIZE)
|
||||
await this.git(['add', '--', ...chunk], worktreePath)
|
||||
await this.git(
|
||||
['add', '--', ...chunk.map((filePath) => this.literalPathspec(filePath))],
|
||||
worktreePath
|
||||
)
|
||||
}
|
||||
} finally {
|
||||
this.clearGitMutationReadCaches()
|
||||
@@ -539,7 +542,10 @@ export class GitHandler {
|
||||
try {
|
||||
for (let i = 0; i < filePaths.length; i += BULK_CHUNK_SIZE) {
|
||||
const chunk = filePaths.slice(i, i + BULK_CHUNK_SIZE)
|
||||
await this.git(['restore', '--staged', '--', ...chunk], worktreePath)
|
||||
await this.git(
|
||||
['restore', '--staged', '--', ...chunk.map((filePath) => this.literalPathspec(filePath))],
|
||||
worktreePath
|
||||
)
|
||||
}
|
||||
} finally {
|
||||
this.clearGitMutationReadCaches()
|
||||
|
||||
Reference in New Issue
Block a user