svn な人のための これだけ読めば (もしかしたら) わかる (かもしれない) git の考え方

はじめに

svn を使っていたあなたが、さくっと git の考え方を理解できますように (*・人・*)

git リポジトリのイメージ

git リポジトリのイメージを絵にしてみました。

絵の中の登場人物について、このあと触れますー

f:id:rinu:20130225021226p:plain

 

リポジトリ 

リポジトリは複数のファイルの変更履歴を記録する入れ物です。

 

リポジトリを使用するときは、どこかにある、誰かのリポジトリを、自分のマシンにまるっとクローンを作って使用します。

 

(対して svn はある時点でのファイルのみを、ローカルに持ちます)

 

 

コミット(図の黄緑の四角いあいつ)

ファイル達に変更を重ねていくと、変化したファイル達の状態が、リポジトリに保存されていきます。

このリポジトリへ変更した内容を保存することを「コミットする」といい、保存されたファイル達の、変化の履歴の1つ1つを「コミット」といいます。

 

それぞれのコミットは 「0abe...」 みたいな英数字の羅列を ID として持っています。

 

また、コミットは、直前のコミット(親コミットといいます)の事も知っています。

図でコミットから前のコミットに向けて線が伸びているのは、そのことを表しています。

 

(対して svn のコミット ID は、増加する数字ですね)

 

(svn とは違い、コミットと他リポジトリへの同期は別のものです。コミットは自分のリポジトリへコミットを保存するのみです)

 

ブランチ(灰色の四角いあいつ)

ブランチは、一連のコミットの流れの最新のコミットを指し示すものです。

 

ブランチに対してコミットを行うと、新しいコミットが作成され、ブランチはそのコミットを指し示すように変更されます。

 

ブランチは、あまり関連しない別々の作業をそれぞれ別のブランチで行い、最後に1つのコミットにあつめるというような使い方をするんですー

 

例えば以下の図では、3つの異なる作業のために、3つのブランチを作ってます。

f:id:rinu:20130225021205p:plain

 

 

master メイン作業用です。これまで start -> abc2 -> 201f という変化の歴史を作って来ました。
バグ修正用 master で起きたやばいバグを修正するために使ってます。master から分岐し、 2cfc というコミットをおこないました
新機能用 これまで start -> abc2 -> bc01 -> efa0 という歴史を。。

 

(svn の trunk ブランチは、 git では master という名前です)

 

HEAD(白くて四角いあいつ)

HEAD はブランチを指し示すものです。

前の例だと、 HEAD は master を指していますね。

 

HEAD は、私が今まさに使っているブランチを指しているものですー

普段は master で開発してて、バグを直す気になったら HEAD を 「バグ修正用」に移動し、新機能を実装したくなったら 「新機能用」 に移動する。。みたいな感じに使うんです。

 

そんな風に HEAD を動かす時、 checkout っていう操作をしますー

 

リモート

ここからは、自分のリポジトリとみんなのリポジトリの間でいろいろする時に使うものたちの説明ですー

 

f:id:rinu:20130225021226p:plain

 

リモート

自分のリポジトリ以外のリポジトリのことを「リモート」と呼ぶんです。

 

自分のコミットをみんなとシェアするには、このリモートを使ってヤリクリしますー

 

この例の私のリポジトリでは、他のリポジトリに対して origin という名前をつけてます。

 

ローカルブランチとリモートブランチ

世の中には2種類のブランチがあります。

ローカルブランチと、そうじゃないブランチです。

 

ローカルブランチ: 自分専用ブランチです。

 

リモートブランチ: リモートに存在するブランチのコピーです。

見ることはできますが、コミットはできません。

変更する時は、ローカルブランチから push という操作を行います。

 

ローカルブランチはリモートブランチと関連付けることができ、この時、このローカルブランチのことをリモート追跡ブランチと呼びます。

 

この例では、私のリポジトリには、 以下のブランチがあります。

origin/master リモートブランチ origin の master ブランチのコピー
master ローカルブランチ(リモート追跡ブランチ) origin/master のリモート追跡ブランチ

 

私が直接コミットできるのは master だけですが、 master から origin/master へ push することで、変更を origin/master へ反映することができます。

 

 

リモートへローカルのコミットを反映

push という操作を行うことで、リモートブランチを変更することができます。

 

まず、上で出てきた例の状態から私のリポジトリの master へコミットを行うと、以下の状態になります。

f:id:rinu:20130225022106p:plain

ここで master から origin/master へ push することで、以下の状態になります。

f:id:rinu:20130225022125p:plain

 

私のリポジトリにあり origin にないコミットが origin に追加され、 origin/master の位置も更新されています。

 

この時、 origin/master から master へ fast-forward 出来る必要があります。

これは後述しますー

 

 

リモートからローカルへコミットの取り込み

リモートの内容は、みんなが変更しているので、その変更内容を取り込みたくなりますよね。

その変更を取り込むには fetch という操作を行います。

 

例えば、こんな風に origin の master に他のユーザが abf3 というコミットを行なっています。

私も master に 3457 というコミットを行なっています。

(私的には origin の master は 201f だと信じています)

 

f:id:rinu:20130225022155p:plain

ここで fetch を行うと、 origin に存在し、私のリポジトリに存在しないものをすべて取り込んで、さらに origin/master が移動して、こんな感じになります。

 

f:id:rinu:20130225022205p:plain

 

例の場合、 201f からの変更が 2つあるため、自分のコミットと、他の人のコミットを両方含んだ状態になります。

 

(svn はこの時点でコンフリクトしたりしますね。git の場合、この例のように分岐するので、後でマージすることになります)

 

私。。 git の事よくわからないけど。。 ここのイメージを持てるかどうかは重要かなーって。。思うの。。

 

例えば、話変わって複数のリモートがあって、それを取り込む時。。

 

f:id:rinu:20130225022215p:plain

 

この mako, tune を両方取り込むと

 

f:id:rinu:20130225022230p:plain

 

全部を組み合わせた状態になります!

 

マージ

リモートから取り込んだ場合や、明示的にブランチを分けた場合等、最終的にはいっぱいあるブランチを 1つにまとめてあげないといけません。

 

新機能がついたアプリ、バグが直ったアプリ、別々にリリースしても怒られちゃいます。。

欲しいのは新機能がついて、バグの直ったアプリです( ー`дー´)

 

マージ

例えばこんなふうに、 origin/master と master に差異がある状態で、 origin/master の abf3 コミット内容を master に取り込みたい場合

f:id:rinu:20130225022257p:plain

 

マージをするとこんな風になります。

f:id:rinu:20130225022314p:plain

 

新しくできた 4d21 というコミットは、親コミットを2つもっています。

(svn とは違い、両方の親コミットのことを知っているため、マージした後も両方の履歴を辿ることができます)

 

master と origin/master が同じファイルの同じ場所を編集していたりすると、マージが失敗(コンフリクト)することもあります。

 

失敗したら、がんばって直します><

 

fast-forward

特定の状況で使われるマージ処理です。

通常のマージとは違い、新しいコミットを作りません。

 

例として、さっきのマージの例を見てみますー

 

master に origin/master をマージしたので、 4d21 というコミットができていますね。

この状況で origin/master に master をマージしたらどうなるでしょう。

 

この場合、結果は 4d21 とおなじになるはずですよね。

そのため git は新しいコミットを作らず、単純に origin/master の位置を移動します。

f:id:rinu:20130225022311p:plain

 

これが fast-forward です。

 

4d21 コミットはすでに存在しますし、以前に検証も済んでいるはずです。

そのため、この fast-forward はとても安全なマージ処理といえます。

 

 

いつもの流れ

ここまで、 git のイメージを書いてきましたが、ここからはちょっと視点を変えて、

実際に使うときの流れを元に説明していきますー

 

コマンドラインで説明しますが、 GUI でも流れは変わらないかと思うです!

 

さいしょにやること

ユーザ名とパスワードの設定

$ git config --global user.email "メアド"

$ git config --global user.name "名前"

 

clone でリモートのリポジトリをコピーします 

$ git clone URL 

 

デフォルトではクローン元のリモートに origin という名前が付けられます。

そして origin の master 用に origin/master というリモートブランチができます。

これは origin リモートの master ブランチです。

 

さらに、 origin/master に紐づいた、 master というリモート追跡ブランチができます。

 

日々やること

リモートブランチからローカルブランチを作成します

clone 時に origin/master に紐づいた、 master というリモート追跡ブランチができています。

 

origin/master 以外のリモートブランチを使用したい場合、まず、リモートブランチからローカルブランチを作成します。

 

例えば origin/b1 ブランチを使いたい場合、こんなかんじです。

$ git checkout -b b1 origin/b1

 

このローカルブランチ b1 に対してコミットし、最終的に push することで、リモートブランチ origin/b1 へ変更を反映出来ます。

 

コミット

コミットしたい内容をインデックスというものへ入れます。

このインデックスに入れたものだけがコミットされるので、ひとまとまりの関係のある内容だけをインデックスに入れてコミットすることができるんです。

 

インデックスへ入れます

$ git add ファイル

 

コミットします

$ git commit

 

リモートの変更を取り込む

さくっとやるなら

$ git pull

 

この pull が実際に何をしているかというと、、

 

まずはリモートの変更を取り込みます

$ git fetch

 

次に、リモートブランチからローカルブランチへ、内容をマージします。

$ git merge origin/master

 

リモートへ変更を反映する

$ git push

 

自分専用のブランチを作り、切り替える

$ git checkout -b my_branch

 

とりあえずこんなところでしょうかー