Android Studio 实战 git-rebase 交互式变基高阶技巧

3,330 阅读9分钟

本文已参与「掘力星计划」,赢取创作大礼包,挑战创作激励金。

系列

由于篇幅问题,一篇文章写完太长,因此分了几篇来写

Android Studio Git 小工具使用介绍简介

Android Studio Git 进阶技巧 交互式变基实战教程

Android Studio Git 周边插件一览 - 掘金

介绍

交互式变基 用命令行实现的话 git rebase -i <base-commit>

-i 是 --interactive 的简写,中文翻译叫 “互动”,与 rebase 结合的话我们一般就称之为 交互式变基

命令行操作

前提

假设我们有3个问题,一个是乱码问题,一个是图片展示问题,一个是跳转问题。

其中乱码问题修复了3次才修复好,然后图片的修复git commit 日志打了错别字,然后跳转问题我还在修改中,代码改了一半,在local changes 中有修改的文件还没有提交。

image.png

所以我们产生了上图所示的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 的交互式页面

image.png

可以看到上面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,假如一切顺利的话,这时候我们会看到一个新的界面,让我们填写下合并后的commitsmessagegit-rebase到这里就结束了

但是很不幸,我们遇到了冲突,这时候我们需要去手动解决冲突,然后执行 git rebase --continue

这时候就来到了新界面,首先是先改写我们合并commit之后的message

image.png

我们把注释上面的都删掉 改成 <fix: 修复乱码问题--rebase过的 test>

image.png

按下:wq之后

接下来会弹出新的界面继续处理我们的 <r e5fdfe9 fix: 修复图片展示维他奶> 这行,也就是修改message

image.png

改完之后,依然是:wq一下

这个时候我们的git-rebase就完成了,我们看下log,是不是我们想要的效果

image.png

完美,perfect 🤞🤞🤞

跌跌撞撞总算是完成了需求,但是我们可以看到,有一定的上手成本,有不少细节,不仅rebase前遇到了困难,rebaseh中还遇到了冲突,这对新手来简直是个是个天灾。

git reflog 时光机器

接下来我们就展示下利用Android Studio 内置的小工具如何轻松实现。

首先,我们先恢复一下刚刚的操作,利用git reflog 轻轻松松拥有时光机器。

image.png

reflog记录了我们在git中的每一步的操作,我们从这里找到要回去的时间点(git commit hash),比如这里就是f48877b

我们敲下命令 git reset --hard f48877b

此时再看下git log

image.png

嗯 又顺利穿越到了杂乱不堪的历史提交记录现场

接下来我们使用Android Studio 来秀一下

Android Studio 内置小工具的交互式变基流程

image.png

要实现这个需求贼简单,打开git log,找到要处理的第一个commit。右键,选择Interactively Rebase from Here... 翻译下就是 从这里开始交互式变基

点击之后就会弹出交互式面板,让我们操作我们选择的commit

image.png

可以看到这个界面将git-rebase的几个参数都做成了按钮,我们点点就完成了复杂的操作,尤其是在还有冲突的时候解决起来贼方便 。

最新的提交在最下面,我们选中某一个commit之后

操作栏就几个按钮

image.png

一二两个是改变顺序

第三个是pick,由于默认就是pick,所以是pick状态的commit是置灰不可点的,当我们将一个commit进行其他action操作之后,这个pick就可以点了,相当于恢复操作。

第四个 就是 reword了,改变message内容

第五个就是squashfix,向上合并

第六个是 drop,丢弃的意思

我们改下

image.png

可以看下,当初在命令行里vim那个界面做的操作,我们这里只要点点就行,有可视化更加较少我们出错的可能性,在vim里,hash被不小心修改了啊,顺序太多忘了啊 都会造成一些困惑。

一切准备好了,我们点右下角 Start Rebasing

毫无疑问,我们必然也会遇到冲突问题,这里就很友好了,Android Studio 直接弹出了冲突面板,直接side by side对比冲突文件,我们解决完,一直到rebase结束,一条龙服务一气呵成,中间无任务断层,也不需要什么continue什么的

image.png

我们看到只有一个文件冲突,我们选中,有3个选择

  • 强制使用我的版本
  • 强制使用别人的版本
  • 手动合并

我们手动合并解决冲突之后,弹出了写message的对话框

image.png

我举得这个例子其实有点复杂的,一般情况下我们不会出现冲突,熟悉的话整个rebase过程只要几秒钟就完事了。

下面我上个gif吧

Animation.gif

这是一次性处理多个commit,假如我们只需要更改一条的message,或者fixup或drop的话,直接右键该commit,在圈出来的地方,直接就有修改的,很方便。

image.png

总结

这里不去讨论什么时候该不该用rebase,不去讨论什么时候merge什么时候rebase这些老生常谈的问题,一切建立在假设你了解rebase黄金原则的情况下。

本文演示的就是在会用rebase的情况下,如果用AS自带的会更便捷一些。

另外,这确实是我工作中用的最频繁的一个功能了,我们要多commit,但是最终push之前一定要整理我们的提交日志,共同维护一个干净的历史提交记录。

另外,我一般还用rebase来将我的个人本地分支同步上游分支,消除merge message

「欢迎在评论区讨论,掘金官方将在掘力星计划活动结束后,在评论区抽送100份掘金周边,抽奖详情见活动文章」。