mirror of
https://github.com/l0ng-ai/openidea.git
synced 2026-11-08 08:52:12 +00:00
Root cause: the Git Log already supported multi-selection, but the Changes pane only read the last clicked commit and the backend had no "changes across several commits" query. Fix (IDEA): ide-vcs Repo::commits_changes merges per-file changes across selected commits (before the first touching commit ↔ after the last, renames chained, add-then-delete dropped); the details pane shows "N files in M commits" with the commit list; exactly two commits get "Compare Versions" (version ↔ version dialog, also fixing added/deleted sides). Siblings covered: Show History for file/folder (same log), Local History multi-select (oldest ↔ newest diff). Verified: ide-vcs tests incl. temp-repo commits_changes, vcs vitest (143), in-app on macOS: 3 contiguous commits → "2 files in 3 commits"; Cmd-click 2 commits → Compare Versions lists the right files. Claude-Session: https://claude.ai/code/session_01VUYsdXA2nkF5v6zsC93dpn
50 lines
2.3 KiB
TypeScript
50 lines
2.3 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { chainNeighbour, revisionChangeDiffData, withChain, type DiffData } from "../diffInput";
|
|
|
|
const diff = (path: string): DiffData => ({ path, left: { type: "rev", rev: "HEAD" }, right: { type: "worktree" } });
|
|
|
|
describe("diff chain", () => {
|
|
const items = [diff("/r/a"), diff("/r/b"), diff("/r/c")];
|
|
|
|
it("moves to the neighbouring file, revealing the nearest difference", () => {
|
|
const b = withChain(items, 1);
|
|
expect(b.path).toBe("/r/b");
|
|
const next = chainNeighbour(b, 1)!;
|
|
expect(next).toMatchObject({ path: "/r/c", reveal: "first" });
|
|
expect(chainNeighbour(b, -1)).toMatchObject({ path: "/r/a", reveal: "last" });
|
|
// The neighbour keeps the chain, so F7 can continue from it.
|
|
expect(chainNeighbour(next, -1)).toMatchObject({ path: "/r/b" });
|
|
});
|
|
|
|
it("stops at the ends and without a chain", () => {
|
|
expect(chainNeighbour(withChain(items, 2), 1)).toBeNull();
|
|
expect(chainNeighbour(withChain(items, 0), -1)).toBeNull();
|
|
expect(chainNeighbour(withChain([diff("/r/a")], 0), 1)).toBeNull();
|
|
expect(chainNeighbour(diff("/r/a"), 1)).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe("combined changes of several commits", () => {
|
|
const base = { oldPath: null, before: "b".repeat(40), after: "a".repeat(40) };
|
|
|
|
it("diffs the state before the first commit with the state after the last", () => {
|
|
expect(revisionChangeDiffData({ ...base, path: "src/x.ts", kind: "modified" })).toMatchObject({
|
|
path: "src/x.ts",
|
|
left: { type: "rev", rev: base.before, label: "bbbbbbbb" },
|
|
right: { type: "rev", rev: base.after, label: "aaaaaaaa" },
|
|
});
|
|
});
|
|
|
|
it("reads a renamed file's old content at its old path", () => {
|
|
const d = revisionChangeDiffData({ ...base, path: "new.ts", oldPath: "old.ts", kind: "renamed" });
|
|
expect(d.left).toMatchObject({ type: "rev", rev: base.before, path: "old.ts" });
|
|
});
|
|
|
|
it("shows an empty side for added and deleted files", () => {
|
|
const added = revisionChangeDiffData({ ...base, path: "n.ts", kind: "added", before: null });
|
|
expect(added.left).toMatchObject({ type: "text", text: "" });
|
|
const deleted = revisionChangeDiffData({ ...base, path: "d.ts", kind: "deleted", after: null });
|
|
expect(deleted.right).toMatchObject({ type: "text", text: "" });
|
|
});
|
|
});
|