GitHub開発フロー

Gitとは

Gitはソースコードの変更履歴を記録・追跡できるバージョン管理ソフトウェアである。GitHub(Gitの仕組みを利用し、オンライン上でのバージョン管理できるようにしたWebサービス)とは明確に区別しておいた方が良いが、セットで利用する場合が多い。

Gitをうまく利用することで、ソースコードの変更理由(5W)を把握でき、必要に応じてコードへの理解を深めたり、任意の時点の状態を復元したりすることに役立つ。

  • When:タイムスタンプを見れば、いつコードを変更したのかがわかる
  • Where:タイムスタンプに対応したファイルが変更されたファイルだとわかる
  • Who:authorを見れば、誰がコードを変更したのかがわかる
  • What:diffを見れば、何を(コードをどのように)変更したのかわかる
  • Why:コミットメッセージを読めば、なぜ変更されたのかわかる
  • When、Where、Who、Whatはシステム側に任せられるため、他人のアカウントを使ってコミットするなどをしなければその恩恵に預かれるが、Whyの部分は「なぜ(どんな目的で)コードを変更したのか」をきちんとコミットメッセージとして残しておく努力(あるいは仕組み)が必要となる。役に立たない変更日付のみのコミットメッセージなどを付けていると、test_0114.txt, test_0117.txt, …, test_0206.txtといったファイルの中から所望のファイルを探すのと大差なくなってしまう(もちろんdiffの恩恵は受けられるが)。

    Gitアンチパターン

    役に立たないコミットメッセージ

    日付や著名などの不要な情報、エラー修正などの漠然とした情報しか記載されていないコミットメッセージは役に立たないコミットメッセージである。役に立たないコミットメッセージばかりだとどんな経緯で変更されてきたのかわからず、どの状態に戻せばよいのかもわからなくなる。

    2022/11/02修正
    部長の指示通りに修正

    コミットの粒度が大きすぎる

    コミットの粒度が大きすぎる場合、コードレビューが大変になるだけでなく、保存ポイントが少ないので修正が難しく、大きな手戻りが発生する可能性がある。コミットはノイズを含まない程度に小さくしておくと良い。適切なコミットメッセージが思いつかない場合は、たいていは粒度が大きすぎる。

    masterブランチで直接作業する

    masterブランチは常にデプロイ可能な状態にしておかなければならない。そのため、masterブランチで直接作業せず、必ずブランチを切って作業すると良い。

    ブランチ名が適切でない

    ブランチ命名規約はプロジェクトによって変わってくるが、何をするブランチなのか過不足なく表現するブランチ名にすると良い。

    ブランチを切りすぎる

    ブランチを切るとマージするときにレビューやコンフリクトの解消などで必ずコストが発生する。必要なところに時間をかけ、そうでないところにはあまり時間をかけないようにブランチ戦略を考えると良い。

    不要なファイルが増えていく

    Gitを使っているのに、test_old.txtなどの不要なファイルが増えていくことがある。バージョン管理はGitに任せて、元のファイルに変更を加えていくか、別のモジュールをして切り出して全く異なる名前をつけるかして、不要なファイルは作らないようにすると良い。

    ファイル名を変更する

    ファイル名を変更すると、Gitに新しく作成したファイルと認識され、それまでのファイルとは別物として管理される。そのため、ファイル名が異なっていて過去の対応するファイルを見つけられないなど、追跡が難しくなる可能性がある。

    他人のアカウントでコミットする

    共有PCを使っている場合など、他人のアカウントでコミットしてしまうことがある。これだと、誰が変更したのかわからなくなってしまう。

    コードにコメントで変更履歴を残す

    変更履歴はGitが管理してくれているため、コードへの記載は不要。コードに記載してしまうとそれはノイズとなり、コードの理解を妨げる。

    # 変更履歴
    # ------------------------------------
    # 2019/10/09:get_description()メソッドの追加
    # 2019/10/15:output_log()メソッドの追加
    # 2019/10/23:Managementクラスの削除
    # 2019/11/02:get_description()メソッドのエラーの修正
    # ボブによる追加

    メンバーの修正を無視してコンフリクトを解消

    メンバーの修正を取り込まず、自分の変更のみを加えたコードで上書きしてしまう。これによって誰かの修正が無視される。自分が修正を加えた部分以外にdiffが発生している場合は、予期しない変更がないか確認すると良い。

    .gitignoreに記載すべきファイルを記載していない

    .envファイルのような共有してはいけないファイル、個人の開発環境固有の設定ファイルやログのような共有する必要のないファイルは、.gitignoreに記載してコミットに含めないようにすると良い。

    Forkを利用しない開発

    社内開発のようなクローズドな開発では、Fork機能は利用せず、対象のリポジトリを直接編集することで開発を行う。

    Notion Image

    開発の流れ

  • リモートリポジトリをローカル環境にcloneする。
  • ローカル環境でトピックブランチを作成する。
  • トピックブランチのコードを変更、コミットする。
  • トピックブランチをリモートリポジトリにpushする。
  • トピックブランチからmasterブランチにPull Requestする。
  • Forkを利用した開発

    不特定多数の人が開発者になるOSSのようなパブリックな開発では、リポジトリへのアクセス権を開発者全員に付与できないため、Forkを利用して開発を行う。

    Notion Image

    開発の流れ

  • Github上でForkし、自分のアカウントにリポジトリをコピーする。
  • Forkしたリポジトリをローカル環境にcloneする。
  • ローカル環境でトピックブランチを作成する。
  • トピックブランチのコードを変更、コミットする。
  • トピックブランチをForkしたリポジトリにpushする。
  • Fork元のリポジトリにPull Requestする。
  • GitHub Flow

    GitHub Flowは、Github社が提唱するブランチ戦略で、後述のGit Flowをよりシンプルにしたもの。GitHub社では、15~20人のチームでGitHub Flowを実践していて、経験上20人程度までは大きな問題は発生しないといわれている。masterとfeatureの2種類のブランチからなるシンプルな構成になっている。

    Notion Image

    開発の流れ

  • masterブランチからfeatureブランチを作成する。
  • featureブランチで実装完了後、masterブランチへPull Requestする。
  • featureブランチの内容をレビューし、問題なければmasterブランチにリポジトリ管理者がマージする。
  • 上記1~3を繰り返して、機能を作り上げる。
  • Git Flow

    Git Flowは、Vincent Driessen氏が提唱しているブランチ戦略である。master、develop、feature、release、hotfixesの5種類のブランチを使用し、ブランチごとに用途が決まっている。使用するブランチの数が多く、小中規模の開発には複雑すぎる場合がある。

    Notion Image

    開発の流れ

  • masterブランチからdevelopブランチを作成する。
  • developブランチからfeatureブランチを作成して機能を実装する。
  • featureブランチで実装完了後、developブランチに変更を取り込む。
  • 上記2と3を繰り返し、リリースできるレベルまで機能を作り上げる。
  • リリースのためのreleaseブランチを作成しリリース作業をする。
  • リリース作業が完了したら、masterブランチと統合し、バージョンタグを打ってリリースする。
  • リリースしたものにバグがあれば、hotfixesブランチで修正する。

  • あらゆる開発フローに共通するルール

    masterブランチは常にデプロイできる状態にする

    masterブランチを常にデプロイできる状態にすることで、大きなバグが入り込んだり、どのブランチが最新の状態かわからなくなったりといったことがなくなり、開発フローがシンプルになる。

    トピックブランチを使う

    チームで開発を行う際には、他の開発者とコンフリクトしないように、masterブランチからトピックブランチを作成する。トピックブランチでは一つのテーマに集中して作業を実施し、作業が完了したらmasterブランチにマージする。

    デプロイ作業は自動化する

    上記の開発フローでは、頻繁にデプロイを実施する。不要な手間や人的ミスが発生しないようにデプロイを自動化しておく必要がある。

    テストを重要視する

    デプロイする前には必ずテストを実施するため、デプロイと同様にテストも自動化が必要になる。同時に開発者各自によるテストも必要である。すべてのテストをパスして、テストのあるコードのみをmasterブランチにマージする。

    必ず他の開発者からレビューを受ける

    他の開発者のレビューを通すことで、思い込みやミスを未然に防ぎ、コードの品質を向上できる。

    レビューを実施しやすいように工夫する

    変更したコードの量が多かったり、変更の粒度が揃っていなかったりするとレビューの負担が大きくなる。しっかりとしたレビューを行うためには、プルリクエストのサイズを小さくしたり、タイポの修正のような不要なコミット履歴は削除するなど工夫する必要がある。

    GitHub Flowを体験する

  • サンプルリポジトリを自分のアカウントにForkする
  • Notion Image

  • Issues機能を有効にする Settings > Features > Issuesにチェックを入れる
  • Notion Image

  • タスクをIssuesとして追加する
  • Notion Image
    Notion Image

  • VSCodeを開き、リポジトリのクローンを押す
  • Notion Image

  • 「Githubから複製」を押し、Forkしたリポジトリを選択する
  • Notion Image
    Notion Image

  • 任意のディレクトリを選択して、「リポジトリの宛先として選択」を押す
  • Notion Image

  • 「クローンしたリポジトリを開きますか?」と聞かれるので「開く」を押す
  • Notion Image

  • 新しくトピックブランチを作成する。
  • Notion Image

  • ブランチ名を「add-good-night」としてEnterを押す
  • Notion Image

  • ブランチが切り替わっていることを確認する
  • Notion Image

  • 新しいブランチで作業していることを伝えるため、リモートリポジトリにプッシュする
  • Notion Image

  • 「このブランチを公開しますか?」と聞かれるので「OK」を押す
  • Notion Image

  • Github上にブランチが作成されていることを確認する
  • Notion Image

  • 以下のようにコードを変更して、新機能を実装する
  • # src/sample.py
    
    class GreetRes():
    
        def __init__(self, request):
            self.request = request
    
        def get(self):
            if self.request == "おはよう":
                return "おはようございます。"
            elif self.request == "こんにちは":
                return "こんにちは。"
            elif self.request == "こんばんは":
                return "こんばんは。"
            elif self.request == "おやすみ": # ←この行を追加
              return "おやすみなさい。"       # ←この行を追加
            else:
                raise Exception("予期しない入力値です。")

  • GitHubに変更をPushする
  • Notion Image

  • ポップアップが表示されるので「OK、今後は表示しない」を押す
  • Notion Image

  • GitHub上で「pull request」ボタンを押す
  • Notion Image

  • 「add-good-night」から「main」へのプルリクエストであることを確認して、プルリクエストを作成する(#で対応するIssueを紐づける)
  • Notion Image

  • プルリクエストを作成したら、他の開発者からのフィードバックを待つ(今回は自分でフィードバックコメントを作成する)

  • 「プルリクエスト」タブから対象のプルリクエストを選択
  • Notion Image

  • コミット情報から、コードの差分を確認して、レビューを実施する
  • Notion Image

  • 該当箇所にカーソルをあわせて「+」ボタンを押し、コメントを入力する
  • Notion Image

  • さらに、テストコードを追加を促すようにコメントする
  • Notion Image

  • レビューを完了する
  • Notion Image

  • 他の開発者からのフィードバックを受けたら、以下のようにコードを修正する。
  • # src/sample.py
    
    class GreetRes():
    
        def __init__(self, request):
            self.request = request
    
        def get(self):
            if self.request == "おはよう":
                return "おはようございます。"
            elif self.request == "こんにちは":
                return "こんにちは。"
            elif self.request == "こんばんは":
                return "こんばんは。"
            elif self.request == "おやすみ":
                return "おやすみなさい。"     # ←この行を修正
            else:
                raise Exception("予期しない入力値です。")
    # src/test_sample.py
    
    def test_Chatbot5():  # ←このテストを追加
        chatbot = sample.Chatbot()
        res = chatbot.get_response("おやすみ")
        assert res == "おやすみなさい。"

  • GitHubに変更をPushする
  • Notion Image

  • コードを修正した旨を伝える
  • Notion Image

  • プルリクエストがマージされたら完了
  • Notion Image

    コンフリクトが発生した場合

  • 「Conflicting files」というメッセージが出てMergeできないので、一旦pull requestを閉じる
  • Notion Image

  • mainブランチに移動して、変更されたmainブランチをpullしてくる
  • Notion Image

  • トピックブランチに移動する
  • Notion Image

  • mainブランチとマージして変更を取り込む
  • Notion Image
    Notion Image
    Notion Image

  • コンフリクトしている部分を編集してコンフリクトを解消する
  • Notion Image

  • 変更をpushしたら、もう一度pull requestを実施し、コンフリクトが解消されていることを確認する
  • Notion Image

    参考資料


    著者画像

    ゆうき

    2018/04からITエンジニアとして活動、2021/11から独立。主な使用言語はPython, TypeScript, SAS, etc.