git rebase 的使用
git rebase 的使用
变基的风险:呃,奇妙的变基也并非完美无缺,要用它得遵守一条准则:如果提交存在于你的仓库之外,而别人可能基于这些提交进行开发,那么不要执行变基。
前言
merge、rebase 这两种整合方法的最终结果没有任何区别,但是 rebase (变基)使得提交历史更加整洁。 你在查看一个经过变基的分支的历史记录时会发现,尽管实际的开发工作是并行的, 但它们看上去就像是串行的一样,提交历史是一条直线没有分叉。
一般我们这样做的目的是为了确保在向远程分支推送时能保持提交历史的整洁——例如向某个其他人维护的项目贡献代码时。 在这种情况下,你首先在自己的分支里进行开发,当开发完成时你需要先将你的代码变基到 origin/master
上,然后再向主项目提交修改。 这样的话,该项目的维护者就不再需要进行整合工作,只需要快进合并便可。
请注意,无论是通过变基,还是通过三方合并,整合的最终结果所指向的快照始终是一样的,只不过提交历史不同罢了。 变基是将一系列提交按照原有次序依次应用到另一分支上,而合并是把最终结果合在一起。
构建一个场景使用一下 rebase
分支 test1 上创建新的分支 test2 ,在 test2 上做了 2 次提交。此时在 test1 上进行了 2 次提交(他人或自己),这就意味着 test1 和 test2 这两个分支各自"前进"了,它们之间"分叉"了。
想要 test2 有 test1 的新提交,可以使用 merge 进行合并,结果看起来就像一个新的"合并的提交"(merge commit)。但是,如果你想让 test2 分支历史看起来像没有经过任何合并一样,你也许可以用 git rebase:
这里 test1 可以理解为 master, test2 可以理解为自己的开发分支,如前言所说自己开发分支完成时你需要先将你的代码变基到
origin/master
上,然后再向主项目提交修改。 这样的话,该项目的维护者就不再需要进行整合工作,只需要快进合并便可。
操作
首先要在 test2 分支上 git checkout test2
执行 git rebase test1
这些命令会把你的 test2 分支里的每个提交(commit)取消掉,并且把它们临时保存为补丁(patch)(这些补丁放到 .git/rebase
目录中),然后把 test2 分支更新到 test1 的最新分支,最后把保存的这些补丁应用到 test2 分支上。
在rebase的过程中,也许会出现冲突(conflict). 在这种情况,Git会停止rebase并会让你去解决 冲突;
在解决完冲突后,用 git-add
命令去更新这些内容的索引(index), 然后,你无需执行 git-commit,只要执行: git rebase --continue
这样 git 会继续应用(apply)余下的补丁。
在任何时候,你可以用--abort
参数来终止rebase的行动,并且"mywork" 分支会回到rebase开始前的状态。
git rebase --abort
实战
我在 test2 分支 LoginController.java 文件提交了2个修改
第一次提交:在 sequence 下和 iUserService 下分别加了一行代码
第二次提交:在 signName 下加了一行代码
在 test1 分支 LoginController.java 文件提交了2个修改
第一次提交:为了制造冲突也在 iUserService 下添加一行代码
第二次提交:为了制造对照与 test2 分支 2 次提交和 test1 的一次提交区分,在 registerUser 方法下添加一行代码
直接从 git rebase test1
开始
在这里遇到冲突,如上操作所说 “用 git-add
命令去更新这些内容的索引(index), 然后,你无需执行 git-commit,只要执行: git rebase --continue
”
这里 git 给的提示也是
hint: Resolve all conflicts manually, mark them as resolved with
hint: "git add/rm <conflicted_files>", then run "git rebase --continue".
hint: You can instead skip this commit: run "git rebase --skip".
hint: To abort and get back to the state before "git rebase", run "git rebase --abort".
提示:手动解决所有冲突,将其标记为已解决
提示:“git add/rm <conflicted_files>”,然后运行“git rebase——continue”。
提示:你可以跳过这个提交:运行“git rebase——skip”。
提示:要中止并返回到“git rebase”之前的状态,请运行“git rebase——abort”。
这里提到 abort、continue、skip
$git rebase --abort
:执行之后,本地内容会回到提交之间的状态,也就是回到以前提交但没有pull是的状态,简单来说就是撤销rebase。git rebase --skip
:引起冲突的commits会被丢弃,对于本文应用的例子来说我对 LoginController.java 文件的commit无效,我自己修改的部分全部无效,因此,在使用skip时请慎重。git rebase --continue
:本地如果产生冲突,手动解决冲突之后,用"git add"命令去更新这些内容的索引(index),然后执行这个命令(git rebase --continue
)继续。一步一步地解决冲突的提交。
skip使用
虽然使用 skip 时需要慎重,但我认为需要使用一下才能知道到底什么丢弃了。
现在我合并位置 LoginController.java 文件提示:可以看到57 行是我 test1 分支第 1 次 commit 的结果,66 行是我 test1 分支第 2 次 commit 的结果。 54、59 行是 test2 分支 第一次 commit 的结果。没有 test2 分支第二次的 commit 的文本,说明需要解决这次提交中的冲突才能继续合并(也说明本次 continue、skip只针对这一次的 commit)。
下面执行:
git rebase --skip
观察到:只有冲突的那个 commit 被放弃了,放弃是那次 commit 的全部代码而不是冲突部分的代码。后续没有冲突的代码依然会合并进来 。
continue
这里保留所有改动
执行 git add .
执行 git rebase --continue
回车发现会进入如下页面:这是个提交 commit 文案的编辑,可以输入改动一下信息,直接在键盘输入 :wq
即可,#
后面的文案将被忽略
然后控制台输出:
PS C:\Users\DELL\project2\ptd-mapview> git rebase --continue
[detached HEAD ac0992e] feat(test2): 1
1 file changed, 2 insertions(+), 1 deletion(-)
Successfully rebased and updated refs/heads/test2.
PS C:\Users\DELL\project2\ptd-mapview>
abort
执行 git rebase --abort
本次 rebase 将被取消
参考链接: