命令行神器之 git 不常用子命令篇

git show-branch

git show-branch显示分支和提交的版本情况,下面示例图可以参考此脚本生成。

  1. 输出分为上下两部分,使用若干个短划线-分隔。两个分支使用两个短划线-,三个分支使用三个短划线-,依次类推,最多不超过29个分支。
  2. 上半部分为层次缩进的分支列表,下半部分为commit列表。
  3. 上半部分的分支列表中,使用*标识当前分支,其他分支使用!标识(不同的分支的!标识颜色不一样)。分支前的标识符*或者!一直垂直贯通到下半部分,这一垂直列的符号都是属于这个分支的。
  4. 下半部分的commit列表中,前导的符号有*+-号:
    1. *表示这一列上的分支(当前分支)有此commit。
    2. +表示这一列上的分支(非当前分支)有此commit。
    3. -表示这是一次合并操作。
  5. 标识符的颜色只是用于容易区分列,一个分支一个颜色,如图
 git show-branch! [br1] v15-br1 ! [br2] v12-br2  ! [br3] v13-br3   * [master] merge br2 into master----   - [master] merge br2 into master + * [br2] v12-br2 + * [br2^] v11-br2 + * [br2~2] v10-br2+  * [br1] v15-br1+  * [br1^] v14-br1+  * [br1~2] v7-br1  +* [br3] v13-br3  +* [br3^] v5-br3   * [master~3] v9-master   * [master~4] v8-master   * [master~5] v6-master  +* [master~6] v4-br3 ++* [master~7] v3-br2+++* [master~8] v2-br1

git cat-file

可以使用git cat-file命令去查询特定对象的信息,并常与git ls-treegit ls-files -s命令一起使用。git cat-file参数说明:

  1. -t: 显示对象的类型。
  2. -s: 显示对象的大小。
  3. -e: 如果对象存在且有效,返回值为0
  4. -p: 根据对象的类型,格式化打印对象内容。
 git cat-file -t 34948cfcommit git cat-file -p 34948cftree df43c4f968f9329b7498b86eabb66a45229f21e2parent 90e415d6b6efa80d74c8fe7ec528b560fa0777f6author yuweijun <yuweijun@live.com> 1502198776 +0800committer yuweijun <yuweijun@live.com> 1502198776 +0800v9-master git cat-file commit 34948cftree df43c4f968f9329b7498b86eabb66a45229f21e2parent 90e415d6b6efa80d74c8fe7ec528b560fa0777f6author yuweijun <yuweijun@live.com> 1502198776 +0800committer yuweijun <yuweijun@live.com> 1502198776 +0800v9-master git ls-tree 34948cf100644 blob 626799f0f85326a8c1fc522db584e86cdfccd51f    v1.txt100644 blob 8c1384d825dbbe41309b7dc18ee7991a9085c46e    v2.txt100644 blob 29ef827e8a45b1039d908884aae4490157bcb2b4    v3.txt100644 blob c694117fd4e76c22ae04348c15861413019aa03b    v4.txt100644 blob 9c0be88a7ecb5f679fe637f1b69838f6b46227d3    v6.txt100644 blob a00f43e3090a7d141b8d181468197f2f5e979040    v8.txt100644 blob cc406030c5d3ed8c4491977a8391e0d5c5850f89    v9.txt git cat-file -t a00f43eblob git cat-file blob a00f43ev8
 git ls-files -s100644 626799f0f85326a8c1fc522db584e86cdfccd51f 0       v1.txt100644 e338b86593faf8b676070731c784a5493c8e98b2 0       v10.txt100644 882cecb02f7b120a45f2fd624811562095b35d1c 0       v11.txt100644 dae199aecb18022d9c525b7af95f6cd1bb44f43a 0       v12.txt100644 a2680a75e29da0261ba8354de83dd09ed700aa2e 0       v13.txt100644 958b5a36e1fad087be8e389960a6a09c50217c47 0       v14.txt100644 75701b22948d38da9f21cdaa720bd8ab26c4286d 0       v15.txt100644 8c1384d825dbbe41309b7dc18ee7991a9085c46e 0       v2.txt100644 29ef827e8a45b1039d908884aae4490157bcb2b4 0       v3.txt100644 c694117fd4e76c22ae04348c15861413019aa03b 0       v4.txt100644 47e5d40a50f8db1524f5308633ae3f0d1de58619 0       v5.txt100644 9c0be88a7ecb5f679fe637f1b69838f6b46227d3 0       v6.txt100644 02a819f21d4c6b8b8b26c4a840bfd54a99872bc1 0       v7.txt100644 a00f43e3090a7d141b8d181468197f2f5e979040 0       v8.txt100644 cc406030c5d3ed8c4491977a8391e0d5c5850f89 0       v9.txt

git rev-parse

如果你想知道某个分支指向哪个特定的sha-1码(如HEAD),或者想看某个提交对应的sha-1码,你可以使用一个叫做rev-parse的git探测工具。

示例

 git rev-parse HEAD@{"2 days ago"}96dea4282684bb249b4bb0e5964f9c8b2717bf27

帮助手册

 git help rev-parse

rev-parse是为了底层操作而不是日常操作设计的,rev-parse的帮助手册里一些关于版本定位的一些表示方式如下:

版本号表示及示例

  1. @, shortcut for HEAD.
  2. <refname>@{<date>}, e.g. master@{yesterday}, HEAD@{"5 minutes ago"}, HEAD@{5.minutes.ago}
  3. <refname>@{<n>}, e.g. master@{1}
  4. @{<n>}, e.g. @{1}
  5. @{-<n>}, e.g. @{-1}
  6. <branchname>@{upstream}, e.g. master@{upstream}, @{u}
  7. <rev>^, e.g. HEAD^, v1.5.1^0
  8. <rev>~<n>, e.g. master~3
  9. <rev>^{<type>}, e.g. v0.99.8^{commit}
  10. <rev>^{}, e.g. v0.99.8^{}
  11. <rev>^{/<text>}, e.g. HEAD^{/"fix nasty bug"}
  12. :/<text>, e.g. :/"fix nasty bug"
  13. :/^foo, search with regular expression
  14. <rev>:<path>, e.g. HEAD:README, :README, master:./README
  15. :<n>:<path>, e.g. :0:README, :README

提交的版本范围

  1. <rev>: Include commits that are reachable from (i.e. ancestors of) <rev>,当前命令从<rev>版本(包括此版本)开始操作。
  2. ^<rev>: Exclude commits that are reachable from (i.e. ancestors of) <rev>,从<rev>版本开始排除命令范围在外,这里注意:
    1. 在提交版本号前的^符是版本排除的作用,表示取反操作;
    2. ^在版本号之后是表示父提交版本。
    3. git log ^HEAD^ HEAD,HEAD所能到达的所有提交,减去其第一个父提交所能到达的所有提交。
    4. git log HEAD^..HEAD,含义同上。
  3. <rev1>..<rev2>: Include commits that are reachable from <rev2> but exclude those that are reachable from <rev1>. When either <rev1> or <rev2> is omitted, it defaults to HEAD,版本<rev2>能到达的版本号中排除<rev1>能到达的版本号,即<rev2><rev1>版本之间的集合差,主要用于比较分支之间的提交差别。
  4. <rev1>...<rev2>: Include commits that are reachable from either <rev1> or <rev2> but exclude those that are reachable from both. When either <rev1> or <rev2> is omitted, it defaults to HEAD,这是二个提交版本的并集再减去二者的交集,过滤后剩下的版本是2个版本互相缺失的那些提交。
  5. <rev>^@: e.g. HEAD^@, A suffix ^ followed by an at sign is the same as listing all parents of <rev> (meaning, include anything reachable from its parents, but not the commit itself),包含除了<rev>之外的所有它的祖先提交。
  6. <rev>^!, e.g. HEAD^! A suffix ^ followed by an exclamation mark is the same as giving commit <rev> and then all its parents prefixed with ^ to exclude them (and their ancestors),与上面刚好相反,仅只包括<rev>这一提交,但不包括其祖先提交。查看以下示例观察运行结果:
    1. git log HEAD^!
    2. git log HEAD
    3. git log HEAD^@

提交范围示例

 git rev-list br1..br299812dcc3c700d044df84b1456d90fdd073ad92c849a0578c735f18182d7d587a1aaca381b9ca01bc3644e4c3af3705bfbc90751571551a328093bad8a1e833c512a1fc5d5592b7838b07226fb121a87

上面输出的4个提交都是在br2分支上的,而br1分支没有这些提交。

 git log br1...br2b15012622287ee05af6ad288d218f1d13ad2526a7422cecd9c4c38ac64f4c58e0f664c4297928d913d040d06546df6eb9c8b5ac8343d759f74bc3d253c04f6148226468c197c53bac362769f8457ead9c1341c3453570ad06883df4b02f13ebe2c12d3b799812dcc3c700d044df84b1456d90fdd073ad92c849a0578c735f18182d7d587a1aaca381b9ca01bc3644e4c3af3705bfbc90751571551a328093bad34948cf11e372cf26107a1278042ff891f57b1ac90e415d6b6efa80d74c8fe7ec528b560fa0777f6265c7df31b9d9b40b68c0205e76f720b283f1de55886c7ae151cc431c87e3cc9654f369315e35e94c5f69516d0b0740904b51879224ad65a86f6cdd28a1e833c512a1fc5d5592b7838b07226fb121a87

输出的提交包含了2部分,一部分是br1分支所能到达而br2分支不能到达的提交,另一部分正好相反。

git rev-list

根据条件显示所有提交版本号。

根据日期来查询相应的版本号

 git rev-list -n 2 --before="Aug 1, 2017 00:00:00" master git rev-list -n 3 --before="2017-05-08 15:51:12" master

显示指定分支上文件的版本号

 git rev-list master -- filename

git show branch:filename

显示指定分支的文件内容,不用切到指定分支,直接查看该分支下的指定文件的内容。

 git show master:index.html

git log

跟踪指定文件的文件名和内容变化。

 git log --follow -p -- filename

如果你曾经与很多小伙伴工作在同一个持久分支上,也许会有这样的经历,父分支(例如: master)上的大量合并同步到你当前的分支。
这使得我们很难分辨哪些变更时发生在主分支,哪些变更发生在当前分支,尚未合并到master分支。

 git log --no-merges master..

可以解决这个问题。注意--no-merges标志意味着只显示没有合并到任何分支的变更,master..选项,意思是指显示没有合并到master分支的变更(在master后面必须有..,省略了HEAD)。

常用日志显示格式化

 git log --pretty=format:'%C(yellow)%h %C(green)| %C(white)%ad %C(green)| %C(blue)%>(15,trunc)%an %C(green)| %C(green)%d %C(reset)%s'c5f6951 | 2017-08-08 21:26:06 +0800 |        test |  v4-br38a1e833 | 2017-08-08 21:26:04 +0800 |        test |  v3-br2a908021 | 2017-08-08 21:26:00 +0800 |        test |  v2-br1b700e2e | 2017-08-08 21:25:57 +0800 |        test |  v1-master

git checkout -m branch

有时,本地做了一些修改,忘记git stash时,切到另一个分支时,可能提示本地有内容修改,无法切换分支:

git checkout develop
error: You have local changes to 'develop'; not switching branches.

You can give the -m flag to the command, which would try a three-way merge:

 git checkout -m develop

如果合并发生冲突,会有相应提示,解决冲突,并git add

git stash branch

git stash branch会创建一个新分支,从stash中弹出一个暂存并应用修改到新分支上,在新分支上继续当时的工作。

 git stashSaved working directory and index state WIP on master: 7422cec merge br2 into masterHEAD is now at 7422cec merge br2 into master git stash branch testSwitched to a new branch 'test'On branch testChanges not staged for commit:  (use "git add <file>..." to update what will be committed)  (use "git checkout -- <file>..." to discard changes in working directory)        modified:   v9.txtno changes added to commit (use "git add" and/or "git commit -a")Dropped refs/stash@{0} (34e6c8eee86972f44676c86f6dac8306d1dabfa5)

git reflog

需要注意的是,git reflog信息只存在于本地 —— 这是一个记录你在你自己的仓库里做过什么的日志。
其他人copy的仓库里的引用日志不会和你的相同;而你新clone一个仓库的时候,reflog是空的,因为你在仓库里还没有操作。

显示当前分支2个月前的提交

如下这条命令只有在你克隆了一个项目至少两个月时才会有用 —— 如果你是五分钟前克隆的仓库,那么它将不会有结果返回。

 git reflog HEAD@{2.months.ago}

显示master分支上所有提交的版本号

 git reflog master

git show-ref

git show-ref列出仓库中所有的引用,仓库中所有的分支和标签实际上都是对应于相应的某个提交版本。

 git show-ref02ac3819c29fadc0f02f43da3991d969d1fc7afa refs/heads/br199812dcc3c700d044df84b1456d90fdd073ad92c refs/heads/br2c1341c3453570ad06883df4b02f13ebe2c12d3b7 refs/heads/br37422cecd9c4c38ac64f4c58e0f664c4297928d91 refs/heads/master7422cecd9c4c38ac64f4c58e0f664c4297928d91 refs/remotes/origin/master

git pull --rebase

这是一个非常有用的变基用法。

举个例子,假设你正在master分支的一个本地版本上工作,你已经向仓库提交了一小部分变更。
与此同时,也有人向master分支提交了他一周的工作成果。
当你尝试推送本地变更时,git提示你需要先运行一下git pull:

To git-branch-test.git
! [rejected] master -> master (non-fast-forward)
error: failed to push some refs to 'git-branch-test.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

尽管这不是什么大问题,也完全安全,但是不太有利于历史记录的整洁。

这种情况下,git pull --rebase是一个不错的选择。

 git pull --rebase

这个命令会迫使git将远程分支上的变更同步到本地,然后将尚未推送的提交重新应用到这个最新版本,就好象它们刚刚发生一样。
这样就可以避免提交一个像下面这样的合并信息了。

Merge remote-tracking branch ‘origin/master’

git remote

查看当前配置有哪些远程仓库,对应于.git/gitconfig文件中的配置。

 git remote git remote -v git remote show remote git help remote

git ls-remote

显示远程仓库的版本引用:

 git ls-remoteFrom git-branch-test.git7422cecd9c4c38ac64f4c58e0f664c4297928d91        HEAD7422cecd9c4c38ac64f4c58e0f664c4297928d91        refs/heads/master

git checkout --track

远程分支检出时自动跟踪分支

也可以在检出之后,用-u--set-upstream-to选项运行git branch来显式地设置,甚至直接编辑配置文件.git/gitconfig

 git checkout --track origin/serverfixBranch serverfix set up to track remote branch serverfix from origin.Switched to a new branch 'serverfix'

恢复当前文件夹下的文件变更

 git checkout -- .

git fsck

该命令显示所有未被其他对象引用的所有对象,可以从dangling commit找到丢失了的commit

 git fsck --fullChecking object directories: 100% (256/256), done.dangling commit 34e6c8eee86972f44676c86f6dac8306d1dabfa5

git gc

git gc指垃圾收集(garbage collect):收集所有松散对象并将它们存入packfile,合并这些packfile进一个大的packfile,然后将不被任何commit引用并且已存在一段时间(数月)的对象删除。

 git gcCounting objects: 373923, done.Delta compression using up to 4 threads.Compressing objects: 100% (106664/106664), done.Writing objects: 100% (373923/373923), done.Total 373923 (delta 232320), reused 370405 (delta 228834)Checking connectivity: 373923, done.

git clean

git clean可以帮你清理当前workspace中未被git版本控制的文件,比如临时文件,构建出来的二进制文件。
这个命令还有-x-X参数要注意慎用,避免误删文件。

 git clean -df

git grep

git grep命令查找git库里面的某段文字是很方便的。当然,你也可以用unix下的grep命令进行搜索, 但是git grep命令能让你不用签出git checkout历史文件,就能查找它们。

 git grep -n 'v'v1.txt:1:v1v14.txt:1:v14v15.txt:1:v15v2.txt:1:v2v7.txt:1:v7

git bisect

利用二分法查找问题版本。

 git bisect start git bisect bad git bisect good master@{7}Bisecting: 4 revisions left to test after this (roughly 3 steps)[3d040d06546df6eb9c8b5ac8343d759f74bc3d25] merge br1 into master$ git bisect goodBisecting: 2 revisions left to test after this (roughly 1 step)[849a0578c735f18182d7d587a1aaca381b9ca01b] v11-br2 git bisect goodBisecting: 0 revisions left to test after this (roughly 1 step)[7422cecd9c4c38ac64f4c58e0f664c4297928d91] merge br2 into master git bisect badBisecting: 0 revisions left to test after this (roughly 0 steps)[99812dcc3c700d044df84b1456d90fdd073ad92c] v12-br2 git bisect resetPrevious HEAD position was 99812dc... v12-br2Switched to branch 'master'

git merge -s ours branch-name

使用ours策略将分支obsolete的合并进当前分支,但是忽略其他分支的所有改变:

Merge branch obsolete into the current branch, using ours merge strategy, ignoring all changes from all other branches.

 git merge -s ours obsolete

使用git merge -s回滚上一个提交,只回滚上一个提交的效果与git revert类似,但是这个方式可以一次回滚到之前任何一个指定提交上。

 git checkout -b previous git reset --hard HEAD~ git merge -s ours master git checkout master git merge previous git branch --delete previous git push origin master:master

git diff -w

给git部分命令加一个-w参数,git将会忽略空白的变更,如下所示:

 git diff -w master^ git blame -w filename

git help hooks

git 能在特定的重要动作发生时触发自定义脚本,有两组这样的钩子:

  1. 客户端的hooks,由诸如提交和合并这样的操作所调用;
  2. 服务器端的hooks,作用于诸如接收被推送的提交这样的联网操作。

钩子都被存储在.git目录下的hooks子目录中,即.git/hooks。更多使用说明可参考在线文档,或者查看帮助手册。

References

  1. git scm - git hooks
  2. git 工具 - 修订版本revision选择
  3. git 工具 - stashing
  4. git 分支 - 远程分支
  5. git 内部原理 - 维护及数据恢复
  6. git-bisect - Use binary search to find the commit