121 lines
3.4 KiB
TypeScript
121 lines
3.4 KiB
TypeScript
import { execSync } from 'child_process'
|
|
import { readFileSync, writeFileSync } from 'fs'
|
|
|
|
function run(cmd: string): string {
|
|
try {
|
|
return execSync(cmd, { encoding: 'utf-8', env: { ...process.env, GIT_EDITOR: 'true' } })
|
|
} catch (e: any) {
|
|
return (e.stdout || '') + (e.stderr || '')
|
|
}
|
|
}
|
|
|
|
function getConflictedFiles(): string[] {
|
|
const out = run('git diff --name-only --diff-filter=U')
|
|
return out.trim().split('\n').filter(Boolean)
|
|
}
|
|
|
|
function resolveConflictMarkers(content: string): string {
|
|
if (!content.includes('<<<<<<<')) return content
|
|
const lines = content.split('\n')
|
|
const result: string[] = []
|
|
let state: 'normal' | 'ours' | 'theirs' = 'normal'
|
|
let oursLines: string[] = []
|
|
let theirsLines: string[] = []
|
|
|
|
for (const line of lines) {
|
|
if (line.startsWith('<<<<<<<')) {
|
|
state = 'ours'
|
|
oursLines = []
|
|
theirsLines = []
|
|
} else if (line === '=======') {
|
|
state = 'theirs'
|
|
} else if (line.startsWith('>>>>>>>')) {
|
|
state = 'normal'
|
|
result.push(...oursLines, ...theirsLines)
|
|
} else if (state === 'ours') {
|
|
oursLines.push(line)
|
|
} else if (state === 'theirs') {
|
|
theirsLines.push(line)
|
|
} else {
|
|
result.push(line)
|
|
}
|
|
}
|
|
return result.join('\n')
|
|
}
|
|
|
|
function resolveConflicts(): boolean {
|
|
const files = getConflictedFiles()
|
|
if (files.length === 0) return true
|
|
console.log(` Conflicted files: ${files.join(', ')}`)
|
|
|
|
for (const file of files) {
|
|
if (file === 'bun.lock') {
|
|
run(`git checkout --theirs "${file}"`)
|
|
} else {
|
|
try {
|
|
const content = readFileSync(file, 'utf-8')
|
|
const resolved = resolveConflictMarkers(content)
|
|
writeFileSync(file, resolved, 'utf-8')
|
|
} catch {
|
|
run(`git checkout --theirs "${file}"`)
|
|
}
|
|
}
|
|
run(`git add "${file}"`)
|
|
}
|
|
return true
|
|
}
|
|
|
|
console.log('Starting automated rebase...\n')
|
|
|
|
let conflictCount = 0
|
|
let iterations = 0
|
|
const maxIterations = 300
|
|
|
|
// Start the rebase
|
|
let output = run('git rebase origin/main')
|
|
console.log(output.substring(0, 500))
|
|
|
|
while (iterations++ < maxIterations) {
|
|
const status = run('git status')
|
|
|
|
if (!status.includes('rebasing') && !status.includes('rebase')) {
|
|
console.log('\nRebase completed!')
|
|
break
|
|
}
|
|
|
|
const conflicted = getConflictedFiles()
|
|
if (conflicted.length > 0) {
|
|
conflictCount++
|
|
const commitMatch = status.match(/Could not apply (\w+)/)
|
|
const commitInfo = commitMatch ? commitMatch[1] : 'unknown'
|
|
console.log(`\n[${iterations}] Conflict #${conflictCount} (${commitInfo}): ${conflicted.join(', ')}`)
|
|
|
|
// Handle delete/modify conflicts
|
|
for (const file of conflicted) {
|
|
const diffOutput = run(`git diff --diff-filter=D -- "${file}"`)
|
|
if (diffOutput.includes('deleted in')) {
|
|
// If file was deleted in the commit being applied, accept the deletion
|
|
run(`git rm "${file}" 2>/dev/null || git checkout --theirs "${file}" && git add "${file}"`)
|
|
}
|
|
}
|
|
|
|
resolveConflicts()
|
|
}
|
|
|
|
output = run('git rebase --continue')
|
|
if (output.includes('successfully rebased')) {
|
|
console.log('\nRebase completed successfully!')
|
|
break
|
|
}
|
|
if (output.includes('CONFLICT') || output.includes('could not apply')) {
|
|
continue
|
|
}
|
|
if (output.includes('fatal') || output.includes('error: failed')) {
|
|
console.error('Fatal error:', output)
|
|
break
|
|
}
|
|
console.log(output.substring(0, 300))
|
|
}
|
|
|
|
console.log(`\nTotal conflicts resolved: ${conflictCount}`)
|