## 搜索
無論倉庫里的代碼量有多少,你經常需要查找一個函數是在哪里調用或者定義的,或者一個方法的變更歷史。 Git 提供了兩個有用的工具來快速地從它的數據庫中瀏覽代碼和提交。 我們來簡單的看一下。
### [Git Grep](http://git-scm.com/book/zh/v2/Git-%E5%B7%A5%E5%85%B7-%E6%90%9C%E7%B4%A2#Git-Grep)
Git 提供了一個?`grep`?命令,你可以很方便地從提交歷史或者工作目錄中查找一個字符串或者正則表達式。 我們用 Git 本身源代碼的查找作為例子。
默認情況下 Git 會查找你工作目錄的文件。 你可以傳入?`-n`?參數來輸出 Git 所找到的匹配行行號。
~~~
$ git grep -n gmtime_r
compat/gmtime.c:3:#undef gmtime_r
compat/gmtime.c:8: return git_gmtime_r(timep, &result);
compat/gmtime.c:11:struct tm *git_gmtime_r(const time_t *timep, struct tm *result)
compat/gmtime.c:16: ret = gmtime_r(timep, result);
compat/mingw.c:606:struct tm *gmtime_r(const time_t *timep, struct tm *result)
compat/mingw.h:162:struct tm *gmtime_r(const time_t *timep, struct tm *result);
date.c:429: if (gmtime_r(&now, &now_tm))
date.c:492: if (gmtime_r(&time, tm)) {
git-compat-util.h:721:struct tm *git_gmtime_r(const time_t *, struct tm *);
git-compat-util.h:723:#define gmtime_r git_gmtime_r
~~~
`grep`?命令有一些有趣的選項。
例如,你可以使用?`--count`?選項來使 Git 輸出概述的信息,僅僅包括哪些文件包含匹配以及每個文件包含了多少個匹配。
~~~
$ git grep --count gmtime_r
compat/gmtime.c:4
compat/mingw.c:1
compat/mingw.h:1
date.c:2
git-compat-util.h:2
~~~
如果你想看匹配的行是屬于哪一個方法或者函數,你可以傳入?`-p`?選項:
~~~
$ git grep -p gmtime_r *.c
date.c=static int match_multi_number(unsigned long num, char c, const char *date, char *end, struct tm *tm)
date.c: if (gmtime_r(&now, &now_tm))
date.c=static int match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt)
date.c: if (gmtime_r(&time, tm)) {
~~~
在這里我們可以看到在 date.c 文件中有?`match_multi_number`?和?`match_digit`?兩個函數調用了?`gmtime_r`。
你還可以使用?`--and`?標志來查看復雜的字符串組合,也就是在同一行同時包含多個匹配。 比如,我們要查看在舊版本 1.8.0 的 Git 代碼庫中定義了常量名包含 “LINK” 或者 “BUF_MAX” 這兩個字符串所在的行。
這里我們也用到了?`--break`?和?`--heading`?選項來使輸出更加容易閱讀。
~~~
$ git grep --break --heading \
-n -e '#define' --and \( -e LINK -e BUF_MAX \) v1.8.0
v1.8.0:builtin/index-pack.c
62:#define FLAG_LINK (1u<<20)
v1.8.0:cache.h
73:#define S_IFGITLINK 0160000
74:#define S_ISGITLINK(m) (((m) & S_IFMT) == S_IFGITLINK)
v1.8.0:environment.c
54:#define OBJECT_CREATION_MODE OBJECT_CREATION_USES_HARDLINKS
v1.8.0:strbuf.c
326:#define STRBUF_MAXLINK (2*PATH_MAX)
v1.8.0:symlinks.c
53:#define FL_SYMLINK (1 << 2)
v1.8.0:zlib.c
30:/* #define ZLIB_BUF_MAX ((uInt)-1) */
31:#define ZLIB_BUF_MAX ((uInt) 1024 * 1024 * 1024) /* 1GB */
~~~
相比于一些常用的搜索命令比如?`grep`?和?`ack`,`git grep`?命令有一些的優點。 第一就是速度非常快,第二是你不僅僅可以可以搜索工作目錄,還可以搜索任意的 Git 樹。 在上一個例子中,我們在一個舊版本的 Git 源代碼中查找,而不是當前檢出的版本。
### [Git 日志搜索](http://git-scm.com/book/zh/v2/Git-%E5%B7%A5%E5%85%B7-%E6%90%9C%E7%B4%A2#Git-日志搜索)
或許你不想知道某一項在?**哪里**?,而是想知道是什么?**時候**?存在或者引入的。?`git log`?命令有許多強大的工具可以通過提交信息甚至是 diff 的內容來找到某個特定的提交。
例如,如果我們想找到?`ZLIB_BUF_MAX`?常量是什么時候引入的,我們可以使用?`-S`?選項來顯示新增和刪除該字符串的提交。
~~~
$ git log -SZLIB_BUF_MAX --oneline
e01503b zlib: allow feeding more than 4GB in one go
ef49a7a zlib: zlib can only process 4GB at a time
~~~
如果我們查看這些提交的 diff,我們可以看到在?`ef49a7a`?這個提交引入了常量,并且在`e01503b`?這個提交中被修改了。
如果你希望得到更精確的結果,你可以使用?`-G`?選項來使用正則表達式搜索。
#### 行日志搜索
行日志搜索是另一個相當高級并且有用的日志搜索功能。 這是一個最近新增的不太知名的功能,但卻是十分有用。 在?`git log`?后加上?`-L`?選項即可調用,它可以展示代碼中一行或者一個函數的歷史。
例如,假設我們想查看?`zlib.c`?文件中`git_deflate_bound`?函數的每一次變更,我們可以執行`git log -L :git_deflate_bound:zlib.c`。 Git 會嘗試找出這個函數的范圍,然后查找歷史記錄,并且顯示從函數創建之后一系列變更對應的補丁。
~~~
$ git log -L :git_deflate_bound:zlib.c
commit ef49a7a0126d64359c974b4b3b71d7ad42ee3bca
Author: Junio C Hamano <gitster@pobox.com>
Date: Fri Jun 10 11:52:15 2011 -0700
zlib: zlib can only process 4GB at a time
diff --git a/zlib.c b/zlib.c
--- a/zlib.c
+++ b/zlib.c
@@ -85,5 +130,5 @@
-unsigned long git_deflate_bound(z_streamp strm, unsigned long size)
+unsigned long git_deflate_bound(git_zstream *strm, unsigned long size)
{
- return deflateBound(strm, size);
+ return deflateBound(&strm->z, size);
}
commit 225a6f1068f71723a910e8565db4e252b3ca21fa
Author: Junio C Hamano <gitster@pobox.com>
Date: Fri Jun 10 11:18:17 2011 -0700
zlib: wrap deflateBound() too
diff --git a/zlib.c b/zlib.c
--- a/zlib.c
+++ b/zlib.c
@@ -81,0 +85,5 @@
+unsigned long git_deflate_bound(z_streamp strm, unsigned long size)
+{
+ return deflateBound(strm, size);
+}
+
~~~
如果 Git 無法計算出如何匹配你代碼中的函數或者方法,你可以提供一個正則表達式。 例如,這個命令和上面的是等同的:`git log -L '/unsigned long git_deflate_bound/',/^}/:zlib.c`。 你也可以提供單行或者一個范圍的行號來獲得相同的輸出。
- 前言
- Scott Chacon 序
- Ben Straub 序
- 獻辭
- 貢獻者
- 引言
- 1. 起步
- 1.1 關于版本控制
- 1.2 Git 簡史
- 1.3 Git 基礎
- 1.4 命令行
- 1.5 安裝 Git
- 1.6 初次運行 Git 前的配置
- 1.7 獲取幫助
- 1.8 總結
- 2. Git 基礎
- 2.1 獲取 Git 倉庫
- 2.2 記錄每次更新到倉庫
- 2.3 查看提交歷史
- 2.4 撤消操作
- 2.5 遠程倉庫的使用
- 2.6 打標簽
- 2.7 Git 別名
- 2.8 總結
- 3. Git 分支
- 3.1 分支簡介
- 3.2 分支的新建與合并
- 3.3 分支管理
- 3.4 分支開發工作流
- 3.5 遠程分支
- 3.6 變基
- 3.7 總結
- 4. 服務器上的 Git
- 4.1 協議
- 4.2 在服務器上搭建 Git
- 4.3 生成 SSH 公鑰
- 4.4 配置服務器
- 4.5 Git 守護進程
- 4.6 Smart HTTP
- 4.7 GitWeb
- 4.8 GitLab
- 4.9 第三方托管的選擇
- 4.10 總結
- 5. 分布式 Git
- 5.1 分布式工作流程
- 5.2 向一個項目貢獻
- 5.3 維護項目
- 5.4 總結
- 6. GitHub
- 6.1 賬戶的創建和配置
- 6.2 對項目做出貢獻
- 6.3 維護項目
- 6.4 管理組織
- 6.5 腳本 GitHub
- 6.6 總結
- 7. Git 工具
- 7.1 選擇修訂版本
- 7.2 交互式暫存
- 7.3 儲藏與清理
- 7.4 簽署工作
- 7.5 搜索
- 7.6 重寫歷史
- 7.7 重置揭密
- 7.8 高級合并
- 7.9 Rerere
- 7.10 使用 Git 調試
- 7.11 子模塊
- 7.12 打包
- 7.13 替換
- 7.14 憑證存儲
- 7.15 總結
- 8. 自定義 Git
- 8.1 配置 Git
- 8.2 Git 屬性
- 8.3 Git 鉤子
- 8.4 使用強制策略的一個例子
- 8.5 總結
- 9. Git 與其他系統
- 9.1 作為客戶端的 Git
- 9.2 遷移到 Git
- 9.3 總結
- 10. Git 內部原理
- 10.1 底層命令和高層命令
- 10.2 Git 對象
- 10.3 Git 引用
- 10.4 包文件
- 10.5 引用規格
- 10.6 傳輸協議
- 10.7 維護與數據恢復
- 10.8 環境變量
- 10.9 總結
- A. 其它環境中的 Git
- A1.1 圖形界面
- A1.2 Visual Studio 中的 Git
- A1.3 Eclipse 中的 Git
- A1.4 Bash 中的 Git
- A1.5 Zsh 中的 Git
- A1.6 Powershell 中的 Git
- A1.7 總結
- B. 將 Git 嵌入你的應用
- A2.1 命令行 Git 方式
- A2.2 Libgit2
- A2.3 JGit
- C. Git 命令
- A3.1 設置與配置
- A3.2 獲取與創建項目
- A3.3 快照基礎
- A3.4 分支與合并
- A3.5 項目分享與更新
- A3.6 檢查與比較
- A3.7 調試
- A3.8 補丁
- A3.9 郵件
- A3.10 外部系統
- A3.11 管理
- A3.12 底層命令