fix(coding-agent): reduce git update fetch noise closes #2548
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
### Fixed
|
||||
|
||||
- Fixed RPC `get_session_stats` to expose `contextUsage`, so headless clients can read actual current context-window usage instead of deriving it from token totals ([#2550](https://github.com/badlogic/pi-mono/issues/2550))
|
||||
- Fixed `pi update` for git packages to fetch only the tracked target branch with `--no-tags`, reducing unrelated branch and tag noise while preserving force-push-safe updates ([#2548](https://github.com/badlogic/pi-mono/issues/2548))
|
||||
|
||||
## [0.62.0] - 2026-03-23
|
||||
|
||||
|
||||
@@ -1279,20 +1279,66 @@ export class DefaultPackageManager implements PackageManager {
|
||||
return match[1];
|
||||
}
|
||||
|
||||
private async getLocalGitUpdateTarget(installedPath: string): Promise<{ ref: string; head: string }> {
|
||||
private async getLocalGitUpdateTarget(
|
||||
installedPath: string,
|
||||
): Promise<{ ref: string; head: string; fetchArgs: string[] }> {
|
||||
try {
|
||||
const upstream = await this.runCommandCapture("git", ["rev-parse", "--abbrev-ref", "@{upstream}"], {
|
||||
cwd: installedPath,
|
||||
timeoutMs: NETWORK_TIMEOUT_MS,
|
||||
});
|
||||
const trimmedUpstream = upstream.trim();
|
||||
if (!trimmedUpstream.startsWith("origin/")) {
|
||||
throw new Error(`Unsupported upstream remote: ${trimmedUpstream}`);
|
||||
}
|
||||
const branch = trimmedUpstream.slice("origin/".length);
|
||||
if (!branch) {
|
||||
throw new Error("Missing upstream branch name");
|
||||
}
|
||||
const head = await this.runCommandCapture("git", ["rev-parse", "@{upstream}"], {
|
||||
cwd: installedPath,
|
||||
timeoutMs: NETWORK_TIMEOUT_MS,
|
||||
});
|
||||
return { ref: "@{upstream}", head };
|
||||
return {
|
||||
ref: "@{upstream}",
|
||||
head,
|
||||
fetchArgs: [
|
||||
"fetch",
|
||||
"--prune",
|
||||
"--no-tags",
|
||||
"origin",
|
||||
`+refs/heads/${branch}:refs/remotes/origin/${branch}`,
|
||||
],
|
||||
};
|
||||
} catch {
|
||||
await this.runCommand("git", ["remote", "set-head", "origin", "-a"], { cwd: installedPath }).catch(() => {});
|
||||
const head = await this.runCommandCapture("git", ["rev-parse", "origin/HEAD"], {
|
||||
cwd: installedPath,
|
||||
timeoutMs: NETWORK_TIMEOUT_MS,
|
||||
});
|
||||
return { ref: "origin/HEAD", head };
|
||||
const originHeadRef = await this.runCommandCapture("git", ["symbolic-ref", "refs/remotes/origin/HEAD"], {
|
||||
cwd: installedPath,
|
||||
timeoutMs: NETWORK_TIMEOUT_MS,
|
||||
}).catch(() => "");
|
||||
const branch = originHeadRef.trim().replace(/^refs\/remotes\/origin\//, "");
|
||||
if (branch) {
|
||||
return {
|
||||
ref: "origin/HEAD",
|
||||
head,
|
||||
fetchArgs: [
|
||||
"fetch",
|
||||
"--prune",
|
||||
"--no-tags",
|
||||
"origin",
|
||||
`+refs/heads/${branch}:refs/remotes/origin/${branch}`,
|
||||
],
|
||||
};
|
||||
}
|
||||
return {
|
||||
ref: "origin/HEAD",
|
||||
head,
|
||||
fetchArgs: ["fetch", "--prune", "--no-tags", "origin", "+HEAD:refs/remotes/origin/HEAD"],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1478,15 +1524,20 @@ export class DefaultPackageManager implements PackageManager {
|
||||
return;
|
||||
}
|
||||
|
||||
// Fetch latest from remote (handles force-push by getting new history)
|
||||
await this.runCommand("git", ["fetch", "--prune", "origin"], { cwd: targetDir });
|
||||
const target = await this.getLocalGitUpdateTarget(targetDir);
|
||||
|
||||
// Fetch only the ref we will reset to, avoiding unrelated branch/tag noise.
|
||||
await this.runCommand("git", target.fetchArgs, { cwd: targetDir });
|
||||
|
||||
const localHead = await this.runCommandCapture("git", ["rev-parse", "HEAD"], {
|
||||
cwd: targetDir,
|
||||
timeoutMs: NETWORK_TIMEOUT_MS,
|
||||
});
|
||||
const target = await this.getLocalGitUpdateTarget(targetDir);
|
||||
if (localHead.trim() === target.head.trim()) {
|
||||
const refreshedTargetHead = await this.runCommandCapture("git", ["rev-parse", target.ref], {
|
||||
cwd: targetDir,
|
||||
timeoutMs: NETWORK_TIMEOUT_MS,
|
||||
});
|
||||
if (localHead.trim() === refreshedTargetHead.trim()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -139,7 +139,10 @@ describe("DefaultPackageManager git update", () => {
|
||||
|
||||
await packageManager.update();
|
||||
|
||||
expect(executedCommands).toContain("git fetch --prune origin");
|
||||
expect(executedCommands).toContain(
|
||||
"git fetch --prune --no-tags origin +refs/heads/main:refs/remotes/origin/main",
|
||||
);
|
||||
expect(executedCommands).not.toContain("git fetch --prune origin");
|
||||
expect(executedCommands).not.toContain("git reset --hard @{upstream}");
|
||||
expect(executedCommands).not.toContain("git reset --hard origin/HEAD");
|
||||
expect(executedCommands).not.toContain("git clean -fdx");
|
||||
@@ -183,8 +186,26 @@ describe("DefaultPackageManager git update", () => {
|
||||
const detachedCommit = getCurrentCommit(installedDir);
|
||||
git(["checkout", detachedCommit], installedDir);
|
||||
|
||||
const executedCommands: string[] = [];
|
||||
const managerWithInternals = packageManager as unknown as {
|
||||
runCommand: (command: string, args: string[], options?: { cwd?: string }) => Promise<void>;
|
||||
};
|
||||
managerWithInternals.runCommand = async (command, args, options) => {
|
||||
executedCommands.push(`${command} ${args.join(" ")}`);
|
||||
const result = spawnSync(command, args, {
|
||||
cwd: options?.cwd,
|
||||
encoding: "utf-8",
|
||||
});
|
||||
if (result.status !== 0) {
|
||||
throw new Error(`Command failed: ${command} ${args.join(" ")}\n${result.stderr}`);
|
||||
}
|
||||
};
|
||||
|
||||
await packageManager.update();
|
||||
|
||||
expect(executedCommands).toContain(
|
||||
"git fetch --prune --no-tags origin +refs/heads/main:refs/remotes/origin/main",
|
||||
);
|
||||
expect(getCurrentCommit(installedDir)).toBe(latestCommit);
|
||||
expect(getFileContent(installedDir, "extension.ts")).toBe("// v3");
|
||||
});
|
||||
@@ -323,12 +344,17 @@ describe("DefaultPackageManager git update", () => {
|
||||
if (args[0] === "rev-parse" && args[1] === "@{upstream}") {
|
||||
return "remote-head";
|
||||
}
|
||||
if (args[0] === "rev-parse" && args[1] === "--abbrev-ref") {
|
||||
return "origin/main";
|
||||
}
|
||||
return "";
|
||||
};
|
||||
|
||||
await packageManager.resolveExtensionSources([gitSource], { temporary: true });
|
||||
|
||||
expect(executedCommands).toContain("git fetch --prune origin");
|
||||
expect(executedCommands).toContain(
|
||||
"git fetch --prune --no-tags origin +refs/heads/main:refs/remotes/origin/main",
|
||||
);
|
||||
expect(getFileContent(cachedDir, "pi-extensions/session-breakdown.ts")).toBe("// fresh");
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user