你好!我看到人们在 Git 中遇到的最常见问题之一是本地分支(如main
)和远程分支(也可能称为main
)出现分歧。
有两件事使这种情况变得困难:
- 如果你不习惯解释 git 的错误消息,那么即使意识到你的
main
已经与远程main
发生了分歧,这也是不平凡的(git 通常只会给你一个令人生畏但通用的错误消息,比如! [rejected] main -> main (non-fast-forward) error: failed to push some refs to 'github.com:jvns/int-exposed'
) - 一旦你意识到你的分支已经偏离了远程
main
,就没有单一明确的方法来处理它(你需要做什么取决于情况和你的 git 工作流程)
那么,让我们来谈谈 a) 当您处于本地分支和远程分支出现分歧的情况时如何识别以及 b) 您可以采取什么措施!这是一个快速目录:
让我们从两个分支“分叉”意味着什么开始。
“分歧”是什么意思?
如果您有本地main
和远程main
,则有 4 种基本配置:
1:最新的。本地和远程main
分支位于完全相同的位置。像这样的东西:
a - b - c - d ^ LOCAL ^ REMOTE
2:本地落后
在这里你可能想要git pull
。像这样的东西:
a - b - c - d - e ^ LOCAL ^ REMOTE
3:遥控器在后面
在这里你可能想要git push
。像这样的东西:
a - b - c - d - e ^ REMOTE ^ LOCAL
4:他们分歧了:(
这就是我们在这篇博文中讨论的情况。它看起来像这样:
a - b - c - d - e \ ^ LOCAL -- f ^ REMOTE
没有一种方法可以解决这个问题(您想要如何处理它取决于具体情况和您的 git 工作流程!),但让我们讨论一下如何认识到您处于这种情况以及如何解决它的一些选项。
识别分支何时分叉
有 3 种主要方法可以判断您的分支已出现分歧。
方式一: git status
最简单的方法是运行git fetch
然后运行git status
。您会收到类似这样的消息:
$ git fetch $ git status On branch main Your branch and 'origin/main' have diverged, <-- here's the relevant line! and have 1 and 2 different commits each, respectively. (use "git pull" to merge the remote branch into yours)
方式二: git push
当我运行git push
时,有时会收到如下错误:
$ git push To github.com:jvns/int-exposed ! [rejected] main -> main (non-fast-forward) error: failed to push some refs to 'github.com:jvns/int-exposed' hint: Updates were rejected because the tip of your current branch is behind hint: its remote counterpart. Integrate the remote changes (eg hint: 'git pull ...') before pushing again. hint: See the 'Note about fast-forwards' in 'git push --help' for details.
这并不总是意味着我的本地main
和远程main
已经分歧(这可能只是意味着我的main
落后了),但对我来说这通常意味着这一点。因此,如果发生这种情况,我可能会运行git fetch
和git status
来检查。
方式三: git pull
如果我在分支出现分歧时git pull
,我会收到以下错误消息:
$ git pull hint: You have divergent branches and need to specify how to reconcile them. hint: You can do so by running one of the following commands sometime before hint: your next pull: hint: hint: git config pull.rebase false # merge hint: git config pull.rebase true # rebase hint: git config pull.ff only # fast-forward only hint: hint: You can replace "git config" with "git config --global" to set a default hint: preference for all repositories. You can also pass --rebase, --no-rebase, hint: or --ff-only on the command line to override the configured default per hint: invocation. fatal: Need to specify how to reconcile divergent branches.
这个问题非常清楚(“你有不同的分支”)。
尽管当您的分支出现分歧时, git pull
并不总是会输出此错误消息:这取决于您如何配置 git。我知道的其他三个选项是:
- 如果你设置
git config pull.rebase false
,它会自动开始合并远程main
- 如果你设置
git config pull.rebase true
,它会自动开始重新定位到远程main
- 如果
git config pull.ff only
,它将退出并显示错误fatal: Not possible to fast-forward, aborting.
既然我们已经讨论了一些方法来识别您所处的情况,即本地分支与远程分支存在分歧,那么让我们谈谈您可以采取哪些措施。
没有一个解决方案
没有“最佳”方法来解决分歧的分支 – 这实际上取决于您的 git 工作流程以及发生这种情况的原因。
我根据具体情况使用3种主要解决方案:
- 我想将两组更改保留在
main
上。为此,我将运行git pull --rebase
。 - 远程更改没有用,我想覆盖它们。为此,我将运行
git push --force
- 本地的修改没用,我想覆盖它们。为此,我将运行
git reset --hard origin/main
以下是有关所有这 3 个解决方案的更多详细信息。
解决方案1.1: git pull --rebase
当我想保留两组更改时,我就是这样做的。它将main
重新设置为远程main
分支。我主要在存储库中使用它,我在main
分支上完成所有工作。
您可以配置git config pull.rebase true
,每次都会自动执行此操作,但我不这样做,因为有时我实际上想使用解决方案 2 或 3 (用远程覆盖我的本地更改,或相反)。我宁愿被警告“嘿,这些分支已经分歧,你想如何处理它?”并自行决定是否要重新设置基准。
解决方案1.2: git pull --no-rebase
这将启动local
和远程main
之间的合并。在这里你需要:
- 运行
git pull --no-rebase
。这将启动合并并(如果成功)打开一个文本编辑器,以便您可以确认是否要提交合并 - 将文件保存在文本编辑器中。
对此我没有太多可说的,因为我从来没有这样做过。我总是使用 rebase 来代替。不过,这是个人的工作流程选择,很多人都有非常合理的理由避免 rebase 。
解决方案2.1: git push --force
有时我知道远程main
上的工作实际上是无用的,我只想用本地main
上的任何内容覆盖它。
我经常在私人存储库上这样做,我是唯一的提交者,例如我可能:
-
git push
一些提交 - 迟来的决定我想改变最近的提交
- 进行更改并运行
git commit --amend
- 运行
git push --force
当然,如果存储库有许多不同的提交者,以这种方式强制推送可能会导致很多问题。在共享存储库上,我通常会启用github 分支保护,这样就不可能强制推送。
解决方案 2.2: git push --force-with-lease
我仍然从未真正使用过git push --force-with-lease
,但我看到很多人推荐它作为git push --force
的替代品,以确保自上次以来没有其他人更改过分支您推送或获取的时间,这样您就不会意外地破坏它们的更改。似乎是一个不错的选择。
解决方案3: git reset --hard origin/main
您可以将其用作git push --force
的反向操作(因为没有git pull --force
)。当我知道我的本地工作不应该在那里并且我想扔掉它并用远程分支上的任何内容替换它时,我就会这样做。
例如,如果我不小心对实际上应该在新分支上的main
进行了提交,我可能会这样做。解决这个问题看起来像这样:
# 1. put the commit on `new-branch` git checkout -b new-branch # 2. go back to the `main` branch I messed up git checkout main # 3. make sure that my `origin/main` is up to date git fetch # 4. double check to make sure I don't have any uncomitted # work because `git reset --hard` will blow it away git status # 5. force my local branch to match the remote `main` # NOTE: replace `origin/main` with the actual name of the # remote/branch, you can get this from `git status`. git reset --hard origin/main
与往常一样, git reset --hard
是一个危险的操作,您可能会永久丢失未提交的工作。我总是先运行git status
以确保没有任何未提交的更改。
使用git reset --hard
的一些替代方法:
- 查看其他一些分支并运行
git branch -f main origin/main
。 - 查看其他一些分支并运行
git fetch origin main:main --force
就这样!
我从来没有真正想过如果你不习惯阅读git push
和git pull
错误消息,它们会多么令人困惑。
原文: https://jvns.ca/blog/2024/02/01/dealing-with-diverged-git-branches/