mirror of
https://github.com/getpaseo/paseo.git
synced 2026-11-11 19:05:16 +00:00
Show removed SQL comments in Git diffs (#6569)
Hunk content resembling patch headers was discarded before line parsing. Metadata is already ignored before the first hunk, so preserve every added and removed line once a hunk starts. Closes #6564 Co-authored-by: Yu Han <73627805+Mar-garet@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
import { execFileSync } from "node:child_process";
|
||||
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { expect, test } from "vitest";
|
||||
import { createDaemonTestContext } from "../test-utils/index.js";
|
||||
|
||||
function git(cwd: string, ...args: string[]): string {
|
||||
return execFileSync("git", args, { cwd, encoding: "utf8" });
|
||||
}
|
||||
|
||||
test.each(["uncommitted", "base", "commit"] as const)(
|
||||
"%s diff preserves removed SQL comments and their deletion counts",
|
||||
async (mode) => {
|
||||
const ctx = await createDaemonTestContext();
|
||||
const cwd = mkdtempSync(join(tmpdir(), "checkout-diff-content-"));
|
||||
|
||||
try {
|
||||
git(cwd, "init", "-b", "main");
|
||||
git(cwd, "config", "user.name", "Test");
|
||||
git(cwd, "config", "user.email", "test@example.com");
|
||||
writeFileSync(join(cwd, "query.sql"), "-- old comment\nSELECT 1;\n");
|
||||
writeFileSync(join(cwd, "control.sql"), "SELECT 1;\n");
|
||||
git(cwd, "add", ".");
|
||||
git(cwd, "-c", "commit.gpgsign=false", "commit", "-m", "initial");
|
||||
writeFileSync(join(cwd, "query.sql"), "-- new comment\nSELECT 1;\n");
|
||||
writeFileSync(join(cwd, "control.sql"), "SELECT 2;\n");
|
||||
|
||||
expect(git(cwd, "diff", "--numstat", "HEAD", "--", "query.sql").trim()).toBe(
|
||||
"1\t1\tquery.sql",
|
||||
);
|
||||
|
||||
if (mode !== "uncommitted") {
|
||||
git(cwd, "checkout", "-b", "feature");
|
||||
git(cwd, "add", ".");
|
||||
git(cwd, "-c", "commit.gpgsign=false", "commit", "-m", "edit");
|
||||
}
|
||||
|
||||
const files =
|
||||
mode === "commit"
|
||||
? [
|
||||
(
|
||||
await ctx.client.getCommitFileDiff(
|
||||
cwd,
|
||||
git(cwd, "rev-parse", "HEAD").trim(),
|
||||
"query.sql",
|
||||
)
|
||||
).file,
|
||||
(
|
||||
await ctx.client.getCommitFileDiff(
|
||||
cwd,
|
||||
git(cwd, "rev-parse", "HEAD").trim(),
|
||||
"control.sql",
|
||||
)
|
||||
).file,
|
||||
]
|
||||
: (await ctx.client.getCheckoutDiff(cwd, { mode, baseRef: "main" })).files;
|
||||
|
||||
const control = files.find((file) => file?.path === "control.sql");
|
||||
expect(control).toMatchObject({ additions: 1, deletions: 1 });
|
||||
expect(control?.hunks.flatMap((hunk) => hunk.lines)).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({ type: "remove", content: "SELECT 1;" }),
|
||||
expect.objectContaining({ type: "add", content: "SELECT 2;" }),
|
||||
]),
|
||||
);
|
||||
|
||||
const query = files.find((file) => file?.path === "query.sql");
|
||||
expect
|
||||
.soft(query)
|
||||
.toMatchObject({ additions: 1, deletions: 1, isNew: false, isDeleted: false });
|
||||
expect
|
||||
.soft(query?.hunks.flatMap((hunk) => hunk.lines))
|
||||
.toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({ type: "remove", content: "-- old comment" }),
|
||||
expect.objectContaining({ type: "add", content: "-- new comment" }),
|
||||
expect.objectContaining({ type: "context", content: "SELECT 1;" }),
|
||||
]),
|
||||
);
|
||||
} finally {
|
||||
await ctx.cleanup();
|
||||
rmSync(cwd, { recursive: true, force: true });
|
||||
}
|
||||
},
|
||||
60_000,
|
||||
);
|
||||
@@ -190,6 +190,34 @@ index 1234567..abcdefg 100644
|
||||
`;
|
||||
|
||||
describe("parseDiff", () => {
|
||||
it.each(["-- comment", "++ marker"])(
|
||||
"preserves hunk content resembling patch headers: %s",
|
||||
(content) => {
|
||||
const files = parseDiff(`diff --git a/example.txt b/example.txt
|
||||
index 1234567..abcdefg 100644
|
||||
--- a/example.txt
|
||||
+++ b/example.txt
|
||||
@@ -1 +1 @@
|
||||
-${content} before
|
||||
+${content} after
|
||||
`);
|
||||
|
||||
expect(files).toHaveLength(1);
|
||||
expect(files[0]).toMatchObject({
|
||||
path: "example.txt",
|
||||
isNew: false,
|
||||
isDeleted: false,
|
||||
additions: 1,
|
||||
deletions: 1,
|
||||
});
|
||||
expect(files[0].hunks[0].lines).toEqual([
|
||||
{ type: "header", content: "@@ -1 +1 @@" },
|
||||
{ type: "remove", content: `${content} before` },
|
||||
{ type: "add", content: `${content} after` },
|
||||
]);
|
||||
},
|
||||
);
|
||||
|
||||
it("parses a simple diff with one hunk", () => {
|
||||
const files = parseDiff(SIMPLE_DIFF);
|
||||
|
||||
|
||||
@@ -79,16 +79,6 @@ function extractPathFromDiffHeader(lines: string[]): string {
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
function isMetadataLine(line: string): boolean {
|
||||
return (
|
||||
line.startsWith("index ") ||
|
||||
line.startsWith("--- ") ||
|
||||
line.startsWith("+++ ") ||
|
||||
line.startsWith("new file mode") ||
|
||||
line.startsWith("deleted file mode")
|
||||
);
|
||||
}
|
||||
|
||||
function parseHunkHeader(line: string): DiffHunk | null {
|
||||
const hunkMatch = line.match(/^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/);
|
||||
if (!hunkMatch) return null;
|
||||
@@ -116,8 +106,6 @@ function parseSectionBody(lines: string[]): ParsedSectionBody {
|
||||
for (let i = 1; i < lines.length; i++) {
|
||||
const line = lines[i];
|
||||
|
||||
if (isMetadataLine(line)) continue;
|
||||
|
||||
const newHunk = parseHunkHeader(line);
|
||||
if (newHunk) {
|
||||
if (currentHunk) hunks.push(currentHunk);
|
||||
|
||||
Reference in New Issue
Block a user