BIGLOBEの「はたらく人」と「トガッた技術」

あなたはmerge派?rebase派?綺麗なGitログで実感したメリット

f:id:biglobe-editor2:20220316093707j:plain

BIGLOBEの開発現場の様子や、developブランチにrebaseで綺麗なコミット履歴を作る方法をご紹介します。

はじめまして!

基盤本部(開発部門)の江角です。 2021年8月にSIerからBIGLOBEに転職し、半年が経過しました。

転職期間中はもちろんコロナ禍で、カジュアル面談も面接も全てオンラインでした(多分今もそうだと思います)。 入社日当日は出社しましたが、入社してから半年の間で出社した日は11日のみという、ほぼフルリモート!

「リモートで仕事していて、どう…?」と色んな方からお声がけいただきますが、正直あまり困っていません。

分からない点は時間を取ってGoogle Meetで教えていただけますし、 IntelliJ IDEAのCode with meを使ったペアプロ・モブプロなども多く実践しています。

週に1度、上司との 1on1 で不安なことや困っていることはざっくばらんに話せていますし、 オンラインランチ会で他チームの方とお話したりすることもあります。

今年2022年の1月には今のチームで「Scala/Akkaのモブプロを酒の肴にリモート飲み会」という居酒屋では出来ないようなこともやりました。

それもこれも、リモートでも困らないよう開発環境をスピーディーに改善していただいたり、チームメンバーのサポートがあってこそだと思っています。 色んな会社があると思いますが、BIGLOBEは開発環境の改善という点では色々対応頂けているように思います!

GitHubを中心に仕事がまわる開発現場

今、私は契約管理に関するチーム(6名体制)でスクラム開発をしています。

前職で経験してきたウォーターフォール型の開発とは違い、 細かい仕様変更や、利用しているライブラリーや言語のバージョンアップ・アップデートにも素早く対応しリリースするスピード感にとても衝撃を受けています。

契約管理の開発チームで使っているツールは以下のような感じです。

  • ソース管理:GitHub
  • インフラ:Amazon Web Service(AWS)
  • 開発言語:Java、JavaScript、Groovy
  • ビルドツール:Gradle

割と見覚えのある言語/ツールではないでしょうか。

GitHub上ではソースコードだけでなく、業務上の課題もIssueで管理しています。 チャットではどうしても流れて追えなくなったりすることもあるので、問題や議論に上がった内容で新しいIssueを作成し、議事録などはIssueにコメントしていくことで作業漏れを防いでいます。 スプレッドシートでの管理と違い、PR(Pull Request)や他Issueとの紐づけが簡単なのも良い点ですね。

ソース管理は、A Successful Git branching modelで紹介されているGit-flowというブランチ管理/マージ法を実践しています。

ブランチ管理が複雑という声もありますが、 (私自身もGitに触れてこなかったため、最初見た時は複雑!と思いました) 定期的なリリースかつリリースタイミングを自由に変更できる点で今のチームにあっていると感じています。

以下はGitをあまり触ってこなかった私が、今のチームで色々教えてもらいつつ、勉強しつつで 今やGitコマンドであまり困らなくなった事を共有させてください。

Git logが綺麗だとバグが起こりにくい?

ここからは、feature ブランチで新機能を追加していくときのlogの話です(develop にマージされた後の操作まではやりません)。

契約管理チームはGit logにも綺麗さを求めます。 契約管理チームのGit logはdevelopブランチ(黒線)に対してfeatureブランチ(青線・緑線)が綺麗に並んでいます。

f:id:biglobe-editor2:20220311214039p:plain

Git logを綺麗にするなんて最初は趣味の問題だと思っていましたが、 開発を進めるにあたって利点が分かってきました。

1. バグが発生しにくい
「どんな開発現場でもこのGit logを実現すればバグが発生しない!」とは言っていません。 …ですが、マージした前後関係などが洗い出しやすいです。 マージする際も不毛なコンフリクトが発生しないため、修正作業による手作業が減り、結果としてバグが起こりにくいと言えるのではないでしょうか。
2. ブランチ迷子にならない
作業中のfeatureブランチは最新のdevelop付近に集まるため、今どのブランチで何が起こっているのかを把握するのが容易です。 以下の例はすこし大袈裟ですが、Git logを俯瞰したとき、スクロールしてそれぞれのfeatureブランチで何が起こっているかを頭で整理する必要もありません。

f:id:biglobe-editor2:20220311214117p:plain

契約管理チームのGit logをどうやって実現しているかを、図解しながら説明していきます。

developブランチを綺麗に保つGit操作(マージ編)

今のGit logは以下のとおりで、別のチームメンバーが新機能A(feature/A)を担当しています。

f:id:biglobe-editor2:20220311214141p:plain

ここで、読者の皆さんが新機能B(feature/B)を実装することになりました。 早速、developから新機能B(feature/B)のブランチを作成して、ソースをcommitします。

しかし、チームメンバーにレビュー依頼をしている間に新機能Aのレビューが終わり、developにマージされてしまいました。

f:id:biglobe-editor2:20220311214203p:plain

feature/Bは、この後どのようにdevelopブランチへマージすると良さそうでしょうか。 皆さんならどのようにマージしますか?

  1. そのまま気にせずdevelopにマージする
  2. 最新のdevelopをfeature/Bブランチに取り込んでからdevelopにマージする
  3. 最新のdevelopにrebaseしてからマージする

では実例を見ていきましょう。

1. そのまま気にせずdevelopにマージする。

結論から言ってしまうと、1. の対応はあまりオススメできません。

なぜなら、最新のdevelopの内容がわからないままfeature/Bをマージすることになるからです。 もしfeature/Aで同じファイルを編集していた場合、コンフリクトが発生したり、 「最新のdevelopとfeature/Bが合わさることでビルドが通らない」なんてことも起こる可能性は大いにあります。

実際の操作だけ見ておきましょう。 GitHub上では以下のようにPRのMerge pull requestを押下してマージができます。 Merge pull requestを押下して実際にマージするとブランチは以下のような形になります。

f:id:biglobe-editor2:20220311214228p:plain

f:id:biglobe-editor2:20220311214244p:plain

2. 最新のdevelopをfeature/Bブランチに取り込んでからdevelopにマージする

実際の操作としては以下のコマンドを叩くことで最新のdevelopの内容をfeature/Bブランチに取り込めます。

feature/Bブランチにいます。
> git branch
  develop
* feature/B

developブランチの最新コミットをローカルに取得します。
> git fetch

最新のdevelopの内容をfeature/Bブランチに取り込み、
> git merge origin/develop

origin/feature/Bブランチに反映します。
> git push

この操作では、1では対応していなかった「最新の内容を取り込む」という操作をするため、 コンフリクトやビルドの可能性を事前にfeature/Bブランチで確認できるようになります。

上記コマンドを実施後PRをマージするとGit logは以下のようになります。

f:id:biglobe-editor2:20220311214315p:plain

git merge origin/developのコマンドでfeature/Bブランチにマージコミット(Ⓜ)が作成されます。 ブランチ作成時の分岐も変わらないため、featureブランチを長く使うほどGit logは平行線が長く、developを取り込むほどマージコミットが増え続けます。

2.はチーム開発としての最低限はしておきたい操作ですが、 Git logを綺麗にしたいチームにはオススメできない対応です。

3. 最新のdevelopにrebaseしてからマージする

今のチームではこの手法が推奨されています。

チームに参画したての頃は『リベースしてからマージしてね』という言葉をよく聞きました。 「リベースって、あの、歴史改変の禁断のコマンドじゃないですか!?えぇ、そんなことやって許されるのですか!?」 ま、先輩が言うならやるんでしょう。やるしかありません! ちょうどGitHubのPR上にRebase and mergeってあるので...、

「コレですね!?」 『それじゃない。ローカルで叩くリベース』

今のチームで飛び交う『リベース』は GitHubのRebase and mergeではなく、-iオプションや--ontoオプションを付けない、git rebaseコマンドを意味します。

f:id:biglobe-editor2:20220311214342p:plain 👈('ω' )コレですね!?(違う)

茶番はさておき、コマンド操作から見て行きましょう。

feature/Bブランチにいます。
> git branch
  develop
* feature/B

developブランチの最新コミットをローカルに取得します。
> git fetch

feature/B作成時の分岐点をdevelopに移動(再定義)します。
> git rebase develop

origin/feature/Bブランチに反映します。
> git push -f origin feature/B

git rebase developでは最新のdevelopからfeature/Bブランチを分岐させる形に変更できます。

rebaseはコミットをもう一度作り直す操作になります。言わば歴史改変のコマンドです。 そのため、git pushは、より強力にした呪文(-f : force push)を唱えないとリモートに反映できません。

これらのコマンドを実行後、PRをマージしたものが以下のGit logになります。

f:id:biglobe-editor2:20220311214443p:plain

1.では対応できていなかった「最新の内容を取り込む」という点でもクリアでき、 2.でネックとなっていた「ブランチの平行線が長くなる」「マージコミットが出来てしまう」という問題も、rebaseで解決できます。

もちろん、意味単位でコミットしておくことで、単なる作業ログにしないように気をつけています。

リベース コワクナイョ

慣れてしまえばなんて事はないですが、最初はドキドキしながら作業してました。何せ歴史改h…

確かにCONFLICT の文字が並ぶと一瞬思考停止してしまいますが、 「コミットを作り直そうとしてコンフリクトが発生したんだな」という理解と1回の深呼吸があれば大丈夫です。

競合しているファイルを修正して再度コミットを作り直した後、git rebase –-continueを叩いてSuccessfully rebased and updatedというゴールが見えるまでrebaseの旅を続けましょう。

git rebase –-continueを叩いては競合を解消してコミットして、を繰り返すのは骨が折れるかもしれませんがgit logを見ることで自分がどこまで進んだのかが解りますし、 単純な競合であれば1クリックするだけで解消してくれる最近のIDEも助けになってくれたりします。

また今回は紹介できませんでしたが、 -iオプションを付けてコミットを整理したり、--ontoオプションでブランチごと付け替える手法もあります。

-iオプションで複数コミットを1つ纏めた後、「やっぱり纏める前に戻したい」という歴史改変はできないので、悩むようであればrebase前にバックアップブランチの取得をお忘れなく!!

最後に

プロジェクトによってブランチやGit運用ルールがあると思うので、今回紹介したブランチ運用が全ての正解とは言えないのですが、同様のブランチ管理をしている方や、このGit log見やすくて良いな!と思ってくださった方、今は個人開発だけど今後Gitを使ったチーム開発に携わる方に、この記事が参考になれば幸いです。

なおBIGLOBEには複数の開発チームがあり、開発言語やツールも様々です。 今回紹介した開発現場は、あくまで一例ということだけご認識ください。

BIGLOBEでは新しい技術を積極的に取り入れた開発に挑戦しています。興味のある方は、カジュアル面談でお話してみませんか?

hrmos.co

※ Google Meetは、Google LLCの商標です。

※ IntelliJ、IntelliJ IDEA は、JetBrains s.r.o.の商標または登録商標です。

※ GitHub は、GitHub Inc.の商標または登録商標です。

※ Amazon Web Servicesは、米国その他の諸国における、 Amazon.com, Inc.またはその関連会社の商標です。

※ Java、JavaScriptは、Oracle Corporationおよびその子会社、関連会社の米国およびその他の国における登録商標です。

※ Gradleは、Gradle Inc.の商標または登録商標です。

※ 記載している団体、製品名、サービス名称は各社またはその関連会社の商標または登録商標です。