当我运行命令(如
git push
或
git push origin master
我的回购协议看起来
B--C--D <- master
/
A--E--F <- foo-branch
而起源看起来就像
A <- master
push包括提交E和F吗?我知道通常它不包括foo-branch
,但是所有的提交仍然会被push吗?
同样,当我这样做的时候
git clone <some-remote-repo>
我知道我通常会得到一个分支(看起来通常是master
),但是我是否也有其他分支的提交的本地副本,即使我没有得到指向它们头部的指针?
3条答案
按热度按时间b4qexyjb1#
部分取决于交通:git有“哑传输”(比如使用http一次传输一个对象)和“智能传输”(使用
git://
或ssh://
协议,其中两个git相互协商,然后--假设接收方表示没问题--发送方构建一个“瘦包”)。它还部分依赖于命令:例如,如果你要求一个“浅”克隆,或者一个分支,你通常会得到比“普通”克隆少的结果。而且,当你运行
git push
时,你可以选择哪些特定的提交ID,如果有的话,你最初会传递给远程仓库,以及你希望它们使用什么分支名称。不过,现在让我们先忽略浅分支和单分支克隆。
给出以下示例:
和
git push origin master
之间的关系(其refspec可能等同于master:master
,即您尚未配置异常推送),其中您的远程origin
当前已提交A
(对于A
它具有什么分支标签并不重要,只要它 * 具有 *A
)并且假设智能协议,握手和传输协议开始时大致如下:此时,你的git知道了需要什么样的 commits 才能把所有的提交提交到远程:如果你在你的仓库中运行
git rev-list master ^A
(当然,填写A
的实际SHA-1),就会列出这些提交。没有必要排除额外的SHA-1,因为远程origin
除了一个分支外什么都没有,它的提示是commitA
。其内部工作方式是
git push
运行git pack-objects
(使用--thin
),然后它运行git rev-list
,将您要求推送的提交ID传递给它,包含排除项(--not
或前缀^
),用于他们的git发送给你的所有提交ID(在我们的例子中,这只是一个commit-IDA
)。请参阅git rev-list
的文档,特别注意--objects-edge
选项(或者在使用浅层克隆时使用--objects-edge-aggressive
)。因此,
git rev-list
输出提交D
的ID,加上它的树的ID以及该树的所有子树和blob,* 除非 * 它结束(通过取反后的ID,在本例中是排除提交A
的^A
),远程git必须已经拥有它们。然后,它输出提交C
的ID及其树,注意,提交A
具有与其相关联的源树;并且假设提交C
具有 * 相同 * 的树-例如,假设提交C
是B
的还原。在这种情况下,不需要发送C
的树:远程 * 必须 * 拥有它,因为远程已提交A
。(This对象查找可以通过位图来优化。我记得有一篇github博客文章描述了这些位图的开发,它是一个解决方案,可以解决遍历大量提交图的相当缓慢的过程,以便根据一些分支提示ID来查找哪些对象必须已经在某个远程仓库中。这对他们有很大的帮助,因为智能协议中的获取过程与推送过程是对称的:我们只需交换发送和接收角色。)
在任何情况下,
git rev-list
的输出都会提供给git pack-objects --thin
。这提供了所有要获取的对象ID(提交D
,如果需要的话,提交它的树,以及任何需要的子树和blob;提交C
和所需对象;提交B
和所需对象),以及明确 not 要获取的ID:提交A
及其对象,如果在A
之前有提交,则提交那些对象及其对象。pack-objects步骤创建一个delta压缩包,其中“take these objects”对象相对于“don 't take these other objects”对象进行压缩。举一个超级简化的例子,假设
A
的树包含一个10 MB的文件,其最后一行是“The end”。假设B
的树包含一个几乎相同的文件,只是去掉了“The end”。Git可以将这个文件压缩成指令“start with blob,then remove the last line”。这些指令的长度远小于10 MB,并且允许在“精简包”中使用。这个“thin pack”是通过网络电话连接(或任何连接两个git示例的数据线)发送的,接收方会将它“加厚”成普通的git pack(普通pack不允许对不在包中的对象进行增量压缩)。
好吧,这是相当长的,但它归结为:你的git不会发送
F
(因为你没有要求它这样做),也不会发送E
(因为你没有发送F
),也不会查看这两个提交所附带的两棵树。如果你运行
git clone
而不运行--single-branch
,你的克隆操作会像往常一样通过调用remote来启动,并获得一个remote的 all 引用列表(就像push!一样)。[数百人被剪掉]
然后你的git会向遥控器请求几乎所有的东西。(在这种情况下,"差不多"是不必要的,但是如果他们给你的是
refs/
而不是heads/
和tags/
,你可能就得不到这些了。你还可以控制你的git带来什么标签。这里的细节有点混乱,但是在大多数普通的仓库中,克隆将带来所有标签。)你这样说是在错误的假设上犯了错:
我知道我通常会得到一个分支(似乎通常是master分支),但是我是否也有其他分支的提交的本地副本,即使我没有得到指向它们头部的指针?
你的git请求并得到了它们的所有分支,但是你的git也 * 重命名了 * 它们,它们都被重命名为
refs/remotes/
命名空间,以remote的名字命名(通常是origin
,但是-o <name>
或--origin <name>
会改变这种情况)。它们的refs/heads/master
变成了你的refs/remotes/origin/master
;他们的refs/heads/maint
变成了你的refs/remotes/origin/maint
;和/或其他信息。您将看到所有这些(略略),它告诉
git branch
显示远程跟踪分支。(再一次,"remote-tracking branches"就是全名以refs/remotes/
开头的分支。来自特定远程的git fetch
通过存储库中的fetch =
指令更新相应的远程跟踪分支。s配置条目。)如果运行
git branch
或git status
,您看到的master
实际上是在clone
中的最后一步创建的。它实际上并不运行git checkout
--它直接内置了相同的代码--但本质上,作为克隆的最后一步操作,它运行的是某个分支名称的git checkout *branch-or-sha1*
(或者,作为最后的尝试,一个原始的SHA-1给出一个"分离的HEAD")。git clone
的值,或者HEAD
所指向的分支,如果你的分支能够解决这个问题,或者它是在协议协商过程中提供的。1如果这些都失败了--假设您没有指示
clone
进程 * not * 执行校验--git clone
将从远程数据库获取的原始SHA-1作为远程数据库的HEAD
进行校验(在上面的ls-remote
输出示例中,这是aa826b651ae3012d1039453b36ed6f1eab939ef9
)。1请注意,
HEAD
看起来是一个原始的SHA-1。很长一段时间以来,git中有一个bug,如果这个SHA-1对应于至少 * 两个 * 分支名称,git clone
就不知道要检查哪个分支。git的人可以添加一个选项,通过这个选项一个git可以告诉另一个git "HEAD指向分支X"。所以现在,即使导入的HEAD
与多个导入的refs/heads/*
名称匹配,git也可以告诉使用哪一个。dgtucam12#
内部的工作方式是
git push
运行git pack-objects
(带有--thin
),git pack-objects
随后运行git rev-list
,并向其传递您要求推送的提交ID此对象查找可通过位图进行优化。
Git 2.4.7(2015年第三季度)之后就没有了
参见Jeff King (
peff
)的commit c8a70d3(2015年7月1日)。(2015年7月10日,由Junio C Hamano --
gitster
--合并至commit ace6325)第1011章:修剪提交时禁用
--use-bitmap-index
签署人:杰夫·金
可达性位图没有足够的信息来告诉我们哪些提交可能改变了路径"foo",所以当前的代码会产生错误的答案:
(it默认忽略"
foo
"限制器)。相反,我们应该退回到普通遍历(退回而不是抱怨是可以的,因为--use-bitmap-index
是一个纯粹的优化,可能因为其他原因而不起作用,比如仓库中没有位图)。这一点在Git 2.26(Q1 2020)中已经提到:对象可达性位图机制和部分克隆机制没有准备好一起工作,因为部分克隆使用的一些对象过滤标准本质上依赖于对象遍历,但位图机制是一种绕过对象遍历的优化。
然而,在某些情况下,他们可以一起工作,他们被教导了这些。
参见Junio C Hamano (
gitster
)的commit 20a5fd8(2020年2月18日)。请参阅commit 3ab3185、commit 84243da、commit 4f3bd56、commit cc4aa28、commit 2aaeb9a、commit 6663ae0、commit 4eb707e、commit ea047a8、commit 608d9c9、commit 55cb10f、commit 792f811和commit d90fe06(2020年2月14日),以及commit e03f928、commit acac50d、commit 551cf8b(2020年2月13日),均由Jeff King (
peff
)提供。(由Junio C Hamano --
gitster
--合并至commit 0df82d9,2020年3月2日)我的天啊!拒绝使用pathspecs进行位图遍历
签署人:杰夫·金
rev-list已经拒绝使用具有路径规范限制的位图,因为c8a70d3509("
rev-list
:删除提交时禁用--使用位图索引",2015 - 07 - 01,Git v2.5.0-rc2--merge).但这不仅对rev-list是正确的,而且对任何调用
prepare_bitmap_walk()
的人也是正确的;代码不适合处理这个案子我们从来没有注意到,因为唯一的其他调用者永远不会通过路径规范限制器。
但是无论如何,我们还是把检查推进到
prepare_bitmap_walk()
中,这是一个更合理的位置,因为调用者不需要知道细节(而且必须准备好回到常规遍历,因为存储库中可能没有位图)。这也会让我们为
_is
_处理这个案例的一天做好准备,但这是不太可能的。例如,我们可以使用位图来生成一组提交,然后对每个提交进行diff,看看它是否与路径规范匹配。这将比实际遍历提交的简单遍历稍快。
但是您可能会做得更好,利用更新的commit-graph特性,使遍历提交变得非常便宜。
在Git 2.27(Q2 2020)中,带有对象过滤器"
--filter=tree:0
"的对象遍历现在可以利用压缩位图。2010年5月10日,第12届全国人民代表大会常务委员会第30次会议通过《中华人民共和国国籍法》。
2010年5月4日,《中国日报》第35期(2010年5月4日)。
(2020年5月13日,由Junio C Hamano --
gitster
--在commit 69ae8ff中合并)我的天啊!使对象过滤函数通用
签署人:泰勒·布劳
在("
pack-bitmap
:实现BLOB_NONE
过滤",2020 - 02 - 14,Git v2.26.0-rc0--merge在batch #8中列出),为"LOFC_BLOB_NONE
"过滤器添加了对位图的过滤支持。在未来,我们希望增加对过滤器的支持,这些过滤器的行为就好像它们排除了某种类型的对象,例如,深度为0的树深度过滤器。
为此,请使一些用于过滤的函数更具通用性,如"
find_tip_blobs
"和"filter_bitmap_blob_none
",以便它们可以处理任意对象类型。为此,创建"
find_tip_objects
"和"filter_bitmap_exclude_type
",并根据它们重新定义上述函数。使用Git 2.32(Q2 2021),优化"
rev-list
--use-bitmap-index--objects
(man)角格,使用负标签作为停止点。它参与描述在
git clone
和git push
期间"克隆"和"推送"了什么内容,此时请注意标记:参见commit 540cdc1(2021年3月22日),作者Patrick Steinhardt (
pks-t
)。(由Junio C Hamano --
gitster
--合并于commit 58840e6,2021年4月7日)签署人:帕特里克·斯坦哈特ps@pks.im.
当准备位图遍历时,我们首先通过迭代挂起对象集来建立拥有和想要对象集:如果一个对象被标记为不感兴趣,那么它就被声明为我们已经拥有的对象,否则就被声明为我们想要的对象。
这两个集合然后被用来计算我们需要获得哪些传递性引用对象。
这里的一个特例是标记对象:当一个标签被请求时,我们将它解析为它的第一个非标签对象,并将解析后的对象以及标签本身添加到have或want集合中。
假设uninteresting-property总是传播到被引用的对象,很明显,如果标记是不感兴趣的,那么它的子对象也是不感兴趣的,反之亦然。
但是我们无法传播该标志,这实际上意味着被引用的对象将始终是有趣的,除非它们已经被显式标记为不感兴趣。
这种贴错标签不会影响正确性:我们现在在“wants”集合中具有它,并且假定我们稍后对“wants”和“haves”集合的位图进行
AND NOT
运算,则很明显结果必须相同。B,我们现在开始不必要地遍历标记的引用对象,以防它是不感兴趣的,即使我们知道每个引用对象无论如何都是不感兴趣的。
在最坏的情况下,这可能导致完全的图遍历,只是为了确定我们不关心任何对象。
通过将
UNINTERESTING
标志传播到标记对象的指针对象来修复此问题,并将带有负修订的基准添加到p5310。通过
linux.git
测试,这显示了一些不错的性能优势:虽然大多数性能指标评测可能都在噪声范围内,但新添加的5310.9和5310.10性能指标评测的表现始终更好。
在Git 2.32(Q2 2021)中,添加了一个配置变量,强制某些引用的提示被赋予可达性位图。
请参见Taylor Blau (
ttaylorr
)的commit 3f267a1、commit 483fa7f、commit dff5e49(2021年3月31日)。(2021年4月13日由Junio C Hamano --
gitster
--合并至commit 0623669)第1011章:初始提交
签署人:泰勒·布劳
添加一个新的“位图”测试工具,该工具可用于列出已接收位图的提交。
理论上,确定的测试器可以运行“
git rev-list --test-bitmap <commit>
”(man)来检查“<commit>
”是否接收到位图,因为当“--test-bitmap
”找不到所请求的提交时,它以非零代码退出。但这是一个值得依赖的可疑行为,因为可以说'
git rev-list
'可以继续它的对象遍历,而位图覆盖了它之外的提交。这将用于测试“
pack.preferBitmapTips
”的行为还有:
建议人:杰夫·金
签署人:泰勒·布劳
当用位图写新包时,有时指示一些引用前缀是方便的,这些引用前缀在选择哪些提交接收位图时应该接收优先级。
一个真正有动机的调用者可以通过设置'
pack.islandCore
'来实现这一点(因为核心岛中的所有提交都被类似地标记为首选),但这要求调用者选择使用delta岛,他们可能想也可能不想这样做.引入新的多值配置“
pack.preferBitmapTips
”,以允许调用方指定引用前缀列表。所有前缀包含在'
pack.preferBitmapTips
'中的引用将把它们的提示标记为“首选”,就像提交被标记为首选以供'pack.islandCore
'选择一样。动词“
prefer
“的选择是有意的:在对象上标记NEEDS_BITMAP
标志 * 不 * 保证该对象将接收位图。它只是保证该提交将比同一窗口中的任何 * 其他 * 提交收到一个位图,间隔为
bitmap_writer_select_commits()
。这个补丁增加的测试也反映了这个怪癖。
它只测试在更改'
pack.preferBitmapTips
'的值以包含位图后是否为位图选择了提交(默认情况下不接收位图)。其他提交可能会丢失其位图,这是选择过程工作方式的副产品(
(bitmap_writer_select_commits()
在看到带有NEEDS_BITMAP
标志的提交后忽略窗口的剩余部分)。此配置将有助于为多包位图选择重要的引用,因为它们不遵守相同的
pack.islandCore
配置。(They可以,但这样做可能会混淆,因为受delta岛配置影响的是包,而不是位图)。
例如,在一个fork网络仓库(一个将给定仓库的所有fork都列为远程仓库的仓库)中,将
pack.preferBitmapTips
设置为'refs/remotes/<root>/heads
'和'refs/remotes/<root>/tags
'是很有用的,其中'<root>
'是一个不透明的标识符,引用位于fork链的基础上的仓库.git config
现在在其手册页中包括:pack.preferBitmapTips
的第一个字符当选择哪些提交将接收位图时,在“选择窗口”中的任何其他提交中,首选任何引用顶端的提交,该提交是此配置的任何值的后缀。
请注意,将此配置设置为
refs/foo
并不意味着必须选择refs/foo/bar
和refs/foo/baz
顶端的提交。这是因为提交是从一系列可变长度的窗口中为位图选择的。如果在窗口中看到任何引用(此配置的任何值的后缀)顶端的提交,则会立即优先于该窗口中的任何其他提交。
使用Git 2.33(Q3 2021),在构建可达性位图时避免重复工作。
参见commit aa9ad6f(2021年6月14日),作者Jeff King (
peff
)。(由Junio C Hamano --
gitster
--合并至commit 1ef488e,2021年7月8日)签署人:杰夫·金
如果一个对象已经在我们正在构建的可达性位图中被提及,那么根据定义,它可以到达的所有对象都是如此。
当我们看到提交已经在位图中时,我们有一个优化来停止遍历提交,但是我们没有对树做同样的操作。
通常不可避免地要递归到树中,因为位图还没有覆盖提交(因为大多数提交通常都有唯一的顶层树)。
但它们通常有与其他提交共享的子树(即提交
_didn
't_touch的所有子树)。并且这些提交中的一些(及其树)可能被位图覆盖。
通常这不是什么大问题,因为在整个遍历过程中,我们总共只访问这些子树一次。
但是如果你有大量的非位图提交,并且你的树很大,那么你可能会毫无理由地打开很多子树。
我们可以在这里对提交使用同样的优化:当我们要打开一个树时,看看它是否在位图中(要么是我们正在构建的,要么是"看到的"位图,当做一个集差时,它覆盖了位图的不感兴趣的一面)。
这一点非常有效,因为我们会在访问任何树之前访问所有提交。
所以即使在这样的历史中:
如果"A"在磁盘上有一个位图,但"B"没有,那么在查看B的树之前,我们已经对A的结果进行了OR运算(因此我们实际上只查看B所接触的树)。
对于大多数存储库,p5310产生的时序并不引人注目。
任何改进都在噪声范围内(测试7中的+3.1%肯定是噪声,因为我们没有递归到树中,因此新代码甚至没有运行)。
git.git的结果同样没有意义。
但这里有一些来自其他真实世界的数据库(不公开)。
这棵树的大小和linux.git差不多,但是有大约16k的refs(所以位图覆盖面不够完整):
下面是另一个非常大的树(约340k个条目)和相当多的引用(约10k):
此修补程序在这些较大的情况下提供了实质性的改进,但在较小的情况下有一些缺点(与实际的树遍历相比,位图检查的成本相当小)。
还是Git 2.33:with Git 2.33(Q3 2021)修正了重新打包和使用压缩位图之间的竞争。
参见Jeff King (
peff
)的commit dc1daac(2021年7月23日)。(由Junio C Hamano --
gitster
--合并于commit 9bcdaab,2021年8月2日)签署人:杰夫·金
当pack-objects向其要打包的对象列表中添加一个条目时,它可能会标记打包文件和包含该文件的偏移量,稍后我们可以使用它来逐字输出对象。
如果在我们运行时删除了包文件(例如,被另一个运行"
git repack
"(man)的进程删除),如果无法打开包文件,我们可能会在use_pack()
中死亡。我们在4c08018("
pack-objects
:保护免受消失的包",2011 - 10 - 14,Git v1.7.8-rc0--merge),方法是确保我们可以在将包记录为源之前打开它。这会检测到一个在生成打包列表时已经消失的包,因为我们保持包的文件描述符(或一个mmap窗口)打开,这意味着我们可以稍后访问它(除非超过core. packedgitlimit)。
稍后添加的位图代码不会这样做;它会将条目添加到packlist,而不检查packfile是否仍然有效,因此容易受到此竞争的攻击。
它需要与4c08018相同的处理。
但是,与其只在一个位置添加它,不如在打开位图时简单地打开并检查包文件。
从技术上讲,您可以使用. bitmap,而无需查看. pack文件(例如,如果您只是打印对象列表而不访问它们),但尽早这样做要简单得多。
这涵盖了包的所有后续直接使用(由于缓存的描述符),而不必直接检查每个包。
例如,在pack-objects中,我们需要保护packlist条目,但我们也可以作为
reuse_partial_pack_from_bitmap()
特性的一部分直接访问包。此修补程序涵盖了这两种情况。
kr98yfug3#
previous answer(Git 2.4.7,2015年第三季度,直到Git 2.33,2021年第三季度)展示了位图(store reachability information about the set of objects in a packfile, or a multi-pack index (MIDX)的结构)是如何演变的。
Git 2.37也是如此:
然而,多包索引代码并没有保护包文件(它将依赖于)在使用中被删除,这一点已经在Git 2.37(Q3 2022)中得到了纠正。
参见Taylor Blau (
ttaylorr
)的commit 4090511、commit 5045759、commit 58a6abb、commit 44f9fd6(2022年5月24日)。(2022年6月3日由Junio C Hamano --
gitster
--合并至commit 0916804)第1011章:打开MIDX位图时检查首选包的有效性
签署人:泰勒·布劳
当pack-objects向其打包列表中添加一个条目时,它会标记包含该对象的打包文件和偏移量,我们稍后可能会在逐字重用过程中使用这些对象(参见
write_reused_pack_verbatim()
)。如果所讨论的包文件在后台被删除(例如,由于并发的
git repack
(man)),我们将由于调用use_pack()
而导致die(),除非我们在包本身上有一个打开的文件描述符。(“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“2011-10-14,Git v1.7.8-rc 0--merge)通过在将其记录为有效源以供重用之前提前打开包来解决这个问题。
4c08018的处理方式意味着我们可以容忍消失的包,因为它确保了我们在任何包上总是有一个打开的文件描述符,我们将其标记为可重用的有效源。
这使得竞争只在我们需要关闭一个打开的包的文件描述符(c.f.,
packfile.c::get_max_fd_limit()
的调用者)* 和 * 该包被删除时发生,在这种情况下,我们将抱怨一个包无法访问和die()
。压缩位图代码也执行此操作,因为在dc1daac(“
pack-bitmap
:打开位图时检查包的有效性”,2021-07-23,Git v2.33.0-rc 0--merge)它也容易受到相同的竞争。MIDX位图代码不执行此操作,因此容易受到相同争用的攻击。
对负责打开多包位图的首选包的例程应用与dc1daac相同的处理,以结束此竞争。
此修补程序专门处理“首选”包(参见
Documentation/technical/pack-format.txt
中的“多包索引反向索引”部分),因为包对象依赖于在reuse_partial_packfile_from_bitmap()
中逐字重复使用该包的确切块。因此,如果无法加载该包,位图的实用性将大大降低。
与dc1daac类似,我们可以在
reuse_partial_packfile_from_bitmap()
中添加此检查,因为可以使用MIDX.bitmap
而无需打开它的任何包。但是,尽早进行检查会更简单,因为它涵盖了首选包的所有直接用途。
请注意,早期执行此检查需要我们也早期调用
prepare_midx_pack()
,因此将该循环的相关部分从load_reverse_index()
移动到open_midx_bitmap_1()
。注意:另请参阅Git 2.38(Q3 2022)的新设置
git -c push.useBitmaps=false push
,以禁用git push
的打包。Git 2.39(Q4 2022)确保在使用后释放与三角洲岛相关的结构。
2022年11月17日,第1120期,第1121期。
(2022年11月23日,由Junio C Hamano --
gitster
--在commit a655f28中合并)第1001章:使用后的自由岛相关数据
签署人:黄家辉
合著者:埃瓦·阿恩菲约德·比贾马森
签署人:泰勒·布劳
在我的用例中,涉及到www.example.com上的771个Linux岛kernel.org,这将减少大约25 MB的内存使用。
其中大部分来自
free_remote_islands,
,因为free_config_regexes
只节省了大约40 k。此内存在内存密集型压缩过程的早期保存,使其可用于长过程的剩余部分。