仅将最新提交拉到浅克隆

Pull only latest commits to shallow clone

我看到过很多关于合并成浅拉的问题,但大多数似乎已经过时了,还有一些不清楚。

我们的 git 存储库历史是巨大的。这是由于过去错误地提交了大量文件造成的(这些文件已被删除)。 执行完整克隆需要很长时间,因为它会提取这些文件,然后才删除它们。它还会导致计算机上出现大量 .git 目录。

为了解决这个问题,我们执行了深度为 1 的浅层克隆。 这一切都很好,我们能够工作并提交并合并回 master。 但是,如果对 master 进行了更改,那么我需要将它们拉入我的分支。这是问题开始的地方。 pull 现在返回并像完整克隆一样获取 master 的所有历史记录。 我需要的只是自上次拉动以来的变化。

那么,有什么方法可以让它这样做吗? 另一个深度为 1 的拉动能解决我的问题吗?

我正在处理的分支是一个特殊功能,需要一些时间才能完成,因此在合并之前将运行 "parallel"交给master一段时间。 拉动深度 1 会断开我的分支与主分支的连接,从而阻止我合并回来吗?

别想pull;这就像试图同时揉你的肚子和拍你的头。可以做到,但最好掌握每一半之后再做。 :-) 所以,把它分解成它的组成部分:

  1. fetch
  2. merge

现在,记住 clone 本质上是 init + remote add + fetch + checkout,我们可以看到 shallow clone 真的是 shallow fetch.

这意味着您要将第 1 步 fetch 修改得较浅。

到目前为止,这没什么大不了的,但现在我们进入第 2 步,merge。进行合并需要一定的深度。但是——深度?好吧,这就是我们拆分问题的原因。所需的深度取决于图中的节点,要 get 节点,我们必须获取它们。假设,目前,我们已经获取了其中的 "enough" 个,不管有多少:我们现在将有一个如下所示的图表:

...--o--*--o--o   <-- yourbranch (HEAD)
         \
          o--o--o   <-- other

git merge otherother 参数,通常是 远程跟踪分支名称 ,如 origin/master,指定您打算提交的其他一些提交合并。 Git 需要 您的 分支提示提交(yourbranch 指向的 o 节点,例如 master,指向),他们的分支提示提交,以及我用*标记的合并基础提交。

对于Git到找到合并基的,并且Git满意地证明这个 合并基础,Git 需要 * 和每个分支提示 之间的所有提交(包括 * 本身)。

那是多少?好吧,这取决于实际图表。我们画了一个图表,其中顶线——你的分支——需要三个:尖端,从尖端后退的第一步,以及从尖端后退的第二步。我们的底线需要四个:尖端,从尖端后退两步,再向后退一步到达 *.

因此,对于这个图,所需的--depth将是4,因为3和4中较大的一个是4。

你的图表需要多少?嗯,这取决于你的图表!没有提前告诉:直到你有足够的图表来确定你是否有足够的图表,你没有足够的图表。一旦你有足够的图表,你可以找到合并基础,然后计算"deepest line"。

请注意,我们绘制了一个非常简单的图形。它可能更复杂,例如:

...--o--*--o--...--o   <-- yours (HEAD)
         \
          \      o--o--o
           \    /       \
            o--o         o--o   <-- other
                \       /
                 o-----o

要找到此图的最小深度,请从 yours 追踪顶部的直线回到 *,并追踪底部的 两条 线从 other 回到 *。 (很明显底部拆分的上半部分会是更长的线,所以我们可以偷懒一点,只计算那些节点。)

在哪里进行计数

现在问题很明显了:为了回溯(并包括)合并基,我们必须找到合并基,这意味着我们需要足够的深度能够找到合并基地

不幸的是,我们必须在具有所有提交 的存储库中执行此操作,并且您在您的存储库中进行了其他存储库中没有的新提交。如果我们可以将您的提交推送到主存储库并在那里完成工作,那将很容易。

(我们实际上不必一次获得所有计数。获得 "your count" 和 "their count" 并取两者中的较大者就足够了。细节有点粘;如果你想这样做,我会让你解决它们。考虑一下合并基础是否包含在你到目前为止获得的子图中;这是你必须实现的两种情况.)

那么,一个解决方案实际上就是这样做:将您的提交推送到一个已经拥有其他所有内容的存储库。 (有关此操作的限制,请参阅 VonC 对 Why can't I push from a shallow clone? 的回答。)为此,您需要在为您保留的更完整的克隆上写入 name做这种手术。例如,您可以有一个保留的分支或标记名称,for-blitz-count-trick 或类似的名称:

git push $remote HEAD:for-blitz-count-trick

然后让 $remote 的 Git 计算合并基础并对提交进行计数。然后只需完全删除名称 for-blitz-count-trick,以便下次需要时再次执行此操作。

暂时假设您计划运行git merge $remote/other,因此$remote上的名字是other,并且您已经完成了这个特殊的推送.您现在登录 $remote,您可以在其中计算正确的 --depth.

如果您愿意超调,可能超调,命令序列:

base=$(git merge-base for-blitz-count-trick other)
git rev-list --count --ancestry-path $base^@..for-blitz-count-trick
git rev-list --count --ancestry-path $base^@..other

应该完成这项工作。

我没有实际测试过这个,但它都是基于明显的图形操作。对于像我展示的更复杂的图这样的情况,它会计算所有分叉和连接序列上的所有节点,这就是它存在过度计数风险的原因。我使用 $base^@ 来包含提交 $base 而排除其父项。还值得注意的是,如果没有共同的合并基础,或者如果有多个合并基础,此方法将失败,因此检查是否只有一个合并基础可能是个好主意。

我觉得--ancestry-path不能和--left-right结合使用,但是类似的命令:

git rev-list --count --left-right --boundary for-blitz-count-trick...other

也应该可以工作,在某些情况下有过度计算边界提交的风险,因为 --boundary 的实现有点草率。这个不会在多个合并基础的存在下失败,并且在一个命令中获得两个计数,因此这可能是实践中的方法。

如果那不可能(甚至太烦人)

可能是您无法登录 $remote 在那里完成这项工作,或者某些策略阻止在那里创建临时名称,或两者兼而有之。在这种情况下,您可以简单地重复增加克隆深度,直到找到合并基础,或者完全取消浅化克隆(当且仅当没有合并基础时才会发生后者)。

从根本上说,问题是您需要足够的深度来计算多少深度才足够。一旦你 拥有 那个深度,你就可以 "re-shallow" 到确切的数字,不管它是什么,但是这个 "re-shallowing" 没有任何实际意义。随着存储库本身通过添加新提交而增长,所需的 --depth 也将趋于增长,但如果您的工作被反复合并(并推送),这将趋于缩小所需的 --depth.

实际上,一次添加 50 个就足够了,直到你有足够的深度,然后保持那个深度,不管它是什么,直到它被证明太浅;然后增加它。请注意,您需要自己将此号码存储在某个地方:请参阅 how to know the depth of a git's shallow clone?

因此采用了丑陋但实用的方法:只需选择一些有效的深度,使用它直到它不起作用,然后增加它。永远不要 运行 git pull,只需将其分解成其组成部分 git fetch 和其他命令(通常 git mergegit rebase 也可以)。