你是否遇到过需要撤销主分支上的更改,因为某些不想要的代码被提交,可能导致生产环境中的错误?

我们在项目中也遇到了类似的问题,一位开发者意外地选择了一个未经测试的提交并将其部署到生产环境。幸运的是,我们的应用是公司内部使用的产品,所以影响最小。

这种情况在分布在不同国家和时区的分布式团队中是可能发生的。以下是我们如何从主(生产)分支中移除特定提交,并快速解决问题。

Git revert 来拯救你git revert 用于创建新的提交,撤销早期提交的效果(通常只是一个有缺陷的提交)。

git revert 命令可以被视为一种“撤销”类型的命令,但它并不是传统的撤销操作。它不会将提交从项目历史中移除,而是反转该提交所引入的更改,并附加一个新的提交,包含反向内容。这防止了 Git 丢失历史,这对修订历史的完整性和可靠协作至关重要。

简单来说,git revert 用于撤销一个提交(例如哈希为 2afe34),但它通过创建一个新提交来实现,该提交移除了你要撤销的提交(2afe34)中的更改。

例如,如果你在追踪一个错误,发现它是由一个提交引入的,那么你可以使用 git revert 自动处理这个问题,而不是手动修复并提交新的更改。

语法git revert [--options] <commit>

git revert 从历史记录中提取指定的提交,反转该提交的更改,并创建一个新的“撤销提交”。

让我们演示一下:
我有一个为了演示目的创建的 index.html 文件。它包含一个水果的有序列表。以下是我的每个提交及其更改:

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Fruits</title>
</head>
<body>
    <div>
        <h3>Fruits list</h3>
        <ol>
            <li>Mango</li>
            <li>Orange</li>
            <li>Banana</li>
        </ol>
        <h3>Other list</h3>
        <ul>
            <li>Apple</li>
            <li>Pineapple</li>
        </ul>
    </div>
</body>
</html>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
7afe5e3 (HEAD -> master) add other list
c8862d9 add banana
8db1cd2 add orange
f4e9e1f add mango
065a354 initial commit
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

我的第一次提交是一个初始提交,仅包含基本的 HTML 结构,如 headbody。后续的提交不言自明。我在提交 f4e9e1f 中添加了芒果,在 8db1cd2 中添加了橙子,在 c8862d9 中添加了香蕉,然后在提交 7afe5e3 中添加了第二个有序列表,当前 HEAD 提交。

假设我想从列表中移除香蕉。在这个简单的例子中,我们可以直接从代码中移除香蕉并创建一个反映该更改的新提交。

然而,在一个有多个贡献者的协作项目中,提交历史变得重要,以便跟踪项目在任何特定时刻的状态。此外,手动撤销大量代码更改并不可行,这时 git revert 成为不可避免的解决方案。

回到我们的演示,为了从第一个有序列表中移除香蕉,我可以简单地运行 git revert c8862d9,这会创建一个新的提交,说明撤销的原因。你会看到香蕉现在已经从列表中移除。

因此,新索引文件和提交历史将如下所示:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Fruits</title>
</head>
<body>
    <div>
        <h3>Fruits list</h3>
        <ol>
            <li>Mango</li>
            <li>Orange</li>
        </ol>
        <h3>Other list</h3>
        <ul>
            <li>Apple</li>
            <li>Pineapple</li>
        </ul>
    </div>
</body>
</html>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.

在执行 git revert 后的提交历史:git log --oneline

828ef17 (HEAD -> master) Revert "add banana"
7afe5e3 add other list
c8862d9 add banana
8db1cd2 add orange
f4e9e1f add mango
065a354 initial commit
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

有时,Git 不能准确判断需要移除的代码,导致合并冲突。在这些情况下,你作为开发者需要稍微帮助一下 Git。

重要的是要理解,git revert 撤销单个提交——它不会像 git reset 那样通过删除所有后续提交来“恢复”项目到之前的状态。

撤销相对于重置有两个重要优点:

  1. 它不会改变项目历史,使其成为一个“安全”的操作,适用于已经发布到共享库的提交。
  2. git revert 可以针对历史中的任意单个提交。