网站搜索

Git rebase:你需要知道的一切


总结:Git rebase 命令将一个分支移动到另一个分支头部的新位置。与 Git 合并命令不同,rebase 涉及重写项目历史记录。它是一个很棒的工具,但不要 rebase 其他开发人员基于其工作的提交。

Git rebase 命令将两个源代码分支合并为一个。 Git merge 命令也可以做到这一点。我们解释了 rebase 的作用、它的使用方式以及何时使用 merge

Git 爆炸式增长

由于对其他版本控制系统及其缓慢的更新和提交感到沮丧,以 Linux 内核闻名的 Linus Torvalds 在 2005 年抽出一个月的时间来编写自己的版本。他将其命名为 Git。

GitHub、GitLab 和 BitBucket 等网站共生地促进了 Git 并从中受益。今天 Git 在全球范围内使用,在 2022 年的一项调查中,71,000 名受访者中有 98% 使用 Git 作为版本控制系统。

Git 的主要设计决策之一是速度。特别是,与分支机构的合作必须尽可能快。分支是版本控制系统的基本组成部分。项目存储库将有一个主分支或主分支。这是项目代码库所在的位置。诸如新功能之类的开发在隔离的侧分支中进行。这可以防止在分支中完成的工作弄乱主分支,并且允许在代码库的不同部分同时进行开发。

随着侧分支中的开发完成,通过将开发分支合并到主分支,将更改转移到主分支。在其他版本控制系统中,使用分支是困难的,而且计算量大。在 Git 中使用分支非常快,而且非常轻量级。在其他系统中曾经是乏味且经常避免的练习,在 Git 中变得微不足道。

Git rebase 命令是另一种将更改从一个分支转移到另一个分支的方法。 mergerebase 命令具有相似的目标,但它们以不同的方式实现目的并产生略有不同的结果。

什么是 Git 合并?

那么 Git merge 命令有什么用呢?假设您创建了一个名为 dev-branch 的分支来处理一项新功能。

您进行了一些提交,并测试了您的新功能。一切正常。现在您想将新功能发送到 master 分支。您必须在 master 分支中才能将另一个分支合并到它。

我们可以通过在合并之前明确检查它来确保我们在 master 分支中。

git checkout master

我们现在可以告诉 Git 将 dev-branch 合并到当前分支,即 master 分支。

git merge dev-branch

我们的 merge 已经为我们完成了。如果你签出 master 分支并编译它,它将包含新开发的功能。 Git实际执行的是三向合并。它比较 masterdev-branch 分支中的最新提交,以及紧接在 dev 之前的 master 分支中的提交-branch 已创建。然后它在 master 分支上执行提交。

合并被认为是非破坏性的,因为它们不会删除任何东西,也不会更改任何 Git 历史记录。 dev-branch 仍然存在,之前的提交都没有改变。创建一个新的提交来捕获三向合并的结果。

合并后,我们的 Git 存储库看起来像一个时间线,其中有一条备用线分支,然后返回到主时间线。

dev-branch 分支已合并到 master 分支中。

如果您在一个项目中有很多分支,项目的历史就会变得混乱。如果一个项目有很多贡献者,通常就是这种情况。因为开发工作分成许多不同的路径,所以开发历史是非线性的。如果分支有自己的分支,理清提交历史变得更加困难。

请注意,如果您在 master 分支中有未提交的更改,则需要先对这些更改执行一些操作,然后才能将任何内容合并到其中。您可以创建一个新分支并在那里提交更改,然后进行合并。然后,您需要将临时分支合并回主分支。

这行得通,但 Git 有一个命令可以实现同样的事情,而无需创建新的分支。 stash 命令为您存储未提交的更改,并允许您使用 stash pop 回调它们。

你会像这样使用它们:

stash

git merge dev-branch

stash pop

最终结果是一个合并的分支,其中恢复了未保存的更改。

什么是 Git 变基?

Git rebase 命令以完全不同的方式实现了它的目标。它从你要变基的分支中获取所有提交,并将它们重播到你要变基的分支的末尾。

以我们之前的例子为例,在我们执行任何操作之前,我们的 Git 存储库看起来像这样。我们有一个名为 dev-branch 的分支,我们想将这些更改移动到 master 分支。

rebase 之后,它看起来像是一个单一的、完全线性的变化时间线。

dev-branch 已被删除,dev-branch 中的提交已添加到 master 分支。最终结果就像 dev-branch 中的提交实际上首先直接提交到 master 分支一样。提交不只是附加到 master 分支,它们被“重放”并重新添加。

这就是为什么 rebase 命令被认为是破坏性的。 rebased 分支不再作为单独的分支存在,并且项目的 Git 历史已被重写。您无法在稍后确定哪些提交最初是对 dev-branch 进行的。

然而,它确实给您留下了一个简化的、线性的历史。与具有数十个甚至数百个分支和合并的存储库相比,阅读 Git 日志或使用图形化 git GUI 查看存储库的图形,重新设置的存储库很容易理解。

如何变基到另一个分支

让我们尝试一个 git rebase 示例。我们有一个项目有一个名为 new-feature 的分支。我们会像这样变基那个分支到master分支。

首先,我们检查 master 分支没有未完成的更改。

git status

我们检查 new-feature 分支。

git checkout new-feature

我们告诉 Git rebase 当前分支到 master 分支。

git rebase master

我们可以看到我们仍然有两个分支。

git branch

我们切换回 master 分支

git checkout master

我们将新功能分支合并到当前分支,在我们的例子中是 master 分支。

git merge new-feature

有趣的是,最终合并后我们仍然有两个分支。

不同的是,现在 new-feature 分支的头部和 master 分支的头部被设置为指向同一个提交,而 Git 历史不会显示除了分支标签之外,曾经有一个单独的 new-feature 分支。

Git Rebase 与 Merge:您应该使用哪一个?

这不是 rebasemerge 的情况。它们都是功能强大的命令,您可能会同时使用它们。也就是说,有些用例中 rebase 并没有真正发挥作用。使用 merge 的错误导致的取消选择错误令人不快,但是使用 rebase 导致的取消选择错误是地狱般的。

如果您是唯一使用存储库的开发人员,那么您使用 rebase 做一些灾难性的事情的可能性就会降低。例如,您仍然可以在错误的方向上rebase,并将您的 master 分支 rebase 到您的 new-feature 分支。要恢复你的 master 分支,你需要再次 rebase,这次是从你的 new-feature 分支到你的 master 分支。这将恢复您的 master 分支,尽管历史看起来很奇怪。

不要在其他人可能工作的共享分支上使用 rebase。当您将重新设置基础的代码推送到远程存储库时,您对存储库的更改会给很多人带来问题。

如果您的项目有多个贡献者,安全的做法是只在您的本地 存储库上使用 rebase,而不是在公共分支上。同样,如果拉取请求构成代码审查的一部分,请不要使用 rebase。或者至少,不要在创建拉取请求后使用 rebase。其他开发人员可能正在查看您的提交,这意味着这些更改位于公共分支上,即使它们不在 master 分支上。

危险在于您要对已经推送到远程存储库的提交进行 rebase,而其他开发人员可能已经基于这些提交进行了工作。您的本地 rebase 将使那些现有的提交消失。如果您将这些更改推送到存储库,您将不会受到欢迎。

其他贡献者将不得不通过混乱的 merge 将他们的工作推回存储库。如果您随后将他们的更改拉回本地存储库,您将面临取消挑选一堆重复更改的麻烦。

变基,还是不变基?

Rebase 在您的项目中可能是非法的。可能存在当地的文化异议。一些项目或组织将 rebase 视为异端和亵渎行为的一种形式。有些人认为 Git 历史应该是不可侵犯的、永久的记录所发生的事情。因此,rebase 可能不在考虑之列。

但是,在本地使用时,在私有分支上,rebase 是一个有用的工具。

你重新定位后推送,并将其限制在你是唯一开发人员的分支。或者至少,所有开发都已停止,并且没有其他人基于您分支机构的提交开展任何其他工作。

这样做,您将避免任何问题。