a pull leaves origin/master where it found it, so every checkout looked ahead

plugins:check reported "2 commit(s) NOT PUSHED" for all twenty-one installed
plugins on a machine where nothing had been committed. the check was right about
what it measured and the cause was here.

`git fetch origin HEAD` writes FETCH_HEAD and nothing else. refs/remotes/origin/
<branch> keeps whatever it had, so after a successful pull the checkout sits at
the newest commit while origin/master still points at an older one — and every
`rev-list origin/master...HEAD` reports the checkout as ahead by however many
commits it just pulled.

so the remote-tracking ref is refreshed too, explicitly, after the fetch that
cannot do it. best effort, because the local branch may not exist on the remote
under that name — which is the same reason the fetch asks for HEAD rather than
for a branch.

fetch also asks for --tags now. tags are how releaseState decides whether there
is anything to update to, so a fetch that skipped them left the Update button
answering from refs older than the release it was offering.

verified by staling a ref on purpose: one false positive before, none after a
single pullPlugin.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Officer-Session: 656f5c85-e49a-4f1f-b9f3-042a04b4d043
This commit is contained in:
2026-08-27 08:46:18 +00:00
co-authored by Claude Opus 5
parent 0d5c2c0083
commit 68f3643df7
+22 -1
View File
@@ -83,10 +83,31 @@ export async function pullPlugin(appName: string, onLog?: OnLog): Promise<PullRe
//
// `HEAD` resolves to whatever the remote's default is, so the local name stops mattering. The
// fast-forward then applies it to whichever branch this checkout happens to be on.
const fetched = await git(dir, ['fetch', 'origin', 'HEAD']);
//
// `--tags` and `--update-head-ok` are not decoration. Tags are how `releaseState` decides whether there
// is anything to update to, so a fetch that skipped them would leave the Update button answering from
// refs older than the release it is offering.
const fetched = await git(dir, ['fetch', '--tags', 'origin', 'HEAD']);
if (fetched.code !== 0) {
return { ok: false, updated: false, before, after: before, error: fetched.err || 'git fetch failed' };
}
/*
ALSO refresh the remote-tracking ref, which fetching `HEAD` does not.
`git fetch origin HEAD` writes FETCH_HEAD and nothing else — `refs/remotes/origin/<branch>` keeps
whatever it had. So after a successful pull the checkout is at the newest commit while `origin/master`
still points at an older one, and every `rev-list origin/master...HEAD` reports the checkout as AHEAD.
That is exactly what `plugins:check` reported on 2026-08-27: twenty-one installed plugins each with
"2 commit(s) NOT PUSHED", on a machine where nothing had been committed at all. A check that cries
wolf is one people stop reading, and the cause was here rather than there.
Best effort: the branch may not exist on the remote under this name, which is the same reason the
fetch above asks for `HEAD`. A failure leaves things exactly as they were before this line.
*/
const branchRef = branch && branch !== 'HEAD' ? branch : null;
if (branchRef) await git(dir, ['fetch', 'origin', `+refs/heads/${branchRef}:refs/remotes/origin/${branchRef}`]);
const pulled = await git(dir, ['merge', '--ff-only', 'FETCH_HEAD']);
if (pulled.code !== 0) {
return { ok: false, updated: false, before, after: before, error: pulled.err || pulled.out || 'git pull failed' };