【Git零基础教程】(7)改写历史(Rewriting History)

来源:互联网 发布:java 标记方法过时 编辑:程序博客网 时间:2024/05/22 16:04

第一种最简单的是使用

git  commit  --amend

它的作用是使本次commit和上一次commit合并在一块。也可以用它只修改上一次提交的log。


用上一章我们讲过的rebase可以实现很多需求:

git  rebase  -i  HEAD~3

我们先不考虑-i
我们先回顾一下git rebase
我们知道git rebase <branch>的意思是将当前分支rebase到<branch>,它其实相当于git rebase <branch> HEAD,HEAD就是当前所在的那个commit(或者说当前分支)。它实际上的行为是将<branch>..HEAD rebase 到 <branch>(注:A .. B在Git中的意思是B中含有的而A中不含有的那些commits,如下图)。

对于git rebase HEAD~3
也就是说它等于git rebase HEAD~3 HEAD
(注1:这里HEAD~3的意思是HEAD往回走3个commits的那个commit,如下图中的710f0f8)
(注2:Git中能用<branch>的地方都是可以用<commit>(如710f0f8或HEAD~3)来替代的,它们本质上都起了一个指示位置的作用)
所以它的实际行为是将 HEAD~3 .. HEAD rebase 到 HEAD~3HEAD~3..HEAD的范围如下图所示:

这里写图片描述

所以这句命令的效果是没有任何变化。

但是如果我们加上 -i,
则会进入一个文本编辑界面:
这里写图片描述

上面的部分是需要rebase的commits的列表,这里commits列出的顺序与git log的顺序相反(见下图)。它会从列表的上到下依次处理每个commit,也就是从时间更远的处理到时间更近的。
下面的部分是一些注释,列出了可用的命令。

这里写图片描述

下面我们来介绍一下常用操作:

1.pick

pick是默认操作,即使用该commit而不做任何操作。如果所有commits都为pick即为正常的rebase。

2.reorder

reorder不是一个命令,而是指手动将列表中的commits的行的顺序调换。rebase会按照从上到下的顺序依次rebase每一个commit。

3.edit

它的作用是处理完指定commit之后暂停。如:

这里写图片描述

表明在处理完f7f3f6d后会暂停rebase。它停下来的时候是f7f3f6d已经commit之后(并且工作目录是干净的)。此时可以使用–amend来修改commit的log:
git commit --amend

然后使用
git rebase --continue
继续下面的操作。

2.squash

fixup?

它的作用是将该commit并入它上面的commit中,如上图这样修改的话,会将这三个commits合并成一个。
(下一章会介绍用reset命令来实现squash,虽然没这个功能强)

3.split

比如我们想将一个很多内容的commit拆分成几个commits:

这里写图片描述

等停在310154e的时候,使用如下命令:

这里写图片描述

就可以将它拆分成两个commits。
git reset的用法下一章会介绍。


git filter-branch

我们经常会遇到这样的问题,不小心将一个大文件commit甚至push了,这样会导致git仓库的体积变得很大,同时其他协作者fetch的时候将会被迫下载该文件。
即使你之后删除了该文件,因为git的机制,它仍然存在于git仓库中。

这时候我们需要用到该工具:

git filter-branch –tree-filter ‘rm -f movie.mp4’ – –all

使用该命令来遍历所有的commits,依次清除该文件。
然而你会发现本地的git仓库还是老样子,需要使用如下命令来彻底清除它:

git push origin –force –all
git push origin –force –tags
git for-each-ref –format=’delete %(refname)’ refs/original | git update-ref –stdin
git reflog expire –expire=now –all
git gc –prune=now
git count-objects -v