更新被拒绝,因为您当前分支的提示落后了 - 但为什么呢?

Updates were rejected because the tip of your current branch is behind - but why?

这个问题已经从另一个角度回答了here。但我的问题是首先试图理解为什么会出现这个问题。我在工作中一直 运行 关注这个问题,建议的解决方案并不过分令人满意,因为它并没有真正解决问题本身,并且有丢失提交的危险。

下面是我发现重现错误的最短 git 交互序列:

   git clone git@github.com:joyofdata/test3.git
   cd test3
   echo "1" > m
   git add .
   git commit -m "m1"
   git push origin master

   git checkout -b feature
   git push -u origin feature
   echo "1" > f
   git add .
   git commit -m "f1"
   git rebase master
   git push origin feature

   git checkout master
   echo "2" >> m
   git add .
   git commit -m "m2"
   git push origin master

   git checkout feature
   echo "2" >> f
   git add .
   git commit -m "f2"
   git rebase master
   git push origin feature (error - see next code box)

我只是在 master 中对文件 m 进行版本化,然后在 feature 中对文件 f 进行版本化,然后在 master 中提交更改,然后在 feature 中提交更改。现在,在将我的更改推送到远程功能分支之前,我想将其重新设置为 master。

这是上面列表中最后一个命令的命令和错误消息:

➜  test3 git:(feature) git push origin feature
To github.com:joyofdata/test3.git
 ! [rejected]        feature -> feature (non-fast-forward)
error: failed to push some refs to 'git@github.com:joyofdata/test3.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

现在 - 我尝试通过首先从远程功能中拉取(如消息中所建议的)来解决问题,然后解决冲突,推送到功能:

➜  test3 git:(feature) git pull origin feature
From github.com:joyofdata/test3
 * branch            feature    -> FETCH_HEAD
Auto-merging f
CONFLICT (add/add): Merge conflict in f
Automatic merge failed; fix conflicts and then commit the result.

➜  test3 git:(feature) ✗ vim f
➜  test3 git:(feature) ✗ git add .
➜  test3 git:(feature) git commit -m "deconflicted"
[feature 3fb647e] deconflicted

➜  test3 git:(feature) git push origin 
Counting objects: 5, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (5/5), done.
Writing objects: 100% (5/5), 565 bytes | 565.00 KiB/s, done.
Total 5 (delta 1), reused 0 (delta 0)
remote: Resolving deltas: 100% (1/1), done.
To github.com:joyofdata/test3.git
   0f31f60..3fb647e  feature -> feature

但是 - 从现在开始 - 我将一次又一次地 运行 一次又一次地进入那个错误,每次我在 master 上重新设置功能 - 再次是合并冲突,再次是那个错误。

➜  test3 git:(feature) git rebase master
First, rewinding head to replay your work on top of it...
Applying: f1
Applying: f1
Using index info to reconstruct a base tree...
Falling back to patching base and 3-way merge...
No changes -- Patch already applied.
Applying: f2

➜  test3 git:(feature) git push origin  
To github.com:joyofdata/test3.git
 ! [rejected]        feature -> feature (non-fast-forward)
error: failed to push some refs to 'git@github.com:joyofdata/test3.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

我不明白!

我的意思是 - 我想做的只是应用以下工作流程:

我简单地说 - 在提交远程功能之前,我想在 master 上重新设置功能。如果我将 master 合并到功能中(git pull origin mastergit merge master 如果本地 master 是最新的)那么我不会 运行 进入那个问题。

我希望这不会太混乱,但我不知道如何说得更简单,这让我很烦。

快速回答是不要对上游分支进行变基,link 解释了问题和解决方案: https://git-scm.com/docs/git-rebase#_recovering_from_upstream_rebase

一个基本的选择可能是这样的: 从您的示例开始,在错误行之前,分支如下所示:

$ git log --pretty=oneline --graph --all
* b5b045591ec6584e8f896d85399b7ed5b08d8098 (HEAD -> feature) f2
* 5187c95d6d91b550b9b2cc10ad673a52add620f6 f1
* cea62f3fd0349390115ee5e263730656b7a52d2d (origin/master, master) m2
| * f043b50940593c1ded4631f3681420ad57c0b190 (origin/feature) f1
|/
* 4847339d95d8f02c25538da7e51be14cbb30530d m1

执行时

$ git push origin feature

您尝试包含从分支功能到 origin/feature 的更改,这是不可能的,因为它们有不同的提交。

所以可以删除远程分支origin/feature

  1. 取消设置上游

    $ git checkout feature
    $ git branch --unset-upstream
    
  2. 删除远程分支

    $ git push origin --delete feature
    
  3. 将您的更改添加到上游

    $ git push -u origin feature
    

支线这样结束

$ git log --pretty=oneline --graph --all
* b5b045591ec6584e8f896d85399b7ed5b08d8098 (HEAD -> feature) f2
* 5187c95d6d91b550b9b2cc10ad673a52add620f6 f1
* cea62f3fd0349390115ee5e263730656b7a52d2d (origin/master, master) m2
* 4847339d95d8f02c25538da7e51be14cbb30530d m1

根据此示例,提交 b5b045 具有提交 f043b50 的更改,因此不会丢失更改,您可以检查

$ git diff f043b50 b5b045

问题的根源在于您正在重新设置 feature。这是一种草率的表达方式:重要的不是分支名称,而是提交。但是变基通过将一些提交复制到新的和(据说)改进的不同提交来工作。这需要丢弃——或放弃——旧的(现在很糟糕)提交。

git push 命令调用另一个 Git 并向他们发送您新的和改进的 feature 提交,然后请求其他 Git 存储库到 扔掉 它旧的(现在很糟糕)提交。它说:不!如果我问你问,我会失去一些宝贵的提交! 这个投诉以这种形式返回:

 ! [rejected]        feature -> feature (non-fast-forward)

non-fast-forward 错误是另一个 Git 告诉你的方式,如果它遵从你的礼貌请求更改它的名字 feature 来命名您建议它使用的新的和改进的提交,这将丢失旧的(被新的和改进的过时的)提交。

当然,这正是您想要它做的。注意:您是否应该 这样做取决于使用其他 Git 存储库(GitHub 上的那个)的其他人是否事先同意这即将发生。

要让另一个 Git,即 GitHub 上的那个,同意放弃其存储库中的提交,您必须在这个特定的 [=12= 上设置 "force flag" ] 手术。不过,有两种不同的强制标志:

  • 总力:我命令你,其他Git仓库,你的分支名称要这样设置!这个简单有效,弃用不仅是旧的和糟糕的提交,你已经通过 rebase 替换为新的和改进的提交,而且其他任何人添加的提交你没有 替换为新的和改进的副本。 (这是几个原因中的一个,每个使用此 GitHub 存储库的人都必须同意提前 将发生变基。)

  • Force-with-lease: 我命令你,其他 Git 存储库,将存储在你的分支名称中的哈希 ID 与我 [=62 的值进行比较=]认为 你有。如果它们相等,那么你应该用我现在提供的新值替换那个值。 这种强制推送的变体更安全:如果你重新设置了一些提交,但其他人从那时起添加了 new 提交您 失败 变基,您的强制租赁操作将失败。另一个 Git,在 GitHub,会说:我存储的哈希 ID 与你说的你认为我拥有的不匹配。所以我终究没有接受你的命令

如果您和此 GitHub 存储库的所有其他用户事先同意可以进行变基,并且您认为有人可能在您不注意的情况下偷偷提交了一些代码,您应该使用--force-with-lease 选项到 git push 以查明是否确实如此。

如果没有其他用户使用 GitHub 存储库(或者 none 他们使用 feature 分支),none 这是必需的,你可以简单地使用 git push --force。您唯一需要同意的人就是您自己:您是否允许自己强制推送 feature 分支?如果是这样,您可以强制推送 feature 分支。

请注意,如果有 个其他用户,并且您和他们都同意此强制推送变基过程,您和他们必须 全部 注意观察任何强制更新,并对 尚未 复制到新的和改进的提交的任何提交进行自己的变基。这是一个相当复杂的工作流程;确保你和你所有的朋友/同事都准备好了。