文章目录
4.4 利用交互式变基聚合版本
在自己的新分支开发一个特性时,可能会产生多个细粒度的提交版本,而往往这些版本并不需要在并入正式分支时参与代码评审或功能测试。这时就需要在合并分支前,将本地分支通过交互式变基压缩成满足要求的精简版。
本节示例从 origin/stable-3.1
签出新分支 rebaseExample3
,并在其中模拟出 6 个新增 commit
记录,然后演示怎样将这 6 个版本压缩为两个指定版本:
$ git checkout -b rebaseExample3 --track origin/stable-3.1 Switched to a new branch 'rebaseExample3' Branch 'rebaseExample3' set up to track remote branch 'stable-3.1' from 'origin'. $ git log origin/stable-3.1..origin/stable-3.2 --oneline --reverse # Reset to the 7th commit listed $ git reset --hard 5218f7b # Rebase interactively $ git rebase --interactive hint: Waiting for your editor to close the file...
弹出编辑器界面如下:
将第二个、第四个版本改为 squash
后保存退出:
接着,git
会按指定的版本弹出两次编辑器窗口,用以确认被并入的两个 commit
的提交信息,可以不作任何修改,退出即可。Git
将自动完成其余工作:
$ git rebase --interactive [detached HEAD f3ca970dd] Do not close ArchiveOutputStream on error Author: Jonathan Nieder <jrn@google.com> Date: Mon Sep 23 17:06:18 2013 -0700 6 files changed, 537 insertions(+), 2 deletions(-) create mode 100644 org.eclipse.jgit.test/tst/org/eclipse/jgit/api/DescribeCommandTest.java create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/api/DescribeCommand.java [detached HEAD b4e20341a] Prepare 3.2.0-SNAPSHOT builds Author: Matthias Sohn <matthias.sohn@sap.com> Date: Thu Oct 3 17:40:22 2013 +0200 67 files changed, 422 insertions(+), 372 deletions(-) rewrite org.eclipse.jgit.http.server/META-INF/MANIFEST.MF (61%) rewrite org.eclipse.jgit.java7.test/META-INF/MANIFEST.MF (66%) rewrite org.eclipse.jgit.junit/META-INF/MANIFEST.MF (73%) rewrite org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF (61%) rewrite org.eclipse.jgit.pgm/META-INF/MANIFEST.MF (63%) rewrite org.eclipse.jgit.test/META-INF/MANIFEST.MF (77%) rewrite org.eclipse.jgit.ui/META-INF/MANIFEST.MF (67%) rewrite org.eclipse.jgit/META-INF/MANIFEST.MF (64%) Successfully rebased and updated refs/heads/rebaseExample3. # Check in gitk $ gitk
结果如下:
示例拓展
除了关键词 squash
,交互式变基过程中还可以使用 fixup
功能,区别在于:squash
会保留被压缩 commit
的提交信息,而 fixup
不会。可以通过 git log -1
查看结果,或者与最终版本比较差异:git diff 5218f7b
。使用 fixup
的情况下,应该没有差异内容输出:
$ git checkout -b rebaseExample4 --track origin/stable-3.1 $ git log origin/stable-3.1..origin/stable-3.2 --reverse --oneline $ git reset --hard 5218f7b33 $ git rebase --interactive # Using fixup or f for short:
保存后关闭编辑器:
$ git rebase --interactive Successfully rebased and updated refs/heads/rebaseExample4. # Check via gitk
# Check via git status $ git status -1 commit 9bb94368060f0af6cbf0138c0d10d9b7df98780a (HEAD -> rebaseExample4) Author: Matthias Sohn <matthias.sohn@sap.com> Date: Thu Oct 3 17:40:22 2013 +0200 Prepare 3.2.0-SNAPSHOT builds Change-Id: Iac6cf7a5bb6146ee3fe38abe8020fc3fc4217584 Signed-off-by: Matthias Sohn <matthias.sohn@sap.com> # no commit message related to fixup commits # # Check git diff $ git diff 5218f7b33 # no output as expected
实测效果:
与前述期望结果一致。
小结
- 交互式变基的编辑器视图中,
commit
列表是按照从近到远的顺序展示的,与git log
的默认顺序 相反;squash
与fixup
的作用对象,是其 上一个 带pick
标记的commit
,时间顺序上看则是 下一个 带pick
的commit
;- 虽然选了 6 个版本来演示版本压缩,但实际进入操作列表的只有 4 个版本,另两个其实是合并产生的
commit
,根据Git
的相关文档,这样的commit
不参与交互式变基,也不推荐使用--preserve-merges
标记保留它们。
4.5 利用交互式变基变更提交者
开发一个新项目时,一开始可能设置的作者和邮箱并不是最终需要的,这时可能需要批量更正为正确的提交人信息。然而一般的 git commit --amend
只对 HEAD
处的 commit 有效,如果要应用到指定范围的 commit
对象上,可以借助交互式变基实现。
示例将从 master
分支签出一个新分支 resetAuthorRebase
,
# Checkout new branch from master $ git checkout -b resetAuthorRebase -t origin/master # Update HEAD's committer info (exit without modifying anything) $ git commit --amend --reset-author # Check the updated status (already updated) $ git log --format='format:%h %an <%ae>' origin/stable-3.2..HEAD -5 b76ed52f8 SafeWinter <zandong_19@aliyun.com> caea5a26f Matthias Sohn <matthias.sohn@sap.com> 284d2b5b9 Matthias Sohn <matthias.sohn@sap.com> 35713588f Matthias Sohn <matthias.sohn@sap.com> 1cffba438 Matthias Sohn <matthias.sohn@sap.com> $ git rebase --interactive --exec "git commit --amend --reset-author --reuse-message=HEAD" origin/stable-3.2 # When the editor view appeared, close it without modification # Check via git log $ git log --format='format:%h %an <%ae>' origin/stable-3.2..HEAD c5365deec SafeWinter <zandong_19@aliyun.com> 13512ccd0 SafeWinter <zandong_19@aliyun.com> d54c9e86b SafeWinter <zandong_19@aliyun.com> 6f090c0ea SafeWinter <zandong_19@aliyun.com> 96260fb33 SafeWinter <zandong_19@aliyun.com> 0d55fae34 SafeWinter <zandong_19@aliyun.com> 308bd65f7 SafeWinter <zandong_19@aliyun.com> 849ec32b1 SafeWinter <zandong_19@aliyun.com> 9f5b85b33 SafeWinter <zandong_19@aliyun.com> 5a74b14af SafeWinter <zandong_19@aliyun.com> ... more logs omitted
实测效果:
小结
- 执行交互式变基时,一个非常重要的参数,是在
--exec
参数指定的命令中,加入--reuse-message=HEAD
,表示将HEAD
的修改结果复用到变基命令指定的commit
对象中。如果不加该参数,Git
就会逐一弹出编辑器窗口让用户确认提交消息。加了--reuse-message=HEAD
后才能自动取消弹窗提示;--exec
有一个特性,就是在每次执行前,自动检测工作区是否有未暂存(unstaged
)的内容,因此如果示例不是修改提交人信息(如修改源代码),则变基过程中可能中断报错。解决方案是将这些操作在一个--exec
中一次性执行完毕。
本节示例功能虽然应用场景不常见,但却非常实用。
4.6 自动聚合版本
在新分支上修复项目 bug 时,本地可能有多个细粒度的提交版本,但最终修复 bug 并入 master
或总开发分支 develop
时,该 bug 应该只产生一个 bug
提交。根据 4.5 介绍的方法,可以使用交互式变基实现 commit
压缩;但作为常规操作流程,Git 还提供了一个现成的自动压缩(autosquashing
),可以很方便地将本地多个 commit
压缩成一个统一的版本。
本节将演示 autosquashing
的用法。继续沿用 chapter4
本地库:
# Checkout a new branch from master branch $ git checkout -b readme_update_developer --track origin/master # Create a new commit $ echo "More information for developers" >> README.md $ git status # here -a means adding any unstaged changes to the commit $ git commit -a -m "Updating information for developers" [readme_update_developer 41cb31e16] Updating information for developers 1 file changed, 1 insertion(+) # remember the abbreviated commit hash -- 41cb31e16 # Add 3 commits, then squash the first 2 onto the original commit with SHA-1 abbreviated as 1648e821c $ echo "even More information for developers" >> README.md $ git commit -a --squash=41cb31e16 --no-edit $ echo "even More information for developers" >> README.md $ git commit -a --squash=41cb31e16 --no-edit # Add the last commit which not squashed into 1648e821c $ echo "Adding configuration information" >> README.md $ git commit -a -m "Updating information on configuration" # Rebase interactively with --autosquash $ git rebase -i --autosquash
由于在需要压缩的提交前加入了 --squash
参数,交互式变基时会自动将所在版本标记为按 squash
关键字进行处理;同时提交信息中也会多出 squash!
字样的前缀,如图所示:
此时无需任何操作,关闭变基交互页即可。关闭后 Git
会按照预定的版本自动弹出提交窗口,以确认聚合版本的注释信息。如果无需改动,则直接跳过即可。最终的命令行结果为:
$ git rebase -i --autosquash [detached HEAD 9728d0cb8] Updating information for developers Date: Wed Dec 15 19:34:32 2021 +0800 1 file changed, 3 insertions(+) Successfully rebased and updated refs/heads/readme_update_developer. # Check via gitk $ gitk
最终效果如下:
如果希望在变基时默认按自动压缩的方式进行,可以设置如下配置:
$ git config rebase.autosquash true
这样,之后的所有 git rebase -i
都会默认追加 --autosquash
标记。
回到本节最开始提到的场景,如果要将本地的所有 bug
分支上的版本压缩为一个版本,则变基交互页关闭后,Git 还会弹出编辑器窗口确认该聚合版本的提交消息(commit message
)。如果不需要该弹窗,后续的本地压缩 commit
在执行提交时,可将 --squash=41cb31e16
改为 --fixup=41cb31e16
即可。