试图真正掌握 git :) git pull 是一个存储库范围的操作吗?意思是,它是更新存储库中的本地分支(跟踪远程分支),还是只为当前签出的分支获取和合并?
推也一样吗?--all 对 push 和 pull 有什么作用?
任何帮助都会摇滚!
另外, fetch 有什么作用?它是否获取特定分支的信息(.git 文件夹内的文件)?或者 .git 文件夹在整个仓库中是否一致?如果我确实 fetch 而不是 clone,那之后我真的什么也做不了,那么在 fetch 之后我该怎么办?
git pull只是git fetch和的组合git merge。
git fetch将更新所有远程分支,并将git merge通过合并相应的远程分支来更新当前分支。
平原的确切行为git push取决于 的输出git config push.default。最近的 git 版本将simple其设置为仅推送当前分支。
对于命令行选项的确切含义,请使用git help push和git help pull。
git clone只是git init, git remote add,git fetch和的组合git checkout。
您的.git文件夹是您的本地存储库,其中包含所有文件的全部历史记录。文件夹外的.git文件是您的“工作树”。更改文件需要工作树,但大多数其他 git 命令(如git log.
答案是“两者都不是”,真的。或“视情况而定”。或类似的东西!
首先,有两个基本操作需要考虑:fetch和push。(pull操作只是一个建立在 之上的shell脚本fetch,所以一旦你知道它是如何工作的,我们就可以pull正确解释。)
两者fetch都push可以访问整个存储库。但一般来说,它们不能通过网络(或其他通信渠道)发送整个存储库来工作。他们根据参考文献工作。
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-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 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。)
操作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)。所以你的本地feature2有remote/origin它的上游(而你feature根本没有上游)。推送到upstream将遵循映射,并将您推feature2送到他们的feature. Push withsimple将拒绝尝试。
因此,如果您git push没有 refspec,git 将查找默认配置3并基于此构造一个 refspec。对于这种matching情况,它会推送您和他们都拥有的每个分支(因此,如果您都拥有masterand ,则将您的branch推master送到他们的master,并将您的推送到branch他们的branch),但不会对只有你们一个人拥有的分支做任何事情。
如果你给出一些明确的 refspec(s),所有这一切都变得毫无意义:push 操作会推送你给它的 refspecs。此外,不带冒号的 refspec 表示“两端使用相同的名称”,因此master是编写完整长版本的简写方式,refs/heads/master:refs/heads/master.
与 fetch 一样,您的 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运行merge或rebase,再次取决于配置条目以及是否使用--rebase. 您将合并或变基到的提交是其 SHA-1 现在存储在FETCH_HEAD.
请注意,这只会合并或重新设置您当前的分支。
1如手册中所述,fetch 默认为“标记跟随”技巧:它查看标记中的 SHA-1,并查看这些是否存在或将存在于您的存储库中。对于那些现在或将要成为的人,它带来了那个标签。您可以使用 关闭此功能--no-tags。
2对象是存储库实际存储的东西:“blob”(文件)、树(包含文件或更多目录的目录)、提交和“带注释的标签”。每个都有一个唯一的 SHA-1 名称。
3但是,您可以使用每个分支配置覆盖它,并且. 您可以通过转动大量配置旋钮来制作大量难以理解的效果。branch.name.pushremoteremote.name.push