Skip to content

Commit e1a90ab

Browse files
committed
diff: split hunks strictly by changes
1 parent 8cba0d3 commit e1a90ab

File tree

6 files changed

+44
-8
lines changed

6 files changed

+44
-8
lines changed

packages/desktop/src/features/git/DiffManager.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@ export type WorkingTreeGroups = {
3737
const MAX_UNTRACKED_FILE_BYTES = 1024 * 1024; // 1MB
3838

3939
export class GitDiffManager {
40-
private readonly WORKING_DIFF_CONTEXT_LINES = 3; // Zed/Git default context
40+
// Zed-like hunking: a hunk is only the contiguous changed lines.
41+
// Any unchanged line should start a new hunk, so we request zero context from git.
42+
private readonly WORKING_DIFF_CONTEXT_LINES = 0;
4143
constructor(
4244
private gitExecutor: GitExecutor,
4345
private logger?: Logger,

packages/desktop/src/features/git/StagingManager.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@ export class GitStagingManager {
310310
scope: 'staged' | 'unstaged',
311311
sessionId: string
312312
): Promise<string> {
313-
const unified = '--unified=3';
313+
const unified = '--unified=0';
314314
const argv =
315315
scope === 'staged'
316316
? ['git', 'diff', '--cached', '--color=never', unified, '--src-prefix=a/', '--dst-prefix=b/', 'HEAD', '--', filePath]

packages/desktop/src/features/git/__tests__/DiffManager.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { GitDiffManager } from '../DiffManager';
33
import type { GitExecutor } from '../../../executors/git';
44

55
describe('GitDiffManager', () => {
6-
it('uses --unified=3 for staged working tree diff', async () => {
6+
it('uses --unified=0 for staged working tree diff', async () => {
77
const mockGitExecutor: GitExecutor = {
88
run: vi.fn(async ({ argv }) => {
99
if (argv[1] === 'rev-parse') {
@@ -34,7 +34,7 @@ describe('GitDiffManager', () => {
3434
.mock.calls.find((c) => (c[0] as any).argv?.[1] === 'diff' && (c[0] as any).argv?.includes('--cached') && !(c[0] as any).argv?.includes('--name-only') && !(c[0] as any).argv?.includes('--shortstat'));
3535

3636
expect(diffCall).toBeTruthy();
37-
expect((diffCall?.[0] as any).argv).toEqual(expect.arrayContaining(['--unified=3']));
37+
expect((diffCall?.[0] as any).argv).toEqual(expect.arrayContaining(['--unified=0']));
3838
});
3939

4040
it('uses --unified=0 for commit diff (git show)', async () => {

packages/desktop/src/features/git/__tests__/StagingManager.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,7 @@ describe('GitStagingManager', () => {
334334

335335
// Verify git diff was called
336336
expect(mockGitExecutor.run).toHaveBeenNthCalledWith(1, expect.objectContaining({
337-
argv: expect.arrayContaining(['git', 'diff', '--unified=3']),
337+
argv: expect.arrayContaining(['git', 'diff', '--unified=0']),
338338
}));
339339

340340
// Verify git apply was called with --cached
@@ -683,7 +683,7 @@ Binary files differ`;
683683
expect(mockGitExecutor.run).toHaveBeenCalledTimes(2);
684684

685685
expect(mockGitExecutor.run).toHaveBeenNthCalledWith(1, expect.objectContaining({
686-
argv: expect.arrayContaining(['git', 'diff', '--cached', '--unified=3']),
686+
argv: expect.arrayContaining(['git', 'diff', '--cached', '--unified=0']),
687687
}));
688688

689689
expect(mockGitExecutor.run).toHaveBeenNthCalledWith(2, expect.objectContaining({
@@ -728,7 +728,7 @@ Binary files differ`;
728728
expect(result.success).toBe(true);
729729
expect(mockGitExecutor.run).toHaveBeenCalledTimes(2);
730730
expect(mockGitExecutor.run).toHaveBeenNthCalledWith(1, expect.objectContaining({
731-
argv: expect.arrayContaining(['git', 'diff', '--unified=3']),
731+
argv: expect.arrayContaining(['git', 'diff', '--unified=0']),
732732
}));
733733
expect(mockGitExecutor.run).toHaveBeenNthCalledWith(2, expect.objectContaining({
734734
argv: expect.arrayContaining(['git', 'apply', '-R']),
@@ -779,7 +779,7 @@ Binary files differ`;
779779
expect(result.success).toBe(true);
780780
expect(mockGitExecutor.run).toHaveBeenCalledTimes(3);
781781
expect(mockGitExecutor.run).toHaveBeenNthCalledWith(1, expect.objectContaining({
782-
argv: expect.arrayContaining(['git', 'diff', '--cached', '--unified=3']),
782+
argv: expect.arrayContaining(['git', 'diff', '--cached', '--unified=0']),
783783
}));
784784
expect(mockGitExecutor.run).toHaveBeenNthCalledWith(2, expect.objectContaining({
785785
argv: expect.arrayContaining(['git', 'apply', '--cached', '-R']),

packages/ui/src/components/panels/diff/ZedDiffViewer.test.tsx

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,34 @@ describe('ZedDiffViewer', () => {
4747
expect(screen.getByTestId('diff-viewer-zed')).toBeInTheDocument();
4848
});
4949

50+
it('expands modified files to full file when fileSources is provided', () => {
51+
const diff = `diff --git a/a.txt b/a.txt
52+
index 1234567..abcdefg 100644
53+
--- a/a.txt
54+
+++ b/a.txt
55+
@@ -2,1 +2,1 @@
56+
-old
57+
+new`;
58+
59+
const { container } = render(<ZedDiffViewer diff={diff} fileSources={{ 'a.txt': 'one\nold\nthree' }} />);
60+
// one (context) + old (delete) + new (insert) + three (context)
61+
expect(container.querySelectorAll('tr.diff-line')).toHaveLength(4);
62+
});
63+
64+
it('does not duplicate content when expanding a new file diff', () => {
65+
const diff = `diff --git a/new.txt b/new.txt
66+
new file mode 100644
67+
index 0000000..abcdefg
68+
--- /dev/null
69+
+++ b/new.txt
70+
@@ -0,0 +1,2 @@
71+
+first
72+
+second`;
73+
74+
const { container } = render(<ZedDiffViewer diff={diff} fileSources={{ 'new.txt': 'first\nsecond' }} />);
75+
expect(container.querySelectorAll('tr.diff-line')).toHaveLength(2);
76+
});
77+
5078
it('stages a hunk when scope is unstaged', async () => {
5179
(API.sessions.stageHunk as any).mockResolvedValue({ success: true, data: { success: true } });
5280
render(

packages/ui/src/components/panels/diff/ZedDiffViewer.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,12 @@ function expandToFullFile(hunks: HunkData[], source: string): HunkData[] {
7676
return all ? [all] : [];
7777
}
7878

79+
// New file diffs typically use @@ -0,0 +1,N @@ and already contain the full content as insertions.
80+
// Expanding using the worktree content would duplicate the file as "plain context" below the inserted hunk.
81+
if (normalized.some((h) => h.oldStart === 0 && h.oldLines === 0)) {
82+
return normalized;
83+
}
84+
7985
const output: HunkData[] = [];
8086
let oldCursor = 1;
8187
let delta = 0; // newLine = oldLine + delta for unchanged lines

0 commit comments

Comments
 (0)