git 为什么我不能从浅克隆推?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/6900103/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why can't I push from a shallow clone?
提问by Philip Oakley
The git clone --depth
command option says
该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.
Why do shallow clones have this limitation? Why is it a patches only workflow?
为什么浅克隆有这个限制?为什么它只是补丁工作流程?
For some project workflows I need to pass just the latest commit from a single branch to a coder, and then have them be able to push
their (fast forward) developments to the main server. This partly for security, IP protection and repo size, and partly to reduce the confusion that a big repo would bring to a naive coder. Is there a git workflow that allows this?
对于某些项目工作流,我只需要将来自单个分支的最新提交传递给编码器,然后让他们能够push
(快进)开发到主服务器。这部分是为了安全、IP 保护和 repo 大小,部分是为了减少大型 repo 会给天真的编码员带来的混乱。是否有允许这样做的 git 工作流程?
Update: Based on Karl Bielefeldt's answer the git checkout --orphan
should be the right answer. But one still needs to 'clone' that branch alone to the new user, and be able to push it effectively.
更新:根据 Karl Bielefeldt 的回答,这git checkout --orphan
应该是正确的答案。但是仍然需要将该分支单独“克隆”给新用户,并能够有效地推送它。
The man page states:
手册页指出:
git checkout [-q] [-f] [-m] [[-b|-B|--orphan] <new_branch>] [<start_point>] --orphan
Create a new orphan branch, named
<new_branch>
, started from<start_point>
and switch to it. The first commit made on this new branch will have no parents and it will be the root of a new history totally disconnected from all the other branches and commits.The index and the working tree are adjusted as if you had previously run
git checkout <start_point>
. This allows you to start a new history that records a set of paths similar to<start_point>
by easily runninggit commit -a
to make the root commit.This can be useful when you want to publish the tree from a commit without exposing its full history. You might want to do this to publish an open source branch of a project whose current tree is "clean", but whose full history contains proprietary or otherwise encumbered bits of code.
If you want to start a disconnected history that records a set of paths that is totally different from the one of
<start_point>
, then you should clear the index and the working tree right after creating the orphan branch by runninggit rm -rf .
from the top level of the working tree. Afterwards you will be ready to prepare your new files, repopulating the working tree, by copying them from elsewhere, extracting a tarball, etc.
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's link to Junio's comments is interesting. I think the manual should provide the guidance in this case, and allow the right command [e.g. clone <branch> --options
] to extract just the relevant part of the repo. Obviously the probability of push
success is increased by having a few linked commits and SHA1s at the bottom of the history that will lock down the repo matching.
VonC 指向 Junio 评论的链接很有趣。我认为手册应该在这种情况下提供指导,并允许正确的命令 [eg clone <branch> --options
] 只提取 repo 的相关部分。显然,push
通过在历史底部有一些链接提交和 SHA1 来锁定回购匹配,成功的可能性会增加。
Update Git 1.9.0 : release notes 14 Feb '14.
更新 Git 1.9.0:2014 年 2 月 14 日发行说明。
"Fetching from a shallowly-cloned repository used to be forbidden, primarily because the codepaths involved were not carefully vetted and we did not bother supporting such usage. This release attempts to allow object transfer out of a shallowly-cloned repository in a more controlled way (i.e. the receiver becomes a shallow repository with a truncated history)."
“过去禁止从浅克隆存储库中获取,主要是因为所涉及的代码路径没有经过仔细,我们也没有费心支持这种用法。此版本试图以更可控的方式允许对象从浅克隆存储库中传输出来(即接收器变成了一个历史被截断的浅层存储库)。”
This is good news for the shallow cloners. Next - Narrow clones possibly.
这对浅层克隆人来说是个好消息。下一步 - 可能是窄克隆。
回答by VonC
As Junio C. Hamano (main Git maintainer) puts it:
正如 Junio C. Hamano(主要 Git 维护者)所说:
Isn't the rule more or less like:
If your shallow repository's history does not extend long enough and the other repository forked before your truncated history, wyou cannot compute the common ancestor and you cannot push out.
规则是不是或多或少像:
如果您的浅层存储库的历史扩展不够长,并且另一个存储库在您截断的历史记录之前分叉,则您无法计算共同祖先,也无法推出。
Update 2014: see "Is git clone --depth 1 (shallow clone) more useful than it makes out?": that limitation will be lifted with Git 1.9!
2014 年更新:请参阅“ git clone --depth 1(浅克隆)是否比它看起来更有用?”:Git 1.9 将取消该限制!
Update 2015: with Git 2.5+, you will even be able to fetch a single commit. See "Pull a specific commit from a remote git repository"
2015 年更新:使用 Git 2.5+,您甚至可以获取单个提交。请参阅“从远程 git 存储库中提取特定提交”
Original answer (August 2011):
原始答案(2011 年 8 月):
Actually, come to think of it, it is a lot stronger than "cannot compute the common".
The history may look like this:
其实仔细想想,比“不能算公”强多了。
历史可能是这样的:
R---R---R
/
--R---R---X---X---S---S---S
where
S
are the commits you have in your shallow repository, andR
are the commits that exist in the repository that receives your push.
Because your history is shallow, neither repository has 'X
' that are the commits that need to exist in order to keep the history of recipient repository complete; the recipient is not shallow to begin with, and we do not want to make it shallow.If you cloned shallowly some time ago, worked without communicating with the other side while the other side progressed, ANDif the other side's progress included a rewind & rebuild of the history, you would see a similar topology.
The leftmost 'S
' in the above picture might have been the tip of the branch when you shallowly cloned with depth 1, and since then the remote end may have discarded topmost three commits and have rebuilt its history that leads to the rightmost 'R
'.
In such a case pushing to the remote'sHEAD
will fail.
S
您在浅层存储库中的提交在哪里,以及R
接收推送的存储库中存在的提交在哪里。
因为您的历史很浅,所以两个存储库都没有“X
”,这是为了保持收件人存储库的历史记录完整而需要存在的提交;接受者一开始并不肤浅,我们不想让它变得肤浅。如果您之前进行了浅薄的克隆,在另一侧进行时不与另一侧通信而工作,并且如果另一侧的进展包括历史的倒带和重建,您将看到类似的拓扑。上图中
最左边的 'S
' 可能是当您以深度 1 进行浅层克隆时的分支的尖端,从那时起,远端可能已经丢弃了最上面的三个提交并重建了它的历史记录,导致最右边的 'R
'。
在这种情况下,推送到遥控器HEAD
将失败。
So it couldwork in some case, but it is not supported:
所以它在某些情况下可以工作,但不受支持:
If I have to say something on this...
如果我必须要说些什么...
I think "is not supported" is a succinct way to give good enough information, but it would only work for intelligent people.
Not everybody is intelligent; some try it out themselves, see that the operation seems towork for their limited number of trials, and would conclude it would work most of the time.
And they congratulate their own intelligence for saying "most of the time", not "always".
And they get upset when they see it does not work, even though they have been warned.
我认为“不支持”是提供足够好的信息的简洁方式,但它只适用于聪明人。
不是每个人都聪明;有些人自己尝试一下,发现该操作似乎对他们有限的试验次数有效,并得出结论,它在大多数情况下都有效。
他们祝贺自己说“大部分时间”而不是“总是”的智慧。
当他们看到它不起作用时,他们会感到不安,即使他们已经被警告过。
For more on the shallow clone update process, see "How to update a git shallow clone?".
有关浅克隆更新过程的更多信息,请参阅“如何更新 git 浅克隆?”。
回答by Karl Bielefeldt
Is there a git workflow that allows this?
是否有允许这样做的 git 工作流程?
Yes, it's to send in fixes as patches. git format-patch
is specially designed to enable this. It's called a "gatekeeper" workflow, if you want to google for more details. It's hard to believe an organization as concerned with "security and IP protection" as yours isn't already using something similar, where one person or a small group is responsible for vetting "untrusted" changes before they make it into the real build.
是的,它是将修复作为补丁发送。 git format-patch
专门设计用于实现这一点。它被称为“看门人”工作流程,如果您想通过谷歌搜索更多详细信息。很难相信像您这样关注“安全和 IP 保护”的组织还没有使用类似的东西,其中一个人或一个小组负责在“不受信任”的更改进入实际构建之前对其进行。
Based on your comment, I now have a better idea of your requirements. What I would recommend is creating an orphanbranch (see git checkout --orphan), from whichever point you want your devs to start. Clone only that branch to a different repository accessible to those devs, and let them clone, push, and pull normally from that repo.
根据您的评论,我现在对您的要求有了更好的了解。我推荐的是创建一个孤立分支(请参阅git checkout --orphan),无论您希望开发人员从哪个点开始。仅将该分支克隆到这些开发人员可访问的不同存储库,并让他们从该存储库中正常克隆、推送和拉取。
Then when you need to reintegrate their changes to the official protected repository, just pull their branch, make a copy of it with git branch
so you don't overwrite your original orphan (in case you want to repeat the process later), then rebase the copy onto your original branch point, and merge or whatever as normal. The history will look like they worked directly from your protected repo.
然后,当您需要将他们的更改重新集成到官方受保护存储库时,只需拉出他们的分支,复制它,git branch
这样您就不会覆盖原始孤儿(以防您以后想重复该过程),然后重新设定副本到您的原始分支点,然后合并或正常进行任何操作。历史看起来像是直接从您的受保护存储库中工作的。
It's a little bit more complicated than normal, but that's the price paid for the extra isolation.
这比正常情况要复杂一些,但这是额外隔离所付出的代价。
回答by Guenther Brunthaler
I found a workaround using git bundles.
我找到了一个使用 git bundles 的解决方法。
This solution will replicate the exact same commits to the other repository like "git push" would do, and will not require rebasing or result in a changed commit ID.
此解决方案将像“git push”一样将完全相同的提交复制到其他存储库,并且不需要重新定位或导致更改提交 ID。
It requires shell-access (such as ssh) to the target host, unfortunately.
不幸的是,它需要对目标主机进行 shell 访问(例如 ssh)。
I will show the solution by example.
我将通过示例展示解决方案。
First we need to get a shallow clone for demonstration purposes.
首先,为了演示目的,我们需要获得一个浅层克隆。
Lets fetch the single commit release v0.5.0.0 from https://github.com/skarnet/s6-rcinto a new repository as a shallow clone.
让我们从https://github.com/skarnet/s6-rc获取单个提交版本 v0.5.0.0作为浅克隆到一个新的存储库。
I will use shell variables in my examples rather than including the example settings directly within the commands, because this will allow you to copy/paste the instruction from this posting directly into your shell, after setting the variables to different values which apply to your situation.
我将在我的示例中使用 shell 变量,而不是直接在命令中包含示例设置,因为这将允许您在将变量设置为适用于您的情况的不同值后,将本次发布的指令直接复制/粘贴到您的 shell 中.
Therefore, feel free to replace the following variable assigmnents by using a different URL and release.
因此,请随意使用不同的 URL 和版本来替换以下变量分配。
In the case of our example, the shallow clone can be created with:
在我们的示例中,可以使用以下命令创建浅克隆:
$ url=https://github.com/skarnet/s6-rc
$ rel=v0.5.0.0
$ git init ${url##*/} && cd ${url##*/}
$ git pull --depth=1 "$url" $rel:master
This will create a "s6-src" (when using the above variable values) subdirectory containing the new clone.
这将创建一个包含新克隆的“s6-src”(当使用上述变量值时)子目录。
Now that we have our shallow clone containing only a single commit with all its parent history missing in the local repository, we bundle this single commit into a git bundle file:
现在我们的浅层克隆只包含一个提交,并且在本地存储库中丢失了它的所有父历史记录,我们将这个单一提交捆绑到一个 git bundle 文件中:
$ b=$rel.gbnd
$ git bundle create $b HEAD
This will create file v0.5.0.0.gbnd making use of the shell variables set before.
这将使用之前设置的 shell 变量创建文件 v0.5.0.0.gbnd。
Now you have to copy this file to the target machine where you would normally like to push to. (Only that git push refuses to push from shallow clones and will therefore not work, at least not using older git versions.)
现在您必须将此文件复制到您通常希望推送到的目标机器上。(只有 git push 拒绝从浅克隆推送,因此不会工作,至少不会使用旧的 git 版本。)
On the target host, do the following in order to create a new repository as a subdirectory, containing the same commit as bundled before:
在目标主机上,执行以下操作以创建一个新的存储库作为子目录,其中包含与之前捆绑的相同的提交:
$ 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 .
Note that you should set the variables to the same values as you did on the host from which the bundle was copied.
请注意,您应该将变量设置为与您在从中复制包的主机上所做的相同的值。
Also note that you can omit the "git init" it the repository already exists.
另请注意,如果存储库已存在,您可以省略“git init”。
That's it!
就是这样!
However, the latter instructions only apply for regular checkouts.
但是,后面的说明仅适用于常规结帐。
Maybe you want to import a shallow clone bundle into a "bare" repository.
也许您想将浅克隆包导入到“裸”存储库中。
In this case, do the following instead:
在这种情况下,请改为执行以下操作:
$ 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