Skip to content

Commit 92efd2c

Browse files
committed
Order PR stack tables by base chain
1 parent b36028c commit 92efd2c

3 files changed

Lines changed: 69 additions & 13 deletions

File tree

src/app/ManualRebase.tsx

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,10 @@ async function run() {
5656
let commit_range = await CommitMetadata.range(commit_map);
5757

5858
// The first revise pass rewrites commits from the merge base upward, so it
59-
// must walk groups in commit application order rather than stack display
60-
// order. We intentionally do not apply the master_base-first reshuffle
61-
// here; that only matters for the follow-up pass that chooses a restart
62-
// point for dirty groups.
63-
commit_range.group_list = CommitMetadata.stack_order(commit_range).reverse();
59+
// must walk groups in commit application order. We intentionally do not
60+
// apply the master_base-first reshuffle here; that only matters for the
61+
// follow-up pass that chooses a restart point for dirty groups.
62+
commit_range.group_list = CommitMetadata.stack_order(commit_range);
6463

6564
for (const commit of commit_range.commit_list) {
6665
const group_from_map = commit_map[commit.sha];

src/core/CommitMetadata.order.test.ts

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,36 @@ test("stack_order preserves stack group order", () => {
1010
expect(ids(actual)).toEqual(["A", "B", "C"]);
1111
});
1212

13-
test("rebase_order reverses stack order", () => {
13+
test("stack_order derives stack group order from base chain", () => {
14+
const commit_range = range(["A", "B", "C"]);
15+
commit_range.group_list.reverse();
16+
17+
const actual = CommitMetadata.stack_order(commit_range);
18+
19+
expect(ids(actual)).toEqual(["A", "B", "C"]);
20+
});
21+
22+
test("stack_order does not drop groups with incomplete base links", () => {
23+
const commit_range = range(["A", "B", "C"]);
24+
commit_range.group_list[1]!.base = "missing";
25+
26+
const actual = CommitMetadata.stack_order(commit_range);
27+
28+
expect(ids(actual)).toEqual(["A", "B", "C"]);
29+
});
30+
31+
test("rebase_order preserves stack group order", () => {
1432
const commit_range = range(["A", "B", "C"]);
1533
const actual = CommitMetadata.rebase_order(commit_range);
1634

17-
expect(ids(actual)).toEqual(["C", "B", "A"]);
35+
expect(ids(actual)).toEqual(["A", "B", "C"]);
1836
});
1937

2038
test("rebase_order moves master_base groups to the front", () => {
2139
const commit_range = range(["A", { id: "B", master_base: true }, "C"]);
2240

2341
const actual = CommitMetadata.rebase_order(commit_range);
24-
expect(ids(actual)).toEqual(["B", "C", "A"]);
42+
expect(ids(actual)).toEqual(["B", "A", "C"]);
2543

2644
const [first_group] = actual;
2745
invariant(first_group, "first_group must exist");

src/core/CommitMetadata.ts

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -331,19 +331,58 @@ export async function range(commit_group_map?: CommitGroupMap) {
331331
}
332332

333333
export function stack_order(commit_range: CommitRange): CommitGroupList {
334-
return [...commit_range.group_list];
334+
const group_by_id = new Map(commit_range.group_list.map((group) => [group.id, group]));
335+
const children_by_base = new Map<string, CommitGroupList>();
336+
337+
for (const group of commit_range.group_list) {
338+
if (!group.base || !group_by_id.has(group.base)) {
339+
continue;
340+
}
341+
342+
const children = children_by_base.get(group.base) || [];
343+
children.push(group);
344+
children_by_base.set(group.base, children);
345+
}
346+
347+
const ordered_group_list: CommitGroupList = [];
348+
const visited = new Set<string>();
349+
350+
function visit(group: CommitGroupList[number]) {
351+
if (visited.has(group.id)) {
352+
return;
353+
}
354+
355+
visited.add(group.id);
356+
ordered_group_list.push(group);
357+
358+
for (const child of children_by_base.get(group.id) || []) {
359+
visit(child);
360+
}
361+
}
362+
363+
for (const group of commit_range.group_list) {
364+
if (!group.base || !group_by_id.has(group.base)) {
365+
visit(group);
366+
}
367+
}
368+
369+
for (const group of commit_range.group_list) {
370+
visit(group);
371+
}
372+
373+
return ordered_group_list;
335374
}
336375

337376
export function rebase_order(commit_range: CommitRange): CommitGroupList {
338377
const state = Store.getState();
339378
const actions = state.actions;
340379

341-
const reversed_group_list = stack_order(commit_range).reverse();
380+
const stack_group_list = stack_order(commit_range);
342381

343382
// order groups with group.master_base first
344383
const group_list_master: CommitGroupList = [];
345384
const group_list_others: CommitGroupList = [];
346-
for (const group of reversed_group_list) {
385+
for (const group of stack_group_list) {
347386
if (group.master_base) {
348387
group_list_master.push(group);
349388
} else {
@@ -354,8 +393,8 @@ export function rebase_order(commit_range: CommitRange): CommitGroupList {
354393
const ordered_group_list = [...group_list_master, ...group_list_others];
355394

356395
// detect if group list order differs
357-
for (let i = 0; i < reversed_group_list.length; i++) {
358-
const original_group = reversed_group_list[i];
396+
for (let i = 0; i < stack_group_list.length; i++) {
397+
const original_group = stack_group_list[i];
359398
const ordered_group = ordered_group_list[i];
360399
invariant(original_group, "original_group must exist");
361400
invariant(ordered_group, "ordered_group must exist");

0 commit comments

Comments
 (0)