Git 完全に理解する
はじめにの前に
この記事は、2023年のアドベントカレンダーで私が執筆した記事を元に、一部修正・改良をした記事となっています。
以前のものを読んだことがある方は知っている内容がほとんどかと思いますが、セルフホスティングサイトによるスタイリングの改善や、誤記の修正、説明方法の変更、画像の追加などを行っていますので、多少新鮮な気持ちで楽しめるものとなっているかと思います。
はじめに
この記事は C3 Advent Calendar 2023 25 日目の記事です。
この記事では初心者でもこれ一つ読むだけで 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/
Gitでバージョン管理してみよう
この章で扱うコマンドは、 git init, git add, git commit, git log, git status になります。
まずは適当に空フォルダを作成し、その中に適当にテキストファイル(画像や動画、音声ファイルなどのバイナリファイルでなければ何でも良し)を作成しましょう。そして、ターミナルでそのフォルダを開きます。私は hoge フォルダに hoge.txt というファイルを作成しました。
ここから hoge フォルダの中身を Git で管理していきます。
まずは、Git のセーブデータのフォルダを作ったり、準備をする必要があるため、 git init というコマンドを打ちましょう。
Initialized empty Git repository in hoge/.git/
こんな感じに表示されれば成功です。
では、管理しているデータがどうなっているのか、管理している最新のデータと現在のフォルダの差分がどうなっているか、それぞれ確認してみましょう。コマンドはそれぞれ、 git log, git status です。
まだ何のファイルも Git で管理していない状態で git log を打つと、まだ何も管理してないよ~とエラーが出て怒られると思います。
git status と打つと、
On branch main
No commits yet
Untracked files:
(use "git add <file>..." to include in what will be committed)
hoge.txt
nothing added to commit but untracked files present (use "git add" to track)
という表示が出てきて、 hoge.txt が赤い文字で表示されていると思います。ファイルを追加したときはこのように表示されていますが、ファイルの変更・削除時は、 No commits yet のところに赤い文字で表示されます。また、この後の章では一番上の行の On branch main といった所に表示される情報も重要になってきます。 git status は Git を操作する上で欠かせないコマンドの一つです。
では早速 Git でファイルを管理してみましょう。
Git は、フォルダ全体をセーブするのではなく、新しいセーブデータに含めるファイルの操作を選んで、それを一つにまとめて新しいセーブデータ、 コミット を作成します。最新のデータからファイルを操作して出来た差分は ワーキングツリー に存在しています。ワーキングツリーの中から新しいコミットに含めるよ、と選択したものは インデックス(ステージ) に移動します。そして、インデックスに入っているものを Git の新しいコミットとして保存していきます。
[ワーキングツリー] → [インデックス(ステージ)] → [コミット]
git add git commit
git init したてのファイルの存在しているフォルダでは、Git の最新コミットは空なので、そのファイルを追加したことがワーキングツリーに存在しています。
ワーキングツリーからコミットに含めるよ、と選択するときに必要なコマンドが git add <ファイル名> です。
先程の git status で赤色に表示されていたのがワーキングツリーに存在している差分です。そのファイルをインデックスに入れてみましょう。
git add hoge.txt
これでもう一度 git status を確認してみると、
On branch main
No commits yet
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: hoge.txt
Changes to be committed という欄に今度は緑色で hoge.txt の名前が表示されました。 git status ではインデックスに存在している差分が緑色で表示されます。
最後に、そのインデックスをコミットとして Git に保存させましょう。
git commit -m "" の "" の中に、そのコミットで変更した差分の作業内容のタイトルやメモ書きを書きます。ではコミットしてみましょう。
git commit -m "hoge.txtファイルを追加した"
さて、これで git status を見ると、
On branch main nothing to commit, working tree clean
全て消えてしまいました。それもそのはず、 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 でのフォルダ管理になります。
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 --soft <コミットID>
そして、コミットを丸々削除する時は、
git reset --hard <コミットID>
です。
Gitでブランチを使ってみよう
この章で扱うコマンドは、 git branch , git switch , git merge , git rebase になります。
Git は 分散型バージョン管理システム と呼ばれています。これまでの説明でバージョン管理システムであることは理解できたと思いますが、分散型らしいところはまだ登場していません。ここで登場するのがブランチ(branch = 枝)という概念です。
雑に説明します。Git は鎖状にコミット列を管理しますが、それに名前をつけて複数扱えます。そのコミット列の 1 つ 1 つが ブランチ です。
ブランチ一覧を確認してみましょう。
git branch
すると、Git で管理しているブランチの名前一覧が表示されます。まだブランチを操作していない場合は main のみになっていますかね?(古い Git の場合は master ですかね?後述の Tips を見てブランチ名を変えておきましょう。)
では実際にブランチを切ってみましょう。
git branch <ブランチ名>
で main (現ブランチ)と同じコミット列を持ったブランチを作成できます。さらにそのブランチに移動してみましょう。
git switch <ブランチ名>
これで指定したブランチに移動出来ます。
ちなみに、上記 2 つを同時にできるコマンドが git switch -c <ブランチ名> で出来ます。私はあまり git branch <ブランチ名> は使ってません。
新しく作成したブランチでコミットを作成してみましょう。ここでコミットを作成しても、 main ブランチ(元いたブランチ)には影響が出ません。これが共同でディレクトリを編集するときにたいへん役に立つのです!!
では、分けたブランチを main ブランチ(元いたブランチ)に取り込んでみましょう。作業したいブランチで作業が終われば、元いたブランチに git switch を使って戻りましょう。
ここで使うコマンドは git merge <ブランチ名> , git rebase <ブランチ名> の 2 つです。それぞれ厳密に挙動は違えど、どちらもコミットを取り込むコマンドです。
それぞれ merge と rebase の違いを説明してこの章を終わりたいと思います。
main と working ブランチがあり、 main に A, B というコミットがあり、 working ブランチを切った後に X というコミットを作成したとします。また working ブランチでは Y のコミットを作成したとします。図に起こすとこんな感じ。
main A → B → X
working A → B → Y
ここで working が持っている差分(この図では Y のコミット)を main に取り込みます。
merge の場合は、各ブランチのコミットの差は X と Y なので、どちらも作業済みというコミットを X の後ろに作成して取り込んでしまおうという作業をします。
イメージはこんな感じ。Z が X と Y 両方終えた状態を指すコミットです。X と Y はコミットを後にしたほうが後に来ます(今回は Y)。
main A → B → X → Y → Z
working A → B → Y
rebase の場合は、 working のブランチをベースとして、差分のコミット(この図では X のコミット)を繋げて行くという作業をします。
イメージはこんな感じ。
main A → B → Y → X
working A → B → Y
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 stash をしてワーキングツリーに下ろして、ブランチを切ってからコミットする事もできると思います。
ですが、間違えて main にコミットした数が複数ある場合、作業するブランチでコミットし直すのは大変です。
私は昔そうしていたのですが、Git のブランチ作成はただのコミット列の複製と知ってからは、以下の手段で作業ブランチにコミットを移動させるようにしました。
本来コミットしたかったブランチを作成し、
git switch -c <作業するブランチ>
main に戻り、
git switch main
main に残したくないコミットを削除して、
git reset --hard <mainに残したい最新コミットのハッシュ値>
作業するブランチに戻ります。
git switch <作業するブランチ>
GitHubも使ってみよう
この章で扱うコマンドは、 git remote , git push , git clone , git fetch , git pull になります。
Git と Github をよく混同されがちですが別物です。Github は Git のデータを Web 上で管理してくれる一番メジャーなサービスです。ついにここまで来ました。ここまで理解できれば他人と Git を操作できるようになれます。
ブラウザでGithubを開きましょう。
Github で新しいリポジトリを作成します。ここで手元にある Git のデータを保存する場所を作成します。
リポジトリ名を設定し、説明文も必要であれば設定したら、 Create Repository で作成しましょう。
作成されたら、緑色の Code ボタンから SSH の URL をコピーします。
ブラウザでの作業はここで一旦終わり。
SSH の URL を使って、Github に登録したい Git リポジトリで以下のコマンドを実行します。
git remote add origin <SSHのURL>
これで remote 関連の push , fetch , pull のコマンドが使えるようになります。
origin という名前で設定した URL を以降のコマンドで指定します。
では main を Github に共有しましょう。
origin に main のブランチを共有する時は以下のコマンドを実行します。
git push origin main
ここでブランチ一覧を確認してみましょう。 -a を付けるとリモート(Github)のブランチも確認できます。
git branch -a
実行すると以下のように表示されると思います。
* main
remotes/origin/main
PC 内で origin というリモートに追加した main ブランチの情報も保持しています。あくまで origin にあるものと PC 内のブランチは別ブランチとして扱っています。
これでリポジトリを Github に上げる側の作業は終わりです。
では Github からリポジトリを持ってくる側の作業を見ていきます。
Github で持ってきたいリポジトリで Code ボタンを押下し、SSH の URL をコピーし、以下のコマンドで PC 内にコピーします。
git clone <SSHのURL>
また、他人が git push で新しいコミットを Github に上げた場合、それを PC 内に取り込む必要があります。取り込むと言えば?そう、 merge か rebase ですね。ここでは merge を使っていきます。
まずは origin/main を Github のブランチと同じになるよう更新する必要があります。
以下のコマンドで origin の main のブランチを PC 内に持ってきて、PC 内のリモートブランチを更新します。
git fetch origin main
そして、 origin/main をローカルの main に merge を使用して取り込みます。
main ブランチで以下のコマンドを実行します。
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 チョットデキル を目指してみましょう!