Git rebase 而不是合并,正确的做法是什么?

Git rebase instead of merge, the correct way to do it?

我想通过使用 rebase 而不是 merge 来避免在我的远程存储库上重复出现分支交叉点。

为了让您更好地理解我想要实现的目标,请考虑以下情况:

$ git lg
* 2345678 hotfix (HEAD -> master)
* 1234567 foo (origin/master, origin/HEAD)

$ git push 
! [rejected]  master -> master (fetch first)

$ git fetch
$ git lg 
* 2345678 hotfix (HEAD -> master)
| * 3456789 other change (origin/master, origin/HEAD)
|/
* 1234567 foo

通常,解决此问题的标准方法是 merge 后跟 push

$ git merge origin/master
$ git lg
* 4567890 Merge remote-tracking branch 'origin/master'
|\
* |  2345678 hotfix (HEAD -> master)
| * 3456789 other change (origin/master, origin/HEAD)
|/
* 1234567 foo
$ git push

我不喜欢这个解决方案,因为在这种特殊情况下我可以很容易地避免分支。因此,让我们使用 git reset --hard head~1 恢复更改并尝试另一种解决方案:

$ git rebase origin/master
First, rewinding head to replay your work on top of it...
Applying: hotfix

$ git lg
* 2345678 hotfix (master)
| * 5678901 hotfix (HEAD)
| * 3456789 other change (origin/master, origin/HEAD)
|/
* 1234567 foo

现在是令人不快的部分,我必须将 master 移回 HEAD

$ git branch -D master
$ git checkout -b master
$ git push
$ git branch --set-upstram-to=origin/master master
Branch master set up to track remote branch master from origin.
$ git lg 
* 5678901 hotfix (HEAD -> master, origin/master, origin/HEAD)
* 3456789 other change
* 1234567 foo

我的问题是如何简化我的 rebase 并避免不愉快的部分?

我认为最简单的方法是更改​​您的拉取工作流程。这里有几个选项。

首先可以使用--rebase标志拉取

git pull --rebase

根据文档,git pull 在其默认配置中执行 fetch 后跟 merge。使用 --rebase 标志会将 merge 替换为 rebase :)

其次,您可以设置默认值以始终使用 git pull

执行此操作
git config --global pull.rebase true

我会推荐第一种方法,因为设置默认的 rebase 让我很紧张。我为 git pull --rebase 创建了一个别名 git pr 以使其更容易。这样我就可以在每次拉动时做出决定。

问题是你在本地直接在 master 分支上工作。当您对 master 分支进行一些本地更改并且在您开始后也有上游更改时,您会遇到问题中描述的冲突。因此,避免这种情况的解决方案就是不直接在 master 分支上工作,而是在一个或多个其他本地分支上工作。

所以你把你的修补程序更改放在一个单独的分支上,比如 hotfix_branch 然后 fetch/pull 通常是 master 分支(没有任何冲突!)。当你想交付你的修补程序更改时,你可以重新设置 botfix 分支以保留在新拉出的 master 分支之上,将 hotfix 分支合并到 master 并推送。

示例命令:

$ git pull master
$ git checkout -b hotfix_branch master
$ $EDITOR some.file
# Time passes and changes are made on origin/master
$ git add some.file
$ git commit -m "hotfix"
$ git pull master                           # No conflicts sine master is "clean"
$ git rebase master hotfix_branch      # This step might have merge conflicts but
                                       # if so those will come no matter what you
                                       # do with regards to branching
$ git checkout master
$ git merge hotfix_branch
$ git push

有一个微小的 window 可能会在您拉动和尝试推送 master 之间 origin/master 上发生新的变化,但如果是这样,您只需要做

$ git checkout master                   # if not already on the master branch
$ git reset origin/master

然后从上面列表中的第二个 pull master 命令重新开始。

我想对已接受的答案提出一种稍微不同的方法。我很少使用 pull 命令,但当我这样做时,我的偏好是在没有指定我希望变基的情况下不自动变基。此外,也许它更罕见,但如果我真的希望进行合并提交怎么办?相反,我的偏好是设置我的配置,如果 fast-forward 不可能,则拉取将失败,使用此配置设置:

[pull]
  ff = only

这样我知道我总是可以安全地 pull 并且它会 fast-forward,或者我会得到一个错误,因为我的分支已经分叉。如果出现错误,我可以决定是否要变基或合并(甚至重置到远程!),并相应地进行操作。