さて、前回は、ブランチを使って状態の「復元」と「分岐」を行うことができるのを見てみましたね。そして、今回はマージについて見てみましょう。
merge というのは「複数のものをがっちゃんこする」という意味ですね。では、ここで言う「複数のもの」とはなんでしょうか? これは簡単で、複数のコミットをがっちゃんこするのです。
前回、master と my_first_branch が、共通の親を持ち、異なる変更履歴を持つコミットオブジェクトを指していたことを思い出してください。このふたつのコミットを merge することで、これらを「がっちゃんこ」します。
何はともあれ、例を見てみましょう。
では、やってみましょう。まずは master ブランチを checkout します。
$ git checkout master
そして、master ブランチから、my_first_branch を merge します。
$ git merge my_first_branch
とすると、コミットのときとおなじくエディタが立ち上がります。
Merge branch 'my_first_branch'
と書かれているとおもうので、まあそのままエディタを保存、終了してみましょう。
Merge made by the 'recursive' strategy.
animals/mow.txt | 1 +
1 file changed, 1 insertion(+)
create mode 100644 animals/mow.txt
という表示が出てきたかと思います。読んで行きましょう。
「Merge made by the 'recursive' strategy.」 とありますね。「'recursive' strategy で merge されたよ」だそうです。'recursive' starategy ってなに……って感じですが、ひとまず今はそれは置いておきます。「なんらかの方法でマージされたんだな」と今は思っておいてください。
その次の行、「animals/mow.txt | 1 +」と表示されています。なんとなくわかるでしょうか。「animals/mow.txtというファイルに1行増えてるよ」くらいの感じですね。その下に表示されている内容も、もうわかりますね。「1ファイル変更があって、1行追加されてるよ」と、「animals/mow.txt ってファイルを作ったよ」ですね。たしかに、my_first_branch にはあるけれど master にはないファイルは、「"mow" と 1 行書かれた書かれた animals/mow.txt」というファイルでした。そのファイルを取り込むのだから、このような結果になるのは納得ですね。
ではここで、履歴がどうなっているのか見てみましょう。
$ git graph
* e175c44 (HEAD, master) 2013-05-05 Shinpei Maruyama Merge branch 'my_first_branch'
|\
| * bab8d49 (my_first_branch) 2013-05-05 Shinpei Maruyama 牛の鳴き声を追加
* | c3ef974 2013-05-05 Shinpei Maruyama 犬ファイルの中身も英語に合わせた
|/
* 5fe17c7 2013-05-04 Shinpei Maruyama 動物ファイルを animals ディレクトリに移動
* bb8f610 2013-05-04 Shinpei Maruyama ファイル名を英語に変更
* 8faaa1f 2013-05-04 Shinpei Maruyama 豚の鳴き声は要らないので削除
* 66c681b 2013-05-04 Shinpei Maruyama 犬と豚の鳴き声を追加
* 66346b5 2013-05-04 Shinpei Maruyama 猫の鳴き声を nyan から mew に変更
* 33028c1 2013-05-03 Shinpei Maruyama 猫の鳴き声を管理するファイルを作成
おお、なんかかっこよくなってる! ひとつひとつ確認して行きましょう。
まず、一番上の行に、あたらしいコミットが増えているのがわかりますね。「Merge branch 'my_first_branch'」というコミットです。これは、マージを行ったときに生まれる特殊なコミット、「マージコミット」と呼ばれるものです。今「特殊なコミット」と言いましたが、なにが特殊なのでしょうか? 再度コミットオブジェクトについて復習してみましょう。
コミットオブジェクトが保持している情報のことを覚えていますか?
- 「"このコミットの前の状態を表す親コミットオブジェクト" はどれか」という情報
- 「そのコミットがされた瞬間のファイルの状態」の情報
を持っているのでした。
それを頭に入れた上で、さきほどの git graph
の結果を再度眺めてみましょう。コミットの親子関係を示す線に注目してください。新しく出来たマージコミットが、親コミットをふたつ持っているのに気づきましたか?
ふつうは、コミットオブジェクトは親をひとつしかもちません。親コミットというのは、「以前どのような状態であったか」を示すものなので、当然ですね。親コミットがふたつあるというのは「以前の状態がふたつある」ということになってしまい、意味がわかりません。しかし、このマージコミットは、親をふたつ持っています。これが、「マージコミット」の特徴です。
では、「親をふたつもっている」というのがどういう状態なのか、考えてみましょう。
コミットには、「以前の状態を表すコミットはどれか」と、「変更を行ったあとの現在の状態」という情報が含まれているのでした。そして、新しいコミットオブジェクトを作ると、現在選択しているブランチの指しているコミットがこの「以前の状態」となるのでしたね。 さきほどわたしたちは、master ブランチを選択していました。そのため、今回のマージコミットにおける「以前の状態」というのは、 master ブランチが指していた状態のこととなります。これが、親コミットその 1 になります。これはいいですね。
さて、わたしたちはさきほど、mater ブランチから my_first_branch をマージしました。このときの「この瞬間のファイルの内容」というのはどのようなものでしょうか。普通のコミットならば、「作業ディレクトリで行った作業の結果を stage したもの」になりますが、今回はなにも stage していませんね。そのかわり、今回はmy_first_branch が保持しているファイルの内容を取り込んでいます。これを言い換えると、「今回のマージコミットで作り出されるファイルの状態」は、master が指していたコミットが持っていた状態と、my_first_branch が指しているコミットが持っていた状態の間の子みたいなもんですね。だから、このコミットはもうひとつ、 my_first_branch が指していたコミットを「親コミットその 2」として持つことになるわけです。
マージコミットは、このようにして、ふたつの親コミットの状態をがっちゃんした状態もつ存在として生まれることになるのです。
さて、これで「分岐」していたブランチが指すコミットが、マージされて「統合」されたことになります。たしかに分岐していた親子関係の線がひとつに統合されていますね。
こんな具合で、マージにより master ブランチに my_firstbranch で行われた変更を取り込むことができ、新しいコミット(マージコミット)が生まれました。ではブランチがどうなっているか見てみましょう。 git graph
の結果を再掲します。
$ git graph
* e175c44 (HEAD, master) 2013-05-05 Shinpei Maruyama Merge branch 'my_first_branch'
|\
| * bab8d49 (my_first_branch) 2013-05-05 Shinpei Maruyama 牛の鳴き声を追加
* | c3ef974 2013-05-05 Shinpei Maruyama 犬ファイルの中身も英語に合わせた
|/
* 5fe17c7 2013-05-04 Shinpei Maruyama 動物ファイルを animals ディレクトリに移動
* bb8f610 2013-05-04 Shinpei Maruyama ファイル名を英語に変更
* 8faaa1f 2013-05-04 Shinpei Maruyama 豚の鳴き声は要らないので削除
* 66c681b 2013-05-04 Shinpei Maruyama 犬と豚の鳴き声を追加
* 66346b5 2013-05-04 Shinpei Maruyama 猫の鳴き声を nyan から mew に変更
* 33028c1 2013-05-03 Shinpei Maruyama 猫の鳴き声を管理するファイルを作成
ふむ、master ブランチは新しいコミットを指すようになっています。一方、my_first_branch は依然として古いコミットを指したままです。
コミットを行うと、
- 選択されているブランチの指していたコミットを親にもつ新しいコミットが作られ
- ブランチはその新しいコミットを指すようになる
のでしたね、その通りの動きをしています。
さて、無事に my_first_branch が持っていた変更履歴の内容が master ブランチに統合されたため、もう my_first_branch は用済みです。要らないブランチは消しちゃいましょう。
$ git branch -d my_first_branch
こんな感じで消せます。 -d は delete の d です。ブランチ一覧を確認してみましょう。
$ git branch
* master
うん、消えてますね。念のため git graph
も確認。
$ git graph
* e175c44 (HEAD, master) 2013-05-05 Shinpei Maruyama Merge branch 'my_first_branch'
|\
| * bab8d49 2013-05-05 Shinpei Maruyama 牛の鳴き声を追加
* | c3ef974 2013-05-05 Shinpei Maruyama 犬ファイルの中身も英語に合わせた
|/
* 5fe17c7 2013-05-04 Shinpei Maruyama 動物ファイルを animals ディレクトリに移動
* bb8f610 2013-05-04 Shinpei Maruyama ファイル名を英語に変更
* 8faaa1f 2013-05-04 Shinpei Maruyama 豚の鳴き声は要らないので削除
* 66c681b 2013-05-04 Shinpei Maruyama 犬と豚の鳴き声を追加
* 66346b5 2013-05-04 Shinpei Maruyama 猫の鳴き声を nyan から mew に変更
* 33028c1 2013-05-03 Shinpei Maruyama 猫の鳴き声を管理するファイルを作成
うん、消えています。
こんな感じで、マージを行うことで分岐したコミット(変更の履歴)を、ひとつのコミットにまとめることが可能です。では今回のまとめ
git merge branch_name
で、今選択されているブランチに branch_name という名前のブランチのコミット内容を取り込むことができる- マージするときには「マージコミット」という特殊なコミットが行われ、マージコミットのコミットオブジェクトは親をふたつ持つ
- そのマージコミットが持つ「この瞬間のファイルの状態」は、ふたつの親が持っていた内容を合わせたものになる
git branch -d branch_name
とすることで、branch_name という名前のブランチを消すことができる
こんなところでしょうか。次回はもう少し実践的なマージを行ってみましょう!