Git Rebase 防止合并提交
在基于主干或敏捷的开发中,尽量减少嘈杂提交的数量以保持任何可能的回归易于回溯通常被认为是一种很好的做法。
曾经有一个工作区,主分支上的内容尽量精简,提交次数也尽可能少。每个人的功能分支都会以单个压缩提交的形式合并。这样做的危险在于,如果某个功能分支中出现了 bug,那么按时间顺序追溯起来会非常困难。
然后,我们遇到了一个工作单位,他们希望每次提交都能讲述一个故事,所以我们会在每个文件的提交中注释更改。无论哪种情况,我们学到的教训是,要小心谨慎,让提交能够讲述所做的更改。
1. 在远程主干上合并 PR 时阻止合并提交
(...其中“trunk”是迭代开发分支)
请参阅Atlassian 针对 Bitbucket 详述的“合并策略”
请参阅Github 的“关于合并方法”
2. 从远程拉取到本地时阻止合并提交
我想这就是您阅读这篇文章的目的。
原则上,这是需要改变的设置
无论何时拉取数据,都要将 git 配置设置为 rebase:
git config --global branch.autosetuprebase always
根据您的工作风格,以下 3 种方法可以在开发过程中,防止远程代码合并后再次发生合并提交。这些方法都与您如何管理自己的变更有关。
如果您不使用上述设置,以下 3 个工作流程将准确说明每次拉取时发生重新定基意味着什么。
方法 1:像往常一样进行本地提交,以及git pull rebase
需要从远程源合并时
- 在您签出的功能分支上,随时提交您的更改 - 它将在您的本地分支上创建提交。
- 您已准备好进行 PR,但意识到 dev 分支已经取得了进展,因此您运行:
git pull --rebase <remote-name> <branch-name>
或者在我们的例子中,git pull origin development —rebase
- 如果新内容
development
与代码中的更改发生冲突,VS 代码将告诉您类似以下内容:
From bitbucket.org:your-organization/your-repo
* branch development -> FETCH_HEAD
1ab345e..d321ff7 development -> origin/development
Auto-merging types/style.d.ts
Auto-merging src/abc-folder/assets/fonts.ts
CONFLICT (content): Merge conflict in src/abc-folder/assets/fonts.ts
Auto-merging src/folder/theme.ts
CONFLICT (content): Merge conflict in src/folder/theme.ts
error: could not apply f4e0681... Rename thing x to thing y
Resolve all conflicts manually, mark them as resolved with
"git add/rm <conflicted_files>", then run "git rebase --continue".
You can instead skip this commit: run "git rebase --skip".
To abort and get back to the state before "git rebase", run "git rebase --abort".
事实上,Git 会列出每个带有标志的合并冲突的文件CONFLICT
!
- 导航到列出的每个文件,VS Code 会将本地更改和传入更改一目了然。您可以选择包含“当前”更改、“传入”更改还是“两者”。您甚至可以在任一版本中进行编辑并保留该版本。
git add
每个冲突文件的名称来确认/暂存更改git rebase —-continue
完成重新定基。- 最后,由于您自己的本地提交而更改的文件也需要进行
git add
-ed。因此添加这些并运行git rebase --continue
- 当你收到以下消息时,你就知道一切都成功了:
Successfully rebased and updated refs/heads/your-feature-branch-name
⚠️ rebase 会将你在当前本地分支上提交的提交取出HEAD
并作为补丁。然后,它会将所有远程提交应用到 之上HEAD
,并将你最新的提交应用到 之上。
⚠️ 强烈建议您在执行此方法之前,先整理一下本地分支的提交历史记录(请参阅交互式 rebasedevelopment
)。如果执行完步骤 3 之后,您的分支被合并到远程分支,则将包含修改后的历史记录。
- 运行
git log
后,您将看到对每个文件所做的选择的线性回顾历史记录,没有合并提交! - 有时,当您准备好推送时,您会发现远程本地现在与您从中拉取并重新设置的内容不同步
development
:
Your branch and 'origin/bug-123' have diverged,
and have 28 and 1 different commits each, respectively.
我们这里拥有的是:
-
本地有 28 个提交,这些提交是我自己本地提交与
development
-
我在远程功能分支上创建并推送到我的远程功能分支的 1 个提交
运行git pull —-rebase
我现在落后的远程本地的内容将启动一个在这种情况下不会有结果的重新定基过程,所以我在我的功能/错误分支上运行git push -f
( )。—-force
如果您已经对当前的本地版本进行了影响测试,并且您非常有信心当前版本是您可以使用的权威版本-f
。
强制推送时务必格外小心,切勿在共享的公共分支上进行。
你实际上正在改写历史!
➕优点:
没有过多的合并提交。
触发较少的合并冲突标记,如果您贡献的代码是新的,则不触发。
在 rebase 结束时,你的分支将与分支HEAD
同步development
➖ 缺点:
这里最大的风险在于,任何原本应该被其他人的合并提交删除的文件或代码,如果仍然保留在你的分支中,可能会被重新添加。在大型功能分支上进行 rebase 更改的过程中,你可能会意外覆盖其他人的工作。幸运的是,即使你这样做了,当你使用这个功能分支发起拉取请求时,你的队友也会注意到你在 PR 中覆盖或删除的任何内容。
如果您将任何已经在远程的提交压缩在一起,那么您将遇到非常困难的合并冲突。
方法 2:存储所有未提交的更改,git pull rebase 从远程拉取,然后提交更改
- 签出一个新分支并开始进行更改。不进行任何提交,因此如果您运行,
git status
将会有很多红色的未跟踪文件。 - 你意识到远程分支已经领先,需要更新本地分支,但你仍需要保留未提交的更改:
git stash
上述操作会将你的项目状态恢复到你进行更改之前的状态。你的本地分支只是落后于远程分支而已development
。 - 您运行
git pull —-rebase
以引入新的更改而不会导致合并提交。 - 恢复你使用
git stash apply
- 然后提交所有
➕ 优点:再次强调,需要解决的合并冲突大大减少。无需合并提交。
➖ 缺点:保留所有不提交的更改需要以非常清晰的方式工作,如果我进行 PR 只是为了向他们展示我遇到的问题,那么很难向队友展示问题。
方法 3:创建一个侧分支来运行 rebase
如果您对这一切还不满意,您可以使用功能分支作为分支,在该分支中您将单独分支中的一系列提交与远程提交合并。
例子:
git checkout development
从开发分支开始git checkout -b feature-branch
签出包含当前开发提交的功能分支- 开始对所有更改进行提交
-
准备就绪后,您可以检出一个临时分支,其中包含功能分支的所有更改,同时仍在
feature-branch
:git checkout -b temporary-branch
-
最后一条命令会创建你的功能分支的“副本”。要将新功能
development
和临时分支合并(在临时分支上)[^1]:git rebase -i development
-
签出您的初始功能分支:
git checkout feature-branch
-
git merge temporary-branch --ff
这里的—-ff
标志告诉我们合并两个分支时不要创建合并提交,而是将指向最近提交(又名HEAD
)的指针快进到最近的提交。
- 像往常一样推送并创建功能分支的 PR。
➕ 优点:这可能是三种方法中最安全的方法
➖ 缺点:这种方法比较冗长,而且可能比较理论化。我之前并没有真正逐字逐句地完成过整个过程。
[^1] 我用引号表示“复制”,因为这样更容易思考,但 Git 实际上是在创建指向初始分支的指针。
推荐阅读
rebase命令
文章来源:https://dev.to/jenc/git-rebase-for-preventing-merge-commits-2len