问题是标题。我知道“git fetch”的一般概念,但只是想确认,如果我的本地主分支不是远程主机的祖先会发生什么,将“git fetch -f origin remote-master:local-master”删除所有我的本地主,然后复制远程主分支?
另一个问题是,“ - f”是否等同于“+”?
答案 0 :(得分:0)
git help fetch
说:
-f, --force
When git fetch is used with <rbranch>:<lbranch> refspec, it refuses to
update the local branch <lbranch> unless the remote branch <rbranch> it
fetches is a descendant of <lbranch>. This option overrides that check.
[...]
If the optional plus + is used, the local ref is updated even if it does
not result in a fast-forward update.
这表明两者确实一样。
如果你想确实可以自由地查看source。
答案 1 :(得分:0)
你在这里缺少一个基本的东西。 git fetch
从不从您的存储库中删除任何对象。但是,它可以通过以下方式更改引用,从而可以从存储库中删除对象 。这就是存在危险的危险所在。
在Git中,对象是存储库中四种对象类型中的任何一种。四种对象类型是commit
,tree
,blob
(文件)和tag
(带注释的标记)。这些对象实际上是Git如何存储您的所有内容。如果它不在某个对象中,则它不是&#34;内容&#34; - 大多数情况下,它是关于内容的某种辅助信息。
要命名对象,请为Git指定其哈希ID(a234567...
)。此ID永远不会更改:它始终唯一地表示该特定对象。此外,宇宙中的每个Git都会为同一个对象计算相同的哈希ID - 这看起来有点像魔术,但是Git是如何实现&#34;分布式&#34;作为分布式版本控制系统的一部分。
提交对象在这里特别有趣,因为每个提交对象都命名其他早期提交对象。具体来说,每个提交都有零个或多个父提交。大多数提交只有一个父母;大多数合并提交只有两个父母(但允许更多);除了在新的空存储库中,总是至少有一个没有父项的 root commit 。 (显然,你做的第一个提交不能有先前的提交,因为之前没有提交。)
哈希ID的问题在于它们很大,笨拙,丑陋且难以忘怀。什么是ed4f38babf3d81693a68d06cd0f5872093c009f6
?它的Git版本为2.1.1,但是谁能记得那个?好吧, Git 可以使用标记来记住 我们:参考名称。 Git的Git存储库中的标记v2.1.1
转换为该哈希(实际上是标记对象的ID,但标记对象然后指向提交对象,因此它同样好 - 或者甚至更好:标签对象是内容;见下文。
引用是这些更令人难忘的名称,它们还存储Git的内部哈希ID。引用不是内容,因此不受版本控制。它们只是关于内容的辅助信息。它们对我们人类非常重要,因为分支和标记名称是两种形式的引用,我们希望让Git通过分支和标记名称获取内容。但是它们还在Git中提供另一个关键功能:它们通过使对象可达来保护对象。
可达性最好使用提交。我们从分支名称master
开始。这在Git中转换为哈希ID。哈希ID称为分支的 tip commit 。显然,我们可以找到提交:读取master
,将其转换为哈希ID,然后读取提交。我们已达到master
!
因为每次提交都会记住它的父级,所以我们可以使用提示提交来查找所有早期的提交。 master
的提示的父级 - 或者父母双方,如果它是典型的双父合并提交 - 就在那个提交中。我们获取ID(或两个ID)并使用它来查找先前的提交。我们已经从master
的一角回到了提交或两次提交的一步!
该提交或那些提交具有早期提交的ID。我们可以使用它们来读取所有两个步骤的提交:我们已经从master
的提示两步返回提交了!
这些提交具有更多早期提交的ID。我们使用它们来读取三个步骤的所有提交......依此类推,一直回到root提交。哇,我们已经找到了分支master
上的每个提交,一直回到根目录!我们已经所有这些提交。
在master
上提交而不是怎么样?好吧,假设develop
上有一些提交。我们读取分支名称develop
以获取其提示,并读取该提交以获得其一步返回提交,并读取这些提交以获得两步提交,因此一直回到root。我们已经到达所有这些提交(请注意,我们现在已经两次到达根目录。)
同时,如果feature
上有一些提交不在其他任何一个分支上,那就没问题:我们可以通过其他名称与他们联系。简而言之,那就是可达性:我们从所有名称开始,转换为对象ID。我们使用这些对象ID来查找对象,并使用对象&#39;内容以查找其他对象。我们找到的所有内容都可以。
如果Git存储库中存在我们不以这种方式查找的对象,则这些对象无法访问。它们已被放弃,它们适合删除:垃圾收集,使用git gc
。这个垃圾收集过程是从Git中删除任何内容的唯一正常方法。
引用还有一个额外的转折:Git有一个名为 reflogs 的功能,它们是跟踪每个引用中存储的先前哈希ID的日志文件。 Reflogs是可选的,reflog条目最终到期 - 默认是在30或90天后使它们过期 - 所以它们通常只提供临时保护,但这通常是我们想要的。通常,如果我们想要保留一个对象,我们确保它具有直接引用 - 例如分支或标记名称 - 或者是在具有引用的提交链中。
现在我们可以回答您的主要问题!
fetch
fetch
命令通过与其他Git通信来实现:
找出其他存储库中不在存储库中的对象。 Fetch根据需要带来(部分或全部)这些对象。 (我说这很简单,实际上涉及到一堆信息学理论,但为了我们的目的,它很容易,因为git fetch
只为我们做了。)
了解其引用标识的对象(其分支,标签,注释等)。这实际上是实施步骤1所必需的;但在步骤1完成后,您的Git会将此信息写入一两处。
在这种情况下,您已告诉您的Git了解他们的Git remote-master
(因为您的refspec是remote-master:local-master
或+remote-master:local-master
)。也就是说,refspec的<src>
部分是remote-master
。此名称不是完全限定的,因此如果Git与标记匹配,您的Git将最终将其视为标记,如果它与分支匹配,则将其视为分支。如果它不匹配,这将失败。要控制它可以匹配哪种引用,您可以完全拼写它(例如refs/heads/remote-master
)。让我们假设它匹配一个分支。
请注意,此分支(或标记)名称映射到某个哈希值。使用git ls-remote origin
查看远程origin
上可用的名称和哈希值;很明显你的Git会选择哪个名称和哈希值。
在这种情况下,您还为Git指定了refspec的非空<dst>
部分。这一点很重要。
Git总是在第一步写入信息的地方是FETCH_HEAD
。因此,您的FETCH_HEAD
文件将获取一个哈希值,以及git fetch
命令留下的一些额外信息,供最早版本的git pull
使用。
您的Git 可能在第2步中写入信息的第二个位置来自您的refspec的<dst>
部分。
如果你根本不给git fetch
<dst>
部分,例如git fetch origin remote-master
- 你的Git往往不会在其他任何地方写信息。对于第2步,它可能只写入FETCH_HEAD
然后终止。这在Git-version-dependent方面很复杂。 1
如果你做给git fetch
<dst>
部分,<dst>
部分可以完全合格(refs/heads/local-master
,{{1} },refs/remotes/origin/local-master
等等)或不完全合格。如果它不是完全合格的,Git会试图直觉获得正确的资格;在这种特殊情况下,我们假设refs/tags/local-master
是另一个Git中的分支名称(remote-master
),因此refs/heads/remote-master
将成为分支名称(local-master
)你自己的Git。
仅仅因为你有一个refs/heads/local-master
部分,并不意味着你的<dst>
引用会被更新!如果你没有设置一个强制标志,你的Git会做一些检查。具体来说,你的Git将首先检查你是否有这样的参考。如果没有,一切都很简单:你的Git将使用相应的哈希创建本地引用。如果你做已经拥有它,那么,那么这又是&#34;没有强制标志&#34;:
这是<dst>
和-f
标志的来源.refspec前面的+
设置特定refspec的强制标志。设置强制标志后,+
即使默认规则不更新,也会更新本地refspec。 git fetch
选项在命令行上为每个 refspec设置强制标志,因此-f
,-f
再次强制更新。
现在让我们最后一次看你的问题:
如果我的本地主分支不是远程主服务器的祖先,会发生什么情况,&#34; git fetch -f origin remote-master:local-master&#34;删除所有我的本地主,然后复制远程主分支?
由于您具有git fetch
标志,即使更改不是快进操作(即,您的本地引用不是传入远程哈希ID的祖先),也会更新本地引用。但是你有向后的操作顺序:-f
已经带来了所有对象。此外,git fetch
会将分支提示ID写入文件git fetch
,无论它是否更新FETCH_HEAD
- 即使local-master
更新了git fetch
引用,也可能将一些提交对象暴露给垃圾收集器,因为它们已被取消引用,仍然尚未删除任何内容。如果local-master
有一个reflog,那么reflog将默认保护这些对象30天。
另一个问题是
local-master
是否相当于-f
?
+
标志只会为每个命令行refspec添加-f
。如果只给出一个refspec,它们就完全一样了。如果您提供多个refspec,+
基本上会为每个引用添加-f
(如果它不存在)。
1 具体来说,在Git版本1.8.4或更高版本中,Git会机会性地更新&#34;远程跟踪分支机构。如果您的配置行显示为+
- 通常您会 - 从+refs/heads/*:refs/remotes/origin/*
读取refs/heads/remote-master
后,您的Git会更新您的origin
。这确实需要存在该配置行。请注意,它的refspec中有一个强制标志(refs/remotes/origin/remote-master
)!
2 我认为 +
和其他参考像标签一样工作,但没有测试过。除了fetch-mirror配置之外,默认情况下不会复制除分支和标签之外的任何内容,fetch-mirror配置由notes
配置行控制,应始终设置强制标志。无论哪种方式,这个问题都不会发生。