mirror of
https://github.com/stablyai/orca.git
synced 2026-10-20 00:03:45 +00:00
fix(runtime): dedupe folder-workspace child repos by path
The same directory can be registered twice (imported directly and picked up by a folder scan). Both copies fanned out, so the merged status listed every file twice and a commit ran against the repo twice. Also records the bulk-path grouping's real failure semantics: resolution is all-or-nothing, execution is not. Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
@@ -31,6 +31,13 @@ describe('listFolderWorkspaceChildRepos', () => {
|
||||
it('excludes the folder repo itself even when registered as a git repo', () => {
|
||||
expect(listFolderWorkspaceChildRepos([repo('self', FOLDER)], FOLDER)).toEqual([])
|
||||
})
|
||||
|
||||
it('keeps one entry when the same directory is registered twice', () => {
|
||||
// Why: a duplicate registration would list every file twice in the merged
|
||||
// status and run the commit against that repo twice.
|
||||
const dupe = repo('api-again', API.path)
|
||||
expect(listFolderWorkspaceChildRepos([API, dupe], FOLDER).map((e) => e.id)).toEqual(['api'])
|
||||
})
|
||||
})
|
||||
|
||||
describe('matchFolderWorkspaceChildRepo', () => {
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { GitStatusEntry, GitStatusResult, Repo } from '../../shared/types'
|
||||
import { isFolderRepo } from '../../shared/repo-kind'
|
||||
import {
|
||||
isPathInsideOrEqual,
|
||||
normalizeRuntimePathForComparison,
|
||||
relativePathInsideRoot,
|
||||
resolveRuntimePath
|
||||
} from '../../shared/cross-platform-path'
|
||||
@@ -12,12 +13,23 @@ import {
|
||||
* must be routed to whichever child repo owns the requested path.
|
||||
*/
|
||||
export function listFolderWorkspaceChildRepos(repos: readonly Repo[], folderPath: string): Repo[] {
|
||||
return repos
|
||||
const matching = repos
|
||||
.filter(
|
||||
(candidate) => !isFolderRepo(candidate) && isPathInsideOrEqual(folderPath, candidate.path)
|
||||
)
|
||||
.filter((candidate) => relativePathInsideRoot(folderPath, candidate.path) !== '')
|
||||
.sort((left, right) => right.path.length - left.path.length)
|
||||
// Why: the same directory can be registered twice (imported once directly and
|
||||
// once by a folder scan). Without this the merged status lists every file twice
|
||||
// and a commit runs against the repo twice.
|
||||
const byPath = new Map<string, Repo>()
|
||||
for (const repo of matching) {
|
||||
const key = normalizeRuntimePathForComparison(repo.path)
|
||||
if (!byPath.has(key)) {
|
||||
byPath.set(key, repo)
|
||||
}
|
||||
}
|
||||
return [...byPath.values()]
|
||||
}
|
||||
|
||||
export type FolderWorkspaceChildRepoMatch = {
|
||||
|
||||
@@ -7214,7 +7214,10 @@ export class OrcaRuntimeService {
|
||||
}
|
||||
const repos = this.requireStore().getRepos()
|
||||
// Why: one bulk request can span several child repos, so paths are grouped by
|
||||
// owning repo and each group runs as its own git invocation.
|
||||
// owning repo and each group runs as its own git invocation. Resolution is
|
||||
// all-or-nothing — one unowned path rejects the batch before any git runs —
|
||||
// but execution is not: the caller's loop has no rollback, so a repo failing
|
||||
// midway leaves earlier repos already staged.
|
||||
const byRepoId = new Map<string, { repo: Repo; relativePaths: string[] }>()
|
||||
for (const relativePath of relativePaths) {
|
||||
const match = matchFolderWorkspaceChildRepo(repos, folderPath, relativePath)
|
||||
|
||||
Reference in New Issue
Block a user