はじめに
前々回では最初のgit
コマンドの基礎的な動作を図で確認し、前回はディレクトリや複数のファイルを含んだコミット動作とコミット内容を復元する処理を確認した。
今回は最初のgit
コマンドで複数のコミットの管理方法について確認する。
最初のgitコマンドを用意する
最初のgit
コマンドをビルドし、`$HOME/bin-git にコピーして、PATHを通す。
$ ls $HOME/bin-git
$ cat-file commit-tree init-db read-tree show-diff update-cache write-tree
$ export PATH=$PATH:$HOME/bin-git
$ which cat-file
/$HOME/bin-git/cat-file
1stコミット
この章の内容は前回行った作業と同等である。すでに作業を行っている場合には2ndコミットへ進む。
ファイルの作成
$ echo "Hello, World" > README.md
mkdir src
$ echo 'echo "Hello"' > src/hello.sh
$ echo 'echo "World"' > src/world.sh
$ cat README.md ; cat src/hello.sh ; cat src/world.sh
Hello, World
echo "Hello"
echo "World"
コミット
作成したファイルを登録して最初のコミットを行う。
$ update-cache README.md src/hello.sh src/world.sh
$ write-tree
937b5d80b2b3b9de8cf3241ab8dd97892f0ad3cf
$ echo "1st hello world" | commit-tree 937b5d80b2b3b9de8cf3241ab8dd97892f0ad3cf
Committing initial tree 937b5d80b2b3b9de8cf3241ab8dd97892f0ad3cf
d2970ed409efe53434b0bff727cc5bc0b56b4958
作成したcommitオブジェクト
はSHA1ハッシュd2970ed409efe53434b0bff727cc5bc0b56b4958
で管理される。
リポジトリの状態
1stコミット直後のリポジトリの状態
ここまでは前回と同様である。
2ndコミット
ファイルを修正し2ndコミットを行いリポジトリ内部を確認する
ファイルアップデート
src/world.sh
の内容をアップデートする。
$ echo 'echo "World, v2"' > src/world.sh
$ cat src/world.sh
echo "World, v2"
コミット
アップデートしたsrc/world.sh
を登録し、write-tree
コマンドとcommit-tree
コマンドで2ndコミットを行う。
$ update-cache src/world.sh
$ write-tree
c7cfa281da8793c504aea1c515d2e9b12522cbf4
$ echo "2st hello world" | commit-tree c7cfa281da8793c504aea1c515d2e9b12522cbf4
Committing initial tree c7cfa281da8793c504aea1c515d2e9b12522cbf4
f6462e93a04c17443d782550cb20b06a354a4815
update-cache
コマンドにはsrc/world.sh
しか引数として渡していないが、index
ファイルには元のREADME.md
, src/hello.sh
, そして修正した後のsrc/world.sh
のblobオブジェクト
へのSHA1ハッシュが保存されている。つまり、一度index
ファイルに登録したファイルは今後の全てのコミットの対象になるファイルだとして取り扱われる。
この動作は最新のgit
の挙動と同一であると言えるだろう。
リポジトリの状態
2ndコミットの対象としたtreeオブジェクト
の内容を確認する。
README.md
ファイルとsrc/hello.sh
ファイルに対しては、1stコミットのtreeオブジェクト
から指していた既存のblobオブジェクト
を指しており、src/world.sh
ファイルについては、さきほどupdate-cashe
コマンドで新規に作成したblobオブジェクト
を指している。
read-tree c7cfa281da8793c504aea1c515d2e9b12522cbf4
100664 README.md (5f60b55715af0273fd592428a179c59299a0987f)
100664 src/hello.sh (c1e79ffabce97edb43548eae29e3b4a1b6fe9071)
100600 src/world.sh (616ca6f7edce8c4f755aa8344cee6383f9d780d5)
つまり、変更があったファイルについてのみ指すblobオブジェクト
を変更している。
そのblobオブジェクト
は変更後のファイルをzlibで圧縮したファイルである。
非常にシンプルな仕組みでコミットを実現していることがわかる。
ファイルの変更があった場合にその変更箇所を保存するようなCVS
やSubversion
といった伝統的なSCM(Source Control Management)とは異なり、変更のあったファイルを そのまま 保存する。差分が必要な場合は動的に差分ファイルを作成する。このような挙動をとるため、git
のコミットはスナップショットであり差分ではない、と言われたりする。
git
からSCMに触れた人には当たり前の話なのだが、古いSCMに慣れたエンジニアからするとコミットはスナップショットと言われるとその仕組を理解したくなるくらいには衝撃的である。