言わなかったこと・発表資料だけではわからないこと

昨日のわんくまで言わなかったこと、発表資料だけではわからないことです。
ちなみに発表資料はこちら→わんくま 名古屋勉強会 #11 の発表用資料 - ぐるぐる〜

PowerShell のコマンド

発表資料の 13〜16 ページで PowerShell を開いてなにやらコマンドを実行してますが、これは現在のフォルダ以下に存在する _svn フォルダの数を数えています。

ls -r -fo | %{ $_svn = 0 }{ if ($_.Name -eq '_svn') { $_svn++ } }{ $_svn }

-r オプションで子の階層も調べ、-fo オプションで隠しフォルダも含めています。
今考えたら、

(ls -i '_svn' -r -fo).Length

でいいですね。
-i オプションで指定した名前のアイテムのみを取得するようにしています。
しょっぱなから Git 関係ない話でごめんなさい。

Windows での Git

msysgit の方が軽いので、以下のどれにも当てはまらない場合は msysgit を使えばいいと思います。

git reset

発表資料の 54〜59 ページで、git reset は git add の取り消しではない、とあります。
これはどういうことかと言うと、git reset の挙動を見ると、git add の取り消しにも見えるけど、実際には違う、ということです。
そのため、リポジトリにファイルが無い状態で git reset をしてもエラーになるだけです。


また、リポジトリ、index、work tree のすべての状態が異なる場合、つまり git add した後で work tree に更に変更を加えた状態ですが、その状態で git add すると、git add する前の index の状態はもう取り戻すことができません。
このあたりの動作もあって、index を積極的には使わず、とにかくコミットしまくって後で git rebase -i で整理する、というやり方を好んで使う理由にもなっています。

Subversion でのブランチの持ち方

「ルートから持つ」の派生として、「必要なブランチを独立に持つ」という方法もありました。
この方法の利点としては、

  • ルートから持つ場合に比べてサイズはよりコンパクトになる
  • 今作業しているブランチのみを持つ場合に比べてブランチの切り替え作業が必要ない

というものがあります。
欠点としては、

  • 自分が今どこにいるのか把握しておく必要がある
  • 今作業しているブランチのみを持つ場合に比べるとサイズが大きい

と、ルートから持つ方法と今作業しているブランチのみを持つ方法の中間あたりの特徴を持ちます。

stash

stash 知った時は便利だすげー!と思っていたんですが、実は最近あんまり使っていません。
というのも、git stash せずに git commit してしまっても、そのブランチに戻ってきて git reset HEAD^ するなりなんなりで同じようなことはできる上、コミットしておけば操作を間違って変なことしても普通の操作で元に戻せる安心感も付いてくるからです*1


最近は、大元のリポジトリに反映してはいけない開発用の設定ファイルを stash しておくために使っています。
これも本来はバージョン管理システムで解決すべき問題ではないので、だんだんこの使い方もやらなくなっていくでしょうね。

git bisect run で使用するテストプログラム

例えば C# での開発の場合、NUnit で書いて、nunit-console でそのテストを実行するようなバッチファイル等を作成すればいいでしょう。
ただし、このテストプログラムは (git bisect 作業中は) リポジトリに含めないようにしましょう。
含めても過去のバージョンには含まれていないので、意味ないです。

同期のタイミング・同期用のコマンドの違い

これは言わなかったわけじゃないけど、説明が足りなかった部分です。
Git では*2リポジトリは独立してます。
なんで、あるリポジトリからクローンしたリポジトリであっても、クローンした方、された方どちらのリポジトリの変更も明示的に同期を指示するまでは相手には伝わりません。


で、同期の指示にもいくつかあって、通常使用するのが

  • git fetch
  • git pull
    • git pull --rebase
  • git push

の 4 種類になります。
git fetch、git pull、git pull --rebase は指定したリポジトリから最新の情報を取得するために使用し、git push は指定したリポジトリに自分のリポジトリへ加えた変更を反映するために使用します。


git fetch と git pull 系の違いは、git fetch では最新の情報を取得するだけで、マージやリベースは行わず、git pull 系では行う、という点です。
この辺は発表資料の 203〜205 と、207〜209 に書いてあります。


git pull と git pull --rebase の違いは、git fetch の後に git merge するか git rebase するかの違いです。
これは発表用資料では全然分からないと思うので、ちょっと画像を用意しました。

最初の状態

リモートのリポジトリ (上) に自分のリポジトリ (下) には無いコミット C が存在しており、自分のリポジトリにはリモートのリポジトリに無いコミット D が存在しています。

git fetch

git fetch するとどうなるかも一応示すと、

こうなります。

git pull

git pull すると、

こうなります。
git fetch と見比べると、git fetch した後にマージされていることが分かります。

git pull --rebase

git pull --rebase すると、

こうなります。
git fetch と見比べると、git fetch した後にリベースされていることが分かります。

git push は git fetch や git pull とは逆方向の処理です。
自分のリポジトリに加えた変更を他のリポジトリに反映させるために、注意点があります。

  • git fetch や git pull では何か衝突があっても自分で解決できるが、git push ではそれができない。そのため、git push を行うためにはまず自分のリポジトリを最新状態にしておく必要がある。通常、git pull や git pull --rebase を行ってから git push を行う。
  • git push するともう後戻りはできないと考え、svn commit 並に注意して行うこと。

git pull や git pull --rebase が git rebase や git reset でやり直せますが、git push はやり直しが基本的にできません*3
注意して行いましょう。

ブランチとタグの違い

これははじめから発表では説明するつもりが無かったところです。
Subversion ではブランチとタグは区別されていませんが、Git では区別されます。


Git でのタグは、ブランチと同じようにコミットを指すポインタのようなものなのですが、ブランチと違い読み取り専用です。
コミットの分かりやすい別名がタグだと考えてもいいでしょう。


Git でのタグは読み取り専用なので、git checkout でタグ名を指定すると、どのブランチにもいない状態となります。
この状態で何らかの変更を行い、コミットすると、そのコミットは reflog もしくはハッシュ値でのみ到達可能なコミットとなります。
このコミットは gitk のコミットのグラフでは確認できません。
このコミットを gitk で確認するためには、そのコミットにタグを付けるなどする必要があります。

Pro Git

直ったみたいです!
http://progit.org/book/ja/
Pro Git、ブランチの説明がわかりやすいんですが、日本語のページが今なぜかインドネシア語のものになってます。
なので、資料では GitHub の URL も載せたんですけど、Google Docs に PDF 版があったので、こちらをどうぞ。

http://docs.google.com/fileview?id=0BxkaLAGEeWgLM2QwNGE3YjAtYTMwZS00ZDM0LWJiZWMtYTg4MzEyY2NjNDU3&hl=ja

*1:git stash でも git gc するまでは元に戻せるけど、面倒だし滅多に使わないコマンドなのでいちいち調べる必要がある

*2:というか他の DVCS でも同じはず

*3:git fetch は取ってくるだけなので自分の作業に影響を与えないのでやり直し云々は関係ない