为什么我不能推送此最新的Git子树?


88

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

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


您为什么不执行git告诉您的内容:git pull?
AlexWien 2012年

8
Git pull与我的问题无关。但是,是的,git pull给了我最新消息,因为原始仓库中没有新内容。
mateusz

1
听起来您所在的分支位于主HEAD所在的分支后面-例如,历史记录...-> A-> B-> C,远程主HEAD在C上,但是您当前的分支在A上(或A的某些后代与C无关)。我肯定会查看我的git历史记录。
Mark Leighton Fisher

我有同样的问题。我一直在--rejoin加速子树推送。当我手动运行运行子树的步骤时subtree split --rejoin,拆分子树分支通常只包含整个子树历史记录,而历史记录只能追溯到最后一次重新加入。对我来说,这是非快进错误的直接原因。我仍然不确定为什么子树拆分会生成截断的历史记录。
hurrymaplelad 2014年

Answers:


98

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

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

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

2
帮助我!谢谢。
Michael Forrest

8
如何在Windows上运行此命令?我得到error: unknown option 'prefix'
pilau,


2
确实,但是以这种方式使用子树的大多数用途可能是推送到heroku的一种方法。通常,heroku不被认为是多用户推送和拉出的实际git repo。
埃里克·伍德拉夫

11
我有一个很难确定什么关系heroku,并pythonapp有答案。将其翻译成Heroku专用语言:git push <your subtree's origin> `git subtree split --prefix=Path/to/subtree master`:master --force
aednichols

39

在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

这是一个更清洁的解决方案。为我工作!
无限

当我运行git subtree split --prefix subtreedirectoryname master时,出现致命的模棱两可的参数'master':未知修订或路径不在工作树中
Demodave

这是一个很好的解决方案,我一直得到git subtree的不是命令。使用您的解决方案后,我能够将文件夹部署到heroku而不是我的所有
仓库


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,则所有上游提交都记录为OK以便使用逐字记录,因此当重写过程作为要重写的合并的父项遇到它们时,它将保留它们而不是尝试对其进行重写以通常的方式。


2
我建议将此答案作为已经对我有两次帮助的答案。我需要了解描述,才能了解如何拆分并推送到正确的位置。谢谢。
卡米尔·沃兹尼亚克

2
什么是“项目共享”?
Colin D

1
@ColinD“项目共享”是远程的名称。在配置要从中提取和推送到的位置时,将选择该名称。您可以使用来获取遥控器列表git remote -v

@entheh您花在此上的时间让jsut成为了我的一天。谢谢!
2016年

1
我发现当您的子存储库具有名称与前缀相同的目录时,总是会发生此错误,例如,将子存储库以“ libxxx”的形式添加到主存储库中,并且子存储库本身也有一个名为“ libxxx”的子目录”。在这种情况下,git会错误地将子存储库中的提交视为主存储库中的提交,并尝试拆分它们。的--onto选项可帮助git识别子存储库中已经存在的提交。
Cosyn

14

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

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

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


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远程分支project-shared


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具有同一棵树z,因为我们有提交C,撤消了提交B,因此B-C序列不会为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,因为删除的提交中通常不会包含关键信息。从长远来看,该错误需要修复,因此拆分历史记录实际上将包含所有提交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

该错误从2020-05-09(星期六)开始修复?@klimkin
halfmoonhalf

该错误已修复。请参阅:contrib / subtree:修复“ subtree split”跳过合并错误。github.com/git/git/commit/...
halfmoonhalf

0

埃里克·伍德拉夫(Eric Woodruff)的回答对我没有帮助,但以下内容可以帮助我:

我通常使用'--squash'选项来'git subtree pull'。看来这确实使事情变得更难以调和,因此这次我需要进行一次子树拉取而无需挤压,解决一些冲突然后再推入。

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


0

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


1
这正是我解决远程存储库与本地子树不同步的相同问题的方式。
阿依达斯手足炎

0

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

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

0

基于Chris Jordan解决方案的Windows用户快速安装工具

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

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

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”输出的令牌。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.