My External Storage

Apr 11, 2018 - 3 minute read - Comments - git tips

git grepでマッチしたワードを含んだ関数名を表示する/関数スコープ全体を表示する #git

git 2.17にすると使える(?)-Wオプション(とついでに-p)が便利すぎたのでメモ。
2018/04/11現在、Macならばbrew upgrade gitで2.1.7に更新できる。

Git 2.17 is now available
https://blog.github.com/2018-04-05-git-217-released/

(?なのは https://git-scm.com/docs/git-grep/ を見ると2.15とかでも使えそうな気配がするから)

TL;DR

  • git grep -Wで検索結果にマッチしたワードを含む関数スコープを全て表示してくれる
  • git grep -pで検索結果にマッチしたワードを含んだ関数の名前を表示してくれる

git grep

https://git-scm.com/docs/git-grep/

git grepサブコマンドを使うとgitリポジトリで管理しているファイルを対象にgrepを実行してくれる。 (gitで管理されない)vendorディレクトリやnode_modulesディレクトリは無視してくれるので便利。

git grep -Wで検索結果にマッチしたワードを含む関数スコープ全体を表示する

-W(--function-context)オプションをつかうとマッチしたワードが含まれる関数スコープを表示してくれる。

    -W, --function-context
                          show the surrounding function

試した感じGoとReactはいい感じで関数を表示してくれた。

普通にgrepするとマッチした行だけしかわからないが、

$ git grep -i hello front/src/*.js
front/src/app.js:      <h1>Hello, React-golang!</h1>

-Wオプションを使うと関数全体を表示してくれる。

git grep -i -W hello front/src/*.js
front/src/app.js=class App extends React.Component {
front/src/app.js-  render() {
front/src/app.js-    return (
front/src/app.js:      <h1>Hello, React-golang!</h1>
front/src/app.js-    );
front/src/app.js-  }
front/src/app.js-}

Goでもこんな感じ。

$ git grep -i -W hello backend/**/*.go
backend/main.go=func handler(w http.ResponseWriter, r *http.Request) {
backend/main.go-        dump, err := httputil.DumpRequest(r, true)
backend/main.go-        if err != nil {
backend/main.go-                http.Error(w, fmt.Sprint(err), http.StatusInternalServerError)
backend/main.go-                return
backend/main.go-        }
backend/main.go-        fmt.Println(string(dump))
backend/main.go:        fmt.Fprintf(w, "<html><body>hello</body></html>\n")
backend/main.go-}

git grep -pで検索結果にマッチしたワードを含む関数の名前を表示する

-p(--show-function)オプションを使うと関数名を表示してくれる。 IDEがあれば関数名だけわかれば十分だし、リファクタリング対象や呼び出し元(呼び出し先)を探すときなどはこちらが便利かも。

    -p, --show-function   show a line with the function name before matches
$ git grep -i -p hello front/src/*.js
front/src/app.js=class App extends React.Component {
front/src/app.js:      <h1>Hello, React-golang!</h1>

Goでやってもこんな感じ。

$ git grep -i -p hello backend/**/*.go
backend/main.go=func handler(w http.ResponseWriter, r *http.Request) {
backend/main.go:        fmt.Fprintf(w, "<html><body>hello</body></html>\n")

終わりに

@deeeetさんのTweeetをみて調べてみたら今回の便利なオプションを見つけてしまった。

冒頭にも書いたとおり、git grepコマンドはgit管理のファイルのみを検索ターゲットとしてくれる。 逆に言うとそれくらいしかあまりメリットを感じていなかったので結局egrepコマンドを多用していた。 (grepでも--exclude-dir=".git" --exclude-dir="vendor"とかつければ変わらない。実行履歴からコマンド引き回せばタイプもしなくて済むし) 前後の行を見たくて今まで-Aとか-Bを微調整しながらgrepしていたので-Wで一気に見れたり、関数名が拾えるのはすごい便利だ。 関数スコープがでかいコードが混ざると-Wがうざったくなるので、関数を小さく作るいい癖もつきそう。

何気なく使っているツールを少し調べるだけでも生産性は上げられるなと改めて痛感。

発展途上なところ

とは言え、言語最適化されていないためグローバルなスコープでワードがマッチすると期待した結果とは異なる表示になる。

たとえば、-Wオプションを使っている時にGoのファイルの関数コメントとマッチしたときはファイル冒頭のimport文まで結果が表示された。

git grep -W TODO backend/**/*.go
backend/mysql/mysql.go=import (
backend/mysql/mysql.go- "log"
backend/mysql/mysql.go- "strconv"
backend/mysql/mysql.go- "time"
backend/mysql/mysql.go-
backend/mysql/mysql.go- "github.com/jinzhu/gorm"
backend/mysql/mysql.go- // database connector
backend/mysql/mysql.go- _ "github.com/jinzhu/gorm/dialects/mysql"
backend/mysql/mysql.go-)
backend/mysql/mysql.go-
backend/mysql/mysql.go:// Task TODO
backend/mysql/mysql.go-// http://doc.gorm.io/models.html