【第4回】 Git/Githubを使って共同開発をしてみよう

会社やサークルなどの組織の中で、複数人で同じプログラムを開発することはよくあります。GitとGithubを使って並行して開発する方法を学びましょう。

Homeブログ一覧【第4回】 Git/Githubを使って共同開発をしてみよう

おさらい

前回【第3回】 Git/Githubを使ってコードを管理しようでは、GitとGithubを実際に使ってみました。

Gitの基本的な流れ

Gitを使う上での基本的な流れをおさらいしておきましょう。

Gitはローカルでファイルの変更履歴を管理する仕組み・ツールです。

まずはローカルにリポジトリを作成します。 リポジトリとは、Gitで管理するファイルやディレクトリのことを指します。 git initコマンドを実行することで、そのディレクトリをリポジトリとして扱うことができます。

  • ファイルを変更する
  • 変更をステージにAddする git add <file>
  • 変更をCommitする git commit -m "<message>"

これを繰り返すことで、変更履歴を管理することができます。

いまどの状態かを確認するには、git statusを実行します。 過去の変更履歴を確認するには、git logを実行します。

Githubの基本的な流れ

Githubを使う上での基本的な流れをおさらいしておきましょう。

GithubはリモートでGitログのクラウド管理を行い、さらに不特定多数の人との共同開発をマネジメントする仕組み・ツール・サイトです。

  • Github上でリポジトリを作成する
  • Gitのローカルリポジトリにリモートリポジトリを登録する git remote add origin <url>
  • ローカルリポジトリの変更をリモートリポジトリにPushする git push origin <branch>

これを繰り返すことで、リモートリポジトリに変更履歴をどんどん追加していくことができます。

使うコマンドまとめ

用語役割コマンド
初期化リポジトリを作成するgit initgit init
アド(ステージ)変更をステージに追加するgit add <file>git add index.html
コミット変更をリポジトリに追加するgit commit -m "<message>"git commit -m "add index.html"
プッシュ変更をリモートリポジトリに追加するgit push origin <branch>git push origin master
ステータス現在の状態を確認するgit statusgit status
ログ過去の変更履歴を確認するgit loggit log
リモートリモートリポジトリを登録するgit remote add origin <url>git remote add origin
サイクル
はい
いいえ
はい
いいえ
ファイルを変更する
変更をステージにAddする
変更をCommitする
ひと段落ついたか
初期化
リモートリポジトリに登録済みか
リモートリポジトリを登録
リモートリポジトリにPush

Gitでの共同開発に必要な知識

ブランチ

ブランチとは、リポジトリの変更履歴を分岐させることができる機能です。 歴史や樹形図のようなものをイメージするとわかりやすいです。

コミットを一まとまりとして扱うことができ、共同開発においてこのブランチが機能開発の単位となります。

ブランチを作成するには、git branch <branch>を実行します。 ブランチを切り替えるには、git checkout <branch>を実行します。

発展

git checkoutコマンドは、ブランチの切り替え以外にも、コミットの切り替えや、ファイルの復元などにも使うことができます。 過去のコミットへ一時的に戻りたいときは、git checkout <commitのハッシュ>を実行します。 (あたらにgit switchコマンドが追加されまして、こちらも利用できます。)

また、便利コマンドとして、git checkout -b <branch>があります。 これは、git branch <branch>git checkout <branch>を同時に実行するコマンドです。 ブランチを作成して、そのブランチに切り替えるという作業を一度に行うことができます。

マージ

マージとは、ブランチを統合することです。 ブランチを作成して機能を開発し、その機能が完成したらマージして統合します。

マージをするには、git merge <branch>を実行します。 マージすると、マージ元のブランチにマージ先のブランチの変更履歴が追加されます。

コンフリクト

コンフリクトとは、マージするときに起こる衝突のことです。 もちろん複数人や複数の歴史で同じ場所を変更すると衝突が起きますよね?

コンフリクトが起きたときは、git statusでコンフリクトの場所を確認することができます。

やってみよう

前回【第3回】 Git/Githubを使ってコードを管理しようで作成したリポジトリを使います。

Gitではinitした時点でmainというブランチが一番大元のブランチとして作成されます。

ブランチを作成する

まずは、ブランチを作成してみましょう。 git branch <branch>を実行することで、ブランチを作成することができます。

git branch add-function

ブランチを切り替える

次に、ブランチを切り替えてみましょう。 git checkout <branch>を実行することで、ブランチを切り替えることができます。

git checkout add-function

いまどのブランチにいるかを確認するには、git branchを実行します。

git branch
* add-function
  main

*がついているブランチが現在のブランチです。

ファイルを変更する

ブランチを切り替えたら、ファイルを変更してみましょう。 ブランチ名のとおり、今回はaddという関数を変更として追加してみます。

main.cpp
#include <bits/stdc++.h>
using namespace std;

int add(int a, int b) {
    return a + b;
}

int main() {
    cout << add(3, -2) << endl;
    return 0;
}

変更をステージにAddする

ファイルを変更したら、変更をステージにAddしてみましょう。

git add main.cpp

変更をCommitする

ファイルを変更したら、変更をステージにAddしてみましょう。

git commit -m "足し算の関数を追加"

これで、変更履歴がリポジトリに追加されました。

確認してみよう

git logを実行して、変更履歴を確認してみましょう。

git log
commit <commitのハッシュ> (HEAD -> add-function)
Author: <username> <email>
Date:   <date>

    足し算の関数を追加

commit <commitのハッシュ> (origin/main, origin/HEAD, main)
Author: <username> <email>
Date:   <date>

    Hello WorldをHello Maximumに修正

commit <commitのハッシュ>
Author: <username> <email>
Date:   <date>

    Hello Worldと表示するプログラムを作成

HEADがついているのが現在のブランチです。 mainではなくadd-functionになっていることがわかります。

視覚的に見る

VSCodeの拡張機能であるGit Graphを使うと、視覚的に変更履歴を確認することができます。 これを使って、変更履歴を確認してみましょう。

はい、こんな感じで枝分かれした先に変更履歴が追加されていることがわかります。

では、mainブランチに戻ってみましょう。

git checkout main

ローカルでマージする

mainブランチに戻ったら、add-functionブランチをmainブランチにマージしてみましょう。

git merge add-function

はい、これでadd-functionブランチの変更履歴がmainブランチに追加されました。

リモートリポジトリにPushする

ローカルでマージしたら、リモートリポジトリにPushしてみましょう。

git push origin main

これで、リモートリポジトリにも変更履歴が追加されました。

Githubでの共同開発に必要な知識

Githubを使うことで、複数人で同じプログラムを開発することができます。 さらにMaximumなど、組織を作って管理権限を付与したり、プロジェクトをまとめて管理したりすることもできます。

Githubでの共同開発に必要な知識を学びましょう。

Issue

Issueとは、Github上でタスク管理を行う機能です。

Issueを作成することで、タスクを登録することができます。 Issue Flowという開発体制があり、一般にはこのフローで開発することが多いため、こちらをベースに説明していきます。

Pull Request

先ほどブランチをローカル環境でMergeしましたが、共同開発たるもの、全て自分がMergeの権限を持つわけではありません。 そこでGithub上で「自分のコードよかったらMergeしてください」というリクエストを出すことができます。

これをPUll Requestと言います。

レビュー

コードがいいかどうかチェックする工程(主にGithub上でのこと)をレビューと言います。基本はテックリードやCTOに当たるポジションのエンジニアが、部下の開発したコードが動くか、正しく欠けているかをチェックします。

リモートでのマージ

リモートでもローカルと同じようにMergeができます。MergeができるタイミングはPull Requestを承諾した時です。

実際にやってみましょう

これ以降説明を簡単にするために、登場人物を設定します。

あなたがAさんで、共同開発者役をBさんが担当するとします。 ここでAさんが監督、Bさんが作業者とします。

Aさん
を作成する

Issueを作成してみます。

今回は先ほど作った足し算の関数の引数が負の数だったときに、エラーを返すように修正するタスクをするとします。

「Issue」タブをクリックして、New Issueをクリックします。

New Issue

タイトルに「負の数の足し算を規制する」と入力し、Assignerにそのタスクを担当する人を選択します。

Assigner

この画像の赤い枠で囲ったところです。 誰か共同開発者役のペアを作って、その人が担当するとわかりやすいです。 もし共同開発者の方の名前を入力しても出てこない場合は、SettingsタブのCollaboratorsから招待してください。

AさんがIssueを作成したら、作業者であるBさんにIssueをAssignします。

Bさん:作業リポジトリをCloneする

もし、作業対象のリポジトリが作業者のローカル環境にない場合は、リモートリポジトリをCloneします。 もしすでにローカルにリポジトリがある場合はこの作業は不要です。

git clone <url>

このとき、リモートリポジトリのURLは、Github上のCodeタブのリポジトリの右上にある緑色Codeボタンからコピーできます。

Bさんはその組織に入ったばかりで、ローカルにリポジトリがないので、リモートリポジトリをCloneしてきます。

Bさん:ブランチを作成する

Issueを作成したら、作業者であるBさんがブランチを作成します。 ローカルでもGithub上でもどちらでもブランチを作成できますが、Issue Flow形式で進めたいので、Github上でブランチを作成します。

Issue画面の右下にCreate a Branchというボタンがあるので、クリックしてブランチを作成します。

Create a Branch

この画像の黄色い枠で囲ったところです。

ブランチ名は英語で、もっと言うとkebab-caseで書くのが一般的です。 今回はvalidate-negative-numberというブランチ名にします。 そうすると、このコマンドを実行してねと言わんばかりのコマンドが出てきます。

git fetch origin
git checkout <設定したブランチ名>

これをコピーして、ローカルで実行します。

git branch

と実行してブランチが作成されていることと、そのブランチにいることを確認します。

BさんはAさんにこのIssueをAssignされたので、その機能を開発するためのブランチを作成します。

Bさん:機能を開発する

ブランチを作成したら、機能を開発します。 今回は、足し算の関数の引数が負の数だったときに、エラーを返すように修正するタスクをするとしました。

main.cpp
#include <bits/stdc++.h>
using namespace std;

int add(int a, int b) {
    if (a < 0 || b < 0) {
        return exit(1);
    }
    return a + b;
}

int main() {
    cout << add(3, -2) << endl;
    return 0;
}

これをローカルでコミットします。

git add main.cpp
git commit -m "負の数の足し算を規制"

BさんはAssignされたIssueの内容に従って、ローカルで機能を開発します。

Bさん:リモートリポジトリにPushする

機能を開発したら、リモートリポジトリにPushします。

git push origin <今いるブランチ名>

Bさんは機能を開発し終えたので、リモートリポジトリにPushします。

Bさん
Requestを作成する

リモートリポジトリにPushしたら、Pull Requestを作成します。 共同開発では直接mainへ開発した機能をマージするのではなく、基本的にはPull Requestを作成して、監督者のレビューを受けてからマージします。 自分が開発したコードに「バグがないか」「コードが汚くないか」「セキュリティ上問題ないか」などをチェックしてもらうためです。

Pull Requestを作成するには、Github上でCompare & pull requestをクリックします。 もし出てこない場合はPull RequestタブからNew pull requestをクリックして、マージ先をmain、マージ元を今pushしたブランチに設定してください。

そしたら、Pull Requestのタイトルと本文をレビューする側の人がわかりやすいように書きます。

Reviewersにレビューする人を設定します。 レビューする人は複数人設定することができます。

Bさんは機能を開発し終えたので、Pull Requestを作成します。 ReviewersにAさんを設定します。

なぜ本文やタイトル、Commit Messageを細かく書くのか

レビューをする時は基本、Pull Requestの本文とコミットメッセージを見て大まかな内容や開発の流れを把握します。 みなさんも他人の書いたコードを読む時、読むのにとても時間がかかると思います。 それと同じで、レビューをする人はPull Requestの本文やコミットメッセージを読むのに時間がかかります。 このレビュワーの負担をできるだけ軽減できる工夫がPull Requestの本文やタイトル、コミットメッセージを細かく書くということです。

Aさん
Requestをレビューする

Pull Requestを作成したら、レビューをします。

レビューをするには、Pull Requestの画面のFiles changedタブをクリックします。 そうすると、Pull Requestに含まれる変更履歴が表示されます。 ここでは変更履歴を確認して、問題がないかをチェックします。

今回の例ではexit(1)という関数を使っているので、これは問題ありです。 exit(1)はプログラムを強制終了させる関数なので、これを使うとプログラムが強制終了してしまいます。

Review

レビューコメントは行単位でつけることができますので、今回私はこのようにexit(1)の行にレビューコメントをつけました。 全てのレビューを終えたら、Review changesをクリックして、レビューを完了します。 このとき、もしレビューに問題がある場合はRequest changesをクリックし、逆に問題がない場合はApproveをクリックします。

AさんはBさんが作成したPull Requestをレビューします。 今回はexit(1)を使っているので、コードに問題があると判断しました。 そのため、Request changesをクリックして、Bさんに修正を依頼します。 exit(0)を使うように修正してもらいましょう。

Bさん:再び機能を開発する

レビューを受けたら、Pull Requestを作成したブランチに戻って、機能を修正します。

main.cpp
#include <bits/stdc++.h>
using namespace std;

int add(int a, int b) {
    if (a < 0 || b < 0) {
        return exit(0);
    }
    return a + b;
}

int main() {
    cout << add(3, -2) << endl;
    return 0;
}

また、AddしてCommitしてPushします。(省略)

Pushすると、Pull Requestに修正が反映されます。 そのため、再度レビューをしてもらうように依頼します。 Reviewers部分のRe-request reviewをクリックすると、レビューを再度依頼することができます。

BさんはAさんからレビューを受けたので、Pull Requestを作成したブランチに戻って、機能を修正します。 今回はexit(1)exit(0)に修正しました。 その後、AddしてCommitしてPushします。 これで、Pull Requestに修正が反映されました。 Reviewers部分のRe-request reviewをクリックして、再度レビューを依頼します。

Aさん:再びPull Requestをレビューする

さあ、再修正されたPull Requestをレビューしましょう。

(省略)

今度は問題ないので、Approveをクリックします。

AさんはBさんから再度レビューを依頼されたので、Pull Requestを再度レビューします。 今回はexit(1)exit(0)に修正してもらったので、問題ないと判断しました。 そのため、Approveをクリックして、Pull Requestを承認します。

Aさん:マージする

Pull Requestを承認したら、マージします。 Pull Requestをマージするのは基本CTOテックリードなどの監督の人が行います。

Pull Requestの画面のMerge pull requestをクリックします。

AさんはPull Requestを承認したので、Pull Requestをマージします。 Pull Requestの画面のMerge pull requestをクリックして、Pull Requestをマージします。 これで、Pull Requestに含まれる変更履歴がmainブランチに追加されました。

マージされると、今までOpenだったPull RequestがMergedになります。 さらに、Issueからブランチを作成したため、IssueもClosedになります。 これでIssue Flowにおける一つの機能開発の単位が完了しました。

まとめ

これを図にすると、以下のようになります。

ある
ない
はい
いいえ
Approve
Request changes
リポジトリがローカルに
リポジトリをClone
Issueを作成 & ブランチを作成
機能を開発
開発したコードをAdd & Commit
ひと段落ついたか
リモートリポジトリにPush
Pull Requestを作成
レビュー
レビューのステータスが
マージ