本文已参与「掘力星计划」,赢取创作大礼包,挑战创作激励金。
系列
由于篇幅问题,一篇文章写完太长,因此分了几篇来写
Android Studio Git 进阶技巧 交互式变基实战教程
Android Studio Git 周边插件一览 - 掘金
介绍
交互式变基
用命令行实现的话 git rebase -i <base-commit>
-i
是 --interactive
的简写,中文翻译叫 “互动”,与 rebase
结合的话我们一般就称之为 交互式变基
。
命令行操作
前提
假设我们有3个问题,一个是乱码问题,一个是图片展示问题,一个是跳转问题。
其中乱码问题修复了3次才修复好,然后图片的修复git commit 日志打了错别字,然后跳转问题我还在修改中,代码改了一半,在local changes 中有修改的文件还没有提交。
所以我们产生了上图所示的4次提交记录。
再假如现在我们要下班了,跳转问题今天搞不定,我打算留到明天,前2个修复的问题我需要push remote了
当然我们可以直接无脑 git push 一下
但是这时候git 提交历史记录就会显的杂乱不堪
作为新时代有追求的程序员,我们肯定不允许啊
这时候要咋办,对已提交的记录做修改和整合呗
命令行处理
下面我先用命令行来处理
PS D:\code\library\VectorMaster> git status
On branch groups-support
Your branch is ahead of 'origin/groups-support' by 4 commits.
(use "git push" to publish your local commits)
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: app/src/main/java/com/sdsmdg/harjot/vectormasterdemo/MainActivity2.java
new file: app/src/main/res/color/color_gradient.xml
new file: app/src/main/res/drawable/svg_gradient.xml
new file: app/src/main/res/drawable/svg_gradient2.xml
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: app/src/main/AndroidManifest.xml
PS D:\code\library\VectorMaster> git rebase -i HEAD~4
error: cannot rebase: You have unstaged changes.
error: additionally, your index contains uncommitted changes.
error: Please commit or stash them.
PS D:\code\library\VectorMaster> git stash
Saved working directory and index state WIP on groups-support: f48877b fix: 还要修复乱码问题 - -
PS D:\code\library\VectorMaster>
可以看到 我们先 git status
查看了下工作区的文件状态,的确有一些文件还没有提交(修复跳转问题ing)
这时候,我们使用命令 git rebase -i HEAD~4
git立马就报错了,说我们还有未提交的改动,让我们提交或者暂存
这就非常不智能了,我现在要操刀的是已经提交的记录,按道理是不相关的,现在非得要 git stash
一下
stash
之后,我们继续刚刚的git-rebase
操作,此时会进入一个 vim 的交互式页面
可以看到上面4行就是我们的提交记录
格式为 <rebase action> <commit hash> <commit message>
下面有一个注释说明,也就是我们接下来要在交互式变基中要使用的命令项,我们一一解读下
-
p, pick = use commit
pick,简写p,意思是使用 commit。
通过上图可以看到,git-rebase内的 commits 默认都是 pick 命令,意思就是选择(需要)这个 commit,并且不做任何改动。
-
r, reword = use commit, but edit the commit message
reword,简写 r,意思是使用 commit,但是需要编辑(修改) commit message。
也就是说,如果我们想改写某个 commit message,就在 commit-hash 之前把默认的 pick 改写为 reword 或简写 r。
-
e, edit = use commit, but stop for amending
edit,简写 e,意思是可以暂时停止 rebase,此时允许修改 文件内容 和/或者 修改 commit message,然后继续 rebase。
也就是说,如果我们想要修改某次 commit 的提交内容,就可以使用 edit 命令。
-
s, squash = use commit, but meld into previous commit
squash,简写 s,意思是使用 commit,但是把修改内容融入到上一个 commit。
我们可以利用这个action来合并多个 commit !!!
-
f, fixup = like "squash", but discard this commit's log message
fixup,简写 f,与 squash 意思一样,但是直接丢弃 commit message。
也就是说在编辑 commit message 时,不显示这个 commit 的 commit message。
-
x, exec = run command (the rest of the line) using shell
exec,简写 x,意思是在 rebase 过程执行脚本命令。
因为在 rebase 交互过程中的改动是未经测试的,所以为了避免在 rebase 交互过程中的修改产生破坏性内容,所以提供执行脚本命令的功能,比如执行一些测试测试脚本。
-
d, drop = remove commit
drop,简写 d,意思是移除 commit。
很显然,就是删除这个 commit,并且会将这次 commit 的内容也删除掉。
现在我们来实际操作下
我们将这4条改成
pick dadb224 fix: 修复乱码问题
s f21a095 fix: 继续修复乱码问题
s f48877b fix: 还要修复乱码问题 - -
r e5fdfe9 fix: 修复图片展示维他奶
解读: 我们将 <e5fdfe9>
这个移动到了最后一行,因为这个排列的顺序就代表git历史记录中的顺序,所以当我们的commits需要改变顺序的时候,直接行与行交换就行了
然后我们将<f21a095>
<f48877b>
这两条commit的action都改成了 squash
我们按下 :wq
,假如一切顺利的话,这时候我们会看到一个新的界面,让我们填写下合并后的commits
的message
,git-rebase
到这里就结束了
但是很不幸,我们遇到了冲突,这时候我们需要去手动解决冲突,然后执行 git rebase --continue
这时候就来到了新界面,首先是先改写我们合并commit之后的message
我们把注释上面的都删掉 改成 <fix: 修复乱码问题--rebase过的 test>
按下:wq
之后
接下来会弹出新的界面继续处理我们的 <r e5fdfe9 fix: 修复图片展示维他奶> 这行,也就是修改message
改完之后,依然是:wq
一下
这个时候我们的git-rebase
就完成了,我们看下log,是不是我们想要的效果
完美,perfect 🤞🤞🤞
跌跌撞撞总算是完成了需求,但是我们可以看到,有一定的上手成本,有不少细节,不仅rebase前遇到了困难,rebaseh中还遇到了冲突,这对新手来简直是个是个天灾。
git reflog 时光机器
接下来我们就展示下利用Android Studio 内置的小工具如何轻松实现。
首先,我们先恢复一下刚刚的操作,利用git reflog 轻轻松松拥有时光机器。
reflog记录了我们在git中的每一步的操作,我们从这里找到要回去的时间点(git commit hash),比如这里就是f48877b
我们敲下命令 git reset --hard f48877b
此时再看下git log
嗯 又顺利穿越到了杂乱不堪的历史提交记录现场
接下来我们使用Android Studio 来秀一下
Android Studio 内置小工具的交互式变基流程
要实现这个需求贼简单,打开git log
,找到要处理的第一个commit
。右键,选择Interactively Rebase from Here...
翻译下就是 从这里开始交互式变基
点击之后就会弹出交互式面板,让我们操作我们选择的commit
可以看到这个界面将git-rebase
的几个参数都做成了按钮,我们点点就完成了复杂的操作,尤其是在还有冲突的时候解决起来贼方便 。
最新的提交在最下面,我们选中某一个commit
之后
操作栏就几个按钮
一二两个是改变顺序
第三个是pick
,由于默认就是pick
,所以是pick
状态的commit
是置灰不可点的,当我们将一个commit
进行其他action
操作之后,这个pick
就可以点了,相当于恢复操作。
第四个 就是 reword
了,改变message
内容
第五个就是squash
和fix
,向上合并
第六个是 drop
,丢弃的意思
我们改下
可以看下,当初在命令行里vim那个界面做的操作,我们这里只要点点就行,有可视化更加较少我们出错的可能性,在vim里,hash被不小心修改了啊,顺序太多忘了啊 都会造成一些困惑。
一切准备好了,我们点右下角 Start Rebasing
毫无疑问,我们必然也会遇到冲突问题,这里就很友好了,Android Studio 直接弹出了冲突面板,直接side by side
对比冲突文件,我们解决完,一直到rebase结束,一条龙服务一气呵成,中间无任务断层,也不需要什么continue
什么的
我们看到只有一个文件冲突,我们选中,有3个选择
- 强制使用我的版本
- 强制使用别人的版本
- 手动合并
我们手动合并解决冲突之后,弹出了写message的对话框
我举得这个例子其实有点复杂的,一般情况下我们不会出现冲突,熟悉的话整个rebase过程只要几秒钟就完事了。
下面我上个gif吧
这是一次性处理多个commit
,假如我们只需要更改一条的message
,或者fixu
p或drop
的话,直接右键该commit
,在圈出来的地方,直接就有修改的,很方便。
总结
这里不去讨论什么时候该不该用rebase
,不去讨论什么时候merge
什么时候rebase
这些老生常谈的问题,一切建立在假设你了解rebase
黄金原则的情况下。
本文演示的就是在会用rebase
的情况下,如果用AS自带的会更便捷一些。
另外,这确实是我工作中用的最频繁的一个功能了,我们要多commit,但是最终push之前一定要整理我们的提交日志,共同维护一个干净的历史提交记录。
另外,我一般还用rebase
来将我的个人本地分支同步上游分支,消除merge message
。
「欢迎在评论区讨论,掘金官方将在掘力星计划活动结束后,在评论区抽送100份掘金周边,抽奖详情见活动文章」。