Skip to content

Commit 4c78998

Browse files
committed
ui: order commits newest first
1 parent e1a90ab commit 4c78998

File tree

2 files changed

+96
-1
lines changed

2 files changed

+96
-1
lines changed

packages/ui/src/components/layout/RightPanel.test.tsx

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,4 +246,74 @@ describe('RightPanel - Zed-style Changes list', () => {
246246
'right-panel-file-untracked-dd.txt',
247247
]);
248248
});
249+
250+
it('orders commits newest-first (working tree first, base last)', async () => {
251+
(API.sessions.getExecutions as any).mockResolvedValue({
252+
success: true,
253+
data: [
254+
{
255+
id: 0,
256+
commit_message: 'Uncommitted changes',
257+
timestamp: new Date('2026-01-01T00:00:00.000Z').toISOString(),
258+
stats_additions: 0,
259+
stats_deletions: 0,
260+
stats_files_changed: 0,
261+
after_commit_hash: '',
262+
parent_commit_hash: null,
263+
author: 'test',
264+
},
265+
{
266+
id: 1,
267+
commit_message: 'older commit',
268+
timestamp: new Date('2026-01-01T00:00:01.000Z').toISOString(),
269+
stats_additions: 1,
270+
stats_deletions: 1,
271+
stats_files_changed: 1,
272+
after_commit_hash: '1111111111111111111111111111111111111111',
273+
parent_commit_hash: null,
274+
author: 'test',
275+
},
276+
{
277+
id: 2,
278+
commit_message: 'newer commit',
279+
timestamp: new Date('2026-01-01T00:00:02.000Z').toISOString(),
280+
stats_additions: 2,
281+
stats_deletions: 0,
282+
stats_files_changed: 1,
283+
after_commit_hash: '2222222222222222222222222222222222222222',
284+
parent_commit_hash: null,
285+
author: 'test',
286+
},
287+
{
288+
id: -1,
289+
commit_message: 'base',
290+
timestamp: new Date('2025-12-31T23:59:59.000Z').toISOString(),
291+
stats_additions: 0,
292+
stats_deletions: 0,
293+
stats_files_changed: 0,
294+
after_commit_hash: 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb',
295+
parent_commit_hash: null,
296+
author: 'test',
297+
},
298+
],
299+
});
300+
301+
const { container } = render(<RightPanel {...mockProps} />);
302+
303+
await waitFor(() => {
304+
expect(screen.getByText(/Commits/i)).toBeInTheDocument();
305+
expect(screen.getByLabelText('Select commit uncommitted changes')).toBeInTheDocument();
306+
});
307+
308+
const selectButtons = Array.from(container.querySelectorAll('button[aria-label^="Select commit"]'));
309+
const labels = selectButtons.map((el) => el.getAttribute('aria-label'));
310+
311+
expect(labels[0]).toBe('Select commit uncommitted changes');
312+
expect(labels[1]).toBe('Select commit 2222222');
313+
expect(labels[2]).toBe('Select commit 1111111');
314+
expect(labels[3]).toBe('Select commit bbbbbbb');
315+
const commitTexts = container.textContent || '';
316+
expect(commitTexts.indexOf('newer commit')).toBeLessThan(commitTexts.indexOf('older commit'));
317+
expect(commitTexts.indexOf('older commit')).toBeLessThan(commitTexts.indexOf('base'));
318+
});
249319
});

packages/ui/src/components/layout/RightPanel.tsx

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,31 @@ StackConnector.displayName = 'StackConnector';
9393
// Zed/Git-like ordering: locale-independent, matches git path sorting.
9494
const compareGitPaths = (a: string, b: string) => (a === b ? 0 : a < b ? -1 : 1);
9595

96+
const toEpochMs = (timestamp: string) => {
97+
const t = Date.parse(timestamp);
98+
return Number.isFinite(t) ? t : 0;
99+
};
100+
101+
const orderCommitsNewestFirst = (items: Commit[]) => {
102+
const uncommitted = items.filter((c) => c.id === 0);
103+
const base = items.filter((c) => c.id === -1);
104+
const others = items.filter((c) => c.id !== 0 && c.id !== -1);
105+
106+
const sessionCommits = others.filter((c) => c.id > 0);
107+
const rest = others.filter((c) => c.id <= 0);
108+
109+
sessionCommits.sort((a, b) => {
110+
const dt = toEpochMs(b.timestamp) - toEpochMs(a.timestamp);
111+
if (dt !== 0) return dt;
112+
const ha = a.after_commit_hash || '';
113+
const hb = b.after_commit_hash || '';
114+
return ha === hb ? 0 : ha < hb ? 1 : -1;
115+
});
116+
117+
// Keep "Working Tree" first and "BASE" last.
118+
return [...uncommitted, ...sessionCommits, ...rest, ...base];
119+
};
120+
96121
interface Commit {
97122
id: number;
98123
commit_message: string;
@@ -481,7 +506,7 @@ export const RightPanel: React.FC<RightPanelProps> = React.memo(({
481506

482507
if (response.success && response.data) {
483508
setError(null);
484-
const next = response.data as Commit[];
509+
const next = orderCommitsNewestFirst(response.data as Commit[]);
485510
setCommits(next);
486511

487512
const hasUncommitted = next.some((c) => c.id === 0);

0 commit comments

Comments
 (0)