拉取请求流程


你一直在自己的分支上开发一个功能,现在它已经完成了。但这个项目是共享的,别人也会往里面提交代码,所以你不能直接推到 main 上碰运气。你希望这个改动经过审查,如果需要就讨论一下,然后以团队其他人都能看到的方式合并进去。从你机器上的一个分支,到代码最终落地到 main,这整条路径就是拉取请求流程,几乎所有在 GitHub 上协作的团队都是这么把改动合并进去的。这个流程里 Git 那一侧用到的都是前面章节学过的命令:分支、提交、推送。
$ git switch -c add-five-day-forecast
Switched to a new branch 'add-five-day-forecast'
$ git add src/forecast.js
$ git commit -m "Add five-day forecast panel"
$ git push -u origin add-five-day-forecast
Branch 'add-five-day-forecast' set up to track 'origin/add-five-day-forecast'.这次推送把你的分支发到了 GitHub 上。此时还没有任何东西被合并,甚至还没有提出审查请求。下一步——发起拉取请求,是在 GitHub 网站上完成的。
发起拉取请求之后会发生什么
在 GitHub 上,像上面这样推送之后,你通常会看到一条横幅,提示可以把你的新分支和 main 进行对比。点它,写一个标题和一段简短说明,介绍改了什么、为什么改,然后打开它生成的页面。这个页面就是一个**拉取请求**,通常简称 PR:一个把一个分支合并到另一个分支的请求,并附带一条持续进行的对话。
拉取请求提出一次合并申请,然后等待批准。有人——可能是你自己,也可能是团队里的其他人——会阅读这个改动,留下评论,觉得没问题了才会合并。在此之前,你的分支和 main 会保持和你打开这个页面之前完全一样的状态。
Fork 和分支的区别
当你对仓库有写权限时,直接从分支发起拉取请求就可以了,这也是在你自己团队的项目里的常规做法。如果要给一个你没有写权限的项目贡献代码,就需要多一步:fork,也就是在你自己的 GitHub 账号下建一份该仓库的副本。
在你自己 fork 出来的仓库里建分支、提交代码,和在自己拥有的项目里做法完全一样,推送到你的 fork 上之后,再从你 fork 的分支向原始仓库发起拉取请求。GitHub 对比这两个仓库的方式,和它在同一个仓库里对比两个分支的方式是一样的,所以无论哪种情况,审查和合并的流程看起来都完全相同。
让你的拉取请求顺利通过审查
一旦 PR 发起,它就变成一场围绕这份 diff 持续进行的讨论。审查者会阅读这个改动,在具体的行上留下评论,然后要么批准,要么要求修改。当有人要求修改时,继续往同一个分支提交、推送就好:每一次推送更新的都是同一个 PR,而不会创建一个新的。一旦审查者批准,并且所有必须通过的检查都通过了,这个 PR 就可以合并了。
为什么拉取请求合并不了
有时候 PR 上的合并按钮会变成灰色不可点,GitHub 会在那个位置显示一条消息:"This branch has conflicts that must be resolved"(这个分支有必须解决的冲突),或者"This branch is out of date with the base branch"(这个分支落后于基础分支)。这两条消息描述的都是一种正常、可以修复的状态:main 在你建分支之后又往前推进了,通常是因为在这期间别人的 PR 合并进去了,你的分支需要先跟上进度,GitHub 才能干净地把两者合并起来。任何有多个贡献者的项目都会遇到这种情况,所以要把它当作意料之中的事,而不是害怕的事。
在本地修复它的方式,和你把任何两个分支合到一起的方式一样:
$ git switch add-five-day-forecast
$ git fetch origin
From github.com:mara-chen/weather-app
9f8e7d6..b7c9e21 main -> origin/main
$ git merge origin/main如果没有任何重叠,合并会自动完成,你推送上去,PR 就会更新,按钮也会解锁。如果双方改了同一行,Git 会在文件里标出冲突,等你来解决,具体做法和 合并与冲突 那一章讲的完全一样:打开文件,删掉冲突标记,改成你想要的结果,然后 git add、git commit 完成这次合并,再推送一次。
卡住的拉取请求,需要的无非是补上进度,或者解决掉冲突。
main,或者和它有冲突。这不是出了什么故障。切到你的分支,fetch,再把 main 合并进来,用和平时一样的方式解决任何冲突,然后再推送一次。 拉取请求底层到底是什么
拉取请求不是一个 Git 对象,也没有哪个 git 命令能创建它。Git 和 GitHub 不是一回事:Git 只知道提交、分支和其他 ref(ref 就是指向某个提交的一个名字)。GitHub 是在这基础上,对比它选定的两个 ref——你的分支(head)和你要合并进去的那个分支(base,通常是 main)——搭建出了整个 PR 页面、评论区和合并按钮。正因为这是一次比较行为,所以你只能在 GitHub 网站上(或者通过 GitHub 自己的命令行工具或 API)发起拉取请求,而不能用一个普通的 git 命令。

