FN-5666: exclude base commit from done-task diff ranges
Prevent done-task diff endpoints from counting base-commit-only lineage as task-owned changes. - add base-boundary handling in done-task merge SHA resolution so `baseCommitSha` is excluded by default and only consulted for boundary checks - short-circuit `/diff` and `/file-diffs` responses to empty results when resolved done-task SHA is at or below `baseCommitSha` - export and use an ancestry helper to detect base-boundary cases, while preserving normal and rebase range behavior - extend done-task diff route tests with FN-5666 cases for no-op merge SHA, lineage association at base commit, normal post-base commits, and rebase ranges - add a patch changeset for `@runfusion/fusion` Files changed: .changeset/fn-5666-base-commit-exclusive.md | 5 + .../src/__tests__/routes-diff-done-tasks.test.ts | 217 ++++++++++++++++++++- .../src/routes/register-session-diff-routes.ts | 57 +++++- 3 files changed, 273 insertions(+), 6 deletions(-) Fusion-Task-Id: FN-5666 Fusion-Task-Lineage: 782f9875-856a-4d31-9505-0bcd638c9c29
This commit is contained in:
@@ -378,17 +378,43 @@ async function resolveAuditCommitSha(taskId: string, scopedStore: DoneTaskAggreg
|
||||
return undefined;
|
||||
}
|
||||
|
||||
async function resolveDoneTaskMergeSha(task: DoneTaskAggregationTask, scopedStore: DoneTaskAggregationStore): Promise<string | undefined> {
|
||||
export async function isAtOrBelowTaskBase(sha: string, baseCommitSha: string, rootDir: string): Promise<boolean> {
|
||||
if (sha === baseCommitSha) return true;
|
||||
try {
|
||||
await runGitCommand(["merge-base", "--is-ancestor", sha, baseCommitSha], rootDir, 5000);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function resolveDoneTaskMergeSha(
|
||||
task: DoneTaskAggregationTask & { baseCommitSha?: string | null },
|
||||
scopedStore: DoneTaskAggregationStore,
|
||||
options?: { includeBaseCommitSha?: boolean },
|
||||
): Promise<string | undefined> {
|
||||
const rootDir = scopedStore.getRootDir();
|
||||
const baseCommitSha = task.baseCommitSha?.trim();
|
||||
const includeBaseCommitSha = options?.includeBaseCommitSha === true;
|
||||
|
||||
const existing = task.mergeDetails?.commitSha?.trim();
|
||||
if (existing) return existing;
|
||||
if (existing) {
|
||||
if (!includeBaseCommitSha && baseCommitSha && existing === baseCommitSha) return undefined;
|
||||
return existing;
|
||||
}
|
||||
|
||||
const auditSha = await resolveAuditCommitSha(task.id, scopedStore);
|
||||
if (auditSha) return auditSha;
|
||||
if (auditSha) {
|
||||
if (!includeBaseCommitSha && baseCommitSha && auditSha === baseCommitSha) return undefined;
|
||||
return auditSha;
|
||||
}
|
||||
|
||||
if (!task.lineageId) return undefined;
|
||||
const associations = await scopedStore.getTaskCommitAssociationsByLineageId(task.lineageId);
|
||||
for (const association of associations) {
|
||||
if (association.commitSha && (await isReachableFromHead(association.commitSha, scopedStore.getRootDir()))) {
|
||||
if (!association.commitSha) continue;
|
||||
if (!includeBaseCommitSha && baseCommitSha && association.commitSha === baseCommitSha) continue;
|
||||
if (await isReachableFromHead(association.commitSha, rootDir)) {
|
||||
return association.commitSha;
|
||||
}
|
||||
}
|
||||
@@ -670,7 +696,19 @@ export function registerSessionDiffRoutes(router: Router, deps: SessionDiffRoute
|
||||
}
|
||||
|
||||
if (task.column === "done") {
|
||||
const mergeShaForBaseBoundary = await resolveDoneTaskMergeSha(task, scopedStore, { includeBaseCommitSha: true });
|
||||
const resolvedMergeSha = await resolveDoneTaskMergeSha(task, scopedStore);
|
||||
if (mergeShaForBaseBoundary && task.baseCommitSha) {
|
||||
// FN-5666: `git diff A..B` is exclusive of A, and baseCommitSha is the
|
||||
// task fork point, so when resolved SHA is at/below base the task has
|
||||
// no owned changes to display.
|
||||
const atOrBelowBase = await isAtOrBelowTaskBase(mergeShaForBaseBoundary, task.baseCommitSha, scopedStore.getRootDir());
|
||||
if (atOrBelowBase) {
|
||||
res.json({ files: [], stats: { filesChanged: 0, additions: 0, deletions: 0 } });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const doneTaskForDiff = resolvedMergeSha
|
||||
? {
|
||||
...task,
|
||||
@@ -931,7 +969,18 @@ export function registerSessionDiffRoutes(router: Router, deps: SessionDiffRoute
|
||||
}
|
||||
|
||||
if (task.column === "done") {
|
||||
const mergeShaForBaseBoundary = await resolveDoneTaskMergeSha(task, scopedStore, { includeBaseCommitSha: true });
|
||||
const resolvedMergeSha = await resolveDoneTaskMergeSha(task, scopedStore);
|
||||
if (mergeShaForBaseBoundary && task.baseCommitSha) {
|
||||
// FN-5666: baseCommitSha is the branch fork point and must remain
|
||||
// exclusive in per-task display diffs.
|
||||
const atOrBelowBase = await isAtOrBelowTaskBase(mergeShaForBaseBoundary, task.baseCommitSha, scopedStore.getRootDir());
|
||||
if (atOrBelowBase) {
|
||||
res.json([]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const doneTaskForDiff = resolvedMergeSha
|
||||
? {
|
||||
...task,
|
||||
|
||||
Reference in New Issue
Block a user