Gitで巨大プロジェクトを扱うときに少し便利なupdate-ref

ビルドに時間がかかる(数十分~数時間以上)プロジェクトを扱うときに役立つかもしれない、Gitの小ネタです。

Gitには git help しても出てこない( git help -a すれば出る)便利なコマンドがたくさんあり(※)、そのうちの1つ update-ref のご紹介です。

※他には例えば update-index --assume-unchanged なども有名ですね。

どんなときに欲しくなるか

こんな感じの、あるヘッダファイルに多数のソースファイルが依存するプロジェクトがあったとします。

repos
 |- common.hpp
 |- source1.cpp
 |- source2.cpp
 |- source3.cpp
 |- source4.cpp
 |- source5.cpp
 |- ...

まずは master ブランチにいます。

$ git status
On branch master
nothing to commit, working tree clean

新機能を開発するので、featureブランチを切ります。

$ git checkout -b feature/hoge
Switched to a new branch 'feature/hoge'

featureブランチでは新機能開発のため、共通ヘッダを更新しました。

$ vi config.hpp

ローカルでビルド・テストしてみて、動作は良好です。

$ cmake .
$ ninja

出来上がったのでpushしてMerge Request / Pull Requestを出します。

$ git commit -am "new feature"
[feature/hoge 45817d2] new feature
 1 file changed, 1 insertion(+), 1 deletion(-)

$ git push origin feature/hoge
Total 0 (delta 0), reused 0 (delta 0)
To git@example.com:practice.git
 * [new branch]      feature/hoge -> feature/hoge

無事レビューを通過し、Web UI上でマージボタンが押されました。

o new feature [feature/hoge]
o initial commit [master]

↓

o Merge branch 'feature/hoge' into 'master' [origin/master]
|-
| | new feature
|-
o master [master]

ここで、自分もブランチから戻るのに、

$ git checkout master
$ git pull origin master

のようにしてしまうと、1行目のコマンドの時点でconfig.hppが一旦古いバージョンに戻ってしまいます
その後2行目のコマンドで新しいバージョンになりますが、ファイルのタイムスタンプが更新されるので、リビルドが必要になります。
リビルドに数時間かかるようなプロジェクトでは、これは致命的です。

update-refの登場

update-ref は、対象のブランチが指すコミットを書き換えるのに使えます。ワークツリーは feature/hoge にいるままで、 master の指す先を origin/master と同じにしてしまいましょう。

$ git fetch
$ git update-ref refs/heads/master origin/master
$ git checkout master

この順番で操作すると、3行目のコマンドでmasterブランチに切り替えても、config.hppの中身に変更がないためタイムスタンプも変わりません。したがって、無駄なリビルドは回避されます。

※なお、gitコマンドを使わず .git/refs/heads/master を書き換えても同じ結果が得られますが、gitコマンドのほうが若干事故りにくいでしょう。

それ以外の対応案と補足

update-refを使わず、この方法でも良いと思います。

$ git branch -D master
$ git fetch
$ git checkout -b master origin/master

社内で聞いてみたら、「masterに直接pushしない運用にしている以上、手元にmasterブランチがある意味がないし、事故の元だから手元にmasterブランチは作らない」という声もありました。

常にこのような形でブランチ運用するわけですね。

$ git fetch
$ git checkout -b feature/new origin/master

言われてみると、とても理にかなった方法です。gitは人によって使いこなし方が違うので、聞いてみると発見があって面白いですね。

僕の場合は癖のレベルですが「とりあえず最新版をビルドしたい」「これから何をやるかまだ決まってないからブランチ名が思いつかない」などによってmasterが欲しい人なので、 update-ref を使い続けています。

その後追記

これで良いのではという指摘をいただきました。こっちのほうが簡単ですね 💦

$ git fetch
$ git branch -f master origin/master

関連記事

【社内勉強会】バージョン管理の重要性とGitの運用について

デザインも頼めるシステム開発会社をお探しならBPS株式会社までどうぞ 開発エンジニア積極採用中です! Ruby on Rails の開発なら実績豊富なBPS

この記事の著者

baba

ゆとりプログラマー。 高校時代から趣味でプログラミングを初め、そのままコードを書き続けて現在に至る。慶應義塾大学環境情報学部(SFC)卒業。BPS設立初期に在学中から参加している最古参メンバーの一人。Ruby on Rails、PHP、Androidアプリ、Windows/Macアプリ、超縦書の開発などを気まぐれにやる。軽度の資格マニアで、情報処理技術者試験(16区分17回 + 情報処理安全確保支援士試験)、技術士(情報工学部門)、Ruby Programmer Gold、AWSソリューションアーキテクト(アソシエイト)、日商簿記2級、漢検準1級などを保有。

babaの書いた記事

夏のTechRachoフェア2019

週刊Railsウォッチ

インフラ

ActiveSupport探訪シリーズ