|
|
|
@ -1,6 +1,7 @@
|
|
|
|
#!/usr/bin/env node
|
|
|
|
#!/usr/bin/env node
|
|
|
|
|
|
|
|
|
|
|
|
const { execSync } = require('child_process');
|
|
|
|
const readline = require('readline');
|
|
|
|
|
|
|
|
const { spawnSync } = require('child_process');
|
|
|
|
|
|
|
|
|
|
|
|
const COLORS = {
|
|
|
|
const COLORS = {
|
|
|
|
red: '\x1b[31m',
|
|
|
|
red: '\x1b[31m',
|
|
|
|
@ -10,116 +11,336 @@ const COLORS = {
|
|
|
|
reset: '\x1b[0m'
|
|
|
|
reset: '\x1b[0m'
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class MergeConflictError extends Error {
|
|
|
|
|
|
|
|
constructor(branchName) {
|
|
|
|
|
|
|
|
super(`合并到 ${branchName} 时发生冲突`);
|
|
|
|
|
|
|
|
this.name = 'MergeConflictError';
|
|
|
|
|
|
|
|
this.branchName = branchName;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function log(message, color = 'reset') {
|
|
|
|
function log(message, color = 'reset') {
|
|
|
|
console.log(`${COLORS[color]}${message}${COLORS.reset}`);
|
|
|
|
console.log(`${COLORS[color]}${message}${COLORS.reset}`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function exec(command, options = {}) {
|
|
|
|
function runCommand(command, args, options = {}) {
|
|
|
|
try {
|
|
|
|
const { silent = false, ignoreError = false } = options;
|
|
|
|
return execSync(command, { encoding: 'utf-8', stdio: options.silent ? 'pipe' : 'inherit', ...options });
|
|
|
|
const result = spawnSync(command, args, {
|
|
|
|
} catch (error) {
|
|
|
|
encoding: 'utf-8',
|
|
|
|
if (options.ignoreError) {
|
|
|
|
stdio: silent ? ['inherit', 'pipe', 'pipe'] : 'inherit'
|
|
|
|
return error.stdout || '';
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (result.error) {
|
|
|
|
|
|
|
|
throw result.error;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (result.status !== 0) {
|
|
|
|
|
|
|
|
if (ignoreError) {
|
|
|
|
|
|
|
|
return result.stdout || '';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const error = new Error(`${command} ${args.join(' ')} failed`);
|
|
|
|
|
|
|
|
error.status = result.status;
|
|
|
|
|
|
|
|
error.stdout = result.stdout || '';
|
|
|
|
|
|
|
|
error.stderr = result.stderr || '';
|
|
|
|
throw error;
|
|
|
|
throw error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return result.stdout || '';
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function git(args, options = {}) {
|
|
|
|
|
|
|
|
return runCommand('git', args, options);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function createPrompt() {
|
|
|
|
|
|
|
|
const rl = readline.createInterface({
|
|
|
|
|
|
|
|
input: process.stdin,
|
|
|
|
|
|
|
|
output: process.stdout
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
|
|
ask(question) {
|
|
|
|
|
|
|
|
return new Promise(resolve => {
|
|
|
|
|
|
|
|
rl.question(question, answer => resolve(answer.trim()));
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
close() {
|
|
|
|
|
|
|
|
rl.close();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function run() {
|
|
|
|
function getCurrentBranch() {
|
|
|
|
log('\n========================================', 'cyan');
|
|
|
|
return git(['branch', '--show-current'], { silent: true }).trim();
|
|
|
|
log(' 合并 master 到 production 分支', 'cyan');
|
|
|
|
}
|
|
|
|
log('========================================\n', 'cyan');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 获取当前分支名
|
|
|
|
function getLocalBranches() {
|
|
|
|
const currentBranch = exec('git branch --show-current', { silent: true }).trim();
|
|
|
|
return git(['branch', '--format=%(refname:short)'], { silent: true })
|
|
|
|
log(`当前分支: ${currentBranch}\n`, 'cyan');
|
|
|
|
.split(/\r?\n/)
|
|
|
|
|
|
|
|
.map(item => item.trim())
|
|
|
|
|
|
|
|
.filter(Boolean);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function hasRemoteBranch(branchName) {
|
|
|
|
|
|
|
|
const output = git(['rev-parse', '--verify', '--quiet', `refs/remotes/origin/${branchName}`], {
|
|
|
|
|
|
|
|
silent: true,
|
|
|
|
|
|
|
|
ignoreError: true
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 暂存未提交的更改
|
|
|
|
return output.trim().length > 0;
|
|
|
|
const status = exec('git status --porcelain', { silent: true });
|
|
|
|
}
|
|
|
|
const hasChanges = status.trim().length > 0;
|
|
|
|
|
|
|
|
if (hasChanges) {
|
|
|
|
function stashChanges(state) {
|
|
|
|
log('>>> 暂存未提交的更改...', 'yellow');
|
|
|
|
const status = git(['status', '--porcelain'], { silent: true });
|
|
|
|
exec('git stash push -m "暂存未提交的更改"');
|
|
|
|
state.hasChanges = status.trim().length > 0;
|
|
|
|
log('已暂存\n', 'green');
|
|
|
|
|
|
|
|
|
|
|
|
if (!state.hasChanges) {
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 拉取最新代码
|
|
|
|
log('>>> 暂存未提交的改动...', 'yellow');
|
|
|
|
log('>>> 拉取远程最新代码...', 'yellow');
|
|
|
|
git(['stash', 'push', '-m', 'auto-stash-before-merge']);
|
|
|
|
try {
|
|
|
|
state.stashCreated = true;
|
|
|
|
exec('git fetch origin');
|
|
|
|
log('已暂存\n', 'green');
|
|
|
|
} catch (error) {
|
|
|
|
}
|
|
|
|
log('\n错误: 拉取远程代码失败', 'red');
|
|
|
|
|
|
|
|
process.exit(1);
|
|
|
|
function restoreWorkspace(state) {
|
|
|
|
|
|
|
|
if (state.currentBranch && getCurrentBranch() !== state.currentBranch) {
|
|
|
|
|
|
|
|
log(`>>> 切换回原分支 ${state.currentBranch}...`, 'yellow');
|
|
|
|
|
|
|
|
git(['checkout', state.currentBranch], { ignoreError: true });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (state.stashCreated) {
|
|
|
|
|
|
|
|
log('>>> 恢复暂存的改动...', 'yellow');
|
|
|
|
|
|
|
|
git(['stash', 'pop'], { ignoreError: true });
|
|
|
|
|
|
|
|
log('已恢复\n', 'green');
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function fetchOrigin() {
|
|
|
|
|
|
|
|
log('>>> 拉取远程最新信息...', 'yellow');
|
|
|
|
|
|
|
|
git(['fetch', 'origin']);
|
|
|
|
log('拉取完成\n', 'green');
|
|
|
|
log('拉取完成\n', 'green');
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 切换到 master 并更新
|
|
|
|
function checkoutBranch(branchName) {
|
|
|
|
log('>>> 切换到 master 分支并更新...', 'yellow');
|
|
|
|
log(`>>> 切换到 ${branchName} 分支...`, 'yellow');
|
|
|
|
try {
|
|
|
|
git(['checkout', branchName]);
|
|
|
|
exec('git checkout master');
|
|
|
|
log(`已切换到 ${branchName}\n`, 'green');
|
|
|
|
exec('git pull origin master');
|
|
|
|
}
|
|
|
|
} catch (error) {
|
|
|
|
|
|
|
|
log('\n错误: 更新 master 分支失败', 'red');
|
|
|
|
function pullBranchIfNeeded(branchName) {
|
|
|
|
process.exit(1);
|
|
|
|
if (!hasRemoteBranch(branchName)) {
|
|
|
|
|
|
|
|
log(`>>> 本地分支 ${branchName} 未关联 origin/${branchName},跳过 pull`, 'yellow');
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
log('master 分支已更新\n', 'green');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 切换到 production 并更新
|
|
|
|
log(`>>> 更新 ${branchName} 分支...`, 'yellow');
|
|
|
|
log('>>> 切换到 production 分支并更新...', 'yellow');
|
|
|
|
git(['pull', 'origin', branchName]);
|
|
|
|
try {
|
|
|
|
log(`${branchName} 已更新\n`, 'green');
|
|
|
|
exec('git checkout production');
|
|
|
|
}
|
|
|
|
exec('git pull origin production');
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
function pushBranchIfNeeded(branchName) {
|
|
|
|
log('\n错误: 更新 production 分支失败,分支可能不存在', 'red');
|
|
|
|
if (!hasRemoteBranch(branchName)) {
|
|
|
|
log('请先创建 production 分支: git checkout -b production\n', 'yellow');
|
|
|
|
log(`>>> 本地分支 ${branchName} 未关联 origin/${branchName},跳过 push`, 'yellow');
|
|
|
|
exec(`git checkout ${currentBranch}`, { ignoreError: true });
|
|
|
|
return;
|
|
|
|
process.exit(1);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
log('production 分支已更新\n', 'green');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 合并 master 到 production
|
|
|
|
log(`>>> 推送 ${branchName} 到远程...`, 'yellow');
|
|
|
|
log('>>> 合并 master 到 production...', 'yellow');
|
|
|
|
git(['push', 'origin', branchName]);
|
|
|
|
|
|
|
|
log(`${branchName} 推送成功\n`, 'green');
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function printConflictGuidance(branchName) {
|
|
|
|
|
|
|
|
log(`\n错误: 合并到 ${branchName} 时发生冲突`, 'red');
|
|
|
|
|
|
|
|
log('请手动解决冲突后再继续', 'red');
|
|
|
|
|
|
|
|
log('\n冲突文件:', 'yellow');
|
|
|
|
|
|
|
|
git(['diff', '--name-only', '--diff-filter=U'], { ignoreError: true });
|
|
|
|
|
|
|
|
log('\n解决冲突后可继续执行:', 'yellow');
|
|
|
|
|
|
|
|
log(' git add .', 'cyan');
|
|
|
|
|
|
|
|
log(' git commit', 'cyan');
|
|
|
|
|
|
|
|
log(` git push origin ${branchName}\n`, 'cyan');
|
|
|
|
|
|
|
|
log('如需放弃本次合并:', 'yellow');
|
|
|
|
|
|
|
|
log(' git merge --abort\n', 'cyan');
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function mergeBranch(sourceBranch, targetBranch, state) {
|
|
|
|
|
|
|
|
log(`>>> 合并 ${sourceBranch} -> ${targetBranch}...`, 'yellow');
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
try {
|
|
|
|
exec('git merge master --no-edit');
|
|
|
|
git(['merge', sourceBranch, '--no-edit']);
|
|
|
|
} catch (error) {
|
|
|
|
} catch (error) {
|
|
|
|
log('\n错误: 合并过程中发生冲突!', 'red');
|
|
|
|
state.shouldRestoreWorkspace = false;
|
|
|
|
log('请手动解决冲突后再提交', 'red');
|
|
|
|
throw new MergeConflictError(targetBranch);
|
|
|
|
log('\n冲突文件:', 'yellow');
|
|
|
|
|
|
|
|
exec('git diff --name-only --diff-filter=U', { ignoreError: true });
|
|
|
|
|
|
|
|
log('\n解决冲突后运行:', 'yellow');
|
|
|
|
|
|
|
|
log(' git add .', 'cyan');
|
|
|
|
|
|
|
|
log(' git commit', 'cyan');
|
|
|
|
|
|
|
|
log(' git push origin production\n', 'cyan');
|
|
|
|
|
|
|
|
log('或放弃合并:', 'yellow');
|
|
|
|
|
|
|
|
log(' git merge --abort\n', 'cyan');
|
|
|
|
|
|
|
|
process.exit(1);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
log('合并成功\n', 'green');
|
|
|
|
log('合并成功\n', 'green');
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 推送到远程
|
|
|
|
function printMenu(title, options) {
|
|
|
|
log('>>> 推送到远程 production 分支...', 'yellow');
|
|
|
|
log(`\n${title}`, 'cyan');
|
|
|
|
try {
|
|
|
|
options.forEach((option, index) => {
|
|
|
|
exec('git push origin production');
|
|
|
|
console.log(` ${index + 1}. ${option.label}`);
|
|
|
|
} catch (error) {
|
|
|
|
});
|
|
|
|
log('\n错误: 推送失败', 'red');
|
|
|
|
console.log('');
|
|
|
|
process.exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async function selectOption(prompt, title, options) {
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
|
|
|
|
printMenu(title, options);
|
|
|
|
|
|
|
|
const answer = await prompt.ask('请输入编号并回车: ');
|
|
|
|
|
|
|
|
const index = Number(answer);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (Number.isInteger(index) && index >= 1 && index <= options.length) {
|
|
|
|
|
|
|
|
return options[index - 1];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
log('输入无效,请重新选择。\n', 'red');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
log('推送成功\n', 'green');
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 切换回原分支
|
|
|
|
async function selectBranch(prompt, title, branches, currentBranch) {
|
|
|
|
log(`>>> 切换回原分支 ${currentBranch}...`, 'yellow');
|
|
|
|
const options = branches.map(branchName => ({
|
|
|
|
exec(`git checkout ${currentBranch}`, { ignoreError: true });
|
|
|
|
value: branchName,
|
|
|
|
|
|
|
|
label: branchName === currentBranch ? `${branchName} (当前分支)` : branchName
|
|
|
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
|
|
// 恢复暂存的更改
|
|
|
|
const selected = await selectOption(prompt, title, options);
|
|
|
|
if (hasChanges) {
|
|
|
|
return selected.value;
|
|
|
|
log('>>> 恢复暂存的更改...', 'yellow');
|
|
|
|
}
|
|
|
|
exec('git stash pop', { ignoreError: true });
|
|
|
|
|
|
|
|
log('已恢复\n', 'green');
|
|
|
|
async function confirm(prompt, message) {
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
|
|
|
|
const answer = (await prompt.ask(`${message} (y/n): `)).toLowerCase();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (answer === 'y' || answer === 'yes') {
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (answer === 'n' || answer === 'no') {
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
log('请输入 y 或 n。\n', 'red');
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async function runSingleBranchMerge(prompt, state) {
|
|
|
|
|
|
|
|
const branches = getLocalBranches();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (branches.length < 2) {
|
|
|
|
|
|
|
|
throw new Error('本地分支数量不足,无法执行单分支合并');
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const sourceBranch = await selectBranch(prompt, '请选择来源分支', branches, state.currentBranch);
|
|
|
|
|
|
|
|
const targetCandidates = branches.filter(branchName => branchName !== sourceBranch);
|
|
|
|
|
|
|
|
const targetBranch = await selectBranch(prompt, '请选择目标分支', targetCandidates, state.currentBranch);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
log(`\n将执行: ${sourceBranch} -> ${targetBranch}\n`, 'cyan');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const accepted = await confirm(prompt, '确认开始合并吗');
|
|
|
|
|
|
|
|
if (!accepted) {
|
|
|
|
|
|
|
|
log('已取消操作', 'yellow');
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
stashChanges(state);
|
|
|
|
|
|
|
|
fetchOrigin();
|
|
|
|
|
|
|
|
checkoutBranch(targetBranch);
|
|
|
|
|
|
|
|
pullBranchIfNeeded(targetBranch);
|
|
|
|
|
|
|
|
mergeBranch(sourceBranch, targetBranch, state);
|
|
|
|
|
|
|
|
pushBranchIfNeeded(targetBranch);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
log('\n========================================', 'green');
|
|
|
|
|
|
|
|
log(` 已完成 ${sourceBranch} -> ${targetBranch} 合并`, 'green');
|
|
|
|
|
|
|
|
log('========================================\n', 'green');
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async function runDeployMerge(prompt, state) {
|
|
|
|
|
|
|
|
if (state.currentBranch === 'production') {
|
|
|
|
|
|
|
|
throw new Error('当前分支为 production,不能执行部署合并');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
log(`\n将执行部署合并: ${state.currentBranch} -> master -> production\n`, 'cyan');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const accepted = await confirm(prompt, '确认开始部署合并吗');
|
|
|
|
|
|
|
|
if (!accepted) {
|
|
|
|
|
|
|
|
log('已取消操作', 'yellow');
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
stashChanges(state);
|
|
|
|
|
|
|
|
fetchOrigin();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
checkoutBranch('master');
|
|
|
|
|
|
|
|
pullBranchIfNeeded('master');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (state.currentBranch !== 'master') {
|
|
|
|
|
|
|
|
mergeBranch(state.currentBranch, 'master', state);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
log('当前分支已是 master,跳过 feature -> master 合并\n', 'yellow');
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pushBranchIfNeeded('master');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
checkoutBranch('production');
|
|
|
|
|
|
|
|
pullBranchIfNeeded('production');
|
|
|
|
|
|
|
|
mergeBranch('master', 'production', state);
|
|
|
|
|
|
|
|
pushBranchIfNeeded('production');
|
|
|
|
|
|
|
|
|
|
|
|
log('\n========================================', 'green');
|
|
|
|
log('\n========================================', 'green');
|
|
|
|
log(' master 已成功合并到 production!', 'green');
|
|
|
|
log(` 已完成 ${state.currentBranch} -> master -> production`, 'green');
|
|
|
|
log('========================================\n', 'green');
|
|
|
|
log('========================================\n', 'green');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async function run() {
|
|
|
|
|
|
|
|
const prompt = createPrompt();
|
|
|
|
|
|
|
|
const state = {
|
|
|
|
|
|
|
|
currentBranch: '',
|
|
|
|
|
|
|
|
hasChanges: false,
|
|
|
|
|
|
|
|
stashCreated: false,
|
|
|
|
|
|
|
|
shouldRestoreWorkspace: true
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
log('\n========================================', 'cyan');
|
|
|
|
|
|
|
|
log(' Git 分支合并工具', 'cyan');
|
|
|
|
|
|
|
|
log('========================================\n', 'cyan');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
state.currentBranch = getCurrentBranch();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!state.currentBranch) {
|
|
|
|
|
|
|
|
throw new Error('当前不在任何本地分支上,无法执行合并');
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
log(`当前分支: ${state.currentBranch}\n`, 'cyan');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const action = await selectOption(prompt, '请选择操作类型', [
|
|
|
|
|
|
|
|
{ label: '单分支合并', value: 'single' },
|
|
|
|
|
|
|
|
{ label: '部署合并(当前分支 -> master -> production)', value: 'deploy' }
|
|
|
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (action.value === 'single') {
|
|
|
|
|
|
|
|
await runSingleBranchMerge(prompt, state);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
await runDeployMerge(prompt, state);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
|
|
if (error instanceof MergeConflictError) {
|
|
|
|
|
|
|
|
printConflictGuidance(error.branchName);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
log(`\n错误: ${error.message}\n`, 'red');
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
process.exitCode = 1;
|
|
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
|
|
prompt.close();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (state.shouldRestoreWorkspace) {
|
|
|
|
|
|
|
|
restoreWorkspace(state);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
run();
|
|
|
|
run();
|
|
|
|
|