ノード:Finding Out What You (And Others) Did -- update And diff, 次:, 前:Making A Change, 上:A Day With CVS



Finding Out What You (And Others) Did - update And diff

前に、リポジトリから作業コピーへ変更を持ってくる方法として、アップデート のことを述べました。これは他の人の変更を取得する方法です。でもアップデー トというのは本当はもう少し複雑なことをしています: 作業コピーの状態全てを、 リポジトリ内のプロジェクトの状態と比較します。チェックアウト時以降、リポ ジトリに何も変更がなくても作業コピーが変更されていれば、アップデートはそ れも表示します:

floss$ cvs update
cvs update: Updating .
M hello.c
cvs update: Updating a-subdir
cvs update: Updating a-subdir/subsubdir
cvs update: Updating b-subdir

hello.c の隣にある「M」は、最後のチェックアウト以降このファイルが変更さ れました、そしてその変更はまだリポジトリへはコミットされていません、とい う意味です。

どのファイルを編集したんだったかをただ知りたいなと思うだけのこともあるで しょう。しかしどんな変更を施したのか詳しく見たいときには、diff 形式のフ ルレポートを取得することもできます。diff コマンドは作業ファイル中の変更 されたであろうファイルと、対応するリポジトリ中のファイルを比較し、全ての 相違を表示します:

floss$ cvs diff
cvs diff: Diffing .
Index: hello.c
===================================================================
RCS file: /usr/local/cvs/myproj/hello.c,v
retrieving revision 1.1.1.1
diff -r1.1.1.1 hello.c
6a7
>   printf ("Goodbye, world!\n");
cvs diff: Diffing a-subdir
cvs diff: Diffing a-subdir/subsubdir
cvs diff: Diffing b-subdir

That's helpful, if a bit obscure, but there's still a lot of cruft in the output. ビギナーの人は最初の数行は無視して構いません。リポジトリ内のファ イル名と、最後にチェックインされたリビジョンの番号が書かれています。 他 の状況では役に立つ情報なんですが(あとで少し詳しく見ていきます)、作業コピー にどんな変更があったかを知りたいだけなら必要のないものです。

diff を読む時にもっと障害になっているのは、CVS がアップデート中に各ディ レクトリに入ったことを知らせている部分です。そのコマンドがどのくらい長く かかったかわかるので、大きいプロジェクトの長いアップデートでなら役に立ち ますが、今回の場合、ただ diff を読みにくくしているだけです。-Q グローバ ルオプションで CVS に静かに仕事しろと言ってみましょう。

floss$ cvs -Q diff
Index: hello.c
===================================================================
RCS file: /usr/local/cvs/myproj/hello.c,v
retrieving revision 1.1.1.1
diff -r1.1.1.1 hello.c
6a7
>   printf ("Goodbye, world!\n");

いいカンジ、少なくとも cruft はいくつかなくなりました。でも、この diff はまだ見にくいですね。6行目に新しい行が追加されて(7行目になって)、内容は 次のようです:

printf ("Goodbye, world!\n");

diff の最初の「>」は、この行は新しいほうのバージョンにあって、古いほうに はない、ということを示します。

でも、このフォーマットはもう少し読みやすいようにできるんじゃないでしょう か。多くの人はコンテキスト diff のほうが読みやすいというのを知っていると 思います。あれは変更の周りの文脈を数行示してくれますからね。コンテキスト diff は diff コマンドに -c フラグを渡せば生成できます:

floss$ cvs -Q diff -c
Index: hello.c
===================================================================
RCS file: /usr/local/cvs/myproj/hello.c,v
retrieving revision 1.1.1.1
diff -c -r1.1.1.1 hello.c
*** hello.c     1999/04/18 18:18:22     1.1.1.1
--- hello.c     1999/04/19 02:17:07
***************
*** 4,7 ****
---4,8 --
  main ()
  {
    printf ("Hello, world!\n");
+   printf ("Goodbye, world!\n");
  }

やっと分かり易くなった! コンテキスト diff を読み慣れていなくてもこの出力 を見れば一目で何が起こったか分かると思います。新しい行が(最初の行の + は 追加行を示します)、Hello, world! と最後の中括弧の間に追加されたのです。

コンテキスト diff を完璧に読みこなす必要はありませんが(それは patch コマ ンドがやることです)、そのフォーマットにちょっと親しむだけの時間を取ったっ て少なくとも損はないでしょう。cruft は飛ばして、最初の2行は

*** hello.c     1999/04/18 18:18:22     1.1.1.1
--- hello.c     1999/04/19 02:17:07

何と何の diff を取ったかを書いてあります。この場合は hello.c のリビジョ ン 1.1.1.1 と、同じファイルの変更されたバージョンです(2行目のほうにはリ ビジョン番号はありませんが、これは作業ファイルだけに施された変更であって リポジトリにはまだコミットされていないからです) 。これ以降 diff 内に出て くるアスタリスクとダッシュの行はセクションを識別しています。行番号範囲を 埋め込んであるアスタリスクの行はオリジナルファイルのセクションを示します。 ダッシュの行、さっきとは違う行範囲が埋め込んであると思いますが、これは変 更されたファイルのセクションを示します。これらのセクションは対比されて 「hunk」というペアになり、一方は古いファイル、他方は新しいファイルになり ます。

今回の diff には hunk がひとつだけあります:

***************
*** 4,7 ****
--- 4,8 --
  main ()
  {
    printf ("Hello, world!\n");
+   printf ("Goodbye, world!\n");
  }

hunk の最初のセクションは空で、オリジナルのファイルからは何も削除されて いないことを意味します。2番目のセクションは、新しいファイルの対応する場 所に1行追加されたことを示します。「+」という印がつけてあります。(diff が ファイルから抜粋をする時は、最初の2カラムは「+」とかの特別なコードのため に空けてあります。そのため、ただ抜粋しているだけの行は空白2つでインデン トされているように見えます。この余分なインデントは diff が適用される時に は削除されます、当たり前ですけど)

行番号範囲は、その hunk がカバーする範囲です(コンテキストを示す行を含む)。 オリジナルファイルではその hunk は4行目から7行目までだったのに対し、新し いファイルでは4行目から8行目になっています(1行追加されましたからね)。オ リジナルファイルから何も削除されていない場合、diff はオリジナルファイル の行を出力する必要がないことに注意して下さい。行範囲と hunk の後半からわ かることです。

わたしの実際のプロジェクトから、他のコンテキスト diff をお見せしましょう:

floss$ cvs -Q diff -c
Index: cvs2cl.pl
===================================================================
RCS file: /usr/local/cvs/kfogel/code/cvs2cl/cvs2cl.pl,v
retrieving revision 1.76
diff -c -r1.76 cvs2cl.pl
*** cvs2cl.pl   1999/04/13 22:29:44     1.76
--- cvs2cl.pl   1999/04/19 05:41:37
***************
*** 212,218 ****
          # can contain uppercase and lowercase letters, digits, '-',
          # and '_'. However, it's not our place to enforce that, so
          # we'll allow anything CVS hands us to be a tag:
!         /^\s([^:]+): ([0=9.]+)$/;
          push (@{$symbolic_names{$2}}, $1);
        }
      }
-- 212,218 --
          # can contain uppercase and lowercase letters, digits, '-',
          # and '_'. However, it's not our place to enforce that, so
          # we'll allow anything CVS hands us to be a tag:
!         /^\s([^:]+): ([\d.]+)$/;
          push (@{$symbolic_names{$2}}, $1);
        }
      }

びっくりマーク(「!」)は、その行が古いファイルと新しいファイルで違うこと を示します。「+」も「-」もないことから、ファイルの行数は変わらなかったこ とが分かります。

同じプロジェクトからもう一つ別のコンテキスト diff を。今回はもう少し複雑 です:

floss$ cvs -Q diff -c
Index: cvs2cl.pl
===================================================================
RCS file: /usr/local/cvs/kfogel/code/cvs2cl/cvs2cl.pl,v
retrieving revision 1.76
diff -c -r1.76 cvs2cl.pl
*** cvs2cl.pl   1999/04/13 22:29:44     1.76
--- cvs2cl.pl   1999/04/19 05:58:51
***************
*** 207,217 ****
}
        else    # we're looking at a tag name, so parse & store it
        {
-         # According to the Cederqvist manual, in node "Tags", "Tag
-         # names must start with an uppercase or lowercase letter and
-         # can contain uppercase and lowercase letters, digits, '-',
-         # and '_'. However, it's not our place to enforce that, so
-         # we'll allow anything CVS hands us to be a tag:
          /^\s([^:]+): ([0-9.]+)$/;
          push (@{$symbolic_names{$2}}, $1);
        }
- 207,212 --
***************
*** 223,228 ****
--- 218,225 --
      if (/^revision (\d\.[0-9.]+)$/) {
        $revision = "$1";
      }
+
+     # This line was added, I admit, solely for the sake of a diff example.

      # If have file name but not time and author, and see date or
      # author, then grab them:

この diff には hunk が2つあります。最初のやつは5行削除です(これらの行は hunk の最初のセクションだけに示されていて、2番目のセクションの行番号は5 行少なくなっています)。途切れていないアスタリスクの行は hunk の区切りで、 2番目の hunk では2行追加されたことが分かります。空行ひとつと無意味なコメ ントが1行ですね。一つ前の hunk を受けて行番号がどう変わっているか、注意 して下さい。オリジナルファイルにおいては2番目の hunk は223行目から228行 目、最初の hunk で5行削除されたので新しいファイルでは218から225行目になっ ています。

おめでとう、これでもうあなたも diff を読むことにかけてはエキスパートです ね。