Skip to content

Commit 525d3b3

Browse files
authored
diff: the pane notices a finished merge by itself, and stays quiet on an unusual branch name (#98357)
1 parent 292c5b8 commit 525d3b3

4 files changed

Lines changed: 224 additions & 36 deletions

File tree

‎mods/diff/hooks/git/probes/head-key-of/head-key-of.ts‎

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ import { stampProbeOf } from '../stamp-probe-of'
99
* A string that changes when HEAD moves: HEAD's text and the timestamps
1010
* of HEAD, its ref file and packed-refs, each a listed real file.
1111
*
12-
* A HEAD that is not a plain file, or a `ref:` outside the safe refname
13-
* grammar, is read through `git rev-parse` instead and nothing is stat'ed;
14-
* every directory above a stat'ed file was listed as a real directory.
12+
* A `ref:` outside the safe refname grammar is dated by HEAD's own log, the
13+
* listed real file `logs/HEAD`; without one, or where HEAD is not a plain
14+
* file, `git rev-parse` answers instead.
1515
*
1616
* @param deps git, listings, timestamps, a file read
1717
* @param repository the repository's directories
@@ -54,13 +54,16 @@ export async function headKeyOf(
5454
const isSymbolic = ref !== ''
5555

5656
const isUnsafeRef = isSymbolic && !isSafeRefName(ref)
57+
const logged = isUnsafeRef ? await stampIfFile(own, 'logs/HEAD') : null
58+
const isUndated = isUnsafeRef && typeof logged !== 'number'
59+
const isRefDated = isSymbolic && !isUnsafeRef
5760

58-
return isUnsafeRef
61+
return isUndated
5962
? viaGit()
6063
: [
6164
head,
6265
await stampIfFile(own, 'HEAD'),
63-
isSymbolic ? await stampIfFile(common, ref) : null,
66+
isRefDated ? await stampIfFile(common, ref) : logged,
6467
await stampIfFile(common, 'packed-refs'),
6568
].join('|')
6669
}

‎mods/diff/hooks/register.ts‎

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,8 @@ export function register(on: On) {
146146
return asked.isAnswered || backend !== null
147147
}
148148

149+
startPoll(engine, probed)
150+
149151
const stored = PaneState.baseModeOf(
150152
await engine
151153
.storeGet(Names.baseStoreKeyOf(probed.repository.toplevel))
@@ -268,6 +270,25 @@ export function register(on: On) {
268270
return [...read.values()].includes(null)
269271
}
270272

273+
function keepBaseline() {
274+
const pinned = backend
275+
276+
if (!pinned || polled.headKey !== '') {
277+
return
278+
}
279+
280+
void pinned
281+
.headKeyOf()
282+
.catch(() => '')
283+
.then(key => {
284+
const isFirst = backend === pinned && polled.headKey === ''
285+
286+
if (isFirst) {
287+
polled.headKey = key
288+
}
289+
})
290+
}
291+
271292
function startPoll(engine: Host, pinned: Backend.Backend) {
272293
const readHeadKey = () => pinned.headKeyOf().catch(() => '')
273294

@@ -339,10 +360,6 @@ export function register(on: On) {
339360
case 'data':
340361
generation += 1
341362

342-
if (pinned) {
343-
startPoll(engine, pinned)
344-
}
345-
346363
break
347364
}
348365

@@ -386,6 +403,18 @@ export function register(on: On) {
386403
)
387404
}
388405

406+
async function markShown(
407+
engine: Host,
408+
trigger: (typeof Record.SHOWN_TRIGGERS)[number],
409+
): Promise<void> {
410+
const sessionId = await engine.sessionId().catch(() => null)
411+
412+
if (sessionId !== null && sessionId !== shownSessionId) {
413+
shownSessionId = sessionId
414+
Record.recorderOf(engine).shown(trigger, Record.widthBucketOf(columns))
415+
}
416+
}
417+
389418
async function openPane(
390419
engine: Host,
391420
trigger: (typeof Record.SHOWN_TRIGGERS)[number],
@@ -422,13 +451,9 @@ export function register(on: On) {
422451
}
423452

424453
isPaneOpen = true
454+
keepBaseline()
425455

426-
const sessionId = await engine.sessionId().catch(() => null)
427-
428-
if (sessionId !== null && sessionId !== shownSessionId) {
429-
shownSessionId = sessionId
430-
Record.recorderOf(engine).shown(trigger, Record.widthBucketOf(columns))
431-
}
456+
await markShown(engine, trigger)
432457

433458
const isStale = isDialog || landed !== landedBefore
434459

@@ -889,7 +914,9 @@ export function register(on: On) {
889914
(isResume ? sessionStartMs : await host.now())
890915

891916
if (isKeptOpen) {
917+
await markShown(host, 'manual')
892918
await pinBackend(host)
919+
keepBaseline()
893920
void refresh(host)
894921
}
895922

‎mods/diff/tests/git/probes/head-key-of.test.ts‎

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ describe('head-key-of', () => {
1616
function recorded(
1717
listings: Readonly<Record<string, ReadonlyMap<string, Git.EntryKind>>>,
1818
head: string,
19+
stampOf: (path: string) => number = () => Fixtures.STAMP,
1920
): Recorded {
2021
const statted: string[] = []
2122
const read: string[] = []
@@ -30,7 +31,7 @@ describe('head-key-of', () => {
3031
mtimeOf: path => {
3132
statted.push(path)
3233

33-
return Promise.resolve(Fixtures.STAMP)
34+
return Promise.resolve(stampOf(path))
3435
},
3536
readFile: path => {
3637
read.push(path)
@@ -57,6 +58,27 @@ describe('head-key-of', () => {
5758
'/r/.git/refs/heads': heads,
5859
}
5960

61+
const logged = {
62+
'/r/.git': new Map<string, Git.EntryKind>([
63+
...plainGitDir,
64+
['logs', 'dir'],
65+
]),
66+
'/r/.git/logs': new Map<string, Git.EntryKind>([['HEAD', 'file']]),
67+
}
68+
69+
async function idleOn(branch: string) {
70+
const probe = recorded(logged, `ref: refs/heads/${branch}\n`)
71+
72+
return {
73+
keys: new Set([
74+
await Git.headKeyOf(probe.deps, Fixtures.CHECKOUT),
75+
await Git.headKeyOf(probe.deps, Fixtures.CHECKOUT),
76+
await Git.headKeyOf(probe.deps, Fixtures.CHECKOUT),
77+
]).size,
78+
ran: probe.ran,
79+
}
80+
}
81+
6082
test('a plain checkout reads HEAD, its ref file, packed-refs', async () => {
6183
const probe = recorded(healthy, 'ref: refs/heads/main\n')
6284
const key = await Git.headKeyOf(probe.deps, Fixtures.CHECKOUT)
@@ -79,6 +101,37 @@ describe('head-key-of', () => {
79101
expect(key).toBe(`rev:${Fixtures.HEAD_SHA}`)
80102
})
81103

104+
test('a branch named with a # starts no git while idle', async () => {
105+
expect(await idleOn('fix/#12')).toEqual({ ran: [], keys: 1 })
106+
})
107+
108+
test('a branch named outside ASCII starts no git while idle', async () => {
109+
expect(await idleOn('höher')).toEqual({ ran: [], keys: 1 })
110+
})
111+
112+
test('on such a branch a moved HEAD is seen at the next look', async () => {
113+
let moves = 0
114+
115+
const probe = recorded(logged, 'ref: refs/heads/fix/#12\n', path =>
116+
path.endsWith('/logs/HEAD') ? Fixtures.STAMP + moves : Fixtures.STAMP,
117+
)
118+
119+
const before = await Git.headKeyOf(probe.deps, Fixtures.CHECKOUT)
120+
121+
moves += 1
122+
123+
expect(await Git.headKeyOf(probe.deps, Fixtures.CHECKOUT)).not.toBe(before)
124+
expect(probe.ran, 'dated by the log of HEAD, no git started').toEqual([])
125+
})
126+
127+
test('with no log of HEAD such a branch is read through git', async () => {
128+
const probe = recorded(healthy, 'ref: refs/heads/fix/#12\n')
129+
const key = await Git.headKeyOf(probe.deps, Fixtures.CHECKOUT)
130+
131+
expect(probe.statted).toEqual([])
132+
expect(key).toBe(`rev:${Fixtures.HEAD_SHA}`)
133+
})
134+
82135
test('a HEAD that is a symbolic link is never read or stat-ed', async () => {
83136
const probe = recorded(
84137
{

0 commit comments

Comments
 (0)