使用git rebase 的前提是多人协作下的分支开发,如果是单人开发,那就没有必要使用它了,这是由git rebase 的作用所决定的,git rebase 有两大作用:一个是与主分支保持同步,一个是在合并分支之前清理commit 信息。先看第一个作用。
当多人协作进行开发的时候,通常都会有一个主分支,如master, 用于合并所有人的代码。而在本地开发的时候,每一个人都会开一个分支,进行分支开发。当我们新开一个分支的时候,就意味着,选择了主分支上的一个点作为开发的起点。如下图所示,
这时,本地开发的时候,我们每一个commit 都会提交到 new_feature 分支上, 同时,我们的同事也在开发功能, 他们开发完成后,都会把代码同步到master 分支上。我们从远程master 分支进行pull 操作,拉取到本地的master 分支上,随着时间的推移,就会形成如下状态,
现在怎么办?我们想使用master 分支上的新功能,你可能想到了merge, 但是new_feature 分支还没有开发完成,还没有到merge的时候,那就用git rebase 把。在 new_feature 分支上,使用git rebase master 命令就可以了。
使用git rebase 后,分支new_feature 以最新的master 作为了起点,这时就可以使用master 上的新功能了。rebase, re + base, 的意思,re 是再一次的意思, base 基的意思,再给它一个基础,就是改变以前的基础,改变分支以前的起点到新的起点上(change the starting point of your local branch)
但有时候,git rebase 会出现冲突,
不过还好,命令行会提示你哪几个文件有冲突。如果你使用的是git bash 命令窗口来执行git rebase 你会发现(1/n), 它也是在告诉你有n 个文件发生了冲突。更好的是它也告诉你怎么操作, 可以手动解决冲突,然后git add 有冲突的文件,最后执行git rebase --continue, 继续rebase, 当然 你可以跳过这个commit, 这个是怎么理解呢?
当我们执行git rebase的时候,它也相当于merge, 把master的内容合并到开发分支上,但它不是一步到位, 而是按照commit 一步一步来进行merge. 如果我们分支上有有5个commit 提交记录,它会先执行第一个commit, 就是上面的Applying: locale tips check, 也就是说,它会把master 分支和这个commit 下面改动文件进行合并,跳过这个commit,就表示,不用合并把master 和这个commit 进行合并,那就没有冲突了,就继续 rebase. 最后,你可以取消这次rebase ,git rebase --abort
现在手动解决冲突,有两种方式,可以找到冲突的文件,解决一个,然后git add 一个 ,然后再git rebase --continue, 比如上面PropertyInfo.jsx 有冲突,可以解决后,执行git add static-content/react/compoents/joinFrom/PropertyInfoWithLocal/PropertyInfo.jsx, 然后执行git rebase --continue, git 会给出第2个冲突的地方,还是解决冲突,git add , 和git rebase --continue, git 会依次给出所有冲突的文件,直到你把所有冲突解决掉。也可以把所有冲突文件都解决掉,然后git add server/api static-content/react/compoents/joinFrom/PropertyInfoWithLocal/PropertyInfo.jsx 等所有文件,然后git rebase --continue. 这里一定要注意,使用git add的时候,一定不要使用git commit, git commit 不是git rebase 解决冲突的步骤,如果使用了, git 会因为你使用步骤之外的命令来终止rebase 操作。 在解决冲突的时候, 有时发现搞错了,这时 git rebase --abort, 终止rebase, 回到 rebase 之前的状态,就像回滚。如果你使用vs code 进行开发,如果git rebase 有冲突,它会在左侧的菜单栏merge 图标上显示数字,表示有几个文件发生冲突,点开以后,就可以看到有冲突的文件,非常方便进行冲突修改。
但是有的时候,git rebase 会出问题,有一次,当执行git rebase的时候,提示冲突,打开vs code 一看,它把本地的文件删除了,而我又执行了git rebase --skip, rebase 成功了,但本地文件删除了,这怎么办,只能回到rebase 之前的状态。本地分支上的操作,可以使作git reset 进行回退,但是也要找到回退到的commit id. 这时使用git reflog 命令,如果使用的是git rebase, git reflog 打印出的日志就是显示rebase ......, 找到最后一个rebase 后面没有rebase 的id.
如上图所示,f883bbd, 就是 我们想要的id, 直接git reset --hard f883bdd 就可以了。再说一下删除文件的事,冲突会把文件删除,以前没有遇到过,有点慌,不知道怎么回事,其实,还是因为没有看git rebase 的命令窗口的信息。看看上上一个黑框: error, fail to open ' static-content/react/compoents/joinFrom/PropertyInfoWithLocal/PropertyInfo.jsx ' , permission denied. error: fial to merge. 本地文件打不开,无法merge,就把文件删除了。在使用vs code 开发的时候,最近会遇到这种问题,我关机重启了,就rebase 成功了。
git rebase 还有一个问题是,当把代码提交到远程分支上时,再在本地执行git rebase ,然后git push 就会报错。 有一次,在本地开了一个分支 samli/confirm_remove_english, 然后提交到远程,创建了一个PR, 由于当时是第二天approved, 导致分支需要update branch. 这时我就在本地git checkout master, git pull 然后,再git checkout samli/confirm_remove_english, git rebase master. 这时git push 该分支,报错了, 不是fast-forward
查了一个资料,git rebase 相当于创建了一个新分支。当初次git push 的时候,本地分支和远程分支如下所示
但是在要本地执行git rebase master的进时候,
本地的分支实际上是D' 和E' ,相当于创建了一个新的分支,而远程还是D和E, 所以当 D' 和E'merge 到远程的D和E,不是fast-forward, 因为D' 和E' 并没有D和E 的后面(图上显示),但是理论上是能合并的,这时git push的时候,要加一个参数,--force, 其实更好的是--force-with-lease, 来使要本地的分支强行覆盖远程分支。git push origin samli/confirm_remove_english --force-with-lease
现在说第二个作用,清理commit 日志。我们再以master 为基础,新建一个分支,就叫new_feature 分支好了,再在该分支上随便提交两次代码,我一次增加了index.js 再一次增加了style.css 文件,
现在把new_feature 分支合并到master 分支上,在合并之前,最好把分支上的提交日志合并成 一条,这样可以保持master 分支上的提交记录的整洁,使用的命令是git rebase --interactive 某一条commit id, --interactive 可以简写成-i, 所能通常会用 git rebase -i 某一条commit id,commit id 指的中哪一条commit 之前的提交记录要合并成一条。 这里的‘之前’是在命令行中显示的。我是从上到下看的,所以到07ab50在fc2617前面, fc2617在 ed4376的前面(ed43e6 就是 master的id,截图没有),如果想合并07ab50, fc2617这两个commit, commit id 就要选ed4376。get rebase -i ed4376,这时弹窗一个vim 编辑器,上面就是提交的分支记录,下面则是每一个命令的说明。
你可以看到,它的日志排列正好和git log 相反, 最后提交的在最下面。合并使用的是squash 命令, 把pick 07ab50c 前面的pick 换成squash, 就是把下面07ab50c 提交日志,合并到fc26711 上。
然后按esc 键退出 编辑操作,然后再输入:wq , 退出这个弹窗,这时又跳出一个弹窗。
这个弹窗主要是告诉你git rebase 做了什么事情,可以 :wq 退出。当然也可以修改提交记录,修改的内容,就变成了这次提交的记录。
可以看到rebase 成功,同时git log 也可以看到 两条记录合并到一条记录上了。
这时,可以愉快地合并到master 分支上了。
有时vim 编辑器不太好用,我把git 的默认编辑器改成了vs code. 再建一个开支体验一下vs code. 假设分支下面有两个提交add a menu 和add a button, 需要合并
可以看到d404ff 在4edae8前面,4edae8 在e25170 的前面,如果想合并d44ff,4edae8这两个commit, commitId 就要选e25170 。get rebase -i e25170
执行上述命令后,会弹出一个vs code窗口
要合并的两条commit 显示出来了。此时把第二行的pick 写成 squash, 双击选中pick, 按s 就提示squash, 单击squash就可以。
千万不要改第一行的pick, 无论有多个commit 要合并,都是从第二行向下 ,把pick 改成squash. 因为squash 是压缩的意思,压缩到什么地方,肯定有一个目的地,目的地就是第一行,把要合并的commit 都压缩到第一行上。把所有的pick改为squash 之后,按ctrl + s 保存,然后关闭文件。稍等一下,你会发现vs code 又自动打开了一个文件COMMIT_EDITMSG
对于git 来说,修改文件之后,要提交commit. COMMIT_EDITMSG 这个文件就是写提交message的, 可以把1-9行都删除掉,然后写这次合并内容,比如,侧边栏开发完成。
然后ctrl + s 保存,关闭. git 控制台没有报错,就合并成功了。