From 98fbee8a917aa35a1f85ab4881662667257453d5 Mon Sep 17 00:00:00 2001 From: "kiloconnect[bot]" <240665456+kiloconnect[bot]@users.noreply.github.com> Date: Mon, 4 May 2026 14:06:37 +0000 Subject: [PATCH] chore(upstream): implement compatibility base tracking for merges Update the upstream merge script to identify and link to the previous compatibility commit. This ensures that pre-merge transformations maintain a proper commit history by using multiple parents when a prior compatibility base is found, rather than creating disconnected commits. - Add `findLatestCompatCommit` to locate previous compatibility commits - Implement `commitTree` and `updateBranch` for low-level git manipulation - Update `merge.ts` to integrate compatibility base detection and multi-parent commits --- script/upstream/merge.ts | 19 ++++++++++++- script/upstream/utils/git.ts | 55 ++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 1 deletion(-) diff --git a/script/upstream/merge.ts b/script/upstream/merge.ts index 02933816c1f..9c66f86eaac 100644 --- a/script/upstream/merge.ts +++ b/script/upstream/merge.ts @@ -421,6 +421,15 @@ async function main() { await git.createBranch(opencodeBranch) logger.info(`Created opencode branch: ${opencodeBranch}`) + const prior = await git.findLatestCompatCommit(config.baseBranch, targetVersion.commit) + if (prior) { + logger.info( + `Found previous compatibility base: ${prior.message} (${prior.commit.slice(0, 8)}) from upstream ${prior.upstream.slice(0, 8)}`, + ) + } else { + logger.warn("No previous compatibility base found; merge base will remain pristine upstream") + } + // Step 6: Apply ALL transformations to opencode branch (pre-merge) // This reduces conflicts by transforming upstream code to Kilo conventions BEFORE merging logger.step(6, 8, "Applying transformations to opencode branch (pre-merge)...") @@ -508,7 +517,15 @@ async function main() { // Commit all transformations await git.stageAll() - await git.commit(`refactor: kilo compat for ${targetVersion.tag}`) + const compatMessage = `refactor: kilo compat for ${targetVersion.tag}` + if (prior) { + const tree = await git.writeTree() + const commit = await git.commitTree(tree, compatMessage, [targetVersion.commit, prior.commit]) + await git.updateBranch(opencodeBranch, commit) + await git.checkout(opencodeBranch) + } else { + await git.commit(compatMessage) + } logger.success("Committed pre-merge transformations") // Step 7: Merge into Kilo branch diff --git a/script/upstream/utils/git.ts b/script/upstream/utils/git.ts index 461cd712e4f..7e44a0e055e 100644 --- a/script/upstream/utils/git.ts +++ b/script/upstream/utils/git.ts @@ -19,6 +19,12 @@ export interface RemoteInfo { url: string } +export interface CompatBase { + commit: string + upstream: string + message: string +} + export async function getCurrentBranch(): Promise { const result = await $`git rev-parse --abbrev-ref HEAD`.text() return result.trim() @@ -181,6 +187,55 @@ export async function getCommitHash(ref: string): Promise { return result.trim() } +export async function getCommitParents(ref: string): Promise { + const result = await $`git show --no-patch --format=%P ${ref}`.text() + return result + .trim() + .split(/\s+/) + .filter((parent) => parent.length > 0) +} + +export async function writeTree(): Promise { + const result = await $`git write-tree`.text() + return result.trim() +} + +export async function commitTree(tree: string, message: string, parents: string[]): Promise { + const args = parents.flatMap((parent) => ["-p", parent]) + const result = await $`git commit-tree ${tree} ${args} -m ${message}`.text() + return result.trim() +} + +export async function updateBranch(name: string, commit: string): Promise { + await $`git update-ref refs/heads/${name} ${commit}` +} + +export async function findLatestCompatCommit(base: string, target: string): Promise { + const grep = "^refactor: kilo compat for " + const result = await $`git log --format=%H%x00%s --grep=${grep} ${base}`.quiet().nothrow() + if (result.exitCode !== 0) { + throw new Error(`Failed to search compatibility commits: ${result.stderr.toString()}`) + } + + const lines = result.stdout + .toString() + .trim() + .split("\n") + .filter((line) => line.length > 0) + + for (const line of lines) { + const [commit, message = ""] = line.split("\0") + if (!commit) continue + + const parents = await getCommitParents(commit) + for (const parent of parents) { + if (await isAncestor(parent, target)) return { commit, upstream: parent, message } + } + } + + return null +} + /** * Check if `commit` is an ancestor of `ref` (i.e. reachable from `ref`). * Uses `git merge-base --is-ancestor`, which exits 0 for yes, 1 for no.