从远程仓库拉取所有分支

10

我可以说:

git push --all origin

它将会把所有分支推送到远程仓库。但如果我执行以下操作:

git pull --all origin

如果它无法从源中拉取所有分支,它只会返回错误,不会执行操作:

fatal: fetch --all does not take a repository argument

好的,我这样做:

git pull --all

购买,然而它却显示:

You asked to pull from the remote '--all', but did not specify
a branch. Because this is not the default configured remote
for your current branch, you must specify a branch on the command line.

那么我如何从远程仓库拉取所有分支(就像我通过git push --all origin将所有分支推送到远程仓库一样)?


如果你执行 git pull origin 会发生什么?或者更好的方式是使用 git fetch origin - zerkms
git fetch 已经可以做到这一点。 - user229044
@meagar,但是fetch和pull不是一样的。 - Incerteza
@meagar,这不是我要找的。 - Incerteza
@AlexanderSupertramp 这不是你想要的吗?它下载所有远程分支。这是最接近“拉取”所有远程分支的方法;你是否被“拉取”这个词卡住了? - user229044
显示剩余2条评论
2个回答

15

VonC的回答包含了所有你需要让各种设置工作的项目,但对于那些对git还比较新的人来说,可能需要一些背景解释。

我认为非常不幸的一点是,git有fetchpushpull。它听起来像pull是与push相反方向的等效操作,但实际上并不是这样!push最接近的相反操作实际上是fetch(即使如此,它们也不完全对称)。

要正确地理解所有这些,您需要知道在git中,分支名称只是特定提交的标签,具有一个非常特殊的属性。像master这样的“普通”或“本地”分支名称-通常被称为“分支”-具有特殊属性,即当您通过分支名称检出并在您的存储库中创建新提交时,该分支名称自动向前移动以包括您的新提交。(每个新提交指向其“父”旧提交,或者对于合并,指向其所有父项。)

Git还提供了“远程分支”,这些分支(按照git的传统)具有某种令人误解的名称,因为它们也存在于您的存储库中,而不是在其他“远程”存储库中。 这些带有远程名称前缀,例如origin,因此您可以将origin/master作为“远程分支”。同样,这些只是提交的标签。与您的本地分支不同,它们不会在您进行提交时移动-但是它们移动。当使用git fetch时它们会移动。

运行git fetch时,您的git会联系某些远程(origin)并询问它拥有哪些分支1,这些标签指向何处。 您可以通过运行git ls-remote来查看此内容:

$ git ls-remote
From ssh://[redacted]
d1574b852963482d4b482992ad6343691082412f    HEAD
222c4dd303570d096f0346c3cd1dff6ea2c84f83    refs/heads/branch
d1574b852963482d4b482992ad6343691082412f    refs/heads/master
d41117433d7b4431a188c0eddec878646bf399c3    refs/tags/tag-foo

一次普通的fetch会将所有分支带到本地仓库,分别以不同的名称写入其中。在这种情况下,master变成了origin/masterbranch变成了origin/branch2

要带回一个分支标签,还需要带回该提交本身(例如上面的222c4dd...),以及使提交完整所需的任何其他底层对象。(这包括他们具有但你尚未具有的任何开发历史。)然后,新的、远程调整后的分支名(origin/branch)被设置为直接指向该提交(222c4dd...)。

因此,正如已经注意到的那样,git fetch会带回所有分支,就像git push --all会推送所有分支一样。fetch不会合并这些更新到您自己的本地分支中。这也是fetchpush停止镜像彼此的地方:当您将一个分支推送到远程时,没有自动重命名3

同样,当我们带回他们的master时,它变成了origin/master。我们没有任何分支命名为origin/whatever,因此这不可能破坏我们的分支。但是当我们将我们的master推送到他们时,我们只是告诉他们将其设置为他们的master。我们不会说:"将我们的master推送到您的alexander/master "。4这就是为什么在推送之前,我们通常必须将我们的master合并或变基到他们的master,以便当我们告诉他们“嘿,将您的master设置为提交af7c315”时,我们确保这个新的提交ID包含了他们所有的历史记录,以便他们不会失去任何东西。

这就是git pull的用处。为了更新他们的master,首先需要使用git fetch将他们的master带到我们本地副本中,并将其重命名为origin/master。然后,一旦我们与他们同步,我们就将我们的master和我们的origin/master(现在与他们的master同步)合并或重建基础架构。最后,当所有这些都完成,且已经确认一切正常之后,我们就可以推送我们的新master - 它现在已基于/与我们的origin/master合并,并且匹配他们的master - 到他们的master。只要所有这些操作足够快,这样就行得通。(如果不够快,如果其他人比我们更快地执行了push,那么我们必须再次进行fetch,再次合并或重新构建基础架构,然后尝试再次push,重复这个过程,直到我们赢得每个人都尝试push的赛跑。) git pull脚本简单地自动化了获取和合并/重建基础架构的部分。但它只使用了一个分支:我们当前检出的任何分支。这是因为,在git中,mergerebase命令只更改一个分支: merge将分支合并到当前分支中,而rebase通常会将当前分支重新建立基础架构。(如果您告诉git rebase重新基于其他分支,那么它会首先检查它。)如果要合并或重建基础架构多个分支,则必须一次检出每个分支。(使用rebase稍微容易一些,因为git rebase origin/master master从执行git checkout master开始,因此已集成到命令中,但您仍然需要使用git checkout。)幸运的是,git fetch origin会同时更新所有远程分支,因此您只需要一个git fetch。Git通常假设除非您计划在分支上进行更改,否则不会检出自己的版本。也就是说,你不会这样做:
$ git checkout --track feature origin/feature

除非你打算对它进行某些操作,否则没有必要将其变基或合并,因此,除非你已经在feature分支上,否则不需要进行变基或合并。

由于git clone会进行初始检出,通常是master,而人们经常使用功能分支来工作,所以很容易会有一个你将要工作的分支(feature)和一个你不需要的本地分支(master)。但如果你还不需要它,那么把这个分支留下,让它越来越落后于origin/master也没有什么问题;或者你甚至可以在git checkout之后删除它:

$ git checkout branch       # newer gits do --track automatically
Branch branch set up to track remote branch branch from origin.
Switched to a new branch 'branch'
$ git branch -d master
warning: deleting branch 'master' that has been merged to
         'refs/remotes/origin/master', but not yet merged to HEAD.
Deleted branch master (was d1574b8).
$ 

(警告很烦人,但无害。)


1fetch也可以看到标签和其他引用,并且可以指示其带更多的内容过来。上面的示例显示了一个标签加上HEAD。添加--tags或更改fetch =行,允许您将标签带过来。HEAD引用是非常特殊的,而且 Git 在克隆操作中处理 HEAD时存在已有很长时间的细微错误,这通常不重要并需要更改协议才能修复,所以没有人修复它。

2更准确地说,refs/heads/*映射到refs/remotes/origin/*,其中*部分被复制过来。所以如果远程有一个refs/heads/this/that,你就会得到refs/remotes/origin/this/that。这就是你的.git/config文件中remote定义下的fetch =行的作用:它告诉fetch如何重新映射远程引用。这也是为什么我说“正常”获取:您可以对其进行配置或使用选项运行它,以使其表现不同。

Git的新手可能会想知道为什么要这样重命名。嗯,假设你首先获取他们的master并创建一个新的本地master分支,以便您可以更改东西。然后您更改了一些内容,并使用git addgit commit创建了至少一个新提交。同时,他们(无论“他们”是谁)也更改了东西并进行了新的提交。您想看看他们做了什么,因此您git fetch他们的新内容。如果它覆盖了你的master,那么你的工作会发生什么?幸运的是,Git将他们的新内容带来了不同的名称:origin/master。您的master保持不变,跟踪您的新提交。

3至少默认情况下是不会的。与 Git 的大多数事情一样,您可以通过命令行标志和配置条目更改此行为。

4实际上,正如注释3中已经指出的那样,我们确实可以这样做,在某些设置中,您甚至可能会想要这样做。但是这通常不是人们在共享存储库中使用 Git 的方式。


1
请注意,“remote branches”实际上是“远程跟踪分支”,即在本地跟踪其在上游存储库中对应的分支。 - VonC
谢谢。如果我知道有一个名为“some_branch1”的远程分支,而且没有本地分支跟踪它,那么从远程到本地“复制”(获取、拉取?)“some_branch1”的正确方法是什么? - Incerteza
普通的 git fetch 将创建或更新 origin/some_branch1,并在此过程中传输任何所需的底层 git 对象。如果您想查看其中的内容,则 git log origin/some_branch1 将向您显示。默认情况下,远程分支也有 reflogs,因此您可以查看自上次更新以来发生的情况:git log origin/some_branch1@{1}..origin/some_branch1 - torek
我有一个本地分支develop,并且在origin上有2个分支。当我调用git fetch origin时,它只更新了我的本地develop分支。为什么它没有更新或创建master分支?我应该先创建并切换到master分支,然后调用git fetch获取origin/master吗? - Incerteza
2
请重新阅读上面的答案,并牢记git fetch origin不会更新任何本地分支。它会更新(您的副本)远程跟踪分支,例如origin/some_branch1origin/master。您必须区分个人分支和“远程分支”,因为Git会这样做。 - torek

5
使用git pull命令时,首先需要执行git fetch命令,这意味着origin名称空间将包括来自origin的所有(远程跟踪)分支(详见获取所有分支)。
但它只会将origin/currentBranch合并到currentBranch中,而不会创建其他分支。
如果它要创建来自origin的所有分支,您的git分支可能会被上游存储库的许多分支“污染”。 通常,您只需要本地工作的那个分支。
如果您想跟踪来自远程的所有分支,可以参考问题“将所有远程git分支作为本地分支跟踪”中的这个一行命令

如果您想追踪所有来自远程的分支,您可以参考问题中的这个一行命令 - 那是一个脚本,但是有没有标准的git命令可以实现呢?对于push操作,有一个命令(git push --all origin),但是pull操作是否也有相同的命令呢? - Incerteza
1
@AlexanderSupertramp 不,这并不存在,因为在99%的情况下,没有人想要创建所有远程分支的本地分支:那会让你的分支列表被大量不需要的分支污染。它们在远程命名空间中进行了获取,但你不需要将它们作为本地分支。 - VonC

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