🏛️

Gitは最初1244行しかなかった

2022/12/14に公開約6,000字

概要

Junio C Hamanoさんに興味を持って調べていると、Linusさんが書いたGitの初版は1244行ということが分かりました。Gitの初版について、軽く行数の確認とビルドチャレンジをして、あまり調べずに動かしながら機能を推測してみました。

はじめに

Highlights from Git 2.39 の冒頭で登場するcommit数が一番多い方「Junio C Hamano」さんを知らなかったので調べてみました。

gihyoのインタビュー記事が面白かったです。Junio C HamanoさんはGitのメンテナで、LinusさんからGitのメンテナを引き継いだすごい方だということを知りました。

このgihyoのインタビュー記事の中で「MLで流れてきたGitのコード行数は1244行だった」というところが気になりました。調べてみると、2020年にTwitterでRui Ueyamaさんへのリプライでも「最初の版は1244行しかなかったんだけどね。」と述べられています。

Gitは現在も活発に開発が続けられている巨大ソフトウェアで、私も日々お世話になっています。どんな巨大ソフトウェアも最初は短い行数からスタートするということは頭で分かってもなかなか直観的にしっくりこないので、興味が湧いてきました。

それでは git clone https://github.com/git/git.git して調べていきます。

目次

  • 環境
  • 最初のコミットを調べる
  • 最初のコミットの行数を調べる
  • 初版のGitをビルドする
  • 実際に動かしてみる
  • 推測
  • 今後

環境

今回は以下の環境で調べました。

$  lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 22.04.1 LTS
Release:        22.04
Codename:       jammy

$ uname -a
Linux <hostname> 5.15.0-56-generic #62-Ubuntu SMP Tue Nov 22 19:54:14 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux

$ gcc --version
gcc (Ubuntu 11.3.0-1ubuntu1~22.04) 11.3.0
...

最初のコミットを調べる

最初のコミットを調べます。
今回はMLを見にいくことはせず、GitHubのミラーの最初を見にいくことにしました。また、 README は最初にあるだろうし更新も少ないだろうと考えて、GitHubのwebページの README.md のhistoryを眺めていきます。

GitのREADME.mdのHistory

一番下の「Renamed from README (Browse History)」をクリックしてさらに辿っていきます。

どうやらそれらしいものを見つけます。

最初のコミットと思われるもの

最初のコミットへのリンク: https://github.com/git/git/tree/e83c5163316f89bfbde7d9ab23ca2e25604af290

(関係ないけど最初のコミットが巡礼スポットになっていて、関係ないコメントを残す人がたくさんいてうむむという気持ちに)

commit hashが分かったので、手元にcloneしてきたリポジトリをcheckoutします。

$ git checkout e83c5163316f89bfbde7d9ab23ca2e25604af290

最初のコミットの行数を調べる

行数を計測します。

$ find . -type d -name '.git' -prune -o -type f -print | xargs wc -l
   43 ./read-tree.c
   81 ./show-diff.c
   93 ./cache.h
  248 ./update-cache.c
  259 ./read-cache.c
   51 ./init-db.c
  172 ./commit-tree.c
  168 ./README
   23 ./cat-file.c
   66 ./write-tree.c
   40 ./Makefile
 1244 total

うおお!本当に1244行です!感動!

どうやらこれはOKっぽいので、MLを調べずにこれが初版のGitということにしてしまいます。
今度はこの初版のGitをビルドしてみようと思います。

ちなみに現在のGitの行数は...

$ find . -type d -name '.git' -prune -o -type f -print | xargs wc -l
...
 1397709 total

139万行!?すごいですね

初版のGitをビルドする

Makefile があるのでmakeしてみます。...うまくいきません。ビルドバトル開始です。

一時間ほど格闘して、以下のようにしてビルドできました。(もし間違っているところあればアドバイスいただけると嬉しいです!)

  • git diff -- Makefile の結果
diff --git a/Makefile b/Makefile
index a6bba79ba1..6bdc90fdbd 100644
--- a/Makefile
+++ b/Makefile
@@ -1,14 +1,14 @@
-CFLAGS=-g
+CFLAGS=-g -std=c99 -fcommon
 CC=gcc
 
 PROG=update-cache show-diff init-db write-tree read-tree commit-tree cat-file
 
 all: $(PROG)
 
-install: $(PROG)
-       install $(PROG) $(HOME)/bin/
+# install: $(PROG)
+#      install $(PROG) $(HOME)/bin/
 
-LIBS= -lssl
+LIBS= -lssl -lz -lcrypto
 
 init-db: init-db.o
  • git diff -- show-diff.c の結果
diff --git a/show-diff.c b/show-diff.c
index b8522886a1..bfada78a58 100644
--- a/show-diff.c
+++ b/show-diff.c
@@ -11,11 +11,11 @@ static int match_stat(struct cache_entry *ce, struct stat *st)
 {
        unsigned int changed = 0;
 
-       if (ce->mtime.sec  != (unsigned int)st->st_mtim.tv_sec ||
-           ce->mtime.nsec != (unsigned int)st->st_mtim.tv_nsec)
+       if (ce->mtime.sec  != (unsigned int)st->st_mtime ||
+           ce->mtime.nsec != (unsigned int)st->st_mtimensec)
                changed |= MTIME_CHANGED;
-       if (ce->ctime.sec  != (unsigned int)st->st_ctim.tv_sec ||
-           ce->ctime.nsec != (unsigned int)st->st_ctim.tv_nsec)
+       if (ce->ctime.sec  != (unsigned int)st->st_ctime ||
+           ce->ctime.nsec != (unsigned int)st->st_ctimensec)
                changed |= CTIME_CHANGED;
        if (ce->st_uid != (unsigned int)st->st_uid ||
            ce->st_gid != (unsigned int)st->st_gid)
  • git diff -- update-cache.c の結果
diff --git a/update-cache.c b/update-cache.c
index 5085a5cb53..a6b304ee07 100644
--- a/update-cache.c
+++ b/update-cache.c
@@ -139,9 +139,9 @@ static int add_file_to_cache(char *path)
        memset(ce, 0, size);
        memcpy(ce->name, path, namelen);
        ce->ctime.sec = st.st_ctime;
-       ce->ctime.nsec = st.st_ctim.tv_nsec;
+       ce->ctime.nsec = st.st_ctimensec;
        ce->mtime.sec = st.st_mtime;
-       ce->mtime.nsec = st.st_mtim.tv_nsec;
+       ce->mtime.nsec = st.st_mtimensec;
        ce->st_dev = st.st_dev;
        ce->st_ino = st.st_ino;
        ce->st_mode = st.st_mode;

Warningはたくさん出ますが、一応コンパイルできたので動かしてみます。

実際に動かしてみる

まずはあまりコードを読まずにとりあえず動かしてみることにします。

update-cache, show-diff, init-db, write-tree, read-tree, commit-tree, cat-file の7つのコマンドがあります。

色々試した結果、以下のように使えそうです。

  • init-dbgit init 相当?
    • カレントディレクトリに .dircache という .git みたいなフォルダが作られて、その中にobjectsがあります。
  • 適当にファイルを作り、 update-cache して write-tree すると、hash値が返ってくる
  • write-tree の結果のhash値を渡して read-tree すると、見慣れた数字100664が!
$ ../read-tree 506a71adf66631615fb1735bc039d237b65a2994
100664 hoge.txt (cf31c276cff9de473ffbedf78ce5b464ef240a7d)
  • さらに、上の read-tree で返ってきた値を commit-tree に渡すとcommitっぽいことができる!
$ echo 'changelog #1' | ../commit-tree cf31c276cff9de473ffbedf78ce5b464ef240a7d
Committing initial tree cf31c276cff9de473ffbedf78ce5b464ef240a7d
db11de0f8cb21c126c3a701a20aa6dd040367637

推測

  • commitの概念、git objectの概念は最初から存在していた
  • ステージングの概念はまだっぽい?(gihyoのインタビューでもそのような話があったような気がします)

今後

  • README を読みたいです。
  • せっかく1244行しかないので、コードを読み解いていきたいと思います。
  • gihyoのインタビューでstageの概念がLinusさんから提案された、みたいな話があったので、commit logをたどりながら調べてみたいです。

Discussion

ログインするとコメントできます