Git保命大全

💥我不小心删除了分支 或者 删除了Stash 或者 丢失了提交(reset —hard) 怎么办!

操作前,建议备份一下当前的分支,或者切换到新的分支进行操作。

1/ 确定正确的SHA-1值

首先,你需要找到分支或者提交的SHA-1值,如果你已经知道丢失分支的SHA-1值,可跳过这个步骤。

通过git reflog命令查找对git作出过的修改,可以查询到HEAD的变更记录,如commit, rebase, checkout, reset等。示例如下:

1
2
3
4
5
6
7
8
># git reflog
a55a2725e HEAD@{846}: checkout: moving from calvinche/3650/voiceopt to calvinche/3650/bugfix
5271cecfc HEAD@{847}: commit: --story=855394891 【语音控制】语音控制功能优化
5413bd7ce HEAD@{848}: checkout: moving from calvinche/3650/bugfix to calvinche/3650/voiceopt
a55a2725e HEAD@{849}: commit: --bug=74100899 【畅听页卡】活动弹窗遮挡播放列表的问题
5413bd7ce HEAD@{850}: checkout: moving from calvinche/3650/voiceopt to calvinche/3650/bugfix
5413bd7ce HEAD@{851}: reset: moving to 5413bd7ce1a1ddfbd12406175a83c863f9d68da6
ff1200963 HEAD@{852}: commit: --story=855394891 【语音控制】语音控制功能优化

第一列就是所在提交的SHA-1值

如果通过reflog无法找到丢失的分支或者提交,那你还可以通过git fsck命令查找git数据库中所有丢失的分支信息:

1
git fsck --full --no-reflogs --unreachable --lost-found | grep commit | cut -d\  -f3 | xargs -n 1 git log -n 1 --pretty=oneline > ~/lost-found.txt

由于输出的结果通常较大,我们一般将命令的输出重定向到一个文本文件中进行查看,其输出的结果如下所示:

1
2
3
4
5
6
e005fc257bb96252e9bc84deb255bba28f829e63 --story=854880635 【技术优化】升级gradle: 修复打包无产物
b90bace85ba53c9a229f84a458935aeb6858bc6c --story=854880635 【技术优化】升级gradle: 修改build_patch失败
9b51c859899d0699a73c699fb349d8ff952cf7ae WIP on master: c5312d039 Merge branch 'calvinche/dev/car_adapt' into 'master'
aa0c44873d24bc07487e4e5b8883189573c55029 --story=64610857 【语音交互】播放控制功能:UI走查
d40e8c2ed3122a30164c82696a72bf4d5fbd8c58 --story=854880635 【技术优化】升级gradle: 修复打包无产物
ed0f98b6ebd7bb9bbb6da21467fdea2931bab598 --story=64610857 【语音交互】播放控制功能:实现语音识别和命令执行

然后我们就可以通过提交的名字或者通过git log -p <commit>查看提交的内容或git cat-file -p <commit>查看提交的信息来确定我们要恢复的提交,确定了要恢复的提交后,复制它的SHA-1值备用。

2/ 恢复

如果你要恢复的是分支,使用类似于下面的命令:

1
git checkout -b <new branch name> <SHA-1>

如果你要恢复的是提交,那么可以直接:

1
git reset --hard <SHA-1>

💥我的提交历史重复臃肿,杂乱无章怎么办!

有时候,在开发过程中,可能被产品或设计打断,或者前期考虑不够严谨导致后面补充了过多的修补提交,这时候可以通过Rebase的交互模式,对提交历史进行“美化”。

例如下面一个提交记录:

1
2
3
4
5
--story=854849047(d01f70f39)【技术优化】fresco图片库更新 hypertian* 2019-05-23 13:12
--story=64746051 【个人中心】增加开机兴趣选择修改: UI走查修改1 calvinche 2019-05-20 22:25
--story=64746051 【个人中心】增加开机兴趣选择修改: 产品补充逻辑1 calvinche 2019-05-23 15:59
--story=64746051 【个人中心】增加开机兴趣选择修改: UI走查修改2 calvinche 2019-05-21 14:50
--story=64746051 【个人中心】增加开机兴趣选择修改:产品补充逻辑2 calvinche 2019-05-23 18:25

可以看出,UI修改和逻辑修改提交了多次,这个时候如果把两个UI走查的提交合并,把两个补充逻辑的提交合并,提交历史看起来会清楚干净很多,于是,我们使用rebase操作来整理提交记录:

1/ 进入Rebase交互模式

找到要修改提交的上一条提交的SHA值,如上面例子,要对下面四个提交进行修改,则需要找到”fresco图片库更新”这个提交的SHA值:d01f70f39,然后运行:

1
git rebase -i d01f70f3

就进入到下面的页面:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
pick 39741ee1a --story=64746051 【个人中心】增加开机兴趣选择修改:  UI走查修改1
pick 8078fa8f9 --story=64746051 【个人中心】增加开机兴趣选择修改: 产品补充逻辑1
pick 6f830afa3 --story=64746051 【个人中心】增加开机兴趣选择修改: UI走查修改2
pick 5efe76189 --story=64746051 【个人中心】增加开机兴趣选择修改:产品补充逻辑2

# Rebase d01f70f39..5efe76189 onto d01f70f39 (4 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup <commit> = like "squash", but discard this commit's log message
# x, exec <command> = run command (the rest of the line) using shell
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# . create a merge commit using the original merge commit's
# . message (or the oneline, if no original merge commit was
# . specified). Use -c <commit> to reword the commit message.
#
# 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.

2/ 使用合适的命令对提交记录进行修改

每次提交按照时间升序排列,也就是最上面的提交是最早的提交。每个提交前面是要进行修改的命令,下面列出了所有可用的命令的名字及其功能,默认是pick,也就是保留这条提交,什么也不做的意思。这里解释一下几个常用的命令:

reword:重命名提交信息

edit:编辑提交的内容,可以用于添加或移出提交的文件

squash:将当前提交和前一条提交合并,使用后,会跳到另一个页面,编辑合并后的提交信息

fixup:将当前提交和前一条提交合并,使用后,丢弃当前的提交信息,直接以上一条提交信息为合并后的提交信息

drop:删除当前的提交

⚠️ 需要注意的是这句话:

These lines can be re-ordered; they are executed from top to bottom.

意思是说,改变上面一行行的提交记录的顺序是可以改变最终的提交顺序的,不过需要注意的是,改变顺序可能会带来conflict,如果遇到冲突,需要解决冲突后才能继续Rebase。

可以看下视频中的演示消化理解

rebase interactively
3/ 恢复到Rebase前

如果你rebase结束后发现并不是自己想要的结果,这个时候怎么恢复到rebase之前呢?很简单,运行下下面这个命令即可:

git reset --hard ORIG_HEAD

In case ORIG_HEAD is no longer useful, you can also use the branchName@{n} syntax, where n is the nth prior position of the branch pointer. So for example, if you rebase featureA branch onto your master branch, but you don’t like the result of the rebase, then you can simply do git reset --hard featureA@{1} to reset the branch back to exactly where it was before you did the rebase. You can read more about the branch@{n} syntax at the official Git docs for revisions.

⚠️⚠️⚠️ Rebase的最基本原则

既然Rebase命令可以删除和编辑历史提交,那同样也有一个重要的原则:

永远不要对已经推到主干分支服务器或者团队其他成员的提交进行Rebase,我们选择Rebase范围应该在自己本地工作范围内。

否则,人民群众会仇恨你,你的朋友和家人也会嘲笑你,唾弃你。

参考:

[1] https://www.internalpointers.com/post/squash-commits-into-one-git

[2] https://ohshitgit.com/

Author

calvinche

Posted on

2019-08-06

Licensed under

CC BY-NC-SA 4.0

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×