Git 完全に理解する
はじめに
この記事は、2023年のアドベントカレンダーで私が執筆した記事を元に、一部修正・改良をした記事となっています。
この記事では初心者でもこれ一つ読むだけで Git を扱えるくらいの知識を得られるのを目標に執筆しております。Git はよく使うよという人にはやや冗長に書いているので、各章終盤でなにか得られる Tips が書ければなと考えております。
また、初心者向けハンズオンの資料やそのベースとしても使える記事を目指しています。
Gitとは?
分散型バージョン管理システム、といって理解できる人はこの章は読み飛ばしていますよね。
Git とは、ディレクトリ内のファイルの追加、編集、削除などの変更差分を鎖状に保存していくシステムです。語弊を恐れずに簡単に言えば、ディレクトリの状態をセーブ・ロードすることができるツールです。
コードや文書などを書いたことのある人は、一度書き進めた後、「一旦あの時のファイルの状態に戻したい」「消して書き直したものをもう一度復活させたい」といった体験をしたことがある人が多いと思います。
また、チームでフォルダ内のファイルを操作(追加・編集・削除)する際に、他の人の操作と衝突して自分の操作が消えてしまうといった場面もあると思います。
これらの問題などをGit の導入で解決・軽減することができます。
ただ、Git の導入には Git の知識が必要になってきます。そのため、この記事で Git の知識を得て、前述の面倒事を避けられる人がエンジニアに限らず増えることを願っています。
Gitをインストールしてみよう
学習はインプットだけでなくアウトプットも併せて行うことで学習できる度合いがグッとあがります。そのため、この記事では手元で動かしながら読み進めていくのを推奨いたします。
詳しい方はこちらを参考にせずに各自の方法で Git をインストールしても当然構いません。
Windowsの場合
Git公式からダウンロードしましょう。
インストールする際は、インストーラから様々な設定を英語で聞かれるのですが、何を聞かれているかわからない人は、適当な Git のインストール手順の記事を見つけて参考にしてください。バージョンが上がるたびに聞かれる設定が変わっていたりするので、各々で投稿日の新しい記事を探してください。(丸投げ)
Linuxの場合
各ディストリビューションのパッケージマネージャーでインストールしてください。
ディストリビューションによっては既に Git が入っている場合もあるので、事前に git -v などで Git が入っているかどうか確認してみてください。
sudo apt install git
Macの場合
XCode をインストールすることで Git も付属してインストールすることができます。
もしくは Homebrew があれば brew で install しましょう。
brew install git
インストールが終わったら
これ以降はターミナルで Git を扱うので、Windows は PowerShell, Linux や Mac はターミナルなど、各自のコマンドを打てる環境を用意しながら読み進めていってください。
インストール後の事前準備
この章は実際に手を動かしながら Git を学習する方のみ必要な章なので、読んでるだけだよ~という方は飛ばしてもらっても構いません。
Gitのアカウントの作成
Git はバージョン管理をする際、コミットした人が誰かという情報を持たなければいけません。個人を一意に定めたり連絡が取れたりすることができるように、アカウント名、メールアドレスを設定します。
次の項目の Github のアカウント作成で使用するアカウント ID とメールアドレスを今から設定するものと同一のものにすれば、Github 上でコードを確認したときにそのアカウントがコミットしたということがわかるようになっています。
git config --global user.name <アカウント名>
git config --global user.email <メールアドレス>
Githubのアカウント作成
後でまた説明しますが、Github は Git のデータを Web 上で管理してくれる一番メジャーなサービスです。
↓こっからアカウント作りましょう。
https://github.com/signup
Gitでバージョン管理してみよう
この章で扱うコマンドは、
git initgit addgit commitgit loggit status
になります。
まずは適当に空フォルダを作成し、その中に適当にテキストファイル(画像や動画、音声ファイルなどのバイナリファイルでなければ何でも良し)を作成しましょう。そして、ターミナルでそのフォルダを開きます。私は hoge フォルダに hoge.txt というファイルを作成しました。
ここから hoge フォルダの中身を Git で管理していきます。

まずは、Git のセーブデータのフォルダを作ったり、準備をする必要があるため、 git init というコマンドを打ちましょう。
Initialized empty Git repository in xxx/hoge/.git/
こんな感じに表示されれば成功です。
では、管理しているデータがどうなっているのか、管理している最新のデータと現在のフォルダの差分がどうなっているか、それぞれ確認してみましょう。
管理データを見るコマンドは git log 、管理データからどんな差分が生まれているかを見るコマンドは git status です。
まだ何のファイルも Git で管理していない状態で git log を打つと、以下のようなエラーが表示され、「まだ何も管理してないよ~」と怒られると思います。
fatal: your current branch 'main' does not have any commits yet
git status と打つと、以下のような表示が出てきて、 hoge.txt が赤い文字で表示されていると思います。

ファイルを追加したときはこのように表示されていますが、ファイルの変更・削除時は、 No commits yet とあったところに赤い文字で表示されます。(こっちを見ることが多いはず)

また、この後の章では一番上の行の On branch main といった所に表示される情報も重要になってきます。
このように git status は使い所が多く、Git を操作する上で欠かせないコマンドの一つです。
では早速 Git でファイルを管理してみましょう。
Git は、フォルダ全体をセーブするのではなく、新しいセーブデータに含めるファイルの操作を選んで、それを一つにまとめて新しいセーブデータ、 コミット を作成します。
最新のセーブデータからファイルを操作して出来た差分は ワーキングツリー に存在しています。
ゲームでいう、電源を消したら消えるデータの部分です。(Git では PC の電源を消してもワーキングツリーは消えませんが。)
ワーキングツリーの中から新しいコミットに含めるよ、と選択したものは インデックス(ステージ) に移動します。
そして、インデックスに入っているものを Git の新しいコミットとして保存していきます。

git init したてのフォルダでは Git はコミットを一つも持っていないので、今回は「hoge.txt を追加した」ことがワーキングツリーに存在しています。
ワーキングツリーからコミットに含めるよ、と選択するときに必要なコマンドが git add <ファイル名> です。
先程の git status で赤色に表示されていたのがワーキングツリーに存在している差分です。表示されていた hoge.txt をインデックスに入れてみましょう。
git add hoge.txt
これでもう一度 git status を確認してみると、

Changes to be committed という欄に今度は緑色で hoge.txt の名前が表示されました。 git status ではインデックスに存在している差分が緑色で表示されます。
最後に、そのインデックスをコミットとして Git に保存させましょう。
git commit -m "" の "" の中に、そのコミットで変更した差分の作業内容のタイトルやメモ書きを書きます。ではコミットしてみましょう。
git commit -m "hoge.txtファイルを追加した"
さて、これで git status を見ると、

全て消えてしまいました。それもそのはず、 git status で確認するのはワーキングツリーとインデックスの差分のみ。
コミットの歴史を確認するのが前述の git log です。ここで git log を実行すると、
commit 1ba59034db6331cf4f4de5476c9ff5fc31964757 (HEAD -> main)
Author: <gitのアカウント名> <メールアドレスは伏せます>
Date: <ここにコミットした日付>
hoge.txtファイルを追加した
と表示されます。ここまでできれば git の実用的な操作は半分くらいできたと言っても過言ではないです。おめでとう!…いや過言かも。
基本的には、適宜 git status や git log で Git の状態を確認しつつ、
ファイルを変更 → コミットする変更差分を git add でインデックスに移す → インデックスに集めた差分を git commit でコミットにする → ファイルを変更 → …
を繰り返すのが Git でのフォルダ管理になります。

そして、このサイクルからわかる通り、コミットは前のコミットからどんな変更をしたかという 差分 の塊なので、コミットたちは分岐したり収束したりせず、鎖のように必ず一列に並びます。

Git はこの鎖状に並ぶコミットたちによって、フォルダ内の歴史を管理しているのです。
Tips その1
ワーキングツリーの変更・削除を消したい!/インデックスの差分をワーキングツリーに戻したい!
git status を実行した際に表示されるのでわざわざ書くまでも無い気もしますが一応。
git add したファイルや git commit したファイルを、それぞれ一つ前の状態に戻したいときに実行するコマンドです。
git restore <ファイル名>
git restore --staged <ファイル名>
ファイルの変更の一部だけインデックスに入れたい!
コミットはなるべく作業内容を細かく分けてコミットするべきと言われていますが、コミットをしないまま大量に変更を作りすぎてしまった場面で以下のコマンドが役立ちます。
git add -p <ファイル名>
このコマンドを実行すると、
(1/n) Stage this hunk [y,n,q,a,d,e,?]?
と聞かれます。ここで何度か、この hunk(差分の一部)をインデックスに入れる?と対話形式で聞かれるので、入れる、入れない、と回答していくと、部分的にコミットできます。
ちなみに選択肢は、
y: 現在のhunkをインデックスに入れる
n: 現在のhunkをインデックスに入れない
q: 現在のhunk以降全てインデックスに入れず中断する
a: 現在のhunk以降全てインデックスに入れる
d: 現在のhunk以降全てインデックスに入れない
g: 存在してるhunkからhunkを探し選ぶ
/: hunkを正規表現で検索する
j: 現在のhunkを未定とし、次の未定のhunkに移る
J: 現在のhunkを未定とし、次のhunkに移る
k: 現在のhunkを未定とし、前の未定のhunkに移る
K: 現在のhunkを未定とし、前のhunkに移る
s: 聞かれているhunkを更に分割する
e: 聞かれているhunkを手動で編集する
?: ヘルプの表示
とのことです。実際に使える返答は Stage this hunk []? の [] の中に書いてあるものになっています。
直前のコミットにインデックスの変更差分を取り入れたい!
しまった、この差分をコミットに入れ忘れた!この差分、直前のコミットに入れた方がコミットのまとまりがいいよな…といった時にこのコマンドが役立ちます。
このコマンドを使う前に、直近のコミットに入れたい差分はしっかりと git add でインデックスに入れましょう。
git commit --amend
このコマンド実行時にコミットメッセージを聞かれるので、修正する場合はここで修正しましょう。
ちなみにコミットメッセージを聞かれる時に TUI のテキストエディタが出てくるので、抜ける時のコマンドも念のため書いておきます。vim を抜けるときは :q 、nano を抜けるときは Ctrl+x です。
ワーキングツリーの差分を一旦なかったことにしたい!なかったことにしたやつを呼び戻したい!
手元の作業内容は消したくない、でも作業内容を追加する前に戻りたい…そんなときに使えるコマンドです。
決して手元の作業内容を消したい時に使うコマンドではないです。ちゃんと git restore を使いましょう。(身内の話)
git stash
stash したものはスタック上に管理されるので、取り出す時は以下のように pop で取り出します。
git stash pop
一旦コミットの差分をワーキングツリーに戻したい!コミットをなかったことにしたい!
コミットを取り消してワーキングツリーやインデックスに戻したり、完全に削除したりする時のコマンドです。
このコマンドで指定するコミット ID は、取り消すコミットの一つ前のコミットの ID です。A→B→C の B と C を消したい時は A の ID を指定します。
ID は git log で表示される 1ba59034db6331cf4f4de5476c9ff5fc31964757 みたいな ID です。
ワーキングツリーに戻す時は、
git reset --mixed <コミットID>
# デフォルトの引数は `--mixed` なので省略しても同様にワーキングツリーに戻ります
git reset <コミットID>
あまりユースケースは無いですが、インデックスに戻す時は、
git reset --soft <コミットID>
そして、コミットを丸々削除する時は、
git reset --hard <コミットID>
です。
Gitでブランチを使ってみよう
この章で扱うコマンドは、
git branchgit switchgit mergegit rebase
になります。
Git は 分散型バージョン管理システム と呼ばれています。これまでの説明で「バージョン管理システム」であることは理解できたと思いますが、「分散型」らしいところはまだ登場していません。
前の章でも、コミットは分岐や収束はせず一列に並ぶという説明をしました。
ですが分岐できた方が嬉しい場面も多いはずです。共同開発するなら各人でそれぞれの歴史を歩んで後から合体できた方が嬉しいです。実験的に編集していく歴史を作り、うまく行ったら合体、駄目だったら簡単に放棄できると嬉しいです。
ここで登場するのが ブランチ(branch = 枝) という概念です。
大づかみに説明すると、Git は鎖状にコミット列を管理しますが、それに名前をつけて複数扱えます。そのコミット列の 1 つ 1 つが ブランチ です。

ブランチ一覧を確認してみましょう。
git branch
すると、Git で管理しているブランチの名前一覧が表示されます。まだブランチを操作していない場合は main のみになっているかと思います。以下のように表示されると思います。
* main
Git の設定によっては master になっているかと思います。気になる場合は後述の Tips を見てブランチ名を変えておきましょう。
master のまま読み進める場合は、main を master に読み替えてください。
では実際にブランチを切ってみましょう。
以下のコマンドで main (現ブランチ)と同じコミット列を持ったブランチを作成できます。
git branch <ブランチ名>
さらにそのブランチに移動してみましょう。以下のコマンドで指定したブランチに移動出来ます。
git switch <ブランチ名>
新しく作成したブランチでコミットを作成してみましょう。ここでコミットを作成しても、 main ブランチ(元いたブランチ)には影響が出ません。
これが共同でディレクトリを編集するときにたいへん役に立つのです!!
ちなみに、上記 2 つを同時にできるコマンドが以下のコマンドで出来ます。これを使うので私は git branch <ブランチ名> は使っていません。
git switch -c <ブランチ名>では、分けたブランチを main ブランチ(元いたブランチ)に取り込んでみましょう。作業したいブランチで作業が終われば、元いたブランチに git switch を使って戻りましょう。
git switch main
現状の Git の状態はこうなっているかと思います。

ここで使うコマンドは git merge <ブランチ名> , git rebase <ブランチ名> の 2 つです。それぞれ厳密に挙動は違えど、どちらもコミットを取り込むコマンドです。
上の図で言えば、「コミット D」の差分を main に取り込ませたいのです。
今回のケースでは、どちらを使っても main は同じように「コミット D」を先頭に持つことができます。
merge と rebase の違い
最後にそれぞれ merge と rebase の違いを説明しておきたいと思います。
先程の例だと違いが説明できないため、説明用の状況を想定して解説したいと思います。
main と working ブランチがあり、 main に 「コミット A」「コミット B」があり、 working ブランチを切った後に 「コミット X」を作成したとします。
また working ブランチでは「コミット Y」を作成したとします。
図に起こすとこんな感じ。

ここで working が持っている差分「コミット Y」を main に取り込みます。
merge の場合
git merge の場合は、各ブランチのコミットの差は X と Y なので、X も Y も作業済みというコミットを main の最後に作成して取り込んでしまおうという作業をします。
イメージはこんな感じ。Z が X と Y 両方終えた状態を指すコミットです。X と Y はコミットした時刻が新しい方が先頭に来ます。(下の図は Y の時刻が新しかったとする)

rebase の場合
rebase の場合は、 working のブランチをベースとして、差分のコミット(この図では X のコミット)を続きに繋げて行くという作業をします。
イメージはこんな感じ。

X と Y が単純に混ぜられない場合に起きる コンフリクト(conflict) 、 merge と rebase はどちらがいいのかなど、まだ詳しく話したいことは山ほどあるのですが、それだけで 1 記事書けそうなので今回はこの程度で。
Tips その2
ブランチ名を変えたい!
うっかりブランチ名を打ち間違えたときや、Git のバージョンが古く main が master になっていたときに使えます。
git branch -m <ブランチ名>
ブランチを削除したい!
merge や rebase で取り込み終わったブランチを削除したい時、間違えてブランチを作成したときに使えます。
消したいブランチに現在いる時は git switch で移動しましょう。
git branch -d <ブランチ名>
現在いるブランチに、消したいブランチのコミットが全て含まれていないときに、本当に消して良いのか?と確認が来ます。(取り込んでもない、うっかり作ってコミットの差がない訳でもないブランチをターゲットにする事が少ないので)
そんなときに強制的に消す時はオプションを -D にしてあげると消せます。
git branch -D <ブランチ名>
前のコミットの状態に一時的に戻りたい!
過去のバージョンに戻りたい時は以下のコマンドで移動できます。
git switch -d <コミットID>
ブランチ移動と同じ理屈で移動しているので、戻る時はブランチ名を指定すれば最新コミットに戻れます。
git switch main
ブランチ切って作業するつもりが main にコミットしちゃった!
git reset をしてワーキングツリーに下ろして、ブランチを切ってからコミットする事もできると思います。
ですが、間違えて main にコミットした数が複数ある場合、作業するブランチでコミットし直すのは大変です。
私は昔そうしていたのですが、Git のブランチ作成はただのコミット列の複製と知ってからは、以下の手段で作業ブランチにコミットを移動させるようにしました。
本来コミットしたかったブランチを作成し、
git switch -c <作業するブランチ>
main に戻り、
git switch main
main に残したくないコミットを削除して、
git reset --hard <mainに残したい最新コミットのハッシュ値>
作業するブランチに戻ります。
git switch <作業するブランチ>
GitHubも使ってみよう
この章で扱うコマンドは、
git remotegit pushgit clonegit fetchgit pull
になります。
Git と Github をよく混同されがちですが別物です。Github は Git のデータを Web 上で管理してくれる一番メジャーなサービスです。
ついにここまで来ました。ここまで理解できれば他人と Git を操作できるようになれます。
ブラウザでGithubを開きましょう。
Github で新しいリポジトリを作成します。ここで手元にある Git のデータを保存する場所を作成します。緑色の「New」というボタンを探して押してください。

リポジトリ名を設定し、説明文も必要であれば設定したら、 Create Repository で作成しましょう。

作成されたら、[ HTTPS | SSH ] のボタンを SSH に切り替え、git@github.com:<アカウント名>/<リポジトリ名>.git の URL をコピーします。

ブラウザでの作業はここで一旦終わり。またターミナルに戻り、コマンドで操作していきます。
先程コピーした SSH の URL を使って、Github に登録したい Git リポジトリで以下のコマンドを実行します。
git remote add origin <SSHのURL>
これで remote 関連の git push , git fetch , git pull のコマンドが使えるようになります。
origin という名前で設定した URL を以降のコマンドで指定します。
では main を Github に共有しましょう。
origin に main のブランチを共有する時は以下のコマンドを実行します。
git push origin main
ここでブランチ一覧を確認してみましょう。 -a を付けるとリモート(Github)のブランチも確認できます。
git branch -a
実行すると以下のように表示されると思います。
* main
remotes/origin/main
PC 内で origin というリモート (= GitHub) に追加した main ブランチの情報もキャッシュとして保持しています。
あくまで、origin にあるもの(remotes/origin/main)と PC 内のブランチ(main)は別ブランチとして扱っています。
これでリポジトリを Github に上げる側の作業は終わりです。
では Github からリポジトリを持ってくる側の作業を見ていきます。
Github で持ってきたいリポジトリで Code ボタンを押下し、SSH の URL をコピーし、以下のコマンドで PC 内にコピーします。

git clone <SSHのURL>
これで手元の PC に他人の Git のデータを持ってくることができました。
また、他人が git push で新しいコミットを Github に上げた場合、それを PC 内に取り込む必要があります。
取り込むと言えば?そう、 git merge か git rebase ですね。ここでは git merge を使っていきます。
まずは PC 内の remotes/origin/main を Github の最新の main ブランチと同じになるよう更新する必要があります。
以下のコマンドで origin (= GitHub) の main のブランチを PC 内に持ってきて、PC 内のリモートブランチ(remotes/origin/main)を更新します。
git fetch origin main
そして、 remotes/origin/main をローカルの main に git merge を使用して取り込みます。main ブランチで以下のコマンドを実行します。
(コマンドで指定する際は remotes/ は書く必要がありません。)
git merge origin/main
ちなみに、上記 2 つのコマンドを一括でしてくれるのが git pull です。解説のために後で説明しましたが、基本的にこちらを使っていきましょう。
git pull origin mainこれでリモートに関しても最低限扱えるようになりました!おめでとうございます!!!
Tips その3
origin に設定したURLを確認したい! origin を別のURLにしたい!
origin に設定した URL は以下のコマンドで確認できます。
git remote get-url origin
また、 origin に設定した URL を変更したい場合は以下のコマンドで更新できます。
git remote set-url origin <新しいURL>
最後に
これだけ長々と Git の知識について書いてきましたが、これで Git 全部理解したわけではありません。多く見積もっても 3 割程度しか触れてないのではないでしょうかね。
ですが、これで Git を使ってディレクトリのバージョン管理をしたり、Github で共同で作業をしたりするときに困ることもなくなるのではないかと思います。
Git「完全に理解した」といって完全に理解していないのはお約束、みなさんもこの記事を起点にもっと Git の様々な使い方や機能を知り、 Git チョットデキル を目指してみましょう!