0

试图真正掌握 git :) git pull 是一个存储库范围的操作吗?意思是,它是更新存储库中的本地分支(跟踪远程分支),还是只为当前签出的分支获取和合并?

推也一样吗?--all 对 push 和 pull 有什么作用?

任何帮助都会摇滚!

另外, fetch 有什么作用?它是否获取特定分支的信息(.git 文件夹内的文件)?或者 .git 文件夹在整个仓库中是否一致?如果我确实 fetch 而不是 clone,那之后我真的什么也做不了,那么在 fetch 之后我该怎么办?

4

2 回答 2

2

git pull只是git fetch和的组合git merge

git fetch将更新所有远程分支,并将git merge通过合并相应的远程分支来更新当前分支。

平原的确切行为git push取决于 的输出git config push.default。最近的 git 版本将simple其设置为仅推送当前分支。

对于命令行选项的确切含义,请使用git help pushgit help pull

git clone只是git init, git remote add,git fetch和的组合git checkout

您的.git文件夹您的本地存储库,其中包含所有文件的全部历史记录。文件夹外的.git文件是您的“工作树”。更改文件需要工作树,但大多数其他 git 命令(如git log.

于 2014-03-12T22:10:41.097 回答
1

TL;DR 总结:“这取决于”。

答案是“两者都不是”,真的。或“视情况而定”。或类似的东西!

首先,有两个基本操作需要考虑:fetchpush。(pull操作只是一个建立在 之上的shell脚本fetch,所以一旦你知道它是如何工作的,我们就可以pull正确解释。)

两者fetchpush可以访问整个存储库。但一般来说,它们不能通过网络(或其他通信渠道)发送整个存储库来工作。他们根据参考文献工作。

fetch 和 push 操作通常采用“refspecs”,它们是引用对(分别为remote:local 和 local:remote)加上一个可选的“force”标志前缀+。但是,可以只给它们一个简单的引用,并且可以使用-f或指定强制标志--force

这两个命令已经存在了很长时间,并且积累了很多“旧东西”。使用远程存储库的“现代”方式是通过称为“远程”的东西git remote add来创建它们(并git clone创建一个origin默认调用的)。这些变成.git/config文件中的条目:

[remote "origin"]
    fetch = +refs/heads/*:refs/remotes/origin/*
    url = ssh://...

url =行给出了 fetch 和 push 的 URL——尽管pushurl =如果需要可以有一个额外的行,以使 push 转到其他地方。(有一些“旧方法”可以直接运行 fetch 和 push 并提供 URL,等等,但我们忽略所有这些......远程更好!)这也提供了 refspecs——嗯,一个 refspec,在这种情况下——对于git fetch

git ls-远程

有了这个,让我们完全从另一个命令开始,git ls-remote. 这就像 afetch但实际上并没有获取任何东西:

$ git ls-remote origin
676699a0e0cdfd97521f3524c763222f1c30a094    HEAD
222c4dd303570d096f0346c3cd1dff6ea2c84f83    refs/heads/branch
676699a0e0cdfd97521f3524c763222f1c30a094    refs/heads/master
d41117433d7b4431a188c0eddec878646bf399c3    refs/tags/tag-foo

这告诉我们远程命名origin具有三个引用名称。两个是分支,一个是标签。(特殊的HEADref 与 具有相同的 SHA-1 refs/heads/master,所以 git 会猜测远程是“在分支上master”,正如git status可能所说的那样。远程协议中有一个错误:git 应该能够说“HEAD是一个符号 ref,指向refs/heads/master“,这样您就不必猜测了。这将解决两个分支具有与 SHA-1 相同的情况HEAD。)

获取

当您运行git fetch origin时,提取操作以相同的ls-remote或多或少的方式开始,因此会看到所有的分支和标签。如果你使用--tags它也会带来所有的标签,否则它会做一些相当复杂的事情1带来所有的分支和一些标签。它还可以查看所有其他引用,但默认情况下,它不会将这些引用带过来:例如,遥控器可能有refs/notes/commits,由 使用git notes,但那个不带过来。

但是,当您更改给定的 refspecs 时git fetch,您会更改所带来的内容。默认值是 , 中的.git/config那个fetch = +refs/heads/*:refs/remotes/origin/*。这个 refspec 说要引入所有refs/heads/*引用 - 所有分支 - 并refs/remotes/origin/使用与远程分支名称相同的名称将它们存储在本地。使用--tags添加了一个额外的 refspec: refs/tags/*:refs/tags/*。这就是 git 带来所有标签的方式:匹配的所有标签,即所有标签,都以匹配的名称refs/tags/*进入您的本地。refs/tags/

(您可以添加更多fetch =行并带来更多内容。例如,请参阅“远程标签”上的此答案。)

现在,除非 git 还带来任何必需的底层对象2由它们的 SHA-1 标识,否则仅仅带来参考名称不会有多大好处。假设您已经拥有,但没有。(你是最新的,但不是。也许你甚至还没有分支。) fetch 操作需要确定提交。该提交可能需要各种文件,以及以前的提交,等等。因此,您与正在查看另一个 git 存储库的远程对象进行通信,并且他们进行了一些对话,每个人都告诉对方他们现在拥有哪些 SHA-1,以及他们仍然需要哪些 SHA-1。如果你的需要676699a...222c4dd...masterbranchbranchgit fetch222c4dd...,它询问另一端“我还需要使用 222c4dd...什么”,检查它是否有这些,如果没有,将它们添加到它的列表中,一旦添加,就会更详细地检查它们,等等。

最终同意交换什么后,他们的 git 将对象发送给您——如果可能,通常以“精简包”的形式发送(细节取决于传输方式)——您的 git 根据需要解包和/或重新打包它们,然后更新您的本地任何新分支、标签或其他引用的引用。(默认情况下,你的 git 只是将他们的分支存储在你的“远程分支”中——你的“我上次与他们交谈时他们拥有的内容”的副本——但会更新你的标签。也就是说,没有“远程标签”,只是“远程分支”。)

一个重要的 git fetch 特例

作为一种特殊情况,如果您提供git fetch的参数超出了遥控器的名称,例如:

git fetch origin master

例如——<em>这些 refspec 会覆盖配置文件中的那些,并且(在 1.8.4 之前的 git 版本中)阻止更新“远程分支”。这通常会限制获取的内容,有时会限制很多。(在 1.8.4 及更高版本中,它们仍然限制获取,但无论如何都会更新远程分支,这更有意义。)在这里,缺少冒号的 refspec(如上面的那个)不会被视为双方同名。相反,“他们的”分支像往常一样被收集起来,但 SHA-1 和分支名称被写入.git/FETCH_HEAD.

(这样做有一个很好的理由:如果git fetch origin master更新你的master,你会丢失你所做的所有新提交!所以你希望它只更新origin/master和/或FETCH_HEAD。)

git 推送

操作push真的很像。fetch虽然它不是完全对称的:你不会推送到“远程分支”,一般来说,你只是直接推送到“分支”。例如,当推送您的分支时master,您的本地引用是refs/heads/master,并且它们的本地引用也是 refs/heads/master. 当然不是refs/remotes/yoursystem/master。因此,用于推送的 refspecs 通常要简单得多。

但是,如果您只是运行git push(或git push origin),则仍然需要提供一些 refspec(s)。

git config 文件中有一个(某种新的)控制旋钮push.default,它允许您配置 git 推送的引用。在当前版本的 git 中,它默认为matching. 在 git 2.0 中,它将更改为simple. 总共有五种可能的设置:

  • nothing: 产生错误
  • current: 将你所在的分支推送到同名分支
  • upstream:将您所在的分支推送到其上游名称
  • simple: 与上游类似,但要求上游名称与本地名称匹配
  • matching: 推送所有同名的分支

其中一些需要进一步解释。“上游名称”是另一端的分支名称。假设您有一个名为 的远程分支origin/feature,并且为它创建了一个本地跟踪分支,但之所以调用它feature2是因为您已经在另一个feature分支上工作(尚未在 上创建origin)。所以你的本地feature2remote/origin它的上游(而你feature根本没有上游)。推送到upstream将遵循映射,并将您推feature2送到他们的feature. Push withsimple将拒绝尝试。

因此,如果您git push没有 refspec,git 将查找默认配置3并基于此构造一个 refspec。对于这种matching情况,它会推送您和他们都拥有的每个分支(因此,如果您都拥有masterand ,则将您的branchmaster送到他们的master,并将您的推送到branch他们的branch),但不会对只有你们一个人拥有的分支做任何事情。

如果你给出一些明确的 refspec(s),所有这一切都变得毫无意义:push 操作会推送你给它的 refspecs。此外,不带冒号的 refspec 表示“两端使用相同的名称”,因此master是编写完整长版本的简写方式,refs/heads/master:refs/heads/master.

与 fetch 一样,您的 git 和他们的 git 通信以确定需要发送哪些存储库对象(如果有)来完成推送。

git 拉

git pull操作以 的四字形式运行git fetch

它的第一步是弄清楚要使用什么遥控器。如果你说出一个:

git pull origin master

它采用你给它的名字;否则它会查看您所在的分支(比如说master),然后.git/config查找branch.master.remote(可能origin)。

然后,它会确定要使用的分支。如果您命名一个,它会使用它;否则,它使用branch.master.merge,这是另一端分支的名称(通常只是master再次)。然后它git fetch使用这些参数运行。

这意味着 fetch 只会带来“有趣”的分支,在这种情况下master,并将 SHA-1 放入FETCH_HEAD. (如果你有 git 1.8.4 或更新版本,它也会更新origin/master。)

最后,pull运行mergerebase,再次取决于配置条目以及是否使用--rebase. 您将合并或变基到的提交是其 SHA-1 现在存储在FETCH_HEAD.

请注意,这只会合并或重新设置您当前的分支。


1如手册中所述,fetch 默认为“标记跟随”技巧:它查看标记中的 SHA-1,并查看这些是否存在或将存在于您的存储库中。对于那些现在或将要成为的人,它带来了那个标签。您可以使用 关闭此功能--no-tags

2对象是存储库实际存储的东西:“blob”(文件)、树(包含文件或更多目录的目录)、提交和“带注释的标签”。每个都有一个唯一的 SHA-1 名称。

3但是,您可以使用每个分支配置覆盖它,并且. 您可以通过转动大量配置旋钮来制作大量难以理解的效果。branch.name.pushremoteremote.name.push

于 2014-03-12T22:17:02.643 回答