Git Reset vs Revert vs Rebase

在本文中,您将了解在 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*

当您重新运行状态命令时,它将反映我刚刚创建的新文件。

$ 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

hard reset的目的是指向指定的commit,更新工作目录和暂存区。 让我再举一个例子。 目前,我的提交可视化如下所示:

  如何将文件上传到 Microsoft Teams

在这里,我将使用 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 的 commit 没有了,head 现在指向 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
newfile

Git 软重置

同样,现在我将向您展示一个软重置的示例。 考虑一下,我已经如上所述再次添加了 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 的旧提交之一

  如何向 Google 幻灯片添加音频(完整指南)

为此,我将运行以下命令。 您需要传递 6 个以上的 SHA 起始字符,不需要完整的 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
newfile

Git 还原

在Git 中,revert 命令用于执行还原操作,即还原某些更改。 它类似于重置命令,但这里唯一的区别是您执行新的提交以返回到特定的提交。 简而言之,可以公平地说 git revert 命令是一次提交。

Git revert 命令在执行还原操作时不会删除任何数据。

假设我要添加 3 个文件并为还原示例执行 git 提交操作。

$ 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(-)

现在要进行必要的更改,与 reset 不同,revert 已经执行了一个新的提交。 再次查看日志,会发现因为 revert 操作而产生了新的 commit。

$ 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 是合适的命令而不是 reset。

Git 变基

在 Git 中,rebase 是一种将一个分支的提交移动或合并到另一个分支的方法。 作为一名开发人员,我不会在真实场景中的 master 分支上创建我的功能。 我会在我自己的分支(一个“功能分支”)上工作,当我在我的功能分支中有一些添加了功能的提交时,我想将它移动到主分支。

  免费下载:微软批量重命名 PowerToy

Rebase 有时可能有点难以理解,因为它与合并非常相似。 合并和变基两者的目标是从我的功能分支中获取提交并将它们放到主分支或任何其他分支上。 考虑一下,我有一个看起来像这样的图表:

假设您正在与其他开发人员一起工作。 在那种情况下,您可以想象这会变得非常复杂,因为您有一群其他开发人员在不同的功能分支上工作,并且他们一直在合并多个更改。 它变得难以追踪。

所以,这就是 rebase 可以提供帮助的地方。 这一次,我不做 git merge,而是做一个 rebase,我想把我的两个功能分支提交并移动到 master 分支上。 变基将从功能分支获取我的所有提交并将它们移动到主分支提交之上。 因此,在幕后,git 正在复制 master 分支上的功能分支提交。

这种方法将为您提供一个干净的直线图,其中所有提交都排成一行。

它可以很容易地跟踪什么提交去了哪里。 你可以想象,如果你在一个有很多开发人员的团队中,所有的提交仍然是连续的。 因此,即使您有很多人同时从事同一个项目,也很容易理解。

让我实际地告诉你这个。

这就是我的主分支目前的样子。 它有 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 命令。 从功能分支,我将对 master 分支进行 rebase。 这将做的是它将根据最新的更改重新锚定我的功能分支。

(feature)
$ git rebase master
Successfully rebased and updated refs/heads/feature.

现在我要继续检查 master 分支。

(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 分支重新设置为我的功能分支。 这将在我的功能分支上获取这两个新提交,并在我的主分支上重播它们。

(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 中的重置、还原和变基命令的全部内容。

结论

这就是关于 Git 中的重置、还原和变基命令的全部内容。 我希望这个分步指南对您有所帮助。 现在,您知道如何使用本文中提到的命令根据需要处理您的提交。