いいかげんgitのことをちゃんと知りたいあなたへ（Gitオブジェクト編）

## いつまでたってもgitさんのことががよく分からなくつらい
恥ずかしながら自分、ほぼ毎日なんらかのかたちでgitに触れているのにもかかわらず、いまだによく分かってないです、gitさんのこと・・・
origin/master?あー、リモートリポジトリってやつ？Githubに置かれてるやつ?
インデックス?あー、ステージングとか、あの辺りのやつ?（Sourcetreeさんに頼ってばかりですんません）
ベアリポジトリ?あー、あの、くまーってやつね（なにが

## gitさんとちゃんとお付き合いさせていただきたい
きみのとこがちゃんと知りたい！
なので[pro git(日本語版)](https://git-scm.com/book/ja/v2)を読んでみた。

### 磁器コマンドと配管コマンドとゆうのがある
日々なんとなく使ってるのによく分かってない自分からすると、[Gitnの内側](https://git-scm.com/book/ja/v2/Git%E3%81%AE%E5%86%85%E5%81%B4-%E9%85%8D%E7%AE%A1%EF%BC%88Plumbing%EF%BC%89%E3%81%A8%E7%A3%81%E5%99%A8%EF%BC%88Porcelain%EF%BC%89)あたりをちゃんと理解するべきだと思いました。そのなかで出てくるコマンドの種類がふたつあります。

#### 磁器（Porcelain）コマンド
```
$ git clone
$ git add
$ git commit
```
お馴染みですよね。たぶん、**磁器のようにどっしりとしてる**からそう呼ぶ、と理解。
配管コマンドの集まりが磁器コマンド、というイメージです。

#### 配管（Plumbing）コマンド
```
$ git hash-object
$ git update-index
$ git write-tree
```

何やら見慣れぬコマンド・・・
ざっくり言うと、**パイプ処理で繋げたりして使用する**比較的低水準なコマンドのことを指すもよう。

たとえば、磁器コマンドを使用して

```
### 1.ファイル作って
$ echo 'aaa' > aaa.txt

### 2~3.インデックスに追加して
$ git add test.txt

### 4~6.コミットする
$ git commit -m 'add aaa.txt'
```

と同じことを、配管コマンドでしようとすると、以下となります。
コメントの番号は両コマンドが行う処理の内容に対応しています。

```
### 1.ファイル作って
$ echo 'aaa' > aaa.txt

### 2.blobとして登録して
$ git hash-object -w aaa.txt
72943a16fb2c8f38f9dde202b7a70ccc19c52f34

### 3.インデックスに追加して
$ git update-index --add --cacheinfo 100644 72943a16fb2c8f38f9dde202b7a70ccc19c52f34 aaa.txt 

### 4.treeを作って
$ git write-tree
a760aa994bbdd615f7ecb442be6bab636e11eba6

### 5.commitする
$ git commit-tree -m "add aaa.txt" a760aa994bbdd615f7ecb442be6bab636e11eba6
1e87843b08cdcd96216ab42ce56d6e0109c53427

### 6.でもってmasterブランチの参照を更新する
git update-ref refs/heads/master 1e87843b08cdcd96216ab42ce56d6e0109c53427
```
初見では「???」ですが、gitの内部構造を少し理解すれば「ははーん」となります。
順をおって説明してきますが、まずはgitの内部構造に軽く触れてみます。

### gitオブジェクトとオブジェクトデータベース
gitは管理対象のファイルやディレクトリ、それにコミット情報なんかを**gitオブジェクト化**して扱います。**化**というのがポイントで、gitはファイルをそのまま登録するわけではありません（後ほど説明します）
また、それらgitオブジェクトを登録する場所を**オブジェクトデータベース**といいます。

#### gitオブジェクト
gitが扱うモノのことをgitオブジェクトといいます。
gitオブジェクトは以下の4つです。

##### blob（big large object）
ファイルです。
ソースコート、画像ファイル、バイナリファイルなど、あらゆるファイルをgitはblobとして扱います。

##### tree
ディレクトリです。
ただ、ディレクトリを指すのみでなく、コミットするときの単位でもあります。

##### commit
コミットです。
コミットするtree、作成者、コミット者、コミットメッセージを保持します

##### tag
タグです。
今回は触れません。

#### オブジェクトデータベース
オブジェクトデータベースとはgitオブジェクトのデータベースです（そのまま）
具体的にいうと、`git init`したあとに作成される`.git`ディレクトリ内の`objects`ディレクトリがオブジェクトデータベースです。

では先ほどつらつらとあげた、配管コマンドを使ってファイルをコミットする様子を見ていきましょう。

## 配管コマンドを使ってコミットしてみる

```
$ mkdir /git-test && cd /git-test
### ローカルリポジトリを作成
$ git init
Initialized empty Git repository in /git-test/.git/
### .git/objects内のファイルを検索
$ find .git/objects -type f
```

適当な場所に実験用のローカルリポジトリを作成しておいてください。
リポジトリ作成直後なのでひとつもファイルが登録されていませんね。

#### ファイルをオブジェクトデータベースに登録する

```
### 1.ファイル作って
$ echo 'aaa' > aaa.txt

### 2.blobとして登録して
$ git hash-object -w aaa.txt
72943a16fb2c8f38f9dde202b7a70ccc19c52f34
```

`git hash-object` はgitオブジェクトの**オブジェクトID**を返します。
オブジェクトIDとは、gitオブジェクトのSHA1ハッシュ値です。gitが扱うリビジョン番号とかと同じノリです。
オプションに`-w`を指定すると、対象ファイルをgitオブジェクト化したのち、オブジェクトデータベースに登録します。
このコマンドにより、gitはファイルを**gitオブジェクト化**します。
**gitオブジェクト**は、*gitオブジェクトの種類*（ファイルの場合は*blob*）と*ファイルサイズ*からなる**ヘッダ**と、**ファイルの内容**を連結したもので、そのSHA1ハッシュ値が**gitオブジェクトID**です。また、実際にオブジェクトデータベースに登録されるときは圧縮されます。
`.git/objects`の内容をリスト表示してみるには[こちら](https://qiita.com/atchy/items/f3149c250b2df2855bf6)をお試しください。
また、このあたりの詳細については[オブジェクトストレージ](https://git-scm.com/book/ja/v2/Git%E3%81%AE%E5%86%85%E5%81%B4-Git%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88#_%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%82%B9%E3%83%88%E3%83%AC%E3%83%BC%E3%82%B8)が詳しいです。

それでは、現時点でオブジェクトデータベースの中身を覗いてみましょう。

```
$ find .git/objects -type f
.git/objects/72/943a16fb2c8f38f9dde202b7a70ccc19c52f34
```

ファイルがひとつ作られていますね。
gitはgitオブジェクトIDの先頭2文字をディレクトリ名として、残りの38文字をファイル名として、`git/objects`以下に配置します。
今の状態はファイルをオブジェクトデータベースに登録しただけです。

#### インデックスに登録する

```
### 3.インデックスに追加して
$ git update-index --add --cacheinfo 100644 72943a16fb2c8f38f9dde202b7a70ccc19c52f34 aaa.txt 
```

さっきgitオブジェクト化したときに返ったSHA1ハッシュ値をパラメータとして、リポジトリのインデックスに登録します。
新規にインデックスに登録するだけなら`git update-index --add aaa.txt`でもいいようです。
インデックスを確認してみましょう。

```
$ git status
On branch master

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
	new file:   aaa.txt
```

ステージングされてますね。

#### コミットする

```
### 4.treeを作って
$ git write-tree
a760aa994bbdd615f7ecb442be6bab636e11eba6

### 5.commitする
$ git commit-tree -m "add aaa.txt" a760aa994bbdd615f7ecb442be6bab636e11eba6
1e87843b08cdcd96216ab42ce56d6e0109c53427

### 6.でもってmasterブランチの参照を更新する
git update-ref refs/heads/master 1e87843b08cdcd96216ab42ce56d6e0109c53427
```

まずは`git write-tree`で、現在のインデックスをtree化します。
返るのはtreeのオブジェクトIDです。
オブジェクトデータベースの中身を覗いてみます。

```
$ find .git/objects -type f
.git/objects/72/943a16fb2c8f38f9dde202b7a70ccc19c52f34
.git/objects/a7/60aa994bbdd615f7ecb442be6bab636e11eba6
```
さっきの追加したファイルに加え、生成されたtreeが登録されていますね。
さて、それではいよいよ、`git commit-tree`コマンドで、このtreeをコミットします。
このコマンドにはコミットメッセージとtreeのオブジェクトIDをパラメータとして渡します。
返る値は**コミットオブジェクト**のオブジェクトIDです。
コミットオブジェクトはタイムスタンプを含むので、生成したタイミングによりオブジェクトIDが異なります。
最後に、`git update-ref`コマンドにより、このコミットオブジェクトのオブジェクトIDを`master`ブランチとして登録します。
今回は最初のコミットなので指定しませんでしたが、コミットオブジェクトには親であるオブジェクトを指定することができます。これにより、コミットの歴史が表現されるわけですね。
たとえば、このリポジトリに`bbb.txt`を追加してそのコミットオブジェクトを追加するときのことを考えてみます。

```
$ echo 'bbb' > bbb.txt
$ git hash-object -w bbb.txt
f761ec192d9f0dca3329044b96ebdb12839dbff6
$ git update-index --add --cacheinfo 100644 f761ec192d9f0dca3329044b96ebdb12839dbff6 bbb.txt
$ git write-tree
75d27669a0c4e9dd702c71c6ac3307d533493ba5
### 親となるコミットオブジェクトIDを指定してコミットする
$ git commit-tree -m "add bbb.txt" -p 1e87843b08cdcd96216ab42ce56d6e0109c53427 75d27669a0c4e9dd702c71c6ac3307d533493ba5
de5c2b4cf1ce3519102b9cc5a606c3089af02dcc
### 参照を更新
git update-ref refs/heads/master de5c2b4cf1ce3519102b9cc5a606c3089af02dcc  
```

`git commit-tree`コマンドの`-p`オプションで親コミットオブジェクトIDを指定します。
`git log`で今の状況を確認してみましょう。

```
commit de5c2b4cf1ce3519102b9cc5a606c3089af02dcc (HEAD -> master)
Author: atchy <atchy@example.com>
Date:   Sat Jan 16 19:07:21 2021 +0900

    add bbb.txt

commit 1e87843b08cdcd96216ab42ce56d6e0109c53427
Author: atchy <atchy@example.com>
Date:   Sat Jan 16 18:59:56 2021 +0900

    add aaa.txt
```

ちゃんと歴史が刻まれていますね！

## もっとくわしく知りたいあなたへ

[pro git](https://git-scm.com/book/ja/v2)をじっくり読むのもいいですが、
[こちら](https://qiita.com/sonatard/items/9c519611a154e8e35319)を利用すると、目からウロコがボトボト落ちます。ジョークソフトと表現されていますが、体感で理解できるという意味では冗談抜きかと思いますｗ

## 次回予告
今回はgitがどのようにファイルを扱い、コミットの歴史を積み上げていくか、にフォーカスしてみました。
次回はリモートリポジトリまわりについて、お伝えできればと考えています！

### 続編書きました

[いいかげんgitのことをちゃんと知りたいあなたへ（Gitリポジトリ編）](https://qiita.com/atchy/items/b3cf5cde7263ec62e9b9)

リモートリポジトリ、というよりリポジトリのことについての記事になりました。