为什么我不能推送这个最新的 Git 子树?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/13756055/
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 this up-to-date Git subtree?
提问by mateusz
I am using Git subtree with a couple of projects that I am working on, in order to share some base code between them. The base code gets updated often, and the upgrades can happen in anyone of the projects, with all of them getting updated, eventually.
我正在将 Git 子树与我正在处理的几个项目一起使用,以便在它们之间共享一些基本代码。基本代码经常更新,升级可以发生在任何项目中,最终所有项目都会更新。
I have ran into a problem where git reports that my subtree is up to date, but pushing gets rejected. For example:
我遇到了一个问题,git 报告我的子树是最新的,但推送被拒绝。例如:
#! git subtree pull --prefix=public/shared project-shared master
From github.com:****
* branch master -> FETCH_HEAD
Already up-to-date.
If I push, I should get a message that there is nothing to push... Right? RIGHT? :(
如果我推送,我应该收到一条消息,说没有什么可推送的……对吗?对?:(
#! git subtree push --prefix=public/shared project-shared master
git push using: project-shared master
To [email protected]:***
! [rejected] 72a6157733c4e0bf22f72b443e4ad3be0bc555ce -> master (non-fast-forward)
error: failed to push some refs to '[email protected]:***'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Merge the remote changes (e.g. 'git pull')
hint: before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
What could be the reason for this? Why is pushing failing?
这可能是什么原因?为什么推送失败?
回答by Eric Woodruff
I found the answer on this blog comment https://coderwall.com/p/ssxp5q
我在这篇博客评论https://coderwall.com/p/ssxp5q上找到了答案
If you come across the "Updates were rejected because the tip of your current branch is behind. Merge the remote changes (e.g. 'git pull')" problem when you're pushing (due to whatever reason, esp screwing about with git history) then you'll need to nest git commands so that you can force a push to heroku. e.g, given the above example:
如果您在推送时遇到“更新被拒绝,因为您当前分支的提示落后。合并远程更改(例如'git pull')”问题(无论出于何种原因,尤其是与 git 历史有关)那么你需要嵌套 git 命令,以便你可以强制推送到 heroku。例如,给定上面的例子:
git push heroku `git subtree split --prefix pythonapp master`:master --force
回答by Chris Jordan
On windows the nested command doesn't work:
在 Windows 上,嵌套命令不起作用:
git push heroku `git subtree split --prefix pythonapp master`:master --force
You can just run the nested bit first:
您可以先运行嵌套位:
git subtree split --prefix pythonapp master
This will (after a lot of numbers) return a token, e.g.
这将(在很多数字之后)返回一个令牌,例如
157a66d050d7a6188f243243264c765f18bc85fb956
Use this in the containing command, e.g:
在包含命令中使用它,例如:
git push heroku 157a66d050d7a6188f243243264c765f18bc85fb956:master --force
回答by entheh
Use the --onto
flag:
使用--onto
标志:
# DOESN'T WORK: git subtree push --prefix=public/shared project-shared master --onto=project-shared/master
[EDIT: unfortunately subtree push
doesn't forward --onto
to the underlying split
, so the operation has to be done in two commands! With that done, I see that my commands are identical to those in one of the other answers, but the explanation is different so I'll leave it here anyway.]
[编辑:不幸的subtree push
是没有转发--onto
到底层split
,所以操作必须在两个命令中完成!完成后,我发现我的命令与其他答案之一中的命令相同,但解释不同,所以无论如何我都会把它留在这里。]
git push project-shared $(git subtree split --prefix=public/shared --onto=project-shared/master):master
Or if you're not using bash:
或者,如果您不使用 bash:
git subtree split --prefix=public/shared --onto=project-shared/master
# This will print an ID, say 0123456789abcdef0123456789abcdef,
# which you then use as follows:
git push project-shared 01234567:master
I spent hours poring through the git-subtree source to figure this one out, so I hope you appreciate it ;)
我花了几个小时仔细研究 git-subtree 源代码来弄清楚这一点,所以我希望你能理解它;)
subtree push
starts by running subtree split
, which rewrites your commit history into a format which should be ready to push. The way it does this is, it strips public/shared/
off the front of any path which has it, and removes any information about files that don't have it. That means even if you pull non-squashed, all the upstream sub-repository commits are disregarded since they name the files by their bare paths.(Commits that don't touch any files under public/shared/
, or merge commits that are identical to a parent, are also collapsed. [EDIT: Also, I've since found some squash detection, so now I'm thinking it's only if you pulled non-squashed, and then the simplistic merge commit collapsing described in yet another answer manages to choose the non-squashed path and discard the squashed path.]) The upshot is, the stuff it tries to push ends up containing any work someone committed to the current host repository you're pushing from, but not work people committed directly to the sub-repository or via another host repository.
subtree push
首先运行subtree split
,它将您的提交历史重写为应该准备好推送的格式。它执行此操作的方式是,它public/shared/
会去除任何具有它的路径的前面,并删除有关没有它的文件的任何信息。这意味着即使您拉取非压缩,所有上游子存储库提交也会被忽略,因为它们以裸路径命名文件。(提交不触及下的任何文件public/shared/
或合并与父项相同的提交也会折叠。[编辑:另外,我已经发现了一些挤压检测,所以现在我认为只有当你拉非挤压,然后另一个答案中描述的简单合并提交折叠设法选择非挤压路径和丢弃压扁的路径。]) 结果是,它尝试推送的内容最终包含某人提交到您正在推送的当前主机存储库的任何工作,但不包含直接提交到子存储库或通过另一个主机的工作人员存储库。
However, if you use --onto
, then all the upstream commits are recorded as OK to use verbatim, so when the rewriting process comes across them as one of the parents of a merge it wants to rewrite, it will keep them instead of trying to rewrite them in the usual way.
但是,如果您使用--onto
,则所有上游提交都被记录为可以逐字使用,因此当重写过程将它们作为要重写的合并的父项之一遇到时,它将保留它们而不是尝试重写它们以通常的方式。
回答by Colin D
For a "GitHub pages" type app, where you deploy a "dist" subtree to a gh-pages branch, the solution might look something like this
对于“GitHub pages”类型的应用程序,您将“dist”子树部署到 gh-pages 分支,解决方案可能如下所示
git push origin `git subtree split --prefix dist master`:gh-pages --force
I mention this since it looks slightly different from the heroku examples given above. You can see that my "dist" folder exists on the master branch of my repo, and then I push it as a subtree to the gh-pages branch which is also on origin.
我提到这一点是因为它看起来与上面给出的 heroku 示例略有不同。您可以看到我的“dist”文件夹存在于我的 repo 的 master 分支上,然后我将它作为子树推送到 gh-pages 分支,该分支也在原点上。
回答by snw
I have encountered this problem before as well, and here is how I solved it.
我以前也遇到过这个问题,下面是我解决它的方法。
What I found out was I had a branch that was not attached to the local master branch. This branch exists and its just hanging in the void. In your case, its probably called project-shared
. Assuming this is the case and when you do a git branch
you can see a local project-shared
branch, then you can 'append' new commits to your existing project-shared
branch by doing a:
我发现我有一个没有附加到本地主分支的分支。这个分支存在,它只是悬在虚空中。在您的情况下,它可能称为project-shared
. 假设是这种情况,并且当您执行 a 时,git branch
您可以看到一个本地project-shared
分支,然后您可以通过执行以下操作将新提交“附加”到现有project-shared
分支:
git subtree split --prefix=public/shared --onto public-shared --branch public-shared
git subtree split --prefix=public/shared --onto public-shared --branch public-shared
The way I understood is git subtree
will start creating new branch from --onto
, in this case its the local public-shared
branch. Then the branch means creating a branch, which just replaces the old public-shared
branch.
我理解的方式是git subtree
从 开始创建新分支--onto
,在这种情况下是本地public-shared
分支。那么分支就意味着创建一个分支,它只是替换旧public-shared
分支。
This will keep all the previous SHA of the public-shared
branch. Finally, you can do a
这将保留public-shared
分支的所有先前 SHA 。最后,你可以做一个
git push project-shared project-shared:master
git push project-shared project-shared:master
Assuming that you have a project-shared
remote as well; this will push the local hanging in the voidproject-shared
branch to the master
branch of the remote project-shared
.
假设你也有一个project-shared
遥控器;这会将挂在 voidproject-shared
分支中的本地推送到master
远程分支project-shared
。
回答by klimkin
This is because of the limitation of original algorithm. When handling merge-commits, the original algorithm uses a simplified criteria for cutting off unrelated parents. In particular, it checks, if there is a parent, which has the same tree. If such a parent found, it would collapse the merge commit and use the parent commit instead, assuming that other parents have changes unrelated to the sub-tree. In some cases this would result in dropping parts of history, which has actual changes to the sub-tree. In particular it would drop sequences of commits, which would touch a sub-tree, but result in the same sub-tree value.
这是因为原始算法的限制。在处理合并提交时,原始算法使用简化的标准来切断不相关的父项。特别是,它会检查是否存在具有相同树的父节点。如果找到这样的父级,它会折叠合并提交并使用父级提交,假设其他父级具有与子树无关的更改。在某些情况下,这会导致删除部分历史记录,这对子树有实际更改。特别是它会删除提交序列,这会触及子树,但会产生相同的子树值。
Lets see an example (which you can easily reproduce) to better understand how this works. Consider the following history (the line format is: commit [tree] subject):
让我们看一个示例(您可以轻松重现)以更好地理解它是如何工作的。考虑以下历史记录(行格式为:commit [tree] subject):
% git log --graph --decorate --pretty=oneline --pretty="%h [%t] %s"
* E [z] Merge branch 'master' into side-branch
|\
| * D [z] add dir/file2.txt
* | C [y] Revert "change dir/file1.txt"
* | B [x] change dir/file1.txt
|/
* A [w] add dir/file1.txt
In this example, we are splitting on dir
. Commits D
and E
have the same tree z
, because we have commit C
, which undone commit B
, so B-C
sequence does nothing for dir
even though it has changes to it.
在这个例子中,我们正在拆分dir
。提交D
并E
具有相同的树z
,因为我们有提交C
,它撤消了提交B
,因此即使B-C
序列dir
对其进行了更改,它也不做任何事情。
Now lets do splitting. First we split on commit C
.
现在让我们进行拆分。首先,我们在 commit 上拆分C
。
% git log `git subtree split -P dir C` ...
* C' [y'] Revert "change dir/file1.txt"
* B' [x'] change dir/file1.txt
* A' [w'] add dir/file1.txt
Next we split on commit E
.
接下来我们拆分 commit E
。
% git log `git subtree split -P dir E` ...
* D' [z'] add dir/file2.txt
* A' [w'] add dir/file1.txt
Yes, we lost two commits. This results in the error when trying to push the second split, since it doesn't have those two commits, which already got into the origin.
是的,我们丢失了两次提交。这会在尝试推送第二个拆分时导致错误,因为它没有已经进入源的那两个提交。
Usually you can tolerate this error by using push --force
, since dropped commits generally won't have critical information in them. In the long term, the bug needs to be fixed, so the split history would actually have all commits, which touch dir
, as expected. I would expect the fix to include a deeper analysis of parent commits for hidden dependencies.
通常你可以通过使用来容忍这个错误push --force
,因为删除的提交通常不会包含关键信息。从长远来看,该错误需要修复,因此拆分历史实际上将包含所有提交dir
,如预期的那样。我希望修复程序包含对隐藏依赖项的父提交的更深入分析。
For reference, here is the portion of original code, responsible for the behavior.
作为参考,这里是原始代码的一部分,负责行为。
copy_or_skip()
...
for parent in $newparents; do
ptree=$(toptree_for_commit $parent) || exit $?
[ -z "$ptree" ] && continue
if [ "$ptree" = "$tree" ]; then
# an identical parent could be used in place of this rev.
identical="$parent"
else
nonidentical="$parent"
fi
...
if [ -n "$identical" ]; then
echo $identical
else
copy_commit $rev $tree "$p" || exit $?
fi
回答by Zsolt Szatmari
Eric Woodruff's answer did not help me, but the following did:
埃里克伍德拉夫的回答对我没有帮助,但以下内容确实有用:
I usually 'git subtree pull' with the '--squash' option. It seems this did make things harder to reconcile, so I needed to do a subtree pull without squashing this time, resolve some conflicts and then push.
我通常使用'--squash'选项'git subtree pull'。看起来这确实让事情变得更难协调,所以这次我需要在不压缩的情况下进行子树拉取,解决一些冲突,然后推送。
I must add that the squashed pull did not reveal any conflicts, told me everything was OK.
我必须补充一点,压扁的拉力没有显示任何冲突,告诉我一切正常。
回答by 9swampy
Another [simpler] solution is to advance the head of the remote by making another commit if you can. After you pull this advanced head into the local subtree then you will be able to push from it again.
另一个 [更简单] 的解决方案是,如果可以的话,通过再次提交来提升遥控器的头部。将这个高级头拉入本地子树后,您将能够再次从它推入。
回答by FunkyKat
You can force push local changes to remote subtree repo
您可以将本地更改强制推送到远程子树存储库
git push subtree_remote_address.git `git subtree split --prefix=subtree_folder`:refs/heads/branch --force
回答by Rtype
A quick powershell for windows users based on Chris Jordan's solution
基于 Chris Jordan 解决方案的 Windows 用户快速 powershell
$id = git subtree split --prefix pythonapp master
Write-Host "Id is: $id"
Invoke-Expression "git push heroku $id`:master --force"