基本的工作循環

Subversion有許多特性、選項和華而不實的高級功能,但日常的工作中你只使用其中的一小部分,在這一節裡,我們會介紹許多你在日常工作中常用的命令。

典型的工作週期是這樣的:

更新你的工作副本

當你在一個團隊的項目裡工作時,你希望更新你的工作副本得到所有其他人這段時間作出的修改,使用svn update讓你的工作副本與最新的版本同步。

$ svn update
U  foo.c
U  bar.c
Updated to revision 2.

這種情況下,其他人在你上次更新之後提交了對foo.cbar.c的修改,因此Subversion更新你的工作副本來引入這些更改。

當伺服器通過svn update將修改傳遞到你的工作副本時,每一個項目之前會有一個字母,來讓你知道Subversion為保持最新對你的工作副本作了哪些工作。關於這些字母的詳細含義,可以看svn update

修改你的工作副本

現在你可以開始工作並且修改你的工作副本了,你很容易決定作出一個修改(或者是一組),像寫一個新的特性,修正一個錯誤等等。這時可以使用的Subversion命令包括svn addsvn deletesvn copysvn move。如果你只是修改版本庫中已經存在的文件,在你提交之前,不必使用上面的任何一個命令。

你可以對工作副本做出兩種修改:文件修改和目錄樹修改。你不需要告訴Subversion你希望修改一個文件,只需要用你的編輯器、字處理器、圖形程序或任何工具做出修改,Subversion自動監測到文件的更改,此外,二進制文件的處理方式和文字文件一樣—也有同樣的效率。對於目錄樹更改,你可以告訴Subversion將文件和目錄預定的刪除、新增、拷貝或移動標記,這些動作會在工作副本上立刻發生效果,但只有提交後才會在版本庫裡生效。

下面是Subversion用來修改目錄樹結構的五個子命令。

svn add foo

預定將文件、目錄或者符號鏈foo新增到版本庫,當你下次提交後,foo會成為其父目錄的一個子對象。注意,如果foo是目錄,所有foo中的內容也會預定新增進去,如果你只想新增foo本身,請使用--non-recursive (-N)參數。

svn delete foo

預定將文件、目錄或者符號鏈foo從版本庫中刪除,如果foo是文件,它馬上從工作副本中刪除,如果是目錄,不會被刪除,但是Subversion準備好刪除了,當你提交你的修改,foo就會在你的工作副本和版本庫中被刪除。[4]

svn copy foo bar

建立一個新的項目bar作為foo的複製品,會自動預定將bar新增,當在下次提交時會將bar新增到版本庫,這種拷貝歷史會記錄下來(按照來自foo的方式記錄),svn copy並不建立中介目錄。

svn move foo bar

這個命令與與運行svn copy foo bar;svn delete foo完全相同,bar作為foo的拷貝準備新增,foo已經預定被刪除,svn move不建立中介的目錄。

svn mkdir blort

這個命令同運行 mkdir blort; svn add blort相同,也就是建立一個叫做blort的文件,並且預定新增到版本庫。

檢查你的修改

當你完成修改,你需要提交他們到版本庫,但是在此之前,檢查一下做過什麼修改是個好主意,通過提交前的檢查,你可以整理一份精確的日誌訊息,你也可以發現你不小心修改的文件,給了你一次恢復修改的機會。此外,這是一個審查和仔細察看修改的好機會,你可通過命令svn status瀏覽所做的修改,通過svn diff檢查修改的詳細訊息。

Subversion已經被優化來幫助你完成這個任務,可以在不與版本庫通訊的情況下做許多事情,詳細來說,對於每一個文件,你的的工作副本在.svn包含了一個「原始的」拷貝,所以Subversion可以快速的告訴你那些文件修改了,甚至允許你在不與版本庫通訊的情況下恢復修改。

查看你的修改概況

為了瀏覽修改的內容,你會使用這個svn status命令,在所有Subversion命令裡,svn status可能會是你用的最多的命令。

如果你在工作副本的最上層目錄運行不帶參數的svn status命令,它會檢測你做的所有的文件或目錄的修改,以下的例子是來展示svn status可能返回的狀態碼(注意,#之後的不是svn status打印的)。

A       stuff/loot/bloo.h   # file is scheduled for addition
C       stuff/loot/lump.c   # file has textual conflicts from an update
D       stuff/fish.c        # file is scheduled for deletion
M       bar.c               # the content in bar.c has local modifications

在這種格式下,svn status打印6列字元,緊跟一些空格,接著是文件或者目錄名。第一列告訴一個文件或目錄的狀態或它的內容,返回代碼如下:

A item

預定加入到版本庫的文件、目錄或符號鏈的item

C item

文件item發生衝突,在從伺服器更新時與本地版本發生交迭,在你提交到版本庫前,必須手工的解決衝突。

D item

文件、目錄或是符號鏈item預定從版本庫中刪除。

M item

文件item的內容被修改了。

如果你傳遞一個路徑給svn status,它只給你這個項目的訊息:

$ 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

這是svn status的「加長形式」,第一列保持相同,第二列顯示一個工作版本號,第三和第四列顯示最後一次修改的版本號和修改人(這些列不會與我們剛才提到的字元混淆)。

上面所有的svn status調用並沒有聯繫版本庫,只是與.svn中的原始資料進行比較的結果,最後,是--show-updates (-u)選項,它將會聯繫版本庫為已經過時的資料新增新訊息:

$ 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
Status against revision:   46

注意這兩個星號:如果你現在執行svn update,你的READMEtrout.c會被更新,這告訴你許多有用的訊息—你可以在提交之前,需要使用更新操作得到文件README的更新,或者說文件已經過時,版本庫會拒絕了你的提交。(後面還有更多關於此主題)。

關於文件和目錄,svn status可以比我們的展示顯示更多的內容,完整的描述可以看svn status

檢查你的本地修改的詳情

另一種檢查修改的方式是svn diff命令,你可以通過不帶參數的svn diff精確的找出你所做的修改,這會輸出統一區別格式的區別訊息:

$ svn diff
Index: bar.c
===================================================================
--- bar.c	(revision 3)
+++ bar.c	(working copy)
@@ -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	(revision 3)
+++ README	(working copy)
@@ -193,3 +193,4 @@
+Note to self:  pick up laundry.

Index: stuff/fish.c
===================================================================
--- stuff/fish.c	(revision 1)
+++ stuff/fish.c	(working copy)
-Welcome to the file known as 'fish'.
-Information on fish will be here soon.

Index: stuff/things/bloo.h
===================================================================
--- stuff/things/bloo.h	(revision 8)
+++ stuff/things/bloo.h	(working copy)
+Here is a new file to describe
+things about bloo.

svn diff命令通過比較你的文件和.svn的「原始」文件來輸出訊息,預定要增加的文件會顯示所有增加的文字,要刪除的文件會顯示所有要刪除的文字。

輸出的格式為統一區別格式(unified diff format),刪除的行前面加一個-,新增的行前面有一個+svn diff命令也打印文件名和打補綴(Patch)需要的訊息,所以你可以通過重定向一個區別文件來生成「補綴(Patch)」:

$ svn diff > patchfile

舉個例子,你可以把補綴(Patch)文件發送郵件到其他開發者,在提交之前審核和測試。

Subversion使用內建區別引擎,預設情況下輸出為統一區別格式。如果你期望不同的輸出格式,你可以使用--diff-cmd指定外部(External)的區別程序,並且通過--extensions傳遞其他參數,舉個例子,察看本地文件foo.c的區別,同時忽略大小寫差異,你可以運行svn diff --diff-cmd /usr/bin/diff --extensions '-bc' foo.c

取消本地修改

假定我們在看svn diff的輸出,你發現對某個文件的所有修改都是錯誤的,或許你根本不應該修改這個文件,或者是從開頭重新修改會更加容易。

這是使用svn revert的好機會:

$ svn revert README
Reverted 'README'

Subversion把文件恢復到未修改的狀態,叫做.svn目錄的「原始」拷貝,應該知道svn revert可以恢復任何預定要做的操作,舉個例子,你不再想新增一個文件:

$ svn status foo
?      foo

$ svn add foo
A         foo

$ svn revert foo
Reverted 'foo'

$ svn status foo
?      foo

注意

svn revertITEM的效果與刪除ITEM然後執行svn update -r BASEITEM完全一樣,但是,如果你使用svn revert它不必通知版本庫就可以恢復文件。

或許你不小心刪除了一個文件:

$ svn status README
       README

$ svn delete README
D         README

$ svn revert README
Reverted 'README'

$ svn status README
       README

解決衝突(合併別人的修改)

我們可以使用svn status -u來預測衝突,當你運行svn update一些有趣的事情發生了:

$ svn update
U  INSTALL
G  README
C  bar.c
Updated to revision 46.

UG沒必要關心,文件乾淨的接受了版本庫的變化,文件標示為U表明本地沒有修改,文件已經根據版本庫更新。G標示合併,標示本地已經修改過,與版本庫沒有重迭的地方,已經合併。

但是C表示衝突,說明伺服器上的改動同你的改動衝突了,你需要自己手工去解決。

當衝突發生了,有三件事可以幫助你注意到這種情況和解決問題:

  • Subversion在更新時打印C標記,並且標記這個文件已衝突。

  • 如果Subversion認為這個文件是可合併的,它會置入衝突標記—特殊的橫線分開衝突的「兩面」—在文件裡可視化的描述重疊的部分(Subversion使用svn:mime-type屬性來決定一個文件是否可以使用上下文的,以行為基礎的合併,更多訊息可以看「文件內容類型」一節。)

  • 對於每一個衝突的文件,Subversion放置三個額外的未版本化文件到你的工作副本:

    filename.mine

    你更新前的文件,沒有衝突標誌,只是你最新更改的內容。(如果Subversion認為這個文件不可以合併,.mine文件不會建立,因為它和工作文件相同。)

    filename.rOLDREV

    這是你的做更新操作以前的BASE版本文件,就是你在上次更新之後未作更改的版本。

    filename.rNEWREV

    這是你的Subversion客戶端從伺服器剛剛收到的版本,這個文件對應版本庫的HEAD版本。

    這裡OLDREV是你的.svn目錄中的修訂版本號,NEWREV是版本庫中HEAD的版本號。

舉一個例子,Sally修改了sandwich.txt,Harry剛剛改變了他的本地拷貝中的這個文件並且提交到伺服器,Sally在提交之前更新它的工作副本得到了衝突:

$ svn update
C  sandwich.txt
Updated to revision 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: Commit failed (details follow):
svn: Aborting commit: '/home/sally/svn-work/sandwich.txt' remains in conflict

如果你遇到衝突,三件事你可以選擇:

  • 手動」合併衝突文字(檢查和修改文件中的衝突標誌)。

  • 用某一個臨時文件覆蓋你的工作文件。

  • 運行svn revert <filename>來放棄所有的本地修改。

一旦你解決了衝突,你需要通過命令svn resolved讓Subversion知道,這樣就會刪除三個臨時文件,Subversion就不會認為這個文件是在衝突狀態了。[6]

$ svn resolved sandwich.txt
Resolved conflicted state of 'sandwich.txt'

手工合併衝突

第一次嘗試解決衝突讓人感覺很害怕,但經過一點訓練,它簡單的像是騎著車子下坡。

這裡一個簡單的例子,由於不良的交流,你和同事Sally,同時編輯了sandwich.txt。Sally提交了修改,當你準備更新你的工作副本,衝突發生了,我們不得不去修改sandwich.txt來解決這個問題。首先,看一下這個文件:

$ 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

通常你並不希望只是刪除衝突標誌和Sally的修改—當她收到三明治時,會非常的吃驚。所以你應該走到她的辦公室或是拿起電話告訴Sally,你沒辦法從從意大利熟食店得到想要的泡菜。[7]一旦你們確認了提交內容後,修改文件並且刪除衝突標誌。

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."

現在我們準備好提交修改了,注意svn resolved不像我們本章學過的其他命令一樣需要參數,在任何你認為解決了衝突的時候,只需要小心運行svn resolved,—一旦刪除了臨時文件,Subversion會讓你提交這文件,即使文件中還存在衝突標記。

記住,如果你修改衝突時感到混亂,你可以參考subversion生成的三個文件—包括你未作更新的文件。你也可以使用三方交互合併工具檢驗這三個文件。

複製文件到你的工作文件

如果你只是希望取消你的修改,你可以僅僅拷貝Subversion為你生成的文件替換你的工作副本:

$ svn update
C  sandwich.txt
Updated to revision 2.
$ ls sandwich.*
sandwich.txt  sandwich.txt.mine  sandwich.txt.r2  sandwich.txt.r1
$ cp sandwich.txt.r2 sandwich.txt
$ svn resolved sandwich.txt

腳註:使用svn 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."
Sending        sandwich.txt
Transmitting file data .
Committed revision 3.

然而,如果你把寫日誌訊息當作工作的一部分,你也許會希望告訴Subversion通過一個文件名得到日誌訊息,使用--file選項:

$ svn commit -F logmsg
Sending        sandwich.txt
Transmitting file data .
Committed revision 4.

如果你沒有指定--message或者--file選項,Subversion會自動地啟動你最喜歡的編輯器(見「設定」一節editor-cmd部分)來編輯日誌訊息。

提示

如果你使用編輯器撰寫日誌訊息時希望取消提交,你可以直接關掉編輯器,不要保存,如果你已經做過保存,只要簡單的刪掉所有的文字並再次保存,然後退出。

$ svn commit
Waiting for Emacs...Done

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

版本庫不知道也不關心你的修改作為一個整體是否有意義,它只檢查是否有其他人修改了同一個文件,如果別人已經這樣做了,你的整個提交會失敗,並且提示你一個或多個文件已經過時了:

$ 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
…

(錯誤訊息的精確措辭依賴於網路協議和你使用的伺服器,但對於所有的情況,其思想完全一樣。)

此刻,你需要運行svn update來處理所有的合併和衝突,然後再嘗試提交。

我們已經覆蓋了Subversion基本的工作週期,還有許多其它特性可以管理你得版本庫和工作副本,但是只使用前面介紹的命令你就可以很進行日常工作了,我們還會覆蓋更多用的還算頻繁的命令。



[4] 當然沒有任何東西是在版本庫裡被刪除了—只是在版本庫的HEAD裡消失了,你可以通過檢出(或者更新你的工作副本)你做出刪除操作的前一個修訂版本來找回所有的東西,詳細請見「找回刪除的項目」一節

[5] 而且你也沒有WAN卡,考慮到你得到我們,哈!

[6] 你也可以手工的刪除這三個臨時文件,但是當Subversion會給你做時你會自己去做嗎?我們是這樣想的。

[7] 如果你向他們詢問,他們非常有理由把你帶到城外的鐵軌上。