Gitのソースコードには、gitコマンドをビルドするためのMakefileが含まれている。Gitコマンドのレイアウトを知らなければ理解の難しいものがいくつかあったので調べた。
Gitコマンドのレイアウト
$PATHに含まれる場所(ほとんどは /usr/bin)にGit関連のコマンドが置かれている。
$ ls -l /usr/bin/git* # コマンド出力は一部加工した git git-cvsserver git-receive-pack git-shell git-upload-archive git-upload-pack
$PATHの他に、Gitが参照するgit-coreというディレクトリがあって、git-coreには$PATHにあるもの全てと、git-addのようにgit-がプリフィックスとして付けられた実行ファイルが入っている。git-coreの場所はgit --exec-path
で調べられる。これらのファイルはGitのサブコマンドとして実行できる。実際のところ、ほとんどのファイルはgitにシンボリックリンクを張ったものだけど、本体に組み込まれない一部のサブコマンドは、実行ファイルがそのまま配置されている。
$ git --exec-path /Library/Developer/CommandLineTools/usr/libexec/git-core $ ls -l $(git --exec-path) # コマンド出力は一部加工した git -> ../../bin/git git-add -> ../../bin/git git-add--interactive git-am -> ../../bin/git git-annotate -> ../../bin/git git-apply -> ../../bin/git git-archive -> ../../bin/git git-bisect git-bisect--helper -> ../../bin/git ...
余談だけどgitコマンドは$PATHとgit-coreの2箇所に置かれている。同じものだけど、片方が欠けると動かない。
ソースコードレイアウト
Gitのビルドでは、まずlibgit.a、xdiff/lib.aなどライブラリをビルドして、Gitの組み込みコマンドとそれらライブラリをリンクするという順番で行う*1。
Gitリポジトリ直下にある*.cファイルの一部、例えばdir.cやdiff.cなどはlibgit.aとしてビルドされる。このライブラリは、Git本体や、Git本体から独立した単体のサブコマンドとリンクする。リポジトリ直下にあるそれ以外の*.cファイルは、Git本体のソースコードであったり、単体サブコマンドのソースコードであったりする。例えばhttp-fetch.cはgit fetch
の実装だが、これはGit本体とは別の単体コマンドとしてビルドされる。
builtin/以下にはGit本体に組み込むサブコマンドのソースコードが置かれている。ほとんどの組み込みサブコマンドはbuiltin/以下のソースコードと1 : 1で対応している。例えばbuiltin/add.cはgit add
コマンドそのものであり、cmd_add(argc, argv, prefix)
という関数がエントリポイントとなる。これらのファイルは最終的にGit本体に組み込まれて、git-coreにgit-addとしてシンボリックリンクが作られる。組み込みサブコマンドのシンボリックリンクはどれもGit本体を参照しており、Gitは実行時のファイル名によって、どのサブコマンドを実行するかを決定する。ただし、少数ではあるがbuiltin以下に対応するファイルを持たない組み込みサブコマンドも存在する。例えばgit cherry-pick
はbuiltin/にファイルを持たないが、git-cherry-pickという名前のシンボリックリンクは用意される。
残りのディレクトリはそれぞれの目的で用意されている。xdiff/はxdiff/lib.aのソースコード、compat/は各プラットフォームの互換性問題を解消するためのソースコードが格納されている。またDocumentation/には詳細なドキュメントが用意されているので眺めてみると面白いと思う。
Makefileの変数
Makefileでは上で説明したファイルをどう扱っているか。Makefile変数を調べた。
LIB_OBJS
- libgit.aを作るためのファイルリスト
BUILTIN_OBJS
- 組み込みサブコマンドのファイルリスト
- Git本体はこれとgit.cと各種ライブラリで作られる
BUILT_INS
- Git本体の組み込みサブコマンドリスト
- BUILTIN_OBJSと、
git cherry-pick
などファイルを持たないもの全て
PROGRAM_OBJS
- Git本体に含まれないサブコマンドのリスト
- git-プリフィックスは省略される
PROGRAMS
- PROGRAM_OBJSにgit-プリフィックスを加えたもの
GITLIBS
- Git本体や単体サブコマンドとリンクするファイル
- libgit.a、xdiff/lib.a、common-main.o
BINDIR_PROGRAMS_NEED_X, BINDIR_PROGRAM_NO_X
- 直接$PATHに置かれる単体コマンド
- Windowsの場合に.exeを付けるかどうかで変数を使い分ける