@icode-js/icode 3.1.0 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +105 -5
- package/package.json +1 -1
- package/src/cli.js +6 -0
- package/src/commands/clean.js +4 -1
- package/src/commands/codereview.js +4 -1
- package/src/commands/completion.js +9 -6
- package/src/commands/config.js +9 -6
- package/src/commands/doctor.js +80 -0
- package/src/commands/explain.js +4 -1
- package/src/commands/help.js +10 -0
- package/src/commands/info.js +4 -1
- package/src/commands/migrate.js +38 -1
- package/src/commands/push.js +45 -18
- package/src/commands/ready.js +110 -0
- package/src/commands/save.js +98 -0
- package/src/commands/undo.js +13 -1
- package/src/core/ai/diff-prompt.js +204 -0
- package/src/core/cli/args.js +23 -2
- package/src/core/cli/completion.js +33 -0
- package/src/core/config/store.js +2 -4
- package/src/core/git/context.js +14 -15
- package/src/core/git/preconditions.js +88 -0
- package/src/core/git/service.js +151 -1
- package/src/core/remote-merge/providers/gitlab.js +149 -90
- package/src/core/tools/interactive.js +209 -34
- package/src/core/tools/loading.js +5 -1
- package/src/core/tools/logger.js +5 -0
- package/src/workflows/ai-codereview-workflow.js +14 -2
- package/src/workflows/ai-commit-workflow.js +14 -6
- package/src/workflows/checkout-workflow.js +14 -2
- package/src/workflows/clean-workflow.js +87 -70
- package/src/workflows/doctor-workflow.js +131 -0
- package/src/workflows/migrate-workflow.js +265 -58
- package/src/workflows/push-workflow.js +232 -50
- package/src/workflows/ready-workflow.js +278 -0
- package/src/workflows/rollback-workflow.js +20 -5
- package/src/workflows/save-workflow.js +152 -0
- package/src/workflows/sync-workflow.js +18 -2
- package/src/workflows/undo-workflow.js +25 -7
|
@@ -6,14 +6,17 @@ import { logger } from '../core/tools/logger.js'
|
|
|
6
6
|
|
|
7
7
|
const INTERACTIVE_COMMIT_LIMIT = 30
|
|
8
8
|
|
|
9
|
+
// 归一化分支名,输入为用户参数,输出去除首尾空白后的分支名。
|
|
9
10
|
function normalizeBranchName(value) {
|
|
10
11
|
return (value || '').trim()
|
|
11
12
|
}
|
|
12
13
|
|
|
14
|
+
// 归一化提交范围,输入为用户参数,输出去除首尾空白后的 range 表达式。
|
|
13
15
|
function normalizeRangeSpec(value) {
|
|
14
16
|
return (value || '').trim()
|
|
15
17
|
}
|
|
16
18
|
|
|
19
|
+
// 构建最近 N 条提交的交互选项,输入最大提交数,输出可供 chooseOne 使用的选项。
|
|
17
20
|
function buildRecentCountChoices(maxCount) {
|
|
18
21
|
const candidates = [1, 2, 3, 5, 8, 10]
|
|
19
22
|
.filter((count) => count <= maxCount)
|
|
@@ -32,10 +35,12 @@ function buildRecentCountChoices(maxCount) {
|
|
|
32
35
|
return candidates
|
|
33
36
|
}
|
|
34
37
|
|
|
38
|
+
// 去重并排序分支候选,输入本地/远程分支列表,输出稳定顺序的分支名数组。
|
|
35
39
|
function uniqueBranches(branches) {
|
|
36
40
|
return Array.from(new Set((branches || []).map((item) => item.trim()).filter(Boolean))).sort((a, b) => a.localeCompare(b))
|
|
37
41
|
}
|
|
38
42
|
|
|
43
|
+
// 读取迁移交互模式的分支候选,输入 GitService,输出本地与 origin 远程分支名。
|
|
39
44
|
async function listBranchCandidates(git) {
|
|
40
45
|
const [localBranches, remoteBranches] = await Promise.all([
|
|
41
46
|
git.listLocalBranches(),
|
|
@@ -44,6 +49,7 @@ async function listBranchCandidates(git) {
|
|
|
44
49
|
return uniqueBranches([...localBranches, ...remoteBranches])
|
|
45
50
|
}
|
|
46
51
|
|
|
52
|
+
// 选择 source/target 分支,输入候选和默认值,输出用户选择的分支名。
|
|
47
53
|
async function pickBranch({ label, candidates, defaultValue, excludedBranch = '' }) {
|
|
48
54
|
const excluded = normalizeBranchName(excludedBranch)
|
|
49
55
|
const normalizedDefault = normalizeBranchName(defaultValue)
|
|
@@ -82,6 +88,7 @@ async function pickBranch({ label, candidates, defaultValue, excludedBranch = ''
|
|
|
82
88
|
return normalizeBranchName(selected)
|
|
83
89
|
}
|
|
84
90
|
|
|
91
|
+
// 手动选择要 cherry-pick 的提交,输入提交 hash 列表,输出被选择的 hash 列表。
|
|
85
92
|
async function pickManualCommits(git, commits) {
|
|
86
93
|
const displayCommits = commits.length > INTERACTIVE_COMMIT_LIMIT
|
|
87
94
|
? commits.slice(-INTERACTIVE_COMMIT_LIMIT)
|
|
@@ -109,12 +116,13 @@ async function pickManualCommits(git, commits) {
|
|
|
109
116
|
return Array.isArray(selected) ? selected : []
|
|
110
117
|
}
|
|
111
118
|
|
|
119
|
+
// 解析交互模式迁移计划,输入 GitService 和初始参数,输出 source/target/range/selectedCommits。
|
|
112
120
|
async function resolveInteractivePlan(git, options) {
|
|
113
121
|
const candidates = await listBranchCandidates(git)
|
|
114
122
|
const sourceBranch = await pickBranch({
|
|
115
123
|
label: 'source 分支',
|
|
116
124
|
candidates,
|
|
117
|
-
defaultValue: options.sourceBranch
|
|
125
|
+
defaultValue: options.sourceBranch || options.currentBranch
|
|
118
126
|
})
|
|
119
127
|
|
|
120
128
|
const targetBranch = await pickBranch({
|
|
@@ -260,6 +268,189 @@ async function resolveInteractivePlan(git, options) {
|
|
|
260
268
|
}
|
|
261
269
|
}
|
|
262
270
|
|
|
271
|
+
// 静默模式下输出 info 日志,输入执行选项和日志文本,输出为空。
|
|
272
|
+
function logInfo(options, message) {
|
|
273
|
+
if (!options.silentLog) {
|
|
274
|
+
logger.info(message)
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// 静默模式下输出 warn 日志,输入执行选项和日志文本,输出为空。
|
|
279
|
+
function logWarn(options, message) {
|
|
280
|
+
if (!options.silentLog) {
|
|
281
|
+
logger.warn(message)
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// 检查 migrate 执行前置条件,输入 GitService,输出当前分支和改动摘要。
|
|
286
|
+
async function assertMigratePreconditions(git) {
|
|
287
|
+
const [currentBranch, operation, changes] = await Promise.all([
|
|
288
|
+
git.getCurrentBranch(),
|
|
289
|
+
git.getInProgressOperation(),
|
|
290
|
+
git.getStatusSummary()
|
|
291
|
+
])
|
|
292
|
+
|
|
293
|
+
if (!currentBranch) {
|
|
294
|
+
throw new IcodeError('当前不在普通分支上,migrate 已停止。', {
|
|
295
|
+
code: 'MIGRATE_DETACHED_HEAD',
|
|
296
|
+
exitCode: 2
|
|
297
|
+
})
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (operation) {
|
|
301
|
+
throw new IcodeError(`检测到未完成的 Git 操作: ${operation}。请先完成或中止该操作。`, {
|
|
302
|
+
code: 'MIGRATE_GIT_OPERATION_IN_PROGRESS',
|
|
303
|
+
exitCode: 2,
|
|
304
|
+
meta: {
|
|
305
|
+
operation
|
|
306
|
+
}
|
|
307
|
+
})
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
if (!changes.clean) {
|
|
311
|
+
throw new IcodeError('工作区存在未提交改动,migrate 已停止。请先执行 save 或手动清理。', {
|
|
312
|
+
code: 'MIGRATE_DIRTY_WORKTREE',
|
|
313
|
+
exitCode: 2,
|
|
314
|
+
meta: {
|
|
315
|
+
changes
|
|
316
|
+
}
|
|
317
|
+
})
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
return {
|
|
321
|
+
currentBranch,
|
|
322
|
+
changes
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// 将本地/远程分支存在性解析为可用于 rev-list 的 ref,输入分支名和存在性,输出明确 ref。
|
|
327
|
+
function resolveBranchRef(branchName, existsLocal, existsRemote, label) {
|
|
328
|
+
if (existsLocal) {
|
|
329
|
+
return branchName
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
if (existsRemote) {
|
|
333
|
+
return `origin/${branchName}`
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
throw new IcodeError(`${label} 分支不存在: ${branchName}`, {
|
|
337
|
+
code: label === 'source' ? 'MIGRATE_SOURCE_MISSING' : 'MIGRATE_TARGET_MISSING',
|
|
338
|
+
exitCode: 2
|
|
339
|
+
})
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// 构建提交展示计划,输入提交 hash 列表,输出 hash 与提交摘要列表。
|
|
343
|
+
async function buildCommitPlan(git, commits) {
|
|
344
|
+
const plan = []
|
|
345
|
+
|
|
346
|
+
for (const commitHash of commits) {
|
|
347
|
+
plan.push({
|
|
348
|
+
hash: commitHash,
|
|
349
|
+
summary: await git.showCommitSummary(commitHash)
|
|
350
|
+
})
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
return plan
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// 构建 migrate 执行步骤,输入计划上下文,输出人类和 agent 都能读取的步骤数组。
|
|
357
|
+
function buildMigrateSteps(plan) {
|
|
358
|
+
const steps = [
|
|
359
|
+
{
|
|
360
|
+
action: 'checkout-target',
|
|
361
|
+
detail: plan.targetExistsLocal
|
|
362
|
+
? `切换到本地目标分支 ${plan.targetBranch}`
|
|
363
|
+
: `从 origin/${plan.targetBranch} 创建 tracking 分支`
|
|
364
|
+
}
|
|
365
|
+
]
|
|
366
|
+
|
|
367
|
+
if (plan.targetExistsRemote) {
|
|
368
|
+
steps.push({
|
|
369
|
+
action: 'pull-target',
|
|
370
|
+
detail: `同步 origin/${plan.targetBranch} 到目标分支`
|
|
371
|
+
})
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
steps.push({
|
|
375
|
+
action: 'cherry-pick',
|
|
376
|
+
detail: `迁移 ${plan.commits.length} 个提交`
|
|
377
|
+
})
|
|
378
|
+
|
|
379
|
+
if (plan.push) {
|
|
380
|
+
steps.push({
|
|
381
|
+
action: 'push-target',
|
|
382
|
+
detail: `推送目标分支 ${plan.targetBranch}`
|
|
383
|
+
})
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
if (plan.originalBranch && plan.originalBranch !== plan.targetBranch) {
|
|
387
|
+
steps.push({
|
|
388
|
+
action: 'restore-branch',
|
|
389
|
+
detail: `切回原分支 ${plan.originalBranch}`
|
|
390
|
+
})
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
return steps
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// 输出迁移计划预览,输入计划对象,输出用户确认前需要看到的关键信息。
|
|
397
|
+
function logMigratePlan(options, plan) {
|
|
398
|
+
const previewLimit = 10
|
|
399
|
+
const previewCommits = plan.commits.slice(0, previewLimit)
|
|
400
|
+
|
|
401
|
+
logInfo(options, '迁移计划:')
|
|
402
|
+
logInfo(options, ` source: ${plan.sourceBranch} (${plan.sourceRef})`)
|
|
403
|
+
logInfo(options, ` target: ${plan.targetBranch} (${plan.targetRef})`)
|
|
404
|
+
logInfo(options, ` range: ${plan.rangeSpec}`)
|
|
405
|
+
logInfo(options, ` commits: ${plan.migratedCommits}`)
|
|
406
|
+
|
|
407
|
+
if (previewCommits.length) {
|
|
408
|
+
logInfo(options, '提交预览:')
|
|
409
|
+
previewCommits.forEach((commit, index) => {
|
|
410
|
+
logInfo(options, ` ${index + 1}. ${commit.summary || commit.hash}`)
|
|
411
|
+
})
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
if (plan.commits.length > previewLimit) {
|
|
415
|
+
logInfo(options, ` ... 还有 ${plan.commits.length - previewLimit} 个提交未显示,可用 --dry-run --json 查看完整计划。`)
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
logInfo(options, '执行步骤:')
|
|
419
|
+
plan.steps.forEach((step, index) => {
|
|
420
|
+
logInfo(options, ` ${index + 1}. ${step.action}: ${step.detail}`)
|
|
421
|
+
})
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// 构建迁移计划结果,输入已解析的分支、范围和提交,输出稳定 JSON 结构。
|
|
425
|
+
async function buildMigratePlan(git, values) {
|
|
426
|
+
const commits = values.selectedCommits.length
|
|
427
|
+
? values.selectedCommits
|
|
428
|
+
: await git.revList(values.effectiveRangeSpec)
|
|
429
|
+
const commitPlan = await buildCommitPlan(git, commits)
|
|
430
|
+
const plan = {
|
|
431
|
+
sourceBranch: values.sourceBranch,
|
|
432
|
+
targetBranch: values.targetBranch,
|
|
433
|
+
sourceRef: values.sourceRef,
|
|
434
|
+
targetRef: values.targetRef,
|
|
435
|
+
originalBranch: values.originalBranch,
|
|
436
|
+
rangeSpec: values.effectiveRangeSpec,
|
|
437
|
+
rangeMode: values.rangeMode,
|
|
438
|
+
selectedCommits: values.selectedCommits,
|
|
439
|
+
commits: commitPlan,
|
|
440
|
+
migratedCommits: commits.length,
|
|
441
|
+
targetExistsLocal: values.targetExistsLocal,
|
|
442
|
+
targetExistsRemote: values.targetExistsRemote,
|
|
443
|
+
push: values.push,
|
|
444
|
+
repoRoot: values.repoRoot
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
return {
|
|
448
|
+
...plan,
|
|
449
|
+
steps: buildMigrateSteps(plan)
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
// 执行提交迁移工作流,输入 source/target/range/push/dryRun 等选项,输出迁移结果或计划。
|
|
263
454
|
export async function runMigrateWorkflow(options) {
|
|
264
455
|
let sourceBranch = normalizeBranchName(options.sourceBranch)
|
|
265
456
|
let targetBranch = normalizeBranchName(options.targetBranch)
|
|
@@ -276,11 +467,12 @@ export async function runMigrateWorkflow(options) {
|
|
|
276
467
|
})
|
|
277
468
|
|
|
278
469
|
const git = new GitService(context)
|
|
279
|
-
const
|
|
470
|
+
const precheck = await assertMigratePreconditions(git)
|
|
471
|
+
const originalBranch = precheck.currentBranch
|
|
280
472
|
|
|
281
|
-
|
|
473
|
+
logInfo(options, `仓库根目录: ${context.topLevelPath}`)
|
|
282
474
|
if (context.inheritedFromParent) {
|
|
283
|
-
|
|
475
|
+
logWarn(options, '当前目录继承了父级 Git 仓库,命令将基于父仓库根目录执行。')
|
|
284
476
|
}
|
|
285
477
|
|
|
286
478
|
await git.fetch()
|
|
@@ -296,6 +488,7 @@ export async function runMigrateWorkflow(options) {
|
|
|
296
488
|
const interactivePlan = await resolveInteractivePlan(git, {
|
|
297
489
|
sourceBranch,
|
|
298
490
|
targetBranch,
|
|
491
|
+
currentBranch: originalBranch,
|
|
299
492
|
range: rangeSpec
|
|
300
493
|
})
|
|
301
494
|
|
|
@@ -334,20 +527,72 @@ export async function runMigrateWorkflow(options) {
|
|
|
334
527
|
|
|
335
528
|
const sourceExistsLocal = await git.branchExistsLocal(sourceBranch)
|
|
336
529
|
const sourceExistsRemote = await git.branchExistsRemote(sourceBranch)
|
|
337
|
-
|
|
338
|
-
throw new IcodeError(`source 分支不存在: ${sourceBranch}`, {
|
|
339
|
-
code: 'MIGRATE_SOURCE_MISSING',
|
|
340
|
-
exitCode: 2
|
|
341
|
-
})
|
|
342
|
-
}
|
|
530
|
+
const sourceRef = resolveBranchRef(sourceBranch, sourceExistsLocal, sourceExistsRemote, 'source')
|
|
343
531
|
|
|
344
532
|
const targetExistsLocal = await git.branchExistsLocal(targetBranch)
|
|
345
533
|
const targetExistsRemote = await git.branchExistsRemote(targetBranch)
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
534
|
+
const targetRef = resolveBranchRef(targetBranch, targetExistsLocal, targetExistsRemote, 'target')
|
|
535
|
+
|
|
536
|
+
// 默认迁移 source 相对 target 的增量提交;交互模式可选择最近 N 条或手动挑选提交。
|
|
537
|
+
const effectiveRangeSpec = rangeSpec || `${targetRef}..${sourceRef}`
|
|
538
|
+
const plan = await buildMigratePlan(git, {
|
|
539
|
+
sourceBranch,
|
|
540
|
+
targetBranch,
|
|
541
|
+
sourceRef,
|
|
542
|
+
targetRef,
|
|
543
|
+
originalBranch,
|
|
544
|
+
rangeMode,
|
|
545
|
+
selectedCommits,
|
|
546
|
+
effectiveRangeSpec,
|
|
547
|
+
targetExistsLocal,
|
|
548
|
+
targetExistsRemote,
|
|
549
|
+
push: Boolean(options.push),
|
|
550
|
+
repoRoot: context.topLevelPath
|
|
551
|
+
})
|
|
552
|
+
|
|
553
|
+
if (options.dryRun) {
|
|
554
|
+
return {
|
|
555
|
+
dryRun: true,
|
|
556
|
+
...plan
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
const commits = plan.commits.map((commit) => commit.hash)
|
|
561
|
+
|
|
562
|
+
if (!commits.length) {
|
|
563
|
+
logWarn(options, '没有可迁移的提交。')
|
|
564
|
+
return {
|
|
565
|
+
dryRun: false,
|
|
566
|
+
sourceBranch,
|
|
567
|
+
targetBranch,
|
|
568
|
+
migratedCommits: 0,
|
|
569
|
+
rangeSpec: effectiveRangeSpec,
|
|
570
|
+
rangeMode,
|
|
571
|
+
repoRoot: context.topLevelPath
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
if (!options.yes) {
|
|
576
|
+
logMigratePlan(options, plan)
|
|
577
|
+
const accepted = (await chooseOne(
|
|
578
|
+
'确认执行以上迁移计划吗?',
|
|
579
|
+
[
|
|
580
|
+
{ value: 'yes', label: '确认执行' },
|
|
581
|
+
{ value: 'no', label: '取消迁移' }
|
|
582
|
+
],
|
|
583
|
+
0
|
|
584
|
+
)) === 'yes'
|
|
585
|
+
if (!accepted) {
|
|
586
|
+
logger.warn('已取消迁移。')
|
|
587
|
+
return {
|
|
588
|
+
canceled: true,
|
|
589
|
+
sourceBranch,
|
|
590
|
+
targetBranch,
|
|
591
|
+
rangeSpec: effectiveRangeSpec,
|
|
592
|
+
rangeMode,
|
|
593
|
+
repoRoot: context.topLevelPath
|
|
594
|
+
}
|
|
595
|
+
}
|
|
351
596
|
}
|
|
352
597
|
|
|
353
598
|
try {
|
|
@@ -359,60 +604,21 @@ export async function runMigrateWorkflow(options) {
|
|
|
359
604
|
|
|
360
605
|
if (targetExistsRemote) {
|
|
361
606
|
await git.pull(targetBranch, {
|
|
362
|
-
allowUnrelatedHistories: true,
|
|
363
607
|
noRebase: true
|
|
364
608
|
})
|
|
365
609
|
}
|
|
366
610
|
|
|
367
|
-
// 默认迁移 source 相对 target 的增量提交;交互模式可选择最近 N 条或手动挑选提交。
|
|
368
|
-
const effectiveRangeSpec = rangeSpec || `${targetBranch}..${sourceBranch}`
|
|
369
|
-
const commits = selectedCommits.length ? selectedCommits : await git.revList(effectiveRangeSpec)
|
|
370
|
-
|
|
371
611
|
if (selectedCommits.length) {
|
|
372
|
-
|
|
612
|
+
logInfo(options, `迁移范围: 手动选择 ${selectedCommits.length} 个提交(mode=${rangeMode})`)
|
|
373
613
|
} else {
|
|
374
|
-
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
if (!commits.length) {
|
|
378
|
-
logger.warn('没有可迁移的提交。')
|
|
379
|
-
return {
|
|
380
|
-
sourceBranch,
|
|
381
|
-
targetBranch,
|
|
382
|
-
migratedCommits: 0,
|
|
383
|
-
rangeSpec: effectiveRangeSpec,
|
|
384
|
-
rangeMode,
|
|
385
|
-
repoRoot: context.topLevelPath
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
if (!options.yes) {
|
|
390
|
-
const accepted = (await chooseOne(
|
|
391
|
-
`确认将 ${commits.length} 个提交从 ${sourceBranch} 迁移到 ${targetBranch} 吗?`,
|
|
392
|
-
[
|
|
393
|
-
{ value: 'yes', label: '确认迁移' },
|
|
394
|
-
{ value: 'no', label: '取消' }
|
|
395
|
-
],
|
|
396
|
-
0
|
|
397
|
-
)) === 'yes'
|
|
398
|
-
if (!accepted) {
|
|
399
|
-
logger.warn('已取消迁移。')
|
|
400
|
-
return {
|
|
401
|
-
canceled: true,
|
|
402
|
-
sourceBranch,
|
|
403
|
-
targetBranch,
|
|
404
|
-
rangeSpec: effectiveRangeSpec,
|
|
405
|
-
rangeMode,
|
|
406
|
-
repoRoot: context.topLevelPath
|
|
407
|
-
}
|
|
408
|
-
}
|
|
614
|
+
logInfo(options, `迁移范围: ${effectiveRangeSpec}`)
|
|
409
615
|
}
|
|
410
616
|
|
|
411
617
|
try {
|
|
412
618
|
await git.cherryPick(commits)
|
|
413
619
|
} catch (error) {
|
|
414
620
|
throw new IcodeError(
|
|
415
|
-
'迁移失败: cherry-pick 发生冲突。请先解决冲突后执行 `
|
|
621
|
+
'迁移失败: cherry-pick 发生冲突。请先解决冲突后执行 `icode undo --recover continue`,或执行 `icode undo --recover abort` 回滚。',
|
|
416
622
|
{
|
|
417
623
|
code: 'MIGRATE_CHERRY_PICK_FAILED',
|
|
418
624
|
cause: error,
|
|
@@ -429,6 +635,7 @@ export async function runMigrateWorkflow(options) {
|
|
|
429
635
|
}
|
|
430
636
|
|
|
431
637
|
return {
|
|
638
|
+
dryRun: false,
|
|
432
639
|
sourceBranch,
|
|
433
640
|
targetBranch,
|
|
434
641
|
migratedCommits: commits.length,
|
|
@@ -442,7 +649,7 @@ export async function runMigrateWorkflow(options) {
|
|
|
442
649
|
try {
|
|
443
650
|
await git.checkout(originalBranch)
|
|
444
651
|
} catch (error) {
|
|
445
|
-
|
|
652
|
+
logWarn(options, `未能自动切回原分支 ${originalBranch}: ${error.message}`)
|
|
446
653
|
}
|
|
447
654
|
}
|
|
448
655
|
}
|