仅拉取最新的提交到浅克隆中

3

我看到有很多关于合并浅拉取的问题,但大多数似乎已经过时,而且其他一些也不清楚。

我们的 git 仓库历史非常庞大。这是由于过去一些巨大文件的错误提交(这些文件已被删除)所致。 执行完整克隆需要很长时间,因为它会拉取这些文件,然后才会删除它们。它还会在计算机上产生一个巨大的 .git 目录。

为了解决这个问题,我们进行了深度为 1 的浅克隆。 这一切都很好运作,我们能够工作、提交和合并回主分支。 然而,如果主分支有变更,那么我需要将它们拉取到我的分支中。这就是问题的开始。 现在的拉取会像完整克隆一样返回所有主分支的历史记录。 我需要的只是自从上次拉取以来的更改。

那么,有没有办法告诉它这样做呢? 使用深度为 1 的另一个拉取会解决我的问题吗?

我正在开发的分支是一个特殊功能,需要一些时间才能完成,因此在合并之前它将与主分支“并行”运行一段时间。 深度为 1 的拉取会断开我的分支与主分支的连接,从而阻止我合并回去吗?

1个回答

2
不要考虑“pull”,这就像试图同时摸肚子和拍头一样。虽然可以做到,但最好在掌握每个部分之后再尝试。所以,将其分解为以下几个组成部分:
1. fetch 2. merge 现在,记住,clone本质上是init + remote add + fetch + checkout,我们可以看到浅克隆实际上是浅获取。
这意味着您需要修改步骤1中的fetch,使其变得更加浅显易懂。
到目前为止,这没有什么大不了的,但现在我们来到第二步,合并。进行合并需要一定程度的深度。但是,需要多少深度?这就是我们拆分问题的原因。所需深度取决于图中的节点,而要获取节点,我们必须获取它们。暂时假设我们已经获取了足够数量的节点,无论是多少:我们现在将拥有一个类似以下的图形:
...--o--*--o--o   <-- yourbranch (HEAD)
         \
          o--o--o   <-- other
git merge other命令中的other参数通常是一个远程跟踪分支名称,比如origin/master,用于指定你想要合并的某个提交。Git需要你的分支末端提交(即yourbranch,例如master所指向的o节点)、他们的分支末端提交以及我用*标记的合并基础提交。
为了让Git能够找到并证明这是合并基础提交,Git需要*和每个分支末端之间的所有提交(包括*本身)。
那需要多少呢?这取决于实际的图形结构。我们画了一个图形,其中最上面的线代表你的分支,需要三个提交:末端、末端的前一步、末端的前两步。我们底部的线需要四个提交:末端、末端的前两步,再往前一步就到达了*
因此,对于这个图形,所需的--depth值为4,因为3和4中较大的是4。
那么对于你的图形需要多少呢?这取决于你的图形!无法提前预测:在你获得足够的图形以找出是否有足够的图形之前,你没有足够的图形。一旦你拥有了足够的图形,就可以找到合并基础提交,然后计算“最深一行”。
请注意,我们画了一个非常简单的图形,它可能更加复杂,例如:
...--o--*--o--...--o   <-- yours (HEAD)
         \
          \      o--o--o
           \    /       \
            o--o         o--o   <-- other
                \       /
                 o-----o

要找到此图的最小深度,请从 yours 沿顶部直线回溯到 *,并从 other 沿底部的两条线路都回溯到 *。(显然,底部拆分的上半部分将是更长的线路,因此我们可以有点懒,只计算这些节点。)

在哪里进行这个计数

现在问题很明显:为了计算回到(包括)合并基础,我们必须找到合并基础,这意味着我们需要足够的深度才能找到合并基础。
不幸的是,我们必须在具有所有提交的存储库中执行此操作,并且您已在您的存储库中进行了新提交,而这些提交不在其他存储库中。如果我们可以将您的提交推送到主存储库并在那里完成工作,那将很容易。
(实际上,我们不必一次获得所有计数。获取“您的计数”和“他们的计数”,并取其中较大的一个即可。细节会有点棘手;如果您想走这条路,我会让您自己解决。考虑合并基础是否包含在您迄今为止获得的子图中,或者不包含在其中;这是您必须实现的两种情况。)
因此,一个解决方案就是确实只做到这一点:将您的提交推送到已经拥有其他所有内容的存储库中。(请参见 VonC 对 为什么无法从浅克隆进行推送? 的回答以获取此操作的限制条件。)为此,您需要写入该更完整克隆的名称,该名称保留供您执行此类操作。例如,您可以有一个保留的分支或标记名称,例如 for-blitz-count-trick 等:
git push $remote HEAD:for-blitz-count-trick

然后让Git在$remote上执行合并基础计算和提交计数。然后完全删除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进行此操作,或者某些策略防止在那里创建临时名称,或者两者都不能。在这种情况下,您可以简单地反复增加克隆深度,直到找到合并基础,或者完全取消浅层克隆(仅当不存在合并基础时才会发生)。

根本问题在于您需要足够的深度来计算足够的深度。一旦您拥有了这个深度,您就可以“重新浅化”到确切的数量,无论它是多少,但是没有真正必要进行这种“重新浅化”。随着存储库本身通过添加新提交而增长,所需的--depth也往往会增长,尽管如果您的工作被反复合并(并推送),那么所需的--depth tend to shrink。

实际上,可能只需要每次添加50个,直到有足够的深度,然后保持在该深度上,无论它是多少,直到证明它太浅为止;然后增加它。请注意,您将需要在某处自己存储此数字:请参见如何知道git的浅克隆的深度?

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


不要想着pull;那就像是一边摸肚子,一边拍头的难题。天啊,现在我正在考虑使用"pull --rebase"命令,它会进行很多合并操作! - VonC
谢谢。最终我们决定通过从别人那里复制整个项目来规避问题。这是一个非常肮脏的技巧,但将来会让生活更轻松。我想这有点回避问题了。 - theblitz
@VonC:是的,但我们都是经过高度训练的杂技演员。或者说是猴子 - torek
@torek,以前我们负责,但现在这个角色被https://stackoverflow.com/users/3440248/drunken-code-monkey接替了;)(开玩笑,我只是喜欢这个用户名) - VonC

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接