Top / GitHub / 基本

Gitは ローカルでのコミットやリモートへのプッシュ・プル、ブランチのマージやナントカカントカなどいろいろとややこしい仕組みがあります。とりあえず自分の理解のために、まずはリモートを使わないローカルでの操作を整理してみました。おもにリポジトリの作成からコミット、マージまでを整理しました。

Gitを始める

$ mkdir tutorial && cd $_
$ git init
Initialized empty Git repository in /Users/masatomix/git/tutorial/.git/
$ echo "master added" >> file1.txt
$ cat file1.txt
master added
$ git add file1.txt
$ git commit -m "master commit"
[master (root-commit) 25c2961] master commit
 1 file changed, 1 insertion(+)
 create mode 100644 file1.txt
$ echo "2 master added" >> file1.txt
$ cat file1.txt
master added
2 master addedgit 
$ git add file1.txt
$ git commit -m "2 master commit"
[master ad0dac1] 2 master commit
 1 file changed, 1 insertion(+)

addしてcommitの繰り返しです。コミットまでできました。

ブランチを作成する

ブランチを作成します

$ git checkout  -b dev   ←作成と移動。作成だけなら git branch dev でいい
Switched to a new branch 'dev'
$ git branch
* dev [#h881c414]
  master

ローカルのワーキングツリーがdevになりました。この時点で、masterとdev それぞれはおなじリビジョンにいることになります。

01.png

devに存在するファイルを修正して、コミットします。

$ echo "dev modified" >> file1.txt  && git add file1.txt && git commit -m "dev commit"
[dev 3f7af4f] dev commit
 1 file changed, 1 insertion(+)
02.png

上記のキャプチャを見て分かるとおり、devだけひとつリビジョンが進んでいますね。

ブランチのマージ

さて、devで修正した内容をmasterにマージします。マージとは、ローカルのワーキングツリー上のブランチに他のブランチの修正を反映させることです。 修正を反映させたいブランチへ移動して、他のブランチを指定してマージのコマンドを実行します。

$ git checkout master
Switched to branch 'master'
$ git merge dev
Updating ad0dac1..3f7af4f
Fast-forward
 file1.txt | 1 +
 1 file changed, 1 insertion(+)
$ git branch
  dev
* master [#fbf5598a]

今回は masterブランチにdevブランチをマージしました。ただmasterについてはdevを作成後、コミットがされていなかったので、ただ単にmasterブランチのリビジョンがdevのリビジョンに進んだだけのようですね。これを早送りするって意味でFast-forwardなマージというようです。

03.png

Fast-forwardでないマージ

masterからdevを作成後、たとえばmasterが別のブランチをマージして先に進んでいる場合を考えます。この場合はdevの修正をmasterにマージする際、単純なFast-forwardにすることはできないはずですよね。どうなるかやってみます。

$ git checkout -b dev2
Switched to a new branch 'dev2'
$ echo "dev2 added " >> file2.txt && git add file2.txt && git commit -m "dev2 commit"
[dev2 134e253] dev2 commit
 1 file changed, 1 insertion(+)
 create mode 100644 file2.txt
$ git checkout master
Switched to branch 'master'
$ git merge dev2
Updating 3f7af4f..134e253
Fast-forward
 file2.txt | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 file2.txt

さあdev2を使って、masterのリビジョンを少し進めました。

04.png

続いて、devも独自にリビジョンをすこしだけ進めておきます。

$ git checkout dev
$ echo "dev added " >> file1.txt  && git add file1.txt && git commit -m "2 dev commit"
[dev 3f7a1ef] 2 dev commit
 1 file changed, 1 insertion(+)
05.png

上記キャプチャで分かるとおり、masterとdev2はおなじ場所にいます。dev はmasterの一個前(?)から派生していて、独自の進化をしています。さあ派生したdevの修正をmasterに反映してみます。

$ git checkout  master
Switched to branch 'master'
$ git merge dev
Merge made by the 'recursive' strategy.
 file1.txt | 1 +
 1 file changed, 1 insertion(+)
06.png

新しいコミットがひとつ実行され、Fast-forwardでないやり方で、ファイルがマージされました*1

今回はFast-forwardではないのでコミットメッセージを入れるダイアログが出たと思いますが、

 $ git merge dev -m "Merge branch dev" 

などとすればメッセージは表示されません。。

最後にdevをmasterまで進めて、完了です。

$ git checkout dev
$ git merge master
Updating 3f7a1ef..56f88d1
Fast-forward
 file2.txt | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 file2.txt

masterとdevはおなじ状態になりました。

07.png

まとめ

このように、あるブランチの修正をマージする場合、そのまま直列に後ろにつなげばよい場合は、Fast-forwardというやり方でブランチの内容が反映されることが分かりました*2。また自分が別のコミットやマージによって、単純に早送りするだけではダメな場合、他ブランチの修正を反映させたコミットを新たに作成することで、マージ処理が行われることが分かりました。

それらの修正がコンフリクトしない場合、gitが正しくマージ処理をしてくれます。かしこいです。おなじファイルをいじっているなど、単純なマージで廃家内場合は人間が競合を取り除くコミットを手動で行うことで、マージ処理を行います。それについてはこちらに詳しく書いてありますのでそちらをご参照のこと。

6. マージでの衝突を解決する

さいごに、リベースってなんだ

別のブランチの修正をマージするにあたって

$ git checkout dev
$ git merge  master

とやることで、devブランチにmasterブランチの修正を反映させることができるのでした。便宜上、マージがさっきの逆のパタンになっているのはご愛敬。。

08.png

さてgit には リベースという概念があります。これはマージが「いまのdevの状態に対して、masterのコミットをまとめた内容をコミットすることで反映する」のに対して「いまのdevの状態(コミット履歴)を待避しておいて、枝分かれしたところへ戻りそこからmasterの修正内容をdevに反映し、そのあと待避していたコミット履歴を新たにコミットする」機能のようです。

$ git checkout dev
$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: dev commit     <- 3fc335e として新たにコミットされてる!
Applying: dev commit     <- 9210c21 として新たにコミットされてる!
Applying: dev commit     <- 09a6a5a として新たにコミットされてる!
09.png

このように、devのコミットがいったん待避されて*3、masterの修正がdevに反映され*4、んで「あらたな」コミットとして、待避していた内容がコミットされることが分かりました。

画面キャプチャからもわかるとおり、修正履歴が一直線となり見やすくなるにはなるのですが。。ココではリモートへのpushの件は出てきませんが、リモートへpush済みのブランチを、それより前のリビジョンからrebaseした場合、巻き戻し&再コミットによってリモートとの不整合が発生するので、そんなケースではやってはいけないようです。

こわくない GitのP.143 に詳しく書いてあります。

関連リンク


この記事は

選択肢 投票
おもしろかった 0  
そうでもない 0  

Top / GitHub / 基本

現在のアクセス:425


*1 今回devでのコミットは一回でしたが、複数回の場合も、それらをまとめた一つのコミットが実行されます
*2 というか自分がただ進むだけということでしょうか
*3 rewinding。巻き戻し
*4 そして、これはきっとFast-forward

トップ   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS