fix(cli): isolate upstream rerere training

This commit is contained in:
kiloconnect[bot]
2026-05-05 08:40:26 +00:00
parent d7643bcdff
commit 33aa8e5184
+29 -20
View File
@@ -4,6 +4,10 @@
*/
import { $ } from "bun"
import { rm } from "node:fs/promises"
import { randomUUID } from "node:crypto"
import { tmpdir } from "node:os"
import { join } from "node:path"
export interface BranchInfo {
current: string
@@ -267,6 +271,11 @@ export async function ensureRerere(): Promise<void> {
await $`git config rerere.autoupdate true`.quiet()
}
async function reset(dir: string): Promise<void> {
await $`git -C ${dir} reset -q --hard`.quiet().nothrow()
await $`git -C ${dir} clean -fdx`.quiet().nothrow()
}
/**
* Train the rerere cache from past merge commits in the repo history.
* Implements the same logic as git's contrib/rerere-train.sh:
@@ -277,14 +286,14 @@ export async function ensureRerere(): Promise<void> {
* Returns the number of resolutions learned.
*/
export async function trainRerere(grep: string): Promise<number> {
// Save the current HEAD so we can restore it afterwards
const headResult = await $`git symbolic-ref -q HEAD`.quiet().nothrow()
const branch = headResult.exitCode === 0 ? headResult.stdout.toString().trim() : null
const originalHead = branch ?? (await $`git rev-parse --verify HEAD`.text()).trim()
const head = (await $`git rev-parse --verify HEAD`.text()).trim()
const dir = join(tmpdir(), `kilo-rerere-train-${randomUUID()}`)
let learned = 0
try {
await $`git worktree add --detach ${dir} ${head}`.quiet()
// Find all merge commits matching the grep pattern (merges have multiple parents)
const revList = await $`git rev-list --parents --all --grep=${grep}`.quiet().nothrow()
if (revList.exitCode !== 0 || !revList.stdout.toString().trim()) return 0
@@ -301,45 +310,45 @@ export async function trainRerere(grep: string): Promise<number> {
const [commit, parent1, ...otherParents] = parts
await reset(dir)
// Checkout the first parent
const coResult = await $`git checkout -q ${parent1}`.quiet().nothrow()
const coResult = await $`git -C ${dir} checkout -q ${parent1}`.quiet().nothrow()
if (coResult.exitCode !== 0) continue
// Attempt the merge - we expect it to fail with conflicts
const mergeResult = await $`git merge --no-gpg-sign ${otherParents}`.quiet().nothrow()
const mergeResult = await $`git -C ${dir} merge --no-gpg-sign ${otherParents}`.quiet().nothrow()
if (mergeResult.exitCode === 0) {
// Cleanly merged — no conflicts to learn from, reset and skip
await $`git reset -q --hard`.quiet().nothrow()
await reset(dir)
continue
}
// Check if rerere recorded a pre-image (MERGE_RR exists and is non-empty)
const mergeRR = Bun.file(`${process.env.GIT_DIR || ".git"}/MERGE_RR`)
const hasMergeRR = await mergeRR.exists().catch(() => false)
const rr = await $`git -C ${dir} rev-parse --git-path MERGE_RR`.text()
const hasMergeRR = await Bun.file(rr.trim())
.exists()
.catch(() => false)
if (!hasMergeRR) {
await $`git reset -q --hard`.quiet().nothrow()
await reset(dir)
continue
}
// Record the conflict pre-image
await $`git rerere`.quiet().nothrow()
await $`git -C ${dir} rerere`.quiet().nothrow()
// Apply the actual resolution by checking out the merge commit's tree
await $`git checkout -q ${commit} -- .`.quiet().nothrow()
await $`git -C ${dir} checkout -q ${commit} -- .`.quiet().nothrow()
// Record the resolution post-image
await $`git rerere`.quiet().nothrow()
await $`git -C ${dir} rerere`.quiet().nothrow()
learned++
await $`git reset -q --hard`.quiet().nothrow()
await reset(dir)
}
} finally {
// Always restore original branch
if (branch) {
await $`git checkout ${branch.replace("refs/heads/", "")}`.quiet().nothrow()
} else {
await $`git checkout ${originalHead}`.quiet().nothrow()
}
await $`git worktree remove --force ${dir}`.quiet().nothrow()
await rm(dir, { recursive: true, force: true })
}
return learned