Git 重置 vs 恢复 vs Rebase
已发表: 2021-05-20在本文中,您将了解在 Git 中处理提交的不同方法。
作为一名开发人员,您可能会多次遇到这种情况,在这种情况下,您想要回滚到之前的提交之一,但不知道该怎么做。 即使你知道像 reset、revert、rebase 这样的 Git 命令,你也不知道它们之间的区别。 因此,让我们开始并了解 git reset、revert 和 rebase 是什么。
Git重置
Git reset 是一个复杂的命令,用于撤消更改。
您可以将 git reset 视为回滚功能。 使用 git reset,您可以在各种提交之间跳转。 运行 git reset 命令有三种模式:–soft、–mixed 和–hard。 默认情况下,git reset 命令使用混合模式。 在 git reset 工作流中,git 的三个内部管理机制进入了画面: HEAD 、暂存区(索引)和工作目录。

工作目录是您当前工作的地方,它是您的文件所在的地方。 使用 git status 命令,您可以查看工作目录中存在的所有文件/文件夹。
暂存区(索引)是 git 跟踪和保存文件中所有更改的地方。 保存的更改反映在 .git 目录中。 您可以使用 git add “filename” 将文件添加到暂存区。 和以前一样,当您运行 git status 时,您将看到暂存区中存在哪些文件。
Git 中的当前分支称为 HEAD。 它指向在当前结帐分支中发生的最后一次提交。 它被视为任何引用的指针。 一旦您结帐到另一个分支,HEAD 也会移动到新分支。
让我解释一下 git reset 在硬模式、软模式和混合模式下是如何工作的。 硬模式用于转到指向的提交,工作目录填充有该提交的文件,并且暂存区被重置。 在软重置中,仅将指针更改为指定的提交。 在重置之前,所有提交的文件都保留在工作目录和暂存区中。 在混合模式(默认)下,指针和暂存区都被重置。
Git 硬重置
git hard reset 的目的是将 HEAD 移动到指定的提交。 它将删除在指定提交之后发生的所有提交。 此命令将更改提交历史记录并指向指定的提交。
在这个例子中,我将添加三个新文件,提交它们,然后执行硬重置。
正如您从下面的命令中看到的,现在没有什么可以提交的。
$ git status On branch master Your branch is ahead of 'origin/master' by 2 commits. (use "git push" to publish your local commits) nothing to commit, working tree clean现在,我将创建 3 个文件并向其中添加一些内容。
$ vi file1.txt $ vi file2.txt $ vi file3.txt将这些文件添加到现有存储库中。
$ git add file*当您重新运行 status 命令时,它将反映我刚刚创建的新文件。
$ git status On branch master Your branch is ahead of 'origin/master' by 2 commits. (use "git push" to publish your local commits) Changes to be committed: (use "git restore --staged <file>..." to unstage) new file: file1.txt new file: file2.txt new file: file3.txt在提交之前,让我告诉你,我目前在 Git 中有 3 次提交的日志。
$ git log --oneline 0db602e (HEAD -> master) one more commit 59c86c9 new commit e2f44fc (origin/master, origin/HEAD) test现在,我将提交到存储库。
$ git commit -m 'added 3 files' [master d69950b] added 3 files 3 files changed, 3 insertions(+) create mode 100644 file1.txt create mode 100644 file2.txt create mode 100644 file3.txt如果我执行 ls-files,您将看到已添加新文件。
$ git ls-files demo dummyfile newfile file1.txt file2.txt file3.txt当我在 git 中运行 log 命令时,我有 4 个提交,并且 HEAD 指向最新的提交。
$ git log --oneline d69950b (HEAD -> master) added 3 files 0db602e one more commit 59c86c9 new commit e2f44fc (origin/master, origin/HEAD) test如果我手动删除 file1.txt 并执行 git status,它将显示更改未暂存以进行提交的消息。
$ git status On branch master Your branch is ahead of 'origin/master' by 3 commits. (use "git push" to publish your local commits) Changes not staged for commit: (use "git add/rm <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory) deleted: file1.txt no changes added to commit (use "git add" and/or "git commit -a")现在,我将运行硬重置命令。
$ git reset --hard HEAD is now at d69950b added 3 files如果我重新检查状态,我会发现没有任何东西可以提交,我删除的文件又回到了存储库中。 发生回滚是因为删除文件后,我没有提交,所以硬重置后,它又回到了以前的状态。
$ git status On branch master Your branch is ahead of 'origin/master' by 3 commits. (use "git push" to publish your local commits) nothing to commit, working tree clean如果我检查 git 的日志,这就是它的外观。
$ git log commit d69950b7ea406a97499e07f9b28082db9db0b387 (HEAD -> master) Author: mrgeek <[email protected]> Date: Mon May 17 19:53:31 2020 +0530 added 3 files commit 0db602e085a4d59cfa9393abac41ff5fd7afcb14 Author: mrgeek <[email protected]> Date: Mon May 17 01:04:13 2020 +0530 one more commit commit 59c86c96a82589bad5ecba7668ad38aa684ab323 Author: mrgeek <[email protected]> Date: Mon May 17 00:54:53 2020 +0530 new commit commit e2f44fca2f8afad8e4d73df6b72111f2f2fd71ad (origin/master, origin/HEAD) Author: mrgeek <[email protected]> Date: Mon May 17 00:16:33 2020 +0530 test硬重置的目的是指向指定的提交并更新工作目录和暂存区。 让我再给你看一个例子。 目前,我提交的可视化如下所示:

在这里,我将使用 HEAD^ 运行命令,这意味着我想重置为上一次提交(返回一次提交)。
$ git reset --hard HEAD^ HEAD is now at 0db602e one more commit您可以看到头指针现在已从 d69950b 更改为 0db602e。
$ git log --oneline 0db602e (HEAD -> master) one more commit 59c86c9 new commit e2f44fc (origin/master, origin/HEAD) test 
如果查看日志,d69950b 的提交已经消失,头部现在指向 0db602e SHA。
$ git log commit 0db602e085a4d59cfa9393abac41ff5fd7afcb14 (HEAD -> master) Author: mrgeek <[email protected]> Date: Mon May 17 01:04:13 2020 +0530 one more commit commit 59c86c96a82589bad5ecba7668ad38aa684ab323 Author: mrgeek <[email protected]> Date: Mon May 17 00:54:53 2020 +0530 new commit commit e2f44fca2f8afad8e4d73df6b72111f2f2fd71ad (origin/master, origin/HEAD) Author: mrgeek <[email protected]> Date: Mon May 17 00:16:33 2020 +0530 Test如果您运行 ls-files,您可以看到 file1.txt、file2.txt 和 files3.txt 不再在存储库中,因为该提交及其文件在硬重置后已被删除。
$ git ls-files demo dummyfile newfileGit 软重置
同样,现在我将向您展示一个软复位的示例。 考虑一下,我已经如上所述再次添加了 3 个文件并提交了它们。 git 日志将显示如下。 你可以看到“软重置”是我最新的提交,HEAD 也指向了这一点。
$ git log --oneline aa40085 (HEAD -> master) soft reset 0db602e one more commit 59c86c9 new commit e2f44fc (origin/master, origin/HEAD) test可以使用以下命令查看日志中提交的详细信息。
$ git log commit aa400858aab3927e79116941c715749780a59fc9 (HEAD -> master) Author: mrgeek <[email protected]> Date: Mon May 17 21:01:36 2020 +0530 soft reset commit 0db602e085a4d59cfa9393abac41ff5fd7afcb14 Author: mrgeek <[email protected]> Date: Mon May 17 01:04:13 2020 +0530 one more commit commit 59c86c96a82589bad5ecba7668ad38aa684ab323 Author: mrgeek <[email protected]> Date: Mon May 17 00:54:53 2020 +0530 new commit commit e2f44fca2f8afad8e4d73df6b72111f2f2fd71ad (origin/master, origin/HEAD) Author: mrgeek <[email protected]> Date: Mon May 17 00:16:33 2020 +0530 test现在使用软重置,我想切换到 SHA 0db602e085a4d59cfa9393abac41ff5fd7afcb14 的旧提交之一

为此,我将运行以下命令。 您需要传递 SHA 的 6 个以上的起始字符,不需要完整的 SHA。
$ git reset --soft 0db602e085a4现在,当我运行 git log 时,我可以看到 HEAD 已重置为我指定的提交。
$ git log commit 0db602e085a4d59cfa9393abac41ff5fd7afcb14 (HEAD -> master) Author: mrgeek <[email protected]> Date: Mon May 17 01:04:13 2020 +0530 one more commit commit 59c86c96a82589bad5ecba7668ad38aa684ab323 Author: mrgeek <[email protected]> Date: Mon May 17 00:54:53 2020 +0530 new commit commit e2f44fca2f8afad8e4d73df6b72111f2f2fd71ad (origin/master, origin/HEAD) Author: mrgeek <[email protected]> Date: Mon May 17 00:16:33 2020 +0530 test但这里的区别在于,我添加了 3 个文件的提交 (aa400858aab3927e79116941c715749780a59fc9) 的文件仍在我的工作目录中。 他们没有被删除。 这就是为什么您应该使用软重置而不是硬重置的原因。 在软模式下没有丢失文件的风险。
$ git ls-files demo dummyfile file1.txt file2.txt file3.txt newfileGit 还原
在 Git 中,revert 命令用于执行还原操作,即还原某些更改。 它类似于 reset 命令,但唯一的区别是您执行新的提交以返回到特定的提交。 简而言之,可以公平地说 git revert 命令是一个提交。
Git revert 命令在执行还原操作时不会删除任何数据。
假设我正在添加 3 个文件并为恢复示例执行 git commit 操作。
$ git commit -m 'add 3 files again' [master 812335d] add 3 files again 3 files changed, 3 insertions(+) create mode 100644 file1.txt create mode 100644 file2.txt create mode 100644 file3.txt日志将显示新的提交。
$ git log --oneline 812335d (HEAD -> master) add 3 files again 0db602e one more commit 59c86c9 new commit e2f44fc (origin/master, origin/HEAD) test现在我想恢复到我过去的提交之一,比方说——“59c86c9 新提交”。 我会运行下面的命令。
$ git revert 59c86c9这将打开一个文件,您将找到您尝试恢复的提交的详细信息,您可以在此处为新提交命名,然后保存并关闭该文件。
Revert "new commit" This reverts commit 59c86c96a82589bad5ecba7668ad38aa684ab323. # Please enter the commit message for your changes. Lines starting # with '#' will be ignored, and an empty message aborts the commit. # # On branch master # Your branch is ahead of 'origin/master' by 4 commits. # (use "git push" to publish your local commits) # # Changes to be committed: # modified: dummyfile保存并关闭文件后,这就是您将获得的输出。
$ git revert 59c86c9 [master af72b7a] Revert "new commit" 1 file changed, 1 insertion(+), 1 deletion(-)现在进行必要的更改,与重置不同,revert 执行了一个新的提交。 如果再次查看日志,会发现由于进行了还原操作,会出现新的提交。
$ git log --oneline af72b7a (HEAD -> master) Revert "new commit" 812335d add 3 files again 0db602e one more commit 59c86c9 new commit e2f44fc (origin/master, origin/HEAD) test 
Git 日志将包含所有提交的历史记录。 如果您想从历史记录中删除提交,那么 revert 不是一个好的选择,但是如果您想在历史记录中维护提交更改,那么 revert 是合适的命令而不是重置。
Git变基
在 Git 中,rebase 是一种将一个分支的提交移动或组合到另一个分支上的方式。 作为开发人员,我不会在真实场景中在 master 分支上创建我的功能。 我会在我自己的分支(一个“功能分支”)上工作,当我在我的功能分支中有一些提交并添加了该功能时,我想将它移动到主分支。
Rebase 有时会让人有点难以理解,因为它与合并非常相似。 合并和变基的目标是从我的功能分支中获取提交并将它们放在主分支或任何其他分支上。 考虑一下,我有一个看起来像这样的图表:

假设您与其他开发人员在一个团队中工作。 在这种情况下,您可以想象这会变得非常复杂,因为您有一群其他开发人员在不同的功能分支上工作,并且他们一直在合并多个更改。 追踪变得混乱。
所以,这就是 rebase 将提供帮助的地方。 这一次,我将做一个 rebase,而不是执行 git merge,我想将我的两个功能分支提交并将它们移动到 master 分支。 变基将从功能分支中获取我的所有提交,并将它们移动到主分支提交的顶部。 因此,在幕后,git 正在复制主分支上的功能分支提交。

这种方法将为您提供一个干净的直线图,其中包含连续的所有提交。

它可以轻松跟踪提交的内容。 你可以想象如果你在一个有很多开发人员的团队中,所有的提交仍然是连续的。 因此,即使您有很多人同时在同一个项目上工作,也很容易遵循。
让我向你展示这一点。
这就是我的主分支目前的样子。 它有 4 次提交。
$ git log --oneline 812335d (HEAD -> master) add 3 files again 0db602e one more commit 59c86c9 new commit e2f44fc (origin/master, origin/HEAD) test我将运行以下命令来创建并切换到一个名为 feature 的新分支,该分支将从第二次提交创建,即 59c86c9
(master) $ git checkout -b feature 59c86c9 Switched to a new branch 'feature'如果您检查功能分支中的日志,它只有 2 个来自 master(主线)的提交。
(feature) $ git log --oneline 59c86c9 (HEAD -> feature) new commit e2f44fc (origin/master, origin/HEAD) test我将创建功能 1 并将其提交到功能分支。
(feature) $ vi feature1.txt (feature) $ git add . The file will have its original line endings in your working directory (feature) $ git commit -m 'feature 1' [feature c639e1b] feature 1 1 file changed, 1 insertion(+) create mode 100644 feature1.txt我将在功能分支中再创建一个功能,即功能 2 并提交它。
(feature) $ vi feature2.txt (feature) $ git add . The file will have its original line endings in your working directory (feature) $ git commit -m 'feature 2' [feature 0f4db49] feature 2 1 file changed, 1 insertion(+) create mode 100644 feature2.txt现在,如果你检查功能分支的日志,它有两个新的提交,我在上面执行过。
(feature) $ git log --oneline 0f4db49 (HEAD -> feature) feature 2 c639e1b feature 1 59c86c9 new commit e2f44fc (origin/master, origin/HEAD) test现在我想将这两个新功能添加到 master 分支。 为此,我将使用 rebase 命令。 从功能分支,我将针对主分支进行变基。 这将做的是它将根据最新更改重新锚定我的功能分支。
(feature) $ git rebase master Successfully rebased and updated refs/heads/feature.现在我要继续检查主分支。
(feature) $ git checkout master Switched to branch 'master' Your branch is ahead of 'origin/master' by 3 commits. (use "git push" to publish your local commits)最后,根据我的功能分支重新设置主分支。 这将在我的功能分支上执行这两个新提交,并在我的主分支上重放它们。
(master) $ git rebase feature Successfully rebased and updated refs/heads/master.现在,如果我检查 master 分支上的日志,我可以看到我的 features 分支的两个提交已成功添加到我的 master 分支。
(master) $ git log --oneline 766c996 (HEAD -> master, feature) feature 2 c036a11 feature 1 812335d add 3 files again 0db602e one more commit 59c86c9 new commit e2f44fc (origin/master, origin/HEAD) test这就是关于 Git 中的 reset、revert 和 rebase 命令的全部内容。
结论
这就是关于 Git 中的 reset、revert 和 rebase 命令的全部内容。 我希望这个分步指南对您有所帮助。 现在,您知道如何使用文章中提到的命令根据需要处理提交。
