为什么我不能从浅层克隆中推出?

时间:2011-08-01 14:43:18

标签: git workflow git-clone

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他们(快进)开发到主服务器。这部分是为了安全,知识产权保护和回购规模,部分是为了减少大回购会给天真编码员带来的困惑。是否有允许这样的git工作流程?


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

手册页说明:

  

git checkout [-q] [-f] [-m] [[-b|-B|--orphan] <new_branch>] [<start_point>] --orphan

     

创建一个名为<new_branch>的新孤立分支,从中开始   <start_point>然后切换到它。第一次提交这个新的   分支将没有父母,它将成为新历史的根源   与所有其他分支和提交完全断开。

     

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

     

当您想要从提交中发布树时,这非常有用   没有暴露其完整的历史。你可能想这样做   发布当前树为的项目的开源分支   “干净”,但其完整历史包含专有或其他   填补了一些代码。

     

如果要启动记录一组的断开连接的历史记录   然后,路径与<start_point>完全不同   您应该在创建后立即清除索引和工作树   通过从顶层运行git rm -rf .来获取孤立分支   工作树。之后您将准备好准备新文件,   通过从其他地方复制工作树来重新填充工作树,   提取tarball等

VonC与Junio评论的链接很有意思。我认为手册应该在这种情况下提供指导,并允许正确的命令[例如clone <branch> --options]只提取回购的相关部分。显然,通过在历史记录底部设置一些链接提交和SHA1来锁定回购匹配,可以提高push成功的概率。


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

从以前禁止的浅克隆存储库中获取, 主要是因为涉及的代码路径未经过仔细审查 我们没有打扰支持这种用法。此版本尝试 允许对象从一个浅层克隆的存储库转移出来 更受控制的方式(即接收器变成浅层存储库 截断历史)。“

这对浅层克隆人来说是个好消息。 接下来 - 可能会缩小克隆。

3 个答案:

答案 0 :(得分:22)

作为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”,这些提交需要存在才能保持收件人存储库的历史记录完整;接收者并不浅薄,我们不想让它变浅。

     

如果你前一段时间克隆得很浅,在对方进展的同时没有与另一方沟通, AND 如果对方的进展包括倒带&amp;重建历史,你会看到类似的拓扑   当你用深度1进行浅层克隆时,上图中最左边的“S”可能是分支的尖端,从那时起,远程端可能已经丢弃了最上面的三个提交并重建了它的历史记录,导致了最右边的'R'   在这种情况下,推送到遥控器HEAD将失败。


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

如果我要对此说些什么......

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

  •   
  • 并非所有人都聪明;有些人自己尝试一下,看到操作似乎在他们有限数量的试验中工作,并且结论它大部分时间都可以工作。
      他们祝贺自己的情报“大部分时间”,而非“永远”   当他们看到它不起作用时,他们会感到不安,即使他们已被警告过。

  •   

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

答案 1 :(得分:9)

  

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

是的,它是将修补程序作为补丁发送。 git format-patch专为实现这一目标而设计。如果你想谷歌了解更多细节,它被称为“看门人”工作流程。很难相信一个组织关注“安全和知识产权保护”,因为你的组织还没有使用类似的东西,一个人或一个小组负责审查“不受信任”的变化,然后再进入真正的构建。 / p>


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

然后,当您需要将他们的更改重新集成到官方受保护的存储库时,只需拉动他们的分支,使用git branch复制它,这样您就不会覆盖原始的孤儿(如果您想重复稍后处理),然后将副本重新绑定到原始分支点,并合并或正常情况下的任何内容。历史看起来就像是直接从受保护的仓库中工作。

它比平常更复杂,但这是额外隔离所付出的代价。

答案 2 :(得分:1)

我找到了使用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 bundle文件中:

$ 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