firisu the shooter

プログラミング、Web開発について

縛りプレイで覚える Git ~git add 編~

序論

次に git add を実装しよう。

前回は git init を実装したが、通常のコマンドしか使わなかったので詰まらなかった。 今回からはちゃんと Plumbing コマンドを使っていくので少しは楽しくなるだろう。

git add って何するの?

git add はインデックスを作成するコマンドである。

インデックスとはワーキングツリーとリポジトリの中間に位置する存在で、 コミット対象のファイルを格納するために使われる。

今回はこのインデックスを手動で作成することを目標とする。

まずは動作を調べる

試しに適当なファイルを git add してみよう。

$ git add dom-init
$ tree .git
.git
├── HEAD
├── branches
├── config
├── description
├── hooks
├── index
├── info
│   └── exclude
├── objects
│   ├── 37
│   │   └── 90bbebb9faa6ed2aa818ee9ec50584237e7bb2
│   ├── info
│   └── pack
└── refs
    ├── heads
    └── tags

10 directories, 6 files

index37/90bbebb9faa6ed2aa818ee9ec50584237e7bb2 というファイルが追加された。

index ってなんぞ?

その名の通り、インデックスを格納するファイルだと思われる。

中身はこんな感じのバイナリファイルだ。

1
2
3
4
5
6
7
00000000: 44 49 52 43 00 00 00 02 00 00 00 01 51 30 10 50 |   DIRC........Q0.P
00000010: 2c 24 32 26 51 30 10 50 2c 05 b3 2f 00 00 fd 00 |   ,$2&Q0.P,../....
00000020: 00 1c 66 43 00 00 81 ed 00 00 01 f4 00 00 01 f4 |   ..fC............
00000030: 00 00 01 fe 37 90 bb eb b9 fa a6 ed 2a a8 18 ee |   ....7.......*...
00000040: 9e c5 05 84 23 7e 7b b2 00 08 64 6f 6d 2d 69 6e |   ....#~{...dom-in
00000050: 69 74 00 00 1d bb d2 6c cd 0e a3 3e 1d 52 fa a0 |   it.....l...>.R..
00000060: 4f e9 9b 3f d7 a2 e4 77                         |   O..?...w

これでは何が書いてあるかわからないので、いよいよ Plumbing コマンドの出番だ。

ここではインデックスの中身を確認できる git ls-files を使う。

$ git ls-files --stage
100755 3790bbebb9faa6ed2aa818ee9ec50584237e7bb2 0       dom-init

なるほどなるほど。

パーミッション 755 の dom-init というファイルのハッシュは 3790… で 登録されている、ということだろう。

3970… ってなんぞ?

こいつもバイナリファイルである。

上記の index とは異なり、いわゆるオブジェクトファイルと呼ばれるバイナリである。

よってオブジェクトファイルの中身を確認できる git cat-file を使う。

$ git cat-file -p 3790
#!/bin/sh

# .git/ を消去
rm -rf .git

# .git/ を作成
mkdir .git
cd .git

# HEAD を作成
echo "ref: refs/heads/master" > HEAD

# ディレクトリをまとめて作成
mkdir -p branches hooks info objects/info objects/pack refs/heads refs/tags

# config を作成
echo << EOT > config
[core]↲
»---repositoryformatversion = 0↲
»---filemode = true↲
»---bare = false↲
»---logallrefupdates = true
EOT

# description は空でOK
touch description

# info/exclude も空でOK
touch info/exclude

コマンド実行後にいきなり dom-init の中身が表示されてしまった。

恐らくこのオブジェクトファイルは、単体のファイルを圧縮して格納しているだけなのだろう。

実現手順

なんとなく何をやっているか分かったので、git add を実現する手順を考えてみよう。

  1. .git/index にファイルの情報を追加
  2. .git/objects/ にオブジェクトファイルを追加

以上。

実装

前回に引き続き、今回もシェルスクリプトで実装する。

dom-add
1
2
3
4
#!/bin/sh

# インデックスに指定されたファイルを追加する
git update-index --add $1

み、短い・・・。

git update-index がインデックス作成に関わるタスクを全部やってくれるので、1行で済んでしまった。

多分 git addgit update-index をユーザフレンドリに使えるようにしただけのコマンドなんだろう。

意外と Plumbing コマンドは高機能のようだ。

結果

$ git status
# On branch master
#
# Initial commit
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       dom-add
#       dom-init
nothing added to commit but untracked files present (use "git add" to track)

$ ./dom-add dom-init

$ git status
# On branch master
#
# Initial commit
#
# Changes to be committed:
#   (use "git rm --cached <file>..." to unstage)
#
#       new file:   dom-init
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       dom-add

無事、新規ファイルとしてインデックスに格納されたようだ。

次 > git commit 編: コミットしてみる

Comments