fix: Diff view now ignores line endings changes/windows autocrlf (#4356)

This commit is contained in:
Luke Parker
2025-11-16 13:18:39 +10:00
committed by GitHub
parent 6527a123f0
commit 35fbb011b2
3 changed files with 34 additions and 13 deletions

View File

@@ -864,18 +864,21 @@ function generateSyntax(theme: Theme) {
scope: ["diff.plus"], scope: ["diff.plus"],
style: { style: {
foreground: theme.diffAdded, foreground: theme.diffAdded,
background: theme.diffAddedBg,
}, },
}, },
{ {
scope: ["diff.minus"], scope: ["diff.minus"],
style: { style: {
foreground: theme.diffRemoved, foreground: theme.diffRemoved,
background: theme.diffRemovedBg,
}, },
}, },
{ {
scope: ["diff.delta"], scope: ["diff.delta"],
style: { style: {
foreground: theme.diffContext, foreground: theme.diffContext,
background: theme.diffContextBg,
}, },
}, },
{ {

View File

@@ -24,6 +24,8 @@ export namespace Snapshot {
}) })
.quiet() .quiet()
.nothrow() .nothrow()
// Configure git to not convert line endings on Windows
await $`git --git-dir ${git} config core.autocrlf false`.quiet().nothrow()
log.info("initialized") log.info("initialized")
} }
await $`git --git-dir ${git} --work-tree ${Instance.worktree} add .`.quiet().cwd(Instance.directory).nothrow() await $`git --git-dir ${git} --work-tree ${Instance.worktree} add .`.quiet().cwd(Instance.directory).nothrow()
@@ -45,10 +47,11 @@ export namespace Snapshot {
export async function patch(hash: string): Promise<Patch> { export async function patch(hash: string): Promise<Patch> {
const git = gitdir() const git = gitdir()
await $`git --git-dir ${git} --work-tree ${Instance.worktree} add .`.quiet().cwd(Instance.directory).nothrow() await $`git --git-dir ${git} --work-tree ${Instance.worktree} add .`.quiet().cwd(Instance.directory).nothrow()
const result = await $`git --git-dir ${git} --work-tree ${Instance.worktree} diff --name-only ${hash} -- .` const result =
.quiet() await $`git -c core.autocrlf=false --git-dir ${git} --work-tree ${Instance.worktree} diff --name-only ${hash} -- .`
.cwd(Instance.directory) .quiet()
.nothrow() .cwd(Instance.directory)
.nothrow()
// If git diff fails, return empty patch // If git diff fails, return empty patch
if (result.exitCode !== 0) { if (result.exitCode !== 0) {
@@ -122,10 +125,11 @@ export namespace Snapshot {
export async function diff(hash: string) { export async function diff(hash: string) {
const git = gitdir() const git = gitdir()
await $`git --git-dir ${git} --work-tree ${Instance.worktree} add .`.quiet().cwd(Instance.directory).nothrow() await $`git --git-dir ${git} --work-tree ${Instance.worktree} add .`.quiet().cwd(Instance.directory).nothrow()
const result = await $`git --git-dir ${git} --work-tree ${Instance.worktree} diff ${hash} -- .` const result =
.quiet() await $`git -c core.autocrlf=false --git-dir ${git} --work-tree ${Instance.worktree} diff ${hash} -- .`
.cwd(Instance.worktree) .quiet()
.nothrow() .cwd(Instance.worktree)
.nothrow()
if (result.exitCode !== 0) { if (result.exitCode !== 0) {
log.warn("failed to get diff", { log.warn("failed to get diff", {
@@ -155,7 +159,7 @@ export namespace Snapshot {
export async function diffFull(from: string, to: string): Promise<FileDiff[]> { export async function diffFull(from: string, to: string): Promise<FileDiff[]> {
const git = gitdir() const git = gitdir()
const result: FileDiff[] = [] const result: FileDiff[] = []
for await (const line of $`git --git-dir ${git} --work-tree ${Instance.worktree} diff --no-renames --numstat ${from} ${to} -- .` for await (const line of $`git -c core.autocrlf=false --git-dir ${git} --work-tree ${Instance.worktree} diff --no-renames --numstat ${from} ${to} -- .`
.quiet() .quiet()
.cwd(Instance.directory) .cwd(Instance.directory)
.nothrow() .nothrow()
@@ -165,10 +169,16 @@ export namespace Snapshot {
const isBinaryFile = additions === "-" && deletions === "-" const isBinaryFile = additions === "-" && deletions === "-"
const before = isBinaryFile const before = isBinaryFile
? "" ? ""
: await $`git --git-dir ${git} --work-tree ${Instance.worktree} show ${from}:${file}`.quiet().nothrow().text() : await $`git -c core.autocrlf=false --git-dir ${git} --work-tree ${Instance.worktree} show ${from}:${file}`
.quiet()
.nothrow()
.text()
const after = isBinaryFile const after = isBinaryFile
? "" ? ""
: await $`git --git-dir ${git} --work-tree ${Instance.worktree} show ${to}:${file}`.quiet().nothrow().text() : await $`git -c core.autocrlf=false --git-dir ${git} --work-tree ${Instance.worktree} show ${to}:${file}`
.quiet()
.nothrow()
.text()
result.push({ result.push({
file, file,
before, before,

View File

@@ -18,6 +18,10 @@ import { Instance } from "../project/instance"
import { Agent } from "../agent/agent" import { Agent } from "../agent/agent"
import { Snapshot } from "@/snapshot" import { Snapshot } from "@/snapshot"
function normalizeLineEndings(text: string): string {
return text.replaceAll("\r\n", "\n")
}
export const EditTool = Tool.define("edit", { export const EditTool = Tool.define("edit", {
description: DESCRIPTION, description: DESCRIPTION,
parameters: z.object({ parameters: z.object({
@@ -91,7 +95,9 @@ export const EditTool = Tool.define("edit", {
contentOld = await file.text() contentOld = await file.text()
contentNew = replace(contentOld, params.oldString, params.newString, params.replaceAll) contentNew = replace(contentOld, params.oldString, params.newString, params.replaceAll)
diff = trimDiff(createTwoFilesPatch(filePath, filePath, contentOld, contentNew)) diff = trimDiff(
createTwoFilesPatch(filePath, filePath, normalizeLineEndings(contentOld), normalizeLineEndings(contentNew)),
)
if (agent.permission.edit === "ask") { if (agent.permission.edit === "ask") {
await Permission.ask({ await Permission.ask({
type: "edit", type: "edit",
@@ -111,7 +117,9 @@ export const EditTool = Tool.define("edit", {
file: filePath, file: filePath,
}) })
contentNew = await file.text() contentNew = await file.text()
diff = trimDiff(createTwoFilesPatch(filePath, filePath, contentOld, contentNew)) diff = trimDiff(
createTwoFilesPatch(filePath, filePath, normalizeLineEndings(contentOld), normalizeLineEndings(contentNew)),
)
})() })()
FileTime.read(ctx.sessionID, filePath) FileTime.read(ctx.sessionID, filePath)