前言

对于最近一次提交的commit的修改可以使用git commit --amend来进行,而对历史版本的commit修改则需要通过git rebase -i来进行。

git rebase -i命令拆解:

  • rebase: 表示基于某个父commit修改其下的子commit
  • -i: 表示交互

比如,我们需要把第二个commit的信息Second commit来进行修改。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 G:\mygitea\GitLearn\learn03   master 
$ git log
commit c1be25756d4945790ee3a930284e5d7448b12fee (HEAD -> master)
Author: GitHub Whiteco-okie <GitHub 1961663351@qq.com>
Date: Wed May 11 09:04:28 2022 +0800

Third commit

commit dab1693e9e6653446d1c2acd636b17b655b56ee9
Author: GitHub Whiteco-okie <GitHub 1961663351@qq.com>
Date: Wed May 11 09:03:42 2022 +0800

Second commit

commit 8653890b85a24cf2f9dd5b95dad6a6de413b4223
Author: GitHub Whiteco-okie <GitHub 1961663351@qq.com>
Date: Wed May 11 09:02:40 2022 +0800

First commit

G:\mygitea\GitLearn\learn03   master 
$ git rebase -i 8653890b85a24c # 选择要变更的commit的父节点id

此时会跳到交互信息页面,Commands标题下展示了交互式页面的命令,我们参考命令对commit进行修改。比如这里我们只是想修改commit dab1693e的message,则需要使用reword命令(简写 r)来修改。

修改后:

保存退出,会跳到需要修改分支的交互式信息页面,对提交信息进行修改:

保存退出,可以看到修改后第二个分支的message变了,同时其commit id也跟着改变了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[detached HEAD c18b52f] The Second commit # 使用到了分离头指针
Date: Wed May 11 09:03:42 2022 +0800
1 file changed, 1 insertion(+)
Successfully rebased and updated refs/heads/master. # 这里分了两个步骤,首先rebase,接着更新头指针信息

G:\mygitea\GitLearn\learn03   master 
$ git log
commit cca417a0c2168f9906f46b0bcfb23d7c8d7d6bd2 (HEAD -> master)
Author: GitHub Whiteco-okie <GitHub 1961663351@qq.com>
Date: Wed May 11 09:04:28 2022 +0800

Third commit

commit c18b52fcb9900973d809f4c3def25ded11cceea4
Author: GitHub Whiteco-okie <GitHub 1961663351@qq.com>
Date: Wed May 11 09:03:42 2022 +0800

The Second commit

commit 8653890b85a24cf2f9dd5b95dad6a6de413b4223
Author: GitHub Whiteco-okie <GitHub 1961663351@qq.com>
Date: Wed May 11 09:02:40 2022 +0800

First commit

原理解析

rebase操作是基于分离头指针进行的。

  1. 首先git对要修改的commit分离头指针;
  2. 接着对分离头指针的commit上做调整;
  3. 调整后将最新的commit用一个指针指向它;—updated refs/heads/master(即当前工作的master分支不指向原先的c1be25了,而是新的cca417,但是文件内容没有变化)
    就是说commit对应的树,树对应的对象blob没有发生变化。

注意:这里的rebase操作仅限于在自己当前的未集成到团队的分支上进行,如果是以及集成到了团队的分支则无法进行此操作。

即当前我们讲的内容都是基于在个人的分支上的操作,还未涉及到团队集成的分支操作内容。

总结

Q1:那如果要更改的是第一次提交,他的父节点是谁呢?

搜索工具可以根据 “how to rebase the oldest commit” 可以搜到 how do i git rebase the first commit, https://stackoverflow.com/questions/22992543/how-do-i-git-rebase-the-first-commit 里面告诉我们执行 “git rebase -i —root” 即可

Q2:方便说下,为什么 rebase -i git 会分离头指针,git 这么设计的目的是什么呢? 谢谢老师

git rebase工作的过程中,就是用了分离头指针。rebase意味着基于新base的commit来变更部分commits。它处理的时候,把HEAD指向base的commit,此时如果该commit没有对应branch,就处于分离头指针的状态,然后重新一个一个生成新的commit,当rebase创建完最后一个commit后,结束分离头状态,Git让变完基的分支名指向HEAD。 后续课程我们再把这个过程通过详细的图示给大家展示一下。这个阶段不妨先尝试用一下。

Q3:我修改了first_commit的message,咋second_commit, third_commit, fourth_commit的commit hash都变了呢

很好的发现! 那是因为 message 也是commit的属性,是确定commit的hash值的一个因子。其次,second_commit 有个 parent 的属性,parent的内容发生了变化,因此,second_commit 的hash也变了。 以此类推,后面的commit也发生了变化。

Q4:可以看到修改了老message后,自父亲commit以下,所有commit都变了,这是为什么,为什么不只修改2个commit,就像链表操作一样,只需要修改当前节点和当前节点的子节点就可以了,是因为git不允许修改commit对象吗?只允许新建commit对象。

blob是只看内容的,两个文件如果内容相同,对应的blob是相同的,即使这俩文件在不同的git仓库。 但commit还包括commit的message,作者,变更时间,父亲等属性,这些🀄️的一个发生变化了,在git眼里就是不同的commit。 如果前3个commit的message变了,前3哥commit就变了,它一变,前2个commit的parent就变了,因此前2个commit也要变,依此类推

Q5:听完之后有个问题就是被变commit的父级commit是怎么识别出来的,就比如视频里的Add refering projects 的父级commit为什么是 Add js这个commit

这个版本树如果是自己创建的,我们肯定事先知道哪个commit先建,哪个commit后建,由此知道哪个是parent。这是一种方法。 其他用git命令的方式也很多。 比如: git show —pretty=format:%P HEAD 这个命令可以返回HEAD指针的父commit。 再比如:gitk 打开的界面上直接显示Parent

初次听老师讲述变基(base),还以为老师发错音了,以为修改message就是对它进行编辑,结果原来是rebase,对基(base)进行变更: 在变基的时候,因为新的base的commit没有基于任何一个分支,所以处于分离头指针状态,git接下来会在我们修改message那个commit往后依次对后面的commit修改hash值但不更改commit的文件内容,让后续的commit的hash值都是基于修改mseeage之前的那个commit依次更新!