Git更改提交的操作

无主题博客 » Git » Git更改提交的操作

git reset — 恢复历史版本

通过前面学习的操作,我们已经学会如何在实现功能后进行提交,积累提交日志作为历史记录,借此不断培育一款软件。

Git 的另一个特征便是可以灵活操作历史版本。借助分散仓库的优势,可以在不影响其他仓库的前提下对历史版本进行操作。

在这里,为了让各位熟悉对历史版本的操作,我们先恢复历史版本,创建一个名为 fix-B 的特性分支(图1)。

图1: 恢复历史,创建 fix-B 分支
201604181527

恢复到创建 feature-A 分支前

让我们先恢复到上一篇文章(Git分支操作总结)feature-A 分支创建之前,创建一个名为 fix-B 的特性分支。

要让仓库的 HEAD、暂存区、当前工作树回复道之顶状态,需要用到 git reset --hard 命令。只要提供目标时间点的hash值,就可以完全恢复至该时间点的状态。让我们执行下面的命令。

注:hash值可以通过 git log 命令查看。

$ git reset --hard 7b42faa9bc69c6c025c42273fa331963f85b4625
HEAD is now at 7b42faa Add index

我们已经成功回复道特性分支(feature-A)创建之前的状态,由于所有文件都恢复到了指定hash值对应的时间点上,README.md 文件的内容也恢复到了当前时间状态。

创建 fix-B 分支

现在我们来创建特性分支(fix-B)。

$ git checkout -b fix-B
Switched to a new branch 'fix-B'

作为这个主题的作业内容,我们在 README.md  文件中添加一行文字。

# Git 教程

 - fix-B

然后直接提交 README.md 文件。

$ git add README.md

$ git commit -m "Fix B"
[fix-B 2a31d63] Fix B
 1 file changed, 2 insertions(+)

现在的状态如图2所示。接下来我们的目标是图3中所示的状态,即主干分支合并 feature-A 分支的修改后,又合并了 fix-B 的修改。

图2:当前 fix-B 分支的状态
201604181610

图3:fix-B 分支的下一个目标
201604181623

推进至 feature-A 分支合并后的状态

首先恢复到 feature-A 分支合并后的状态。不妨称这一操作叫做“推进历史”。

git log 命令只能查看以当前状态为终点的历史日志。所以这里要是使用 git reflog 命令,查看当前仓库的操作日志。在日志中找出恢复历史之前的hash值,通过 git reset --hard 命令恢复到恢复历史前的状态。

首先执行 git reflog 命令,查看当前仓库执行过的操日志。

$ git reflog
2a31d63 HEAD@{0}: commit: Fix B
7b42faa HEAD@{1}: checkout: moving from master to fix-B
7b42faa HEAD@{2}: reset: moving to 7b42faa9bc69c6c025c42273fa331963f85b4625
fb565d4 HEAD@{3}: merge feature-A: Merge made by the 'recursive' strategy.
7b42faa HEAD@{4}: checkout: moving from feature-A to master
40a049f HEAD@{5}: commit: Add feature-A
7b42faa HEAD@{6}: checkout: moving from master to feature-A
7b42faa HEAD@{7}: commit: Add index
37f0170 HEAD@{8}: commit (initial): First commit

在日志中,我们可以看到 commitcheckoutresetmerge 等 Git 命令的执行记录。只要不进行 Git 的 GC(Garbage Collection,垃圾回收),就可以通过日志随意调取近期的历史状态,就像给时间机器指定一个时间点,在过去未来中自由穿梭一般。即便开发者错误执行了 Git 操作,基本也都可以利用 git reflog 命令恢复到原先状态。

从上面数第四行表示 feature-A 特性分支合并后的状态,对应 hash 值为 fb565d4 。我们将 HEAD、暂存区、工作树恢复到这个时间点的状态。

$ git checkout master

$ git reset --hard fb565d4
HEAD is now at fb565d4 Merge branch 'feature-A'

之前我们使用 git reset –hard 命令恢复了历史,这里有再次通过他恢复到了恢复前的历史状态。当前的状态如图4所示。

图4:恢复历史后的状态
201604181527

消除冲突

现在只要合并 fix-B 分支,就可以得到我们想要的状态。让我们赶快进行合并操作。

$ git merge --no-ff fix-B
Auto-merging README.md
CONFLICT (content): Merge conflict in README.md
Automatic merge failed; fix conflicts and then commit the result.

这时,系统告诉我们 README.md 文件发生了冲突(Conflict)。系统在合并 README.md 文件时,feature-A 分支更改的部分与本次想要合并的 fix-B 分支更改的部分发生了冲突。

不解决冲突就无法完成合并,所以我门打开 README.md 文件,解决这个冲突。

查看冲突部分并将其解决

用编辑器打开 README.md 文件,就会发现其内容变成了下面这个样子。

# Git 教程

<<<<<<< HEAD
 - feature-A
=======
 - fix-B
>>>>>>> fix-B

======= 以上的部分是当前 HEAD 的内容,以下的部分是要合并的 fix-B 分支中的内容。我门在编辑器中将其改成想要的样子。

# Git 教程

 - feature-A
 - fix-B

如上所示,本次修改让 feature-Afix-B 的内容并存于文件之中。但是在实际的软件开发中,往往需要删除其中之一,所以各位在处理冲突时,无必要仔细分析冲突部分的内容后再进行修改。

提交解决后的结果

冲突解决后,执行 git add 命令与 git commit 命令。

$ git add README.md

$ git commit -m "Fix conflict"
[master 5e3a33b] Fix conflict

由于本次更改解决了冲突,所以提交信息标记为“Fix conflict”。

git commit –amend —- 修改提交信息

要修改上一条提交信息,可以使用 git commit --amend 命令。

我门将上一条提交信息记为了“Fix conflict”,但是其实是 fix-B 分支的合并,解决合并时发生的冲突之时过程之一,这样标记是在不妥。于是,我门要修改这条提交信息。

$ git commit --amend

执行上面的命令后,编辑器就会启动。

Fix conflict

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# Date: Mon Apr 18 17:29:46 2016 +0800
#
# On branch master
# Changes to be committed:
# modified: README.md
#

编辑器中显示的内容如上所示,其中包含之前的提交信息。请将提交信息的部分修改为 Merge branch ‘fix-B’,然后保存文件,关闭编辑器。

[master 906c818] Merge branch 'fix-B'
 Date: Mon Apr 18 17:29:46 2016 +0800

随后会显示上面这条结果。现在执行 git log --graph 命令,可以看到提交到日志的响应内容已被修改。

$ git log --graph
* commit 906c818ab0e4e6b234f5c9def09a0a9bafcae55e
|\ Merge: fb565d4 2a31d63
| | Author: Frank <zhwuzhuo@gmail.com>
| | Date: Mon Apr 18 17:29:46 2016 +0800
| |
| | Merge branch 'fix-B'
| |
| * commit 2a31d639d78d3f5123a63fe73ae25d22cbc6321f
| | Author: Frank <zhwuzhuo@gmail.com>
| | Date: Mon Apr 18 16:00:10 2016 +0800
| |
| | Fix B
| |
* | commit fb565d4ef3d8752989703c6429765b93999fef6c
|\ \ Merge: 7b42faa 40a049f
| |/ Author: Frank <zhwuzhuo@gmail.com>
|/| Date: Mon Apr 18 15:18:55 2016 +0800
| |
| | Merge branch 'feature-A'
| |
| * commit 40a049f709b8974fb25bc3f5396546c8f70b8180
|/ Author: Frank <zhwuzhuo@gmail.com>
| Date: Mon Apr 18 15:17:49 2016 +0800
|
| Add feature-A
|
* commit 7b42faa9bc69c6c025c42273fa331963f85b4625
| Author: Frank <zhwuzhuo@gmail.com>
| Date: Mon Apr 18 15:16:03 2016 +0800
|
| Add index
|
* commit 37f01708491d8cb2c410d2b9edc8db1bbc39bb8e
 Author: Frank <zhwuzhuo@gmail.com>
 Date: Mon Apr 18 15:14:42 2016 +0800

 First commit

git rebase -i —- 压缩历史

在合并特征分支之前,如果发现已经提交的内容中有些许拼写错误等,不妨提交一个修改,然后将这个修改包含到前一个提交中,压缩成一个历史记录。这是个会经常用到的技巧,让我门来实际操作体会一下。

创建 feature-C 分支

首先,新建一个 feature-C 特性分支。

$ git checkout -b feature-C
Switched to a new branch 'feature-C'

作为 feature-C 的功能实现,我门在 README.md 文件中添加一行文字,并且故意留下拼写错误,以便以后修正。

# Git 教程

 - feature-A
 - fix-B
 - faeture-C

提交这部分内容。这个小小的改变就没必要先执行 git add 命令在执行 git commit 命令来,我门用 git commit -am 命令来一次完成两步操作。

$ git commit -am "Add feature-C"
[feature-C fc16a63] Add feature-C
 1 file changed, 1 insertion(+)

修正拼写错误

现在来修正刚才预留的拼写错误。请各位自行修正 README.md 文件的内容,修正后的差别如下所示。

$ git diff
diff --git a/README.md b/README.md
index 11516d7..b95b1e4 100644
--- a/README.md
+++ b/README.md
@@ -2,4 +2,4 @@

 - feature-A
 - fix-B
- - faeture-C
+ - feature-C

然后进行提交。

$ git commit -am "Fix typo"
[feature-C 6fbaf88] Fix typo
 1 file changed, 1 insertion(+), 1 deletion(-)

错字漏字等失误称作 typo,所以我门将提交信息标记为“Fix typo”。实际上,我门不希望在历史记录中看到这类提交,因为健全的历史记录并不需要她们。如果能在最初提交之前就发现并修正这些错误,也就不会出现这类提交了。

更改历史

因此,我们来更改历史。将“Fix typo” 修正的内容与之前一次的提交合并,在历史记录中合并为一次完美的提交。为此,我门要用到 git rebase 命令。

$ git rebase -i HEAD~2

用上述方式执行 git rebase 命令,可以选定当前分支中包含 HEAD(最新提交)在内的两个最新历史记录为对象,并在编辑器中打开。

pick fc16a63 Add feature-C
pick 6fbaf88 Fix typo

# Rebase 906c818..6fbaf88 onto 906c818 (2 command(s))
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out

我门将 6fbaf88 的 Fix typo 的历史记录压缩到 fc16a63 的 Add feature-C 里。按照下图所示,将 6fbaf88 左侧的 pick 部分删除,改写为 fixup

pick fc16a63 Add feature-C
fixup 6fbaf88 Fix typo

保存编辑器里的内容,关闭编辑器。

[detached HEAD 1b18032] Add feature-C
 Date: Mon Apr 18 17:45:12 2016 +0800
 1 file changed, 1 insertion(+)
Successfully rebased and updated refs/heads/feature-C.

系统显示 rebase 成功。也就是以下面这两个提交作业对象,将“Fix typo”的内容合并到了上一个提交“Add feature-C”中,改写成了一个新的提交。

  • fc16a63 Add feature-C
  • 6fbaf88 Fix typo

现在再查看提交日志时会发现 Add feature-C 的hash值已经不是 fc16a63 了,这证明提交已经被更改。

$ git log --graph
* commit 1b18032f91a7e6c551473ef35ed7d8b145b505f7
| Author: Frank <zhwuzhuo@gmail.com>
| Date: Mon Apr 18 17:45:12 2016 +0800
|
| Add feature-C
|
* commit 906c818ab0e4e6b234f5c9def09a0a9bafcae55e
|\ Merge: fb565d4 2a31d63
| | Author: Frank <zhwuzhuo@gmail.com>
| | Date: Mon Apr 18 17:29:46 2016 +0800
| |
| | Merge branch 'fix-B'
| |
| * commit 2a31d639d78d3f5123a63fe73ae25d22cbc6321f
| | Author: Frank <zhwuzhuo@gmail.com>
| | Date: Mon Apr 18 16:00:10 2016 +0800
| |
| | Fix B
......省略......

这样一来,Fix typo 就从历史中被抹去,也就相当于 Add feature-C 中从来没有出现过拼写错误。这算是一种良性的历史改写。

合并至 master 分支

feature-C 分支的使命告一段落,我门将它与 master 分支合并。

$ git checkout master
Switched to branch 'master'

$ git merge --no-ff feature-C
Merge made by the 'recursive' strategy.
 README.md | 1 +
 1 file changed, 1 insertion(+)

master 分支整合了 feature-C 分支。开发进展顺利。


文章内容参考:《GitHub入门与实践》

《Git更改提交的操作》有一个想法

发表评论

电子邮件地址不会被公开。 必填项已用*标注