git rebase命令: --继续, --中止, 取消, --交互式, 压扁
本文的主题是 git rebase
命令。
git rebase
命令与git merge
命令一起,是合并两个或多个分支的主要方式。
让我们介绍一下使用它的基本知识,以及一些常用的选项。
1. 正确使用git rebase
正如我在之前的 git merge
一文中提到的,使用 git rebase
命令合并两个或多个分支,可以得到一个直接的提交历史。
下面是rebase工作原理的图示。
例如,假设你想从feature-a
分支到main
分支做一个rebase。
Git 会将两个分支的共同祖先在feature-a
分支的所有提交合并到main
分支的最后一次提交。
同时,它将删除旧的feature-a
分支上任何属于两个分支的提交。
结果是一个新的 feature-a
分支,从 main
分支的最后一个分支开始。
这就改变了提交历史,创建了一个而不是两个历史。
我们用来做这件事的命令是
与git merge
命令不同,我们进入source分支
,对target分支
运行该命令。
在上面的例子中,它看起来像这样
$ git switch feature-a
$ git rebase main
下面是它的样子
rebase合并了 develop
和 main
两个分支,它们有一个共同的祖先,即 6519
提交。
结果,一个新的develop
分支被创建,从main
分支开始,而原来的develop
分支消失了。
如果在重构过程中发生冲突,会显示以下信息
$ git rebase main
结果:
Auto-merging 1.md
CONFLICT (content): Merge conflict in 1.md
error: could not apply 7fc042b... 1.md edited in develop
hint: Resolve all conflicts manually, mark them as resolved with
hint: "git add/rm ", 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".
Could not apply 7fc042b... 1.md edited in develop
如果你想在这里完成重建,用git add命令解决冲突并将文件分阶段。 然后使用选项来继续重建过程。
继续重建过程的选项是
$ git rebase --continue
如果你想查看冲突并恢复重建,请键入以下命令,恢复到重建前的状态。
$ git rebase --abort
我们将在后面的章节中更详细地研究这些选项中的每一个。
2. 当冲突发生时用--abort中止重构
当Git在重写过程中,如果发现有冲突,它会暂停。
然后它将给我们一个选项,要么解决冲突,继续重写,要么中止。
在这一点上,如果我们想放弃,我们有--abort
选项。
如果你使用这个选项,任务树将恢复到运行重建前的状态。 如果你遇到冲突或问题,使得继续重建不合理,这可能很有用。
该命令的使用方法如下
$ git rebase --abort
让我们看看实际的执行结果,看看在哪些情况下可以使用它。
看一下日志信息,我们可以看到1.md
文件被两个分支修改了。
在这种情况下,执行rebase命令时发现了一个冲突,导致rebase暂停了一段时间。
当我们用--abort
选项中止rebase时,它又回到了原状。
3. 当冲突发生时,用--continue选项完成重建
接下来,我们将看到如何使用--continue
选项来解决冲突并完成重建过程。
解决冲突的过程与git merge
过程相同。
你需要在编辑器中打开冲突的文件并进行选择。
参见git merge post以获得更多信息。
一旦你修复了这些文件,就用git add command把它们归档。 然后,我们不再像合并那样提交新的文件,而是使用 rebase 命令选项继续这个重塑过程。
像这样使用它
$ git rebase --continue
让我们看看在一个真实的崩溃案例上运行它的结果。
看一下日志信息,我们发现两个分支都修改了1.md
文件。
当我们运行 rebase 时,冲突被确认,Git 暂停了 rebase 过程。
我用编辑器清理了冲突的行,把它们归档,并使用--continue
选项。
结果,我们可以看到rebase成功完成。
4. 用git reset撤消重定向
如果你想撤销一个已经完成的重构,你需要使用git reflog
和git reset
命令。
正如我们在git commit post中看到的,正常的提交也可以通过git log
命令输出的提交ID来撤销或删除。
然而,如果你运行了git rebase
,你已经对现有的提交进行了修改,所以你不能恢复到当前提交历史中的信息。
git reflog
命令显示了你对所有存储为ref
的值的操作日志,包括分支、标签等。
你可以把它看作是一个更详细的git log
,用于调试。
多亏了这个,你可以回到之前的提交,即使你已经运行了git rebase
命令。
下面是git reflog
命令输出的与我们在第3节运行的rebase有关的部分日志。
$ git reflog
结果:
69fd9e0 (HEAD -> develop) HEAD@{0}: rebase (finish): returning to refs/heads/develop
69fd9e0 (HEAD -> develop) HEAD@{1}: rebase (continue): 1.md edited in develop
5e110f7 (main) HEAD@{2}: rebase (start): checkout main
7fc042b HEAD@{3}: rebase (abort): returning to refs/heads/develop
从日志中,我们可以看到,我们刚刚运行的rebase的起点是在HEAD@{2}
,我们需要回到HEAD@{3}
才能回到之前。
现在你知道要回到哪里了,运行git reset
命令。
使用--mixed
选项在工作树上保留消失的修改,或者使用--hard
选项在返回时删除所有内容。
下面是使用--mixed
选项的结果
你也可以看到,被删除的提交的修改在rebasing之后仍然保留在工作树中。
5. 用--interactive选项随意修改提交历史(壮举:squash)
git rebase
命令的--interactive
或-i
选项是一个运行交互式rebase会话的选项。
在这个会话中,我们可以修改当前分支的提交,压制它们,重新排序,等等。
结果是,我们改变了当前分支的提交历史。
值得注意的是,与我们之前看到的git rebase
命令不同,我们只是在操作一个分支的提交。
现在让我们看看行动的结果。
要运行一个会话,你需要输入你要操作的提交的最后一个提交ID,像这样
$ git rebase -i [commit-hash]
当前的提交历史看起来是这样的
在这里,上面的命令会打开一个像下面这样的窗口。
这里,所有以#
开头的注释都是指令。
最上面没有注释的部分是本次rebase会话的提交列表,按年龄排序。
pick 7fc042b 1.md edited in develop
pick 6d80db8 1.md edited in develop 2
在我们的例子中,它是这一部分。你可以用任何其他命令替换pick
部分,保存它,然后用它做你想做的事。
5.1. 关键命令和如何使用它们
pick
: 保持当前状态reword
: 只改变提交信息edit
: 改变细节,如作者、日期等。squash
:创建一个新的提交信息,与之前的提交合并fixup
: 使用上次的提交信息,同时与上一次的提交合并drop
: 删除一个提交
例如,要修改7fd0
的提交信息,并将6d80
的提交与前一个提交合并,请做如下修改,保存,并关闭编辑器。
reword 7fc042b 1.md edited in develop
fixup 6d80db8 1.md edited in develop 2
当你执行一个需要新信息的操作时,比如 reword
、edit
或 squash
,Git会根据需要打开尽可能多的新编辑器来询问必要的信息。
上面的屏幕显示了在reword命令之后,编辑器窗口提示了新的提交信息。
下面的注释显示,我们目前正在进行reword(Last command done
)、
而我们接下来将运行fixup操作(Next command to do
)。
如果你看一下结果,你可以看到我们上面写的命令已经完成了修改提交信息和合并前次提交的任务。
正如我们在前面的命令 git commit --amend 中所看到的,即使是简单的消息修改,Git 也会采用创建一个新的提交并替换它的方法。因此,在协作时,你应该谨慎使用rebase中的所有命令。
6. 结束语
git rebase
命令具有改变提交历史的能力,这是Git的不成文规定之一。
尽管这个命令很强大,它也足以在协作过程中造成意想不到的混乱。
出于这个原因,git rebase
应该总是公开使用,按照你团队的惯例。
