morimorihogeです。残暑やばい。
※元々は2014年に書いた記事ですが、2020年になっていろいろと事情も変わっているので2020年revise版として更新しました。
弊社ではバージョン管理システムにGitを使っています。
数ヶ月以上一緒にやっているある程度ツーカーなメンバーだけのプロジェクトなら問題無いのですが、案件によっては協力会社の方が一時的にJOINしたり、新規参入メンバーの参加などで、これまでGitを使ったことがない、または本格的なチーム開発でGitを使ったことがない人が参加することもあります。
※2020年現在では流石に全くGitを使ったことのない開発者というのはほぼ見なくなりましたが、チーム開発できちんと運用に乗せて使ったことがない、という所は今でもそこそこあるようです。
Gitは自由度の高いシステムですが、その分概念を覚えることが必要なため、導入の敷居が高い方だと思います。そして、仕事で開発をしている場合はどうしても概念の勉強などよりも結果を早く出すことが求められるため、Gitのことをよく理解しないまま間違ったコミットや変更をgit pushしてしまい、リポジトリを壊してしまったり、復旧が面倒なミスをしてしまうことが起こり得ます。
本記事では僕がここ10年ほどまともにGitを使ってチーム開発を続けてきて発生した「やっちゃった」事例を紹介しようと思います。Railsアプリ開発を前提としていますが、Rails以外でも応用は利くと思います。
前提環境(弊社における基本運用方針)
GitリポジトリサーバにはGitLabを使っており、ブランチ運用方針は原則git-flowを用いますが、案件によっては社内開発用とお客様確認用環境のブランチを分割して運用したりすることもあるため、厳密にgit-flowとは限りません。
また、masterブランチは本番環境リポジトリになるためGitLabの設定でプロジェクトの責任者以外からはprotectするようにしていますが、そもそも弊社ではプロジェクトの開発チームが1〜3名ということも多いので、protectの意味がないこともあります。merge requestについても同様で、少人数プロジェクトではオーバーヘッドの割に効果は無いと考えているため、基本的には使っていません(プロジェクトによって使っているものもある)。
最後に、masterブランチ、developブランチに対してそれぞれJenkinsで自動テストを回しています。自動テストはテストのcoverageが低くてもrakeタスクが起動しないレベルの破壊的変更程度なら検知できるので、とりあえず設定しておいて損はないと思います。
※2020年現在、GitLab Runnerを主に使うようになっています。
事例一覧
あるある事例を見ていきましょう。
⚓ プロジェクト独自のリポジトリ運用ルールに従わないで作業してしまう
仕事で開発していると、諸々のしがらみやお客様側の特殊な要求条件などの問題から、綺麗にgit-flow(またはその組織が通常採用しているメジャーなリポジトリ運用方式)に乗らないことがままあります。
既に走り出したプロジェクトに後からJOINする場合、必ずそのプロジェクトでのリポジトリ運用方針を確認しましょう。
Git運用は色々なケースが考えられるので一概には言えないのですが、以下の点辺りは確認しておくべきだと思います。
- コミットコメントの規則(日本語OK/英語のみ、Redmineなどのチケット番号の記載有無などなど)
- featureブランチのdevelopへのmergeルール(勝手にmergeしてよい/merge request必須など、rebase派/merge派)
- プロジェクト独自のルール(developにpushしたらstaging環境にcap deployするなど)
- 自動テストなど、Git連携するものの把握
⚓ git push -f
でリモートブランチの過去を改変してしまい、他のメンバがpushもpullもできなくなった
多分一番よくやる奴だと思います。push -fに至るまでの経過は色々なケースが考えられますが、原則他のメンバがcheckoutしている可能性のあるリモートブランチに対してpush -fしてはいけません。
リモートブランチに対してpush -fが許容されるのは多くの場合「自分しか利用していないリモートブランチだけ」です。
push -fはリモートリポジトリに既に公開されている(== 他のメンバも手元にpullしている)コミット履歴を改変してしまうので、他のメンバがpushしたコミットを削除してしまう可能性があります。Gitのエラーメッセージやググったサイトに「push -fすればいいよ」と書いてあっても従ってはいけません。
自分で判断できなければ判断できるプロジェクトメンバに相談するのが一番良いと思います。
git-flowを使った具体的な例で言えば、masterやdevelopはpush -fしてはいけないブランチ、featureブランチは一人で開発している場合、かつdevelopにmergeする前においてのみpush -fしても大丈夫です。
具体的なケースとしては、バックアップがてら毎日の作業途中のfeatureブランチをサーバにpushしておき、最終的なdevelopブランチのmerge前にsquash等でコミット内容を整理したいといったことが挙げられるでしょう。
⚓ 開発環境に便利だからドットファイルやdatabase.ymlを追加・編集してpushしたらテストサーバが起動しなくなったり、他メンバの環境が動かなくなった
開発に慣れてきた頃にやりがちだと思います。ここで言うドットファイルとは「.ruby-version」や「.gitignore」、「.idea(RubyMineの設定ファイルディレクトリ)」などがあります。
他にもRailsなら「Gemfile.lock」、「db/schema.rb」については原則リポジトリに置くべきファイルですが(hachi8833の記事参照)、運用方針の都合などであえてリポジトリに配置していないこともあるかもしれません。
また、サーバ環境に依存するファイルである「config/database.yml」「config/environments/(production|development).rb」なんかも一開発者の個人判断で編集・追加・削除すると危険です。
この手の環境依存系のファイルを修正する際は、修正する前にプロジェクトメンバに一声かけて、影響がないかどうかを確認した上で修正しましょう。特に、本番環境に詳しい人のチェックはしてもらった方が良いです。
⚓ 日本語ファイル名のファイルであばばばば
Mac-Linux環境間で起こります。Mac環境の人はgit configでcore.precomposeunicodeを有効にしましょう。
$ git config --global core.precomposeunicode true
⚓ 使い終わったremote branchがサーバ上に残り、branchが増えていく
手動でmerge運用をしていると、mergeし終わったfeatureブランチを消し忘れてしまい、大量のリモートブランチが残ってしまうことがあります。
直ちに問題になることはないと思いますが、リポジトリの見通しを良くするためにも使い終わったbranchは削除しましょう。
$ git branch -d unused_branch
$ git push origin :unused_branch
2020年版事例たち
2020年になってちょいちょい思うことなどを追記してみました。
⚓ Windows環境でconfig.autocrlfを有効にして改行コード変更のコミットを大量生成してしまう
ここ最近の問題というわけではないのですが、個人的にmacOS環境からWindows環境に移ったので事例として挙げておきます。
config.autocrlf
というコンフィグパラメータがあるのですが、これがエディタの自動改行コード変換機能などと干渉すると、ただ開いただけのつもりのファイルの改行コードが片っ端から置換されていく悲劇が発生することがあります。
config.autocrlf
の設定値については以下の記事が詳しいので参考までに。
なお、コマンドライン経由ではなくIDE経由でGitを使っている場合などはこの手の設定パラメータが良しなに設定されて(またはIDE側の設定で上書きされて)CLI実行時と挙動が異なることがあります。
この辺り、Windowsネイティブなシェル環境で作業をする場合には抑えておきましょう。
※僕はWSLやLinux VM環境から作業することが多いので今ではハマること自体がなくなりました
⚓ 意味のないマイクロコミットが大量にある
Gitのコミット粒度をどうするかというのは意見がいろいろとあるところではありますが、少なくともPRベース開発におけるコミットはある程度区切りのついたまとまりにすべきでしょう。特に、大きめのPRであればある程度作業の区切りごとになっている方がレビューしやすいということもあると思います。
ごくたまにローカルファイル保存のような感覚でcommitしているケースを見るのですが、あれはつらい。
最近のGitHubやGitLabにはPRのmerge時にcommitをsquashするオプションもありますので、PRの時点でコミットが複数あることはそれほど問題にならないこともあるのですが、squash運用しない場合は特にコミット粒度についてはある程度意味のあるまとまりにしておくことが大事です。
後々コードを読む人がgit blame
したときに意味不明なことにならない程度には後で追えるようにしておきたいものです。
⚓ やたら古いバージョンのGitを使っている
Gitは頻繁にバージョンアップされています。手持ちの開発環境でもし古いGitを使っているようなら、できればアップグレードすべきでしょう。速度面でも有利ですし、便利なコマンドが増えています。
最近の僕のお気に入りはgit switch
で、checkout -b
とかやっていた時代にはもう戻りたくないですね(毎回オプションをググっていた)。
macOS環境だとOS/Developer Tool付属の古いGitを使っている人がたまにいますが、Homebrewで個別にGitをインストールすれば新しいものが入りますので入れてみると良いかと思います。
⚓ 雑なgit add *
運用によりいらんファイルがcommitされてしまった
コマンドラインでのGit操作をしている際に、差分ファイルが増えてくるとついgit add *
とやってしまう人がいるのを見ます。
この際、追加するつもりのなかったファイルもaddしてしまい、そのまま気づかずcommitしてしまうというパターンです。
.gitignore
を適切に書いておけばエディタの一時ファイルなどは阻止できることもあるのですが、手元でこねくり回していた作業用のCSVファイルだったり、ビルドツールが作った自動生成ファイル、temporaryな作業用ディレクトリなどを事故で追加してしまうことはあり得ます。
コマンドライン運用するならgit commit前のgit status
チェックは念入りにやりましょう。エイヤでcommitすると修正するのが大変です。
また、そもそも慣れてないならコマンドライン運用しないという手もあると思います(後述)。
⚓ コマンドライン操作に固執した結果いらんミスをしてしまう
Gitの使い方解説サイトや解説書を読むと、コマンドライン上でのGitコマンドを使った方法が解説されていることが多いのですが、あまりコマンドライン操作に慣れていない人であればGit操作の概念の学習ができた後はGUIツールを使うことをおすすめしたいです。
ツールは何でもよいのですが、以下の機能があればよいでしょう。
- commit時にcommit対象ファイルごとのdiffを閲覧しながら確認できること
- branch作成、pushなど一般的なPRベース開発で使う操作が簡単な操作でできること
- ある程度リッチなrebase支援機能があること
僕がメインで使っているのはJetBrains IDEのGitツールで、主にRubyMineに内蔵されたものを使っています。
コミット時にファイルごとのdiffを見つつ、チェックボックスで追加・解除できたり、
今チェックアウトしているbranchを任意のbranchにrebaseしたりといった操作が簡単にできます。
※conflictが発生すると、都度mergeエディタが開いて解決していく形になります。
その他にも、SourcetreeやVisual Studio CodeのGit機能など、最近のツールであれば概ね似たような機能があるので自分の好みに合わせたものを選べばよいと思います。
Gitのコマンドライン操作のいらんミスの多くはGUIツールを使うことで避けられることが多いので、コマンドライン操作でミスするくらいなら潔くGUIツールを使うべきでしょう。僕もrebase作業なんかはCLIでやる気がしないです。
まとめ
そんなわけでいくつか実際に起こった事例をベースに書いてみました。ここに書いた以外にも色々な事例があるかとは思いますが、どんなケースでも重要なのはチーム開発においては「郷に入っては郷に従え」ということです。
はてブホッテントリに上がってきたりするような記事ばかり追いかけていると最新の環境や思想、ツールが目に入りがちですが、実際の現場では歴史的事情やメンバ(社内外問わず)のスキルレベルの問題から新しいものを使えないこともあります。
より良いとされている方法があっても、チーム開発ではメンバ全体で共有されていなければ開発の障害になってしまうこともあるので、これからチーム開発を始める人は気をつけると良いと思います。
ちなみに個人で開発している場合はここに書いてきたことは当てはまりません。どんどん新しいものを試してノウハウを溜めて、良さそうなものならチームに提案して改善していけると良いですね。
個人的にはGitは細かく見ていくと複雑で人類には早すぎるシステムだと思っています。なるべくイレギュラーなことはしないように、一般的な手順だけをするようにした方が地雷は踏みにくいと思います。
2020年版で内容をreviseしてみて思いましたが、ここ5-6年ほどでGitの利用自体は広まりましたが初心者的なハマりどころは昔と大して変わらないように思います。
使い方に慣れてしまうと昔自分も初心者だったことを忘れて新しい初心者に対してイキりがちですが、そこはぐっとこらえて説明してあげられる心の余裕を持ちたいものですね。