ビルドに時間がかかる(数十分~数時間以上)プロジェクトを扱うときに役立つかもしれない、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