gpt4 book ai didi

git - “git fetch --tags --force”和 “git pull ”是可交换的操作吗?

转载 作者:行者123 更新时间:2023-12-02 11:25:47 31 4
gpt4 key购买 nike

通常,git标签是对提交的固定引用。但是有时它们被用来标记某些事件(last-buildbase-line等),并且它们经常更改。

我有一个脚本,可以从引用资料库中刷新那些“ float ”标签。

git fetch --tags --force

并从一个分支中 pull 出:
git pull origin <mybranch>

我知道许多git用户警告使用 float 标签,但我不得不对此进行处理。我的问题是:

如果分支被这些 float 标记之一标记...命令的执行顺序重要吗?

恐怕 git pull在本地存在时不会刷新标签,并且如果它先运行,它可能会与所有标签的引用一起工作。
git pull具有 --force选项,但是选项 --no-tags的帮助部分将默认行为解释为:

By default, tags that point at objects that are downloaded from the remote repository are fetched and stored locally.



这是否意味着应该首先下载对象才能刷新标签?在这种情况下, git pull应该放在第一位。

哪个是正确的顺序?

最佳答案

这进入了Git较为模糊的角落之一,但最后的答案是“最初使用哪个顺序都没有关系”。但是,我建议通常避免使用git pull,并且无论如何也不要在脚本中使用它。另外,正如您将在下面看到的那样,它确实以不同的方式准确地影响了何时获取。因此,我建议您先运行自己的git fetch,然后根本不使用git pull
git fetch
普通的git fetch(不带--tags)默认情况下使用怪异的混合标签更新,尽管每个 Remote 都可以定义一个默认标签选项来覆盖此默认值。您引用的是奇怪的混合体:指向从远程存储库下载的对象的标记被获取并存储在本地。用于此的基 native 制有些棘手,我将在以后讨论。

--tags参数中添加git fetch与在命令行上指定refs/tags/*:refs/tags/*具有几乎相同的效果。 (稍后我们会看到区别。)请注意,这在refspec中没有设置force标志,但是测试表明无论如何都对获取的标签进行了强制更新。

添加--force与在每个显式refspec中设置force标志具有相同的效果。换句话说,git fetch --tags --force大致等同于运行git fetch '+refs/tags/*:refs/tags/*':如果 Remote 上有标记refs/tags/foo指向提交1234567...,则您的Git将替换任何现有的refs/tags/foo,因此您现在拥有自己的refs/tags/foo也指向commit 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

(从之前没有 --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]

稍后,我们将回到这一点。

提取可能取决于找到的其他引用规范(通常由 remote.origin.fetch配置条目控制),以更新一组远程跟踪分支,并创建或更新一些标记。如果将您配置为获取镜像,并且将更新refspec设置为 +refs/*:refs/*,那么您将获得所有内容。请注意,此refspec设置了force标志,并覆盖所有分支,所有标签,所有远程跟踪分支和所有注释。关于何时使用什么refspec的细节还有更多晦涩的细节,但是使用 --tags(带有或不带有 --force)不会覆盖配置条目(尽管编写一组明确的refspecs确实如此,所以这是一种方法-也许是唯一的方法- --tags与众不同从写出 refs/tags/*:refs/tags/*)。

您自己的引用空间(通常是您自己的远程跟踪分支和标签)中的更新确实很重要,但是...对于 pull而言并不重要,我们将在下一部分中看到。
git pull
我想说的是 git pull仅运行 git fetch,然后运行第二个Git命令,除非您指示它使用 git merge,否则第二个命令默认为 git rebase。这是正确且正确的,但途中有一个晦涩的细节。在将 git fetch重写为C代码之前,这更容易说:在返回脚本时,您可以遵循脚本的 git fetchgit merge命令并查看实际参数是什么。

git pull运行 git mergegit rebase时,它不使用您的远程跟踪分支和标记。而是使用 FETCH_HEAD中留下的记录。

如果查看上面的示例,您会发现它们告诉我们,最初, refs/heads/master上存储库中的 git.kernel.org指向commit e05806d...。在我运行 git fetch --tags之后,新的 FETCH_HEAD文件告诉我们 refs/heads/master上存储库中的 git.kernel.org指出了(在我运行 fetch时,它可能现在已经更改了)来提交 d7dffce...

git pull运行 git mergegit 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来更新所有内容。现在, Remote 有可能再次移动了 last-build。如果是这样,您将获得新值,并更新您的本地标签。

按照这种顺序,您可能从未见过 8888888...。您可能有一个 merge 了该提交的分支,但不知道该标签的提交—现在您要更新标签,现在也不知道该标签的 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 ,尽管此处未使用单词“peeled”)。

然后,默认情况下,您的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 mergegit rebase,使用当前分支的上游配置 merge 或重新设置基础。您可以根据需要在任意多个分支重复此操作,根本不需要运行 git pull(及其冗余 fetch)。

关于git - “git fetch --tags --force”和 “git pull <branch>”是可交换的操作吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41708925/

31 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com