commit 87ec879def405f0bb1495135e7b69993e045f817 Author: Vitaly Date: Tue Sep 19 23:03:15 2023 +0300 introduce some kind of cherry pick scripts diff --git a/buildCommits.ts b/buildCommits.ts new file mode 100644 index 00000000..c210354b --- /dev/null +++ b/buildCommits.ts @@ -0,0 +1,161 @@ +import { + execSync as exec +} from 'child_process' +import fs from 'fs' + +const isOnGoingMode = process.argv[2] === 'on' + +const targetBranch = 'main-light' +// const sourceBranch = 'main' +const sourceBranch = 'origin/next' + +// show message of last commit on branch next +const lastTargetMessage = exec(`git log -2 --pretty=format:%s ${targetBranch}`).toString().split('\n').find(x => !x.startsWith('Merge ')) +if (!lastTargetMessage) throw new Error('no last target message') +const sourceCommits = exec(`git log --pretty=format:%h:%s ${sourceBranch}`).toString().split('\n') +// find the commit hash of the commit with the same message on branch + +type Config = { + parent: string, + hardcodedDropCommits: string[], + hardcodedRemoveFilesCommits: Record, +} + +const {hardcodedDropCommits, hardcodedRemoveFilesCommits, parent}: Config = { + parent: 'upstream/master' || process.argv[3], + hardcodedDropCommits: [], + hardcodedRemoveFilesCommits: {} +} + +const lines = exec(`git log --pretty=format:%h:%s --name-only ${sourceBranch}`).toString().split('\n') + +type Commit = { + hash: string + msg: string + files: string[] +} + +const commits: Commit[] = [] + +let isHeader = true +for (const line of lines) { + if (isHeader) { + const [hash, ...msg] = line.split(':') + const message = msg.join(':'); + if (message === lastTargetMessage) break + console.log('Picking', message) + commits.push({ hash, msg: message, files: [] }) + isHeader = false + continue + } + + if (line === '') { + isHeader = true + continue + } + + commits[commits.length - 1].files.push(line) +} + +const dropPaths = [ + 'prismarine-viewer/', + '.github/workflows/cherry-pick-upstream.yml', + '.github/workflows/sync-upstream.yml', +] + +const isFileChangeShouldBeIgnored = (file: string) => { + return dropPaths.some(path => file.startsWith(path)) +} + +let editCommits: string[] = [] +const dropCommits: string[] = [] +let newFeatures: string[] = [] +let newFixes: string[] = [] +const conventionalRegex = /(?:\[.+]\s)??(\w+)(\(\S+\))?:/g +for (const commit of commits) { + const fileShouldBeIgnored = (file) => { + if (isFileChangeShouldBeIgnored(file)) return true + if (hardcodedRemoveFilesCommits[commit.hash]?.includes(file)) return true + } + + const forcefullyDrop = hardcodedDropCommits.includes(commit.hash); + if (forcefullyDrop || commit.files.some(fileShouldBeIgnored)) { + if (forcefullyDrop || commit.files.every(fileShouldBeIgnored)) { + console.log('drop', commit.msg, commit.files) + dropCommits.push(commit.hash) + } else { + console.log('edit', commit.msg, commit.files.filter(file => fileShouldBeIgnored(file))) + editCommits.push(commit.hash) + } + } + + // for of matches + const matches = [...commit.msg.matchAll(conventionalRegex)] + for (const [i, match] of matches.entries()) { + const [, type, scope] = match + const nextMatchI = matches[i + 1]?.index ?? commit.msg.length + const message = commit.msg.slice(match.index, nextMatchI) + if (type === 'feat') { + newFeatures.push(message) + } else if (type === 'fix') { + newFixes.push(message) + } + } +} + +console.log(commits, dropCommits, editCommits) + +for (const commit of commits) { + if (dropCommits.includes(commit.hash)) continue + if (editCommits.includes(commit.hash)) { + throw new Error('not implemented') + // exec('git cherry-pick --no-commit ' + commit.hash) + continue + } + exec('git cherry-pick ' + commit.hash) +} + +throw new Error('stop') + +// dropCommits.reverse() + +// let actions = '' +// for (const commit of dropCommits) { +// actions += `drop ${commit}\n` +// } + +// const dropPathsPerCommit = editCommits.reduce((acc, commit) => { +// acc[commit] = commits.find(c => c.hash === commit)!.files.filter(isFileChangeShouldBeIgnored) +// return acc +// }, {}) + +// // const commandsExec = [] +// const filesToRemove = editCommits.map(commit => dropPathsPerCommit[commit]) + +// for (const commit of editCommits) { +// // actions += `edit ${commit}\n` +// const filesToRemove = dropPathsPerCommit[commit] +// // commandsExec.push([...filesToRemove.map(file => `rm ${file}`), 'git add .', 'git rebase --continue'].join(' && ')) +// } +// if (!isOnGoingMode) { +// const newActions = [...editCommits.map(editCommit => `edit ${editCommit}`), ...dropCommits.map(dropCommit => `drop ${dropCommit}`)].join('\n') + '\n' +// fs.writeFileSync('./actions', newActions, 'utf8') +// exec(`git rebase -i --rebase-merges ${parent}`, { +// env: { +// GIT_SEQUENCE_EDITOR: 'tsx replaceScript.ts', +// }, +// }) +// } + +// for (const commit of filesToRemove) { +// const headCommit = fs.readFileSync('./.git/HEAD', 'utf8').trim().slice(0, 7) +// const files = dropPathsPerCommit[headCommit] +// console.log('removing', files.join(', ')) +// files.forEach(file => fs.unlinkSync(file)) +// exec('git add -u') +// exec('git rebase --continue', { +// env: { +// GIT_EDITOR: 'true', +// }, +// }) +// } diff --git a/replaceScript.ts b/replaceScript.ts new file mode 100644 index 00000000..910c88ae --- /dev/null +++ b/replaceScript.ts @@ -0,0 +1,15 @@ +import fs from 'fs' + +let file = fs.readFileSync('./.git/rebase-merge/git-rebase-todo', 'utf8') + +const actions = fs.readFileSync('./actions', 'utf8') + +actions.split('\n').forEach(action => { + const [type, commit] = action.split(' ') + // if (!file.includes(`pick ${commit}`)) throw new Error(`Commit ${commit} not found`) + file = file.replace(`pick ${commit}`, `${type} ${commit}`) +}) + +fs.writeFileSync('./actions2', file, 'utf8') + +fs.writeFileSync('./.git/rebase-merge/git-rebase-todo', file, 'utf8')