Git子树 - 子树最新但无法推送

时间:2012-12-07 02:27:23

标签: git push pull git-subtree

我正在使用Git子树和我正在处理的几个项目,以便在它们之间共享一些基本代码。基本代码经常更新,升级可以在任何项目中进行,最终都会更新。

我遇到了一个问题,git报告我的子树是最新的,但是推送被拒绝了。例如:

#! git subtree pull --prefix=public/shared project-shared master
From github.com:****
* branch            master     -> FETCH_HEAD
Already up-to-date.

如果我推,我应该得到一个信息,没有什么可以推动的......对吗?对? :(

#! git subtree push --prefix=public/shared project-shared master
git push using:  project-shared master
To git@github.com:***
! [rejected]        72a6157733c4e0bf22f72b443e4ad3be0bc555ce -> master (non-fast-forward)
error: failed to push some refs to 'git@github.com:***'
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.

这可能是什么原因?为什么推动失败?

11 个答案:

答案 0 :(得分:84)

我在此博客评论https://coderwall.com/p/ssxp5q

上找到了答案
  

如果您遇到“更新被拒绝,因为您当前分支的提示是   背后。在推动时合并远程更改(例如'git pull')“问题(由于   无论什么原因,特别是与git历史搞砸了)然后你需要嵌套git   命令,以便您可以强制推送到heroku。例如,鉴于以上例子:

git push heroku `git subtree split --prefix pythonapp master`:master --force

答案 1 :(得分:33)

在Windows上,嵌套命令不起作用:

git push heroku `git subtree split --prefix pythonapp master`:master --force

您可以先运行嵌套位:

git subtree split --prefix pythonapp master

这将(在很多数字之后)返回一个令牌,例如

157a66d050d7a6188f243243264c765f18bc85fb956

在包含命令中使用它,例如:

git push heroku 157a66d050d7a6188f243243264c765f18bc85fb956:master --force

答案 2 :(得分:27)

使用--onto标志:

# DOESN'T WORK: git subtree push --prefix=public/shared project-shared master --onto=project-shared/master

[编辑:遗憾的是subtree push并未将--onto转发给基础split,因此操作必须在两个命令中完成!完成后,我看到我的命令与其他答案中的命令相同,但解释不同,所以无论如何我都会把它留在这里。]

git push project-shared $(git subtree split --prefix=public/shared --onto=project-shared/master):master

或者,如果您不使用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

我花了几个小时仔细研究git-subtree来源来解决这个问题,所以我希望你能够理解它;)

subtree push首先运行subtree split,它会将您的提交历史记录重写为应该可以推送的格式。它的方式是,它将public/shared/从任何包含它的路径的前面剥离,并删除有关没有它的文件的任何信息。 这意味着即使您拉动非压缩,所有上游子存储库提交都会被忽略,因为它们通过裸路径命名文件。(承认不要触摸{{下的任何文件) 1}},或合并与父母相同的提交也会被折叠。[编辑:此外,我发现了一些南瓜检测,所以现在我只是在想你了拉动非压扁,然后在另一个答案中描述的简化合并提交折叠设法选择非压扁路径并丢弃压扁路径。])结果是,它试图推送的东西最终包含任何有人承诺的工作到您当前的主机存储库,但不是直接提交到子存储库或通过其他主机存储库的人员。

但是,如果你使用public/shared/,那么所有的上游提交都被记录为OK以便逐字使用,所以当重写过程作为合并的父项之一遇到它想要重写时,它将会保留它们而不是试图以通常的方式重写它们。

答案 3 :(得分:3)

我之前也遇到过这个问题,这就是我解决它的方法。

我发现的是我有一个没有附加到本地主分支的分支。这个分支存在,它只是挂在虚空中。在你的情况下,它可能被称为project-shared。假设是这种情况,当您执行git branch时,您可以看到本地project-shared分支,那么您可以'追加'对现有project-shared的新提交通过做一个分支:

git subtree split --prefix=public/shared --onto public-shared --branch public-shared

我理解的方式是git subtree将开始从--onto创建新分支,在本例中是其本地public-shared分支。然后分支意味着创建一个分支,它只是替换旧的public-shared分支。

这将保留public-shared分支的所有先前SHA。最后,你可以做一个

git push project-shared project-shared:master

假设你有一个project-shared遥控器;这会将本地悬挂在void project-shared分支中,推送到远程master的{​​{1}}分支。

答案 4 :(得分:2)

这是因为原始算法的局限性。处理merge-commits时,原始算法使用简化的标准来切断不相关的父项。特别是,它检查是否存在具有相同树的父节点。如果找到这样的父级,它将解除合并提交并改为使用父提交,假设其他父级具有与子树无关的更改。在某些情况下,这会导致丢失历史部分,这会对子树进行实际更改。特别是它会丢弃提交的序列,这些提交会触及子树,但会产生相同的子树值。

让我们看一个例子(你可以很容易地重现),以便更好地理解它是如何工作的。请考虑以下历史记录(行格式为: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

在这个例子中,我们分裂dir。提交DE具有相同的树z,因为我们已提交C,撤消提交B,因此B-C序列不执行任何操作对于dir即使它有变化。

现在让我们分开。首先,我们在提交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

接下来我们分开提交E

% git log `git subtree split -P dir E` ...
* D' [z'] add dir/file2.txt
* A' [w'] add dir/file1.txt

是的,我们失去了两次提交。这在尝试推送第二个拆分时会导致错误,因为它没有那两个已经进入原点的提交。

通常,您可以使用push --force来容忍此错误,因为丢弃的提交通常不会在其中包含重要信息。从长远来看,错误需要修复,因此拆分历史记录实际上会按预期触及dir的所有提交。我希望修复程序包含对隐藏依赖项的父提交的更深入分析。

作为参考,这是原始代码的一部分,负责行为。

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

答案 5 :(得分:0)

Eric Woodruff的回答对我没有帮助,但以下情况确实如此:

我通常使用'--squash'选项'git subree pull'。看起来这确实让事情变得更难调和,所以我需要在没有压缩的情况下做一个子树拉,解决一些冲突然后推进。

我必须补充说,压扁的拉力没有发现任何冲突,告诉我一切都没问题。

答案 6 :(得分:0)

另一个更简单的解决方案是,如果可以的话,通过再次提交来推进遥控器的头部。将此高级头部拉入本地子树后,您将能够再次从该子树中推出。

答案 7 :(得分:0)

您可以强制将本地更改推送到远程子树仓库

git push subtree_remote_address.git `git subtree split --prefix=subtree_folder`:refs/heads/branch --force

答案 8 :(得分:0)

基于Chris Jordan的解决方案为Windows用户提供的快速shellhell

$id = git subtree split --prefix pythonapp master
Write-Host "Id is: $id"
Invoke-Expression "git push heroku $id`:master --force"

答案 9 :(得分:0)

这就是我根据@entheh所说的内容写的。

for /f "delims=" %%b in ('git subtree split --prefix [name-of-your-directory-on-the-file-system-you-setup] -b [name-of-your-subtree-branch]') do @set token=%%b
git push [alias-for-your-subtree] %token%:[name-of-your-subtree-branch] --force

pause

答案 10 :(得分:0)

也有此问题。根据主要答案,这就是我所做的事情:

给出:

#! git subtree push --prefix=public/shared project-shared master
git push using:  project-shared master
To git@github.com:***
! [rejected]        72a6157733c4e0bf22f72b443e4ad3be0bc555ce -> master (non-fast-forward)
error: failed to push some refs to 'git@github.com:***'
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.

输入此内容:

git push <origin> 72a6157733c4e0bf22f72b443e4ad3be0bc555ce:<branch> --force

请注意,'git push'中使用了'git subtree push'输出的令牌。