開発のチームリーダーになる前に知っておくべき9個のgitコマンドとtips

前回の記事「ぼっちプログラマがチームに入る前に知っておきたい6つの git コマンド」を読んでぼっちは脱却するわチーム作業はうまくいくわ人間関係も円滑だわでえらい事になったあなたに、チームリーダーの話が回ってきました。出世はうれしいが初めてのチーム管理、どうやればいいのか……。
いま困っている人と、まだ来ない話を夢見る人に git プロジェクトマネジメントのコツをお届けします。

❗ 前回に引き続き、1つの中央リポジトリに対して push/pull をしていくものとして説明していきます

.gitignore

プロジェクトでの使用言語やフレームワークが決まったら、まずは不要なファイルを見極めましょう。

  • プロジェクトによるもの: ビルドシステムで生成されるファイル・設定ファイル・ログ
  • 開発環境によるもの: エディタのスワップファイル・ IDE のプロジェクト設定ファイル

プロジェクトによるものはリポジトリ内の .gitignore に書けばいいですね。データベースへの接続設定を行うファイルなど、環境によって異なる内容が必要なファイルは、テンプレートを database.yml.sample などとして本物を .gitignore へ追加すると便利です。

開発環境によるものは個人で管理してもらいましょう。

core.excludesfile に設定ファイルを指定すると、そこに書いたファイルはどのリポジトリでも無視されます。たとえば OS X で vim を使っているならば、以下のコマンドで最低限の対応ができます。

$ echo "*.swp" >> ~/.gitignore_global  # ファイル名は好きなもので OK
$ echo ".DS_Store" >> ~/.gitignore_global
$ git config --global core.excludesfile ~/.gitignore_global

これで、プロジェクトを新規作成するたびにメンバーの開発環境を確認しながら .gitignore を書く手間が省けます。ただし初めてチーム作業をするメンバーにはこのことを忘れずに伝えなければなりません。

  • github/gitignore · GitHub: github が提供している .gitignore。環境やプロジェクトルールに合わせて使いましょう。
  • How to manage Rails database.yml: 環境固有の設定ファイルをリポジトリへ入れないためのベストプラクティス。デプロイ時の対応もあり。

コミットメッセージのフォーマット

コミットメッセージは意図を反映したわかりやすいものを書くというのが鉄則ですが、複数人でログを重ねていくと微妙なフォーマットの違いが読みづらさを招きます。形式を揃えるにははじめが肝心。できるだけプロジェクトの開始前に決めておきましょう。

コミットメッセージを揃えなかった例

コミットメッセージを揃えなかった例。行ごとに体裁が変わって読みづらい。

統一しておくとよいものは例えば…

言語:
英語や日本語など。業務上のやり取りでメインの言語を採用しましょう。オープンソース化を考えているならば英語一択です。

文体:
英語ならば過去形/現在形の扱い、文頭を大文字にするかどうか。日本語なら、常体・敬体・言い切り。

関連するチケット:
プロジェクトマネジメントツールやバグ管理システムを使っている場合に。書き方や位置も揃えないと、例えば人によって行頭・行末・3行目以降とバラバラでは読みづらくなります。チケット駆動開発では必須。

メールを書くつもりで、1行目を件名・残りを本文として書くように徹底すると最低限のフォーマットは揃います。ポイントは2行目に空行を入れること。これを忘れると git log --oneline の結果がひどいことになります。。

上: 空行を入れなかった場合 下: 2行目を空にした場合

上: 空行を入れなかった場合
下: 2行目を空にした場合

オープンソースプロジェクトのルールを参考にするのもいいでしょう。

フック

メッセージのフォーマットの他にも守るべきルールは様々です。コーディング規約に違反するコードや、テストに通らないコードはできるだけ早くに弾いてしまいたいもの。
こうした場合に便利なのが git の特定の場面でスクリプトを動作させる仕組み フック です。

たとえば pre-commit フックは git commit の直前(pre)にスクリプトを動かし、その終了ステータスによってはコミットが中止されます。

デフォルトでサンプルが同梱されていますから試してみましょう。行末の空白文字をチェックするスクリプトです。

$ cp -a .git/hooks/pre-commit.sample .git/hooks/pre-commit
$ echo "spaces->  " > space
$ git add space
$ git commit
space:1: trailing whitespace.
+spaces-> 

コミットが中止されました。これで規約違反も簡単に発見できます。

問題は --no-verify オプションで回避できることですが、そのあたりはリーダーの采配にかかっていますね。他に、特定のブランチに push されたらデプロイする・更新をメールで通知するなど様々な使い道が考えられます。

権限管理

メンバーが増えてきたら権限管理が大切です。同じ会社の同僚であっても、プロジェクトとまったく関係のない人間からのコミットは歓迎できませんし、極秘のプロジェクトや守秘義務のからんだ案件など読み取りすら禁止したい場合もあるはずです。

gitosis ではリポジトリに対する読み書きの権限を設定できます。設定の管理に git を使うユニークなシステム…でしたが、既に開発が止まっていて別のシステムが推奨されています。

それが gitolite

アクセス制御の対象を、リポジトリだけでなくブランチやファイル単位で設定できます。

merge とブランチモデル

ブランチモデル

下準備が終わったら、採用するブランチモデルを選びましょう。要は開発の中で主軸となる共有ブランチを、いくつ作るか・どのように使い分けるかを決めるということです。個人のローカルブランチを制限する法はありませんよ。

複数人で取り組む大きめのプロジェクトでは、開発版・安定版ブランチを使うモデルが適しています。
通常は開発版ブランチへコミットを重ね、統合テストが無事に完了したことを確認して安定版ブランチへマージします。信頼性が高まりますし、いつでも安定版を取得できるメリットがあります。たいていは master / develop ブランチという名称が使われます。

逆に、静的なウェブサイトやドキュメント管理といった、不安定なバージョンが特になく派生も生じないようなプロジェクトでは master ブランチのみでも事足りるでしょう。

merge の種類

git のマージには2種類あります。 fast forward マージか、そうでないマージか。

fast forward は git の端々で見かけますが、マージ先が共通の祖先から進んでいないとき、ブランチを表すポインタを移動することでマージに変えることを意味します。それ以外の場合ではマージコミットを作成しなければなりません。

feature ブランチを develop ブランチにマージしたいとします。このとき develop ブランチが枝分かれしたときから進んでいるかどうかで fast forward の可否が決まります。

fast forwad の可否

fast forwad の可否

fast forward マージ

fast forward マージは、マージ先のブランチにコミットだけが追加され、余計な情報は残されません。マージしたことをほぼ意識せずに使えるので、例えばタスクごとにブランチを切るような、短いスパンで大量のブランチを使い捨てていく場合に向いています。

fast forward マージ

fast forward マージ

マージ先に変更があると fast forwad できませんが、マージの元となるブランチ(先の例では feature)をあらかじめ rebase すると枝分かれがなくなります。ただし、 rebase はコミットを書き換えます。すでに送信したコミットを commit --amend できない(してはならないよう)に、ブランチを公開していない・自分しかそのブランチを使っていないという具合に使える場合が限られています。

派生元ブランチが進んでしまった

派生元ブランチが進んでしまったら

rebase で分岐点を進める

rebase で分岐点を進める

fast forwad でないマージ

もう一つは、マージ時にマージコミットを作る方法です。
コンフリクトは、最後のマージコミット(紫の四角)内で解消されます。このマージコミット内には、通常のコミットにあるコミットしたひとや日付だけでなく、どのコミット同士をマージしたかという情報が含まれます。

図ではコミットオブジェクトはマージ元のブランチにしかないように見えますが、実際はどちらのブランチにも存在しています。

マージコミットが作られる方のマージ

マージコミットが作られる方のマージ

fast forwad マージと比べて次のような特徴があります。

  • マージコミットが目印となり、区切りが明確になる
    → 開発ブランチを安定版ブランチにマージするときに
  • マージコミットを見ればどこからどこまでがマージされたかわかるので、後々不具合が起こっても特定・修正しやすい
    → pull request など、品質に100%の保証がないブランチをマージするときに
  • マージ前に rebase でコミットを改変しなくてよい
    → 複数人で使っている・マージした後も使い続けるブランチをマージするときに

状況に応じてこの2つのマージを使い分けることが、リポジトリ管理のコツです。

merge

これで開発の下地が整いました。あとは実際にブランチを切ってコミットを重ねて……ブランチが区切りを迎えたらマージを実行する番です。
マージには merge コマンドを使います。マージの種類を変えるにはオプションを付けましょう。

  • merge --ff もしくは単に merge では fast forward マージを試み、できないときはコンフリクトを解消したことを示すマージコミットを作成します。
  • merge --ff-only とすると fast forward できないときはマージを中止します。
  • merge --no-ff オプションをつけると fast forward できる場合でも必ずマージコミットを作ります。

マージの種類や作法についてもっと知りたい方はリンクを参照してください。

merge –squash

コードレビューとやり直しを繰り返すうちに、気づけば最初のコミットの後は小さな修正ばかりが続いていませんか?

大きな失敗と修正であれば成長の過程として残しておいてもいいかもしれませんが、ちょっとしたタイプミスや凡ミスまでログに残るのはすこし恥ずかしい。しかもレビューのために push する必要があったため、もう commit --amend はできません。
こんなときは、ブランチを1つのコミットにまとめる merge --squash でマージしてもらいましょう。

ブランチ内のコミットが1つにまとめられる

ブランチ内のコミットが1つにまとめられる

merge --squash を実行すると、差分がすべて反映されるところはまでは通常のマージと同じですが、変更点がすべてステージングされるところまで終わり、コミットオブジェクトは作られません。

あとは commit を実行すれば、古いブランチに入っていたすべてのコミットの情報(コミットハッシュ・Author・Date・コミットメッセージ)がコミットメッセージとして入っていますから、適当に編集して完了です。

ブランチ内に違う目的のコミットが入っていた場合、まとめられたコミットの意図が不明瞭なものになってしまうので気をつける必要はあります。多用は難しく、使い所が限られていますが、歴史修正主義者・ログ潔癖症の方には喜ばれるかもしれません。

format-patch & am

コミットのやり取りは push/pull 以外でも行うことができます。format-patch でコミットをパッチに変えましょう。

$ git format-patch -M origin/develop
0001-Add-A-Feature-Test.patch
0002-...
:

現在のブランチにあって origin/develop にないコミットを、1コミット1ファイルとして書き出せます。 -M はファイルのリネームを追跡するオプションです。

できたパッチをメールやストレージ経由で受け取ったら、今度は am でパッチからコミットを作ります。

$ git am 0001-Add-A-Feature-Test.patch
Applying: Add A Feature Test
$ git log -1 --oneline
3c912ee Add A Feature Test
$ git am 002-...
:

たとえばこんな使い道があります。

  • 権限管理の代替案として
  • オフライン環境からの push 代行に
  • pull request の代わりに
    • どうしても公開リポジトリが持てないとき
    • 受け取る側の許可を得てからにしましょう
  • 差分をウェブで公開したい
    • パッチの本分ですね

branch -d

無事にマージが終わったら、用済みになったブランチに別れを告げましょう。

$ git branch -d oldbranch  #ローカル
$ git push origin :oldbranch  # リモート
$ git push --delete origin oldbranch # リモート (v1.7.0 以上)

おしまい

これでひとまずのチーム運営はできそうですね。チームリーダーどころかプロジェクトマネージャ、ひいては社内すべてのリポジトリを任される git マネージャにまでなれるかも?

その前に git での共同作業について確認しておきたいなら「ぼっちプログラマがチームに入る前に知っておきたい6つの git コマンド」をどうぞ。

Reactions on Twitter

  1. @moichi82 より:

    開発のチームリーダーになる前に知っておくべき9個のgitコマンドとtips http://t.co/bOURobjjzQ

    解りやすい。

  2. @zyunnosuke より:

    コレは勉強になった〜〜

    開発のチームリーダーになる前に知っておくべき9個のgitコマンドとtips http://t.co/M99tvAbwK4

  3. @yohhatu より:

    続けて読んだ。フックは便利だなぁと思うけど、チームによっては窮屈になったりしないのかな? 開発のチームリーダーになる前に知っておくべき9個のgitコマンドとtips http://t.co/NO2qPOTYNA

  4. @tomotomobile より:

    タイトルに釣られ / “開発のチームリーダーになる前に知っておくべき9個のgitコマンドとtips” http://t.co/za9iBog2Db

  5. @uokada より:

    開発のチームリーダーになる前に知っておくべき9個のgitコマンドとtips http://t.co/8PDXK2I2Ef 最初からここまでやらなくても回り始めた段階でここにたどり着けばいいのか

  6. @tera911 より:

    開発のチームリーダーになる前に知っておくべき9個のgitコマンドとtips http://t.co/sk2DUnUpTO これは覚えておきたい。fast forwadとか何も考えてなかったけどこういうのを理解して使えるようにならないとなぁ・・・