试图真正掌握 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
具有三个引用名称。两个是分支,一个是标签。(特殊的HEAD
ref 与 具有相同的 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...
master
branch
branch
git fetch
222c4dd...
,它询问另一端“我还需要使用 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
情况,它会推送您和他们都拥有的每个分支(因此,如果您都拥有master
and ,则将您的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.pushremote
remote.name.push