如何在 GitHub 上更新或同步派生仓库?

4685

我fork了一个项目,做了一些更改,并创建了一个被接受的拉取请求。后来该存储库添加了新的提交。如何将这些提交合并到我的fork中?


157
这也可以通过GitHub界面完成。我想要向这位其他发帖者致谢。 - Mike Schroll
5
这是一篇非常好的博客文章 - 如何更新 GitHub 的 Fork 仓库 - Arup Rakshit
4
在 Github 的帮助文章中找到了这个链接:https://help.github.com/articles/syncing-a-fork/ - Pranav
2
这是 https://dev59.com/GG865IYBdhLWcg3wQMWW 的重复吗? - David Cary
4
自从2021年5月起,不需要创建额外的拉取请求,就可以直接在GitHub用户界面上进行同步操作,详见changelog和https://dev59.com/Smw05IYBdhLWcg3wcRUj#67425996。 - Marcono1234
显示剩余4条评论
31个回答

5355
在你本地克隆的派生存储库中,你可以将原始的GitHub存储库添加为“远程”(“远程”就像存储库URL的昵称 - 例如,origin)。然后,你可以从上游存储库获取所有分支,并将你的工作重新基于上游版本继续工作。命令可能如下所示:
# Add the remote, call it "upstream":

git remote add upstream https://github.com/whoever/whatever.git

# Fetch all the branches of that remote into remote-tracking branches

git fetch upstream

# Make sure that you're on your master branch:

git checkout master

# Rewrite your master branch so that any commits of yours that
# aren't already in upstream/master are replayed on top of that
# other branch:

git rebase upstream/master

如果你不想重写主分支的历史记录(例如因为其他人可能已经克隆了它),那么应该将最后一个命令替换为git merge upstream/master。但是,为了使进一步的拉取请求尽可能干净,重新设置基础可能更好。
如果您已经将您的分支变基到upstream/master,则需要强制推送才能将其推送到GitHub上自己的派生存储库中。可以使用以下命令进行强制推送:
git push -f origin master

在重新设置基准后,您只需要第一次使用-f


120
由于您的分叉仅存在于 Github 上,而 Github 没有用于通过 Web 接口执行合并的工具,因此正确的做法是在本地执行上游合并,然后将更改推送回您的分叉。 - Tim Keating
65
请注意,与其必须将您自己的主分支进行变基以确保您开始时处于干净的状态,您应该在单独的一个分支上进行工作,并从那里创建一个拉取请求。这样可以使您的主分支保持清洁,以备将来合并使用,并且它还可以防止您不得不使用“-f”重写历史记录,这会影响到任何可能克隆您版本的人。 - Mateusz Kowalczyk
13
我用了git merge --no-ff upstream/master代替rebase命令,这样你的提交记录就不再在最上面了。 - Steckdoserich
30
@jww:你问为什么需要告诉Git关于原始的GitHub存储库。这是因为Git是一个分布式版本控制系统,与GitHub没有任何关系;很明显Git是在GitHub之前创建的。当你在GitHub上创建一个派生存储库并将其克隆时,GitHub知道该存储库是一个派生;Git没有理由也不会知道。 (为什么克隆不会复制Git远程?Git是分散的;不同的人会想要不同的远程;这样做没有意义)。请参阅https://github.com/github/hub 以在Git中集成GitHub。 - Radon Rosborough
9
@Sinjai,这些 Web 服务确实有明确的支持。它被称为远程系统。您指定 URL,然后进行推送/拉取。说实话,除非硬编码支持某个特定服务,否则我认为它根本不可能变得更简单。如果 Git 要在没有您提供信息的情况下对不同仓库之间的关系有某种隐含理解,那么它将需要一个集中式数据库。将这个功能集成到分布式版本控制系统中就没有合理的方式。 - Radon Rosborough
显示剩余27条评论

829

从2014年5月开始,可以直接从GitHub更新分支。截至2017年9月,这仍然有效,但会导致一个混乱的提交历史。

  1. 在GitHub上打开你的分支。
  2. 点击Pull Requests
  3. 点击New Pull Request。 默认情况下,GitHub将比较原始版本和你的分支,如果你没有进行任何更改,则不应该有任何要比较的内容。
  4. 如果您看到该链接,请单击“switching the base”。否则,手动将base fork下拉菜单设置为你的分支,将head fork设置为上游。现在,GitHub将比较你的分支和原始版本,并显示所有最新的更改。 enter image description here
  5. 创建pull request并给你的请求指定一个可预测的名称(例如,Update from original)。
  6. 向下滚动到Merge pull request,但还不要点击任何东西。
现在你有三个选项,但每个选项都会导致不太干净的提交历史。
  1. 默认选项将创建一个丑陋的合并提交。
  2. 如果您单击下拉菜单并选择“Squash and merge”,所有中间提交将被压缩成一个。这通常不是您想要的。
  3. 如果您单击 Rebase and merge,则所有提交都将“与”您一起进行,原始PR将链接到您的PR,并且GitHub将显示This branch is X commits ahead, Y commits behind <original fork>

因此,是的,您可以使用GitHub Web UI使您的存储库与其上游保持更新,但这样做会玷污您的提交历史。相反,请坚持使用 the command line - 这很容易。


21
这个过程第一次很顺利。但第二次执行时出现问题:点击“切换基础”链接时没有显示出来。当我点击“点击创建拉取请求”时,它在源代码库上创建了一个PR。这不是我想要的结果。 - WestCoastProjects
32
仍然可以使用(2015年3月),尽管“转换基础”的链接已经不存在。您需要更改“基础”下拉菜单,使两者都指向您的分支,然后您将收到一个提示,询问是否“跨存储库进行比较”,这将带您到您想要的地方。 - mluisbrown
8
2015年4月份。工作内容。谢谢。我确实看到了“切换到基础分支”的提示。然而,第六步是“创建拉取请求”->输入评论->“创建拉取请求”。最终会比原始版本多1个提交。 - cartland
5
@cartland(或其他人)- 是的,它说“此分支领先1次提交……” 这是令人担忧的吗?有没有可能摆脱这个消息? - RenniePet
24
加一个简单的更新或同步按钮不是更好吗! - Transformer
显示剩余13条评论

618

以下是GitHub关于同步分叉的官方文档:

同步分叉

设置

在同步之前,您需要添加一个指向上游仓库的远程分支。您在最初分叉时可能已经完成了此操作。

提示:同步分叉仅会更新你本地仓库的副本,不会更新你在 GitHub 上的仓库。

$ git remote -v
# List the current remotes
origin  https://github.com/user/repo.git (fetch)
origin  https://github.com/user/repo.git (push)

$ git remote add upstream https://github.com/otheruser/repo.git
# Set a new remote

$ git remote -v
# Verify new remote
origin    https://github.com/user/repo.git (fetch)
origin    https://github.com/user/repo.git (push)
upstream  https://github.com/otheruser/repo.git (fetch)
upstream  https://github.com/otheruser/repo.git (push)

同步

同步您的存储库与上游需要完成两个步骤:首先,您必须从远程获取,然后您必须将所需分支合并到本地分支中。

获取

从远程存储库获取将带入其分支及其各自的提交。这些存储在您的本地存储库中的特殊分支下。

$ git fetch upstream
# Grab the upstream remote's branches
remote: Counting objects: 75, done.
remote: Compressing objects: 100% (53/53), done.
remote: Total 62 (delta 27), reused 44 (delta 9)
Unpacking objects: 100% (62/62), done.
From https://github.com/otheruser/repo
 * [new branch]      master     -> upstream/master

现在我们将上游的主分支存储在本地分支中,即upstream/master

$ git branch -va
# List all local and remote-tracking branches
* master                  a422352 My local commit
  remotes/origin/HEAD     -> origin/master
  remotes/origin/master   a422352 My local commit
  remotes/upstream/master 5fdff0f Some upstream commit

合并

现在我们已经获取了上游仓库,我们希望将其更改合并到我们的本地分支中。这将使该分支与上游同步,而不会丢失我们的本地更改。

$ git checkout master
# Check out our local master branch
Switched to branch 'master'

$ git merge upstream/master
# Merge upstream's master into our own
Updating a422352..5fdff0f
Fast-forward
 README                    |    9 -------
 README.md                 |    7 ++++++
 2 files changed, 7 insertions(+), 9 deletions(-)
 delete mode 100644 README
 create mode 100644 README.md
如果您的本地分支没有任何独特的提交,Git 将执行“快进”操作:
$ git merge upstream/master
Updating 34e91da..16c56ad
Fast-forward
 README.md                 |    5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

提示:如果您想在GitHub上更新您的代码库,请按照这里的说明操作。


Tip: 如果你想在GitHub上更新你的仓库,遵循这里的指示。

2
这更新了我的本地分支,但是在Github.com上的我的分支仍然显示“落后43个提交”。我不得不使用lobzik的技巧为自己创建一个拉取请求,将主分支的更改合并到我的Github.com分支中。 - Michael McGinnis
18
在本地合并后,您需要将更改推送到Github。命令为:git push origin master - jumpnett
1
我必须分别为所有分支执行 git merge upstream/master,然后切换到 develop 分支并执行 git merge upstream/develop - Shobi
1
这很有帮助。合并章节对我很重要。 - rundekugel
1
我发现这种方法比Git文档更有帮助! - push33n
显示剩余4条评论

131
很多答案最终会使您的分支比父存储库多一个提交。此答案总结了 此处 所发现的步骤,这些步骤将把您的分支移动到与父存储库相同的提交。
1. 切换到本地存储库的目录。 - 如果没有,请切换到主分支git checkout master 2. 添加父存储库作为远程存储库:git remote add upstream <repo-location> 3. 执行 git fetch upstream 4. 执行 git rebase upstream/master - 在此阶段,您可以通过键入git status来检查将要合并的提交。
5. 执行 git push origin master 有关这些命令的更多信息,请参阅 第3步

14
你需要将以下内容翻译成通俗易懂的中文,不要改变原意,也不要添加任何额外的信息:@MT: 不过你这些命令是在哪里输入呢?我理解你的问题是如何通过 GitHub 在远程分支上实现个人分支与主项目的同步更新,而且要全部在 GitHub 上完成。换句话说,你想知道如何在没有本地代码库的情况下更新远程分支。 - John Y
7
在GitHub上进行操作会始终创建一个额外的提交记录。为了避免这个额外的提交记录,你需要在本地仓库的shell中完成所有操作。 - Jonathan Cross

81

如果像我一样,你从不直接向主分支提交任何内容(其实你真应该这么做),可以按照以下步骤操作。

从本地克隆的分叉中创建上游远程。 这只需要做一次:

git remote add upstream https://github.com/whoever/whatever.git

每当您想要追上上游存储库的主分支时,您需要执行以下操作:

git checkout master
git pull upstream master

假设您从未在主分支上提交过任何内容,则应该已经完成了。现在,您可以将本地主分支推送到您的原始远程GitHub分支。您还可以在现在已更新的本地主分支上对开发分支进行变基操作。

在完成最初的上游设置和主分支检出后,您需要运行以下命令来将您的主分支与上游同步:git pull upstream master


1
你也可以将你的开发分支在最新的本地主分支上进行变基(rebase)。我该如何操作? - Niels
首先运行 git checkout my-dev-branch 切换到你的开发分支,然后运行 git rebase master。 你也可以直接运行 git rebase master my-dev-branch,这相当于将这两个命令合并在一起。参见 git rebase文档 - Slion
1
+1 鼓励不要直接在分支的主干上工作,而是在一个独立的开发分支上进行变基。大多数其他顶级答案没有提到这一点,这样做一次可能没问题,但之后会因为重写的提交(变基)或者一个不在上游的合并提交(合并)而导致麻烦。 - undefined

69

前言:你的分支是“origin”,而你从中派生的仓库则是“upstream”。

假设你已经使用类似以下命令将你的分支克隆到了本地电脑上:

git clone git@github.com:your_name/project_name.git
cd project_name
如果已经给出这个条件,那么你需要按照以下顺序继续执行:
  1. 将 "upstream" 添加到你的克隆仓库("origin")中:

  2. git remote add upstream git@github.com:original_author/project_name.git
    
  3. 从"upstream"获取提交记录(和分支):

  4. git fetch upstream
    
  5. 切换到你 fork 的 "origin" 的 "master" 分支:

  6. git checkout master
    
  7. 将您的“主”分支的更改藏起来:

    git stash
    
    将"upstream"仓库的"master"分支的更改合并到您自己"origin"仓库的"master"分支中:
  8. git merge upstream/master
    
  9. 如果有合并冲突,请解决它们并提交您的合并

  10. git commit -am "Merged from upstream"
    
  11. 将更改推送到您的派生分支

  12. git push
    
  13. 取回您隐藏的更改(如果有)

  14. git stash pop
    
  15. 你完成了!恭喜你!

GitHub还提供了有关此主题的说明:同步 fork


1
部分帮助:git remote add upstream git@github.com:original_author/project_name.git 只是 git remote add upstream https://github.com/original_author/project_name.git 的别名吗? - Wolf
2
Wolf,猜测你现在已经知道了,但为了后人... 这是ssh的格式。https://help.github.com/articles/configuring-a-remote-for-a-fork/ - Brad Ellis
2
非常感谢。git stashgit stash pop部分非常有帮助。 - Krishna
这个有效。在 git 合并 upstream/master 后,由于存在未合并路径,自动合并失败,因此我必须运行 git add -A 然后 git commit -m "message" ,然后才是最新版本。 - Vicente Antonio G. Reyes

58
有三种方法可以实现:从Web UI(选项1)、从GitHub CLI(选项2)或从命令行(选项3)。

选项1 - Web UI

  1. 在GitHub上,导航到您想要与上游存储库同步的分叉存储库的主页面。

  2. 选择“获取上游”下拉菜单。

enter image description here

审查上游代码库提交的详细信息,然后点击“获取并合并”。

enter image description here


选项 2 - GitHub CLI

要从父仓库更新远程分支,请使用gh repo sync子命令并将您的分支名称作为参数提供。

$ gh repo sync owner/cli-fork

如果上游仓库的更改导致冲突,则 GitHub CLI 无法同步。您可以设置 -force 标志以覆盖目标分支。

选项3 - 命令行

在将自己的分支与上游存储库同步之前,必须在Git中配置指向上游存储库的远程

1 打开Git Bash。

2 将当前工作目录更改为本地项目。

3 从上游存储库获取分支及其各自的提交。 BRANCHNAME的提交将存储在本地分支upstream / BRANCHNAME中。

$ git fetch upstream
> remote: Counting objects: 75, done.
> remote: Compressing objects: 100% (53/53), done.
> remote: Total 62 (delta 27), reused 44 (delta 9)
> Unpacking objects: 100% (62/62), done.
> From https://github.com/ORIGINAL_OWNER/ORIGINAL_REPOSITORY
>  * [new branch]      main     -> upstream/main

4 检查您的分支库的本地默认分支 - 在这种情况下,我们使用 main。

$ git checkout main
> Switched to branch 'main'

5 将上游默认分支中的更改-在本例中,是upstream/main-合并到您的本地默认分支。这将使您的分支与上游存储库同步,而不会丢失本地更改。

$ git merge upstream/main
> Updating a422352..5fdff0f
> Fast-forward
>  README                    |    9 -------
>  README.md                 |    7 ++++++
>  2 files changed, 7 insertions(+), 9 deletions(-)
>  delete mode 100644 README
>  create mode 100644 README.md

如果本地分支没有任何独特的提交,Git 将执行“快进”操作:
$ git merge upstream/main
> Updating 34e91da..16c56ad
> Fast-forward
>  README.md                 |    5 +++--
>  1 file changed, 3 insertions(+), 2 deletions(-)

注意:同步一个分支只会更新本地库的副本。要在GitHub.com上更新自己的分支,必须推送更改

来源:GitHub文档 - 同步分支


1
这个对我来说很好用。Github的GUI工具非常不错。 - Lucas Gabriel

54

GitHub现在推出了一个一键同步派生项目的功能

进入你的派生项目,点击获取上游,然后点击获取并合并以直接将你的派生项目与其父级仓库同步

enter image description here

你也可以点击比较按钮,在合并之前比较更改

参考资料: GitHub的文档


我尝试了一下,但没有创建 PR,酷!如果您的分支可以与快进合并同步,那么就不会出现分歧。 - li ki
3
目前,这个功能首先会比较原始库和派生库之间的分支名称。如果找到相同的名称,则在派生库中与其同名的分支将作为该分支的上游;如果未找到相同的名称,则默认分支(HEAD)将成为该分支的上游。在大多数情况下,这种方法都能很好地工作,但是如果在原始库中进行了一些分支修改(例如添加或删除了与已存在于派生库中的同名分支,或更改了默认分支),则同步的结果可能无法满足您的期望。 - li ki

48

自2013年11月以来,GitHub一直存在一个非官方的功能请求,要求他们添加一种非常简单和直观的方法来使本地分支与上游同步:

https://github.com/isaacs/github/issues/121

注意:由于该功能请求是非官方的,建议联系support@github.com,以便支持实现这样的功能。上述非官方功能请求可以用作证明,表明人们对实现这一功能的兴趣程度。


25

2
虽然我认为这个工具是个好主意,但现实情况是它已经坏了。它只从我的账户中加载了20个仓库,甚至页脚重定向到一个不存在的网站。如果这个问题得到解决,我会成为它的大力支持者。 - sorin
2
截至今天,我已成功使用upriver将分支与上游repo同步,所以它对我的目的非常有效,我会继续使用它。 - NauticalMile
1
@sorin,这个限制20个repo/branch(现在是30个)来自于GitHub默认的分页设置。需要对代码进行一些调整才能处理它。 - Andreas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接