Learn Git with Bitbucket Cloud

Learn how to undo changes in Git using Bitbucket Cloud

Objective

了解如何在与他人协作时撤消在本地计算机和 Bitbucket Cloud 存储库上所做的更改。

Mission Brief

本教程涵盖以下命令:git revertgit resetgit loggit status

时间 观众 先决条件

40 分钟

本教程假定您熟悉以下 Git 命令:

git clonegit commitgit pullgit push
您已经安装 Git
您拥有 Bitbucket 帐户

每个人都会犯错。不是每次推送都是完美的。本教程将帮助您使用最常见的 Git 功能来安全地撤消一个或多个更改。

本教程假定您熟悉以下 Git 命令:

如果您不了解这些命令,我们可以帮助您使用 Bitbucket Cloud 来学习 Git,然后您可以再返回此处,了解如何撤消更改。这些 Git 命令适用于 Windows 或 UNIX 环境。本教程在说明文件系统导航时使用了 UNIX 命令行实用程序。

Undoing changes on your local machine

当您想要撤消的更改位于您的本地系统上且尚未推送到远程存储库时,有两种主要的撤消更改方法:

命令 定义

git revert

这是一个撤消式的命令,但并不是执行传统的撤消操作。它不会删除提交,而是弄清楚如何反转提交中的更改,然后用反转的内容附加一个新的提交。这可以防止 Git 丢失历史记录,对于维护修订历史记录的完整性以及可靠的协作都非常重要。

git reset

这是一个用于撤消更改的多用途 Git 命令。git reset 命令拥有众多功能强大的选项,但在本教程中,我们将只使用以下重置模式:

  • --soft:仅将 HEAD 重置为您选择的提交。工作原理与 git checkout 基本相同,但不会创建独立的 HEAD 状态。
  • --mixed:将 HEAD 重置为您在历史记录中选择的提交,并撤消索引中的更改。
  • --hard:将 HEAD 重置为您在历史记录中选择的提交,撤消索引中的更改并撤消工作目录中的更改。本教程不会测试硬重置。

有关 git reset 工作原理的完整说明,请参阅 git-scm.com 上的Git 工具 – 重置解密

在学习本教程的过程中,您还会在了解如何撤消更改的同时学习其他若干 Git 命令。让我们开始吧。

Fork a repository

我们首先创建一个包含原存储库所有代码的唯一存储库。此流程称为“克隆存储库”。克隆是一个扩展的 Git 流程,并在共享存储库由第三方托管服务(例如 Bitbucket)托管时启用。

  1. 单击或输入以下 URL:https://bitbucket.org/atlassian/tutorial-documentation-tests/commits/all
  2. 单击左侧边栏上的 + 符号,然后选择克隆此存储库,查看对话框内容并单击克隆数据库
  3. 此时您将转到新存储库的概述。
  4. 单击 "+" 符号,然后选择复刻此存储库
  5. 在您的计算机上复刻此存储库。
  6. 导航到包含已复刻存储库的目录。

现在,您已经在本地系统上拥有了一个装满代码和现有历史记录的存储库,可以开始撤消某些更改了。

Find changes on your local system

您必须能够找到并引用要撤消的更改。这可以通过在 Bitbucket 上浏览提交 UI 来完成,并且有一些命令行实用程序也可以找到特定的更改。

git status

git status 将返回您的工作目录(存储库在本地系统上的位置)和暂存区域(您在其中准备了一组更改以添加到项目历史记录中)的状态,并将显示任何有更改的文件以及这些更改是否已添加到暂存区域。现在,我们来执行 git status 并检查存储库的当前状态。

$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
nothing to commit, working tree clean

此时的 git status 输出表明远程主分支的所有内容都是最新的,并且没有等待提交的待办更改。在下一个示例中,我们将对存储库进行一些编辑,并在待办更改状态下对其进行检查。这表示您有对本地系统上存储库中的文件所做的更改,但这些更改尚未准备(或暂存)添加到项目历史记录中。

为了在下个示例中演示这一点,首先请打开 myquote2.html 文件。对 myquote2.html 的内容进行一些修改,然后保存并退出文件。我们再次执行 git status,检查这个状态下的存储库。

$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
 
Changes not staged for commit:
 (use "git add <file>..." to update what will be committed)
 (use "git checkout -- <file>..." to discard changes in working directory)
 
 Modified: myquote2.html
 
no changes added to commit (use "git add" and/or "git commit -a")
--

此时的输出表明,存储库里存在针对 myquote2.html 的待办修改。非常好!跟上面的例子一样,如果您有要撤消但尚未添加到暂存区域的更改,那您可以直接编辑文件,然后继续即可。只有您将更改添加到暂存区域并且随后将其提交到项目历史记录后,Git 才会开始跟踪更改。

现在,我们来撤消对 myquote2.html 进行的一些更改。因为这是一个简化的示例,只做了很少的更改,所以我们有两种方法来撤消更改。如果执行 git checkout myquote2.html,存储库会将 myquote2.html 还原到之前提交的版本。或者,我们也可以执行 git reset --hard,这会将整个存储库还原到最后一次提交的状态。

git log

您可以使用 git log 命令来列出项目历史记录、对其进行筛选并搜索特定的更改。而使用 git status,您可以检查工作目录和暂存区域,git log 仅显示已提交的历史记录。

通过访问存储库的“提交”视图,您可以在 Bitbucket UI 中找到相同的已提交历史记录的日志。您可以在这里找到我们演示存储库的提交视图:https://bitbucket.org/dans9190/tutorial-documentation-tests/commits/all。该视图将显示与 git log 命令行实用程序相似的输出。这可以用于查找和确定要撤消的提交。

在下面的示例中,您可以看到历史记录中的一些内容,但由于每个更改从根本上来说都是一个提交,因此我们需要查找和撤消这些提交。

$ git status
On branch master
Your branch is up-to-date with 'origin/master'.

nothing to commit, working tree clean

$ git log

commit 1f08a70e28d84d5034a8076db9103f22ec2e982c
Author: Daniel Stevens <dstevens@atlassian.com>
Date:   Wed Feb 7 17:06:50 2018 +0000

    Initial Bitbucket Pipelines configuration

commit 52f823ca251a132225dd1cc18ad768de8d336e84
Author: Daniel Stevens <dstevens@atlassian.com>
Date:   Fri Sep 30 15:50:58 2016 -0700

    repeated quote to show how a change moves through the process

commit 4801b87c2147dce83f1bf31acfcffa6cb1d7e0a5
Merge: 1a6a403 3b29606
Author: Dan Stevens [Atlassian] <dstevens@atlassian.com>
Date:   Fri Jul 29 18:45:34 2016 +0000

    Merged in changes (pull request #6)

    Changes

让我们更进一步了解一下列表中的一个提交:

commit 52f823ca251a132225dd1cc18ad768de8d336e84
Author: Daniel Stevens <dstevens@atlassian.com>
Date:   Fri Sep 30 15:50:58 2016 -0700
 
    repeated quote to show how a change moves through the process

您可以看到,每个提交消息都有四个要素:

要素 描述

提交哈希

用于标识此特定更改的字母数字字符串(SHA-1 编码)

作者

更改的提交者

日期

将更改提交到项目的日期

提交消息

描述更改的文本字符串。

最佳实践提示:编写简短的描述性提交消息,这将有助于为大家打造一个更加和谐的工作存储库。


Locate a specific commit

大多数情况下,您要撤消的更改位于项目历史记录某个更远一点的地方,这个范围可能相当广泛。因此,我们来了解一些使用 git log 查找特定更改的基本操作。

  1. 转到终端窗口,使用 cd(更改目录)命令导航到本地存储库的顶层。
$ cd ~/repos/tutorial-documentation-tests/

输入 git log --oneline 命令。添加 --oneline 将会一行显示一条提交,这样您可以在终端中查看更多历史记录。

您可以随时按 q 键退出提交日志并返回到命令提示符。

您应能看到类似以下示例的内容:

$ git log --oneline
1f08a70 (HEAD -> master, origin/master, origin/HEAD) Initial Bitbucket Pipelines configuration
52f823c repeated quote to show how a change moves through the process
4801b87 Merged in changes (pull request #6)
1a6a403 myquote edited online with Bitbucket
3b29606 (origin/changes) myquote2.html edited online with Bitbucket
8b236d9 myquote edited online with Bitbucket
235b9a7 testing prs
c5826da more changes
...
  1. q 键返回到命令提示符。
  2. git log 命令生成的列表中,使用哈希 c5826da 找到提交以及更多更改。有人未编写描述性的提交消息,所以我们必须弄清楚是否有我们需要的更改。
  3. 在终端窗口中的 git log 结果中,突出显示并复制提交哈希 c5826da
  4. 输入 git show,粘贴或抄写您复制的提交哈希,然后按 Enter。您应能看到类似下面这样的内容:
$git show c5826daeb6ee3fd89e63ce35fc9f3594fe243605
commit c5826daeb6ee3fd89e63ce35fc9f3594fe243605
Author: Daniel Stevens <dstevens@atlassian.com>
Date:   Tue Sep 8 13:50:23 2015 -0700

    more changes

diff --git a/README.md b/README.md
index bdaee88..6bb2629 100644
--- a/README.md
+++ b/README.md
@@ -11,12 +11,7 @@ This README would normally document whatever steps are necessary to get your app
 ### How do I get set up? ###

 * Summary of set up
-* Configuration
-* Dependencies
-* Database configuration
-* How to run tests
-* Deployment instructions
-* more stuff and things
:

系统将继续填写底部的提示符,直到显示整个更改。按 q 键退出命令提示符。

Filter the git log to find a specific commit

您可以添加以下内容来筛选和调整 git log 的输出:

筛选参数 作用 示例命令 结果
-

限制显示的提交的数量

git log -10

历史记录中最近的 10 次提交

--after

--before

将显示的提交限制在相关的时间范围内

您也可以使用 --after "yyyy-mm-dd" --before "yyyy-mm-dd"

git log --after 2017-07-04

2017 年 7 月 4 日以后的所有提交

--author="name"

列出作者与 name 匹配的所有提交

git log --author="Alana"

名称字段中有 Alana 的作者所做的所有提交

--grep="message string"

返回提交消息与您输入的字符串匹配的所有提交

git log --grep="HOT-"

返回消息中使用 HOT- 作为文本字符串的所有提交

这里只是简单介绍一下 git log 命令,如果您喜欢使用命令工作,那您可以了解一下高级 git log 教程

Undo a change with git reset

首先,我们来撤消历史记录中的最新提交。在这个示例中,我们假设您刚刚启用了 Bitbucket 的 CI/CD 解决方案管道,但是意识到脚本不太正确。

  1. 在终端窗口中输入 git log --oneline
  2. log: 52f823c 中复制第二次提交的提交哈希,然后按 q 键退出日志。
  3. 在终端窗口中输入 git reset --soft 52f823c。如果成功,该命令应该在后台运行。这样,您就完成了第一次撤消更改。我们来看一下这个操作的结果。
  4. 在终端窗口中输入 git status,您将看到提交已经撤消,现在则是一个未提交的更改。它应该类似于以下内容:
$ git status
On branch master
Your branch is behind 'origin/master' by 1 commit, and can be fast-forwarded.
  (use "git pull" to update your local branch)
 
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)
 
    new file:   bitbucket-pipelines.yml
  1. 在终端窗口中输入 git log --oneline。您应能看到类似下面这样的内容:
$ git log --oneline
52f823c repeated quote to show how a change moves through the process
4801b87 Merged in changes (pull request #6)
1a6a403 myquote edited online with Bitbucket
3b29606 (origin/changes) myquote2.html edited online with Bitbucket
8b236d9 myquote edited online with Bitbucket
235b9a7 testing prs
c5826da more changes
43a87f4 remivng
d5c4c62 a few small changes
23a7476 Merged in new-feature2 (pull request #3)
5cc4e1e add a commit message
cbbb5d6 trying a thing
438f956 adding section for permissions and cleaning up some formatting
23251c1 updated snipptes.xml organization into resources. other files misc changes
3f630f8 Adding file to track changes
...
  1. 您可以看到分支的新 HEAD 是提交 52f823c,这正是您想要的。
  2. q 键退出日志。让终端保持打开状态,现在,您已经了解了如何执行简单的重置,让我们来尝试一些更复杂的操作。

Undo several changes with git reset

假设您已经意识到需要重新编写第 6 个拉取请求 (4801b87),并且想要保留干净的历史记录,这次,您将使用 git reset 命令,将 HEAD 重置为提交 1a6a403

  1. 输入 git log --online
  2. 复制提交哈希 1a6a403(使用 Bitbucket 在线编辑的 myquote),这正是我们想撤消更改的第 6 个拉取请求下的提交。
  3. 在终端窗口中输入 git reset 1a6a403。输出应该类似于以下内容:
$ git reset 1a6a403
Unstaged changes after reset:
M README.md
M myquote2.html

您可以看到更改现在处于未提交状态。这意味着现在我们已经从项目历史记录和暂存区域中删除了一些更改。

  1. 在终端窗口中输入 git status。输出应该类似于以下内容:

$ git status
On branch master
Your branch is behind 'origin/master' by 6 commits, and can be fast-forwarded.
  (use "git pull" to update your local branch)
 
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)
 
    modified:   README.md
    modified:   myquote2.html
 
Untracked files:
  (use "git add <file>..." to include in what will be committed)
 
    bitbucket-pipelines.yml
 
no changes added to commit (use "git add" and/or "git commit -a")

您可以看到,我们撤消的第一个更改(bitbucket-pipelines.yml 文件)现在完全未受 Git 跟踪。这是因为调用 git reset 会同时删除分支 HEAD 和 Git 的跟踪或索引区域中的更改。底层的执行流程要比我们这里介绍的内容更复杂一些,您可以在 git reset 中了解更多信息。

  1. 在终端窗口中输入 git log --oneline
1a6a403 myquote edited online with Bitbucket
8b236d9 myquote edited online with Bitbucket
43a87f4 remivng
d5c4c62 a few small changes
23a7476 Merged in new-feature2 (pull request #3)
5cc4e1e add a commit message
cbbb5d6 trying a thing
438f956 adding section for permissions and cleaning up some formatting
23251c1 updated snipptes.xml organization into resources. other files misc changes
3f630f8 Adding file to track changes
e52470d README.md edited online with Bitbucket
e2fad94 README.md edited online with Bitbucket
592f84f Merge branch 'master' into new-feature2 Merge branch  especially if it merges an updated upstream into a topic branch.
7d0bab8 added a line
879f965 adding to the quote file
8994332 Merged in HOT-235 (pull request #2)
b4a0b43 removed sarcastic remarks because they violate policy.
b5f5199 myquote2.html created online with Bitbucket
b851618 adding my first file
5b43509 writing and using tests

现在,日志输出显示提交历史记录也被修改了,并且是从提交 1a6a403 开始的。为了演示和进一步举例,假设我们现在要撤消刚才所执行的重置。经过进一步考虑,我们可能想保留第 6 个拉取请求的内容。

Pushing resets to Bitbucket

git resetGit 提供的若干撤消方法之一。重置通常被认为是“不安全”的撤消更改选项。在本地处理独立代码时,重置很安全,但如果与团队成员共享,重置就会带来风险。

要与远程团队共享已重置的分支,必须执行“强制推送”。通过执行 git push -f 可以启动“强制推送”。强制推送将会破坏在推送之后在分支上建立的所有历史记录。

以下是这种“不安全”场景的示例:

  • 开发团队 A 一直在某一分支上开发新功能。
  • 开发团队 B 一直在同一分支上开发单独的功能。
  • 开发团队 B 决定在开发团队 A 和自己开始工作之前将分支重置为较早的状态。
  • 然后,开发团队 B 强制将重置分支推送到远程存储库。
  • 开发团队 A 拉取分支以接收更新。在此次拉取期间,开发团队 A 收到了强制的更新。开发团队 A 还没有完成开发功能的工作,这次重置导致他们的本地分支回到了较早的状态,也丢失了他们的提交。

Undo a git reset

到目前为止,我们一直是将 git commit Sha 哈希传送到 git reset。当前,git log 输出缺少我们已经重置的提交。如何恢复这些提交呢?Git 永远不会彻底删除提交,除非它已经脱离了指向它的任何指针。此外,Git 还存储一个单独日志,里面记录了所有引用操作,它被称为“回流”。我们可以通过执行 git reflog 来检查回流。

1a6a403 HEAD@{0}: reset: moving to 1a6a403
1f08a70 HEAD@{1}: reset: moving to origin/master
1f08a70 HEAD@{2}: clone: from git@bitbucket.org:dans9190/tutorial-documentation-tests.git

git reflog 的输出应该类似于以上内容。您可以看到代码存储库上的操作历史记录。第一行引用了我们执行的重置来重置第 6 个拉取请求。现在我们来重置这一重置,以还原第 6 个拉取请求。此回流输出的第二列指明了对代码存储库执行的修改操作的引用指针。这里的 HEAD@{0} 引用了我们之前执行的重置命令。我们不想回复此重置命令,因此我们会将代码存储库还原到 HEAD@{1}。

$ git reset --hard HEAD@{1}
HEAD is now at 1f08a70 Initial Bitbucket Pipelines configuration

现在,我们使用 git log --oneline 来检查一下代码存储库提交历史记录:

$git log --online
1f08a70 Initial Bitbucket Pipelines configuration
52f823c repeated quote to show how a change moves through the process
4801b87 Merged in changes (pull request #6)
1a6a403 myquote edited online with Bitbucket
3b29606 myquote2.html edited online with Bitbucket
8b236d9 myquote edited online with Bitbucket
235b9a7 testing prs
c5826da more changes
43a87f4 remivng
d5c4c62 a few small changes
23a7476 Merged in new-feature2 (pull request #3)
5cc4e1e add a commit message
cbbb5d6 trying a thing
438f956 adding section for permissions and cleaning up some formatting
23251c1 updated snipptes.xml organization into resources. other files misc changes
3f630f8 Adding file to track changes
e52470d README.md edited online with Bitbucket
e2fad94 README.md edited online with Bitbucket
592f84f Merge branch 'master' into new-feature2 Merge branch  especially if it merges an updated upstream into a topic branch.
7d0bab8 added a line
:

在此,我们可以看到代码存储库的提交历史记录已经还原到了我们之前试验的版本。第一次重置操作后显示丢失的提交 4801b87 也已经还原。git reflog 是一个用于撤消存储库中的更改的强大工具。您可在 git reflog 页面深入了解它的用法。

git revert

前面的示例使用 git resetgit reflog 执行了一些非常耗时的撤消操作。Git 还有另一种撤消实用程序,并且通常被认为比重置更安全。“还原”将创建新提交,其中包含指定提交更改的反转内容。然后,可以安全地将这些还原提交推送到远程存储库,与其他开发人员共享。

下一节将演示 git revert 的用法。我们继续使用上一节中的示例。首先,让我们检查日志并找到要还原的提交。

$ git log --online
1f08a70 Initial Bitbucket Pipelines configuration
52f823c repeated quote to show how a change moves through the process
4801b87 Merged in changes (pull request #6)
1a6a403 myquote edited online with Bitbucket
1f08a70 Initial Bitbucket Pipelines configuration
52f823c repeated quote to show how a change moves through the process
4801b87 Merged in changes (pull request #6)
1a6a403 myquote edited online with Bitbucket
3b29606 myquote2.html edited online with Bitbucket
8b236d9 myquote edited online with Bitbucket
235b9a7 testing prs
c5826da more changes
43a87f4 remivng
d5c4c62 a few small changes
23a7476 Merged in new-feature2 (pull request #3)
5cc4e1e add a commit message
cbbb5d6 trying a thing
438f956 adding section for permissions and cleaning up some formatting
23251c1 updated snipptes.xml organization into resources. other files misc changes
3f630f8 Adding file to track changes
e52470d README.md edited online with Bitbucket
e2fad94 README.md edited online with Bitbucket
592f84f Merge branch 'master' into new-feature2 Merge branch  especially if it merges an updated upstream into a topic branch.
7d0bab8 added a line
:

在此示例中,我们选择最新的提交 1f08a70 作为我们要执行操作的提交。在此场景中,假设我们要撤消在该提交中所做的编辑。执行:

$ git revert 1f08a70

这将启动 git merge 工作流。Git 将创建一个新提交,其内容与指定进行还原的提交的内容相反。然后,Git 将打开配置的文本编辑器,提示输入新的提交消息。凭借此提交工作流,还原被认为是更安全的撤消选项。在执行撤消操作时,创建还原提交会在提交历史记录中留下清晰的记录。

You just learned how to undo changes!

恭喜,您已经完成了本教程!您可以随时返回本教程,或转到“撤消更改”一节进行更深入的了解。继续利用 Bitbucket 出色地完成工作吧!