はじめに
2021年の秋にはじめてハッカソンに出場してから、今まで4回ほどチーム開発を経験してみて、Git操作やコミットメッセージについて思うことがあったので、備忘録としてここにまとめることにします。
内容としては、チーム開発する上でのGitの基本操作(ハンズオン形式)やコミットメッセージの記法等です。
前提知識:Gitの基本概念をざっくりと理解している
実行環境:Windows10, GitHub, GitBash(Macの人でも理解できる内容)
到達レベル:チーム開発でGitを使う分には不自由なく使いこなせるようになる
そもそもGitとは?
Gitとはソースコードのバージョン管理を行うためのシステムのこと。
開発をしていると、変更前のソースコードに戻したくなる時は誰しも経験あるはず!!
Gitを知らないと、ソースコードのバックアップをこまめに取りながら開発をしなければなりません。
Gitがあれば、そのような面倒な手間を省くことができます。
たまに、「Gitはチーム開発の時しか使わないものだと勘違いしてた」という人を見かけますが、上記の点からも分かるように、個人開発でもGitは当たり前のように使われます。
Gitの準備
Gitのユーザー情報を設定
-
Windows OSの場合
C:\Users\[ユーザー名]\.gitconfig
上記のファイルを編集。 -
MacOSの場合
~/.gitconfig
上記のファイルを編集。 -
CLIで設定する場合
$ git config --global user.name [ユーザー名] $ git config --global user.email [メールアドレス]
を実行する。
Gitのパスを通す
Gitがインストールできたら、Path(環境変数)を通します。
環境変数の設定方法は以下のサイトを参考にすると分かりやすいです。
設定後はPCを再起動させることをお忘れなく!
GitHubの登録
チーム開発の主な流れ
ここでは、既にチーム開発のリモートリポジトリが存在している前提で解説します。
また、GitはGUIでもCLIでも操作可能ですが、この記事ではCLIで解説します。
理由としては、CLI操作の方が断然簡単だからです。
この記事を読んでイマイチよく理解できない人は、SourcetreeなどのGUI操作ツールでGitの基本概念を一通り学んでから読み直すと理解しやすいかと。
クローン
さっそく開発に貢献していくためには、チームのリポジトリをローカル環境に持ってくる必要があります。また、基本的にクローンはプロジェクトを開始する最初の一回のみ実行することが多いです。
(次回以降は、後述する「プル」という動作でリモートリポジトリの更新をローカルリポジトリに反映させます。)
まず、チームのリポジトリのURLにアクセスし、緑色の「Code」ボタンを選択し、HTTPSのURLをコピーします。
その後、コマンドプロンプト(ターミナル)またはその他のコマンドツールを用いて、自分がこれから作業したいディレクトリに移動します。
cd [任意のディレクトリ]
git clone [コピーしたリポジトリのURL]
例)
cd hoge/foo/bar
git clone https://github.com/hogehoge/hogehogehoge.git
実際に、エクスプローラーを開いて指定したディレクトリにクローンしてきたリポジトリ(フォルダ)があるか確認する。
フォルダの中身を見ると、「.git」というフォルダが確認できます。
Windowsではエクスプローラーの表示メニューから「隠しファイル」のチェックを入れると見れるようになります。
これは、「このフォルダはGit管理されている」ことを示しているくらいの理解で十分かと。
これでローカル環境の準備は整いました!
ステージング
引用元:https://eng-entrance.com/git-add
流れをざっくり言うと、
「クローンしてきたリポジトリを編集して、ローカルリポジトリに反映させたい時は、自分のローカルのディレクトリ(ワーキングツリー)をインデックスにステージング(git add)し、それをローカルリポジトリにコミット(git commit)する必要がある。。。。。。」
百聞は一見に如かずということで、さっそくステージングしてみます。
git add [ステージングするファイル名]
例) git add test.txt
基本的に編集したファイルを上記のコマンドを実行してステージングします。
さすがにこれで一個ずつ実行するのは面倒なので、
git add .
を実行することでワーキング・ツリーのすべてのファイルをステージングすることができます。
また、以下のコマンドでローカルリポジトリとの差分(変更・作成・削除したファイル)だけをステージングすることもできます。
git add -u
そして、上記の「git add .」と「git add -u」を同時に実行するコマンド
git add -A
も存在します。
困ったときは「git add .」で十分。
ステージングを取り消したい場合
git reset HEAD [取り消したいファイル名]
HEADは現在自分がいるブランチを指す。
コミット
ステージングが完了したら、コミットしてローカルリポジトリに反映させます。
コミットは以下のコマンドで実行できます。
git commit -m "コメントを記入"
-m は --message の略記
ここで、コミットする際は、コメントを残して変更履歴の内容を記録する必要があります。
なぜなら、Gitで前のバージョンにソースコードを戻したい時にコミット単位で戻すからです。
なので、機能毎でこまめにコミットすることがオススメ!!
ここで、重要なのはコメントの書き方です。
個人的には、コメントの書き方を共有したいがためにこの記事を書いたまであるかも。
具体的なコメントの記法をチーム内で統一している場合は問題ないですが、特に定まってない場合は、チームメンバーへの配慮のためにも以下のようなコメントを残すと良いと思います。
[update] 更新した内容
[add] 追加した内容
[remove] 削除した内容
[fix] 修正した内容
このように、コメントに変更種別と内容を記入することで一目で分かりやすくなります。
また、できれば変更理由も記入したほうがより良いです。
例)
git commit -m "[fix] 日付データが更新されないバグの修正"
コミットID
また、Gitには一つ一つのコミットに対してハッシュ値が割り振られています。
このハッシュ値をコミットIDと呼びます。
これは、後述するバージョン変更の際に、コミット単位で戻すため、コミットを識別する必要があるからです。
コミットID の確認方法
コミットIDの確認方法はGUI操作とCLI操作どちらでも確認できます。
-
git log
コマンドで確認 - GitHubで確認
git log
で確認
$ git log
commit xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ※ コミットID (HEAD -> main, origin/main, origin/HEAD)
Author: hogehoge <hogehoge@gmail.com>
Date: Fri Oct 14 13:29:10 2022 +0900
[update] テスト
commit xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ※ コミットID
Author: hogehoge <hogehoge@gmail.com>
Date: Mon Sep 5 22:47:35 2022 +0900
[fix] コードスタイル修正
commit xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ※ コミットID
Author: hogehoge <hogehoge@gmail.com>
Date: Wed Aug 31 13:13:01 2022 +0900
[fix] フォント修正
git log
コマンドで直近のコミットの履歴を確認できます。
commit
の行に横一列に書かれているのがコミットIDです。
また、--oneline
オプションを使用することで
$ git log --oneline
d1583da (HEAD -> main, origin/main, origin/HEAD) テスト
f7414b8 コードスタイル修正
e422094 フォント修正
810f93d 最終調整
0810198 最終確認
c1f412c スクロールアップ実装
005ca17 複製手動
2cf4633 シングルモデル
53dab07 モデルの複製未完
のように簡潔に表示させることもできます。
GitHubで確認
GitHubのリポジトリ画面を開く。
ファルダ内のCommits
を選択。
そうすると、今までのコミットの履歴をコミットIDとともに確認することができます。
プッシュ
ローカルリポジトリを反映した後は、最終的にリモートリポジトリにも反映させる必要があります。
git push [オプション] [リモートリポジトリ名] [ローカルのブランチ名]:[リモートのブランチ名]
git pushの基本的な形は上記の通り。
一般的に使われているコマンドに
git push origin main
がありますが、これは、
git push origin main:main
と同値です。
つまり、ローカルリポジトリのブランチ名と、リモートリポジトリのブランチ名が同じ場合は、
「:リモートのブランチ名」の記述を省略することができます。
例) ローカルのmainブランチの内容をリモートのdevelopブランチに反映したい場合
git push origin main:develop
ここで「originってなんぞや??」となったかもしれませんが、リモートリポジトリの名前はクローンした時点で自動的にoriginという名前になっているみたいです。
上流ブランチ
上記のように、リモートリポジトリ名とブランチ名をいちいち設定するのは面倒に感じます。
そんな時は、上流ブランチを設定すると良いです。
上流ブランチを設定すると、プッシュ時のリモートリポジトリ名とブランチ名を省略することができます。
つまり、
git push
のコマンドを実行するだけでプッシュできるようになります。
上流ブランチの設定方法は簡単!
git push --set-upstream [リモートリポジトリ名] [ブランチ名]
git pull --set-upstream [リモートリポジトリ名] [ブランチ名]
git fetch --set-upstream [リモートリポジトリ名] [ブランチ名]
もしくは、
git push -u [リモートリポジトリ名] [ブランチ名]
例)
git push -u origin test // ブランチ名:test
-u は --set-upstream の略記で、「git push」のみ使用可能。
例えば、mainブランチを上流ブランチに設定する時は、
git push -u origin main
意味) ローカルのmainブランチの上流ブランチに、リモートリポジトリoriginのmainブランチを設定する
となります。同様に、
他のブランチで省略する際は、別途上流ブランチを設定する必要があります。
ここで、「毎回ブランチを作成するごとに、上流ブランチを設定するのは面倒くさい」という極度の面倒くさがり屋もいるかもしれない。。。
そのような人には以下のコマンド
git config --global push.default current
を試してみて欲しいです。
これは、現在のブランチの内容をリモートブランチに同名でプッシュする設定です。
つまり、上流ブランチに存在していればそのブランチにそのままプッシュし、していなければ現在のブランチを上流ブランチに同名で設定します。
しかも、この設定は異なるプロジェクトでも適用されるので、もうgit push
時のエラー(後ほど解説)に悩まされることはないです!!!
ちなみに、上流ブランチの確認は、
git branch -vv
で確認できます。
例)
#上流ブランチがない場合
$ git branch -vv
* main gc69k27 コメント
例)
#上流ブランチある場合
$ git branch -vv
* main gc69k27 [origin/main] コメント
上流ブランチが存在する時は、[リモートリポジトリ名/リモートのブランチ名]が出力されます。
上の例では、ローカルのmainブランチでgit push
を実行すると、リモートリポジトリoriginのmainブランチにプッシュされます。
ブランチ
個人開発だとあまり意識して使わない人も一定数いるかもしれませんが、個人、チームに関わらず、普段からブランチを切って作業した方が何かと便利!!
リポジトリをクローンしてきたら、まずはブランチを確認する。
git branch
リポジトリのディレクトリ内で上記のコマンドを実行すると、そのリポジトリに存在している全てのブランチを一覧表示できる。
Git Bashでは、今自分がいるブランチは「*」がついて表示される。(HEADの位置を指す)
新しくブランチを作るときは、
git branch [新しいブランチ名]
もしくは、
git checkout -b [新しいブランチ名]
前者は、ブランチを新しく作成するだけなのに対して、後者は、ブランチを作成し、そのブランチに移動することができます。
ブランチ移動は、
git checkout [移動先のブランチ名]
で移動することができます。
ブランチ削除は、
git branch -d [削除したいブランチ名]
で削除することができます。
また、ブランチを新しく作成してgit pushする際に、以下のようなエラーがでる場合があります。
$ git push
fatal: The current branch firebase has no upstream branch.
To push the current branch and set the remote as upstream, use
git push --set-upstream origin test
これは先程も説明したように、git push
する際に上流ブランチ(ここではtest)を設定していないため、リモートブランチ名を省略できないからです。そのため、
git push -u origin test
もしくは、
git config --global push.default current
git push
を実行して上流ブランチ(ここではtest)を設定すれば、次回以降はgit push
だけで実行することができます。
マージ
ブランチは、最終的に統合できないと意味を成しません。
ブランチ同士を統合させるにはマージを行います。
git merge [合体させたいブランチ名]
を実行してマージします。
引用元:https://deepinout.com/git/git-cmd/git-merge-commit.html
ここで注意なのが、現在の自分のブランチの確認です。
上図のように、「4」にいる(masterブランチにいる)状態でgit merge branch1
を行うと、下図のように、branch1がmasterに統合されます。
また、マージは新たにコミットを生成するため、
git merge [ブランチ名] -m "コメントも追加できる"
のように-m
オプションをつけることでコメントもできます。
デフォルトでは、「Merge branch [指定したブランチ名] into [実行したブランチ名]」
というコメントになっています。
コンフリクト
チーム開発をする上で厄介なのがコンフリクトです!
ひと言で説明すると、自分の変更箇所と他の人の変更箇所が重複している状態です。
Gitはファイル単位ではなく、「変更箇所の差分」で履歴を管理しているため、同じファイルでも変更箇所が異なればコンフリクトは起きません。
コンフリクトが起きると、Gitはどちらの変更を採用すればよいのか分からない。
なので、私たちが修正する必要があります。これを一般的にコンフリクトを解消するといいます。
コンフリクト解消
マージした時にコンフリクトが発生すると以下のようなエラー文が出ます。
$ git merge aa
Auto-merging [マージするファイル名]
CONFLICT (content): Merge conflict in [コンフリクトが起きたファイル名]
Automatic merge failed; fix conflicts and then commit the result.
コンフリクトを解消する方法は2つ。
- コードを編集する
- マージを取り消す
コードを編集して解消する方法
コンフリクトが発生しているファイルをVSCodeで開くとコンフリクトの場所が以下のようにカラーで明示されています。
引用元:https://www.engilaboo.com/git-conflict/
上図を解釈すると以下のようになっている。
<<<<<<< HEAD (Current Change)
現在取り込もうとしている元の部分
=======
自分の変更内容
>>>>>>> [コミットコメント] (Incoming Change)
Current Changeとなっている上側の部分が、その名の通り自分がいるブランチの内容となっています。
一方で、Incoming Changeとなっている下側の部分が、新たに入れ込もうとしている自分の変更内容です。
解消するには、採用したい内容を残して、<<<<<や===>>>などの不要な記号や内容は削除します。
その後、add
→commit
→push
を行えばOKです!
また、pushする際に、
$ git push origin [解消したファイル名]
To github.com:xxx/yyy.git
! [rejected] test -> test (non-fast-forward)
error: failed to push some refs to 'git@github.com:xxx/yyy.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
というエラーがでる場合があります。
これは既にリモートリポジトリに過去のファイルをプッシュしている時に発生します。
この場合は、
git push origin [解消したファイル名] -f
-f
オプションを使用して強制的にプッシュします。
マージを取り消す方法
また、編集したくない場合は、
git merge --abort
を実行すればマージをキャンセルすることができます。
コンフリクトを防ぐ
コンフリクトを生じさせないような役割分担やコードの書き方を意識すると、コンフリクトの解消をする負担が減り、開発により多くの時間を割けることができます。
色々やり方はありますが、一番オーソドックスなのは、
リモートリポジトリのmainブランチ等が更新される度に、各々で作業しているブランチにmainブランチをマージさせる方法です。
そうすることで、最終的にmainブランチ等にマージさせる際に、変更差分が少なくなり必然的にコンフリクトが起きにくくなります。
これを意識するだけでリポジトリ管理者はだいぶ助かるかと。
プル
リモートリポジトリの更新をローカルリポジトリに反映させるためにはプルする必要があります。
ここで、「クローンとは何が違うの?」と思った人もいるかもしれませんが、両者は、リモートリポジトリをローカルリポジトリにコピーする点では一緒ですが、
clone:すべてのファイルをコピー
pull:リモートとローカルで差異があるファイルをコピー
という点で異なります。
よって、最初の1回だけクローンを実行して、2回目以降はプルでローカルリポジトリを更新する流れになっています。
プルのコマンドは以下の通りです。
git pull [リモートリポジトリ名] [ブランチ名]
例)
git pull origin main
また、プッシュと同様に上流ブランチを設定していれば
git pull
を実行するだけでプルすることができます。
フェッチ
ここで少し発展的な話をすると、pullコマンドは内部的に「fetch」と「merge」を実行しています。
つまり、「pull」=「fetch」+「merge」というわけです!
ここでの両者の説明は以下の通り。
fetch:リモートのブランチをローカルの追跡ブランチ(トラッキングブランチ)に持ってくる
merge:ローカルの追跡ブランチをローカルのブランチと結合
ここで「追跡ブランチとはなんぞや?」となりますが、実はプルの工程は以下のようになっています。
引用元:https://tech.amefure.com/web-git-clone-push
普段、プルだけを使っていると意識することはないですが、実は追跡ブランチというものがリモートとローカルのブランチ間には存在しています。
追跡ブランチとは、その名の通り、ローカルリポジトリにあり、リモートのブランチの変化を追跡しているブランチです。
git fetch [リモートリポジトリ名] [ブランチ名]
を実行し、リモートのブランチを追跡ブランチに反映することができます。
その後、
git merge [リモートリポジトリ名]/[ブランチ名]
もしくは、
git merge FETCH_HEAD
FETCH_HEADはフェッチで取得したブランチの最新のコミットの場所を指す。
で追跡ブランチとローカルのブランチを結合させ、結果的に変更部分を更新します。
これらの動作をまとめて実行しているのが「pull」です。
ここまで聞くと、「pull便利!」と思うかもしれませんが、企業によってはpullコマンドを禁止しているところもあるそうです。
理由としては、pullはブランチ情報を持ってくるだけでなく、勝手にマージも行うため、細かい内部の挙動を把握することが困難であり、場合によってはpullを使うことで逆に遠回りになってしまうこともあるからです。
バージョン変更
Gitの強みがバージョン変更です!!!ソースコードを変更前に戻したい時に実力を発揮します。
バージョン変更はコミット単位で行います。
バージョン変更のコマンドは2つあります。
git revert
とgit reset
です。
前者は指定したコミットを打ち消すコミットを新たに作るのに対し、後者は指定したコミットまで全てをリセットします。
つまり、git revert
は今までのコミットログを残したままバージョン変更するが、git reset
は間違ったコミットログを削除してバージョン変更します。
ここまで聞くと、「git revert
のほうがログも残るし、安心だから全部revert
で済ませばいいじゃん」と思うかもしれませんが、誤った履歴が残ってしまうため、コミットログが見づらくなるなどのデメリットもあります。
詳しくは、以下のサイトを参考に。
https://qiita.com/Sammy_U/items/e37c7242544fd1da81be#reset
revert
git revert
の基本形は以下の通りです。
git revert [対象のコミットID]
また、revert
は新たなコミットを生成するため、オプションを指定してコミットコメントを編集するか否かを選択することができます。
デフォルトでは、編集ありで実行する。
編集あり
git revert [対象のコミットID] -e
-e は --edit の略記
編集なし
git revert [対象のコミットID] --no-edit
また、revert
は自動的にコミットを生成しますが、インデックスに戻すだけでコミットを行なわないようにすることもできます。
コミットしない
git revert [対象のコミットID] -n
もしくは、
git revert [対象のコミットID] --no-commit
これは複数のコミットをrevert
するときに、一括でコミットすることができるので便利!!!
マージコミット
マージコミットを取り消そうとした場合、マージした2つのコミットのうち、どちらに戻すのかを指定する必要があります。
その時は、-m
オプションの後に戻したいコミット(親)を数字(基本的に1もしくは2)で指定し、revert
を実行します。
1がマージされた側のブランチ、2がマージする側のブランチになります。
git revert -m 1 [対象のマージコミットID]
-m は --mainline の略記
数字の番号がよく分からない場合は、git show
コマンドやgit log
コマンドで対象のマージコミットを確認することでコミット(親)の数字がわかります。
$ git show
commit axaxaxaxaxaxaxaxaxax
Merge: aaa bbb #ここに注目
Author: xxxx xxxx
Date: Mon Oct 10 14:00:00 2022 +0000
Merge commit
「axaxaxaxaxaxaxaxaxax」がコミットIDで、
このマージコミットは、「1a1a1a」というコミットに「2b2b2b」というコミットがマージされてできたものです。
「Merge:」という行に書かれている順番に1、2となります。
この場合、「aaa」の番号が1、「bbb」の番号が2となります。
revert
を取り消す方法
また、revert
を取り消したい場合は、
git revert --abort
でやり直すことができます。
reset
git reset
の基本形は以下の通り。
git reset [対象のコミットID]
また、reset
はステージングの章で紹介したようにgit add
を取り消すこともできます。
git reset
もしくは、
git reset [取り消したいファイル名]
でステージングをキャンセルできます。
今までコミットIDを指定してバージョン変更をしてきましたが、最近のコミットに戻したい場合は、以下のようにHEAD
を使用すればIDを指定する必要はありません。
直前のコミットを取り消したい
git reset HEAD^
n 個前のコミットまで戻したい
git reset HEAD~n
-
HEAD^
:直前のコミットを表す -
HEAD~n
:n 個前のコミットを表す
その他のオプション
reset
には--mixed
、--soft
、--hard
の3つのオプションがあり、それぞれリセットされる内容が異なります。
オプション | ステージングエリア | ワーキングツリー | HEADの位置 |
---|---|---|---|
--mixed(デフォルト) | リセット | リセットされない | リセット |
--soft | リセットされない | リセットされない | リセット |
--hard | リセット | リセット | リセット |
例)
git reset --hard HEAD^ // 直前のコミットを取り消す(ワーキングツリーもリセット)
git reset --soft HEAD~2 // 2個前のコミットを取り消す(HEADの位置を変更)
git reset --hard ORIG_HEAD // resetを取り消す
ORIG_HEAD は最新の一つ手前のコミットを指す。
プルリクエスト
ブランチで作業して、ある程度プログラムが形になってきたら、最終的にmainブランチに組み込む必要があります。マージをしてブランチを統合するわけですが、mainブランチやdevelopブランチなどのメインのブランチに直接マージすると、仮に不具合やエラーがあった時に修正が面倒になってしまいます。
そのため、メインとなるブランチにマージする際は、プルリクエストをしてリポジトリの管理者にマージしてもいいのか許可をとるようにすれば、少なくともエラーや不具合を減らすことができます。
(相手側にプルしてもらえるようにリクエストする)
ブランチを切って作業してプッシュすると、GitHubのリポジトリの画面にCompare & pull requestと親切に表示されます。
mainブランチにマージしたくなったら、この部分をクリックして管理者にプルリクエストを送ります。
プルリクの作成画面では、
- タイトル
- コメント
の2点を入力する必要があります。
自分が編集した箇所の概要をタイトルに入力し、より詳細な説明をコメントに入力すれば大丈夫!
あくまでも、読み手の管理者が理解しやすい文章を心がける。
入力完了したら、「Create pull request」をクリックし、プルリクを送ります。
その後は、管理者から修正等の連絡が来た場合は、その都度修正しプッシュし直します。
最終的に、管理者が納得のいくコードになったら、プルリクが承認され、mainブランチに反映されます。
補足
基本的なGit操作はここまでにして、以降はGit操作の補足情報を記述します。
コラボレイターの追加
チーム開発をする上でリポジトリにコラボレイターとして共同開発者を追加しないと、リモートリポジトリにアクセスすることができません。
リポジトリ作成者は作成したリポジトリを開き、Settingsを選択します。
リポジトリの設定画面が開いたら、左のAccessリストから「Collaborators」を選択。
設定によっては、ここでユーザ認証が行われる。
Collaboratorsの画面が開けたら、以下のような画面に遷移するので、Manage accessの「Add people」を選択。
すると、新たなポップアップが出現するので、共同開発者のGitHubに登録しているユーザ名やメールアドレスを1人ずつ入力していく。
以下のように、Manage accessにメンバー全員が表示されたら、コラボレイターの追加作業は完了。
まとめ
今回は、自分のはじめてのTech記事執筆としてGit入門についてまとめました。
いつもは利用させていただく立場でしたが、執筆する立場になると、それなりの理解力や責任を問われるのを感じました。今後も自分の好きなように技術発信していきたいです。