git(1)の最初のコミットをビルドして使ってみた

gitが10周年ということで、gitの最初のコミットのリビジョンのソースコードをビルドして動かしてみた。最初のコミットとはつまり、最初にgitがセルフホスティングされたリビジョンということになる。

参考文献: [本の虫: gitの10周年を記念したLinus Torvalsへのインタビューの翻訳](http://cpplover.blogspot.jp/2015/04/git10linus-torvals.html)

https://github.com/git/git/tree/e83c5163316f89bfbde7d9ab23ca2e25604af290

```
commit e83c5163316f89bfbde7d9ab23ca2e25604af290
Author: Linus Torvalds <torvalds@ppc970.osdl.org>
Date:   Thu Apr 7 15:13:13 2005 -0700

    Initial revision of "git", the information manager from hell
```

わずか11ファイル、READMEを含めて1200行あまりのコミットだから、ビルドくらいはできるはず。

OSX 10.10 + clangでビルドしようとすると若干エラーになるので、いろいろコマンドオプションで与える。

とりあえず、一箇所だけどうにもならなそうな箇所があったのでパッチを当てた。

```diff
diff --git a/init-db.c b/init-db.c
index 25dc13f..a2083f1 100644
--- a/init-db.c
+++ b/init-db.c
@@ -20,7 +20,7 @@ int main(int argc, char **argv)
 	if (sha1_dir) {
 		struct stat st;
 		if (!stat(sha1_dir, &st) < 0 && S_ISDIR(st.st_mode))
-			return;
+			return 0;
 		fprintf(stderr, "DB_ENVIRONMENT set to bad directory %s: ", sha1_dir);
 	}
```

また、実際に動かしてみた結果SEGVしたので調べたところ、以下の変更が必要だった。

```diff
diff --git a/read-cache.c b/read-cache.c
index c924a6e..35862e0 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -228,7 +228,7 @@ int read_cache(void)
 		map = NULL;
 		size = st.st_size;
 		errno = EINVAL;
-		if (size > sizeof(struct cache_header))
+		//if (size > sizeof(struct cache_header))
 			map = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
 	}
 	close(fd);
```

その後以下のコマンドでビルドできるはず。

```shell-session
make all CC=clang CFLAGS='-g -Dst_mtim=st_mtimespec -Dst_ctim=st_ctimespec' LIBS='-lssl -lz -lcrypto'
```

これでいくつかのコマンドができるが、現在のgitとかなり違っているのでよくわからない…。

とりあえず、`git init` は `./init-db` っぽい。

```shell-session
$ ./init-db
defaulting to private storage area
```

`git add <files>` は `./update-cache <files>` だろうか。

```shell-session
$ ./update-cache *.c
```

それでコミット…のまえにtreeをつくらないといけない雰囲気。

```shell-session
$ ./write-tree
810215aba4330d399c1c91d3f3ae601dcf1ea344
```

この状態で `./read-tree <sha1>` するとちょっとgitっぽい出力を得られる。

```shell-session
$ ./read-tree 810215aba4330d399c1c91d3f3ae601dcf1ea344
100644 cat-file.c (fd690acc02ef9c06d7c4c3541f69b10ca4b4f8c9)
100644 commit-tree.c (a4a8c3d9ef0c4cc6c82b96b5d1a91ac6d3bed466)
100644 init-db.c (34595e65f9a9105ad1dfdaa3ace6604650c8f043)
100644 read-cache.c (b26b6052a8d100748923ef2a4241e6ef5be91616)
100644 read-tree.c (ec0f167a6a505659e5af6911c97f465506534c34)
100644 show-diff.c (00a29c403e751c2a2a61eb24fa2249c8956d1c80)
100644 update-cache.c (00e616ab887879cf274af786eda1d4efd0430559)
100644 write-tree.c (7abeeba116b2b251c12ae32c7b38cb048199b574)
```

コミットは `./commit-tree` かな。

```shell-session
$ echo 'Hello, git!' | ./commit-tree 810215aba4330d399c1c91d3f3ae601dcf1ea344
Committing initial tree 810215aba4330d399c1c91d3f3ae601dcf1ea344
db2204d8c4cdfadd2ff03956e17f832393660d86
```

`./cat-file` はSHA1にもとづいてファイルの中身を見る‥のではなく、SHA1の種類を見るだけだった。

```shell-session
$ ./cat-file a4a8c3d9ef0c4cc6c82b96b5d1a91ac6d3bed466
temp_git_file_m4w7JH: blob

$ ./cat-file 810215aba4330d399c1c91d3f3ae601dcf1ea344
temp_git_file_DYHcyk: tree

$ ./cat-file db2204d8c4cdfadd2ff03956e17f832393660d86
temp_git_file_U1t0y0: commit
```

さらにファイルを編集して `./show-diff` すると、 `git diff` 相当のものが得られる。

```shell-session
# cat-file.cに一行空白をいれた
$ ./show-diff
cat-file.c:  fd690acc02ef9c06d7c4c3541f69b10ca4b4f8c9
--- -	2015-04-08 22:47:47.000000000 +0900
+++ cat-file.c	2015-04-08 22:47:45.000000000 +0900
@@ -19,5 +19,6 @@
 		usage("unable to create tempfile");
 	if (write(fd, buf, size) != size)
 		strcpy(type, "bad");
-	printf("%s: %s\n", template, type);
+
+    printf("%s: %s\n", template, type);
 }
commit-tree.c: ok
init-db.c: ok
read-cache.c: ok
read-tree.c: ok
show-diff.c: ok
update-cache.c: ok
write-tree.c: ok
```

これでコマンドひと通り網羅した。 `git log` 相当のものがないのでコミット履歴を追うことはできないものの、最初のコミット当初からgit-add / git-commitの区別があり、git-logよりも先にgit-diffがあったのだということがわかる。

たまにはこんな考古学も悪くない。