✨ feat(vscode): 代码阅读侧栏可以按审查工单只看 PR 改过的文件:标题栏「选工单」列出看板「审查」列,选中后树里只剩改过的文件(M / A / D 着色),点开的是本机工作副本里 PR 最新的代码,没有就拉 PR 分支建一个,「显示全部」退出
This commit is contained in:
1 parent
c009187f6c
commit
8e2cdfaf17
18 files changed
+1053
-40
No files matched your search
@@ -166,6 +166,48 @@ export async function ensureWorktreeFromRemote(opts: WorktreeOpts): Promise<stri
|
||||
return worktreePath
|
||||
}
|
||||
|
||||
/** 从 origin 拉 PR 的最新提交:Gitea 给每个 PR 留了 `refs/pull/<n>/head`,PR 的分支本机没有也拉得到。 */
|
||||
export async function fetchPullRequestHead(workspaceRoot: string, pr: number): Promise<void> {
|
||||
await runGit(workspaceRoot, ['fetch', 'origin', `refs/pull/${pr}/head`], `拉取 PR #${pr}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 审查用的 worktree:停在 commit 上、不挂分支(detached),不和实施用的 feature 分支抢同一个分支名。
|
||||
* 先 prune 掉目录已经没了的登记,否则同一路径的目录被手动删过之后再建会被 git 拦下。
|
||||
*/
|
||||
export async function addDetachedWorktree(opts: { workspaceRoot: string, worktreePath: string, commit: string }): Promise<void> {
|
||||
await runGit(opts.workspaceRoot, ['worktree', 'prune'], 'git worktree prune')
|
||||
await fsp.mkdir(path.dirname(opts.worktreePath), { recursive: true })
|
||||
await runGit(opts.workspaceRoot, ['worktree', 'add', '--detach', opts.worktreePath, opts.commit], 'git worktree add')
|
||||
}
|
||||
|
||||
/** 快进 worktree 的结果:已快进 / 本来就含那个提交 / 有没提交的改动所以没动 / 本地另有提交快进不了所以没动。 */
|
||||
export type FastForwardResult = 'updated' | 'current' | 'dirty' | 'diverged'
|
||||
|
||||
/**
|
||||
* 把 worktree 快进到 commit(commit 要已经在本机,先 fetch)。只在不丢东西时动:
|
||||
* 有没提交的改动(未跟踪的文件不算)、本地有 commit 之外的提交时原样不动。
|
||||
*/
|
||||
export async function fastForwardWorktree(worktreePath: string, commit: string): Promise<FastForwardResult> {
|
||||
// 目录本身得是工作副本的根:只是某个仓库里的普通目录时,git -C 会一路找上去动到外面那个仓库
|
||||
const top = (await runGit(worktreePath, ['rev-parse', '--show-toplevel'])).trim()
|
||||
if (await fsp.realpath(top) !== await fsp.realpath(worktreePath))
|
||||
throw new Error(`${worktreePath} 不是 git 工作副本的根目录`)
|
||||
if ((await runGit(worktreePath, ['status', '--porcelain', '--untracked-files=no'])).trim())
|
||||
return 'dirty'
|
||||
// 左边是 HEAD 有而 commit 没有的提交数,右边反过来
|
||||
const [ahead, behind] = (await runGit(worktreePath, ['rev-list', '--left-right', '--count', `HEAD...${commit}`]))
|
||||
.trim()
|
||||
.split(/\s+/)
|
||||
.map(Number)
|
||||
if (behind === 0)
|
||||
return 'current'
|
||||
if (ahead > 0)
|
||||
return 'diverged'
|
||||
await runGit(worktreePath, ['merge', '--ff-only', commit], 'git merge --ff-only')
|
||||
return 'updated'
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除一个 worktree 目录并清掉 git 侧注册。
|
||||
*
|
||||
|
||||
@@ -24,6 +24,11 @@ export function slugFromBranch(branch: string): string {
|
||||
return branch.startsWith('feature/') ? branch.slice('feature/'.length) : branch
|
||||
}
|
||||
|
||||
/** 本机没有工单的 worktree 时,审查它的 PR 建的 worktree 用这个目录名:按工单号,和实施用的 `feature/<slug>` 目录分开。 */
|
||||
export function reviewWorktreeSlug(issueNumber: number): string {
|
||||
return `review-${issueNumber}`
|
||||
}
|
||||
|
||||
/**
|
||||
* 把 worktreeDirectory 模板展开成绝对路径。`~` 在这里展开:git/execFile 不经 shell,
|
||||
* 不会自己做 tilde 展开。
|
||||
|
||||
Reference in new issue
Block a user