git三个小技巧:删除指定 commit、修改历史 commit 中的作者信息、合并某文件到当前分支

三个小技巧,工作中遇到了好几次,记录一下,方便后人和自己查看。

一、删除指定 commit

假设 commit 是最后一次 commit,这个很简单,直接

1
git reset HEAD^

使用 soft 模式进行 reset(默认就是这样的),就可以取消 commit ,保留修改过后的文件,非常方便。 假设 commit 是很久以前的一条 commit,就要使用一种操作,叫rebase。比如某次commit我们不小心上传了某个 world.txt文件,后来不想要这个文件了,就可以用这个操作。 例如这三次commit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
commit 3bfcfea5366ca96c8aa900b7568daea2963c0741 (HEAD -> master)
Author: LeadroyaL <243850435@qq.com>
Date: Tue Jul 17 20:46:57 2018 +0800

add haha.txt

commit 4df80acee1fe3744abca55d8a8e3e4bc66d78df3
Author: LeadroyaL <243850435@qq.com>
Date: Tue Jul 17 20:46:43 2018 +0800

add world.txt

commit 7c37f2fe648b63fd079fb82c8382191649f2869f
Author: LeadroyaL <243850435@qq.com>
Date: Tue Jul 17 20:46:25 2018 +0800

add hello.txt

分别上传了三个文件,假设我们不要这个 world.txt 了,就要退到它的前一个版本

1
git rebase -i 7c37f2fe648b63fd079fb82c8382191649f2869f

会出现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
pick 4df80ac add world.txt
pick 3bfcfea add haha.txt

# Rebase 7c37f2f..3bfcfea onto 7c37f2f (2 commands)
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit
#
# 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.
#
# Note that empty commits are commented out

改为

1
drop 4df80ac add world.txt

就会永久丢弃掉这个 commit,并且后面的 commit 还在。 之后强行推送到服务器,覆盖远程分支

1
git push --force

但是存在一个缺点,可能被服务器丢弃掉以前每个commit的修改时间,可以认为是在这一刻依次将历史 commit 都重新 commit 了一遍。

二、修改 commit 中的作者信息

这个大家肯定都遇到过,某些 repo 里的作者信息搞错了,需要批量修改。 https://help.github.com/articles/changing-author-info/ 按照这个链接操作一下就可以了,非常简单,非常舒服。 操作成功如下

1
2
3
4
5
➜  MyRepo.git git:(master) ./run.sh
Rewrite aaaabbbbccccddddeeeeffff (39/71) (1 seconds passed, remaining 0 predicted)
WARNING: Ref 'refs/heads/dev' is unchanged
WARNING: Ref 'refs/heads/master' is unchanged
Ref 'refs/heads/online' was rewritten

之后推送到远程即可,但是注意一下,链接里覆盖远程的代码是:

1
git push --force --tags origin 'refs/heads/*'

这个方法是有一定风险的,可能我们只是想改某个分支的,就不要用通配符,可以用:

1
git push --force --tags origin 'refs/heads/online'

三、合并某文件到当前分支

emmmm,讲道理这个需求挺奇怪的,为什么要从分支 A 的某文件复制到分支 B 呢?讲个以前见过的操作,Android 发布时,会从各个 feature 中抽取出文件,汇总一下发布。所以这个需求也是有的,刚好被我碰上了,我的需求是将master里新建的.gitignore复制到dev里,而 dev 已经被我开发了很久了(别问我为什么有这么奇怪的需求),我不想合并它们,只想拿个文件过来。 这里讲个操作,叫cherry-pick,和 merge 的区别在于分支还是相互独立的,不会合并。

1
2
git checkout dev
git cherry-pick head-from-master-aabbccdd

这样就会将 aabbccdd 这个commit信息合并到dev,非常方便,当然也会生成一个额外的commit,自动的。 默认是将全部文件都复制过来,可以再多操作几下,删掉无用的文件。