Git入門:バージョン管理のきほん
チームでのコンフリクトを減らす
この回でやること
コンフリクトは解決するより起こさないほうが速いです。ブランチを長生きさせない、こまめに main を取り込む、道具をそろえるという 3 つの予防策を身につけましょう。
- 読む 約 8 分
30 ファイルのコンフリクトは、2 週間前に決まっている
コンフリクトの解決そのものは、慣れれば数分で終わります。本当のコストは時間ではありません。両方の意図を読み解いて正しい形を作る作業なので、判断を間違えると他人の修正を無言で消してしまいます。しかも誰も気づきません。目標は「速く直せる」ではなく「起きる回数を減らす」です。
Git がコンフリクトを出すのは、次の 3 つが同時に成立したときだけです。裏を返せば、どれかを崩せば起きません。
| 条件 | 崩し方 |
|---|---|
| 分岐してから長く経っている | ブランチを短命にする |
main との差が開いている | こまめに main を取り込む |
| 同じファイルの近い行を両方が触る | 道具で無意味な差分を減らす |
いちばん効くのは 1 つ目です。ブランチが生きている期間が長いほど、その間に main へ入る他人の変更が増えます。自分の変更も大きくなるので、衝突する組み合わせは掛け算で増えていきます。画面全体を一度に作らず、まず表示だけ、次に入力、次に保存、と動く最小単位に割ってください。
1 日 1 回取り込むだけで、直す量が変わる
ブランチを切ったら、そこから先は他人の変更を自動では受け取りません。放っておくと、自分の手元だけが古い main を基準にした世界になります。
ターミナル
$ git fetch origin
$ git merge origin/main毎日やってもコンフリクトが起きなくなるわけではありません。変わるのは、起きるコンフリクトが小さくなり、記憶が新しいうちに解決できることです。昨日の自分と昨日の同僚の変更なら、どちらの意図も覚えています。3 週間分がまとめて来ると、そうはいきません。なお取り込みは、自分の作業をコミットしてから行います。
内容が対立していないのに、全行がぶつかる
コンフリクトの中には、内容の対立がまったくないものがあります。Windows は改行を CRLF、Mac と Linux は LF で表すので、何もしないと Windows の人が保存しただけで全行が変更扱いになります。リポジトリの直下に .gitattributes を置いて固定します。
プレーンテキスト
* text=auto eol=lf
*.png binary1 行目は「テキストファイルはリポジトリ内では LF で持つ」という指定です。設定がリポジトリに入るので全員に同じルールが効き、個人設定に頼るより確実です。すでに CRLF で入っているファイルは、git add --renormalize . で一度そろえます。
もう 1 つの原因は自動整形です。A さんのエディタはセミコロンを付け、B さんのは付けない。同じファイルを触ると、実質の変更が 1 行でも差分は数十行になり、そのすべてが衝突の候補になります。Prettier や ESLint の設定もリポジトリに入れて、全員の結果をそろえてください。
--ours で片付けると、消えたことに誰も気づかない
2 週間かけたブランチをマージしようとして、30 ファイルがコンフリクトしたとします。ここで最もやりがちなのが、片側を丸ごと採用して片付けることです。
ターミナル
$ git checkout --ours src/api.js--ours は自分側の内容をそのまま採用するという意味です。解決したように見えますが、この 2 週間に他の人が src/api.js に入れた修正は、すべて消えます。しかもコンフリクトは解消されているので、Git は何も警告しません。バグとして現れるのは数週間後です。
--ours と --theirs は、package-lock.json のような生成物、つまり片方を採ってから作り直せばよいと確信できるファイルにだけ使います。人が書いたコードには使いません。そしてこの 30 ファイルは、2 週間の間に 1 日 1 回 main を取り込んでいれば起きませんでした。予防が効くのは、こういう場面です。
- コンフリクトは「分岐が長い」「差が開く」「同じ行を触る」の 3 条件で起きる
- ブランチは 1〜3 日で終わらせ、1 日 1 回
mainを取り込む .gitattributesとフォーマッタ設定で、内容のない差分をなくす--oursを人が書いたコードに使わない。相手の修正が無警告で消える