дзеркало
https://github.com/l0ng-ai/openidea.git
синхронізовано 2026-11-09 19:25:54 +02:00
perf(vcs): keep multi-root Git Log refresh bounded (#1554)
Refreshing the combined Git Log of several repositories merged every repository's prefetched candidates into the result, so 300 loaded rows could grow into thousands and the graph layout took seconds. A refresh now keeps the loaded window through its last surviving commit (plus new arrivals); older history comes in with paging as before. Commit selection no longer rebuilds the layout when linear-history collapsing is off, and current-branch highlighting uses the backend's reachability, falling back to a single traversal from all heads. Co-authored-by: shohruh-genius <[email protected]>
Цей коміт міститься в:
1 джерело
7071fd9107
коміт
24cf1a52d5
5 files changed
+58
-13
No files matched your search
@@ -1263,16 +1263,20 @@ function CommitList() {
|
||||
),
|
||||
[all, heads, headCommit],
|
||||
);
|
||||
const keep = useMemo(
|
||||
() =>
|
||||
opts.collapseLinear || fragments.collapsed.size
|
||||
? new Set([...headKeys, ...selection, ...Object.keys(bookmarks)])
|
||||
: null,
|
||||
[opts.collapseLinear, fragments.collapsed, headKeys, selection, bookmarks],
|
||||
);
|
||||
const {
|
||||
commits,
|
||||
rows,
|
||||
linear,
|
||||
collapsed,
|
||||
graph: displayedGraph,
|
||||
} = useMemo(
|
||||
() => presentLogGraph(all, opts, fragments, new Set([...headKeys, ...selection, ...Object.keys(bookmarks)])),
|
||||
[all, opts, fragments, headKeys, selection, bookmarks],
|
||||
);
|
||||
} = useMemo(() => presentLogGraph(all, opts, fragments, keep ?? new Set()), [all, opts, fragments, keep]);
|
||||
const { edges: highlighted, nodes: highlightedNodes } = useMemo(() => {
|
||||
const empty = { edges: new Set<string>(), nodes: new Set<string>() };
|
||||
if (!hoverColor) return empty;
|
||||
@@ -1321,8 +1325,9 @@ function CommitList() {
|
||||
const onlyCurrent = filterBranches.length > 0 && filterBranches.every((b) => b === "HEAD" || b === currentBranch);
|
||||
const onBranch = useMemo(() => {
|
||||
if (!opts.highlightCurrentBranch || onlyCurrent) return null;
|
||||
const reach = new Set<string>();
|
||||
for (const head of headKeys) for (const id of reachableFrom(all.map(graphCommit), head)) reach.add(id);
|
||||
const reach = all.some((c) => c.onCurrentBranch == null)
|
||||
? reachableFrom(all.map(graphCommit), headKeys)
|
||||
: new Set<string>();
|
||||
for (const c of all) {
|
||||
if (c.onCurrentBranch === true) reach.add(commitKey(c));
|
||||
else if (c.onCurrentBranch === false) reach.delete(commitKey(c));
|
||||
|
||||
@@ -90,8 +90,9 @@ describe("long edges", () => {
|
||||
describe("highlighting", () => {
|
||||
it("finds the loaded commits of the current branch", () => {
|
||||
const commits = [c("s", ["a"]), c("h", ["a"]), c("a", ["r"]), c("r")];
|
||||
expect([...reachableFrom(commits, "h")].sort()).toEqual(["a", "h", "r"]);
|
||||
expect(reachableFrom(commits, null).size).toBe(0);
|
||||
expect([...reachableFrom(commits, ["h"])].sort()).toEqual(["a", "h", "r"]);
|
||||
expect([...reachableFrom(commits, ["h", "s"])].sort()).toEqual(["a", "h", "r", "s"]);
|
||||
expect(reachableFrom(commits, []).size).toBe(0);
|
||||
});
|
||||
|
||||
it("matches my commits by email or name", () => {
|
||||
|
||||
@@ -249,6 +249,39 @@ describe("Git log background refresh", () => {
|
||||
expect(useGitLog.getState().commits.map((c) => c.id)).toEqual(history.slice(0, PAGE * 3).map((c) => c.id));
|
||||
});
|
||||
|
||||
it("keeps the loaded multi-root window on refresh without emitting older candidates", async () => {
|
||||
const roots = useVcs.getState().roots;
|
||||
const history = Object.fromEntries(
|
||||
roots.map((repo, root) => [
|
||||
repo,
|
||||
Array.from({ length: 180 }, (_, i) => commit(`${root}-${i}`, 1000 - i * 2 - root)),
|
||||
]),
|
||||
);
|
||||
log.mockImplementation(async (repo: string, q: LogQuery) => ({
|
||||
status: "ok",
|
||||
data: history[repo]!.slice(q.skip, q.skip + q.limit).map((c, i) => ({
|
||||
...c,
|
||||
logPosition: q.skip + i,
|
||||
logTotal: history[repo]!.length,
|
||||
})),
|
||||
}));
|
||||
await reloadLog();
|
||||
const loaded = useGitLog.getState().commits.map((c) => c.id);
|
||||
expect(loaded).toHaveLength(PAGE);
|
||||
|
||||
history[roots[0]!]!.unshift(commit("new", 2000));
|
||||
await reloadLog({ preserve: true });
|
||||
expect(useGitLog.getState().commits.map((c) => c.id)).toEqual(["new", ...loaded]);
|
||||
expect(useGitLog.getState().done).toBe(false);
|
||||
|
||||
await loadMore();
|
||||
const expected = Object.values(history)
|
||||
.flat()
|
||||
.sort((a, b) => (b.timeMs ?? 0) - (a.timeMs ?? 0));
|
||||
expect(useGitLog.getState().commits.map((c) => c.id)).toEqual(expected.map((c) => c.id));
|
||||
expect(useGitLog.getState().done).toBe(true);
|
||||
});
|
||||
|
||||
it("retains the current history if refreshing fails", async () => {
|
||||
useVcs.setState({ roots: ["/project/front"] });
|
||||
log.mockResolvedValue({ status: "ok", data: [commit("old")] });
|
||||
|
||||
@@ -264,14 +264,13 @@ export function collapseLinear<
|
||||
return { commits: visible, graph };
|
||||
}
|
||||
|
||||
/** Ids of the loaded commits reachable from `head` (log order: children before parents). */
|
||||
/** Ids reachable from the given heads (log order: children before parents). */
|
||||
export function reachableFrom(
|
||||
commits: ReadonlyArray<{ id: string; parents: readonly string[] }>,
|
||||
head: string | null,
|
||||
heads: Iterable<string>,
|
||||
): Set<string> {
|
||||
const out = new Set<string>();
|
||||
if (!head) return out;
|
||||
out.add(head);
|
||||
const out = new Set(heads);
|
||||
if (!out.size) return out;
|
||||
for (const c of commits) if (out.has(c.id)) for (const p of c.parents) out.add(p);
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -994,6 +994,13 @@ function loadPage(replace: boolean, id: string, before = false): Promise<void> {
|
||||
before ? streams.map((stream, i) => [...stream, ...(currentStreams[i] ?? [])]) : streams,
|
||||
before || joined || s.seek || (replace && s.commits.length) ? Number.MAX_SAFE_INTEGER : limit,
|
||||
);
|
||||
if (replace && roots.length > 1 && s.commits.length && !s.seek) {
|
||||
// Keep the loaded window through its last surviving commit. Older candidates
|
||||
// from each repository are fetched again when paging.
|
||||
const loadedKeys = new Set(s.commits.map(commitKey));
|
||||
const last = page.findLastIndex((c) => loadedKeys.has(commitKey(c)));
|
||||
page.length = Math.min(page.length, Math.max(limit, last + 1));
|
||||
}
|
||||
const cur = logState(id);
|
||||
if (cur.generation !== gen) return;
|
||||
if (replace && results.length > 0 && failures.length === results.length && cur.commits.length) {
|
||||
|
||||
Reference in new issue
Заблокувати користувача