add merge context (branch log, diff stats, per-file diffs) to improve conflict resolution quality
This commit is contained in:
parent
e88de1aed3
commit
ae6604caad
|
|
@ -29,13 +29,46 @@ export async function action(branch: string) {
|
||||||
try {
|
try {
|
||||||
await vm.ensure((msg) => { spin.text = msg })
|
await vm.ensure((msg) => { spin.text = msg })
|
||||||
|
|
||||||
|
// Gather merge context
|
||||||
|
spin.text = "Gathering merge context"
|
||||||
|
const current = await git.currentBranch(root)
|
||||||
|
const base = await git.mergeBase("HEAD", branch, root)
|
||||||
|
const branchLog = await git.log(`${base}..${branch}`, root)
|
||||||
|
const branchDiffStat = await git.diffStat(`${base}..${branch}`, root)
|
||||||
|
|
||||||
for (const file of conflicts) {
|
for (const file of conflicts) {
|
||||||
spin.text = `Resolving ${file}`
|
spin.text = `Resolving ${file}`
|
||||||
|
|
||||||
|
const oursDiff = await git.fileDiff(base, "HEAD", file, root)
|
||||||
|
const theirsDiff = await git.fileDiff(base, branch, file, root)
|
||||||
const content = await Bun.file(join(root, file)).text()
|
const content = await Bun.file(join(root, file)).text()
|
||||||
|
|
||||||
const resolved = await vm.claudePipe(
|
const context = [
|
||||||
|
`## Merge Context`,
|
||||||
|
`Merging branch \`${branch}\` into \`${current}\``,
|
||||||
|
``,
|
||||||
|
`### Commits on \`${branch}\`:`,
|
||||||
|
branchLog || "(no commits)",
|
||||||
|
``,
|
||||||
|
`### Overall changes on \`${branch}\`:`,
|
||||||
|
branchDiffStat || "(no changes)",
|
||||||
|
``,
|
||||||
|
`---`,
|
||||||
|
`## Conflicted File: ${file}`,
|
||||||
|
``,
|
||||||
|
`### Changes made on current branch (\`${current}\`):`,
|
||||||
|
oursDiff || "(no changes to this file)",
|
||||||
|
``,
|
||||||
|
`### Changes made on incoming branch (\`${branch}\`):`,
|
||||||
|
theirsDiff || "(no changes to this file)",
|
||||||
|
``,
|
||||||
|
`### File with conflict markers:`,
|
||||||
content,
|
content,
|
||||||
"resolve this merge conflict. output ONLY the resolved file content with no markdown fences, no explanation, no surrounding text.",
|
].join("\n")
|
||||||
|
|
||||||
|
const resolved = await vm.claudePipe(
|
||||||
|
context,
|
||||||
|
"Resolve the merge conflicts in the file shown at the end. Use the diffs and commit history to understand the intent of each side. Preserve all non-conflicting changes from both sides. Output ONLY the resolved file content — no markdown fences, no explanation, no surrounding text.",
|
||||||
)
|
)
|
||||||
|
|
||||||
if (resolved.exitCode !== 0) {
|
if (resolved.exitCode !== 0) {
|
||||||
|
|
|
||||||
27
src/git.ts
27
src/git.ts
|
|
@ -126,6 +126,33 @@ export async function isDirty(worktreePath: string): Promise<boolean> {
|
||||||
return result.text().trim().length > 0
|
return result.text().trim().length > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Find the merge base (common ancestor) between two refs. */
|
||||||
|
export async function mergeBase(ref1: string, ref2: string, cwd: string): Promise<string> {
|
||||||
|
const result = await $`git merge-base ${ref1} ${ref2}`.cwd(cwd).nothrow().quiet()
|
||||||
|
if (result.exitCode !== 0) {
|
||||||
|
throw new Error(`Could not find merge base between "${ref1}" and "${ref2}"`)
|
||||||
|
}
|
||||||
|
return result.text().trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get a one-line-per-commit log for a revision range. */
|
||||||
|
export async function log(range: string, cwd: string): Promise<string> {
|
||||||
|
const result = await $`git log --oneline ${range}`.cwd(cwd).nothrow().quiet()
|
||||||
|
return result.text().trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get a diff stat summary for a revision range. */
|
||||||
|
export async function diffStat(range: string, cwd: string): Promise<string> {
|
||||||
|
const result = await $`git diff --stat ${range}`.cwd(cwd).nothrow().quiet()
|
||||||
|
return result.text().trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get the diff for a specific file between two refs. */
|
||||||
|
export async function fileDiff(ref1: string, ref2: string, file: string, cwd: string): Promise<string> {
|
||||||
|
const result = await $`git diff ${ref1} ${ref2} -- ${file}`.cwd(cwd).nothrow().quiet()
|
||||||
|
return result.text().trim()
|
||||||
|
}
|
||||||
|
|
||||||
/** Check if a branch has commits beyond main. */
|
/** Check if a branch has commits beyond main. */
|
||||||
export async function hasNewCommits(worktreePath: string): Promise<boolean> {
|
export async function hasNewCommits(worktreePath: string): Promise<boolean> {
|
||||||
const main = await mainBranch(worktreePath)
|
const main = await mainBranch(worktreePath)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user