Git: 好用到爆命令和配置

让 Git 的更好用的配置和命令。

别名配置

git 的配置文件通常是 home 目录下的 .gitignore

可以追加以下内容:

1
2
3
4
5
6
7
8
9
10
11
12
[color]
ui = true # 启用git输出的颜色控制功能
[alias]
st = status
co = checkout
ci = commit
br = branch
di = diff
cane = commit --amend --no-edit # 将staged的改动与上次的commit合并(使用上次的提交信息)
unstage = reset HEAD
last = log -1
lg = log --color --graph --pretty=format:'%C(red)%h%C(reset)%C(green)(%cr)%C(reset) %s %C(yellow)%d%C(reset) %C(bold blue)%an%Creset' --abbrev-commit
  • 可实现 git st == git status 这样的效果。
  • 上图中 git lg 可以实现图形化展示 git 提交过程。

命令备忘

push

1
2
git push <remote> <tag>
git push --tag

tag

1
2
3
4
5
git tag -a 1.0.1 -m ‘fixed bug’ #添加一个tag

git tag -a 2.0.0 -m ‘msg’ COMMITID #指定commit号的tag

git tag v0.1.2 #轻量级的tag

checkout:从远端切分支出来

1
git co -b local_name origin/romote_name

delete 远端分支、tag

1
2
git push origin --delete <branchName>
git push origin --delete tag <tagname>

rebase:压缩几个 commit 到一个 commit

1
2
3
4
5
# 从 commmit1 之后到 head 压缩到一起
git rebase -i commit1
git rebase --continue
git rebase --edit-todo
git rebase --abort

压缩 commit 时:
在执行的 rebase 的编辑处,
应该把不要的 commit 的记录,从 pick 改成 squash
然后,squash 的 commit 就会被压缩到它前一个 commit 上 (形成一个新的 commitID)

修正 Github Contributions 中的统计数据

检查 git log 中的用户名 & 密码是否正确,通常统计数据不正确是因为账户中的邮箱,跟提交代码的邮箱不一致导致。
修复统计数据的过程实际上就是修改历史提交中邮箱的过程

修复已经提交的 commit:

  • 先克隆一个新的仓库

git clone --bare https://github.com/user/repo.git

  • 再在仓库中执行下面的脚本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/bin/sh
git filter-branch --env-filter '
OLD_EMAIL="旧的Email地址"
CORRECT_NAME="正确的用户名"
CORRECT_EMAIL="正确的邮件地址"
if [ "$GIT_COMMITTER_EMAIL" = "$OLD_EMAIL" ]
then
export GIT_COMMITTER_NAME="$CORRECT_NAME"
export GIT_COMMITTER_EMAIL="$CORRECT_EMAIL"
fi
if [ "$GIT_AUTHOR_EMAIL" = "$OLD_EMAIL" ]
then
export GIT_AUTHOR_NAME="$CORRECT_NAME"
export GIT_AUTHOR_EMAIL="$CORRECT_EMAIL"
fi
' --tag-name-filter cat -- --branches --tags
  • 重新推送代码

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

git diff 输出详解

这里的内容以软件 diff 为例,内容是以前在笔记中写的。git diff 与之类似,就不再重写了。

假设有 test1.md, test2.md 两个文本文件.

test1.md

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
1
2
3
4
this is line 5 of test1
6
7
8
9
10
11
12
this is line 13 of test1
14
15
16
17

test2.md

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
1
2
3
4
this is line 5 of test2
6
7
8
9
10
11
12
this is line 13 of test2
14
15
16
17

两个文件只有第 5 和 13 行是不一样的.
我们在终端中执行以下代码即可将两个文件的比较结果写入新的 diff.md 中:

1
diff -u test1.md test2.md > diff.md

解释:
-u 是 diff 的参数,表示输出结果时同时输出上下文.
test1.md test2.md 接下来是参与比较的两个文件
> diff.md 表示将结果输出到 diff.md 这个文件中 (默认是输出到控制台)
以上生成的 diff.md 文件内容是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
--- test1.md    2015-09-04 19:23:06.708723075 +0800
+++ test2.md 2015-09-04 19:23:36.500721968 +0800
@@ -2,7 +2,7 @@
2
3
4
-this is line 5 of test1
+this is line 5 of test2
6
7
8
@@ -10,7 +10,7 @@
10
11
12
-this is line 13 of test1
+this is line 13 of test2
14
15
16

下面是对上面每行的解释:

  • 1-2 行:分别记录了 原始文件目标文件 的文件名和时间戳.
  • 3-11 行:以 @@ 开头到下一个 @@开头行之前叫做一个差异小节 (表示一处不同).
  • 3 行,12 行:差异定位语句,此行以 @@ 开头和结束. -2,7 表示本节包含了原始文件的内容是从第 2 行开始的接下来 7 行. +2,7 则表示目标文件.
  • 4-6 行,9-11 行:以空格开头表示本行的文本在两个文件中是相同的,在这里是作为上下文输出的.
  • 7 行,16 行:以 - 开头表示本行是原始文件的内容.
  • 8 行,17 行:以 + 开头表示本行是原始文件的内容.
  • 在一个差异小节中, -+ 开头的行表示了文本的差异.

用 patch 来进行 diff 的反向操作

我们用 diff 生成了 diff.md 文件之后,可以用 patch 进行反向还原.
先将 test2.md 文件删除,然后执行以下命令:

1
patch test2.md < diff.md

然后会根据 test1.mddiff.md 生成 test2.md .

当需要利用 test2.mddiff.md 生成 test1.md 时,可以执行:

1
patch-R test1.md < diff.md

这样可以反向生成 test1.md

最后,diff 和 patch 还可以用来对目录进行比较,但是他们存在局限性:不能对二进制文件进行比较,Git 中对 对 diff 进行了扩展,支持二进制文件的版本控制和差异比较.

git hooks

client hook

  • 提交相关
    1. pre-commit: 测试代码,style lint,检查 doc,检查文档结果的空白字符是否存在;以非 0 值 exit 出时将放弃提交
    2. prepare-commit-msg: 默认提交信息创建之后,在启动提交信息编辑器运行之前运行;可以看到本次提交的快照信息;可以编辑提交信息模版,合并提交
    3. commit-msg: 接受一个参数:文件路径,存有当前的提交信息,以非 0 值 exit 出时将放弃提交
    4. post-commit: 一般用于通知,可以使用 git log -1 HEAD 获得最后一次提交的信息
  • 电子邮件相关
    1. applypatch-msg: 接收但个参数,包含请求合并信息的临时文件的名字;如果脚本返回非零值,Git 将放弃该补丁;可以用该脚本来确保提交信息符合格式,或直接用脚本修正格式错误。
    2. pre-applypatch: 运行于应用补丁 之后,产生提交之前,所以你可以用它在提交前检查快照。
    3. post-applypatch: 运行于提交产生之后,可以用它把结果通知给一个小组或所拉取的补丁的作者。
  • 其他
    • pre-rebase: 可以使用这个钩子来禁止对已经推送的提交变基
    • post-rewrite: 被那些会替换提交记录的命令调用 (git commit –amend 和 git rebase),有一个参数(触发重写的命令名),同时从标准输入中接受一系列重写的提交记录。 这个钩子的用途很大程度上跟 post-checkout 和 post-merge 差不多。
    • post-checkout: 可以用于可以根据你的项目环境用它调整你的工作目录,放入大的二进制文件、自动生成文档或进行其他类似这样的操作
    • post-merge: git merge 成功运行后被调用,可以用来验证某些在 Git 控制之外的文件是否存在,这样就能在工作区改变时,把这些文件复制进来
    • pre-push: 接受远程分支的名字和位置作为参数,同时从标准输入中读取一系列待更新的引用。可以在推送开始之前,用它验证对引用的更新操作
    • pre-auto-gc: git gc –auto 运行前调用,可以用来设置一些提醒

server hook

这些钩子脚本在推送到服务器之前和之后运行。 推送到服务器前运行的钩子可以在任何时候以非零值退出,拒绝推送并给客户端返回错误消息,还可以依你所想设置足够复杂的推送策略。

  • pre-receive: 可以用这个钩子阻止对引用进行非快进(non-fast-forward,git 仓库中已经有一部分代码,所以它不允许你直接把你的代码覆盖上去。)的更新,或者对该推送所修改的所有引用和文件进行访问控制。
  • update: pre-receive 脚本十分类似,不同之处在于它会为每一个准备更新的分支各运行一次;推送者同时向多个分支推送内容,pre-receive 只运行一次,相比之下 update 则会为每一个被推送的分支各运行一次。接受三个参数:引用(分支)的名字, 推送前引用指向的 SHA-1 值,用户准备推送的 SHA-1 值。如果 update 脚本以非零值退出,只有相应的那一个引用会被拒绝;其余的依然会被更新。
  • post-update
  • post-receive: 在整个过程完结以后运行,可以用来更新其他服务或者用户。接受与 pre-receive 相同的标准输入数据。可以用来通知 CI,更新 Issue 追踪系统,