git add/commit/checkout

# 今回はGitのデータ管理とコマンド

皆さんGit、使ってますか？コマンド覚えたり、追加したり、コミットしたりなんかもう大変ですね。今回は、なかなかわかりにくいGitのファイル管理とコマンドの関係をお伝えしたいなと思います。

# Gitを使う目的
ところでGitってなんであんなにめんどくさいんでしょう。たったひとつのファイルを更新するだけで、ファイルを保存して、addしてコミットしてって、**面倒くさい**。いいじゃんセーブ！保存！それだけで！

いやー初心者の皆さんの心の叫びが聞こえてきますよ。本当に。

なんであんなに面倒くさいんでしょうか。その面倒臭さが安全の確保のためには必要だからです。

少しそもそもの話をしましょう。gitって何を目的としてるんでしょうか？

- プルリクやりたいから？ちがいます。
- 情報を共有したいから？ちがいます。

情報共有だけならファイルサーバーで事足ります。プルリクはそもそもGitの機能ではありません。

Gitはファイルのバージョン管理をするための仕掛けです。バージョン管理に必要な機能は何でしょう。

- ファイルの過去の一点の状態を自由に再現できる
- 削除してしまったファイルも含めて再現可能
- 誰がいつ更新したのか、更新内容も含めて管理できる

例えばファイル名に日付、更新者の名前を入れる。タイムスタンプと日付が全く噛み合わない。「助けて！どれが最新だかわからないの！」**悲しいけどこれ、あるあるなのよね**

じゃぁフォルダに…同じですよ。**誰かがルールを破ればおしまい**です。しかもソフトウェア開発で必要なファイルって数千〜数万のオーダーなんですよね。これを手作業とルールでどうにかさせるって無理ですわ。

だいたい、**_強制力のないルールなんて使えんですわ。_**　誰かが絶対にサボりますもん。

人の手によらず、システムの手を借りてこれを実現するのがGitを始めとしたSCM(software configuration management)の役割です。これを実現するために、SCMはリポジトリもしくはその前段の概念を導入しています。なぜなのか。ファイルシステムは先の、要件を満たさないので、新たな概念を導入する必要があるわけです。

# Git  を導入して版管理をしてみよう

さて、SCMの役割を説明したところで、実際の開発作業を例に、Gitの使い方を見てみましょう。

## 前提

- git は既に導入されている
- 実行するコマンドはbashで実行する

## 1-1. ファイルを作成する。

さて、プログラムやドキュメントの開発が始まりました。この段階で**gitを意識する必要はありません**普通に開発や執筆活動をしましょう。仮に作成物の構成ファイルを図にしてみます。ファイルの名前やディレクトリ、ディレクトリパスの深さなどは仮置きですので意味はありません。**ファイル名は自由ですし、ディレクトリの深さも自由です。**
![git-init-add-commit-フォルダ管理.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/837176/a8d6226c-6878-732a-46ab-2ead75d7605e.png)
あたり前のことですが、この状態ではファイルの変更や履歴は管理されてていません。ファイルシステム(通常のファイルやディレクトリ)にそうした機能がないからです。さて困りました。このままではこんな要求に応えることができません。

- 今日の作業をなかったことにして、昨日の終わりの状態にしたい
- 三日前は動いていたのに！！！何が違うのか確認したい
- あれ？前の実装の方が良かったな。戻そう。。
  
これをうまくやるための仕掛けが・・・そうSCM(≒git)です。

## 1-2. git 管理を開始する
さて、「ファイルの版管理のためにgitが欲しいな。。。」と思い始めました。「始めたいと思ったら始めればいい」のです。なにせ簡単ですから。c:\dev\rps\sample1で以下のコマンドを実行するだけです。

```bash: git 管理開始
git init
```

このコマンドなにをするものなのでしょうか。gitコマンドのuageをみてみます。

```
init              Create an empty Git repository or reinitialize an existing one
```

実行時のカレントディレクトリを対象に「Git repository」を作るか、既にあるなら初期化する。もっぱら「リポジトリを作る」と思っておけばOKです。ところでリポジトリってなに？

### リポジトリってなに？
Gitリポジトリは、管理対象のディレクトリの現在までの変更内容を管理するデータの集まりです。誤解を恐れずに言えばある種のデータベースと考えることもできます。過去の変更履歴を全て持っているため、いつだれが何のために何を修正したのかが分かります。

git init はカレントディレクトリを対象にこのgitリポジトリを作成するのです。ただし、この時点ではリポジトリ内には**何のデータも登録されていません。** 見えているファイルやディレクトリも「リポジトリの管理対象」であるという事実を除いては**通常のファイル**です。参考ですがこの管理対象になっているファイル群のことを「作業copy」とよぶ場合があるので覚えておいてください。

![git-init-add-commit-gi init.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/837176/71387892-cfb2-6a72-94bb-4ca93ecd01e7.png)

## 1-3. 格納したいファイルの指定
さて、作業がある程度進んできました。そろそろgitでのファイル管理を開始して、いつでも今の時点に戻る、いわゆる「セーブポイント」を作りたいと考えています。ここでは。

- dir1 の中のファイルは優先して作業している。だから今すぐ管理化にいれたい
- dir2 の中は作業は進んでいないので登録する必要はない。

前提で考えます。つまり、次に履歴を登録したくなるのはdir1 だということです。「次にこのファイルの履歴を登録しよう」これさえ決まっていればいいのです。気分かもしれません。登録直前に決まるかもしれません。いつでもいいのですが、「これからこのファイルの履歴を追加するよ」とファイル、ディレクトリに印をけることを **「ステージする」** と呼びます。

さぁdir1をステージしましょう。

```bash:管理対象をステージ
git add sample/dir1
```
![git-init-add-commit-git add.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/837176/e5fa0c0d-8796-cebe-2aee-e98bf9b8eb2e.png)

ステージは「印をつける作業」と先ほど言いました。ですが、後に続くコミットの作業をシンプルにするため、gitはこの段階で、将来リポジトリに格納するであろう差分データ（前回コミット箇所からの変更データ）を作成します。

ただ、前準備だけされているだけで、**リポジトリには何も格納されていない**ことに注意してください。つまり**うっかりファイルを消してしまっても、簡単に復活させることはできません。**

## 1-4. 変更を登録する。

次はいよいよファイルをリポジトリに登録する版です。この作業を「コミット」と言います。やってみましょう。簡単です。

```bash:コミット
git commit -m "初期登録”
```

このコマンドは、リポジトリにステージされたファイルの変更を登録するためのコマンドです。-m はコミットにつけるコミットメッセージを指定する事ができます。この時の動作は以下の通りです。
![git-init-add-commit-git commit.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/837176/dc37a0f8-56b6-bacc-78fd-be64529ddeca.png)

このとき、コミットには差分データから算出される「ハッシュ値」よばれる名前がつけられます。この場合は「0ca01da42ab」がそれにあたります（本当はもっと長いです。)ところでこの番号、人間が見て「何をしたか、いつのものか」分かりやすいものでしょうか？断言しますが**秒で忘れます。** なので、何をしたのか人間の言葉でコメントを残す必要があります。それが-m で指定した「初期登録」というコメントなのです。

```bash:ログを確認する
git log
```
コマンドで以下のようなログが確認できます。

![スクリーンショット 2022-02-26 001008.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/837176/1a4e15c4-6915-3867-2918-b40c53e49a65.png)

## 2-1. 開発作業を進める
さて、開発対象はコミットされ、**変更しようが、削除しようが、現状に戻す事ができるようになりました** とりあえずコミットしたファイルについては、心おきなく変更することができます。開発作業を進めましょう。

![git-init-add-commit-ファイルの修正.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/837176/c93a2678-0acc-ba04-9f1d-f509d84d4026.png)

ここではfile1/file2/file3に内容を書き足している事にしましょう。ただし、file3はまだgitに管理されていないの「修正差分」として図に記載していません。注意してほしいのは、この時点ではリポジトリ上のデータは**何も変更されていない**ことです。先ほどコミットしたときのまま、何も変更されない。**だがそれがいい!!** なにも変更されていないから、いつだってその状態に戻すことができるのです。キリッ!!

## 2-2. 修正のステージ
なんかいい感じになってきたな。「今の状態を保存したいなぁ～ file1だけ、あとついでに作業の進んでいるfile3(dir2)も登録しておこうかなぁ～」なんて考え始めました。そろそろリポジトリにセーブポイントを登録します。登録するには、「このファイル、ディレクトリの差分を保存してね」とgitさんに教える(ステージする)必要がありましたね。今回はfSample/dir1/file1とSample/dir2です。(or Sample/dir2/file3)

```bash:変更をステージする
git add Sample/dir1/file1 Sample/dir2
```
![git-init-add-commit-ステージ.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/837176/5147c0dd-bbd6-3128-f05f-81b2ef766a61.png)

git add は引数にステージするファイルをいくつも持つことが可能です。今回は Sample/dir1/file1 と Sample/dir2をステージするので、引数は2つとしています。

くどいようですが、この時点では変更したファイルはリポジトリ上に保存されていません。安心して消しちゃったりすると、後悔しかのころないので注意してください。

## 2-3. コミットする

リポジトリに変更を登録しましょう。

```bash:コミット
git commit -m "開発途中”
```
![git-init-add-commit-修正コミット.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/837176/78a9094f-b141-65b2-f8df-e2dbbf337679.png)

今回は、**file1の修正箇所**とdir2の中身丸ごとが「差分データ」としてリポジトリに登録されました。このコミットの名前であるハッシュ値は「1ca41faba1d」それに紐づけたコメントは「開発途中」です。

# ファイルを削除してみる

ここまで、リポジトリにファイルを追加することだけをやってきています。今度はリポジトリからデータを取り出すことをやってみましょう。たとば、自分の作業が気に入らなくて、前のセーブポイントまで戻りたいとき。等々。

## 3-1 ファイル削除

まずは、豪快にぜー－んぶ消してみます。

```bash:ファイル削除
rm -r Sample
```
![git-init-add-commit-ファイル削除.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/837176/8c77dee1-e8cf-108f-7829-4e0c9beaaad6.png)

あら。c:\dev\reps\sampl1からホントにファイル消えちゃいました。絶体絶命ですね。。ファイルを全部消してしまった僕の**明日はどっちだ！！**

## 3-2 復活の呪文を唱える

全部削除しちゃうと精神的なダメージでかいですね。でも、見えているファイルとは別にリポジトリに「過去にコミットしたデータ」がすべて残っています。慌てることはありません。復活の呪文を唱えましょう。

```bash: 復活の呪文
git checkout .
```

git checkout はリポジトリの状態を、ファイルとして引き出す（再現する）コマンドです。いまディレクトリには全く何もないので、最初のコミットから順番に再現していきます。

まずは、最初のコミットの再現です。dir1だけのコミットだったので再現結果はこの状態です。
![git-init-add-commit-復活過程1.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/837176/e23742b5-8e5f-ea36-bd71-5518a73cec0c.png)

次に、2度目のコミットの差分を適用します。ただし、あの時対象外としていたfile2の編集内容はリポジトリに格納されていないため、再現できません。

![git-init-add-commit-復活過程2.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/837176/7fcf1345-2bf9-f0fb-9055-dfbff16d9250.png)

実際に復活結果を比べてみましょう。左が削除前、右が復活後です。やはりコミットしていない情報は再現できません。

![git-init-add-commit-復活結果.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/837176/35137d56-464b-ce4b-8d37-72d710a83c8d.png)

# まとめ
今回はGitでの作業について以下のことを紹介しました。

- gitリポジトリとファイルは直接繋がってないよ
- commit 時に差分確認する対象はgit addで指定するよ。このことを「ステージする」というよ。
- gitはcommit時に前のコミットと差分データを格納するよ
- このデータはステージしたときにあらかじめ作られているよ


以上です。