2015年10月13日
Gitを学んでいて「なるほど!」となる瞬間
(2010-03-10)by Kalid Azad
本記事は、原著者の許諾のもとに翻訳・掲載しております。
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の世界観を分かりやすくしてくれていると思います。
その他の関連記事
- A Visual Guide to Version Control
- Intro to Distributed Version Control (Illustrated)
- Aha! Moments When Learning Git
他にも記事を Guides や Programming に投稿しています。この記事は git や version control にタグしています。
株式会社リクルート プロダクト統括本部 プロダクト開発統括室 グループマネジャー 株式会社ニジボックス デベロップメント室 室長 Node.js 日本ユーザーグループ代表
- Twitter: @yosuke_furukawa
- Github: yosuke-furukawa