【问题标题】:Why can't I push from a shallow clone?为什么我不能从浅克隆推送?
【发布时间】:2011-10-17 12:35:33
【问题描述】:

git clone --depth 命令选项表示

--depth <depth> 
Create a shallow clone with a history truncated to the specified number of revisions. 
A shallow repository has a number of limitations 
(you cannot clone or fetch from it, nor push from nor into it),
 but is adequate if you are only interested in the recent history of a large project with a long history,
 and would want to send in fixes as patches. 

为什么浅克隆有这个限制?为什么它只是一个补丁工作流程?

对于某些项目工作流程,我只需将来自单个分支的最新提交传递给编码器,然后让他们能够push 他们的(快进)开发到主服务器。这部分是为了安全、IP 保护和 repo 大小,部分是为了减少大型 repo 给幼稚的编码人员带来的混乱。是否有允许这样做的 git 工作流程?


更新:根据 Karl Bielefeldt 的回答,git checkout --orphan 应该是正确的答案。但是仍然需要将该分支单独“克隆”给新用户,并能够有效地推送它。

手册页指出:

git checkout [-q] [-f] [-m] [[-b|-B|--orphan] &lt;new_branch&gt;] [&lt;start_point&gt;] --orphan

创建一个新的孤立分支,命名为&lt;new_branch&gt;,开始于 &lt;start_point&gt; 并切换到它。对这个新的第一个提交 分支将没有父母,它将成为新历史的根源 与所有其他分支和提交完全断开。

索引和工作树的调整就像你以前一样 运行git checkout &lt;start_point&gt;。这允许您开始一个新的 记录一组类似于&lt;start_point&gt; 的路径的历史记录 轻松运行 git commit -a 以进行 root 提交。

当您想从提交中发布树时,这可能很有用 在不暴露其全部历史的情况下。你可能想这样做 发布当前树为的项目的开源分支 “干净”,但其完整历史记录包含专有或其他 累赘的代码。

如果你想开始一个断开连接的历史记录,记录一组 与&lt;start_point&gt; 的路径完全不同的路径,然后 您应该在创建后立即清除索引和工作树 通过从顶层运行 git rm -rf . 来创建孤立分支 工作树。之后,您将准备好准备新文件, 通过从其他地方复制它们来重新填充工作树, 提取压缩包等。

VonC 与 Junio 的 cmets 的链接很有趣。我认为手册应该在这种情况下提供指导,并允许正确的命令[例如clone &lt;branch&gt; --options] 仅提取 repo 的相关部分。显然,push 成功的概率会通过在历史底部有一些链接提交和 SHA1 来增加,这将锁定 repo 匹配。


更新 Git 1.9.0:2014 年 2 月 14 日发布说明。

“过去禁止从浅克隆存储库中获取数据, 主要是因为所涉及的代码路径没有经过仔细审查 我们也懒得支持这种用法。此版本尝试 允许对象从一个浅克隆的存储库中转移出来 更可控的方式(即接收器成为浅层存储库 具有截断的历史记录)。”

这对浅层克隆者来说是个好消息。 下一个 - 可能是狭义克隆。

【问题讨论】:

  • “减少大型 repo 给幼稚编码人员带来的困惑” 我认为您需要新的开发人员:)
  • 那些新的开发者是从天真的程序员开始的 ;-) 有些困惑已经习惯了 git 本身,这可能是一个挑战,所以我们从简单的开始......
  • 拥有提交列表的想法可能是 Git 中最基本的概念。如果我被介绍给 Git 时总是只包含 1 次提交,我想我会更加困惑。
  • @Josh,最初一个刚进入 git 的新开发人员(或团队)可能只从浅深度开始(六次提交?) - 这与一些现有的做法相比他们所看到的只是最后一次入住!当产品周期为多年时,历史悠久的 CM 风格是缓慢的,因此这是一个巨大的文化变革。
  • 目前看来可以:查看stackoverflow.com/a/21217267/4398050

标签: git workflow git-clone


【解决方案1】:

作为 Junio C. Hamano(主要 Git 维护者)puts it

规则是不是有点像:

如果您的浅层存储库的历史没有延长足够长的时间,并且另一个存储库在您截断的历史之前分叉,那么您将无法计算共同祖先并且您无法推出。

2014 年更新:请参阅“Is git clone --depth 1 (shallow clone) more useful than it makes out?”:Git 1.9 将取消该限制!

2015 年更新:使用 Git 2.5+,您甚至可以获取单个提交。见“Pull a specific commit from a remote git repository


原始答案(2011 年 8 月):

其实想想,比“不能计算”强多了 共同的”。

历史可能如下所示:

          R---R---R
         /
  --R---R---X---X---S---S---S

其中S 是您在浅层存储库中的提交,R 是接收您推送的存储库中存在的提交。
因为您的历史很浅,所以两个存储库都没有'X',这些提交是为了保持收件人存储库的历史完整而需要存在的;接受者一开始并不肤浅,我们也不想让它变得肤浅。

如果你前段时间进行了浅层克隆,在对方进行过程中没有与对方沟通工作,并且如果对方的进度包括回溯和重建历史,你会看到类似的拓扑。
上图中最左边的 'S' 可能是当您以深度 1 进行浅层克隆时分支的尖端,从那时起,远程端可能已经丢弃了最上面的三个提交并重建了导致最右边的历史记录R'.
在这种情况下,推送到遥控器的HEAD 将失败。


所以它可以在某些情况下工作,但不支持:

如果我不得不就此说点什么……

  • 我认为“不支持”是提供足够好的信息的简洁方式,但它只适用于聪明人。

  • 不是每个人都是聪明的;有些人自己尝试一下,发现该操作似乎在他们有限的试验中有效,并得出结论认为它在大多数情况下都有效。
    他们祝贺自己的智慧说“大多数时候”,而不是“总是”。
    当他们看到它不起作用时,他们会感到不安,即使他们已经被警告过。


有关浅层克隆更新过程的更多信息,请参阅“How to update a git shallow clone?”。

【讨论】:

  • 另外,浅克隆的收益并不是很好:blogs.gnome.org/simos/2009/04/18/…
  • Ta 供 nabble 参考。我无法从我当前的防火墙访问博客,所以我稍后再看。在我们的案例中,我们确实知道 R-R-R 会加入 S-S-S,因为我们是这样设置的。这是一个找出什么会起作用 [如果有的话] 及其先决条件的案例。
  • 啊,是的,我在做一些研究时看到了那个帖子。这不是文件增长的 Linus 定律之一,所以反向打包增量吗?另外,如果我理解正确的话,基本的clone --depth 1 将使所有分支都达到深度 1,而不仅仅是一个特定的分支达到深度 1。所以博客示例会更多。
  • @Philip:delta 用于推送或获取操作。否则,所有内容都只是在基础中压缩(除非您明确触发创建对象的打包档案)。但是,在推回浅层克隆时缺乏共同历史确实是一个潜在问题。
  • 在 git-owner@vger.kernel.org article.gmane.org/gmane.comp.version-control.git/177617 上也有一条关于深度问题的评论很好地解释了它。
【解决方案2】:

是否有允许这样做的 git 工作流?

是的,它是以补丁的形式发送修复。 git format-patch 是专门为实现这一点而设计的。如果您想谷歌了解更多详细信息,它被称为“看门人”工作流程。很难相信像您这样关注“安全和 IP 保护”的组织还没有使用类似的东西,其中一个人或一小群人负责在“不受信任”的更改进入实际构建之前对其进行审查。


根据您的评论,我现在对您的要求有了更好的了解。我建议创建一个 orphan 分支(请参阅git checkout --orphan),无论您希望开发人员从哪个点开始。仅将该分支克隆到这些开发人员可以访问的不同存储库,并让他们从该存储库正常克隆、推送和拉取。

然后,当您需要将他们的更改重新集成到受保护的官方存储库中时,只需拉出他们的分支,使用git branch 复制它,这样您就不会覆盖原来的孤儿(以防您以后想重复该过程) ),然后将副本重新定位到您的原始分支点,然后合并或正常进行任何操作。历史看起来就像他们直接从您受保护的仓库中工作一样。

这比正常情况要复杂一些,但这就是额外隔离所付出的代价。

【讨论】:

  • 问题不在于他们的变化,而在于所有其他分支和深厚的历史,此外我们还需要减轻管理层的“担忧”。无论如何,他们只会推回自己的开发分支,并且需要使其看起来“集成”。这么多相互冲突的优先事项!
  • orphan branch 选项看起来确实很有趣。我会仔细研究一下,看看我们当地的组织问题的优缺点。
  • 我不认为您可以轻松地将孤立分支的更改重新定位到您的主分支,因为没有共同的提交可供搜索。
  • 创建孤儿分支的麻烦在于它启动了一个完全不同的提交历史,这意味着从一个分支合并到另一个是不可能的。
  • @eloyesp:你没有变基到 master,你变基到“你原来的分支点”。这确实工作很好(刚刚测试过)。
【解决方案3】:

我找到了一个使用 git bundles 的解决方法。

此解决方案将与“git push”一样将完全相同的提交复制到其他存储库,并且不需要变基或导致更改的提交 ID。

不幸的是,它需要对目标主机进行 shell 访问(例如 ssh)。

我将举例说明解决方案。

首先,我们需要获得一个浅层克隆用于演示目的。

让我们从https://github.com/skarnet/s6-rc 获取单个提交版本 v0.5.0.0 作为浅克隆到新存储库。

我将在我的示例中使用 shell 变量,而不是将示例设置直接包含在命令中,因为这将允许您在将变量设置为适用的不同值之后,将来自此帖子的指令直接复制/粘贴到您的 shell 中根据你的情况。

因此,请随意使用不同的 URL 和版本替换以下变量分配。

在我们的示例中,可以使用以下方法创建浅克隆:

$ url=https://github.com/skarnet/s6-rc
$ rel=v0.5.0.0
$ git init ${url##*/} && cd ${url##*/}
$ git pull --depth=1 "$url" $rel:master

这将创建一个包含新克隆的“s6-src”(使用上述变量值时)子目录。

现在我们的浅层克隆只包含一个提交,其所有父历史记录都在本地存储库中丢失,我们将这个单一提交捆绑到一个 git 捆绑文件中:

$ b=$rel.gbnd
$ git bundle create $b HEAD

这将使用之前设置的 shell 变量创建文件 v0.5.0.0.gbnd。

现在您必须将此文件复制到您通常希望推送到的目标机器上。 (只有 git push 拒绝从浅层克隆推送,因此无法工作,至少不使用旧 git 版本。)

在目标主机上,执行以下操作以创建一个新存储库作为子目录,其中包含与之前捆绑的相同提交:

$ url=https://github.com/skarnet/s6-rc
$ rel=v0.5.0.0
$ git init ${url##*/} && cd ${url##*/}
$ c=`git bundle unbundle $b | cut -d " " -f 1`; echo "$c"
$ git tag $rel $c # optional: create a tag for the imported commit.
$ git reset --hard $c
$ git fetch --depth=1 .

请注意,您应该将变量设置为与复制包的主机上相同的值。

另请注意,如果存储库已经存在,您可以省略“git init”。

就是这样!

但是,后面的说明仅适用于常规结帐。

也许您想将浅层克隆包导入“裸”存储库。

在这种情况下,请改为执行以下操作:

$ url=https://github.com/skarnet/s6-rc
$ rel=v0.5.0.0
$ cd ${url##*/}.git
$ c=`git bundle unbundle $b | cut -d " " -f 1`; echo "$c"
$ git tag $rel $c
$ git fetch --depth=1 . $rel

【讨论】:

    猜你喜欢
    • 2012-07-07
    • 1970-01-01
    • 2020-06-25
    • 2013-04-28
    • 2020-07-04
    • 1970-01-01
    • 2013-11-25
    • 2015-02-21
    相关资源
    最近更新 更多