Undo and Revert Commits in Git – Git中的撤销和恢复提交

最后修改: 2021年 9月 22日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.绪论

We often find ourselves needing to undo or revert a commit while using Git, whether it’s to roll back to a particular point in time or revert a particularly troublesome commit.

在使用Git的过程中,我们经常发现自己需要撤销或恢复一个提交,不管是为了回滚到某个特定的时间点,还是为了恢复一个特别麻烦的提交。

In this tutorial, we’ll go through the most common commands to undo and revert commits in Git. We’ll also demonstrate the subtle differences in the way these commands function.

在本教程中,我们将介绍在Git中撤销和恢复提交的最常用命令。我们还将演示这些命令在运行方式上的细微差别。

2. Reviewing Old Commits With git checkout

2.用git checkout审查旧的提交文件

To start, we can review the state of a project at a particular commit by using the git checkout command. We can review the history of a Git repository by using the git log command. Each commit has a unique SHA-1 identifying hash, which we can use with git checkout in order to revisit any commit in the timeline.

首先,我们可以通过使用git checkout命令来查看项目在某个特定提交时的状态。我们可以通过使用git log命令来查看一个Git仓库的历史。每个提交都有一个唯一的SHA-1识别哈希值,我们可以用git checkout来重访时间轴上的任何提交。

In this example, we’ll revisit a commit that has an identifying hash of e0390cd8d75dc0f1115ca9f350ac1a27fddba67d:

在这个例子中,我们将重新审视一个提交,其识别哈希值为e0390cd8d75dc0f1115ca9f350ac1a27fddba67d

git checkout e0390cd8d75dc0f1115ca9f350ac1a27fddba67d

Our working directory will now match the exact state of our specified commit. Thus, we can view the project in its historical state and edit files without worrying about losing the current project state. Nothing we do here saves to the repository. We call this a detached HEAD state.

我们的工作目录现在将与我们指定的提交的确切状态一致。因此,我们可以查看项目的历史状态并编辑文件,而不用担心丢失当前的项目状态。我们在这里做的任何事情都不会保存到版本库中。我们称其为detached HEAD状态。

We can use git checkout on locally modified files to restore them to their working copy versions.

我们可以在本地修改的文件上使用git checkout,将它们恢复到工作拷贝版本。

3. Reverting a Commit With git revert

3.用git revert来恢复一个承诺

We can revert a commit in Git by using the git revert command. It’s important to remember that this command isn’t a traditional undo operation. Instead, it inverts changes introduced by the commit, and generates a new commit with the inverse content.

我们可以通过使用git revert 命令来恢复Git中的提交。重要的是要记住,这个命令不是一个传统的撤销操作。相反,它颠倒了提交所带来的变化,并生成了一个具有反转内容的新提交。

This means that we should only use git revert if we want to apply the inverse of a particular commit. It doesn’t revert to the previous state of a project by removing all subsequent commits, it simply undoes a single commit.

这意味着我们应该只在想应用某个特定提交的反转时使用git revert。它不会通过删除所有后续的提交来恢复到项目之前的状态,它只是撤消了一次提交。

git revert doesn’t move ref pointers to the commit that we’re reverting, which is in contrast to other ‘undo’ commands, such as git checkout and git reset. Instead, these commands move the HEAD ref pointer to the specified commit.

git revert不会将参考指针移到我们要恢复的提交上,这与其他 “撤销 “命令不同,比如git checkoutgit reset。相反,这些命令将HEAD参考指针移到指定的提交。

Let’s go through an example of reverting a commit:

让我们来看看还原一个提交的例子。

mkdir git_revert_example
cd git_revert_example/
git init .
touch test_file
echo "Test content" >> test_file 
git add test_file
git commit -m "Adding content to test file"
echo "More test content" >> test_file 
git add test_file
git commit -m "Adding more test content"
git log
git revert e0390cd8d75dc0f1115ca9f350ac1a27fddba67d
cat test_file

In this example, we created a test_file, added some content, and committed it. Then we added and committed more content to the file before running a git log to identify the commit hash of the commit we want to revert.

在这个例子中,我们创建了一个test_file,添加了一些内容,并提交了它。然后我们在运行git log以确定我们要恢复的提交的commit hash之前,向该文件添加并提交了更多内容。

In this instance, we’re reverting the most recent commit. Finally, we run git revert, and verify that the changes in the commit were reverted by outputting the file’s contents.

在这个例子中,我们还原的是最近的提交。最后,我们运行git revert,并通过输出文件的内容来验证提交中的修改是否被恢复。

4. Reverting to Previous Project State With git reset

4.用git reset恢复到以前的项目状态

Reverting to a previous state in a project with Git is achieved by using the git reset command. This tool undoes more complex changes. It has three primary forms of invocation that relate to Git’s internal state management system: –hard, –soft, and –mixed. Understanding which invocation to use is the most complicated part of performing a git revert.

通过使用git reset命令,可以将项目恢复到之前的状态。这个工具可以撤消更复杂的改动。它有三种主要的调用形式,与Git的内部状态管理系统有关。-硬-软,和-混合。了解使用哪种调用是执行git revert的最复杂部分。

git reset is similar in behavior to git checkout; however, git reset will move the HEAD ref pointer, whereas git checkout operates on the HEAD ref pointer and doesn’t move it.

git reset的行为与git checkout相似;不过,git reset会移动HEAD参考指针,而git checkout是对HEAD参考指针进行操作,不会移动它

To understand the different invocations, we’ll look at Git’s internal state management system, also known as Git’s three trees.

为了理解不同的调用,我们将看看Git的内部状态管理系统,也被称为Git的三棵树

The first tree is the working directory. This tree is in sync with the local file system, and represents immediate changes made to content in files and directories.

第一个树是工作目录。这棵树与本地文件系统同步,并代表对文件和目录中的内容所做的即时更改。

Next, we have the staging index tree. This tree tracks changes in the working directory, in other words, changes that have been selected with git add to be stored in the next commit.

接下来,我们有暂存索引树。这个树追踪工作目录中的变化,换句话说,就是那些被git add选中的变化,将被储存在下一次提交中。

The final tree is the commit history. The git commit command adds changes to a permanent snapshot that’s stored in the commit history.

最后一棵树是commit historygit commit命令将改动添加到一个永久快照中,该快照存储在commit history

4.1. –hard

4.1.

The most dangerous and frequently used option with this invocation is commit history, as ref pointers update to the specified commit. After this, the staging index and working index reset to match that of the specified commit. Any previously pending changes to the staging index and working directory reset to match the state of the commit tree. We’ll lose any pending or uncommitted work in the staging index and working index.

这个调用中最危险也是最常用的选项是提交历史,因为Ref指针会更新到指定的提交。在此之后,暂存索引工作索引被重置为与指定的提交一致。任何先前对暂存索引工作目录的待定更改都会被重置,以匹配提交树的状态。我们会失去暂存索引工作索引中的任何待定或未提交的工作

Adding on to the example above, let’s commit some more content to the file, and also commit a brand new file to the repository:

在上面的例子基础上,让我们再提交一些内容到文件中,同时提交一个全新的文件到版本库。

echo "Text to be committed" >> test_file
git add test_file
touch new_test_file
git add new_test_file
git commit -m "More text added to test_file, added new_test_file"

Let’s say we then decide to revert to the first commit in the repository. We’ll achieve this by running the command:

假设我们决定恢复到版本库中的第一次提交。我们将通过运行以下命令来实现这一目的。

git reset --hard 9d6bedfd771f73373348f8337cf60915372d7954

Git will tell us that the HEAD is now at the commit hash specified. Looking at the contents of test_file shows us that our latest text additions aren’t present, and our new_test_file no longer exists. This data loss is irreversible, so it’s critical that we understand how –hard works with Git’s three trees.

Git会告诉我们,HEAD现在是在指定的提交哈希值。看一下test_file的内容,就会发现我们的最新文本添加不存在了,我们的new_test_file也不再存在了。这种数据丢失是不可逆的,所以我们必须了解-hard如何与Git的三个树一起工作。

4.2. –soft

4.2.-软

When invocation happens with –soft, the ref pointers are updated, and the reset stops there. Thus, the staging index and working directory remain in the same state.

当用-soft进行调用时,Ref指针被更新,重置也就到此为止。因此,暂存索引工作目录保持在相同的状态。

In our previous example, the changes we committed to the staging index wouldn’t have been deleted if we used the –soft argument. We’re still able to commit our changes in the staging index.

在我们之前的例子中,如果我们使用-soft参数,我们提交到staging index中的修改就不会被删除。我们仍然能够在staging index中提交我们的修改。

4.3. –mixed

4.3.混合型

If no argument is passed, the default operating mode is –mixed, which offers a middle ground between the –soft and –hard invocations. The staging index resets to the state of the specified commit and ref pointers update. Any undone changes from the staging index move to the working directory.

如果没有传递参数,默认的操作模式是-mixed,它在-soft-hard的调用之间提供了一个中间地带。暂存索引重设为指定提交的状态,参考指针更新。任何来自暂存索引的未完成的修改都会移动到工作目录

Using –mixed in our example above means that our local changes to the files aren’t deleted. Unlike –soft, however, the changes are undone from the staging index and await further action.

在我们上面的例子中使用-mixed,意味着我们对文件的本地修改不会被删除。然而,与-soft不同的是,这些更改staging index被撤销,并等待进一步的行动

5. Conclusion

5.总结

A simple way to summarize the two methods is that git revert is safe, and git reset is dangerous. As we saw in our example, there’s a possibility of losing our work with git reset. With git revert, we can safely undo a public commit, whereas git reset is tailored toward undoing local changes in the working directory and staging index. 

简单总结这两种方法的方法是:git revert是安全的,而git reset是危险的。正如我们在例子中看到的,用git reset有可能会丢失我们的工作。使用git revert,我们可以安全地撤销一个公开的提交,而git reset是专门为撤销工作目录暂存索引中的本地更改而设计的。

git reset will move the HEAD ref pointer, whereas git revert will simply revert a commit and apply the undo via a new commit to the HEAD. It’s also important to note that we should never use git reset when any subsequent snapshots have been pushed to a shared repository. We must assume that other developers are reliant upon published commits.

git reset将移动HEAD参考指针,而git revert将简单地恢复一个提交,并通过一个新的提交应用撤销到HEAD。同样重要的是要注意,当任何后续快照被推送到共享仓库时,我们不应该使用git reset。我们必须假设其他开发者依赖于已发布的提交。