LoginSignup
4
0

【Git】初心者向け Gitをつかってみる その2(git branch と git merge)

Last updated at Posted at 2023-12-01

はじめに

Git Advent Calendar 2023 2日目の投稿です。よろしくおねがいします。
前回の記事ではgit initからgit commitまでやりました。今回も初心者向けに、開発シーンでよく使うブランチを切ってマージするところまでGitを使ってみようと思います。
(今回も自分向けの頭の整理も兼ねてます:writing_hand:

準備

  • 「git/sample」リポジトリを用意
  • リポジトリに「hello」と書き込んだ「sample.txt」をコミットしておく
    (前回の内容を進めていると上記の内容になってます)

git logした状態(git logはコミットの履歴を確認するコマンドです)

 ~/Desktop/git/sample (master)
$ git log
commit 9db45bbb0a8487fde3a648f5eb507768613f8018 (HEAD -> master)
Author: hogehoge <fugafuga>
Date:   Wed Nov 8 10:51:13 2023 +0900

    はじめてのコミット

ブランチとは

ブランチとは簡単に言うとある開発ラインから別の開発ラインへと分岐するための方法です。ここで、Gitがどのようにファイルを管理するかについて説明しつつ、ブランチが何なのか説明してみます。
(イメージがついてると以降の理解の助けになるかなと思って書きます。)

Gitは以下の4種類のオブジェクトを使ってファイルを管理しています。

  1. ブロブ
    • ある時点のファイル。
  2. ツリー
    • 1階層分のフォルダ情報。
    • ブロブとツリーを参照する。
  3. コミット
    • 変更のメタデータ(ファイルの作成者とか、日付とか)。
    • ツリーとコミットを参照する。
  4. タグ
    • 特定のオブジェクトに対して人が読みやすくなるようにした名前。
    • コミットを参照する。

オブジェクトはすべてSHA1ハッシュ(40桁の16進数。例えば「a564b5f376a8cacf10b87369d122743bbd82fc19」みたいな値)の一意な名前で管理されます。

例えば、リポジトリの直下に「hello world」と書かれたファイルAと、「bye world」と書かれたファイルBを配置して、コミットしたときのオブジェクト間の関係を図示してみると以下のようになります。

  • リポジトリの直下にファイルを2つ配置してコミットした場合
    無題.png

コミットがツリーを、ツリーがブロブを参照してリポジトリの中身を表現しています。
つまり、コミットを参照することである時点のリポジトリの中身を知れます。

ここで、リポジトリの最新のコミットを参照するのがブランチです。

  • ブランチのイメージ図
    無題.png

(おいおい、ちょっと待てよ、ブランチは開発ラインを分岐するための方法ちゃうんかい、どこがやねん:sweat_smile:と思った方、もう少し辛抱して下さい:pray:

ブランチは常に最新のコミットを参照し続けます。
無題.png

ブランチを複数作るとどうなるでしょうか。
同じコミットを参照するように新たにanotherブランチを作ると以下の図のようになります。
無題.png

今は同じコミットを参照している状態ですが、ここでanotherブランチに対して作業をしてコミットをしてみると以下の図のようになります。
無題.png

ブランチは最新のコミットを参照し続けるので、anotherブランチは最新のコミットを参照するようになり、masterブランチは1つ前のコミットを参照したままの状態になります。

このようにして、masterブランチはある時点のリポジトリの状態を表現することを保ったまま、anotherブランチではanotherブランチ専用で作業を続けることができます(これを指して冒頭でブランチは開発ラインを分ける方法と言いました)。

リポジトリのデフォルトのブランチはmasterという名前が付けられてて、ほとんどの開発ではこのmasterを最も信頼できるソースコードの履歴として使っていると思います。
そして、他のブランチは、開発用だったりバグ修正用だったり様々な状況の開発ラインとして利用して、最後にmasterに集める、、なんてことをすると思います。

ブランチを作る(ある開発ラインから別の開発ラインへと分岐する)理由は様々あります。

  • 作業者ごとに開発できるようにする
  • ソースコードのバージョンを分ける
    …など

色々書きましたが、実務ではとりあえずはブランチを作ることとは自分だけの作業領域ができる理解でいいと思います。

git branch

では、実際にブランチを作ってみましょう。
準備として下記のコマンドを打って3回コミットしておきます(コミット何個かあった方がおもしろそうなので)。

説明:文字を「sample.txt」に書き込んでコミット
~/Desktop/git/sample (master)
$ echo "hello world" > sample.txt

~/Desktop/git/sample (master)
$ git add .

~/Desktop/git/sample (master)
$ git commit -m"2回目のコミット"
[master 437e558] 2回目のコミット
 1 file changed, 1 insertion(+), 1 deletion(-)

説明:文字を「sample.txt」に書き込んでコミット
~/Desktop/git/sample (master)
$ echo "hello hello world" > sample.txt

~/Desktop/git/sample (master)
$ git add .

~/Desktop/git/sample (master)
$ git commit -m"3回目のコミット"
[master c4a4731] 3回目のコミット
 1 file changed, 1 insertion(+), 1 deletion(-)

説明:文字を「sample.txt」に書き込んでコミット
~/Desktop/git/sample (master)
$ echo "hello hello world release" > sample.txt

~/Desktop/git/sample (master)
$ git add .

~/Desktop/git/sample (master)
$ git commit -m"1回目のリリース"
[master 630817a] 1回目のリリース
 1 file changed, 1 insertion(+), 1 deletion(-)

説明していないコマンドとオプションをいくつか使っているので説明しておきます。

  • echoはGitのコマンドではなくコンソールに文字を表示するLinuxのコマンドです。表示した文字は>でファイルに上書きで転送することができます。
  • git add ..は作業フォルダで編集があったすべてのファイルがインデックスに登録されます。ファイル名打つのが面倒な時かつすべてステージしてよい時に使ってます。
    ※ここで改行コードのwarningが出るかもしれませんが、いったんは気にしなくていいです。
  • git commit -m <msg>で指定された<msg> をコミットメッセージとして使用します。エディタでの入力を省略できます。

こうすることで以下のコミットの履歴ができます。この図はコミットツリーと呼ばれます。
無題.png

:arrow_down:図の説明です。

  • 丸がコミットです。
  • 冒頭の図では、コミットの参照としてツリーが、ツリーの参照としてブロブがついていました。が、コミットが分かればある時点のリポジトリの状態がわかるのでツリーやブロブは省略して書いてます。
  • また、参照の向きを表す矢印はどちらの方向か推測できるので省略してます。

上の図はコマンドでも確認することができて、確認するにはgit log --graphを使います。

~/Desktop/git/sample (master)
$ git log --graph
* commit 5b09212c8a6602f39c9abe12266eff6c3f94fd35 (HEAD -> master)
| Author: hogehoge <fugafuga>
| Date:   Fri Nov 10 09:52:31 2023 +0900
|
|     1回目のリリース
|
* commit 9baeecead59dcc40b594db13a9cfc0189f9545cf
| Author: hogehoge <fugafuga>
| Date:   Fri Nov 10 09:51:52 2023 +0900
|
|     3回目のコミット
|
* commit d1d0866818415586c47819f032e80b9ac2a3dff4
| Author: hogehoge <fugafuga>
| Date:   Fri Nov 10 09:50:15 2023 +0900
|
|     2回目のコミット
|
* commit 677b7f568e7a1563213e0de75677949449108703
  Author: hogehoge <fugafuga>
  Date:   Fri Nov 10 09:48:03 2023 +0900

      はじめてのコミット

新しいコミットから古いコミットへさかのぼる形です。

では、さっそくブランチを作りましょう。
git branch <branchname>現在のコミットから分岐して新たなブランチができます。また、ブランチの一覧を確認するにはgit branchです。

~/Desktop/git/sample (master)
$ git branch another

~/Desktop/git/sample (master)
$ git branch
  another
* master

git branchで表示した際に*がついているブランチが現在作業しているブランチです(ここではカレントブランチと言います)。

  • 今の状況
    無題.png

仮にこの後コミットをすると、カレントブランチであるmasterブランチにコミットがくっつきます。言い換えると、次のコミットはmasterの参照先になります。

anotherブランチで作業を続けましょう。ブランチを切り替えるにはgit checkout <branchname>を使います。

~/Desktop/git/sample (master)
$ git checkout another
Switched to branch 'another'

~/Desktop/git/sample (another)
$ git branch
* another
  master

カレントブランチをあらわす*anotherブランチに切り替わりました。

ちなみに、少し話がそれますがカレントブランチはGitではHEADで表示されます。
少しだけHEADの話をします。git log --onelineをしてみましょう(--onelineは各コミットのコミットメッセージの1行目だけのgit logが表示されます)。

~/Desktop/git/sample (another)
$ git log --oneline
5b09212 (HEAD -> another, master) 1回目のリリース
9baeece 3回目のコミット
d1d0866 2回目のコミット
677b7f5 はじめてのコミット

(HEAD -> another, master)がログに付与されています。この->の意味は、「カレントブランチ(HEAD)はanotherブランチを参照している」という意味になります。
HEADも参照なので、図であらわすと以下のようになります。
無題.png

git checkout <branchname>で指定のブランチにHEADの参照が切り替わるわけです。
無題.png

、、すみません、話が脱線しました。
さて、話を戻します。繰り返しの図になりますが、今は以下の状況でした。
無題.png

ここで再度コミットを何度かしてみるとどうなるでしょうか。

説明:「sample.txt」の中身を更新してコミット
~/Desktop/git/sample (another)
$ echo "another hello" > sample.txt

~/Desktop/git/sample (another)
$ git add .

~/Desktop/git/sample (another)
$ git commit -m""anotherブランチで1回目のコミット
[another ec679ef] anotherブランチで1回目のコミット
 1 file changed, 1 insertion(+), 1 deletion(-)

説明:「sample.txt」の中身を更新してコミット
~/Desktop/git/sample (another)
$ echo "another hello world" > sample.txt

~/Desktop/git/sample (another)
$ git add .

~/Desktop/git/sample (another)
$ git commit -m"anotherブランチで2回目のコミット"
[another 96a71fa] anotherブランチで2回目のコミット
 1 file changed, 1 insertion(+), 1 deletion(-)
  • git log
~/Desktop/git/sample (another)
$ git log
commit 96a71fa7be0f180a0118ef8d090e9d51bb3d37df (HEAD -> another)
Author: hogehoge <fugafuga>
Date:   Fri Nov 10 09:59:48 2023 +0900

    anotherブランチで2回目のコミット

commit ec679efe389eb8167f70e9739e1959d27eff4a71
Author: hogehoge <fugafuga>
Date:   Fri Nov 10 09:58:43 2023 +0900

    anotherブランチで1回目のコミット

commit 5b09212c8a6602f39c9abe12266eff6c3f94fd35 (master)
Author: hogehoge <fugafuga>
Date:   Fri Nov 10 09:52:31 2023 +0900

    1回目のリリース

commit 9baeecead59dcc40b594db13a9cfc0189f9545cf
Author: hogehoge <fugafuga>
Date:   Fri Nov 10 09:51:52 2023 +0900

    3回目のコミット

commit d1d0866818415586c47819f032e80b9ac2a3dff4
Author: hogehoge <fugafuga>
Date:   Fri Nov 10 09:50:15 2023 +0900

    2回目のコミット

commit 677b7f568e7a1563213e0de75677949449108703
Author: hogehoge <fugafuga>
Date:   Fri Nov 10 09:48:03 2023 +0900

    はじめてのコミット
  • 今の状況イメージ図
    無題.png

このようにして、masterブランチでは最新のコミットがハッシュ値5b0921を指し示し、anotherブランチでは開発を進めてハッシュ値96a71fまでコミットをすることができました。

ブランチを作って開発を進めることが出来ましたね。

git merge

次に、anotherブランチのコミットをmasterブランチに取り込んでみましょう。分岐したコミットの履歴を統合させることをマージと言います。
git merge <branchname>でカレントブランチにbranchnameの履歴を取り込めます。カレントブランチ取り込むことに注意です。
マージされたことが分かるようにここでは--no-ffオプションを使ってマージします(後述します)。

~/Desktop/git/sample (another)
$ git checkout master
Switched to branch 'master'

~/Desktop/git/sample (master)
$ git merge --no-ff another
Merge made by the 'recursive' strategy.
 sample.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

コミットメッセージを入力するエディタが表示されるので任意のコメントを入力してコミットしてください(ここではデフォルトの内容でいきます)。

git log --oneline --graphをするとマージの状況が分かります。

~/Desktop/git/sample (master)
$ git log --oneline --graph
*   3124c16 (HEAD -> master) Merge branch 'another'
|\
| * 96a71fa (another) anotherブランチで2回目のコミット
| * ec679ef anotherブランチで1回目のコミット
|/
* 5b09212 1回目のリリース
* 9baeece 3回目のコミット
* d1d0866 2回目のコミット
* 677b7f5 はじめてのコミット

masterブランチ上でanotherブランチを取り込む操作をしたので、masterブランチにanotherブランチの内容を取り込むコミットが1つ生成されてます。

  • イメージ図(git logと一緒ですが、、)
    無題.png

ファイルの内容を確認すると、anotherブランチの内容が反映されていることが分かります(コミットID96a71faの内容です)。

~/Desktop/git/sample (master)
$ cat sample.txt
another hello world

catはファイルの中身を確認したいときに利用されるLinuxのコマンドです。

ここで、マージするときにつけた--no-ffオプションについて説明しておきます。
Gitはマージするときにまず、縮退マージと呼ばれる以下の2つのマージを試みます。

  • Already up-to-date
    • マージする側のブランチ(ここでいうとmaster)に、マージされる側のブランチ(ここでいうとanother)からのコミットがすべて含まれている場合にGitから言われる。マージする内容がなく、コミットは追加されない。
  • Fast-forward
    • マージされる側のブランチ(another)に、マージする側のブランチ(master)のコミットがすべて含まれている場合におこなわれる。この際は、マージする側のブランチにマージされる側のコミットを付け足して、カレントブランチ(HEAD)は付け足した最新のコミットに移動する。また、マージのコミットが作られない。

今回は↑に書いた、Fast-forwardに当てはまります(anotherブランチがmasterブランチのコミットをすべて持っている状態)。
無題.png
そのため、オプションをつけずに(--no-ffをつけずに)git merge anotherを行った場合は、Fast-forwardが行われてmasterブランチにanotherコミットで付け足した2つのコミットがくっつくだけの処理となります。
無題.png

--no-ffとはFast-forwardせずにマージせよ、というオプションになります。
マージが行われたことが分かりにくいので今回は--no-ffをつけてマージコミットを作りました。

競合

次に、2つのブランチで同一箇所を修正してマージしてみましょう。

説明:masterブランチ上で「sample.txt」の中身を更新してコミット
~/Desktop/git/sample (master)
$ echo "master branch hello" > sample.txt

~/Desktop/git/sample (master)
$ git add .

~/Desktop/git/sample (master)
$ git commit -m"masterブランチでコミット"
[master 1cff7a9] masterブランチでコミット
 1 file changed, 1 insertion(+), 1 deletion(-)

説明:anotherブランチ上で「sample.txt」の中身を更新してコミット
~/Desktop/git/sample (master)
$ git checkout another
Switched to branch 'another'

~/Desktop/git/sample (another)
$ echo "another branch hello" > sample.txt

~/Desktop/git/sample (another)
$ git add .

~/Desktop/git/sample (another)
$ git commit -m"anotherブランチでコミット"
[another 9a8b34b] anotherブランチでコミット
 1 file changed, 1 insertion(+), 1 deletion(-)

masterブランチとanotherブランチで同じファイルに修正を行いました。
コミットツリーは以下のようになっています。
無題.png

git show-branchコマンドを使ってみます。
これはブランチが分岐してから現在までのコミットを表示するコマンドです。各ブランチのコミットをさくっと知りたいときに便利です。

~/Desktop/git/sample (another)
$ git show-branch
* [another] anotherブランチでコミット
 ! [master] masterブランチでコミット
--
*  [another] anotherブランチでコミット
 + [master] masterブランチでコミット
*+ [another^] anotherブランチで2回目のコミット

--の上が各ブランチの最新のコミット、--の下が分岐後にブランチごとに何のコミットがあったかおおよそ時系列ごとに表示しています。すべてのブランチのログを見るgit log -allの結果と一緒にみることで、先ほどマージした後にmasteranotherでそれぞれコミットがおこなわれたことが分かります。

~/Desktop/git/sample (another)
$ git log --oneline --all
21085b7 (HEAD -> another) anotherブランチでコミット
1cff7a9 (master) masterブランチでコミット
3124c16 Merge branch 'another' ←マージコミット
96a71fa anotherブランチで2回目のコミット
ec679ef anotherブランチで1回目のコミット
5b09212 1回目のリリース
9baeece 3回目のコミット
d1d0866 2回目のコミット
677b7f5 はじめてのコミット

それでは、masterブランチにanotherブランチをマージしてみましょう。

~/Desktop/git/sample (another)
$ git checkout master
Switched to branch 'master'

~/Desktop/git/sample (master)
$ git merge --no-ff another
Auto-merging sample.txt
CONFLICT (content): Merge conflict in sample.txt
Automatic merge failed; fix conflicts and then commit the result.

~/Desktop/git/sample (master|MERGING)
$

メッセージを日本語に訳すと「競合が発生してマージは失敗したよ。競合を解消して結果をコミットしてね。」とのことです。
先ほどのマージではanotherブランチのみを修正してmasterブランチとマージしたので、競合が発生せず自動的にマージされました。しかし、今回は双方のブランチで修正をおこなっており、自動的にマージができず競合がおこっている状態です。
この場合は人間が解消して手動でマージコミットを作ってあげる必要があります。
また、Git Bushでは(master|MERGING)のようにブランチ名の後ろにマージ中であることが表示されます。

私はこの競合が怖くてマージ操作ができなかったので、先に競合した際にマージ前の状況に戻る方法から書いておきます。マージ前の状況に戻るにはgit merge --abortです。

~/Desktop/git/sample (master|MERGING)
$ git merge --abort

~/Desktop/git/sample (master)

注意点
ちなみに、マージは作業フォルダで作業されるので、マージ前に作業フォルダで作業中のファイルが無いようにした方がいいです。git merge --abortが困難になる可能性があります。

では、競合を解消してみましょう。どこで競合が起きたかはGitがファイルに直接書いています(気になる方は「sample.txt」を開いてみてください)。
git diffコマンドでどこに競合が発生したか確認してみましょう。

~/Desktop/git/sample (master|MERGING)
$ git diff
diff --cc sample.txt
index 81f4d78,2eb1448..0000000
--- a/sample.txt
+++ b/sample.txt
@@@ -1,1 -1,1 +1,5 @@@
++<<<<<<< HEAD
 +master branch hello
++=======
+ another branch hello
++>>>>>>> another

マージする側(master)の変更が<<<<<<<=======の間、マージされる側(another)の変更が=======>>>>>>>の間に書いてあります。競合の解消は人間がどうしたいか決めてファイルを編集します。
ここではanotherの方を取り込むことにしましょう。今回は面倒なので、echoコマンドで上書きます(実際は、修正者にどういう意図で編集したか確認したりしてよく考えてやりましょう)。
手動でファイルを修正してgit addgit commitでマージコミットを作るとマージが完了します。

説明:競合が発生しているファイルを上書きで修正してコミット
~/Desktop/git/sample (master|MERGING)
$ echo " another branch hello" > sample.txt

~/Desktop/git/sample (master|MERGING)
$ git add .

~/Desktop/git/sample (master|MERGING)
$ git commit
[master a97dcd5] Merge branch 'another'

マージを2回おこなったのでgit log --oneline --graphは以下のようになります。

~/Desktop/git/sample (master)
$ git log --oneline --graph
*   a97dcd5 (HEAD -> master) Merge branch 'another'
|\
| * 21085b7 (another) anotherブランチでコミット
* | 1cff7a9 masterブランチでコミット
* |   3124c16 Merge branch 'another'
|\ \
| |/
| * 96a71fa anotherブランチで2回目のコミット
| * ec679ef anotherブランチで1回目のコミット
|/
* 5b09212 1回目のリリース
* 9baeece 3回目のコミット
* d1d0866 2回目のコミット
* 677b7f5 はじめてのコミット

また、マージの完了後にマージをした内容を破棄したい場合はgit reset --hard ORIG_HEADするとマージ前の状況に戻れます。

補足

ところで、マージをした時にMerge made by the 'recursive' strategy.とでました。

~/Desktop/git/sample (master)
$ git merge --no-ff another
Merge made by the 'recursive' strategy.
 sample.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

recursiveマージについて、簡単に補足しておきます。
マージの際はマージコミットを作り出すためのマージのアルゴリズムを選択できます(これをマージ戦略といいます)。
recursiveマージはマージ戦略の1つです。同時に2つのブランチのみ扱えて、すべてのマージ起点のもとになった共通的な位置に一時的なマージを作り出して、そこをマージ基点として通常の3wayマージをおこなう戦略です。

ところで3wayマージって?

以下の説明を、簡略化して和訳してみます(参照してくださいでもいいんですけど自分のために和訳してメモとして載せてます)。

マージのジレンマ

2人以上の開発者が同じファイルにそれぞれ変更を加え、あとでそのバージョンをマージしようとすると競合が起こる可能性があります。
しばらくの間、バージョン管理システムを忘れて、具体的なケースを考えてみましょう。2人の開発者、太郎と花子が同じ既存の「index.html」ファイルに変更を加え、最終的にそれぞれ1つの新たなリビジョンを作成したとします。この2つのリビジョンの変更を失うことなくマージするにはどうすればよいでしょうか?

(太郎側)index.html
<html>
    <head>
        <title>サンプル</title>
    </head>
    <body>
        <ul class="animals">
            <li>Mouse</li>
            <li>Cat</li>
            <li>Horse</li>
        </ul>
    </body>
</html>
(花子側)index.html
<html>
    <head>
        <title>サンプル</title>
    </head>
    <body>
        <ul class="animals">
            <li>Moose</li>
            <li>Cat</li>
            <li>Dog</li>
        </ul>
    </body>
</html>

両者とも<ul>ブロックの動物リストを編集していることがわかりますね。もし、この2つのリビジョンから最終的なマージのバージョンを作ることになった場合、あなたならどうするでしょうか?
以下のマージのジレンマが発生します。

  1. 太郎がMooseMouseに変えたのでしょうか?それとも、花子がMouseMooseに変えたのでしょうか?
  2. HorseDogはそれぞれの開発者が加えたのでしょうか?それとも、2人の開発者のどちらかもしくはどちらもがリストの最後の項目を以前の項目から更新したのでしょうか?

この2つのリビジョンの情報では、上記を知ることは不可能です。このシナリオは、2つのリビジョンを静的に比較して手動でマージさせる2wayマージと呼ばれます。

3wayマージ

上のように各開発者がそれぞれどのような変更をおこなったか手動でたどる代わりに、3wayマージがあります。最新のバージョン管理システムでは「最も近い共通の先祖」(マージ基点と言います)を自動的に見つけることができます。このマージ基点も合わせた3つのリビジョンを使って最終的なマージのリビジョンを作成することを「3wayマージ」と呼びます。
例えば、マージ基点が以下だったとしましょう。

(マージ基点)index.html
<html>
    <head>
        <title>サンプル</title>
    </head>
    <body>
        <ul class="animals">
            <li>Moose</li>
            <li>Cat</li>
        </ul>
    </body>
</html>

マージ基点を特定すれば、望ましい最終バージョンを作成するために何を変更したか見つけ出すのは簡単です。
マージ基点と太郎と花子のリビジョンの比較を見ると、マージ後の最終バージョンがどうあるべきかを理解するのがずっと簡単になります。
マージのジレンマの「1. 太郎がMooseMouseに変えたのでしょうか?それとも、花子がMouseMooseに変えたのでしょうか?」については、太郎が変更をしたことが明らかになります。
また、「2. HorseDogはそれぞれの開発者が加えたのでしょうか?それとも、2人の開発者のどちらかもしくはどちらもがリストの最後の項目を以前の項目から更新したのでしょうか?」については2人の開発者それぞれがHorseDogを追加したことが分かります。
これを踏まえると、マージコミットとして最終的なリビジョンを作成することができます。

要するに

3wayは3つのコミットを表しています。

①マージ元ブランチのポインタに該当するコミット
②マージ先ブランチのポインタに該当するコミット
③①、②の共通祖先となるコミット

「①と③の差分」と、「②と③の差分」をマージ元ブランチに取り込む方法を「3wayマージ」と言います。(↓の説明をお借りしました。)

ちなみに、今回使用したバージョン2.22ではrecursiveがデフォルト戦略として選択されますが、最新のバージョンのデフォルト戦略はortというやつでした。

まとめ

今回はgit branchをしてブランチを2本用意して、git mergeでブランチ間をマージしてみました。
間違いがあればコメントでご指摘いただけるとうれしいです:pray:

参考

4
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0