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
This commit is contained in:
kiloconnect[bot]
2026-05-04 14:06:37 +00:00
parent 614bca7cff
commit 419ddea52a
2 changed files with 73 additions and 1 deletions
+18 -1
View File
@@ -429,6 +429,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)...")
@@ -524,7 +533,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
+55
View File
@@ -15,6 +15,12 @@ export interface RemoteInfo {
url: string
}
export interface CompatBase {
commit: string
upstream: string
message: string
}
export async function getCurrentBranch(): Promise<string> {
const result = await $`git rev-parse --abbrev-ref HEAD`.text()
return result.trim()
@@ -177,6 +183,55 @@ export async function getCommitHash(ref: string): Promise<string> {
return result.trim()
}
export async function getCommitParents(ref: string): Promise<string[]> {
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<string> {
const result = await $`git write-tree`.text()
return result.trim()
}
export async function commitTree(tree: string, message: string, parents: string[]): Promise<string> {
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<void> {
await $`git update-ref refs/heads/${name} ${commit}`
}
export async function findLatestCompatCommit(base: string, target: string): Promise<CompatBase | null> {
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.