【初心者向け】Gitの基本とできること【コマンド集】

# これを書く背景
エンジニアの方で、Gitを知らない方はいないだろうと思うくらい、常識的なソフトウェアです。
私もずっとGitという単語は耳にしていましたし、概念的に知っているという状態でした。
先日Gitについて具体的に学ぶ機会がありましたので、Gitについてまとめておきたいと思います。

Gitとは何か、何ができるのか、どう扱うのかを書いていきます。
初めてGitに触れる方も、私と同じで、具体的には初めて知る方にも有益な内容になることを願っています。
また、エンジニア以外の方でも知っておくととても強力なツールとなることは間違いないです。

# そもそもGitとは
Gitは、ファイルバージョン管理ソフトです。バージョン管理ソフトはsvnなど他にも存在しますが、分散型である点でGitは優秀です。これを実現するのに、サーバ等は必要なく、Gitをインストールするのみです。

## Gitでできること
- 誰がいつ、どこを、どのように変更したのかを調べることができる
- 変更前の状態に戻すことができる
- 第三者と同じファイル群を同時に修正し、修正結果を一つに統合することができる
- 　GitHubなどのファイル管理サービスなどと連携することで、第三者と履歴を共有することができる

# Gitのインストール
Macの場合は、Xcode、もしくは、Xcode Command Line ToolsをインストールするとGitも一緒にインストールされます。
もしくは、以下のコマンドでGitをインストールできます。
```
$ brew install git
```

以下のコマンドでインストールされたGitのバージョンを確認することができます。
```
$ git --version
```
# Gitで使われる領域の説明
実際にGitを使ってみる前に、領域について知っておきましょう。Gitではバージョン管理すると一言で言っても、さまざまなコマンドを使用します。
それぞれのコマンドがどの領域で何を行っているのかをイメージしながら学習すると理解が深まるはずです。
- **ワーキングディレクトリ(ワークツリー)**
    - ディレクトリやフォルダそのもので、この領域ではGit管理されていない領域です。
- **ステージングエリア(インデックス)**
    - Git管理されているファイルのある領域です。
    - Gitをインストールしても、どのファイルをGit管理配下に置きたいのか明示的に指示する必要があります。
- **ローカルリポジトリ**
    - バージョン管理情報が格納されている領域です。
    - ステージングエリアの状態が、バージョン管理されています。
- **リモートリポジトリ**
    - インターネット上にあるリポジトリです。リポジトリを公開して第三者と共有するために必要な領域です。

# Gitで管理する
## ディレクトリをGit管理できる状態にする
では、早速Git管理してみましょう。
まずはGit管理したいディレクトリに移動してGit管理するためのコマンドを実行します。
```
$ git init
```
次に、初期設定を行いましょう。
ユーザー名を登録しておきます。これから変更していく履歴際に記録する名称になります。
```
$ git config --global user.name "名前"
$ git config --global user.email　Githubで利用するメールアドレス
```
以下のコマンドで、configにどんな設定がされているかの一覧を表示することができます。
```
$ git config --list 
```

## ワーキングディレクトリにあるファイルをステージングエリアに登録する
```
$ git add ファイル名
```
## ステージングエリアの状態を、ローカルリポジトリに登録する
commitコマンドで1つのバージョンとして登録することになりますので、どんな修正をおこなっったのか、必ずコメントを付けるようにしましょう。
```
$ git commit -m "コメント"
```
参考までに、-aオプションを付けると、addとcommitを同時に実行することができます。ですが、これを実際に使う機会は少ないでしょう。
```
$ git commit -a -m "コメント"
```
ワーキングディレクトリではさまざまなファイルに変更が加えられている状況がほとんどです。そんな状態で、とにかく全部コミットするというのは大変危険です。
なぜならば複数機能をひとまとめにしてしまうと、1つのコミットの中に複数の変更が含まれるようになり、バージョン管理しにくくなってしまいます。
1コミット=1機能修正という形でコミット単位を最小限に分割することを心がけましょう。機能単位にコミットすることで、ひとつ前のコミットに戻りやすくなりますし、その方がコメントも短くシンプルになるはずです。

## 領域の状態を確認する
ワーキングエリア、ステージングエリア、ローカルリポジトリの状態を表示してくれます。
```
$ git status
```
- untracked
    - ワーキングディレクトリに、ステージングエリアで登録されていないファイルがある状態。
- modified
    - すでにGit管理されているファイルが、ワーキングディレクトリ、もしくは、ステージングエリアで変更されている状態。
- new file
    - ステージングエリアの中に、まだコミットされていない新しいファイルがある状態。
## 領域の状態を詳しく確認する
ワーキングエリア、ステージングエリア、ローカルリポジトリの状態を表示してくれます。
```
$ git diff    //ワーキングディレクトリとステージングエリアの差分を表示する
$ git diff --staged    //ステージングエアリアとローカルリポジトリの差分を表示する
```
実行すると、「-」や「+」という表現が出てきます。変更がない場合は何も表示されません。
- 「-」は、変更前の状態を表示している
- 「+」は、変更後の状態を表示している
## 変更を取り消す
行った変更を取り消したい場合は、以下のコマンドを使用します。
```
$ git restore ファイル名    //ワーキングエリアに加えた変更を、ステージングエリアの状態に戻したい場合
$ git restore --staged ファイル名    //ステージングエリアに加えた変更を、ローカルリポジトリの状態に戻したい場合
```
## ワーキングディレクトリ内のファイルをGit管理の対象にしないようにする
ワーキングディレクトリないに、「.gitignore」という隠しファイルを作成し、このファイルの中に、管理対象から外したいファイル名を記載して保存します。
作成したら、.gitignoreファイルもコミットする必要があります。忘れないように実施しましょう。
## ファイルを削除する
```
$ git rm ファイル名    //ワーキングディレクトリと、ステージングエリアの両方からファイルを削除する
$ git rm --cached ファイル名    //ステージングエリアのみからファイルを削除する
```
参考に、以下のコマンドを実行すると、ステージングエリアのファイル一覧を表示することができます。
```
$git ls-files
```
## ファイル名を変更、移動する
```
$ git mv 変更前ファイル名 変更後ファイル名    //ワーキングディレクトリと、ステージングエリアの両方からファイル名を変更、移動する
```

## コミットの履歴や内容を確認するさまざまなコマンド
### コミットの履歴を確認する
```
$ git log
$ git log ファイル名    //特定のファイルへの変更だけを表示する
$ git log --oneline    //各コミットを一行で表示する
$ git log --patch   //具体的に何が変更されているのかを表示する
$ git log -数字   //全てのコミットではなく、直近の数字分のコミットだけ表示する
$ git log --auther="ユーザー名"    //コミットユーザーでフィルタリングする
$ git log --after="YYYY-MM-DD"    //コミット日時でフィルタリングする
$ git log --grep="キーワード"    //コミットメッセージをキーワードで検索したい場合
$ git log --all    //以降で説明する全てのブランチのログを表示する
$ git log --graph    //ブランチの状態を視覚的に表示するオプションです

```
### コミットの内容を確認する
```
$ git show コミットID    //コミットIDを指定して特定のコミットの内容を表示する
$ git show HEAD~数字    //HEADの位置から相対的なコミット位置を指定して内容を表示する
```
### 特定ファイルの変更を誰がコミットしたのか、責任の所在を確認する
```
$ git blame ファイル名   //ファイル名内のどこを誰が変更したのか一覧を表示する
```
## コミットにダグを付ける
コミットにタグをつけることで意味のあるバージョンとして管理することができます。またタグ名だけではなく、メッセージをつけることで、バージョンの内容について説明を加えることができます。
```
$ git tag   //タグの一覧を表示する
$ git tag タグ名　(コミットID)   //コミットIDを指定しない場合は、最新のコミットに対してタグづけされます。
$ git tag -a タグ名　(コミットID) -m "説明"   //説明をつけてタグ付けを行う
```
## ブランチを作成して作業する
ブランチは、「枝、別れたもの」という分岐を意味しています。新しい機能の開発などを行う際にブランチを切って作業を進めると、変更前の状態（masterブランチ）に影響を与えることなく開発や変更を加えることができます。
- **masterブランチ**
    - 表現が難しいですが、正式な、主となるブランチです。新しくブランチを切って作業などを進めますが、最終的にはmasterブランチに取り込むのか、破棄するのかを選択することになります。
### ブランチを作成する
```
$ git branch ブランチ名
```
### ブランチの一覧を表示する
```
$ git branch
```
### ブランチを切り替える
今作業しているブランチを示すHEADの位置を変更します。
```
$ git switch ブランチ名
$ git switch -c ブランチ名    //-cオプションを付けると、ブランチの作成と切り替えを同時に行ってくれます。
```
### ブランチとの変更を確認する
```
$ git log master..ブランチ名    //masterブランチからのコミット履歴を確認する
$ git diff master..ブランチ名    //masterブランチからの変更内容を確認する
```
## ブランチをマージする
ブランチを切って作業を進めた内容を、masterブランチに取り込みますが、その方法には2つのパターンがあります。
この辺りの操作は、gitが行ってくれるのであまり意識する必要はないです。
```
$ git merge 統合したいブランチ名
```
#### ファストフォワードマージ
masterブランチが、分岐した時点から一度もコミットされていない場合に行われるマージの方法です。
#### 3ウェイマージ
masterブランチが、分岐した時点から、コミットされている場合に行われるマージです。

:::note warn
マージし終わったブランチはすぐに削除するようにしましょう。
```
$ git branch -d 削除したいブランチ名
```
:::
### スカッシュマージ
ブランチが分岐している(3wayマージ)の状態でマージを実行すると、コミットの履歴に分岐が含まれてしまい、コミット履歴が一本にならず美しくない状態になります。
その際に、スカッシュマージを実行すると、コミット履歴を一本にすることができます。簡単に動きを説明すると、ブランチを切って、コミットした履歴を、masterブランチの次のコミットして、一つにまとめるという動作をします。
ただし、複数名で開発する場合は、履歴を書き換えることになるので推奨はされていません。
```
$ git merge --squash 統合したいブランチ名
```
:::note warn
マージし終わったブランチはすぐに削除するようにしましょう。しかし、スカッシュマージした際のブランチを削除する場合は、-Dオプションをつけてましょう。
スカッシュマージのブランチを削除しようとすると、マージ前のエラーが表示されます。スカッシュマージは、内部的には分岐したブランチをマージされないためです。
```
$ git branch -D 削除したいブランチ名
```
:::
### リベース
ブランチが分岐している(3wayマージ)の状態の時に、スカッシュマージを行うのではなく、ブランチを切った際の分岐点をmasterブランチに変更（リベース）し、その後に分岐させていたブランチをマージする方法でも、コミット履歴を一本にすることができます。
```
$ git rebase master
$ git switch master
$ git merge ブランチ名
$ git branch -d ブランチ名
```
## ローカルリポジトリのコミット履歴を書き換える
基本的に、コミット履歴を書き換えることはご法度です。しかし、コミットが細かすぎたり、大きすぎたりする場合に適切なサイズに変更したくなります。

:::note alert
共同開発を行なっている際は、コミット履歴を書き換える操作は絶対に行わないようにしましょう。
特に、一度リモートリポジトリにプッシュしているコミット履歴を操作するのは危険です。他の人が過去にプッシュしていたコミットをクローンしていた場合、その方がリモートリポジトリにプッシュできなくなってしまします。
あくまで、そういった共同開発の状況をコントロールできる範囲での場合にとどめてください。
:::

### コミット履歴を消す
HEADとmasterのポインタの位置を変更することで、過去の履歴を消すことができます。
```
$ git reset --soft HEAD~数字    //ローカルリポジトリだけ巻き戻す
$ git reset --mixed HEAD~数字    //ステージングエリアも巻き戻す。オプションをつけない場合は、この動作になります。
$ git reset --hard HEAD~数字    //ワーキングディレクトリも巻き戻す
```
### コミットを打ち消す
resetではコミット履歴そのものを消していましたが、revertではコミット履歴を消すのではなく、コミットしたことを打ち消すコミットを、新たに実行します。
```
$ git revert HEAD~数字    //数字で指定したコミット分まで打ち消す
```
### 誤って消したコミットを復元する
そんなことできるの？って感じですが、できます。
HEADの動作ログというのを以下で確認して、過去の時点を指定してresetを実行することで、コミットを削除する前の状態に戻ることができるのです。
まず以下のコマンドで、HEADの動作ログを確認します。
```
$ git reflog
$ git reset --hard HEAD@{数字}    //HEADが今の時点から数字分前に戻る
```
### 最新のコミット内容を変更する
commitコマンドですが、以下のコマンドのようにコミットを実行すると、直前のコミットを修正することができます。
```
$ git commit --amend -m "コメント"
```
### 過去のコミット内容を書き換える
コミットID時点からリベースすることで以降のコミットをそれぞれ変更することができます。
ここでは動作は、各自で確認してみてください。
```
$ git rebase -i コミットID    //指定するコミットIDは、変更したいコミットの親コミットIDを指定します
```
-iオプションをつけてrebaseを実行すると、ひとつひとつのコミットに対しての操作を聞かれます。
上記のコマンドを実行すると以下のような表示さがされます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1499675/af27b3e5-2bce-810f-59ab-671d2b80abca.png)
変更したいコミット行の「pick」となっている部分を、「edit」に書き換えて保存します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1499675/6f48d0ed-aead-4b18-e37c-812c79298bac.png)

この状態で、加えたい変更を実際に行い、コミットを行います。

:::note warn
コミットの修正になるので、この時のコミットは修正になりますので、--amendをオプションをつけてください。
:::

次のコミット編集に移動したい場合は以下のコマンドで、操作を続けることができます。
```
$ git rebase --continue    //次のコミット編集操作に遷移します。次の編集コミットがない場合は、rebaseを全て実行します。
$ git rebase --abort    //rebaseを中止したい場合はこちらのオプションで抜けることができます。
```
### コミット履歴の順序を変更する
```
$ git rebase -i コミットID    //指定するコミットIDは、変更したいコミットの親コミットIDを指定します
```
上記のコマンドを実行すると以下のような表示さがされます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1499675/af27b3e5-2bce-810f-59ab-671d2b80abca.png)
コミット行の順序を入れ替えることで、コミットの順序を変更することができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1499675/2ce4c89b-773d-8290-becc-40996df216ef.png)
行を入れ替えたら保存してファイルを閉じましょう。
### コミットを統合する
```
$ git rebase -i コミットID    //指定するコミットIDは、変更したいコミットの親コミットIDを指定します
```
上記のコマンドを実行すると以下のような表示さがされます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1499675/af27b3e5-2bce-810f-59ab-671d2b80abca.png)
統合したいコミット行を「squash」に変更して保存します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1499675/da4c7037-ece8-7903-8c64-b6ecbd16e9e5.png)
そうすると、それ以前のコミットが統合されます。今回の場合、1,2行目のコミットが統合されるわけです。

# まとめ
Gitについて、基本的なコマンドと共に操作を見てきました。今やエンジニア以外でも活用が増えているGitについて学ぶことで、開発に必要な基本的な知識の獲得につながってくれたら嬉しいです。
最後にご紹介しているコミット履歴の操作は、正直覚える必要はないと思いますが、そんなこともできるんだって頭の片隅にでも置いておくことで今後のスキル幅が広がっていくかなと思い掲載しました。
（おそらく、この辺りの操作は、不具合の元なので、よっぽど迫られた状況以外で使うのはやめておきましょう。）

私自身、まだGitにつちえ学んだばかりですので、一緒にGitライフを楽しんでいきましょう！！
ログの見方慣れてないと見づらいよね。その辺りも早く慣れていきたいなぁと思っています。
ではこの辺りで。