93

我将 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.

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

4

12 回答 12

103

我在此博客评论https://coderwall.com/p/ssxp5q上找到了答案

如果您在推送时遇到“由于当前分支的尖端落后而导致更新被拒绝。合并远程更改(例如'git pull')”问题(由于任何原因,尤其是与 git 历史记录有关)那么您需要嵌套 git 命令,以便您可以强制推送到 heroku。例如,给定上面的例子:

git push heroku `git subtree split --prefix pythonapp master`:master --force
于 2013-03-25T19:49:57.383 回答
46

在 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
于 2015-03-06T17:18:31.260 回答
29

使用--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/任何包含它的路径的前面,并删除有关没有它的文件的任何信息。这意味着即使您提取非压缩文件,所有上游子存储库提交都将被忽略,因为它们以裸路径命名文件。(不涉及任何文件的提交public/shared/,或与父级相同的合并提交,也会被折叠。[编辑:另外,我已经发现了一些壁球检测,所以现在我认为只有当你拉出非挤压,然后在另一个答案中描述的简单合并提交折叠设法选择非挤压路径和丢弃压扁的路径。]) 结果是,它尝试推送的内容最终包含有人提交到您正在推送的当前主机存储库的任何工作,但不包含直接提交到子存储库或通过其他主机的工作人员存储库。

但是,如果您使用--onto,则所有上游提交都被记录为可以逐字使用,因此当重写过程遇到它们作为要重写的合并的父级之一时,它将保留它们而不是尝试重写它们以通常的方式。

于 2015-06-08T17:10:54.833 回答
16

对于“GitHub pages”类型的应用程序,您将“dist”子树部署到 gh-pages 分支,解决方案可能看起来像这样

git push origin `git subtree split --prefix dist master`:gh-pages --force

我提到这一点是因为它看起来与上面给出的 heroku 示例略有不同。您可以看到我的“dist”文件夹存在于我的 repo 的 master 分支上,然后我将它作为子树推送到 gh-pages 分支,该分支也在原点上。

于 2016-06-23T19:16:23.867 回答
4

也有这个问题。根据最佳答案,这是我所做的:

鉴于:

#! 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 subtree push”输出的令牌在“git push”中使用。

于 2019-03-21T08:52:32.383 回答
3

我以前也遇到过这个问题,下面是我的解决方法。

我发现我有一个未附加到本地主分支的分支。这个分支存在,它只是悬在虚空中。在您的情况下,它可能称为project-shared. 假设是这种情况,并且当您执行 a 时,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-sharedmasterproject-shared

于 2013-08-08T16:32:33.710 回答
3

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

让我们看一个示例(您可以轻松复制)以更好地了解其工作原理。考虑以下历史(行格式为: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拥有相同的 tree z,因为我们有 commit C,它撤销了 commit B,所以即使它对它进行了更改, B-Csequence 也不做任何事情。dir

现在让我们进行拆分。首先,我们在 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

接下来我们在 commit 上拆分E

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

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

通常你可以通过使用来容忍这个错误push --force,因为丢弃的提交通常不会包含关键信息。从长远来看,这个 bug 需要修复,因此拆分历史实际上会包含所有的提交,这些提交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
于 2015-02-06T01:11:47.387 回答
0

埃里克伍德拉夫的回答对我没有帮助,但以下内容对我有帮助:

我通常使用“--squash”选项来“git subtree pull”。看起来这确实让事情变得更难调和,所以这次我需要在不压缩的情况下进行子树拉取,解决一些冲突然后推送。

我必须补充一点,压扁的拉力没有显示任何冲突,告诉我一切都很好。

于 2015-03-20T15:49:30.663 回答
0

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

于 2015-05-27T14:34:50.513 回答
0

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

git push subtree_remote_address.git `git subtree split --prefix=subtree_folder`:refs/heads/branch --force
于 2017-03-13T10:40:37.730 回答
0

基于 Chris Jordan 解决方案的 Windows 用户快速 powershell

$id = git subtree split --prefix pythonapp master
Write-Host "Id is: $id"
Invoke-Expression "git push heroku $id`:master --force"
于 2018-01-26T02:40:40.463 回答
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
于 2019-01-11T20:07:19.847 回答