マージコミットと通常のコミットのただ一つの違い
タイトルは先日、会社の先輩に突然出題されたもの。その時は答えられず教えてもらった。
コミットオブジェクトとは
Gitによって記録されるのは、SHA-1でハッシュ化されたものにすぎない。Gitの内部では、ファイルのコンテンツをハッシュ化したblobオブジェクトと、それを指すことでディレクトリ名やファイル名を記録できるtreeオブジェクトが生成される。このtreeオブジェクトを指すものがコミットオブジェクトであり、コミットオブジェクトを指定すればtreeオブジェクトがわかり、そこを見ることでblobを取り出せる。
しかし、それだけだとコミットの順番がわからない。そこでコミットオブジェクトは、生成時に直前のコミットオブジェクトのSHA-1ハッシュ値を指定する。こうすることで、あるコミットから親のコミットをたどることができ、コミットの履歴がわかるようになる。
コミットオブジェクトを見るには、 git cat-file -p <コミットオブジェクトのSHA-1値>
と指定すればよい。
$ git cat-file -p 5c8c49c tree 00906ce530257852785a7149b8df0c6a75a48ad2 parent cdba5fbd79ee3a484cfd45a97cfbbe90f77c1e6e author Udomomo <udomomo@example.com> 1563115462 +0900 committer Udomomo <udomomo@example.com> 1563115462 +0900 add bar.txt
このように、parentとして前のコミットが表示されるのがわかる。
マージコミットとは
これをふまえて考えると、マージコミットもtreeオブジェクトを指すという点では同じである。しかし、違う点が1つだけある。以下はmasterブランチにfeatブランチをマージしたときのコミットを取り出したものである。
git cat-file -p 4afea17 tree 76ed3224820042a02d6af1886a2fb3259f1d8cb5 parent f8d2537e168efec54ce288769e1f4ca460258330 parent 9b0e505f0fd9a9b540f9783ebcbef9f2be3bddb4 author Udomomo <udomomo@example.com> 1563115699 +0900 committer Udomomo <udomomo@example.com> 1563115699 +0900 Merge branch 'feat'
parentが2つあるのがわかる。それぞれ、masterブランチとfeatブランチの以前のコミットを示している。
これがマージコミットと通常のコミットの違いである。逆に言えば違いはこれしかない。Gitの内部では両方ともコミットオブジェクトであり、ブランチというのは利用者向けにわかりやすく作られた概念にすぎない。
もう1つ異なる性質のコミット
マージコミットと通常のコミットは親の数が違うが、この点でいうともう1つ異なるコミットがある。それはinitial commitである。initial commitを取ってきてみると以下のようになる。
$ git cat-file -p cdba5fb tree 09a13b897d3d0f528d487c704da540cb952d7606 author Udomomo <udomomo@example.com> 1563115391 +0900 committer Udomomo <udomomo@example.com> 1563115391 +0900 first commit
parentが1つもない。最初のコミットだけは例外的にparentを持たないことがわかる。