🐛 fix(vscode): 摘修复列表与角标认 cherry-pick -x 尾注,解过冲突的复制品不再重复显示

解过冲突的 cherry-pick 复制品补丁内容已变,按文件指纹对不上,列表和角标
都把它当没摘。现在额外扫发布分支独有提交的说明,`(cherry picked from
commit <sha>)` 尾注命中的来源提交直接判已摘,列表过滤与 rev-list 计数同步扣掉。

Claude-Session: https://claude.ai/code/session_018HDxMmNDyfLa3bMTUctBzt
This commit is contained in:
cruldra committed 2026-09-06 13:55:28 +08:00
1 parent e99aed15c2
commit df0660cff7
4 files changed
+42 -9

No files matched your search

+22 -7
View File
@@ -162,12 +162,23 @@ export async function getReleaseStatus(o: { cwd: string, devBranch: string, pref
const current = pickCurrentRelease(o.prefix, await remoteBranchNames(o.cwd, o.prefix))
if (!current)
return base
const count = await mustGit(
const shas = await mustGit(
o.cwd,
['rev-list', '--count', '--right-only', '--cherry-pick', '--no-merges', unreleasedRange(current, o.devBranch)],
['rev-list', '--right-only', '--cherry-pick', '--no-merges', unreleasedRange(current, o.devBranch)],
'统计未发布提交失败',
)
return { devBranch: o.devBranch, current, unreleased: Number(count) }
const picked = await cherryPickedSources(o.cwd, `origin/${o.devBranch}..origin/${current}`)
const unreleased = shas.split('\n').filter(sha => sha && !picked.has(sha)).length
return { devBranch: o.devBranch, current, unreleased }
}
/**
* `cherry-pick -x` 在提交说明末尾留的来源提交号。解过冲突的复制品补丁内容已经变了,
* 指纹对不上,只剩这条线索能认出它摘过。
*/
async function cherryPickedSources(cwd: string, range: string): Promise<Set<string>> {
const bodies = await mustGit(cwd, ['log', '--no-merges', '--format=%b', range], '读取发布分支提交说明失败')
return new Set(Array.from(bodies.matchAll(/\(cherry picked from commit ([0-9a-f]{40})\)/g), m => m[1]))
}
/** 新→旧;不 fetch,调用方自己决定何时刷新远端 */
@@ -276,20 +287,24 @@ export async function listUnreleasedCommits(o: { cwd: string, devBranch: string,
)
if (!out)
return []
const releaseOnly = `origin/${o.devBranch}..origin/${o.current}`
const picked = await cherryPickedSources(o.cwd, releaseOnly)
const commits = out.split('\x1E').flatMap((chunk) => {
const trimmed = chunk.replace(/^\n+/, '').trimEnd()
if (!trimmed)
return []
const [sha, subject, author, date, body = ''] = trimmed.split('\0')
return [{ sha, subject, author, date, body: body.trim() }]
return picked.has(sha) ? [] : [{ sha, subject, author, date, body: body.trim() }]
})
if (commits.length === 0)
return []
const perCommit = await fileChangeKeysByCommit(o.cwd, ['--right-only', '--cherry-pick', unreleasedRange(o.current, o.devBranch)])
const files = new Set<string>()
for (const keys of perCommit.values()) {
for (const key of keys)
for (const c of commits) {
for (const key of perCommit.get(c.sha) ?? [])
files.add(key.slice(0, key.indexOf('\0')))
}
const onRelease = await fileChangeKeysByCommit(o.cwd, [`origin/${o.devBranch}..origin/${o.current}`], files)
const onRelease = await fileChangeKeysByCommit(o.cwd, [releaseOnly], files)
const released = new Set<string>()
for (const keys of onRelease.values()) {
for (const key of keys)