基本的な作業サイクル

Subversionはたくさんの機能、オプション、おまけが付いていますが、 日々の作業では、おそらくその中のいくつかを使うだけでしょう。 この章では一番よく起こることを説明します。

典型的な作業サイクルは次のようなものです:

作業コピーの更新

チームを作って作業してるプロジェクトでは、自分の作業コピーを 更新してプロジェクトの他のメンバーが自分の 更新処理後に加えた変更点をすべて受け取りたくなるでしょう。 svn updateを使って自分の作業コピーを リポジトリの最新バージョンにあわせてください。

$ svn update
U  foo.c
U  bar.c
リビジョン 2 に更新しました。

この場合、あなたが最後に更新してから、誰か別の人が foo.cbar.c の両方に加えた変更をコミットし、Subversionはこの変更をあなたの 作業コピーに加えるために更新しました。

When the server sends changes to your working copy via svn update, a letter code is displayed next to each item to let you know what actions Subversion performed to bring your working copy up-to-date. To find out what these letters mean, see svn update.

作業コピーに変更を加えること

Now you can get to work and make changes in your working copy. It's usually most convenient to decide on a discrete change (or set of changes) to make, such as writing a new feature, fixing a bug, etc. The Subversion commands that you will use here are svn add, svn delete, svn copy, svn move, and svn mkdir. However, if you are merely editing files that are already in Subversion, you may not need to use any of these commands until you commit.

There are two kinds of changes you can make to your working copy: file changes and tree changes. You don't need to tell Subversion that you intend to change a file; just make your changes using your text editor, word processor, graphics program, or whatever tool you would normally use. Subversion automatically detects which files have been changed, and in addition handles binary files just as easily as it handles text files—and just as efficiently too. For tree changes, you can ask Subversion to 「mark」 files and directories for scheduled removal, addition, copying, or moving. These changes may take place immediately in your working copy, but no additions or removals will happen in the repository until you commit them.

ここでは、Subversion でツリーの変更にもっともよく利用される、五つのサブコマンドを概観します。

svn add foo

Schedule file, directory, or symbolic link foo to be added to the repository. When you next commit, foo will become a child of its parent directory. Note that if foo is a directory, everything underneath foo will be scheduled for addition. If you only want to add foo itself, pass the --non-recursive (-N) option.

svn delete foo

Schedule file, directory, or symbolic link foo to be deleted from the repository. If foo is a file or link, it is immediately deleted from your working copy. If foo is a directory, it is not deleted, but Subversion schedules it for deletion. When you commit your changes, foo will be entirely removed from your working copy and the repository. [4]

svn copy foo bar

Create a new item bar as a duplicate of foo and automatically schedule bar for addition. When bar is added to the repository on the next commit, its copy history is recorded (as having originally come from foo). svn copy does not create intermediate directories.

svn move foo bar

This command is exactly the same as running svn copy foo bar; svn delete foo. That is, bar is scheduled for addition as a copy of foo, and foo is scheduled for removal. svn move does not create intermediate directories.

svn mkdir blort

This command is exactly the same as running mkdir blort; svn add blort. That is, a new directory named blort is created and scheduled for addition.

自分の変更点の確認

Once you've finished making changes, you need to commit them to the repository, but before you do so, it's usually a good idea to take a look at exactly what you've changed. By examining your changes before you commit, you can make a more accurate log message. You may also discover that you've inadvertently changed a file, and this gives you a chance to revert those changes before committing. Additionally, this is a good opportunity to review and scrutinize changes before publishing them. You can see an overview of the changes you've made by using svn status, and dig into the details of those changes by using svn diff.

Subversion has been optimized to help you with this task, and is able to do many things without communicating with the repository. In particular, your working copy contains a hidden cached 「pristine」 copy of each version controlled file within the .svn area. Because of this, Subversion can quickly show you how your working files have changed, or even allow you to undo your changes without contacting the repository.

自分がした変更の概要確認

To get an overview of your changes, you'll use the svn status command. You'll probably use svn status more than any other Subversion command.

If you run svn status at the top of your working copy with no arguments, it will detect all file and tree changes you've made. Below are a few examples of the most common status codes that svn status can return. (Note that the text following # is not actually printed by svn status.)

A       stuff/loot/bloo.h   # 追加準備されたファイル
C       stuff/loot/lump.c   # 更新して競合が発生したファイル
D       stuff/fish.c        # 削除準備されたファイル
M       bar.c               # bar.c にローカルで変更された内容

In this output format svn status prints six columns of characters, followed by several whitespace characters, followed by a file or directory name. The first column tells the status of a file or directory and/or its contents. The codes we listed are:

A の項目

通常ファイル、ディレクトリ、シンボリックリンクのいずれかである item はリポジトリに追加予告されています。

C の項目

ファイル item は競合の状態にあります。つまり、自分の作業コピーにあるローカルな変更が、更新時にサーバから受け取った変更部分と重なっています。リポジトリに自分の変更点をコミットする前に、この競合を解決しなくてはなりません。

D の項目

通常ファイル、ディレクトリ、シンボリックリンクのいずれかである item は リポジトリからの削除予告をされています。

M の項目

ファイルitemの内容は 修正されています。

If you pass a specific path to svn status, you get information about that item alone:

$ svn status stuff/fish.c
D      stuff/fish.c

svn status--verbose (-v) オプションがありますが、その場合、作業コピー中の すべての項目に対して、たとえ変更がなくても状態を表示するという意味になります。

$ svn status -v
M               44        23    sally     README
                44        30    sally     INSTALL
M               44        20    harry     bar.c
                44        18    ira       stuff
                44        35    harry     stuff/trout.c
D               44        19    ira       stuff/fish.c
                44        21    sally     stuff/things
A                0         ?     ?        stuff/things/bloo.h
                44        36    harry     stuff/things/gloo.c

This is the 「long form」 output of svn status. The letters in the first column mean the same as before, but the second column shows the working-revision of the item. The third and fourth columns show the revision in which the item last changed, and who changed it.

None of the prior invocations to svn status contact the repository—instead, they compare the metadata in the .svn directory with the working copy. Finally, there is the --show-updates (-u) option, which contacts the repository and adds information about things that are out-of-date:

$ svn status -u -v
M      *        44        23    sally     README
M               44        20    harry     bar.c
       *        44        35    harry     stuff/trout.c
D               44        19    ira       stuff/fish.c
A                0         ?     ?        stuff/things/bloo.h
状態の背景となるリビジョン:   46

二つのアスタリスク ('*') に注意してください:この状態で svn update を実行すると READMEtrout.c の変更点を受け取ることになります。 これは非常に役に立つ情報です—コミットする前には更新して README に関するサーバ上の変更点を取得 しなくてはなりません。さもなければ、最新でないという理由で コミットは失敗するでしょう(詳しくは後で述べます)。

svn status can display much more information about the files and directories in your working copy than we've shown here—for an exhaustive description of svn status and its output, see svn status.

Examine the details of your local modifications

Another way to examine your changes is with the svn diff command. You can find out exactly how you've modified things by running svn diff with no arguments, which prints out file changes in unified diff format:

$ svn diff
Index: bar.c
===================================================================
--- bar.c        (リビジョン 3)
+++ bar.c        (作業コピー)
@@ -1,7 +1,12 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <stdio.h>

 int main(void) {
-  printf("Sixty-four slices of American Cheese...\n");
+  printf("Sixty-five slices of American Cheese...\n");
 return 0;
 }

Index: README
===================================================================
--- README        (リビジョン 3)
+++ README        (作業コピー)
@@ -193,3 +193,4 @@
+Note to self:  pick up laundry.

Index: stuff/fish.c
===================================================================
--- stuff/fish.c        (リビジョン 1)
+++ stuff/fish.c        (作業コピー)
-Welcome to the file known as 'fish'.
-Information on fish will be here soon.

Index: stuff/things/bloo.h
===================================================================
--- stuff/things/bloo.h        (リビジョン 8)
+++ stuff/things/bloo.h        (作業コピー)
+Here is a new file to describe
+things about bloo.

svn diff コマンドは.svn 領域 にある、「修正元リビジョン」 のコピーに対して作業コピー 中のファイルを比較した結果を出力します。 追加予告ファイルはすべて追加されたテキストとして表示され 削除予告されているファイルはすべて削除されたファイルとして表示 されます。

Output is displayed in unified diff format. That is, removed lines are prefaced with - and added lines are prefaced with +. svn diff also prints filename and offset information useful to the patch program, so you can generate 「patches」 by redirecting the diff output to a file:

$ svn diff > patchfile

たとえば、パッチファイルを別の開発者に送り、コミット前に再検討や テストをすることができます。

Subversion uses its internal diff engine, which produces unified diff format, by default. If you want diff output in a different format, specify an external diff program using --diff-cmd and pass any flags you'd like to it using the --extensions (-x) option. For example, to see local differences in file foo.c in context output format while ignoring case differences, you might run svn diff --diff-cmd /usr/bin/diff --extensions '-i' foo.c.

変更の取り消し

Suppose while viewing the output of svn diff you determine that all the changes you made to a particular file are mistakes. Maybe you shouldn't have changed the file at all, or perhaps it would be easier to make different changes starting from scratch.

This is a perfect opportunity to use svn revert:

$ svn revert README
'README' を元に戻しました

Subversion はそのファイルを.svn 領域に ある「修正元リビジョン」のコピーを上書きすることに よって、修正以前の状態に戻します。 しかし、svn revertどのような 予告操作も取り消すことができるのに注意してください—たとえば 最終的に新しいファイルを追加することをやめることができます:

$ svn status foo
?      foo

$ svn add foo
A         foo

$ svn revert foo
'foo' を元に戻しました

$ svn status foo
?      foo

注意

svn revert ITEM は、作業コピーから ITEMを削除し、それからsvn update -r BASE ITEMを実行したのとまったく同じ効果があります。 しかし、もしファイルをもとに戻そうとしているのなら、 svn revertには一つ重要な違いがあります—それはファイル を元に戻すにあたってリポジトリと通信する必要がないのです。

あるいは間違ってバージョン管理からファイルを消して しまったのかも知れません:

$ svn status README
       README

$ svn delete README
D         README

$ svn revert README
'README' を元に戻しました

$ svn status README
       README

競合の解消 (他の人の変更点のマージ)

We've already seen how svn status -u can predict conflicts. Suppose you run svn update and some interesting things occur:

$ svn update
U  INSTALL
G  README
C  bar.c
リビジョン 46 に更新しました。

UG のコードは考える ことはありません。この二つはリポジトリからの変更を きれいに吸収することができました。 U でマークされたファイルは ローカルでは何の変更もありませんでしたが、リポジトリからの 修正分で更新(Updated)されました。 Gはマージ( merGed)されたことを意味して いますが、これは、ファイルはローカルで変更されていたが、 リポジトリからの変更部分とまったく重ならなかったことを意味 しています。

But the C stands for conflict. This means that the changes from the server overlapped with your own, and now you have to manually choose between them.

競合が起こると、普通はその競合を知らせて解決することができるように、以下の三つのことが起こります。

  • Subversion は更新処理中に C を 表示して、そのファイルが競合していることを知らせます。

  • Subversion は競合マーカ — 競合を起こした「両方」の内容を区切る特別なテキスト文字列のこと — を重なっている場所に置き、競合内容を見てわかるように します。

  • 競合を起こしたファイルのそれぞれについて、Subversion は、次のようなバージョン管理対象にならない特殊なファイルを三つ、作業コピーに置きます。

    filename.mine

    This is your file as it existed in your working copy before you updated your working copy—that is, without conflict markers. This file has only your latest changes in it. (If Subversion considers the file to be unmergeable, then the .mine file isn't created, since it would be identical to the working file.)

    filename.rOLDREV

    これは、作業コピーを更新する前のBASEリビジョンにあったファイル の内容です。つまり、そのファイルは最後にした編集の直前にした チェックアウト時点でのファイルです。

    filename.rNEWREV

    これは Subversionクライアントプログラムが作業コピーを更新したときに サーバから受け取ったファイルです。これは、リポジトリのHEAD リビジョンに対応しています。

    ここで OLDREV.svn ディレクトリにあるファイルのリビジョン番号で、NEWREVHEAD リポジトリのリビジョン番号です。

たとえば Sally がリポジトリにあるsandwich.txt に変更を加えるとします。たった今、Harryは自分の作業コピーのそのファイルを変更 してコミットしました。Sally は自分が加えた変更をコミットする前に 作業コピーを更新しますが、そのとき競合の報告を受けます:

$ svn update
C  sandwich.txt
リビジョン 2 に更新しました。
$ ls -1
sandwich.txt
sandwich.txt.mine
sandwich.txt.r1
sandwich.txt.r2

このときSubversionは三つの一時ファイルが削除されるまで sandwich.txtのコミットを許可 しません

$ svn commit -m "Add a few more things"
svn: コミットに失敗しました (詳しい理由は以下のとおりです):
svn: コミットを中断しています: '/home/sally/svn-work/sandwich.txt' がまだ競合しています

もし競合があった場合は、以下の三つのうちのどれかをする必要があります。

  • 手で」 (ファイル中の競合マーカを調べ編集して) 競合テキストをマージします。

  • 作業ファイルに、一時ファイルのどれかを上書きします。

  • svn revert <filename>を 実行して、ローカルでしたすべての変更を捨てます。

ひとたび競合を解消したら、svn resolved を実行してSubversion にそのことを伝えます。これは三つの一時ファイルを削除して、Subversionはもうそのファイルが競合状態にあるとは考えなくなります。[6]

$ svn resolved sandwich.txt
'sandwich.txt' の競合状態を解消しました

競合を手でマージ

手で競合をマージするのは最初とても嫌なものですが、 少し練習すれば自転車から降りるのと同じくらい簡単にできるようになります。

Here's an example. Due to a miscommunication, you and Sally, your collaborator, both edit the file sandwich.txt at the same time. Sally commits her changes, and when you go to update your working copy, you get a conflict and you're going to have to edit sandwich.txt to resolve the conflicts. First, let's take a look at the file:

$ cat sandwich.txt
Top piece of bread
Mayonnaise
Lettuce
Tomato
Provolone
<<<<<<< .mine
Salami
Mortadella
Prosciutto
=======
Sauerkraut
Grilled Chicken
>>>>>>> .r2
Creole Mustard
Bottom piece of bread

小なり記号の文字列、イコールサイン、そして 大なり記号の文字列を競合マーカと呼びますが、これは実際の競合を起こしたデータの一部ではありません。一般的には次のコミットの前に取り除く必要があります。次の最初の二つのマーカの間のテキストは競合領域にあなた自身がした変更です。

<<<<<<< .mine
Salami
Mortadella
Prosciutto
=======

次の二番目と三番目の競合マーカの間のテキストは、 Sally のコミットからのテキストです。

=======
Sauerkraut
Grilled Chicken
>>>>>>> .r2

Usually you won't want to just delete the conflict markers and Sally's changes—she's going to be awfully surprised when the sandwich arrives and it's not what she wanted. So this is where you pick up the phone or walk across the office and explain to Sally that you can't get sauerkraut from an Italian deli.[7] Once you've agreed on the changes you will check in, edit your file and remove the conflict markers.

Top piece of bread
Mayonnaise
Lettuce
Tomato
Provolone
Salami
Mortadella
Prosciutto
Creole Mustard
Bottom piece of bread

これで、svn resolved を実行し 自分の変更をコミットする用意ができました:

$ svn resolved sandwich.txt
$ svn commit -m "Go ahead and use my sandwich, discarding Sally's edits."

Note that svn resolved, unlike most of the other commands we deal with in this chapter, requires an argument. In any case, you want to be careful and only run svn resolved when you're certain that you've fixed the conflict in your file—once the temporary files are removed, Subversion will let you commit the file even if it still contains conflict markers.

If you ever get confused while editing the conflicted file, you can always consult the three files that Subversion creates for you in your working copy—including your file as it was before you updated. You can even use a third-party interactive merging tool to examine those three files.

作業ファイルの上にファイルをコピーすること

競合が起こり、自分のした変更を捨てる場合には、以下のように Subversion が作った一時ファイルのどれかを単に作業コピー上に上書きすることができます。

$ svn update
C  sandwich.txt
リビジョン 2 に更新しました。
$ ls sandwich.*
sandwich.txt  sandwich.txt.mine  sandwich.txt.r2  sandwich.txt.r1
$ cp sandwich.txt.r2 sandwich.txt
$ svn resolved sandwich.txt

Punting: svn revertの利用

競合が起こり、調査の結果、自分の変更を捨てて編集をやり直す場合は、以下のように単に変更を revert することができます。

$ svn revert sandwich.txt
Reverted 'sandwich.txt'
$ ls sandwich.*
sandwich.txt

競合ファイルを元に戻すときは svn resolved を実行する必要はないことに注意してください。

変更点のコミット

やっとここまできました。編集は終了し、サーバからの変更を すべてマージしました。これで自分の変更をリポジトリにコミット する準被ウ器コできました。

svn commit コマンドは自分の変更点の すべてをリポジトリに送ります。変更をコミットするときには 変更点を説明するログメッセージを 与えてやる必要があります。ログメッセージは自分が作った 新しいリビジョンに付けられます。ログメッセージが簡単な 場合は--message (あるいは-m) オプションを使ってコマンドライン上で指定することができます:

$ svn commit -m "Corrected number of cheese slices."
送信しています              sandwich.txt
ファイルのデータを送信しています .
リビジョン 3 をコミットしました。

However, if you've been composing your log message as you work, you may want to tell Subversion to get the message from a file by passing the filename with the --file (-F) option:

$ svn commit -F logmsg
送信しています              sandwich.txt
ファイルのデータを送信しています .
リビジョン 4 をコミットしました。

If you fail to specify either the --message or --file option, then Subversion will automatically launch your favorite editor (see the editor-cmd section in config項) for composing a log message.

ティップ

If you're in your editor writing a commit message and decide that you want to cancel your commit, you can just quit your editor without saving changes. If you've already saved your commit message, simply delete the text, save again, then abort.

$ svn commit
Waiting for Emacs...Done

Log message unchanged or not specified
a)bort, c)ontinue, e)dit
a
$

リポジトリは、変更点の内容に意味があるかどうかはまったく 気にしません。Subversionはあなたが見ていないところで、同じファイルに 他の人が修正していないことだけを確認します。もし他の人がそのような 変更を していたら 、コミットはあなたの変更 したファイルのどれかが最新ではないというメッセージを出して失敗します:

$ svn commit -m "Add another rule"
Sending        rules.txt
svn: Commit failed (details follow):
svn: Your file or directory 'sandwich.txt' is probably out-of-date
…

(The exact wording of this error message depends on the network protocol and server you're using, but the idea is the same in all cases.)

このような場合は、svn updateを実行し その結果のマージや競合を解消し、もう一度コミットしてください。

That covers the basic work cycle for using Subversion. There are many other features in Subversion that you can use to manage your repository and working copy, but most of your day-to-day use of Subversion will involve only the commands that we've discussed so far in this chapter. We will, however, cover a few more commands that you'll use fairly often.



[4] Of course, nothing is ever totally deleted from the repository—just from the HEAD of the repository. You can get back anything you delete by checking out (or updating your working copy to) a revision earlier than the one in which you deleted it. Also see 削除されたアイテムの復活項.

[5] And also that you don't have a WAN card. Thought you got us, huh?

[6] 一時ファイルはいつでも自分で削除することができますが、Subversion がせっかくコマンドを用意しているのに本当にそうしたいのでしょうか? そうは思えませんが。

[7] And if you ask them for it, they may very well ride you out of town on a rail.