git工具之重写历史
很多时候,在你准备将本地commit对象push到远程站点与别人共享时,你想要修改你的提交历史,这在GIT中是可以的。上一篇你可以在提交暂存区前决定哪些文件分拆到哪次提交,这节主要讲解如何修改commit的对象信息,包括修改提交次序、改变说明、修改提交中包含的文件,将提交归并、拆分或完全删除。
改变最近一次提交
1.git commit - - amend 此命令会带你进入文本编辑器,你在文本编辑器修改掉最后一次提交的commit信息时退出保存即可。
2.如下是在修改最后一次提交对象包含的文件:新增一个,你只需要正常git add 然后运行git commit --amend,你发现newfile被添加进来了
特别注意:这样会生成一个新的hash值,所以不要在你最近一次提交被推送到远程还去修正他。
3.如果只是修改最近一笔提交的作者,下边的命令会用到
git commit --amend --author='Name <email>'
修改多个提交说明
要修改提交的更早历史,你可以通过交互式的rebase工具来实现停留在每一次提交后,去修改历史某笔提交的提交说明、增加或删除文件。git rebase -i 交互式的运行rebase,这里你需要传参指明你需要重写的提交的回溯深度。
例如:现在需要修改最近三次的提交说明,或者其中任意一次,那么你需要传参HEAD~3(因为你想修改最近三次提交;但是请记住你事实上所指的是四次提交之前,即你想修改的提交的父提交)
git rebase -i HEAD~3 运行这个命令会为你的文本编辑器提供一个提交列表,注意这个提交顺序与你的log命令看到的是相反的,交互式的rebase给你一个即将运行的脚本,它会从你在命令行上指明的提交开始(HEAD~3),然后自上而下重播每次提交里引入的变更。
将你想修改的提交的pick改为edit保存退出,GIT会倒回至列表中最后一次提交,并提示你运行的命令git commit --amend ,然后运行git rebase --continue,这个命令会自动应用其他两次提交,你就完成任务了。
如果你将更多行的 pick 改为 edit ,你就能对你想修改的提交重复上述两步。Git每次都会停下,让你修正提交,完成后继续运行。
特别注意:此命令无论你是否修改说明,HEAD~3..HEAD
范围内的每一次提交都会被重写,也就说会生成新的hash值,切记不要涵盖你已经推送到中心服务器的提交。
重排提交
如果你想删除前第三次的提交,并将前第二次与最近一次调整顺序,那么如下图修改保存退出。
保存退出编辑器后,git将分支倒回到这些提交的额父提交,应用5eb250c,然后应用0ac7fe6,然后停止。
特别注意:此命令无论你是否修改说明,HEAD~3..HEAD范围内的每一次提交都会被重写,也就说会生成新的hash值,切记不要涵盖你已经推送到中心服务器的提交。
压制(squashing)提交
交互式的rebase工具还可以将多笔提交压缩为一次提交。
将pick改为squash,git会应用squash变更及他之前的变更并将提交说明合在一起,生成一个新的提交去覆盖之前的n次提交。如下图就是将三次提交压为一条。
当你保存并退出编辑器,Git 会应用全部三次变更然后将你送回编辑器来归并三次提交说明,保存退出查看合并提交日志如下:
特别注意:此命令无论你是否修改说明,HEAD~3..HEAD范围内的每一次提交都会被重写,也就说会生成新的hash值,切记不要涵盖你已经推送到中心服务器的提交。
拆分提交
拆分提交就是撤销一次提交,然后多次部分地暂存或提交直到结束。
例如:在最近三笔提交中,你想要将cdbb915的这条记录分开为“add file2”与“add file3”两条。
1.运行git rebase -i HEAD~3,将cdbb915前的pick改为‘edit’,保存退出。 Git 倒回到列表中第一次提交的父提交,应用第一次提交7132168,应用第二次提交,然后将你带入控制台。
2.用git reset HEAD^将cdbb915进行一次混合的重置,这将撤销此笔提交,并且将修改的文件撤回(可以运行git status查看到文件已经在工作区)。
3.重新添加文件file2为一次提交,再添加file3为一次提交
4.运行git rebase --continue结束,查看近四次提交可以看到中间一次已经被拆分为两条
特别注意:此命令无论你是否修改说明,HEAD~3..HEAD范围内的每一次提交都会被重写,也就说会生成新的hash值,切记不要涵盖你已经推送到中心服务器的提交。
简单延伸核弹级选项(filter-branch),尽量不用,全局性的历史数据修改出错了可能造成重大损失:
1.从所有提交中删除一个被误提交的文件: git filter-branch --tree-filter 'rm -f passwords.txt' HEAD
2.将一个子目录设置成项目提交根目录:git filter-branch --subdirectory-filter trunk HEAD
3.全局性的更换电子邮件新地址:
git filter-branch --commit-filter '
if [ "$GIT_AUTHOR_EMAIL" = "poa_ea_scm@99bill.com" ];
then GIT_AUTHOR_NAME="xiao.fan@99bill.com";
GIT_AUTHOR_EMAIL="xiao.fan@99bill.com";
git commit-tree "$@";
else git commit-tree "$@";
fi' HEAD