当前位置: 首页 > news >正文

在 Git 中撤消更改的 6 种方法!

目录

1. 修改最近的提交

2. 将分支重置为较旧的提交

硬重置

软重置分支

创建备份分支

3. 交互式变基

删除旧提交

改写提交消息

编辑旧提交

压缩

4. 还原提交

5. 签出文件

6. 使用 Git Reflog


        当使用 Git 进行项目代码管理时,难免会出现一些错误操作或需求变更,需要对代码进行撤销或修改。Git 提供了多种方式来撤消已有的更改。本文将介绍 Git 中常用的 6 种撤消更改的方法,帮助你更好地处理这些问题!

在开始示例之前,先来创建一个简单的 Git 仓库,其中包含于一些提交:

git init && \
echo {} > package.json && git add . && git commit -m "Add package.json" && \
echo FOO=bar > .env && git add . && git commit -m "Add .env" && \
touch README.md && git add . && git commit -m "Add README" && \
touch .gitignore && git add . && git commit -m "Add .gitignore"

对于这些命令,实际上包含以下历史操作:

* 4753e23 - (HEAD -> master) Add .gitignore (4 seconds ago) <AleksandrHovhannisyan>
* 893d18d - Add README (4 seconds ago) <AleksandrHovhannisyan>
* 2beb7c7 - Add .env (4 seconds ago) <AleksandrHovhannisyan>
* 0beebfb - Add package.json (5 seconds ago) <AleksandrHovhannisyan>

1. 修改最近的提交

在创建并提交了 .gitignore 文件后不久,决定修改这个文件:

echo node_modules > .gitignore

        但不想在 git 日志历史记录中添加一个对于如此微小更改的提交记录。或者需要在最近的提交消息中纠正一个拼写错误。

这两种情况都是使用 git amend 命令的经典用例:

git commit -a --amend

        简单来说,git amend 命令用于在 git 中编辑 commit 和提交消息。这是 git 中撤销更改的最基本方式之一。

        当运行上述代码时,git 会打开选择的编辑器并显示最近的提交,在其中加入更改以进入暂存环境:

Add .gitignore# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# Date:      Sun Oct 11 08:25:58 2022 -0400
#
# On branch master
# Changes to be committed:
#       new file:   .gitignore
#

保存并关闭文件,git 将修改最近的提交以包括新更改。也可以在保存文件之前编辑提交消息。

        如果要做的只是更新提交消息本身,例如修正一个拼写错误,那实际上并不需要进入暂存环境。只需要运行这个命令:

git commit --amend

在编辑器中更改提交消息并保存文件,关闭即可。

在修改了最近的提交后,日志将会看起来像这样:

* 7598875 - (HEAD -> master) Add .gitignore (31 seconds ago) <AleksandrHovhannisyan>
* 893d18d - Add README (79 seconds ago) <AleksandrHovhannisyan>
* 2beb7c7 - Add .env (79 seconds ago) <AleksandrHovhannisyan>
* 0beebfb - Add package.json (80 seconds ago) <AleksandrHovhannisyan>

        现在,假设修改之前已经将旧提交推送到了远程分支。如果运行 git status,就会被告知本地分支和远程分支有一个提交不同:

On branch master
Your branch and 'origin/master' have diverged,
and have 1 and 1 different commits each, respectively.(use "git pull" to merge the remote branch into yours)

        这也是正常的,因为远程分支有旧的提交,而本地分支有修改过的提交。它们的哈希值不同,因为修改提交会更改其时间戳,这会强制 git 计算新的哈希值。要想用新的提交更新远程分支,就需要强制推送它:git push -f。这将用本地分支覆盖远程分支的历史记录。

        需要注意,这里我们是在自己分支上进行的强制推送,在实际工作中,我们不应该强制推送到公共分支;如果这样做,每个人的本地 master 副本都将与远程副本不同,并且任何基于旧 master 的新功能现在都将具有不兼容的提交历史记录。

2. 将分支重置为较旧的提交

目前为止,我们有如下提交历史:

* 7598875 - (HEAD -> master) Add .gitignore (31 seconds ago) <AleksandrHovhannisyan>
* 893d18d - Add README (79 seconds ago) <AleksandrHovhannisyan>
* 2beb7c7 - Add .env (79 seconds ago) <AleksandrHovhannisyan>
* 0beebfb - Add package.json (80 seconds ago) <AleksandrHovhannisyan>

我们再来向 master 添加一个提交:

touch file && git add . && git commit -m "Add a file"

现在提交历史变成了这样:

* b494f6f - (HEAD -> master) Add a file (5 seconds ago) <AleksandrHovhannisyan>
* 7598875 - Add .gitignore (3 minutes ago) <AleksandrHovhannisyan>
* 893d18d - Add README (4 minutes ago) <AleksandrHovhannisyan>
* 2beb7c7 - Add .env (4 minutes ago) <AleksandrHovhannisyan>
* 0beebfb - Add package.json (4 minutes ago) <AleksandrHovhannisyan>

硬重置

        几分钟后,出于某种原因,我们决定不再保留最近的提交。要想删除它,只需在 HEAD 指针之前硬重置一次提交,该指针始终指向当前分支上的最新提交:

git reset --hard HEAD~1

        波浪号 (~) 后跟一个数字告诉 git 它应该从给定的提交(在本例中为 HEAD 指针)回溯多少次提交。由于 HEAD 总是指向当前分支上的最新提交,这告诉 git 对最近提交之前的提交进行硬重置。

输出结果如下:

HEAD is now at 7598875 Add .gitignore

        硬重置是撤消 git 更改的一个便捷方法,但这是一个破坏性过程——该提交中的所有更改都将丢失,找回它们的唯一方法是通过 git reflog命令(后面会详细介绍)。

我们还可以重置为 HEAD~nth 提交,在这种情况下,该提交期间和之后的所有工作都将丢失:

git reset --hard HEAD~4

或者甚至是特定的提交,如果有它的哈希:

git reset --hard <hash-id>

当然也不限于仅针对当前分支中的提交进行重置,还可以重置本地分支以指向另一个本地分支:

git reset --hard <someOtherBranch>

甚至到远程分支:

git reset --hard origin/master

        这个就很有用,例如,如果不小心将内容提交到本地 master 分支。假设应该在一个 feat/X 分支上进行提交,但忘记了创建它,而且一直在向本地 master 提交代码。

        当然,我们也可以使用 git cherry-pick 来解决这个问题,但是如果有很多次提交怎么办?这有点痛苦,而 reset 可以轻松搞定。

要解决此问题,现在需要创建功能分支:

git checkout -b feat/X

并强行将本地 master 分支重置为远程 master 分支:

git checkout master && git reset --hard origin/master

并且不要忘记回到功能分支,这样就不会重复同样的错误:

git checkout feat/X

软重置分支

        正如上面提到的,如果进行硬重置,将丢失在该提交时或之后所做的所有工作。当然,可以从那个状态中恢复过来,但需要额外的操作。相反,如果想在 git 的暂存环境中保留更改,可以进行软重置:

git reset --soft HEAD~1

同样,可以只使用提交哈希而不是从 HEAD 指针回溯:

git reset --soft a80951b

        该提交引入的所有更改以及它之后的任何提交都将出现在 git 的暂存环境中。在这里,可以使用 git reset HEAD file(s) 取消暂存文件,对已经暂存的文件进行所需的任何更改。然后,可以根据需要进行任何新的提交。

        用例:在一个提交中提交了文件 A 和文件 B,但后来意识到它们实际上应该是两个独立的提交。可以执行软重置并选择性地提交一个文件,然后单独进行另一个提交,所有这些操作都不会丢失任何工作内容。

创建备份分支

        我们可以将分支用作备份机制,以防你知道即将运行的某个命令(例如 git reset --hard)可能会损坏分支的提交历史记录。在运行这些命令之前,可以简单地创建一个临时备份分支(例如 git branch backup)。如果出现任何问题,就可以针对备份分支执行硬重置操作:

git reset --hard backup

3. 交互式变基

        Git 的交互式变基是其最强大、最灵活的命令之一,允许倒回历史并进行任何所需的更改。如果想要删除旧的提交、更改旧的提交消息或者将旧的提交压缩成其他的提交,那么这就是你需要使用的命令。所有交互式变基都始于 git rebase -i 命令,并且必须指定一个提交来重新设置当前分支。

删除旧提交

目前为止,我们有如下提交历史:

* 7598875 - (HEAD -> master) Add .gitignore (20 minutes ago) <AleksandrHovhannisyan>
* 893d18d - Add README (21 minutes ago) <AleksandrHovhannisyan>
* 2beb7c7 - Add .env (21 minutes ago) <AleksandrHovhannisyan>
* 0beebfb - Add package.json (21 minutes ago) <AleksandrHovhannisyan>

        第二次提交看起来有点可疑,为什么要将本地环境变量 (.env) 检查到 git 中? 显然,我们需要删除此提交,同时保留所有其他提交。为此,我们将针对该提交运行交互式变基:

git rebase -ir 2beb7c7^

这将调出这个编辑器:

pick 2beb7c7 Add .env
pick 893d18d Add README
pick 7598875 Add .gitignore

要删除 2beb7c7,需要将 pick 命令更改为 drop(或 d)并保持其他不变:

drop 2beb7c7 Add .env
pick 893d18d Add README
pick 7598875 Add .gitignore

现在关闭并保存文件,会得到如下确认:

Successfully rebased and updated refs/heads/master.

现在,如果执行 git log,将不再看到该提交:

* 11221d4 - (HEAD -> master) Add .gitignore (6 seconds ago) <AleksandrHovhannisyan>
* 9ed001a - Add README (6 seconds ago) <AleksandrHovhannisyan>
* 0beebfb - Add package.json (50 minutes ago) <AleksandrHovhannisyan>

        注意,在已删除提交之后的所有提交哈希都将被重新计算。因此,虽然根提交仍保持为 0beebfb,但在它之后的所有哈希值都已更改。正如现在已经看到几次,如果之前将此分支推送到了远程仓库中,那么本地分支和远程分支现在将不同步。因此,只需要进行一次强制推送即可更新远程分支:

git push -f

改写提交消息

我们再来添加两个提交,提交消息是随便写的:

* 094f8cb - (HEAD -> master) Do more stuff (1 second ago) <AleksandrHovhannisyan>
* 74dab36 - Do something idk (59 seconds ago) <AleksandrHovhannisyan>
* 11221d4 - Add .gitignore (3 minutes ago) <AleksandrHovhannisyan>
* 9ed001a - Add README (3 minutes ago) <AleksandrHovhannisyan>
* 0beebfb - Add package.json (53 minutes ago) <AleksandrHovhannisyan>

现在想修改这两个提交消息。我们将从交互式变基开始。在这里,我们将针对最后两个提交:

git rebase -i HEAD~2

这将打开编辑器:

pick 74dab36 Do something idk
pick 094f8cb Do more stuff

现在,只需将任何要更改其消息的提交的 pick 替换为 r(或 reword):

reword 74dab36 Do something idk
reword 094f8cb Do more stuff

        关闭并保存文件,对于想要改写的每个提交,git 将打开编辑器,就像正在修改该提交一样,允许编辑消息。

也许在第一次提交时这样做:

Update README with getting started instructions# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# Date:      Sun Oct 11 09:17:41 2022 -0400
#
# interactive rebase in progress; onto 11221d4
# Last command done (1 command done):
#    reword 74dab36 Do something idk
# Next command to do (1 remaining command):
#    reword 094f8cb Do more stuff
# You are currently editing a commit while rebasing branch 'master' on '11221d4'.
#
# Changes to be committed:
#       modified:   README.md
#

第二次这样做:

Add name and author to package.json# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# interactive rebase in progress; onto 11221d4
# Last commands done (2 commands done):
#    reword 74dab36 Do something idk
#    reword 094f8cb Do more stuff
# No commands remaining.
# You are currently rebasing branch 'master' on '11221d4'.
#
# Changes to be committed:
#       modified:   package.json
#

我们会得到以下输出确认:

[detached HEAD 665034d] Update README with getting started instructionsDate: Sun Oct 11 09:17:41 2020 -04001 file changed, 5 insertions(+)
[detached HEAD ba88fb0] Add name and author to package.json1 file changed, 4 insertions(+), 1 deletion(-)
Successfully rebased and updated refs/heads/master.

现在提交历史看起来像这样:

* ba88fb0 - (HEAD -> master) Add name and author to package.json (31 seconds ago) <AleksandrHovhannisyan>
* 665034d - Update README with getting started instructions (53 seconds ago) <AleksandrHovhannisyan>
* 11221d4 - Add .gitignore (6 minutes ago) <AleksandrHovhannisyan>
* 9ed001a - Add README (6 minutes ago) <AleksandrHovhannisyan>
* 0beebfb - Add package.json (56 minutes ago) <AleksandrHovhannisyan>

编辑旧提交

        编辑提交就意味着在提交之后立即转到历史记录中的那个点。这允许修改提交并包含(或删除)想要的任何更改。

目前为止,我们有如下提交历史:

* ba88fb0 - (HEAD -> master) Add name and author to package.json (31 seconds ago) <AleksandrHovhannisyan>
* 665034d - Update README with getting started instructions (53 seconds ago) <AleksandrHovhannisyan>
* 11221d4 - Add .gitignore (6 minutes ago) <AleksandrHovhannisyan>
* 9ed001a - Add README (6 minutes ago) <AleksandrHovhannisyan>
* 0beebfb - Add package.json (56 minutes ago) <AleksandrHovhannisyan>

假设要编辑根提交(0beebfb)并添加第二个文件:

touch .yarnrc

我们将针对该提交使用交互式变基,在这种编辑根提交的特殊情况下,需要使用 --root 选项:

git rebase -i --root

这将打开编辑器,按时间顺序显示提交:

pick 0beebfb Add package.json
pick 9ed001a Add README
pick 11221d4 Add .gitignore
pick 665034d Update README with getting started instructions
pick ba88fb0 Add name and author to package.json

我们需要做的就是为该列表中的第一个提交将 pick 替换为 edit :

edit 0beebfb Add package.json
pick 9ed001a Add README
pick 11221d4 Add .gitignore
pick 665034d Update README with getting started instructions
pick ba88fb0 Add name and author to package.json

关闭并保存文件,应该从 git 中看到这条消息:

Stopped at 0beebfb... Add package.json
You can amend the commit now, withgit commit --amendOnce you are satisfied with your changes, rungit rebase --continue

下面来运行这两个命令:

git add .yarnrc && git commit --amend

现在只需要修改提交,编辑器应如下所示:

Add package.json# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# Date:      Sun Oct 11 08:25:57 2020 -0400
#
# interactive rebase in progress; onto 666364d
# Last command done (1 command done):
#    edit 0beebfb Add package.json
# Next commands to do (4 remaining commands):
#    pick 9ed001a Add README
#    pick 11221d4 Add .gitignore
# You are currently editing a commit while rebasing branch 'master' on '666364d'.
#
#
# Initial commit
#
# Changes to be committed:
#       new file:   .yarnrc
#       new file:   package.json
#

        现在将该消息更改为 Initialize npm package 保存并退出。现在,根据 git 的建议,需要继续 rebase:

git rebase --continue

现在,提交历史现在看起来像这样:

* 436e421 - (HEAD -> master) Add name and author to package.json (6 seconds ago) <AleksandrHovhannisyan>
* beb7c13 - Update README with getting started instructions (6 seconds ago) <AleksandrHovhannisyan>
* 1c75f66 - Add .gitignore (6 seconds ago) <AleksandrHovhannisyan>
* 69c997b - Add README (6 seconds ago) <AleksandrHovhannisyan>
* 36210ec - Initialize npm package (56 seconds ago) <AleksandrHovhannisyan>

压缩

        压缩可以将 n 个提交合并为一个,使提交历史更加紧凑。如果一个功能分支引入大量提交,并且只希望该功能在历史记录中表示为单个提交(称为 squash-and-rebase 工作流),这有时很有用。但是,如果将来需要,将无法恢复或修改旧的提交,这在某些情况下可能是不可取的。

同样,作为参考,我们有如下提交历史:

* 436e421 - (HEAD -> master) Add name and author to package.json (6 seconds ago) <AleksandrHovhannisyan>
* beb7c13 - Update README with getting started instructions (6 seconds ago) <AleksandrHovhannisyan>
* 1c75f66 - Add .gitignore (6 seconds ago) <AleksandrHovhannisyan>
* 69c997b - Add README (6 seconds ago) <AleksandrHovhannisyan>
* 36210ec - Initialize npm package (56 seconds ago) <AleksandrHovhannisyan>

下面来创建一个功能分支并添加一些提交:

git checkout -b feature && \
touch file1 && git add . && git commit -m "Add file1" && \
touch file2 && git add . && git commit -m "Add file2" && \
touch file3 && git add . && git commit -m "Add file3"

现在提交历史如下:

* 6afa3ac - (HEAD -> feature) Add file3 (4 seconds ago) <AleksandrHovhannisyan>
* c16cbc6 - Add file2 (4 seconds ago) <AleksandrHovhannisyan>
* 0832e96 - Add file1 (4 seconds ago) <AleksandrHovhannisyan>
* 436e421 - (master) Add name and author to package.json (12 minutes ago) <AleksandrHovhannisyan>
* beb7c13 - Update README with getting started instructions (12 minutes ago) <AleksandrHovhannisyan>
* 1c75f66 - Add .gitignore (12 minutes ago) <AleksandrHovhannisyan>
* 69c997b - Add README (12 minutes ago) <AleksandrHovhannisyan>
* 36210ec - Initialize npm package (12 minutes ago) <AleksandrHovhannisyan>

我们可以使用以下命令将所有这些压缩为一个:

git rebase -i master

        这会将功能分支重新设置为 master 分支。请注意, master 是对特定提交的引用,就像其他任何提交一样:

* 436e421 - (HEAD -> master) Add name and author to package.json (6 seconds ago) <AleksandrHovhannisyan>

这样做也是一样的:

git rebase -i 436e421

无论如何,一旦运行了这些命令中的任何一个,git 就会打开编辑器:

pick 0832e96 Add file1
pick c16cbc6 Add file2
pick 6afa3ac Add file3

我们会将最后两个提交压缩到第一个提交中,所以将它们的 pick 命令更改为 squash

pick 0832e96 Add file1
squash c16cbc6 Add file2
squash 6afa3ac Add file3

保存并退出,git 将打开编辑器,通知我们将要合并三个提交:

# This is a combination of 3 commits.
# This is the 1st commit message:Add file1# This is the commit message #2:Add file2# This is the commit message #3:Add file3# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# Date:      Sun Oct 11 09:37:05 2020 -0400
#
# interactive rebase in progress; onto 436e421
# Last commands done (3 commands done):
#    squash c16cbc6 Add file2
#    squash 6afa3ac Add file3
# No commands remaining.
# You are currently rebasing branch 'feature' on '436e421'.
#
# Changes to be committed:
#       new file:   file1
#       new file:   file2
#       new file:   file3
#

        现在可以将 Add file1 更改为 Add files 1、2 和 3,或者想要的任何其他提交消息。保存并关闭文件,现在提交历史已经很紧凑了:

* b646cf6 - (HEAD -> feature) Add files 1, 2, and 3 (70 seconds ago) <AleksandrHovhannisyan>
* 436e421 - (master) Add name and author to package.json (14 minutes ago) <AleksandrHovhannisyan>
* beb7c13 - Update README with getting started instructions (14 minutes ago) <AleksandrHovhannisyan>
* 1c75f66 - Add .gitignore (14 minutes ago) <AleksandrHovhannisyan>
* 69c997b - Add README (14 minutes ago) <AleksandrHovhannisyan>
* 36210ec - Initialize npm package (15 minutes ago) <AleksandrHovhannisyan>

4. 还原提交

上面学习了两种从 git 历史记录中删除提交的方法:

  • ·在要删除的提交范围之前,将 HEAD 指针软或硬重置为提交。

  • ·对不想保留的任何提交执行交互式变基并更改 pick 为 drop

        不幸的是,这两种方法都会重写提交历史。以使用交互式变基从 master 分支中删除 .env文件为例。如果在现实中这样做,在像 master 这样的共享分支上删除提交会导致一些麻烦,团队中的每个人都必须硬重置本地的 master 分支以匹配 origin/master

        问题出现在人们正在进行功能分支上的工作时,特别是如果他们是从旧的 master 分支切出来的——删除的文件仍然存在。变基就是行不通的,因为它可能会重新引入在 master 分支上删除的文件;同样地,将 master 分支合并到功能分支中也不起作用,因为 git 没有公共历史可供解决:

fatal: refusing to merge unrelated histories

        这就是 git revert 出现的原因。与通过变基或硬/软重置删除提交不同,revert 命令创建一个新提交以撤消目标提交引入的任何更改:

git revert <hash-id>

假设在 master 分支上,想要用 beb7c13 的哈希恢复提交:

* 436e421 - (HEAD -> master) Add name and author to package.json (8 hours ago) <AleksandrHovhannisyan>
* beb7c13 - Update README with getting started instructions (8 hours ago) <AleksandrHovhannisyan>
* 1c75f66 - Add .gitignore (8 hours ago) <AleksandrHovhannisyan>
* 69c997b - Add README (8 hours ago) <AleksandrHovhannisyan>
* 36210ec - Initialize npm package (8 hours ago) <AleksandrHovhannisyan>

为此,执行以下命令:

git revert beb7c13

Git 将打开编辑器:

Revert "Update README with getting started instructions"This reverts commit beb7c132882ff1e3214dbd380514559fed0ef38f.# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# On branch master
# Changes to be committed:
#       modified:   README.md
#

保存并关闭文件,然后运行 git log 以查看历史记录:

* e1e6e06 - (HEAD -> master) Revert "Update README with getting started instructions" (58 seconds ago) <AleksandrHovhannisyan>
* 436e421 - Add name and author to package.json (8 hours ago) <AleksandrHovhannisyan>
* beb7c13 - Update README with getting started instructions (8 hours ago) <AleksandrHovhannisyan>
* 1c75f66 - Add .gitignore (8 hours ago) <AleksandrHovhannisyan>
* 69c997b - Add README (8 hours ago) <AleksandrHovhannisyan>
* 36210ec - Initialize npm package (8 hours ago) <AleksandrHovhannisyan>

        注意,原始提交仍然存在于历史记录中,并且其哈希值得以保留。唯一改变的是在分支的顶部添加了一个新的提交,以还原此前提交所引入的更改,就好像手动删除了最初引入的更改。显然,使用 git revert 比手工操作更合理。

        因此,与交互式变基或重置相比,撤销提交会引入额外的一个提交,因此会更加混乱。但这并不是非常重要的问题。而且,好处在于,它不会破坏公共分支。

5. 签出文件

git checkout 命令是另一种撤消 git 更改的基本方法。它有三个目的:

  • ·创建新分支:git checkout -b <newBranch>

  • ·切换到分支或提交:git checkout <existingBranch>

  • ·恢复不同版本的文件。

        这里我们将重点关注第三个目的。如果对本地文件进行了未暂存的更改,则可以使用 checkout 命令轻松撤消这些更改:

git checkout <pathspec>

        这里,<pathspec> 可以是任何有效的路径说明符,例如:. 对于当前目录、path/to/filefile.extension,甚至是正则表达式。

        这将清除对指定文件的所有未暂存更改并恢复当前分支的文件的未修改版本。此命令不会影响暂存文件——只会清除未暂存的更改。

        例如,如果想清除当前目录中所有未暂存的更改并从头开始,最简单的方法是使用 git checkout 命令和 . 作为路径规范:

git checkout .

        我们也可以使用 git checkout 来恢复文件的本地或远程版本。例如,可以签出远程 master分支上的某个文件:

git checkout origin/master -- <pathspec>

        这个命令的作用是将远程分支 origin/master 上指定的文件 <pathspec> 签出到本地分支上。该命令会用远程分支上的文件覆盖本地分支上的同名文件,即用远程分支的版本替换本地分支的版本,从而确保本地分支与远程分支保持同步。

同样,也可以签出另一个本地分支上的某个文件:

git checkout localBranchName -- <pathspec>

6. 使用 Git Reflog

        可以将 reflog 视为 git 的 git — 就像一个内部记录保存系统,可以跟踪大部分操作。

        reflog 代表“参考日志”:HEAD 指针随时间的不同状态的一系列快照。这意味着任何时候引入、删除或修改提交,或者签出新分支,或者重写旧提交的哈希,这些更改都将记录在 reflog 中。我们将能够回到过去撤消可能不需要的更改,即使它们看似不可逆转。

查看 Git 仓库的 reflog 的方式如下:

git reflog

例如,在功能分支上可以签出一个新分支,git 将记录该活动:

git checkout -b feature2

Reflog:

b646cf6 (HEAD -> feature2, origin/feature, feature) HEAD@{0}: checkout: moving from feature to feature2

这是因为 HEAD 指针从功能分支的首端重定向到新分支 feature2 的首端。

如果深入挖掘 reflog,还可以查看本文中的所有更改:

b646cf6 (HEAD -> feature2, origin/feature, feature) HEAD@{0}: checkout: moving from feature to feature2
b646cf6 (HEAD -> feature2, origin/feature, feature) HEAD@{1}: rebase -i (finish): returning to refs/heads/feature
b646cf6 (HEAD -> feature2, origin/feature, feature) HEAD@{2}: rebase -i (squash): Add files 1, 2, and 3
f3def0a HEAD@{3}: rebase -i (squash): # This is a combination of 2 commits.
0832e96 HEAD@{4}: rebase -i (start): checkout 436e421
6afa3ac HEAD@{5}: commit: Add file3
c16cbc6 HEAD@{6}: commit: Add file2
0832e96 HEAD@{7}: commit: Add file1
436e421 (master) HEAD@{8}: checkout: moving from master to feature
436e421 (master) HEAD@{9}: rebase -i (finish): returning to refs/heads/master
436e421 (master) HEAD@{10}: rebase -i (pick): Add name and author to package.json
beb7c13 HEAD@{11}: rebase -i (pick): Update README with getting started instructions
1c75f66 HEAD@{12}: rebase -i (pick): Add .gitignore
69c997b HEAD@{13}: rebase -i (pick): Add README
36210ec HEAD@{14}: commit (amend): Initialize npm package
04ba759 HEAD@{15}: rebase -i (edit): Add package.json
2bef9d4 HEAD@{16}: rebase -i (edit): Add package.json
666364d HEAD@{17}: rebase -i (start): checkout 666364da6703fc41e23515b1777de5ac84c8ad5e
ba88fb0 HEAD@{18}: rebase -i (finish): returning to refs/heads/master
ba88fb0 HEAD@{19}: rebase -i (reword): Add name and author to package.json
665034d HEAD@{20}: rebase -i (reword): Update README with getting started instructions
74dab36 HEAD@{21}: rebase -i: fast-forward
11221d4 HEAD@{22}: rebase -i (start): checkout HEAD~2
094f8cb HEAD@{23}: commit: Do more stuff
74dab36 HEAD@{24}: commit: Do something idk
11221d4 HEAD@{25}: rebase -i (finish): returning to refs/heads/master
11221d4 HEAD@{26}: rebase -i (pick): Add .gitignore
9ed001a HEAD@{27}: rebase -i (pick): Add README
0beebfb HEAD@{28}: rebase -i (start): checkout 2beb7c7^
7598875 HEAD@{29}: reset: moving to HEAD~1
b494f6f HEAD@{30}: commit: Add a file
7598875 HEAD@{31}: commit (amend): Add .gitignore
4753e23 HEAD@{32}: commit: Add .gitignore
893d18d HEAD@{33}: commit: Add README
2beb7c7 HEAD@{34}: commit: Add .env
0beebfb HEAD@{35}: commit (initial): Add package.json

可以通过检查这些提交哈希来快速查看这些状态中的任何一个:

git checkout <hash-id>

或者,可以将分支重置为历史记录中的某个点:

git reset --soft 7598875

这将当前的 feature2 分支软重置为以下历史:

* 7598875 - (HEAD -> feature2) Add .gitignore (84 minutes ago) <AleksandrHovhannisyan>
* 893d18d - Add README (85 minutes ago) <AleksandrHovhannisyan>
* 2beb7c7 - Add .env (85 minutes ago) <AleksandrHovhannisyan>
* 0beebfb - Add package.json (85 minutes ago) <AleksandrHovhannisyan>

甚至可以运行另一个 reflog 来查看更改:

7598875 (HEAD -> feature2) HEAD@{0}: reset: moving to 7598875

        如果不希望本地分支被覆盖,可以再次运行 reflog 命令并将分支重置到在执行该操作之前的 HEAD

git reset --hard b646cf6

这就回到了之前的状态:

* b646cf6 - (HEAD -> feature2, origin/feature, feature) Add files 1, 2, and 3 (13 minutes ago) <AleksandrHovhannisyan>
* 436e421 - (master) Add name and author to package.json (26 minutes ago) <AleksandrHovhannisyan>
* beb7c13 - Update README with getting started instructions (26 minutes ago) <AleksandrHovhannisyan>
* 1c75f66 - Add .gitignore (26 minutes ago) <AleksandrHovhannisyan>
* 69c997b - Add README (26 minutes ago) <AleksandrHovhannisyan>
* 36210ec - Initialize npm package (27 minutes ago) <AleksandrHovhannisyan>

        Git 的 reflog 命令很有用,以防进行硬重置并丢失所有工作,只需查看 reflog 并重置到进行硬重置之前的点,就轻松搞定!

最后,如果出于某种原因想清理 reflog,可以使用以下方法从中删除行:

git reflog delete HEAD@{n}

        将 n 替换为要从 reflog 中删除的任何行。HEAD@{0} 指的是 reflog 中的最新行,HEAD@{1} 指的是之前的一行,依此类推。

参考:https://www.aleksandrhovhannisyan.com/blog/undoing-changes-in-git/

相关文章:

在 Git 中撤消更改的 6 种方法!

目录 1. 修改最近的提交 2. 将分支重置为较旧的提交 硬重置 软重置分支 创建备份分支 3. 交互式变基 删除旧提交 改写提交消息 编辑旧提交 压缩 4. 还原提交 5. 签出文件 6. 使用 Git Reflog 当使用 Git 进行项目代码管理时&#xff0c;难免会出现一些错误操作或需…...

LiveGBS国标GB/T28181国标平台功能-电子地图移动位置订阅mobileposition地图定位GPS轨迹坐标位置获取redis获取位置

LiveGBS国标GB/T28181国标平台功能-电子地图移动位置订阅mobileposition地图定位GPS轨迹坐标位置获取redis获取位置 1、位置订阅1.1、国标设备编辑1.2、选择设备开启位置订阅1.3、全局开启位置订阅1.4、通过目录订阅获取位置(少数情况) 2、经纬度信息查询2.1、访问接口获取2.1.…...

编程(38)----------计算机的部分原理

本篇主要总结一些计算机的理论部分. 计算机在发展历程中,无论是最早的巨无霸机器,还是现在小到可以拿在手中的掌机.只要其本质上是计算机,在最基础的结构上,都是以冯诺依曼体系所构建的. 冯诺依曼体系大致将计算机分为几个最重要的部分:输入,输出,中央处理器,存储设备.也就是…...

若依框架快速搭建(二)

目录 数据库设计功能模块设计XXX信息管理xxx查询xxx添加xxx删除xxx修改xxx导出 功能模块实现运行数据库自动代码生成在IDEA中找到RuoYi-generator&#xff0c;修改配置运行前后端项目&#xff0c;在网页中找到代码生成模块导入表后点击确定&#xff0c;序号前打勾&#xff0c;再…...

为建筑物的供暖系统实施MPC控制器的小型项目(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…...

【概率论】中心极限定理(二)

文章目录 主观题主观题 每袋味精的净重为随机变量,平均重量为 100 克,标准差为 10 克。一箱内装 200 袋味精,求一箱味精的净重大于 20500 克的概率? 解: ① E ( X i ) = 100 , D ( X i ) = 1 0 2...

Blender UV展开流程

目录 1. UV1.1 blender默认物体1.2 创建物体1.3 UV参考图1.4 标记缝合边1.5 UV拉伸1.6 孤岛模式 1. UV 1.1 blender默认物体 默认物体已经自动生成UV 在UV编辑工作区&#xff0c;编辑模式&#xff0c;全选物体在左边自动展开UV 在物体数据属性-UV贴图-存在默认的UV贴图&#…...

Flutter 笔记 | Flutter 核心原理(二)关键类和启动流程

Widget、Element、BuildContext 和 RenderObject Widget Widget关键类及其子类继承关系如图所示&#xff1a; 其中&#xff0c;Widget是Widget Tree所有节点的基类。Widget的子类主要分为3类&#xff1a; 第1类是RenderObjectWidget的子类&#xff0c;具体来说又分为SingleCh…...

Android:主题切换

一.概述 正在开发的应用做了一版新UI&#xff0c;原打算将新版UI按项目名做成资源包&#xff0c;再在build.gradle里productFlavors{ }多渠道打包实现 但被告知新旧两个项目共用一个分支&#xff0c;那就做成两个主题(Theme1/Theme2)来适配了 如果只是变更UI&#xff0c;做成…...

terminalworks ASP.NET Core PDF 浏览器-Crack

ASP.NET Core 的 PDF 查看器 terminalworks在 ASP.NET Core 网页或应用程序中添加可靠的 PDF 查看器的简单方法。 我们的 Web PDF 查看器基于经过验证和测试的 Mozilla PdfJS 解决方案&#xff0c;该解决方案在 Firefox 中用作默认 PDF 查看器。我们专门设计了我们的查看器&…...

Rust每日一练(Leetday0020) 最后单词的长度、螺旋矩阵II、排列序列

目录 58. 最后一个单词的长度 Length of Last Word &#x1f31f; 59. 螺旋矩阵 II Spiral Matrix II &#x1f31f;&#x1f31f; 60. 排列序列 Permutation Sequence &#x1f31f;&#x1f31f;&#x1f31f; &#x1f31f; 每日一练刷题专栏 &#x1f31f; Rust每日…...

短视频矩阵源码如何做应用编程?

短视频矩阵源码&#xff0c; 短视频矩阵系统技术文档&#xff1a; 可以采用电子文档或者纸质文档的形式交付&#xff0c;具体取决于需求方的要求。电子文档可以通过电子邮件、远程指导交付云存储等方式进行传输、 短视频矩阵{seo}源码是指将抖音平台上的视频资源进行筛选、排…...

【运维知识进阶篇】Ansible实现一套完整LNMP架构

前面介绍了PlayBook怎么写服务部署&#xff0c;把服务部署上后&#xff0c;我们来用Ansible来部署项目&#xff0c;实现一套完整的LNMP架构。我们部署wordpress、wecenter、phpshe、phpmyadmin这四个项目。将其所有的剧本都写入lnmp.yml中&#xff0c;相关备份数据都放入root/a…...

Spring Boot 自动配置一篇概览

一、什么是自动配置 bean 自动配置类通过添加 AutoConfiguration 注解实现。 因为 AutoConfiguration 注解本身是以 Configuration 注解的&#xff0c;所以自动配置类可以算是一个标准的基于 Configuration 注解的类。 Conditional 注解可以用于声明自动配置启用条件&#x…...

深入理解设计原则之接口隔离原则(ISP)【软件架构设计】

系列文章目录 C高性能优化编程系列 深入理解软件架构设计系列 深入理解设计模式系列 高级C并发线程编程 LSP&#xff1a;接口隔离原则 系列文章目录1、接口隔离原则的定义和解读2、案例解读3、如何判断一个接口是否符合接口隔离原则&#xff1f;小结 1、接口隔离原则的定义和…...

IMX6ULL裸机篇之I2C实验主控代码说明二

一. I2C实验 I2C实验内容&#xff1a; 学习如何使用 I.MX6U 的 I2C 接口来驱动 AP3216C&#xff0c;读取 AP3216C 的传感器数据。 I2C读写数据时序图&#xff1a; I2C写数据时序图如下&#xff1a; I2C读数据时序图如下&#xff1a; 二. I2C主控读写时序 1. 读数据与写数…...

【计算机组成原理与体系结构】数据的表示与运算

目录 一、进位计数制 二、信息编码 三、定点数数据表示 四、校验码 五、定点数补码加减运算 六、标志位的生成 七、定点数的移位运算 八、定点数的乘除运算 九、浮点数的表示 十、浮点数的运算 一、进位计数制 整数部分&#xff1a; 二进制、八进制、十六进制 --…...

如何入门编程

随着信息技术的快速发展&#xff0c;编程已经成为一个越来越重要的技能。那么&#xff0c;我们该如何入门编程呢&#xff1f;欢迎大家积极讨论 一、自学编程需要注意什么&#xff1f; 对于我个人的理解&#xff0c;其实自学编程最重要的就是兴趣。你得培养编程兴趣。 所以在学…...

SQL中CONVERT转化日期函数的使用方法

SQL中CONVERT转化日期函数的使用方法 SQL中CONVERT函数最常用的是使用convert转化长日期为短日期&#xff0c;如果只要取yyyy-mm-dd格式时间, 就可以用convert(nvarchar(10),field,120) 120 是格式代码, nvarchar(10) 是指取出前10位字符. 例如 SELECT CONVERT(nvarchar(10),…...

SpringBoot2-核心技术(一)

SpringBoot2-核心技术&#xff08;一&#xff09; 了解SpringBoot配置文件的使用 文章目录 SpringBoot2-核心技术&#xff08;一&#xff09;了解SpringBoot配置文件的使用一、文件类型1. properties2. yaml 二、yaml的基本使用1. 基本语法2. 数据类型2.1 字面量 2.2 对象2.3 …...

mac host学习

参考&#xff1a; SSH中known_hosts文件作用和常见问题及解决方法 https://blog.csdn.net/luduoyuan/article/details/130070120在 Mac 上更改 DNS 设置 https://support.apple.com/zh-cn/guide/mac-help/mh14127/mac mac中有时候你输入的域名&#xff0c;但会跳转到与期望ip不…...

Java之~指定String日期时间,5分钟一截取时间

// 截取5分钟时间Testpublic void timeCutForDay() throws ParseException {String startTime "2023-03-28 09:16:03";String endTime "2023-03-31 23:59:59";SimpleDateFormat dateFormat new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");D…...

【chatGPT4结对编程】chatGPT4教我做图像分类

开始接触深度学习 大语言模型火了之后&#xff0c;我也想过是否要加入深度学习的行业当中来&#xff0c;一开始的想法就是AI大模型肯定会被各大厂垄断&#xff0c;我们作为普通应用型软件工程师直接调用api就完事&#xff0c;另外对自己的学历也自卑(刚刚够线的二本&#xff0…...

Different romantic

001 他暗恋上我们班上的一个女生。 He has a crush on a girl in our class. crush n. 迷恋 have a crush on (someone) 暗恋&#xff08;某人&#xff09; crush 也可以指“暗恋的对象”。例如&#xff0c;“他在大学曾经暗恋过两个人”&#xff0c;英语就是He had two crushe…...

learn C++ NO.7——C/C++内存管理

引言 现在是5月30日的正午&#xff0c;图书馆里空空的&#xff0c;也许是大家都在午休&#xff0c;也许是现在37摄氏度的气温。穿着球衣的我已经汗流浃背&#xff0c;今天热火战胜了凯尔特人&#xff0c;闯入决赛。以下克上的勇气也激励着我&#xff0c;在省内垫底的大学中&am…...

SDUT数据库原理——第十章作业(参考答案)

1. 简述使用检查点方法进行数据恢复的一般步骤。 答: (1)使用检查点方法进行数据恢复,首先从重新开始文件(见P302页图10.3)中找到最后一个检查点记录在日志文件中的地址,由该地址在日志文件中找到最后一个检查点记录。 (2)由该检查点记录得到检查点建立时刻所有正在…...

My Note of Diffusion Models

Diffusion Models Links: https://theaisummer.com/diffusion-models/ Markovian Hierachical VAE rvs: data: x 0 x_{0} x0​,representation: x T x_{T} xT​ ( p ( x 0 , x 1 , ⋯ , x T ) , q ( x 1 , ⋯ , x T ∣ x 0 ) ) (p(x_0,x_1,\cdots,x_T),q(x_1,\cdots,x_{T…...

【P37】JMeter 仅一次控制器(Once Only Controller)

文章目录 一、仅一次控制器&#xff08;Once Only Controller&#xff09;参数说明二、测试计划设计2.1、测试计划一2.1、测试计划二 一、仅一次控制器&#xff08;Once Only Controller&#xff09;参数说明 可以让控制器内部的逻辑只执行一次&#xff1b;单次的范围是针对某…...

cleanmymac要不要下载装机?好不好用

当我们收到一台崭新的mac电脑&#xff0c;第一步肯定是找到一款帮助我们管理电脑运行的“电脑管家”&#xff0c;监控内存运行、智能清理系统垃圾、清理Mac大文件旧文件、消除恶意软件、快速卸载更新软件、隐私保护、监控系统运行状况等。基本在上mac电脑防护一款CleanMyMac就够…...

DNS风险分析及防护研究(五):常见的DNS威胁与防御(中科三方)

DNS是互联网运行重要的基础设施&#xff0c;在全球互联网运转中扮演重要作用。互联网中的每一次访问都开始于一次DNS查询&#xff0c;从而将人们更好辨识的域名转换为数字化的IP地址。随着互联网的快速发展以及网络技术的快速发展&#xff0c;DNS固有的缺陷逐步暴露出来&#x…...