Maintaining shadow branches for GitHub PRs
好,我现在需要帮用户总结这篇文章的内容,控制在100个字以内。首先,我得通读整篇文章,抓住主要信息。 文章介绍了一个名为pr-shadow的工具,用于维护GitHub PR的影子分支。这个工具解决了force-push带来的问题,比如比较视图混乱和内联评论失效。作者提到在大型项目如LLVM中,频繁的rebase会导致这些问题,而pr-shadow通过创建独立的PR分支,并使用git commit-tree来同步,避免了force-push。此外,工具支持多种工作流程和GitHub企业版。 接下来,我需要将这些关键点浓缩到100字以内。重点包括:工具名称、功能、解决的问题、工作原理以及适用范围。 可能的结构是:介绍pr-shadow的作用和优势,然后说明它如何解决GitHub PR中的问题。 现在组织语言: pr-shadow是一个维护GitHub PR影子分支的工具,避免force-push带来的问题。它通过创建独立的PR分支,并同步本地修改,使比较视图清晰,并支持多种工作流程和GitHub企业版。 检查字数:刚好在100字左右。 </think> 这篇文章介绍了pr-shadow工具,用于维护GitHub PR的影子分支以避免force-push的问题。该工具通过创建独立的PR分支并同步本地修改,确保比较视图清晰,并支持多种工作流程和GitHub企业版。 2026-1-22 08:0:0 Author: maskray.me(查看原文) 阅读量:0 收藏

I've created pr-shadow, a tool that maintains a shadow branch for GitHub PRs that never requires force-pushing. This addresses pain points I described in Reflections on LLVM's switch to GitHub pull requests.

The problem

GitHub structures pull requests around branches, enforcing a branch-centric workflow. When you force-push a branch after a rebase, the UI displays "force-pushed the BB branch from X to Y". Clicking "compare" shows git diff X..Y, which includes unrelated upstream commits—not the actual patch difference. For a project like LLVM with 100+ commits daily, this makes the comparison essentially useless.

Inline comments suffer too: they may become "outdated" or misplaced after force pushes. Due to these difficulties, some recommendations suggest less flexible workflows that only append new commits and discourage rebases. When working with both the latest main branch and the pull request branch, switching between branches results in numerous rebuilds.

In a large repository, avoiding rebases isn't realistic—other commits frequently modify nearby lines, and rebasing is often the only way to discover that your patch needs adjustments due to interactions with other landed changes.

The solution

pr-shadow maintains a separate PR branch (e.g., pr/feature) that only receives commits—never force-pushed. You work freely on your local branch (rebase, amend, squash), then sync to the PR branch using git commit-tree to create a commit with the same tree but parented to the previous PR HEAD.

1
2
3
4
5
6
Local branch (feature)     PR branch (pr/feature)
A A
| |
B (amend) C1 "Fix bug"
| |
C (rebase) C2 "Address review"

Reviewers see clean diffs between C1 and C2, even though the underlying commits were rewritten.

When a rebase is detected (merge-base with main/master changed), the new PR commit is created as a merge commit with the new merge-base as the second parent. GitHub displays these as "condensed" merges, preserving the diff view for reviewers.

Usage

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

git checkout feature
prs init
prs init --draft


git fetch origin main:main
git rebase main
git commit --amend


prs push "Fix bug"
prs push --force "Rewrite"


prs desc


prs gh view
prs gh checks

The tool supports both fork-based workflows (pushing to your fork) and same-repo workflows (for branches like user/<name>/feature). It also works with GitHub Enterprise, auto-detecting the host from the repository URL.

Prior art

The program name "prs" is a tribute to my previous favorite, https://github.com/spacedentist/spr. I actually use Justin Bogner's fork to have a succinct message (removing the reviewer list from the commit message). The tool resembles Phabricator's arc diff and is great.

However, the tool pushes a user branch to the main repository (instead of a fork) to create a PR. While this is necessary for stacked pull requests, abusing it for single pull requests is discouraged. Our Lead maintainer Nikita Popov is not happy with this - but I have abused this for a long time to make my life less miserable (I don't want numerous rebuilds due to switching between old branches).


文章来源: https://maskray.me/blog/2026-01-22-pr-shadow-github-pr-without-force-push
如有侵权请联系:admin#unsafe.sh