通常,git标签是对提交的固定引用.但有时它们被用来纪念某些事件(last-build
,base-line
,等),他们经常改变.
我有一个脚本,从参考存储库刷新那种"浮动"标签.
git fetch --tags --force
并从一个分支拉出:
git pull origin
我知道很多git用户警告使用浮动标签,但我不得不处理这个问题.我的问题是:
如果分支由其中一个浮动标记标记...命令的执行顺序是否重要?
我担心git pull
当它们存在于本地时不会刷新标记,并且如果它首先运行,它可能与所有标记的引用一起工作.
git pull
有一个--force
选项,但该选项的帮助--no-tags
解释了默认行为:
默认情况下,指向从远程存储库下载的对象的标记将被提取并存储在本地.
这是否意味着应首先下载对象以便能够刷新标签?在那种情况下git pull
应该先行.
哪个订单正确?
这进入了Git较为模糊的角落之一,但最终答案是"最初使用哪个订单并不重要".但是,我建议git pull
一般避免使用它,并且永远不要在脚本中使用它.此外,这非常重要,以不同的方式,正是当你取,因为我们将在下面看到.所以我建议先运行自己的git fetch
,然后根本不使用git pull
.
git fetch
普通git fetch
(不--tags
)默认使用奇怪的混合标记更新,尽管每个远程可以定义一个覆盖此默认值的默认标记选项.奇怪的混合是您所引用的:指向从远程存储库下载的对象的标记被提取并存储在本地. 这个潜在的机制有点棘手,我会留待以后.
添加--tags
到git fetch
参数具有几乎为指定,在命令行上相同的效果,refs/tags/*:refs/tags/*
.(稍后我们会看到差异.)请注意,这没有在refspec中设置强制标志,但测试表明所取出的标签仍然是强制更新的.
添加--force
与在每个显式refspec中设置force标志具有相同的效果.换句话说,git fetch --tags --force
大致相当于运行git fetch '+refs/tags/*:refs/tags/*'
:如果远程标记refs/tags/foo
指向提交1234567...
,您的Git将替换任何现有的,refs/tags/foo
以便您现在拥有自己refs/tags/foo
也指向提交1234567...
.(但正如在实践中所观察到的那样,即便只是这样做--tags
.)
请注意,在所有情况下,都会git fetch
写入有关它所获取内容的信息FETCH_HEAD
.例如:
$ cat .git/FETCH_HEAD e05806da9ec4aff8adfed142ab2a2b3b02e33c8c branch 'master' of git://git.kernel.org/pub/scm/git/git a274e0a036ea886a31f8b216564ab1b4a3142f6c not-for-merge branch 'maint' of git://git.kernel.org/pub/scm/git/git c69c2f50cfc0dcd4bcd014c7fd56e344a7c5522f not-for-merge branch 'next' of git://git.kernel.org/pub/scm/git/git 4e24a51e4d5c19f3fb16d09634811f5c26922c01 not-for-merge branch 'pu' of git://git.kernel.org/pub/scm/git/git 2135c1c06eeb728901f96ac403a8af10e6145065 not-for-merge branch 'todo' of git://git.kernel.org/pub/scm/git/git
(从之前的fetch运行开始--tags
,然后):
$ git fetch --tags [fetch messages] $ cat .git/FETCH_HEAD cat .git/FETCH_HEAD d7dffce1cebde29a0c4b309a79e4345450bf352a branch 'master' of git://git.kernel.org/pub/scm/git/git a274e0a036ea886a31f8b216564ab1b4a3142f6c not-for-merge branch 'maint' of git://git.kernel.org/pub/scm/git/git 8553c6e5137d7fde1cda49817bcc035d3ce35aeb not-for-merge branch 'next' of git://git.kernel.org/pub/scm/git/git 31148811db6039be66eb3d6cbd84af067e0f0e13 not-for-merge branch 'pu' of git://git.kernel.org/pub/scm/git/git aa3afa0b4ab4f07e6b36f0712fd58229735afddc not-for-merge branch 'todo' of git://git.kernel.org/pub/scm/git/git d5aef6e4d58cfe1549adef5b436f3ace984e8c86 not-for-merge tag 'gitgui-0.10.0' of git://git.kernel.org/pub/scm/git/git [much more, snipped]
我们将在短时间内回到这一点.
获取可能取决于它找到的任何其他refspec-这通常由remote.origin.fetch
配置条目控制- 更新一些远程跟踪分支,并创建或更新一些标记.如果你被配置为一个获取镜像,你的更新refspec +refs/*:refs/*
,你可以得到一切.请注意,此refspec设置了强制标志,并带来所有分支,所有标记,所有远程跟踪分支和所有注释.关于refspecs使用什么的更多模糊细节,但是使用--tags
,有或没有--force
,不会覆盖配置条目(而写一组明确的refspecs,所以这是一种方式 - 可能是唯一的方式 - --tags
不同于写出refs/tags/*:refs/tags/*
).
您自己的参考空间中的更新 - 您自己的远程跟踪分支和标签,通常是重要的,但是......不是pull
,正如我们将在下一节中看到的那样.
git pull
我想说git pull
只是运行git fetch
后跟第二个Git命令,其中第二个命令默认为默认,git merge
除非你指示它使用git rebase
.这是真实和正确的,但在方式上有一个模糊的细节.在git fetch
重写为C代码之前,这更容易说:当它是一个脚本时,您可以按照脚本git fetch
和git merge
命令查看实际参数是什么.
当git pull
运行git merge
或者git rebase
,它不使用您的远程跟踪分支和标签.相反,它使用留下的记录FETCH_HEAD
.
如果你检查上面的例子,你会看到他们告诉我们最初,refs/heads/master
在git.kernel.org
指向提交的存储库中e05806d...
.在我运行之后git fetch --tags
,新FETCH_HEAD
文件告诉我们refs/heads/master
在git.kernel.org
指向的存储库中(当我运行时fetch
,它可能已经改变了)提交d7dffce...
.
当git pull
运行git merge
或git rebase
,它通过穿过这些原始SHA-1的数字. 因此,引用名称解析的内容并不重要.在git fetch
我跑事实上更新做origin/master
:
$ git rev-parse origin/master d7dffce1cebde29a0c4b309a79e4345450bf352a
但即使它没有,git pull
也会d7dffce1cebde29a0c4b309a79e4345450bf352a
转到第二个命令.
所以,假设你没有获取标签--force
并获得了对象1234567...
.进一步假设,假若你是获取标签与力量,这将是的结果git rev-parse refs/tags/last-build
,而是因为你根本没有用--force
,你自己的仓库左边last-build
指向8888888...
(在中国:-)一个非常幸运的提交).如果你个人说"告诉我last-build
"你会得到修改8888888...
.但是git pull
知道它已经得到了1234567...
,无论发生了什么,它只会将数字传递1234567...
给它的第二个命令,如果事情需要它.
再次,它得到了这个数字FETCH_HEAD
.所以这里的重要内容是(完整的)内容FETCH_HEAD
,这取决于你是否使用-a
/ 获取--append
.您只需要/想要--append
在这里不适用的特殊情况(当您从多个单独的存储库中获取,或者为了调试目的而在单独的步骤中获取时,或某些此类).
如果你想要/需要你的last-build
标签得到更新,你将不得不git fetch --tags --force
在某个时刻运行- 现在我们遇到了原子性问题.
假设您运行git fetch
,有或没有--tags
,有或没有--force
,可能通过运行没有git pull
运行.您现在已在本地提交,名称指向(未更新)或(已更新).现在你运行更新所有内容.现在,遥控器可能会再次移动.如果是这样,您将获得新值,并更新您的本地标记.git fetch
--tags
1234567...
last-build
8888888...
1234567...
git fetch --tags --force
last-build
按照这个顺序,你可能从未见过8888888...
.您可能有一个包含该提交的分支,但不知道该标记的提交 - 现在您正在更新您的标记,您现在也不会知道8888888...
该标记.这是好事,坏事还是无动于衷?随你(由你决定.
git pull
由于git pull
仅运行git fetch
后跟第二个命令,您可以git fetch
自己运行,然后运行第二个命令.这使您可以完全控制fetch
步骤,并允许您避免冗余提取.
由于您确实控制了该fetch
步骤,因此您可以使用refspecs精确指定您想要更新的内容.现在是时候访问奇怪的混合标签更新机制了.
获取您方便并运行的任何存储库git ls-remote
.这将显示git fetch
连接时看到的内容:
$ git ls-remote | head From git://git.kernel.org/pub/scm/git/git.git 3313b78c145ba9212272b5318c111cde12bfef4a HEAD ad36dc8b4b165bf9eb3576b42a241164e312d48c refs/heads/maint 3313b78c145ba9212272b5318c111cde12bfef4a refs/heads/master af746e49c281f2a2946222252a1effea7c9bcf8b refs/heads/next 6391604f1412fd6fe047444931335bf92c168008 refs/heads/pu aa3afa0b4ab4f07e6b36f0712fd58229735afddc refs/heads/todo d5aef6e4d58cfe1549adef5b436f3ace984e8c86 refs/tags/gitgui-0.10.0 3d654be48f65545c4d3e35f5d3bbed5489820930 refs/tags/gitgui-0.10.0^{} 33682a5e98adfd8ba4ce0e21363c443bd273eb77 refs/tags/gitgui-0.10.1 729ffa50f75a025935623bfc58d0932c65f7de2f refs/tags/gitgui-0.10.1^{}
你的Git从远程Git获取所有引用及其目标的列表.对于带有(注释)标记的引用,这也包括标记对象的最终目标:就是gitgui-0.10.0^{}
这里.此语法表示去皮标签(请参阅gitrevisions
,但此处不使用"去皮"一词).
默认情况下,你的Git会带来每个分支 - 所有命名的东西refs/heads/*
- 通过询问它们指向的提交,以及完成这些提交所需的任何其他提交和其他对象.(您不必下载已有的对象,只需要那些缺少但需要的对象.)然后,您的Git可以浏览所有剥离的标记,以查看是否有任何标记指向其中一个提交.如果是这样,您的Git接受或不接受--force
模式,具体取决于您的获取 - 给定标记.如果该标记指向标记对象,而不是直接指向提交,则Git也会将该标记对象添加到集合中.
在1.8.2之前的Git版本中,Git错误地应用了分支规则来推送标签更新:--force
只要结果是快进,它们就被允许.也就是说,先前的标记目标仅需要是新标记目标的祖先.这只会影响轻量级的标签,显然,在任何情况下的Git版本1.8.2及以上有"永远不更换标签--force
的行为" 推.然而,Git 2.10.x和2.11.x的观察行为是标签在使用时被取代--tags
.
但无论如何,如果您的目标是以通常的方式强制更新所有标签和所有远程跟踪分支,那么git fetch --tags --force --prune
就会这样做; 或者您可以git fetch --prune '+refs/tags/*:refs/tags/*' '+refs/heads/*:refs/remotes/origin/*'
使用+
语法强制标记和远程跟踪分支更新.(这--prune
是通常可选的.)force标志可能是不必要的,但在这里至少是无害的,并且可能在某些Git版本中做一些有用的事情.现在您的标签和远程跟踪分支已更新,您可以使用git merge
或git rebase
根本不使用任何参数,使用当前分支的上游配置进行合并或变基.您可以根据需要为多个分支重复此操作,从不需要运行git pull
(具有冗余fetch
).