Show recent commit history in the explorer (#2312)

* feat(app): show recent commit history in explorer

* fix(app): keep recent commit history accurate

* fix(app): include merged commits in recent history

* perf(app): bound recent commit classification

* fix(app): refresh recent commits on Git updates
This commit is contained in:
Mohamed Boudra
2026-07-21 22:12:33 +02:00
committed by GitHub
parent 2ed1f4f353
commit 309672c8e5
15 changed files with 180 additions and 97 deletions
@@ -2,7 +2,11 @@
import { QueryClient } from "@tanstack/react-query";
import { beforeEach, describe, expect, it, vi } from "vitest";
import type { CheckoutStatusUpdate } from "@getpaseo/protocol/messages";
import { checkoutPrStatusQueryKey, checkoutStatusQueryKey } from "@/git/query-keys";
import {
checkoutCommitsQueryKey,
checkoutPrStatusQueryKey,
checkoutStatusQueryKey,
} from "@/git/query-keys";
import {
prPanePipelineQueryKey,
prPaneTimelineQueryKey,
@@ -132,6 +136,25 @@ describe("applyCheckoutStatusUpdateFromEvent", () => {
expect(queryClient.getQueryData(checkoutStatusQueryKey(serverId, cwd))).toEqual(pushed);
});
it("invalidates recent commits when checkout status is pushed", () => {
const queryClient = createQueryClient();
queryClient.setQueryData(checkoutCommitsQueryKey(serverId, cwd), { commits: [] });
queryClient.setQueryData(checkoutCommitsQueryKey(serverId, "/repo2"), { commits: [] });
applyCheckoutStatusUpdateFromEvent({
queryClient,
serverId,
message: checkoutStatusUpdate(checkoutStatus()),
});
expect(queryClient.getQueryState(checkoutCommitsQueryKey(serverId, cwd))?.isInvalidated).toBe(
true,
);
expect(
queryClient.getQueryState(checkoutCommitsQueryKey(serverId, "/repo2"))?.isInvalidated,
).toBe(false);
});
it("writes the PR status cache when prStatus is present, and skips it otherwise", () => {
const queryClient = createQueryClient();
const pushedPr = prStatus({ requestId: "pr-1" });
@@ -2,6 +2,7 @@ import type { QueryClient } from "@tanstack/react-query";
import type { CheckoutStatusResponse, CheckoutStatusUpdate } from "@getpaseo/protocol/messages";
import equal from "fast-deep-equal/es6";
import {
checkoutCommitsQueryKey,
checkoutPrStatusQueryKey,
checkoutStatusQueryKey,
invalidatePrPaneTimelineForCheckout,
@@ -49,6 +50,9 @@ export function applyCheckoutStatusUpdateFromEvent({
: undefined;
const cachePayload = prStatus ? { ...payload, prStatus } : payload;
queryClient.setQueryData(checkoutStatusQueryKey(serverId, payload.cwd), cachePayload);
void queryClient.invalidateQueries({
queryKey: checkoutCommitsQueryKey(serverId, payload.cwd),
});
expireStaleDiffModeOverrides({
serverId,
cwd: payload.cwd,
+17
View File
@@ -2,6 +2,7 @@ import { QueryClient } from "@tanstack/react-query";
import { describe, expect, it } from "vitest";
import {
checkoutDiffQueryKey,
checkoutCommitsQueryKey,
checkoutPrStatusQueryKey,
checkoutStatusQueryKey,
invalidateCheckoutGitQueriesForClient,
@@ -24,6 +25,8 @@ describe("checkout query keys", () => {
files: [],
});
queryClient.setQueryData(checkoutPrStatusQueryKey(serverId, cwd), { status: { number: 12 } });
queryClient.setQueryData(checkoutCommitsQueryKey(serverId, cwd), { commits: [] });
queryClient.setQueryData(checkoutCommitsQueryKey(serverId, "/tmp/other"), { commits: [] });
queryClient.setQueryData(prPaneTimelineQueryKey({ serverId, cwd, prNumber: 12 }), {
items: [],
});
@@ -62,6 +65,12 @@ describe("checkout query keys", () => {
expect(queryClient.getQueryState(checkoutPrStatusQueryKey(serverId, cwd))?.isInvalidated).toBe(
true,
);
expect(queryClient.getQueryState(checkoutCommitsQueryKey(serverId, cwd))?.isInvalidated).toBe(
true,
);
expect(
queryClient.getQueryState(checkoutCommitsQueryKey(serverId, "/tmp/other"))?.isInvalidated,
).toBe(false);
expect(
queryClient.getQueryState(prPaneTimelineQueryKey({ serverId, cwd, prNumber: 12 }))
?.isInvalidated,
@@ -102,6 +111,8 @@ describe("checkout query keys", () => {
queryClient.setQueryData(checkoutStatusQueryKey(serverId, cwd), { isGit: true });
queryClient.setQueryData(checkoutStatusQueryKey(serverId, otherCwd), { isGit: true });
queryClient.setQueryData(checkoutPrStatusQueryKey(serverId, cwd), { status: { number: 12 } });
queryClient.setQueryData(checkoutCommitsQueryKey(serverId, cwd), { commits: [] });
queryClient.setQueryData(checkoutCommitsQueryKey(otherServerId, cwd), { commits: [] });
queryClient.setQueryData(prPaneTimelineQueryKey({ serverId, cwd, prNumber: 12 }), {
items: [],
});
@@ -128,6 +139,12 @@ describe("checkout query keys", () => {
expect(queryClient.getQueryState(checkoutPrStatusQueryKey(serverId, cwd))?.isInvalidated).toBe(
true,
);
expect(queryClient.getQueryState(checkoutCommitsQueryKey(serverId, cwd))?.isInvalidated).toBe(
true,
);
expect(
queryClient.getQueryState(checkoutCommitsQueryKey(otherServerId, cwd))?.isInvalidated,
).toBe(false);
expect(
queryClient.getQueryState(prPaneTimelineQueryKey({ serverId, cwd, prNumber: 12 }))
?.isInvalidated,
+4
View File
@@ -62,6 +62,9 @@ export async function invalidateCheckoutGitQueriesForClient(
queryClient.invalidateQueries({
predicate: checkoutQueryPredicate("checkoutPrStatus", identity),
}),
queryClient.invalidateQueries({
queryKey: checkoutCommitsQueryKey(identity.serverId, identity.cwd),
}),
queryClient.invalidateQueries({
predicate: checkoutQueryPredicate(prPaneTimelineQueryKind, identity),
}),
@@ -81,6 +84,7 @@ export async function invalidateCheckoutGitQueriesForServer(
const kinds = [
"checkoutStatus",
"checkoutPrStatus",
"checkoutCommits",
prPaneTimelineQueryKind,
prPanePipelineQueryKind,
];
+2 -2
View File
@@ -788,12 +788,12 @@ export const ar: TranslationResources = {
deletedFile: "تم الحذف",
commits: {
title: "الإيداعات",
countLabel: "{{count}} إيداعات قبل الأساس",
countLabel: "{{count}} من الإيداعات الأخيرة",
fileDiffEmpty: "لا توجد تغييرات لعرضها",
fileDiffError: "تعذّر تحميل فروق الملف",
loading: "جارٍ تحميل الإيداعات…",
loadError: "تعذّر تحميل الإيداعات",
empty: "لا توجد إيداعات قبل الأساس",
empty: "لا توجد إيداعات بعد",
},
},
openInEditor: {
+2 -2
View File
@@ -798,12 +798,12 @@ export const en = {
deletedFile: "Deleted",
commits: {
title: "Commits",
countLabel: "{{count}} commits ahead of base",
countLabel: "{{count}} recent commits",
fileDiffEmpty: "No changes to display",
fileDiffError: "Failed to load file diff",
loading: "Loading commits…",
loadError: "Failed to load commits",
empty: "No commits ahead of base",
empty: "No commits yet",
},
},
openInEditor: {
+2 -2
View File
@@ -819,12 +819,12 @@ export const es: TranslationResources = {
deletedFile: "Eliminado",
commits: {
title: "Commits",
countLabel: "{{count}} commits por delante de la base",
countLabel: "{{count}} commits recientes",
fileDiffEmpty: "No hay cambios para mostrar",
fileDiffError: "Error al cargar el diff del archivo",
loading: "Cargando commits…",
loadError: "Error al cargar los commits",
empty: "No hay commits por delante de la base",
empty: "Aún no hay commits",
},
},
openInEditor: {
+2 -2
View File
@@ -818,12 +818,12 @@ export const fr: TranslationResources = {
deletedFile: "Supprimé",
commits: {
title: "Commits",
countLabel: "{{count}} commits en avance sur la base",
countLabel: "{{count}} commits récents",
fileDiffEmpty: "Aucune modification à afficher",
fileDiffError: "Échec du chargement du diff du fichier",
loading: "Chargement des commits…",
loadError: "Échec du chargement des commits",
empty: "Aucun commit en avance sur la base",
empty: "Aucun commit pour le moment",
},
},
openInEditor: {
+2 -2
View File
@@ -799,12 +799,12 @@ export const ja: TranslationResources = {
deletedFile: "削除済み",
commits: {
title: "コミット",
countLabel: "ベースより先のコミット数: {{count}}",
countLabel: "最近のコミット数: {{count}}",
fileDiffEmpty: "表示する変更はありません",
fileDiffError: "ファイル差分の読み込みに失敗しました",
loading: "コミットを読み込み中…",
loadError: "コミットの読み込みに失敗しました",
empty: "ベースより先のコミットはありません",
empty: "コミットはまだありません",
},
},
openInEditor: {
+2 -2
View File
@@ -810,12 +810,12 @@ export const ptBR: TranslationResources = {
deletedFile: "Excluído",
commits: {
title: "Commits",
countLabel: "{{count}} commits à frente da base",
countLabel: "{{count}} commits recentes",
fileDiffEmpty: "Nenhuma alteração para exibir",
fileDiffError: "Falha ao carregar diff do arquivo",
loading: "Carregando commits…",
loadError: "Falha ao carregar commits",
empty: "Nenhum commit à frente da base",
empty: "Ainda não há commits",
},
},
openInEditor: {
+2 -2
View File
@@ -810,12 +810,12 @@ export const ru: TranslationResources = {
deletedFile: "Удалено",
commits: {
title: "Коммиты",
countLabel: "{{count}} коммитов впереди базы",
countLabel: "{{count}} последних коммитов",
fileDiffEmpty: "Нет изменений для отображения",
fileDiffError: "Не удалось загрузить различия файла",
loading: "Загрузка коммитов…",
loadError: "Не удалось загрузить коммиты",
empty: "Нет коммитов впереди базы",
empty: "Коммитов пока нет",
},
},
openInEditor: {
+2 -2
View File
@@ -780,12 +780,12 @@ export const zhCN: TranslationResources = {
deletedFile: "已删除",
commits: {
title: "提交",
countLabel: "领先基线 {{count}} 个提交",
countLabel: "最近 {{count}} 个提交",
fileDiffEmpty: "没有可显示的更改",
fileDiffError: "加载文件差异失败",
loading: "正在加载提交…",
loadError: "加载提交失败",
empty: "没有领先基线的提交",
empty: "暂无提交",
},
},
openInEditor: {