Skip to content

Commit 292c5b8

Browse files
authored
diff: the pane reads every file's hunks with one git process, where it started one per file (#98445)
1 parent 9778ad7 commit 292c5b8

50 files changed

Lines changed: 1158 additions & 145 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎mods/diff/hooks/backend/types/backend/backend.ts‎

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,13 @@ export type Backend = {
3030
fetchDiff: (mode: Git.BaseMode) => Promise<Git.FetchOutcome>
3131

3232
/**
33-
* One row's hunks against the base its fetch read (Git.fetchFileHunks).
33+
* The rows' hunks by path against the base their fetch read, null where
34+
* they could not be read (Git.fetchHunks).
3435
*/
35-
fetchFileHunks: (
36+
fetchHunks: (
3637
data: Git.DiffData,
37-
file: Git.FileStat,
38-
) => Promise<Git.FileHunks | null>
38+
files: readonly Git.FileStat[],
39+
) => Promise<ReadonlyMap<string, Git.FileHunks | null>>
3940

4041
/**
4142
* A string that changes when the checked-out commit moves
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/**
2+
* What the hunks read adds to the shared diff argv: the listed names and
3+
* every file's patch from one child, each file as it reads when asked alone.
4+
*
5+
* `--raw -z` heads the answer with the names as git holds them, NUL-ended
6+
* and never quoted; no rename is paired among the paths asked together, as
7+
* none can be for one path; the header's `a/` and `b/` hold whatever a
8+
* person's `diff.noprefix`, `diff.mnemonicPrefix` or prefixes are set to.
9+
*/
10+
export const HUNKS_ARGS = [
11+
'--no-renames',
12+
'--src-prefix=a/',
13+
'--dst-prefix=b/',
14+
'--raw',
15+
'-z',
16+
'-p',
17+
] as const
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export * from './hunks-args.js'
2+
3+
export * as default from '.'

‎mods/diff/hooks/git/argv/index.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export * from './diff-leading-args.js'
22
export * from './git-child-env'
3+
export * from './hunks-args'
34
export * from './no-optional-locks.js'
45
export * from './pinned-lead-of'
56

‎mods/diff/hooks/git/fetch-file-hunks/fetch-file-hunks.ts‎

Lines changed: 0 additions & 51 deletions
This file was deleted.
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import Limits from '../../limits'
2+
import Argv from '../argv'
3+
import { EMPTY_FILE_HUNKS } from '../empty-file-hunks'
4+
import Parse from '../parse'
5+
import type Types from '../types'
6+
import { isLastOfDiff } from './is-last-of-diff'
7+
import { withClosingLine } from './with-closing-line'
8+
9+
/**
10+
* The rows' hunks against the base their counts were read against, from one
11+
* git child over their literal paths; each but the last, a closing empty row.
12+
*
13+
* A row with no body (untracked, binary, renamed, or staged then edited on
14+
* an unborn HEAD) is never named to git. Paths past MAX_PATHSPEC_CHARS and
15+
* files a cut answer left unread go to a further child.
16+
*
17+
* @param run runs git against the pinned repository
18+
* @param data the fetch the rows belong to
19+
* @param files the rows
20+
* @returns each row's parsed body by path, null where git failed
21+
*/
22+
export async function fetchHunks(
23+
run: Types.GitRun,
24+
data: Types.DiffData,
25+
files: readonly Types.FileStat[],
26+
): Promise<ReadonlyMap<string, Types.FileHunks | null>> {
27+
const bodied = files.filter(
28+
file =>
29+
!file.isUntracked &&
30+
!file.isBinary &&
31+
file.renamedFrom === null &&
32+
!data.stalePaths.includes(file.path),
33+
)
34+
35+
const bodiless = files
36+
.filter(file => !bodied.includes(file))
37+
.map(file => [file.path, EMPTY_FILE_HUNKS] as const)
38+
39+
if (bodied.length === 0) {
40+
return new Map(bodiless)
41+
}
42+
43+
let spent = 0
44+
45+
const asked = bodied
46+
.map(file => file.path)
47+
.filter((path, at) => {
48+
spent += path.length
49+
50+
return at === 0 || spent <= Limits.MAX_PATHSPEC_CHARS
51+
})
52+
53+
const { exitCode, stdout } = await run([
54+
'--literal-pathspecs',
55+
...Argv.DIFF_LEADING_ARGS,
56+
...Argv.HUNKS_ARGS,
57+
data.baseRef,
58+
'--',
59+
...asked,
60+
])
61+
62+
const hasAnswered = exitCode === 0
63+
64+
const settled = hasAnswered
65+
? Parse.parseFileDiffs(stdout, asked)
66+
: new Map<string, Types.FileHunks>()
67+
68+
const waiting = bodied.filter(file => !settled.has(file.path))
69+
const isStuck = settled.size === 0
70+
71+
const rest = isStuck
72+
? waiting.map(file => [file.path, null] as const)
73+
: await fetchHunks(run, data, waiting)
74+
75+
return new Map<string, Types.FileHunks | null>([
76+
...bodiless,
77+
...bodied.flatMap(file => {
78+
const body = settled.get(file.path)
79+
const isLast = isLastOfDiff(data, file)
80+
81+
return body
82+
? [[file.path, isLast ? body : withClosingLine(body)] as const]
83+
: []
84+
}),
85+
...rest,
86+
])
87+
}

mods/diff/hooks/git/fetch-file-hunks/index.ts renamed to mods/diff/hooks/git/fetch-hunks/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export * from './fetch-file-hunks.js'
1+
export * from './fetch-hunks.js'
22
export * from './is-last-of-diff'
33
export * from './with-closing-line'
44

mods/diff/hooks/git/fetch-file-hunks/is-last-of-diff/index.ts renamed to mods/diff/hooks/git/fetch-hunks/is-last-of-diff/index.ts

File renamed without changes.

mods/diff/hooks/git/fetch-file-hunks/is-last-of-diff/is-last-of-diff.ts renamed to mods/diff/hooks/git/fetch-hunks/is-last-of-diff/is-last-of-diff.ts

File renamed without changes.

mods/diff/hooks/git/fetch-file-hunks/with-closing-line/index.ts renamed to mods/diff/hooks/git/fetch-hunks/with-closing-line/index.ts

File renamed without changes.

0 commit comments

Comments
 (0)