入門Git (Chapter9)


9章は変更履歴を追いかけるということで、これまでの章から少し内容が変わっています。


最初にLinus君がなぜgitを作ろうとおもったのか、理想の版管理とは?ということについて述べられています。
gitではあるファイル名をAからBに変更した際でも、コミットにファイル名を変更したことは記憶する必要なくHEADから追跡することで変更された事実を見つけ出せるということが言われています。(多分。。)
ここの説明は正直イマイチピンとこない部分でもありました。

パス名で制限した履歴検索

  • ここではgitが以下にログの検索を効率よく行えるような設計になっているのかという説明があります。言われてみるとなるほどと思うことであり、ここは是非本を手にとって読んで欲しいところだと思いました。
  • その他では、ログの検索の方法として下記のようなものが紹介されていました。
  • コミットで検索
# コミットログをさかのぼる始点を指定する
# v1.0というタグが切られているとする
% git log v1.0 -- foo/bar
# コミットログでも検索出来る
% git log eec6a53fe798dc5b1366cfc850c50a219da1f82b

# 期間を区切っての検索も可能
% git log v1.0..v2.0
  • ファイルパスで検索
# foo と aaa/bbb 以下のファイルに対するコミットを検索
% git log -- foo aaa/bbb
  • その他
# 指定した期間の間のコミット数
% git rev-list v1.0..v2.0

# コミットログを要約して表示
% git shortlog
# 誰が一番コミットしているかを調べる
% git shortlog -s -n

マージされた歴史に対する探索や単純化

  • マージにより親コミットが2つある場合、探索ファイルが変更されている方のブランチのみを歴史と捉え、他方はなかったものにされるという単純化が行われるそうです。
  • この辺りはコマンドと違って知ってどうというわけではないですが、gitの動作を理解するためにはとても勉強になると思います。
  • 先程挙げたファイルパスでの検索では、さらに関係ないコミットをスキップすることで単純化が行われているそうです。

行範囲変更の検出

  • 下記で指定文字列が変更に含まれているコミットだけを出力することが出来ます。
% git log -S'search word'
  • git logの出力ログ形式は「--pretty=short」のような形で指定することが出来ます。詳しいオプションや出力については、helpの「PRETTY FORMATS」の項目を見るのがわかりやすいと思います。
% git log --help
:
PRETTY FORMATS
:
       o    oneline

               <sha1> <title line>

           This is designed to be as compact as possible.

       o    short

               commit <sha1>
               Author: <author>

               <title line>
:

ファイル名の変更やコピーを検出する

  • あるファイルの内容を変更してさらにコピーしたときに「-M」オプションでうまく変更を検出してくれます。
    • git diffやgit show、git logなどで使えるのでファイルをまとめて移動させた時のdiffの取得には覚えておくと便利ですね。
% mv hello.pl hellohello.pl
% vim hellohello.pl
:
% git add .
% git rm hello.pl
% git diff --cached
 →hello.plが削除されたというdiffとhellohello.plが追加されたというdiffが表示されます。
% git diff -M --cached
 →hello.plがhellohello.plにrenameされたというdiffとそのあと変更した内容が表示される。
diff --git a/hello.pl b/hellohello.pl
similarity index 87%
rename from hello.pl
rename to hellohello.pl
  • ここで表示されている「similarity index」はファイルの内容がどれくらい似ているかを%表示しているものです。

git blame

  • 基本的な使い方「git blame <コミットオブジェクト> <パス>」
% git blame 3ab506b1 hello.pl

d43fc4de (koba04 2011-03-01 00:41:14 +0900  1) use strict;
d43fc4de (koba04 2011-03-01 00:41:14 +0900  2) use strict;
d43fc4de (koba04 2011-03-01 00:41:14 +0900  3) use warnings;
d43fc4de (koba04 2011-03-01 00:41:14 +0900  4) 
7a181f05 (koba04 2011-03-01 00:52:45 +0900  5) print "hello world\n";
  • こんな感じでファイル中のどの行がいつ誰に変更されたかがわかるようになります。コミットオブジェクトの所は「d43fc4de..7a181f05」などと範囲をして出力することも出来ます。
「-b」オプション
  • blameでは指定範囲の中で一番古いコミットにはオブジェクトの左に「^」が付くのですが、-bを付けるとそこが空欄になりより変更がわかりやすくなります。範囲指定と合わせて使用すると便利そうですね。
「-L」オプション
  • 表示する範囲を指定するためのオプションで、「-L 1,10」で1行目から10行目まで、「-L'/^use /',+2」でuseで始まる行から2行というような指定も可能です。
「-C」オプション
  • ここ色々試しては見たのですが、どういう動作をするかなど正直理解出来ていない部分も多いです。色々試しているのですが想定している動作にならなかったり。
  • 本から引用すると、「-C」が一つの場合は、

同じコミットで変更されたファイルの変更前の状態に、新しく加えられた行と同じような行がないか探す

「-C」が二つの場合(-C -Cってこと?)は

新しく加えられた行と同じような行を当該コミットでは変更されていないファイルからも探す

とあります。つまり「-C」が一つだけの場合は、Aというファイルからある部分をBというファイルに移動させてコミットしたときに検出してくれて、「-C」が二つの場合は、Aというファイルからある部分を削除せずにBというファイルにコピペしてコミットしたものを検出してくれる。検出されると移動以前のコミットによる変更履歴も表示してくれるものだと思っています。うまく動いていないのですが。。間違っていたらご指摘頂けると幸いです。

  • 試した手順
  • A.pmとb.plを作成してコミットする。
---------------------
package A;

# 特にコードに意味はない
sub hello {
    print "hello world";
}

sub add {
    my ($a, $b) = @_;
    return $a + $b;
}
1;
------------------------
b.pl

use A;
A::hello;
print A::add(1,2) . "\n"; 
  • 変更履歴を作るため、再度編集してコミット。
------------------
A.pm

print "hello world!!!!!!!";

sub sum {
------------------
b.pl

print A::sum(1,2) . "\n"; 
  • b.plにA.pmのhelloはコピー、sumは移動をそれぞれ行いコミットする。(コピーする方は、今回のコミットで変更されない別ファイルの内容の方がいいのかもしれません)
------------------
A.pm
package A; 
# 特にコードn意味はない 
sub hello { 
    print "hello world!!!!!!!!!\n"; 
} 
1; 
------------------
b.pl
hello(); 
print sum(1,2) . "\n"; 
 
sub hello { 
    print "hello world!!!!!!!!!\n"; 
} 
sub sum { 
    my ($a, $b) = @_; 
    return $a + $b; 
} 
  • これでA.pmのhelloはコピー、sumは移動になっているのでそれぞれ「-C」、「-C -C」で検出出来るのかなと思ったのですが、結局全て同じ出力に。
% git blame b.pl

c94af2ab (koba04 2011-03-06 02:43:48 +0900  1) hello();
c94af2ab (koba04 2011-03-06 02:43:48 +0900  2) print sum(1,2) . "\n";
c94af2ab (koba04 2011-03-06 02:43:48 +0900  3) 
c94af2ab (koba04 2011-03-06 02:43:48 +0900  4) sub hello {
c94af2ab (koba04 2011-03-06 02:43:48 +0900  5)     print "hello world!!!!!!!!!\n";
c94af2ab (koba04 2011-03-06 02:43:48 +0900  6) }
c94af2ab (koba04 2011-03-06 02:43:48 +0900  7) sub sum {
c94af2ab (koba04 2011-03-06 02:43:48 +0900  8)     my ($a, $b) = @_;
c94af2ab (koba04 2011-03-06 02:43:48 +0900  9)     return $a + $b;
c94af2ab (koba04 2011-03-06 02:43:48 +0900 10) }

% git blame -C b.pl

c94af2ab (koba04 2011-03-06 02:43:48 +0900  1) hello();
c94af2ab (koba04 2011-03-06 02:43:48 +0900  2) print sum(1,2) . "\n";
c94af2ab (koba04 2011-03-06 02:43:48 +0900  3) 
c94af2ab (koba04 2011-03-06 02:43:48 +0900  4) sub hello {
c94af2ab (koba04 2011-03-06 02:43:48 +0900  5)     print "hello world!!!!!!!!!\n";
c94af2ab (koba04 2011-03-06 02:43:48 +0900  6) }
c94af2ab (koba04 2011-03-06 02:43:48 +0900  7) sub sum {
c94af2ab (koba04 2011-03-06 02:43:48 +0900  8)     my ($a, $b) = @_;
c94af2ab (koba04 2011-03-06 02:43:48 +0900  9)     return $a + $b;
c94af2ab (koba04 2011-03-06 02:43:48 +0900 10) }

% git blame -C -C b.pl

c94af2ab (koba04 2011-03-06 02:43:48 +0900  1) hello();
c94af2ab (koba04 2011-03-06 02:43:48 +0900  2) print sum(1,2) . "\n";
c94af2ab (koba04 2011-03-06 02:43:48 +0900  3) 
c94af2ab (koba04 2011-03-06 02:43:48 +0900  4) sub hello {
c94af2ab (koba04 2011-03-06 02:43:48 +0900  5)     print "hello world!!!!!!!!!\n";
c94af2ab (koba04 2011-03-06 02:43:48 +0900  6) }
c94af2ab (koba04 2011-03-06 02:43:48 +0900  7) sub sum {
c94af2ab (koba04 2011-03-06 02:43:48 +0900  8)     my ($a, $b) = @_;
c94af2ab (koba04 2011-03-06 02:43:48 +0900  9)     return $a + $b;
c94af2ab (koba04 2011-03-06 02:43:48 +0900 10) }
  • これは要調査かなと思っています。実際のコードで使ってみたらわかるかも。


次の10章はパッチベースのワークフローということで、なんだかオープンソースプロジェクトな感じになってきます。