前言
团队项目代码管理从 SVN 整体 切换到 Git 已经有一段时间了,小伙伴们也在不断的适应新的版本控制开发规范。
就在最近的一个项目的 code review 中,一位同学抱怨另一位跟他协同开发的 git log 太杂乱了,经常出现一些意义不明确或者重复的 commit。于是我就和他们一块查看下项目的 git log。
1 | commit 45e488ac83cc25c8d8d26425aac36f6c568b7e61 (HEAD -> master) |
从 log 中能看出来,这名同学在提交某块功能代码时候经常出现反复修改的情况,具体看了下提交的内容,有一些还真是因为粗心导致的补救型提交。这位同学也表示粗心确实不应该,只是放着问题也不能不补救提交下啊,那么要如何合并一些意义不大的 commit 那?
至此,也就引到了 git rabase 上来了,合并 commit 需要用到 git rebase -i
。
git rebase -i
如上面的例子,获取下简略 log:
1 | ➜ git log --oneline |
需要把最近三次提交,45e488a
b19ad90
47136b5
合并到一起
使用 git rebase -i
,-i 是 –interactive 的简写,交互式 rebase,接下会有交互提示编辑 commit。
1 | git rebase -i 36579b1 |
参数 36579b1
指最新一个想完整保留 commit,也就是之后所有提交的 45e488a
b19ad90
47136b5
会被合并修改,虽然看似合并至47136b5 Feature: add XXX menu
,但其实本身也是进行了修改。
或者可以 HEAD~3
这样更直观的表达合并最近3次 commit
1 | git rebase -i HEAD~3 |
然后进入 commit 选择界面
1 | pick 47136b5 Feature: add XXX menu |
上面这个界面会倒序(靠下越新)列出来指定的最近3次提交,下面还列出来一些选择命令,默认为 Pick
因为大部分情况肯定是要保留合并 commit 的,这里着重说明下 use commit 相关:
p, pick :使用这个 commit
r, reword:使用这个 commit,同时编辑 commit 信息
e, edit:使用这个 commit,rebase 执行中会暂停让用户修正 commit
s, squash:使用这个 commit,同时和上一个 commit 信息合并
f, fixup:与 squash 类似,但丢弃这个 commit 信息
这里选择使用 squash,进入接下里的 commit 信息编辑界面后还会列出来之前的 commit 信息,方便我们思考合并后的 commit 信息内容,当然也能手动忽略之前的。
1 | pick 47136b5 Feature: add XXX menu |
按 INSERT
键进入编辑模式,类似上面把需要向上合并的 commit 改为 s
,是squash
的简写,节省时间。
然后按ESC
,输入:wq
保存退出,会自动进入 commit 编辑界面:
1 | # This is a combination of 3 commits. |
上面就列出来了要合并的3个 commit 信息,这里被 # 注释是掉的都会被忽略,剩下的作为 rebase 后的 commit 信息。这里就注掉最新的2个,在add XXX menu
的基础上进行修改,当然也可全删除直接写新的。
1 | # This is a combination of 3 commits. |
同样输入:wq
保存退出,git 会自动执行,然后检查下现在的 git log
1 | ➜ git log --oneline |
修改完成,使用git push -f
强制覆盖远端(注意!一定要确定远端无其他人同时在修改当前分支,防止将其他人提交覆盖)
引起的反思
虽然通过 rebase 合并了部分不必要的 commit 让 log 看起来更整洁了些,但是 git log 存在的意义就是真实的反馈代码的开发记录。尤其在多人协作时,强制使用git push -f
覆盖远端可能会造成不可预计的后果。
因此看了一些讨论,有两个观念:
- log 历史应该真实的反馈记录,被尊重并且不应该被修改
- log 历史应该清晰明确,方便被查阅
从哲学上讲,存在即合理,凡事都有相对的一面,不极端的去禁止或者推崇。
如何判断是否进行 git rebase -i 重写 git log
个人理解的判断标准:
- 多人协作的分支,所修改 commit 已提交远端,绝对禁止进行重写并强制覆盖
- 多人协作的分支,所要修改的 commit 在本地尚未被提交,允许重新 git log 但仅限于个人新增历史
- 分支仅个人使用的,远端可以强制覆盖更新,允许重新 git log
这样适当的使用 git rebase -i 可以帮助项目的 commit log 更清晰。