Gitを学んでいて「なるほど!」となる瞬間

Gitは速く柔軟性がありますが、理解に時間のかかる分散型バージョン管理システムです。Gitを始める前に次を理解しておきましょう。

学習書指南書はGitを理解するのに役に立ちました。しかし、その他にもGitの理解に至ったきっかけがありますのでご紹介します。

ステージング・エリアがある

Gitにはステージング・エリアがあります。繰り返しますが、ステージング・エリアがあるのです

これには混乱しました。リポジトリ(「オブジェクトデータベース」)とステージング・エリア(「インデックス」と呼ばれる)の両方がGitにはあります。チェックインには2段階あります。

  • git add foo.txt
    • インデックスにfoo.txtを追加します。これだけでは、チェックインは完了していません。
  • git commit -m "message"
    • リポジトリにステージ済ファイルを記録します。これで、ファイルをトラッキングできます。
    • "git add --update"は、既にトラッキング対象であり、かつ修正があったファイルをステージします。

なぜステージなのでしょう? Gitには柔軟性があります。例えばa、b、cが変更された場合、個別にコミットすることもできれば、同時にコミットすることができます。

取り消し方法も2つあります。

  • git checkout foo.txt
    • ローカルの変更を取り消すことができます(svn revertのように)。
  • git reset HEAD foo.txt
    • ファイルのステージングを取り消すことができます(ローカルのコピーは修正済のままです)。

追加してコミットする、追加してコミットする。Gitにはリズムがあります。

ブランチングはSaveAsメソッド

ブランチはSaveAsメソッドのようにディレクトリに保存します。しかも、次のような利点があります。

  • ブランチをオリジナルに簡単に統合(マージ)できます(分岐したブランチは管理されるため、重複してマージされることはありません)。
  • スペースを無駄にしません(共通のファイルは1回しか保存されません)。

なぜブランチなのでしょう? 通常のファイル保存の際に使用するSaveAsメソッドを考えてみてください。オリジナルファイルは安全な場所に保管しておいて、試行錯誤をするものです。Gitでもこれを可能にするだけでなく、オリジナルとの統合を可能にしてくれています(実際、svnは1回しかバックアップを取らない1つの共有ドライブのようなものです)。

仮想ディレクトリをイメージする

私はブランチを.gitフォルダの中にある「仮想ディレクトリ」として見ています。物理的なディレクトリ(c:\projectまたは~/project)においては、仮想ディレクトリ間をチェックアウトで行き来します。

  • git checkout master
    • masterブランチに戻ります(“cd master”)。
  • git branch dev
    • 既存のブランチから新しいブランチを作成します(“cp * dev”)。
    • しかし、”git checkout dev”に”cd”が必要になります。
  • git merge dev
    • (ブランチをmasterに統合したい場合)devの変更履歴を取得します(“cp dev/* .”)。
  • git branch
    • 全てのブランチをリストで表示します(“ls”)。

私は、「devディレクトリに変更(チェックアウト)、変更して、変更を保存(追加・コミット)、masterディレクトリに変更、devへの変更をコピー(統合)」と覚えています。

物理的なディレクトリはメモ帳です。仮想ディレクトリはgitコマンドの入力で動きます。

  • rm foo.txt
    • サンドボックスからfoo.txtを削除します(ブランチを再びチェックアウトすれば復元します)。
  • git rm foo.txt
    • 現在の仮想ディレクトリからfoo.txtを削除します。
    • その変更をコミットする必要があります。

現在のブランチを知るには

現在のディレクトリを確認する時のように、現在のブランチをプロンプトに表示してください。


私の.bash_profileは次のとおりになっています。

parse_git_branch() { 
    git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* (.*)/(1)/' 
}

export PS1="[33[00m]u@h[33[01;34m] W [33[31m]$(parse_git_branch) [33[00m]$[33[00m] " 

ブランチ構築を可視化する

Gitでは好きなようにブランチを構築することができます。Nvie.comでは、素晴らしいブランチングモデルを紹介しています(訳注 : 日本語訳の記事があります)。

  • メインブランチ(master)を保持しましょう。右端に置いておきましょう。
  • ブランチ(master -> dev)やサブブランチ(dev -> featureX)を作成しましょう。masterから離れれば離れるほど、突飛なものと言えるでしょう。
  • 統合は分岐の近いブランチのみで行いましょう(master -> dev -> feature X、あるいはfeatureX -> dev -> master)。

ブランチのレイアウトは整理整頓しておきましょう。私の場合、masterでsvnプロジェクトを管理し、devでコードを管理しています。私は通常、masterをクリーンな状態にしておくので、必要になった場合、いつでも修正ブランチを作成することができます。

ローカルとリモートを理解する

Gitにはローカル操作とリモート操作があります。私も初めは混乱しました(git checkoutとgit pullの使い方など)。基本的にローカルで作業して、リモートでの作業は必要に応じて行えばいいのです。

ローカルデータ

  • git init
    • ローカルリポジトリを作成します。
    • git addやgit commit、git branchを使ってローカルで作業します。

リモートデータ

  • git remote add name path-to-repo
    • 既存のリポジトリからリモートリポジトリ(大抵origin)をトラッキングします。
    • リモートブランチは”origin/master”あるいは”origin/dev”などです。
  • git branch -a
    *全てのブランチ(リモートおよびローカル)をリストアップします。
  • git clone path-to-repo
    • リモートリポジトリをコピーし、新しいローカルのGitリポジトリを作成します。
    • ローカルmasterはリモートのmasterをトラッキングします。
  • git pull
    • リモートリポジトリからローカルリポジトリにブランチを統合します(origin/devからdevにプルします)。
  • git push
    • ローカルリポジトリからリモートリポジトリにブランチを送ります(devからorigin/devへとプッシュします)。

なぜローカルとリモートなのでしょう? 未完成の作業をコミットしないように、Subversionのチェックインは集中管理されています。Gitでは、頻繁にローカルコミットを実行し、プッシュは準備が整った場合のみ実行します。

GUIDは素晴らしい

Gitではコンテンツをハッシュ値(GUIDで管理します。2つのブランチが同じ場合は、同じGUIDを持ちます(異なるブランチの場合、GUIDも当然異なります)。

なぜこれが素晴らしいのでしょう? 独立したブランチを複数作成し、それらを統合しても、共通のGUIDを持つことができます。集中的な番号管理は必要ありません。大抵の場合、最初の数桁を見ます。例えば、「a93」で始まっているかなど。

ヒントとコツ

次を.gitconfigに利用してみてください。

[alias] 
    ci = commit 
    st = status
    co = checkout 
    oneline = log --pretty=oneline 
    br = branch 
    la = log --pretty="format:%ad %h (%an): %s" --date=short 

Git用のGUIツールは存在しますが、私はツールを使わず、コマンドを覚えながら学ぶ方が好きです。Gitは、自己主張のあるソフトウェアです(私の気に入っているポイントです)。ロジックがgitの世界観を分かりやすくしてくれていると思います。

その他の関連記事

  1. A Visual Guide to Version Control
  2. Intro to Distributed Version Control (Illustrated)
  3. Aha! Moments When Learning Git

他にも記事をGuidesProgrammingに投稿しています。この記事はgitversion controlにタグしています。