Gitの意味をやっと理解した

こんにちは。

恥ずかしい話、
"git push origin master"
というコマンドは魔法のように今まで使っていました・・・。
最終的には上記のコマンドの意味合いを着地点として、Gitの使い方をさっと見ていく場とする。


# Gitは何のために存在するか？
結論からGitの役割を言うと、**ファイルの変更履歴を保存し、管理するためのツール**である。

プログラムを作成する上で、私たちはファイルの中身を常に更新（削除も含む）を行なっていく。その中では、**間違った更新や挑戦的な更新**も含まれている。また急なトラブルによりファイルが壊れたり、ファイル自体が消えてしまうこともあるだろう。こんなことが起きてしまったときに、人間がタイムマシーンで時間を遡れればいいのであるが、現実的ではない。そんなときに便利となるのが、「**バックアップ**」である。

バックアップを取るのはGit以外でももちろん可能である。
例えば、Linuxを勉強した者にとっては"cp"コマンドを使ってバックアップを取ればいいのではと思うだろう。
しかし、cpコマンドでは保存はできるが、管理には向いていない。

```
$ ls
aaa.txt		backup02.txt	backup04.txt	backup06.txt	backup08.txt
backup01.txt	backup03.txt	backup05.txt	backup07.txt	backup09.txt
```
仮にcpコマンドでバックアップを取ろうと思ったら、上記のようにファイルでいっぱいになり、どれがどれかわからなくなるであろう。これを解決する一つの手段がGitである。

Gitの役割を理解したところで、これからGitの仕組みと使い方を見ていく。

# Gitのインストール

```
$ git --version
git version 2.29.1
```
上記のように動けばすでにインストール済みである。
もしインストールされていなければ、CentOSnの場合はyumで、Ubuntuの場合はapt-getで"git-core"をインストールしよう。

# Gitの基本的な使い方

## Gitの初期化

これから下記のようなファイル構造を考える。
![スクリーンショット 2021-05-04 10.27.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255565/cbc621c8-00f1-1f57-6b1b-5507aef53fa2.png)

今回は簡単のためにfirst_projectの中にsay_hello.shというシェルスクリプトを作成・修正する履歴をGitで管理する状況を考える。

Gitを使うためには、**"リポジトリ"**と呼ばれる、Gitがファイルの履歴を保存する場所を生成します。
そのためには、first_project下で下記のコマンドを打ちます。

```
$ git init
```

![スクリーンショット 2021-05-04 10.27.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255565/4fe112ea-a543-e1f0-17bd-6d6625e33312.png)

これにより、".git"と呼ばれるディレクトリが作成されます。（ls -a で確認できる。)
これがGitのリポジトリの実体であり、この中にファイルの履歴が保存されていきます。つまり、このファイルがGitにおいてとても大切になっているので、このファイルのバックアップを取っておく必要があります。（後ほど方法は記載します。）

.gitの中のファイルは特別な形式に変換されているので直接編集することはできません。そのファイルを編集するためには、リポジトリの内容を通常ファイルとして復元する必要がある。このリポジトリの内容をファイルとして展開する場所を**ワークツリー**と呼ぶ。
上記では
リポジトリ：     /home/git/first_project/.git/
ワークツリー :  /home/git/first_project/


## リポジトリへファイルを追加

```
git add <ファイル名>
```
上記のコマンドによりリポジトリにファイルを追加することができます。
まず、ファイルを追加するためにsay_hello.shというシェルスクリプトを作成します。

```
$ vim say_hello.sh
........編集........

$ chmod 744 say_hello.sh
```
chmodはsay_hello.shを実行可能にするために使っている。

このファイルは下記であるとする。

```say_hello.sh
#!/bin/bash

echo "Hello!"
```

今のファイル構造は下記である。

![スクリーンショット 2021-05-04 10.50.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255565/ddb085a3-bb66-dc8e-b3eb-81de1a3bda2e.png)


ここで注意したいのが、ワークツリーにファイルは作られたが、リポジトリには反映されていない。
よって次の作業はリポジトリに今作ったファイルを取り込むことである。

Gitのリポジトリに履歴として追加するためには

1. git add
1. git commit

を使う。
この２種類のコマンドを理解するためには、「インデックス」というものを理解する必要がある。

説明のため、次の小節ではsay_hello.shの他に、explain.txtというファイルがあると仮定する。

### インデックスとは

**インデックスとはワークツリーとリポジトリの間にある空間**である。
下記の図と睨めっこをしてほしい。

![スクリーンショット 2021-05-04 11.19.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255565/0979942a-a679-3986-7ee3-6f78f32c94c4.png)

インデックスは単なる空間であるが、インデックスの役割はかなり大きい。
それは、リポジトリにそれぞれのファイルごとに追加することができることである。
詳しくは後ほど述べるが、リポジトリに追加する前には、何を変更したのかをコメントする機能がついている。
もし仮に、インデックスが無く、一度に全てのファイルを追加しなければならなければ、コメントが綺麗にならず、見返すときに苦労する。

上記の図の
①が git add に対応し、
②が git commit に対応する。

つまり、 
git add <ファイル名> : 更新されたファイルをインデックスに追加すること
git commit : インデックスのファイルをリポジトリに追加すること。
である。


さて、先程コメントの話をしたが、 なぜコメントをつける必要があるのか？
それは、cpの話を思い出してほしいのだが、cpは保存はできるが管理には向いていない。
なぜなら、違うファイルを比べたときに何が変更されたかがファイルの中身を見るまではわからないからである。一方で、コメントがあることで、毎回commitされる度にどんな変更が加えられたかが明らかである。チーム開発では「他人にもわかる状態」というのはかなり大切な部分となる。

さぁ、コメントの付け方を見てゆこう。
git commitにはコメントの付け方が2つ存在する。

1. オプションをつける。
1. vimで書く。

１つ目は、

```
$ git commit -m "コメント"
```

これで、コメントがつけられる。創造であるが、小さな変更、コメントが短い場合はオプションで良いと思われる。

２つ目は git commitと打つと自動的にvimが開かれる。これは大きな変更、コメントするべきことが多いときに使われると思われる。

ここまでで、リポジトリへの追加方法を見てきた。
この二つさえ知っておけば、リポジトリに履歴を登録はできる。
次のステップは、そのリポジトリの見方である。

## git log と　git diff

読者の皆さん、git logと入力してみてほしい。何が表示されるだろうか？
commitという一意的なコミットID、Author、Date、コメントが表示されると思う。
これはいわゆる、いつ誰が何を変更したかを見れる状態になっているものである。コミットIDは更新する前に戻りたいなどと言ったときの指定に使うことができる。


次に git diffであるが、これはワークツリーとインデックスの差分を表す。
これは、コミットする前に何を変更したかを可視化するために用いられる。詳しい内容は、ファイルを変更して、addして、git diffを自分で入力して確かめてほしい。そんなに難しい内容ではないと思われる。

皆さんもご存知の通り、Gitには3つの領域が存在する。それぞれの差分を表示するには下記のコマンドを使う。
git diff : ワークツリーとインデックス
git diff --cached : インデックスとリポジトリ
git diff HEAD : ワークツリーとリポジトリ

何を変更したかを具体的にみたいときにかなり役立つので覚えておきたい。

## ブランチという存在

ブランチは単独開発では使い道をイメージしづらいが、皆さんは一人で自分自身のポートフォリオを作成しているとしよう。大体は作り終わったのだが、ヘッダーをアップデートしたい状況を考える。このとき、皆さんはそのまま作り始めるだろうか？「うん！」と頷く人も多いだろうが、リスクとして、その更新がうまくいかない可能性もある。また、急にフッターを変更する必要があるかもしれない。そんなときにブランチというものがあったら便利である。

ブランチとは**枝分かれしたそれぞれの履歴の道筋**のことである。履歴を枝分かれできるんだなぁ。と覚えていただけたら良い。

![スクリーンショット 2021-05-05 6.53.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255565/39651d83-f566-6579-dafb-252a0a84653f.png)

イメージとしては、上のイメージである。
headerブランチで更新した内容はこの後説明するマージをするまではmasterブランチには影響を及ぼさない。つまり、headerブランチで仮にデータが壊れたとしてもmasterブランチには影響ない。上記の図の状態を作るためには下記のように進めていく。

### git branch

上記のコマンドで、今存在するブランチと今どのブランチにいるかがわかる。おそらく皆さんは、masterというブランチしか存在せず、横にアスタリスクがあるであろう。アスタリスクが意味するのは自分がいるブランチである。

### git branch <ブランチ名>

上記で新しいブランチが生成できる。今回であれば

```
$ git branch header
```

である。これで新しいブランチが作成された。git branchで確認されたい。

### git checkout <移動先ブランチ>

上記でブランチを移動できる。今回であれば、

```
$ git checkout header
```

とかける。これで、もう一度git branchをするとアスタリスクの位置が変わっているのがわかると思う。その後何かファイルを変更して、addとcommitを2度行い、さらにmasterブランチでもファイルを変更してaddとcommitを行うと上の図の状態になる。


### git merge <マージするブランチ名>

次にマージする方法を見ていく。マージとは**1つのブランチ内容を別のブランチに取り組むこと**である。
考える状況としては下記である。
![スクリーンショット 2021-05-05 7.04.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255565/863daea1-3017-de44-eec6-8a0fe6db8256.png)

今回はheaderブランチをmasterブランチに取り込みたいので、まずmasterブランチに移動して上記のコマンドを入力する。今回であれば

```
$ git merge header
```

である。これを入力すると、commitと同じようにvimが起動し、コメントを求められる。これで、マージが完了する。

```
$ git branch -d header
```

その後、上記でheaderブランチを削除できる。

今まで見てきたように、ブランチを切り替えることで、互いに依存せず異なる作業を並行して行うことができる。これはチーム開発でも大きく役にたつ。

最後に git push と git remote add の使い方を見ていく。

ここでは、リポジトリのバックアップを作成することを通じて、上記の2つのコマンドを理解を進める。

![スクリーンショット 2021-05-05 8.18.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255565/60249e76-b130-4a13-3b0d-be3db325b8fd.png)

上記のようなファイル構造を考える。first_project.gitの中にバックアップ用のリポジトリを作成する。

### バックアップ用リポジトリの作成

```
$ cd /home/git/Share/first_project.git
$ git --bare init
```

git init に--bareを付けるとバックアップ用リポジトリが作成される。
次にこのリポジトリにバックアップを取りたいリポジトリを送る作業を行う。

### 変更の履歴を送る

変更の履歴を送るためには、git pushを使う。
文法は、"git push <送信先リポジトリ> <送信元リポジトリ>:<送信先ブランチ>"である。
今回の例で言えば、

```
$ git push /home/git/Share/first_project.git master:master
```
である。これで、今のリポジトリのバックアップは取れた。もし、/home/git/first_projectのリポジトリが壊れた場合は

```
$ git clone <複製元リポジトリ>
```
で復元することができる。

ここで、楽をしよう。正直毎回、/home/git/Share/first_project.gitと打つのはめんどくさい・・・。
こんなときに remote addを使用する。Linuxコマンドでいうと alias みたいなもので、push先のリポジトリのパスに別名を付けることができる。

```
$ git remote add origin /home/git/Share/first_project.git
```
と書くと、このパスは"origin"という名前に生まれ変わる。この"origin"という割り当てる名前はなんでも良いが、慣習としてこの名前が使われる。

一度このコマンドを使うと、次からpushする際は

```
$ git push origin master
```
とかけてスッキリするし、よく見慣れているコマンドになる。（本質的理解）
ちなみに、master:masterは左と右が同じ場合は単にmasterと省略できる。


私は最後のコマンドを呪文のように覚えていたので、今回改めて本質を見れたところでスッキリしている。
今回は一人でgitを使うことしか想定できていないが、チームで使うとなるともっと大きな恩恵を得られる。

チームでの話はまた後日書くとする。
では。