Git带着问题去实践(三)

本篇继续之前系列,实践变更、回滚提交的操作。

怎么修改最近一次提交?

假设最近完成了一次提交,但是对提交的内容以及描述的信息都不满意,那么就可以使用git commit --amend指令。

完整的格式可以是:

1
git commit -a --amend -m "描述信息"

如果不想修改描述信息,可以将-m "描述信息"更改为--no-edit

这次提交不会修改提交时间,但是是一次新的提交,之前的旧的提交会被取消。

所以一般来说,这个命令的作用有两个:

  • 如果提交后,发现有漏掉的文件没有添加,可以将其git add,将其添加进暂存区,使用该命令会替换上一次提交的结果;
  • 如果单纯想要修改上一次提交的描述信息,可以使用该命令;

git reset –soft和git reset –hard的区别是什么?

首先测试一番git reset --soft命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$git log --oneline
5100572 (HEAD -> master) text git reset soft02
ec78d25 add reset head02
a05a787 After stash02.
06961ba (branch02) Second commit
2d7fae0 First commit
$git status
On branch master
nothing to commit, working tree clean
$git reset --soft head^
$git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: text01.md

上面的结果可以看出,git reset --soft head^将当前的版本回退到上一次提交,但回退后会保留上一次提交后staged状态下的内容。

git reset --hard head^命令不会保留上一次提交后staged状态下的内容。

git reset(AKA git reset –mixed)和git reset –hard区别是什么?

这里引用《Git团队合作》中的图片:

这张图讨论的情形并不周全,比如之前第二个问题涉及的git reset --soft命令就没有出现,翻译也不精准,但是还是可以凑合的说明git reset --hardgit reset的区别。

当修改处于staged状态下的内容时,通过git reset --hard可以将staged状态下的内容清除;而通过git reset则会将staged状态下的内容转为modified状态。

当修改已经提交的内容时,git reset --hard <commit>会将当前版本切换到<commit>提交的那次版本,modified以及staged状态下的内容都清空;而git reset <commit>会回退到<commit>提交后的modified状态。

什么是变基?

首先使用git log --oneline --graph --decorate --all打印提交图查看当前的提交状况:

1
2
3
4
5
6
7
8
9
10
11
12
13
$git log --oneline --graph --decorate --all
* 5100572 (HEAD -> master) text git reset soft02
* ec78d25 add reset head02
| * d06e697 (refs/stash) On master: have modified have staged
| |\
|/ /
| * 11e6b30 index on master: a05a787 After stash02.
|/
* a05a787 After stash02.
| * 336688c (branch01) commit branch01
|/
* 06961ba (branch02) Second commit
* 2d7fae0 First commit

切换到提交ec78,创建分支rebasebranch,修改text01.md然后保存并提交,当前的提交状况变为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
* 73725f1 (HEAD -> rebasebranch) add rebasebranch
| * 5100572 (origin/master, master) text git reset soft02
|/
* ec78d25 add reset head02
| * d06e697 (refs/stash) On master: have modified have staged
| |\
|/ /
| * 11e6b30 index on master: a05a787 After stash02.
|/
* a05a787 After stash02.
| * 336688c (branch01) commit branch01
|/
* 06961ba (branch02) Second commit
* 2d7fae0 First commit

使用命令git rebase master进行变基,结果出现冲突:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
First, rewinding head to replay your work on top of it...
Applying: add rebasebranch
Using index info to reconstruct a base tree...
M text01.md
Falling back to patching base and 3-way merge...
Auto-merging text01.md
CONFLICT (content): Merge conflict in text01.md
error: Failed to merge in the changes.
Patch failed at 0001 add rebasebranch
hint: Use 'git am --show-current-patch' to see the failed patch
Resolve all conflicts manually, mark them as resolved with
"git add/rm <conflicted_files>", then run "git rebase --continue".
You can instead skip this commit: run "git rebase --skip".
To abort and get back to the state before "git rebase", run "git rebase --abort".

进入文件修改冲突,然后使用git add进行提交,继续输入命令git rebase --continue有:

1
2
3
4
5
6
7
8
9
10
11
12
13
* 050b5ea (HEAD -> rebasebranch) add rebasebranch
* 5100572 (origin/master, master) text git reset soft02
* ec78d25 add reset head02
| * d06e697 (refs/stash) On master: have modified have staged
| |\
|/ /
| * 11e6b30 index on master: a05a787 After stash02.
|/
* a05a787 After stash02.
| * 336688c (branch01) commit branch01
|/
* 06961ba (branch02) Second commit
* 2d7fae0 First commit

此时切换到master分支并运行merge命令,有:

1
2
3
4
5
6
7
8
9
10
11
12
13
* 050b5ea (HEAD -> master, rebasebranch) add rebasebranch
* 5100572 (origin/master) text git reset soft02
* ec78d25 add reset head02
| * d06e697 (refs/stash) On master: have modified have staged
| |\
|/ /
| * 11e6b30 index on master: a05a787 After stash02.
|/
* a05a787 After stash02.
| * 336688c (branch01) commit branch01
|/
* 06961ba (branch02) Second commit
* 2d7fae0 First commit

本人将每一步历史记录的提交图贴出,目的就是为了说明rebase的作用。总的来说,它和merge拥有一样的功能,但是和merge不一样的点在于,它能保持一个线性的项目提交史。

在上文中,如果要进行变基,需要首先切换到rebasebranch,然后进行git rebase master,为了简便,可以使用git rebase master rebasebranch的方法来取代。

变基遵循法则:不要对在你的仓库外有副本的分支执行变基。公共仓库通常都拥有副本,所以不要在公共仓库的分支中使用它。该条同样适用于git commit --amend以及git reset命令。

git revert如何使用?

前面提到了git reset等命令也遵循变基遵循的法则,那么如果要在公共仓库对版本进行回退,可以使用git revert命令。

执行命令git revert head得到结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Revert "add rebasebranch"
This reverts commit 050b5ea26b2594f7d78c6a9271221edf86dfe6e9.
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# On branch master
# Your branch is up to date with 'origin/master'.
#
# Changes to be committed:
# modified: text01.md
#
~
...

保存后退出,完成了一次新的提交,该提交是之前head指向的提交撤销了更改的一个版本。线性提交的各个版本罗列如下:

1
2
3
4
5
6
7
455760d (HEAD -> master) Revert "add rebasebranch"
050b5ea (origin/master, rebasebranch) add rebasebranch
5100572 text git reset soft02
ec78d25 add reset head02
a05a787 After stash02.
06961ba (branch02) Second commit
2d7fae0 First commit

可见git revert命令不会删除回退的版本,这是它和git reset最大的不同。正是因为这个特性,在公共仓库进行回退操作时优先使用git revert

查看text01.md,其内容变更为:

1
2
3
4
5
6
Test git reset --hard command.
Test amend command.
Test git reset --soft.

而之前add rebasebranch版本的提交是添加了add rebasebranch语句的,所以的确拥有回退的效果。

小结

本篇重点介绍了一些前篇没有涉及的一些git命令,并对这些命令的功能、使用场景进行了简要分析。

参考

Git团队协作

progit-zh-v2.1.1.pdf