The RIV Developer Corner

Random musings, tutorials, articles, and rants on such topics as Flash/Flex/AIR, J2ME, BlackBerry, PHP, and Ruby on Rails development.

この文書は ここを元にF.Uがいいかげんな訳を行ったものです。 誤字脱字、変な表現など、日本語版の不備は私のせいですが、責任は取れません。 それでもいい人だけご利用下さい。

Thursday, August 14, 2008

My Git Cheatsheet

もし君が CVS で何とかやっているのなら、Subversion はより美しい世界 をもたらすように見えるだろうが、それでも恐ろしいブランチから逃れられな いということを認めはしないだろう。 君はより良い方法を見付ける必要がある。 Along came Git and the world of SCM was like crawling through the Wardrobe.
ここ から Git をダウンロードしたんだろう。
そしてググって情報を探し、このチートシートを見付けたというわけだ。 The answer to your prayers? No. きっと君は Git について多少の知識があるはずだ。 しかし、君はどうにかしてこのチートシートに辿り着いた。 So just hold on tight.

Git の初期設定


君は既に自力で Git をインストールしただろう。 そうしたら次のことを行っておくとよい。
git config --global user.name "Joe Smith"
git config --global user.email "joe@smith.com"
git config --global color.diff auto
git config --global color.status auto
git config --global color.branch auto
もちろん名前とメールアドレスは自分のものに置き換えただろうね? もし違うのなら、ここで読むのを一旦やめて、やり直してみよう。 上で設定した color コマンド群は長いステータス表示をより楽しくしてくれる。 この設定は一度だけ行えばよく、リポジトリごとに繰り返す必要はない。

リポジトリの作り方


リポジトリを準備するには二つの方法がある。 一つは自分自身のリポジトリを新規に作成することだ。これはディレクトリを 作って、そこで
git init
を実行すればよい。 もう一つは既に存在するパブリックリポジトリを見付け、
git clone git://git.assembla.com/my-whatever-project.git
を実行する。
これで君自身のプライベートなリポジトリを持つことができ、そこで達成した 結果をパブリックなリポジトリに公開することもできる。 プライベートなリポジトリを持つことで、例えばコミットをなかったことにし たり、ブランチを片っ端から消してしまったりといった、以前なら考えもしな かった行動も取れるようになる。
おめでとう、君は Git の準備を完了した。 さあ、これから Git で何をしようか? 材料は一般的なものからそうでないものまでたくさん揃っている。 まずは一般的なものから手をつけてみるとしよう。

基本操作


ファイルの追加とコミット:
vi MyNewClass.as (コンテンツを用意する)
git add MyNewClass.as (変更点をインデックスへ通知)
git commit (インデックスをリポジトリへコミット)
もしくはこうする:
vi MyNewClass.as
git commit MyNewClass.as (ファイルを明示的に指定することで、変更点を教えてからコミットする)

もし複数のファイルを編集したなら:
vi MyNewClass.as
vi MyOtherNewClass.as
git add . (カレントディレクトリを再帰的に処理)
git commit
またはこうもできる:
vi MyNewClass.as
vi MyOtherNewClass.as
git commit -a (-a オプションで明示的に add を行わせる)

ここで、ファイルは作業領域からインデックスへと移動され、 それからリポジトリへとコミットされていることを覚えておこう。 インデックスは Perforce のチェンジセット(コミットされるもの)だと考えてもよい。 また、ご希望なら、このインデックス操作をスキップすることも簡単にできる。 このコンセプトは他の操作にも用いられている。
最後にコミットされた内容を確認したいなら、こう打ってみる:
git show

おや、このコミットでへまをやらかしてしまったようだ。 どこかを壊してしまった。 すぐに前の版に戻る必要がある。しかしどうやって?
git revert HEAD

これで直前(HEAD)の変更点を取り消すための新しい版を手に入れられる。 They don't go back into your workspace, though. しかし、君はまだオリジナルの変更点をリポジトリから入手できる。 なぜならその変更点は削除されたのではなく、単に巻戻されただけだからだ。
HEAD はリビジョンシンボルである。これは「最後の版」を示す。 例えば、そう、二つ前のコミットの変更点だけ取り消したい時は次のように打つ:
git revert HEAD^

こうすればリポジトリには順に、オリジナルのコミット、その次のコミット、オリジナルの変更点だけ取り消したコミット(二番目のコミット内容は保持される)と並ぶ。 クールだろう? HEAD^ might be arbitrary, but the power of reverting a commit undeniable. 指定するコミットは何でもよい。 ではコミット履歴を見てみよう:
git log

40桁のIDが見付かったかな? これを使って、いつでも次のようにして変更を取り消せる:
git revert f728 (対象を一意に特定できる分だけの桁数でよい)

ファイルを消すにはこうする:
git rm MyClass.as
git commit

これでインデックスにファイルの削除が記録される。 さて、それでは作業領域の状況を表示してみよう。こう打ってみる:
git status

もしファイルに対する詳細な変更情報を得たいなら、こう打つ:
git diff MyClass.as

覚えておいて欲しい点として、変更したファイルをインデックスに登録し、 その後でさらに変更した場合、前半部分だけをコミットしたいのでないかぎり、 もう一度ファイルをインデックスに add し直す必要がある。 インデックスにはファイルが入るのではなく、変更点が格納される。 つまり、インデックスは「変更依頼の明細」なのである。

タグ付け


君はタグ付けが何か知っているだろう。文字通り時間の特定の点に対してタグを打つことだ。 君が依頼主に作成したバージョン1.0のアプリケーションを送り、 後から彼らに対して何を提供したか参照できるようにしたいとする。 その場合、次のようにしてタグを付ける:
git tag v1.0

うーん、単純すぎる名前だ… やっぱりやめよう:
git tag -d v1.0

本当に消えたかな? こう打って確かめよう:
git tag -l

今度は FIRST_RELEASE という分かりやすい名前にしよう:
git tag FIRST_RELEASE

おや、ちょっと待った。まだ変更点が一つ残っていた。 add して commit しよう。 その後で同じタグ名を付けなおそう:
git tag -f FIRST_RELEASE (-f オプションで置き換える)

これでタグが新しい HEAD に付いた。 タグはコミットした場所やブランチとして扱うことができる:
git checkout FIRST_RELEASE

これで revert はできない。コミットではなく時間中の一点だからね。 覚えたかい?

修正の仕方


君がいくつかの変更をコードに対して行い、それらを捨てることに決めたとしよう。 インデックスと作業領域を掃除するにはこうする:
git reset --hard HEAD (最後のコミットへ戻す)

もし捨てると決めたのがコミット後だったら? かなり危険な方法ではあるが、次のようにすると一つ前のコミット時点まで戻せる:
git reset --hard HEAD^ (注意して使うこと! 警告したよ)

コミット自体を捨ててしまうのは、ほとんど正気とは言えない行動で、充分な注意が必要だ。 このコマンドをもてあそんで失敗した時には、とても後悔することになるだろう。 もちろん、学習するには既に遅いわけだけど。 まあ、唯一の救いとしてはリポジトリがプライベートだったという点かな。 パブリックリポジトリでは決してこんなことをしてはいけない。
実際には、こちらの方がよりよい:
git reset --soft HEAD^ (--soft オプションは変更を作業領域に残す)

大きな違いとしては、変更点が破棄されないということだ。 それらは、さらに変更できるように作業領域へ記録される。 すばらしい。
このようにした場合、次の方法でコミットしよう:
git commit -c

こうすると、古いコミットメッセージが表示される。 君はおそらくこのメッセージを書き換えることだろう。 その結果、古いメッセージは消え、新しいメッセージが記録される。
もしこの動作を避けたい場合、こんな方法がある。 これはコミットした後でいくつかの変更点をそれに加え忘れた場合にとても役に立つ。 まずはソースに変更を加えて、こう入力する:
git commit --ammend

これは新しい変更点を直前のコミットへ組み込む。 これは私が Subversion でやっていたように一つの変更に三つものコミットを行うよりも、よい方法である。 Git は我々に優しいのだ。
さて、時には一つのファイルだけが問題となり、他の変更は残したい場合もあるだろう。 変更をインデックスへ add してしまった、あるいは消してはマズいファイルを削除してしまったとしたら、こう打つとよい:
git reset HEAD FILE (インデックスのファイル FILE を戻す)
git checkout FILE (ファイル FILE を復元する)


一時保管


この機能は常に望んでいたが存在を知らなかったものだろう。 仮に君が CR239―誰かが見付けたバグ― をメールで報告してもらったとしよう。 君はこれを直す必要がある。しかし、ちょうど今は他の作業中だった。くそっ。 今から作業中のファイルを適当な場所にコピーし、作業領域をリセットした上で 問題のバグを直し、衝突が起きないよう神に祈りながらさっきの変更を 取ってくるのだ。これは正しい方法かい?
そう、間違いだ。
次のようにして、今の変更を一時保管しておける:
git stash "Localization Work" (今の作業内容を説明しておく)

これで作業中のファイル群はどこかにいった!もう変更点は何も残っていない。 それらは一時的に保管されている。こう打つと保管内容を見られる:
git stash list

さっきの登録内容があるだろう。これで作業領域はキレイになった。 変更がインデックスに記録されていたかどうかは気にしなくていい。 それらはちゃんと保存されている。 さあ、CR239 のバグを直そう。そして終わったらここへ戻ってこよう。
終わったかい? よろしい。さすがだ。 当然今の修正はチェックインしてあるだろう?よし。 それではさっきの作業内容を戻そう:
git stash pop

もしくは、こうやって明示的に指定する:
git stash apply stash@{0} (正しい一時保管ファイル名を使うこと)
git stash drop stash@{0}

最高にクールだろう? もし一時保管オブジェクトを大量に作って、それらを戻さなかったとしたら、 そこは段々混雑してくる。 上で説明した drop コマンドを使って一つずつ消していってもいい。 一発で全部消したいならこうだ:
git stash clear

うーん、速い。あとは今消したコードが必要にならないよう祈ろう。

ブランチ


深く気にしなくていい。さっき習ったやつだ。今度はもっとクールで簡単だ。 また別のバグ CR833 が報告されたとしよう。 君はこれを別のブランチで修正したい。つまり、メインの開発からバグフィックスを 切り離したいとしよう。
まずは、あるならば今までの変更点を一時保管しよう:
git stash "CR833 が報告される前までにやってた仕事"

オーケイ。では別のブランチでの作業を始めよう:
git checkout -b CR833 (-b オプションで新ブランチを作り、それをチェックアウトする)

新ブランチになったことを確認してみよう:
git branch

アスタリスク(*)が付いている方が、今使っているブランチだ。簡単だろう? ではバグフィックスして、終わったら戻ってこよう。
よし、よくやった。チェックインもしたかい?よろしい。 それでは master ブランチへ戻ろう:
git checkout master (ブランチは既に存在するので -b は不要)

ここでマージの必要がでてくる。

マージ


この時点でさっきの変更を master に持ってきたい。 つまり、変更はブランチに適用されているが、それらを公式ビルドである master に移したい。ここでマージする必要がある:
git merge CR833 (この実行はマージ先となるブランチ(主に master)で行う)

衝突(conflict)が無ければ無事に終了するはずだ。 この場合、変更は適切に適用され、add され、commit されている。 Don't worry, the merge will be clean because the file will be specially marked as merges.
ブランチをマージしたので、それを消去することができる(もちろん、消さなければならない必要はない):
git branch -d CR833

我々はプライベートリポジトリを使っているので、好き勝手にブランチを消してよい。 マージがどれだけクールに行われるか分かったかい? 常にクリーンな状態を保とう。ブランチは簡単なんだから。

リベース(Rebase)


それでは、CR833 がとても難しくて、直すのにまるまる一ヶ月かかったと仮定しよう。 まあ、他のことに気を取られて長い間放置していたのでもいい。 その場合、新しいスタッフが master に色々チェックインしていて、 君はそれに追随しなければならないだろう。例えば Sean がプログラムのクラッシュを 止めるための変更をしていたとか。
リベース(Rebase)は、いつブランチしたかを変更する作業だと言える。 もし先週ブランチして、今の状態にリベース(rebase)したとすると、 それらが終わった時点ではあたかも今ブランチしたかのように見える。 君が master を自分の作業結果にマージしたのではない。 これは一旦(コミットも含めて)現在までの作業を破棄し、master (またはそれに代わる何か)にアップデートし、それから(コミットも含めた) 全ての変更を再度適用したのと等しい。
これはスゴい。
そうするにはリベースしたいブランチからこうする:
git rebase master (またはベースを動かす方のブランチ)

もし君が非常に長い間ブランチせずにいたとしても、これらは適切に働くだろう。 しかし、時々衝突が起こることがある。君がブランチ上で変更したファイルのどれかを どこかのバカが master でも変更してしまった場合だ。 その時、君はこの衝突を解決する必要がある。こうする:
git rebase --continue (--continue オプションでリベースを継続する)

リベースが完了するまで継続させた後、内容がひどく汚い状態になってしまい リベース作業自体をキャンセルしたくなるかもしれない。 その時はこうすればいい:
git rebase --abort

全ての変更点は元通りになり、作業の開始時点と同じ状態に戻れるだろう。 ああ、なんて臆病なことだろう。

リポジトリへのプッシュ


Git は場所に囚われないようにデザインされた。 開発者は他の開発者たちと変更点を共有できる。 君はいくつものリポジトリを(何度も clone することで)使えるし、それらに対する 変更をどこかに送ったり、どこかから持ってきたりすることもできる。 もし君が混乱した時には、そのリポジトリがプライベートなものであることを 思い出そう。それは君だけのもので、他の誰のものでもない。 それでは続きを読んでほしい。汝に天啓あれ。
変更をプッシュするのは、他のリポジトリへ君の変更点を加えることになる。 分かりやすい場合としては、全員が参照して公式ビルドの元になるパブリック リポジトリが考えられる。 君は自分の成果を「公式な記録」としてパブリックリポジトリへプッシュできる。
これは奇妙に聞こえるかもしれない。 Subversion ではリポジトリは一つだけであり、作業領域が各人のものである。 コミットできるのはそのリポジトリに対してのみだ。 これは単純であるが、but branching sucked. Now you have simple branching.
しかし、今君は複数のリポジトリを持てる。大量だ。 だが、こちらの方がクールである。
たとえば君がリポジトリを作り、多少の Git 領域を Assembla.com (または Unfuddle、 もしくは GitHub) から買ったとする。 まだこれらのアカウントを持っていないなら、ぜひ取りに行こう。これはクールだ。 そうでないなら、それらを想像してみてほしい。
では、君のリポジトリの内容を Assembla に送ろう:
git remote add assembla git://git.assembla.com/my-whatever-project.git
git push assembla master

もうちょっとハッキリやってみると:
git remote add assembla git://git.assembla.com/my-whatever-project.git
git push assembla CR833:master

君がブランチのどれかを master に送りたいなら、最後の文を使おう。 ただし、それはあまり一般的じゃない。
これで君の変更はパブリックリポジトリに入った。どうだい、クールだろう? しかし、僕はこれはいい機能だと思っていたけど、今では余計なステップじゃないかと思っている。 まあ、これで誰にでも君の作品を送ることができるのは覚えておいてほしい。 Maybe your work buddy. Keep it secure, no one has to know.

リポジトリからのプル


どこかから変更を引っ張ってきたいときにはこうする:
git pull assembla master

これでもう、そこにあった変更を(可能なブランチを含めて)持って来れた。

クリーンアップ


Git はもしかすると実行するたびに多少のフラグメントを起こすかもしれない。 君のハードディスクは 1990 年代のものじゃないかい? その場合にはたまにこうしよう:
git gc (gc はガベージコレクト(ゴミ集め))
git fsck

これで全部だ。さあ git しよう。