本文經原作者同意從原始位置 https://www.open.collab.net/community/subversion/articles/merge-info.html 轉載。已移除或更新失效連結。
作者:Paul Burba
摘要:了解 Subversion 合併追蹤功能的內部機制。
發布 2008-05-06
本文假設讀者已具備 Subversion 書籍第 4 章 中所涵蓋主題的基本認識,或者具備使用 Subversion 1.5 的經驗;無論是 open.collab.net 上提供的早期採用者 1.5 二進位檔,或是 1.5.0 候選版本之一。本文中的範例是使用候選版本 4 撰寫的。截至 2008 年 5 月 6 日,最新的候選版本為 Subversion 1.5.0 候選版本 5。
合併資訊,或更具體地說,版本化屬性 svn:mergeinfo
,是已合併至特定檔案或目錄的合併歷程記錄。本文將詳細說明合併資訊;它的意義和運作方式。
svn:mergeinfo
屬性值只是一個換行分隔的合併來源路徑清單(相對於儲存庫根目錄),每個來源路徑後面都跟著一個冒號,冒號後面則是先前從該來源合併的版本清單。如果您好奇確切的語法,它是在 Subversion 程式碼中指定的,請參閱 svn_mergeinfo.h 中的註解「@c SVN_PROP_MERGEINFO 屬性的概觀」。
注意:這是一篇長文章,涵蓋許多非典型使用案例。因此,如果您遵循 Subversion 書籍 中所述的「同步和重新整合」範例,那麼本文的大部分內容可能都是過度說明。在這種情況下,您可能只想閱讀前幾節,直到「明確合併資訊繼承」,然後跳到「結語」部分。
當路徑設定了 svn:mergeinfo
屬性時,該路徑便被視為具有明確合併資訊。明確合併資訊通常是在合併的工作副本目標上建立(或在已存在的情況下修改)(從這裡開始,我們將其簡稱為合併目標)。我之所以說「通常」,是因為有時候合併不會設定或修改合併資訊,請參閱「我的合併資訊在哪裡?」部分。
明確合併資訊也可以在合併目標的子樹上建立或修改。通常,這些子樹在合併之前就有明確的合併資訊,因為它們本身曾經是合併目標。儘管在某些情況下,合併資訊會建立在合併之前沒有明確合併資訊的子樹上,請參閱「合併資訊繼承和不可繼承範圍」一節。
讓我們來看看一些實際的明確合併資訊,本例中是 Subversion 自身 1.5.x 分支的工作副本。這個分支是我們在準備發布 1.5 時從主幹複製出來的。在建立後幾乎立即開始選擇性地將主幹的變更合併(即精選)到分支中
>svn info \svn\src-1.5.x
路徑:\SVN\src-1.5.x
URL:http://svn.collab.net/repos/svn/branches/1.5.x
存放庫根目錄:
http://svn.collab.net/repos/svn
存放庫 UUID:612f8ebc-c883-4be0-9ee0-a4e9ef946e3a
版本:30056
節點類型:目錄
排程:正常
最後變更作者:hwright
最後變更版本:30056
最後變更日期:2008-03-26 00:32:28 -0400 (星期三,2008 年 3 月 26 日)
>svn pg svn:mergeinfo \svn\src-1.5.x –recursive
\SVN\src-1.5.x - /trunk:29085-29089,29091,29094-29107,29111,29114,29117,29126-29127, 29129-29133,29135-29150,29153-29164,29166,29174,29176-29186,29188-29189, 29193-29194,29198-29200,29202-29206,29208-29251,29254-29256,29261, 29267-29273,29277,29280-29281,29284,29287-29303,29305-29307,29309-29325, 29327-29343,29345-29348,29358-29379,29381-29392,29397,29399,29401,29409, 29412,29414-29415,29417-29423,29425-29426,29429,29433-29434,29436-29447, 29449-29466,29468-29478,29482,29484,29486-29487,29489,29491,29493,29496, 29498,29508,29527-29528,29531,29533,29539-29540,29542,29544,29546,29551, 29553,29556,29559,29565,29567-29569,29571-29578,29581,29583,29591,29594, 29600,29603,29607,29611,29613-29614,29619,29623,29625-29626,29630-29631, 29634,29642,29648,29650,29656,29659-29660,29663-29664,29671-29672, 29677-29680,29692,29738-29739,29742-29744,29746,29751,29763,29769-29770, 29784,29787,29797,29801,29821,29824,29828,29835,29855,29868-29869,29878, 29883-29884,29895,29898,29900,29914,29920,29925,29930,29940,29950,29958, 29962,29968,29980,29994-29997,30004,30020注意:您會注意到這些範例中有一堆路徑包含反斜線。不要試著調整您的顯示器或預約驗光師...是的,我在 Windows 上進行 Subversion 開發。
在先前的範例中,只有一個合併來源路徑,/trunk,後面接著先前從它合併到 1.5.x 分支的修訂清單。請注意,與許多 Subversion 子指令使用的 -rX:Y 符號不同,svn:mergeinfo 中的版本範圍使用 A-B 格式,其中修訂 A 是包含的。換句話說,如果您合併 -r3:7,請預期會看到範圍為 4-7 的 mergeinfo。
請務必注意,列在特定來源的 svn:mergeinfo
屬性中的所有修訂不一定要在該來源上運作。檢視先前的範例,1.5.x 分支的 mergeinfo 顯示 r29093:29107 已從 trunk 中合併。但如果我們檢視 r29095 的記錄,會發現它並非影響 trunk 的變更
>svn log
http://svn.collab.net/repos/svn/trunk
-v -r29095
------------------------------------------------------------------------
>
那麼,為什麼 r29095 會包含在 1.5.x 的 mergeinfo 中?因為在某個時間點,有人從 trunk 合併了一系列修訂 -rX:Y 到 1.5.x,其中 X < 29095 <= Y。開發社群決定在這種情況下記錄非運作修訂的 mergeinfo,以減少 mergeinfo 屬性的片段化,讓人類更容易閱讀。檢視 1.5.x 上的 mergeinfo 時,您可能會想「這有什麼意義?它已經很片段化了!」。您說得沒錯,因為 1.5.x 是發行分支,但對於其他使用案例(例如最終重新整合到 trunk 的功能分支),此行為可以讓 mergeinfo 保持簡潔有序,通常只有一個範圍。
在某些情況下,合併不會建立或修改 mergeinfo
>svn co %url% wc
A wc\A
A wc\A\B
A wc\A\B\lambda
A wc\A\B\E
A wc\A\B\E\alpha
A wc\A\B\E\beta
A wc\A\B\F
A wc\A\mu
A wc\A\C
A wc\A\D
A wc\A\D\gamma
A wc\A\D\G
A wc\A\D\G\pi
A wc\A\D\G\rho
A wc\A\D\G\tau
A wc\A\D\H
A wc\A\D\H\chi
A wc\A\D\H\omega
A wc\A\D\H\psi
A wc\iota
簽出版本 1。請注意,此工作副本沒有合併資訊
>svn pg svn:mergeinfo wc -R
>然後我們對文字進行簡單的變更,並將其提交為 r2
>echo 'text change to a file' > wc\A\mu
>svn ci -m "" wc
傳送 wc\A\mu
傳輸檔案資料。
提交版本 2。糟糕,我們發現我們不想要那個變更,並從自身反向合併 r2。Subversion 總是允許您執行此類型的合併,無論設定什麼合併資訊
>svn merge %url%/A/mu wc/A/mu -c-2
--- 將 r2 反向合併至「wc\A\mu」
U wc\A\mu由於目前的合併資訊實作無法表示反向合併,因此先前的合併不會留下合併資訊的證據
>svn pg svn:mergeinfo wc -R
>在此,我們有一個合併,不會設定或修改任何合併資訊。
再次檢視先前範例,檢查 Subversion 1.5.x 分支工作副本上的合併資訊。注意到 --recursive
選項嗎?這表示我們會取得在根目錄為 \SVN\src-1.5.x 的整個工作副本樹狀結構上設定的所有 svn:mergeinfo 屬性清單。但是,我們只會在 \SVN\src-1.5.x 上看到唯一的明確合併資訊。從 trunk 到 \SVN\src-1.5.x 的許多合併肯定會影響 \SVN\src-1.5.x 的子樹狀結構吧?挑選一個最近從 trunk 合併的版本,我們可以看到這是真的
>svn log -v -q -c30020
http://svn.collab.net/repos/svn/trunk
------------------------------------------------------------------------
r30020 | pburba | 2008-03-24 11:15:48 -0400 (週一,2008 年 3 月 24 日)
已變更路徑:
M /trunk/subversion/libsvn_client/copy.c
------------------------------------------------------------------------
那麼,\SVN\src-1.5.x\subversion\libsvn_client\copy.c 如何「知道」trunk 中的 r30020 已合併至其中?它透過合併資訊繼承得知。如果路徑沒有明確的 svn:mergeinfo,但其父目錄(或祖父目錄,或曾祖父目錄,等等)有明確的合併資訊,則它仍可以有繼承的合併資訊。
「最近的父層」的概念並不限於工作副本。在判斷沒有明確合併資訊的路徑的繼承合併資訊時,Subversion 會先盡可能地爬梳工作副本,尋找具有明確合併資訊的父層。如果爬梳到工作副本的最上層仍找不到這樣的父層,它會再詢問儲存庫是否有任何其他父層路徑,必要時會爬梳到儲存庫的根目錄。只有在儲存庫中找不到可繼承的合併資訊時,我們才能最終判定路徑完全沒有合併資訊(有時我們甚至無法判定,如我們將在下一節中看到)。
讓我們再次檢視 \SVN\src-1.5.x\subversion\libsvn_client\copy.c,以了解繼承的實際範例。我們從 \SVN\src-1.5.x 上的合併資訊得知,從 trunk 合併 r30020 應該會變更這個檔案,但我們也知道 copy.c 本身沒有任何明確的合併資訊。但由於我們也知道 copy.c 具有明確合併資訊的最近父層是 \SVN\src-1.5.x,因此由於合併資訊繼承,copy.c 的繼承合併資訊等同於 \SVN\src-1.5.x 上的合併資訊。
不幸的是,沒有簡單的方法可以直接從命令列看到這一點。1.5 確實提供了新的 svn mergeinfo 子指令,但對於 1.5,它只提供從特定來源先前合併(或符合合併資格)的修訂清單
>svn mergeinfo --show-revs merged http://svn.collab.net/repos/svn/trunk/subversion/libsvn_client/copy.c\svn\src-1.5.x\subversion\libsvn_client\copy.c
r229
r282
r316
r326
r380
.
.
<省略 *長* 修訂清單>
.
.
r28472
r28512
r28825
r29374
r30020
如您所見,輸出更適合輸入到指令碼中,因為每個修訂會個別列出。另請注意,與 svn:mergeinfo
不同,只會列出有效的修訂;前述輸出中列出的每個修訂實際上都對 http://svn.collab.net/repos/svn/trunk/subversion/libsvn_client/copy.c 進行了變更。
更容易隱含地看到合併資訊繼承。讓我們嘗試直接合併兩個修訂版到 1.5.x 工作副本中的 copy.c。其中一個修訂版是 r30020,應該被忽略,因為 copy.c 的繼承合併資訊顯示它已經被合併。為了找到另一個有資格合併的運作修訂版,我們再次使用 svn mergeinfo,但這次使用 --show-revs eligible 選項
>svn mergeinfo --show-revs eligible
http://svn.collab.net/repos/svn/trunk/subversion/libsvn_client/copy.c\svn\src-1.5.x\subversion\libsvn_client\copy.c
r29167
r29961
好的,讓我們將 r29167 和 r30020 合併到 copy.c(請注意,在 1.5 中,我們現在可以使用合併的 -c
選項指定多個修訂版)
>svn merge -c29167,30020
http://svn.collab.net/repos/svn/trunk/subversion/libsvn_client/copy.c\svn\src-1.5.x\subversion\libsvn_client\copy.c
--- 將 r29167 合併到 '\SVN\src-1.5.x\subversion\libsvn_client\copy.c':
U \SVN\src-1.5.x\subversion\libsvn_client\copy.c
從 Subversion 書籍中回想,1.5 合併追蹤的一個關鍵功能是避免重複合併。從前面的輸出,我們看到 Subversion 只嘗試合併 r29167,因為它意識到修訂版 r30020,根據 copy.c 的繼承合併資訊,已經被合併!
這涵蓋了大多數關於明確合併資訊和合併資訊繼承的基本知識。在繼續進行更進階的主題之前,有兩個關於合併資訊和繼承的最終重點必須牢記
首先,如果路徑有明確的合併資訊,則該合併資訊完全描述對該路徑所做的合併,路徑不會從任何地方繼承其他任何東西。換句話說,只有當路徑沒有明確的合併資訊時,繼承才能發揮作用。
其次,這可能非常明顯,但值得強調:當路徑繼承合併資訊時,它只從具有明確合併資訊的最近父代繼承。因此,如果我們有一個具有以下合併資訊的工作副本
>svn pg svn:mergeinfo -R src-branch
src-branch\subversion - /trunk/subversion:30045-30191,30210
src-branch - /trunk:30045-30197
src-branch\www - /trunk/www:30045-30212
檔案 src-branch\subversion\libsvn_repos\reporter.c 會繼承哪些合併資訊? src-branch\www 上有合併資訊,但它並非 reporter.c 的父路徑。 src-branch 上有合併資訊,而且它是 reporter.c 的父路徑,但有一個更近的父路徑 src-branch\subversion。 因此,reporter.c 會從那裡取得合併資訊。
無論路徑的合併資訊是明確的或繼承的,每個路徑都有自然歷程,而 Subversion 會將此視為隱含合併資訊。 坦白說,這不是一般使用者需要了解的事項,因此如果您只是想看個概觀,可以安心跳過這個主題。
還在嗎? 那麼讓我們檢出 Subversion 開發人員正在處理的功能分支的工作副本,並看看隱含合併資訊的實際運作
>svn co http://svn.collab.net/repos/svn/branches/in-memory-cache mem-cache-wc -q
執行幾個記錄子指令後,我們可以看到這個分支最近是由 r29755 中 Subversion trunk 的副本建立的
>svn log --stop-on-copy -q mem-cache-wc
------------------------------------------------------------------------
r30314 | glasser | 2008-04-04 19:08:12 -0400 (星期五,2008 年 4 月 4 日)
------------------------------------------------------------------------
r30313 | glasser | 2008-04-04 19:07:41 -0400 (星期五,2008 年 4 月 4 日)
------------------------------------------------------------------------
r30312 | glasser | 2008-04-04 19:06:14 -0400 (星期五,2008 年 4 月 4 日)
------------------------------------------------------------------------
.
.
<為了簡潔起見,剪掉部分輸出>
.
.
------------------------------------------------------------------------
r29775 | glasser | 2008-03-07 13:57:45 -0500 (星期五,2008 年 3 月 7 日)
------------------------------------------------------------------------
r29773 | glasser | 2008-03-07 13:23:54 -0500 (星期五,2008 年 3 月 7 日)
------------------------------------------------------------------------
r29755 | glasser | 2008-03-06 19:46:43 -0500 (星期四,2008 年 3 月 6 日)
------------------------------------------------------------------------
>svn log -v -r29755 mem-cache-wc
------------------------------------------------------------------------
r29755 | glasser | 2008-03-06 19:46:43 -0500 (星期四,2008 年 3 月 6 日) | 14 行
已變更路徑
A /branches/in-memory-cache(來自 /trunk:29754)
<剪輯實際記錄訊息>
------------------------------------------------------------------------
現在檢視分支的合併資訊
>svn pg svn:mergeinfo -R mem-cache-wc
mem-cache-wc - /branches/svn-mergeinfo-enhancements:30045-30214
/trunk:29755-30312
請注意 /trunk 的合併資訊從 r29755 開始?如果我們嘗試合併 r29754 之前的某些修訂版本,會怎樣?Subversion 是否會嘗試合併 in-memory-cache 自身歷史中的那些變更?讓我們嘗試看看,不要執行與主幹進行一般功能分支同步(在此情況下我們不指定修訂版本範圍),而是明確設定一個範圍。
>svn merge
http://svn.collab.net/repos/svn/trunk
mem-cache-wc -r23000:HEAD
--- 合併 r30313 至 r30422 至 'mem-cache-wc' 中:
U mem-cache-wc\COMMITTERS
U mem-cache-wc\subversion\libsvn_fs_base\tree.c
U mem-cache-wc\subversion\libsvn_fs_base\bdb\node-origins-table.c
.
.
<剪輯>
.
.
U mem-cache-wc\contrib\client-side\svnmucc\svnmucc-test.py
U mem-cache-wc\configure.ac
G mem-cache-wc
檢視通知,Subversion 只嘗試合併 r30313 之後的版本。從我們已經看到的內容,我們不預期 Subversion 會合併 r29754:30312,因為該範圍已經在明確的合併資訊中。但是 -r23000:29754 呢?為何 Subversion 沒有嘗試合併那些修訂版本?答案在於 /branches/in-memory-cache 的隱含合併資訊。由於 /branches/in-memory-cache 是在 r29755 中從 /trunk 複製而來,因此前者作為一個副本,與 /trunk 在 r29754 及更早版本中共享其歷史記錄。Subversion 在決定從主幹合併哪些內容時,會將這段「自然」歷史記錄視為隱含合併資訊。這實際上表示 /branches/in-memory-cache 除了其明確合併資訊之外,還具有隱含合併資訊「/trunk:1-29754」。請注意,在合併之後,in-memory-cache 分支上的合併資訊只新增了「/trunk:30313-30422」(因為描述路徑自身歷史記錄的合併資訊是多餘的)
>svn pg svn:mergeinfo -R mem-cache-wc
mem-cache-wc - /branches/1.5.x-r30215:30238
/trunk:29755-30422
Subversion 允許工作副本是儲存庫的不完整表示。這在淺層結帳、切換的子樹或因為授權限制而無法結帳樹的一部分時是可能的。如果您希望合併到不完整的樹中,Subversion 會努力保持您的合併資訊準確。它主要使用不可繼承合併資訊範圍來執行此操作。展示這些運作方式最簡單的方法是透過範例
首先,我們透過對 Subversion 1.5.x 分支進行淺層結帳來建立不完整的工作副本
注意:--depth 選項在 1.5 中也是新的,它將 Subversion 子命令的運作限制在目標工作副本或 URL 中的特定深度。在此情況下,我們使用「immediates」值,它只會提供 1.5.x 分支的根目錄,以及根目錄的每個直接檔案或目錄子項。
>svn co
http://svn.collab.net/repos/svn/branches/1.5.x@30435
1.5.x --depth immediates
A 1.5.x\Makefile.in
A 1.5.x\STATUS
A 1.5.x\build.conf
A 1.5.x\www
A 1.5.x\win-tests.py
A 1.5.x\COMMITTERS
A 1.5.x\TRANSLATING
A 1.5.x\notes
A 1.5.x\README
A 1.5.x\subversion
A 1.5.x\build
A 1.5.x\tools
A 1.5.x\BUGS
A 1.5.x\contrib
A 1.5.x\configure.ac
A 1.5.x\HACKING
A 1.5.x\doc
A 1.5.x\INSTALL
A 1.5.x\COPYING
A 1.5.x\CHANGES
A 1.5.x\autogen.sh
A 1.5.x\gen-make.py
A 1.5.x\aclocal.m4
A 1.5.x\packages
U 1.5.x
檢出版本 30435。
現在變更「subversion」目錄的深度,讓整個子樹都存在
注意:這裡我們使用 --set-depth 選項,這也是 1.5 中的新功能。--depth 本質上是對子命令的一般限制,而 --set-depth 是更新的動作操作。這裡我們使用「無限」值,將 1.5.x\subversion 子樹完全展開。
>svn up 1.5.x\subversion --set-depth infinity --quiet
花點時間查看我們在 1.5.x 分支上的合併資訊
>svn pg svn:mergeinfo -R 1.5.x
1.5.x\CHANGES - /branches/1.5.x-r30215/CHANGES:30236,30238,30245,30288
/branches/svn-mergeinfo-enhancements/CHANGES:30122
/trunk/CHANGES:29085-29089、29091、29094-29107、29111、29114、29117、29126-29127、29129-29133、29135-29150、29153-29164、29166-29170、29174、29176-29186、29188-29189、29193-29194、29198-29206、29208-29251、29254-29256、29261、29267-29273、29277、29280-29281、29284、29287-29303、29305-29307、29309-29343、29345-29348、29358-29379、29381-29392、29397、29399、29401、29409、29412、29414-29415、29417-29423、29425-29426、29429、29433-29434、29436-29447、29449-29466、29468-29478、29482、29484、29486-29487、29489、29491、29493、29496、29498、29508、29527-29528、29531、29533、29539-29540、29542、29544、29546、29551、29553、29556、29559、29565、29567-29569、29571-29578、29581、29583、29591、29594、29600、29603、29607、29611、29613-29614、29619、29623、29625-29626、29630-29631、29633-29634、29642、29645、29648、29650、29656、29659-29660、29663-29666、29671-29672、29677-29680、29692、29738-29739、29741-29744、29746、29751、29763、29767、29769-29770、29784、29786-29787、29797、29801、29815、29821、29824、29828、29835、29852、29854-29855、29857-29859、29868-29869、29876、29878、29883-29884、29895、29898、29900、29914、29920、29922、29925、29930、29939-29940、29942、29950、29958、29962、29965、29967-29968、29980、29986、29994-29997、30004、30009、30020、30030、30050、30053-30054、30059、30061-30062、30067、30070、30074、30086、30098、30101、30112、30117、30124、30129-30130、30137、30145、30151、30159、30161-30162、30180-30181、30185、30210、30233、30237、30239、30246、30249、30256、30278-30279、30281、30285、30297、30299、30304、30319-30321、30328、30335-30336、30340、30342、30347、30362、30368、30373、30375、30378、30380、30392、30402、30407-30409、30412
1.5.x - /trunk:29085-29089、29091、29094-29107、29111、29114、29117、29126-29127、29129-29133、29135-29150、29153-29164、29166-29170、29174、29176-29186、29188-29189、29193-29194、29198-29206、29208-29251、29254-29256、29261、29267-29273、29277、29280-29281、29284、29287-29303、29305-29307、29309-29343、29345-29348、29358-29379、29381-29392、29397、29399、29401、29409、29412、29414-29415、29417-29423、29425-29426、29429、29433-29434、29436-29447、29449-29466、29468-29478、29482、29484、29486-29487、29489、29491、29493、29496、29498、29508、29527-29528、29531、29533、29539-29540、29542、29544、29546、29551、29553、2955
1.5.x\CHANGES 已直接與其進行合併(亦即,它曾經是合併目標),因此請注意它有自己的明確合併資訊。當合併至 1.5.x 時,我們將 1.5.x\CHANGES 視為具有不同合併資訊的子樹。我們稍後會再回到這些子樹。
現在,讓我們將 trunk 中的一些變更合併到這個工作副本中。我們將一次合併 trunk 中的兩個版本。一個版本將對工作副本中現有的部分進行變更,另一個版本將嘗試對因稀疏簽出而不存在的路徑進行一些變更。
我們將使用的特定版本是 r30431 和 r30435:
>svn log --verbose --quiet -r30430:30435
http://svn.collab.net/repos/svn/trunk
------------------------------------------------------------------------
r30431 | epg | 2008-04-07 19:40:11 -0400 (週一,2008 年 4 月 7 日)
已變更路徑
M /trunk/subversion/tests/cmdline/changelist_tests.py
------------------------------------------------------------------------
r30435 | julianfoad | 2008-04-08 09:56:02 -0400 (週二,2008 年 4 月 8 日)
已變更路徑
A /trunk/notes/tree-conflicts/policy.txt
------------------------------------------------------------------------
好的,讓我們進行合併
注意:我們在此使用 --depth infinity 來強制將合併套用到存在的 1.5.x 的任何子項。由於原始的淺層簽出,1.5.x 的深度為「立即」且合併作業的深度預設為合併目標的深度。如果我們未指定無限深度,Subversion 甚至不會嘗試合併 r30435。
>svn merge --depth infinity
http://svn.collab.net/repos/svn/trunk
1.5.x -r30430:30435
已略過遺失的目標:'1.5.x\notes\tree-conflicts\policy.txt'
已略過遺失的目標:'1.5.x\notes\tree-conflicts'
--- 將 r30431 至 r30435 合併到 '1.5.x' 中:
U 1.5.x\subversion\tests\cmdline\changelist_tests.py
由於 1.5.x\notes\tree-conflicts 和 1.5.x\notes\tree-conflicts\policy.txt 不存在於稀疏工作副本中,因此在嘗試合併 r30435 時,會略過這些檔案。不過 1.5.x\subversion\tests\cmdline\changelist_tests.py 存在,因此 r30431 的變更會合併到該檔案中。
讓我們看看 1.5.x 上現在有哪些合併資訊
>svn pg svn:mergeinfo 1.5.x
/trunk:29085-29089,29091,29094-29107,29111,29114,29117,29126-29127,29129-29133,29135-29150,29153-29164,29166-29170,29174,29176-29186,29188-29189,29193-29194,29198-29206,29208-29251,29254-29256,29261,29267-29273,29277,29280-29281,29284,29287-29303,29305-29307,29309-29343,29345-29348,29358-29379,29381-29392,29397,29399,29401,29409,29412,29414-29415,29417-29423,29425-29426,29429,29433-29434,29436-29447,29449-29466,29468-29478,29482,29484,29486-29487,29489,29491,29493,29496,29498,29508,29527-29528,29531,29533,29539-29540,29542,29544,29546,29551,29553,29556,29559,29565,29567-29569,29571-29578,29581,29583,29591,29594,29600,29603,29607,29611,29613-29614,29619,29623,29625-29626,29630-29631,29633-29634,29642,29645,29648,29650,29656,29659-29660,29663-29666,29671-29672,29677-29680,29692,29738-29739,29741-29744,29746,29751,29763,29767,29769-29770,29784,29786-29787,29797,29801,29815,29821,29824,29828,29835,29852,29854-29855,29857-29859,29868-29869,29876,29878,29883-29884,29895,29898,29900,29914,29920,29922,29925,29930,29939-29940,29942,29950,29958,29962,29965,29967-29968,29980,29986,29994-29997,30004,30009,30020,30030,30050,30053-30054,30059,30061-30062,30067,30070,30074,30086,30098,30101,30117,30124,30129-30130,30137,30145,30151,30159,30161-30162,30180-30181,30185,30210,30233,30237,30239,30246,30249,30256,30278-30279,30281,30285,30297,30299,30304,30319-30321,30328,30335-30336,30340,30342,30347,30362,30368,30373,30375,30378,30380,30392,30402,30407-30409,30412,30431-30435
我們可以看到,/trunk 的 -r30430:30435 已新增到 1.5.x 的合併資訊中。這很有道理,因為這是我們在合併中要求的範圍!另外請注意,r30432、r30433、r30434 雖然都是 trunk 上的無效版本,但仍記錄在合併資訊中,如「合併資訊中的無效與有效版本」部分所述。
現在您可能會想:「很好,但如果我們提交此變更,然後其他人檢出完整的 1.5.x 工作副本會怎樣? 他們 notes\tree-conflicts\policy.txt 的副本是否會錯誤地從根目錄繼承 r30430:30435? 該檔案實際上從未在合併中變更過!」 這完全正確,但稍早我只查看工作副本根目錄的合併資訊,有點誤導。 讓我們深入一點,並查看工作副本的狀態
>svn st 1.5.x
M 1.5.x
M 1.5.x\www
M 1.5.x\notes
M 1.5.x\build
M 1.5.x\subversion\tests\cmdline\changelist_tests.py
M 1.5.x\contrib
M 1.5.x\tools
M 1.5.x\doc
M 1.5.x\CHANGES
M 1.5.x\packages
顯然 Subversion 在合併期間執行的動作,比它通知我們的還要多,因為在以空深度檢出的 1.5.x 的每個子目錄上都有屬性修改。 那麼,所有這些屬性修改究竟是什麼? 讓我們使用 svn diff 來查看其中一個
>svn diff 1.5.x\www
屬性變更於:1.5.x\www
___________________________________________________________________
已新增:svn:mergeinfo
合併 /trunk/www:r29085-29089,29091,29094-29107,29111,29114,29117,29126-29127,29129-29133,29135-29150,29153-29164,29166-29170,29174,29176-29186,29188-29189,29193-29194,29198-29206,29208-29251,29254-29256,29261,29267-29273,29277,29280-29281,29284,29287-29303,29305-29307,29309-29343,29345-29348,29358-29379,29381-29392,29397,29399,29401,29409,29412,29414-29415,29417-29423,29425-29426,29429,29433-29434,29436-29447,29449-29466,29468-29478,29482,29484,29486-29487,29489,29491,29493,29496,29498,29508,29527-29528,29531,29533,29539-29540,29542,29544,29546,29551,29553,29556,29559,29565,29567-29569,29571-29578,29581,29583,29591,29594,29600,29603,29607,29611,29613-29614,29619,29623,29625-29626,29630-29631,29633-29634,29642,29645,29648,29650,29656,29659-29660,29663-29666,29671-29672,29677-29680,29692,29738-29739,29741-29744,29746,29751,29763,29767,29769-29770,29784,29786-29787,29797,29801,29815,29821,29824,29828,29835,29852,29854-29855,29857-29859,29868-29869,29876,29878,29883-29884,29895,29898,29900,29914,29920,29922,29925,29930,29939-29940,29942,29950,29958,29962,29965,29967-29968,29980,29986,29994-29997,30004,30009,30020,30030,30050,30053-30054,30059,30061-30062,30067,30070,30074,30086,30098,30101,30117,30124,30129-30130,30137,30145,30151,30159,30161-30162,30180-30181,30185,30210,30233,30237,30239,30246,30249,30256,30278-30279,30281,30285,30297,30299,30304,30319-30321,30328,30335-30336,30340,30342,30347,30362,30368,30373,30375,30378,30380,30392,30402,30407-30409,30412,30431-30435*
1.5.x\www 現在有明確的合併資訊,說明從 /trunk/www 合併的內容。仔細查看,你會發現版本本身與 1.5.x 上的版本相同,只有一個重要的例外:在此合併中新增到 1.5.x 的 r30430:30435 範圍,在 1.5.x\www 中有一個 '*' 後綴。此 '*' 是不可繼承合併資訊範圍的標記。'*' 表示只有明確設定合併資訊的路徑已將此範圍合併其中。
我不會詳細說明這一點,但現在 1.5.x 的每個空子代碼都有自己的明確合併資訊。此合併資訊結合了子代碼在合併前從 1.5.x 繼承的內容,以及新增的 30431-30435*,表示這些版本僅合併到空子代碼。
如果我們提交此變更,而另一個使用者要對 1.5.x 分支執行完整的 --depth infinity
簽出,則他們 notes\tree-conflicts\policy.txt 的副本將繼承 notes 中的所有合併資訊,但 30431-30435 除外,這是件好事,因為這些版本從未實際合併到 policy.txt 中。
當 Subversion 無法存取合併目標的子樹時,就會發生類似的程序。如果子樹切換到另一個 URL 或根本不存在於磁碟上,則無法存取子樹。後者可能是由上述的淺層工作副本所造成,也可能是由於授權限制所致。不論子樹遺失的原因為何,Subversion 始終會嘗試執行我們剛剛在上面看到的動作
注意:由於切換的路徑本身也會取得明確的合併資訊,因此由於切換而遺失的子樹甚至比這裡所描述的還要複雜,不過我會留待以後的文章再說明。
現在,一般來說,大多數使用者不會將遺失子樹的目標合併,但如果您需要合併,請放心,Subversion 會嘗試讓每個路徑上的明確/繼承合併資訊準確反映僅合併至該路徑的內容。
空的合併資訊是一個 svn:mergeinfo 屬性,其值為空字串。它只表示「此路徑的狀態就像從未合併任何內容一樣」。空的合併資訊通常會在執行工作副本至工作副本的移動或複製(即將發表的 Submerged 文章的主題)或反向合併所有先前合併的子樹時發生。我們來看後者的範例
我們目前的作業目錄是一個簡單的分支
>svn ls -R .
B/
B/E/
B/E/alpha
B/E/beta
B/F/
B/lambda
C/
D/
D/G/
D/G/pi
D/G/rho
D/G/tau
D/H/
D/H/chi
D/H/omega
D/H/psi
D/gamma
mu
查看合併資訊,我們會看到已將多個版本合併到此分支
>svn pl -vR .
「.」的屬性
svn:mergeinfo : /A:2-6
如果我們將所有這些版本反向合併出分支的子樹,會發生什麼事?
>svn merge %url92%/A/D/H .\D\H -r6:1
--- 反向合併 r6 到 r2 至「D\H」
U D\H\omega
U D\H\psi
>svn pl -vR .
「.」的屬性
svn:mergeinfo : /A:2-6
「D\H」的屬性
svn:mergeinfo
發生了合併資訊為空的情況。如果 .\D\H 沒有明確的空合併資訊,它會錯誤地從「.」繼承合併資訊 /A/D/H:2-6。
每次合併結束時,Subversion 會嘗試「合併」任何多餘的子樹合併資訊。此合併程序稱為刪除。合併完成後,Subversion 會瀏覽以合併目標為根的檔案總管樹狀結構。如果它找到具有明確合併資訊且具有等效明確合併資訊的子樹的路徑,則會刪除(移除)子樹的合併資訊。此處的「等效」定義如下:
換句話說,移除子樹的合併資訊是安全的,因為如果子樹的合併資訊等於其最近具有明確合併資訊的父節點,則子樹從該父節點繼承的合併資訊已經足以描述對子樹的合併。現在可能已經很清楚,合併資訊繼承和省略只是檢視同一件事的兩種方式:繼承是合併資訊從父節點「向下」滑動到子節點,省略是合併資訊從子節點「向上」滑動到父節點。
理論說夠了,讓我們來看看省略的實際應用。在此範例中,我們將為目前的 Subversion 功能分支簽出工作副本
>svn co http://svn.collab.net/repos/svn/branches/dont-save-plaintext-passwords-by-default no_pass_wc -q
>svn pg svn:mergeinfo -R no_pass_wc
no_pass_wc - /branches/1.5.x-r30215:30238
/branches/diff-callbacks3:29985-30687
/branches/svn-mergeinfo-enhancements:30045-30214
/trunk:30654-30731
此分支的開發人員已定期將其與 Subversion 的主幹同步。但是,主幹上的開發幾乎持續進行,而使用 svn mergeinfo 子指令,我們會看到主幹上有一些可供合併的版本
>svn mergeinfo --show-revs eligible http://svn.collab.net/repos/svn/trunk no_pass_wc
r30735
r30736
r30738
r30741
r30743
r30745
r30746
r30747
r30748
r30749
r30750
r30751
r30753
r30754
r30756
假設我們正在處理這個分支,而且我們是 Windows 開發人員。我們知道主幹 (r30754) 上的變更對於在 Windows 上建置分支是必要的,但我們現在不想合併主幹上的所有可用變更(也許我們知道會有許多衝突需要解決,而我們還沒有準備好這麼做)。我們決定只將我們需要的微小修正直接合併到我們分支上出問題的檔案 build.conf
>svn merge http://svn.collab.net/repos/svn/trunk/build.confno_pass_wc\build.conf -c30754
--- 合併 r30754 至 'no_pass_wc\build.conf'
U no_pass_wc\build.conf
正如預期的那樣,分支的 diff 顯示已將明確的合併資訊新增至 build.conf,而且它看起來等同於工作拷貝根目錄上的合併資訊,但 r30754 除外。
>svn diff no_pass_wc
索引:no_pass_wc/build.conf
===================================================================
--- no_pass_wc/build.conf (版本 30756)
+++ no_pass_wc/build.conf (工作拷貝)
<剪輯文字 diff>
no_pass_wc\build.conf 上的屬性變更:
___________________________________________________________________
已新增:svn:mergeinfo
已合併 /branches/diff-callbacks3/build.conf:r29985-30687
已合併 /trunk/build.conf:r30654-30731,30754
已合併 /branches/1.5.x-r30215/build.conf:r30238
已合併 /branches/svn-mergeinfo-enhancements/build.conf:r30045-30214
稍後我們決定準備好與主幹同步,並執行此動作,暫時忘記我們已對 build.conf 執行的合併。
>svn merge http://svn.collab.net/repos/svn/trunk no_pass_wc
--- 合併 r30732 至 r30753 至 'no_pass_wc'
U no_pass_wc\subversion\include\svn_client.h
A no_pass_wc\subversion\include\private\svn_opt_private.h
U no_pass_wc\subversion\include\svn_opt.h
U no_pass_wc\subversion\libsvn_wc\status.c
U no_pass_wc\subversion\libsvn_subr\opt.c
U no_pass_wc\subversion\libsvn_subr\mergeinfo.c
A no_pass_wc\subversion\libsvn_client\cmdline.c
U no_pass_wc\subversion\libsvn_client\merge.c
U no_pass_wc\subversion\libsvn_client\prop_commands.c
U no_pass_wc\subversion\libsvn_client\mergeinfo.h
U no_pass_wc\subversion\tests\libsvn_client\client-test.c
U no_pass_wc\subversion\tests\cmdline\special_tests.py
U no_pass_wc\subversion\tests\cmdline\basic_tests.py
U no_pass_wc\subversion\tests\cmdline\merge_tests.py
U no_pass_wc\subversion\tests\cmdline\depth_tests.py
U no_pass_wc\subversion\svn\merge-cmd.c
U no_pass_wc\subversion\svn\cl.h
U no_pass_wc\subversion\svn\propdel-cmd.c
U no_pass_wc\subversion\svn\checkout-cmd.c
U no_pass_wc\subversion\svn\move-cmd.c
U no_pass_wc\subversion\svn\mkdir-cmd.c
U no_pass_wc\subversion\svn\cat-cmd.c
U no_pass_wc\subversion\svn\revert-cmd.c
U no_pass_wc\subversion\svn\diff-cmd.c
U no_pass_wc\subversion\svn\copy-cmd.c
U no_pass_wc\subversion\svn\mergeinfo-cmd.c
U no_pass_wc\subversion\svn\list-cmd.c
U no_pass_wc\subversion\svn\util.c
U no_pass_wc\subversion\svn\blame-cmd.c
U no_pass_wc\subversion\svn\propget-cmd.c
U no_pass_wc\subversion\svn\changelist-cmd.c
U no_pass_wc\subversion\svn\log-cmd.c
U no_pass_wc\subversion\svn\update-cmd.c
U no_pass_wc\subversion\svn\resolved-cmd.c
U no_pass_wc\subversion\svn\cleanup-cmd.c
U no_pass_wc\subversion\svn\commit-cmd.c
U no_pass_wc\subversion\svn\add-cmd.c
U no_pass_wc\subversion\svn\propset-cmd.c
U no_pass_wc\subversion\svn\switch-cmd.c
U no_pass_wc\subversion\svn\delete-cmd.c
U no_pass_wc\subversion\svn\import-cmd.c
U no_pass_wc\subversion\svn\proplist-cmd.c
U no_pass_wc\subversion\svn\resolve-cmd.c
U no_pass_wc\subversion\svn\export-cmd.c
U no_pass_wc\subversion\svn\status-cmd.c
U no_pass_wc\subversion\svn\propedit-cmd.c
U no_pass_wc\subversion\svn\lock-cmd.c
U no_pass_wc\subversion\svn\info-cmd.c
U no_pass_wc\subversion\svn\unlock-cmd.c
U no_pass_wc\subversion\libsvn_fs_fs\structure
--- 合併 r30754 到 r30757 進入 'no_pass_wc'
U no_pass_wc\subversion\include\svn_config.h
U no_pass_wc\subversion\libsvn_wc\merge.c
請注意,Subversion 會了解 r30754 已合併到 build.conf 中,並且不會嘗試重複合併的部分。不僅如此,一旦合併完成,Subversion 會注意到工作副本根目錄上的合併資訊和 build.conf 上的合併資訊是等效的,並將後者省略掉。我們可以在工作副本的狀態中看到這是正確的,或直接查看 svn:mergeinfo
屬性
>svn st no_pass_wc
M no_pass_wc
M no_pass_wc\build.conf
<snip>
M no_pass_wc\subversion\svn\unlock-cmd.c
M no_pass_wc\subversion\libsvn_fs_fs\structure
>svn pg svn:mergeinfo -R no_pass_wc
no_pass_wc - /branches/1.5.x-r30215:30238
/branches/diff-callbacks3:29985-30687
/branches/svn-mergeinfo-enhancements:30045-30214
/trunk:30654-30757
如果我們可以在合併完成但刪除發生之前停止 subversion,我們將看到此合併資訊
no_pass_wc - /branches/1.5.x-r30215:30238
/branches/diff-callbacks3:29985-30687
/branches/svn-mergeinfo-enhancements:30045-30214
/trunk:30654-30757
no_pass_wc\build.conf - /branches/1.5.x-r30215/build.conf:30238
/branches/diff-callbacks3/build.conf:29985-30687
/branches/svn-mergeinfo-enhancements/build.conf:30045-30214
/trunk/build.conf:30654-30757
由於 no_pass_wc/build.conf 上的合併資訊等同於 no_pass_wc 上的合併資訊,因此前者會被省略掉。
1.5 中的新合併子命令選項為 --record-only。使用 --record-only
選項完成的合併不會嘗試合併任何內容,但它們會記錄並刪除合併資訊,就像進行實際合併一樣。這對於讓變更看起來已合併,但實際上並未合併(封鎖)以及清理子樹合併資訊很有用。
1.5 中的封鎖相當簡單,只要使用 --record-only 來合併一個修訂版本,Subversion 就會讓合併資訊看起來像是已經合併過一樣。從此之後,Subversion 會看到合併資訊,並視同該修訂版本真的已經合併,封鎖未來再次合併的嘗試。例如,再次檢視我們先前範例中的乾淨檢出,我們首先合併一個符合資格的修訂版本,不使用 --record-only
>svn merge http://svn.collab.net/repos/svn/trunk no_pass_wc -c30757
--- 合併 r30757 至 'no_pass_wc'
U no_pass_wc\subversion\include\svn_config.h
正如預期,此合併會更新合併目標上的合併資訊
>svn st no_pass_wc
M no_pass_wc
M no_pass_wc\subversion\include\svn_config.h
>svn diff no_pass_wc --depth empty
no_pass_wc 上的屬性變更:
___________________________________________________________________
已修改:svn:mergeinfo
已合併 /trunk:r30757
現在我們還原該合併,並重複執行,但這次使用 --record-only
>svn revert -R no_pass_wc
已還原 'no_pass_wc'
已還原 'no_pass_wc\subversion\include\svn_config.h'
>svn merge http://svn.collab.net/repos/svn/trunk no_pass_wc -c30757 –record-only
>
合併沒有產生任何輸出,這很合理,因為沒有合併任何內容。不過,合併資訊已經變更,如下所示
>svn diff no_pass_wc
no_pass_wc 上的屬性變更:
___________________________________________________________________
已修改:svn:mergeinfo
已合併 /trunk:r30757
如果我們再次嘗試合併,不使用 --record-only
,我們會看到它已被封鎖
>svn merge http://svn.collab.net/repos/svn/trunk no_pass_wc -c30757
>
注意:1.5 封鎖並非有些人所稱的「真正封鎖」,無法輕易看出實際合併的內容與僅被封鎖的內容。在 1.5 中,可以判斷某個路徑的特定合併資訊所代表的所有來源和範圍是否實際上已合併或僅被封鎖,但這並不總是顯而易見。開發社群正在考慮在未來版本中加入某種類型的真正封鎖。
至於使用 --record-only
來清除子樹合併資訊,我們來看一個簡單的範例。檢出一個非常簡單的儲存庫
>svn co %SIMPLEURL% simple_wc
A simple_wc\A
A simple_wc\A\B
A simple_wc\A\B\lambda
A simple_wc\A\B\E
A simple_wc\A\B\E\alpha
A simple_wc\A\B\E\beta
A simple_wc\A\B\F
A simple_wc\A\mu
A simple_wc\A\C
A simple_wc\A\D
A simple_wc\A\D\gamma
A simple_wc\A\D\G
A simple_wc\A\D\G\pi
A simple_wc\A\D\G\rho
A simple_wc\A\D\G\tau
A simple_wc\A\D\H
A simple_wc\A\D\H\chi
A simple_wc\A\D\H\omega
A simple_wc\A\D\H\psi
A simple_wc\iota
A simple_wc\A_branch
A simple_wc\A_branch\B
A simple_wc\A_branch\B\lambda
A simple_wc\A_branch\B\E
A simple_wc\A_branch\B\E\alpha
A simple_wc\A_branch\B\E\beta
A simple_wc\A_branch\B\F
A simple_wc\A_branch\mu
A simple_wc\A_branch\C
A simple_wc\A_branch\D
A simple_wc\A_branch\D\gamma
A simple_wc\A_branch\D\G
A simple_wc\A_branch\D\G\pi
A simple_wc\A_branch\D\G\rho
A simple_wc\A_branch\D\G\tau
A simple_wc\A_branch\D\H
A simple_wc\A_branch\D\H\chi
A simple_wc\A_branch\D\H\omega
A simple_wc\A_branch\D\H\psi
已查看版本 4。
從記錄中,我們看到此儲存庫包含一棵根目錄為 A 的樹,該目錄在 r1 中新增,在 r2 中複製到一個分支,然後在 r3 和 r4 中修改
>svn log --verbose -r1:HEAD simple_wc
------------------------------------------------------------------------
r1 | jrandom | 2008-04-23 13:00:56 -0400 (週三, 2008 年 4 月 23 日) | 1 行
已變更路徑
A /A
A /A/B
A /A/B/E
A /A/B/E/alpha
A /A/B/E/beta
A /A/B/F
A /A/B/lambda
A /A/C
A /A/D
A /A/D/G
A /A/D/G/pi
A /A/D/G/rho
A /A/D/G/tau
A /A/D/H
A /A/D/H/chi
A /A/D/H/omega
A /A/D/H/psi
A /A/D/gamma
A /A/mu
A /iota
修訂版 1 的日誌訊息。
------------------------------------------------------------------------
r2 | pburba | 2008-04-23 13:02:04 -0400 (週三, 2008 年 4 月 23 日) | 1 行
已變更路徑
A /A_branch (來自 /A:1)
從 A 建立一個分支
------------------------------------------------------------------------
r3 | pburba | 2008-04-23 13:02:44 -0400 (週三, 2008 年 4 月 23 日) | 1 行
已變更路徑
M /A/D/H/psi
------------------------------------------------------------------------
r4 | pburba | 2008-04-23 13:03:09 -0400 (週三, 2008 年 4 月 23 日) | 1 行
已變更路徑
M /A/B/E/beta
------------------------------------------------------------------------
首先,我們將 r3 直接合併到 A_branch\D\H\psi
>svn merge %SIMPLEURL%/A/D/H/psi simple_wc\A_branch\D\H\psi -c3
--- 合併 r3 至 'simple_wc\A_branch\D\H\psi'
U simple_wc\A_branch\D\H\psi
>svn ci -m "merged r3 to A_branch/D/H/psi" simple_wc
傳送 simple_wc\A_branch\D\H\psi
傳輸檔案資料。
提交修訂版 5。
>svn pl -vR merge_tests-92
>svn pl -vR simple_wc
'simple_wc\A_branch\D\H\psi' 的屬性
svn:mergeinfo : /A/D/H/psi:3
然後,我們將 r4 合併到分支的根目錄
>svn merge %SIMPLEURL%/A simple_wc\A_branch -c4
--- 合併 r4 至 'simple_wc\A_branch'
U simple_wc\A_branch\B\E\beta
>svn pl -vR simple_wc
「simple_wc\A_branch」的屬性
svn:mergeinfo : /A:4
'simple_wc\A_branch\D\H\psi' 的屬性
svn:mergeinfo : /A/D/H/psi:3-4
由於 simple_wc 在儲存庫中為版本 5,因此我們必須更新工作副本,以避免提交時出現過期錯誤
>svn up simple_wc
於版本 5。
>svn ci -m "" simple_wc
傳送 simple_wc\A_branch
傳送 simple_wc\A_branch\B\E\β
傳送 simple_wc\A_branch\D\H\psi
傳輸檔案資料。
已提交版本 6。
請注意,合併目標的子樹 A_branch\D\H\psi 也更新了其合併資訊
>svn pl --verbose -R simple_wc
「simple_wc\A_branch」的屬性
svn:mergeinfo : /A:4
'simple_wc\A_branch\D\H\psi' 的屬性
svn:mergeinfo : /A/D/H/psi:3-4
糟糕,我們才剛想起公司政策是僅合併至分支的根目錄,以將明確的合併資訊整合在那裡。我們在進行第一次合併時忘記了這一點,現在尖酸刻薄的老闆正在對我們施壓。我們該如何解決這個問題?好吧,我們知道 r3 僅影響 A/D/H/psi,因此如果我們將「/A:r3」新增至 A_branch 的合併資訊,那麼我們的合併資訊在語意上仍等同於我們現在擁有的資訊。當然,如果我們這樣做,A_branch\D\H\psi 上的合併資訊就會等同於 A_branch 上的合併資訊,並且可能會被省略。我們可以使用「--record-only」合併,在一個快速步驟中執行此操作。不過,我們首先需要更新工作副本(請參閱「混合版本工作副本」以了解為何需要這樣做)
>svn up simple_wc
於版本 6。
>svn merge %SIMPLEURL%/A simple_wc\A_branch -c3 –record-only
>svn st simple_wc
M simple_wc\A_branch
M simple_wc\A_branch\D\H\psi
>svn pl -vR simple_wc
「simple_wc\A_branch」的屬性
svn:mergeinfo : /A:3-4
請注意,--record-only
合併將 r3 新增至 A_branch 上的合併資訊,然後省略 A_branch\D\H\psi 上等同的合併資訊。是時候向 PHB 要求加薪了!
Subversion 的基本設計原則之一是盡可能地靈活。靈活性通常也意味著更複雜,而複雜性越高,混淆的可能性就越大。混合版本工作副本和合併資訊就是這種情況
靈活性:Subversion 允許您合併到混合版本工作副本中。
複雜性:合併資訊繼承和省略取決於工作副本中一致的工作版本。
混淆:處理混合版本工作副本時,合併資訊繼承和省略可能無法像您預期的那樣運作。
在執行「子樹合併」(即合併到分支的根目錄,而不是根目錄的子樹)時,最有可能出現問題。對於以下範例,我們使用 openCOLLABNET 的合併追蹤專案中的範例儲存庫。
首先,我們檢出範例儲存庫的新工作副本
>svn co %URL% wc --quiet
儲存庫以相當常見的方式建構,在根目錄下有 trunk、branches 和 tags 資料夾...
>svn ls wc
branches/
tags/
trunk/
...以及 branches 下的幾個 trunk 副本
>svn ls wc\branches
a/
b/
c/
我們將使用 c 分支,因此讓我們看看那裡的合併資訊
>svn pg svn:mergeinfo -R wc\branches\c
wc\branches\c - /branches/a:3-11
/branches/b:10-13
/trunk:5-14
現在我們對 trunk 進行一些變更
正在進行一些工作...
>svn ci -m "some changes under trunk" wc
傳送 wc\trunk\jobs\index.html
傳輸檔案資料。
已提交版本 18。
正在進行一些工作...
>svn ci -m "some changes under trunk" wc
傳送 wc\trunk\jobs\index.html
傳輸檔案資料。
已提交版本 19。
正在進行一些工作...
>svn ci -m "some changes under trunk" wc
傳送 wc\trunk\about\index.html
傳送 wc\trunk\jobs\index.html
傳送檔案資料 ..
提交版本 20。
現在開始合併。假設我們需要將 r18、r19 和 r20 的變更合併到 c 分支,但我們只想要影響 jobs 子樹的變更,而不是 about 子樹的變更,因此我們將變更直接合併到 branches\c\jobs
>svn merge %URL%/trunk/jobs wc\branches\c\jobs -c18,19,20
--- 將 r18 合併到 'wc\branches\c\jobs'
U wc\branches\c\jobs\index.html
--- 將 r19 合併到 'wc\branches\c\jobs'
G wc\branches\c\jobs\index.html
--- 將 r20 合併到 'wc\branches\c\jobs'
G wc\branches\c\jobs\index.html
這當然表示 branches\c\jobs 現在有自己的明確合併資訊,與 branches\c 不同
>svn pg svn:mergeinfo -R wc\branches\c
wc\branches\c - /branches/a:3-11
/branches/b:10-13
/trunk:5-14
wc\branches\c\jobs - /branches/a/jobs:3-11
/branches/b/jobs:10-13
/trunk/jobs:5-14,18-20
一旦我們提交合併,請注意我們現在有一個混合版本工作副本
>svn ci -m "將一些變更從 trunk 合併到 c 分支的子樹" wc
傳送 wc\branches\c\jobs
傳送 wc\branches\c\jobs\index.html
傳輸檔案資料。
提交版本 25。
>svnversion wc\branches\c
17:25
版本 25?看起來其他開發人員對儲存庫進行了變更,導致我們最初簽出和提交之間出現新的版本 r21-r24。
稍後,我們需要將 r20 中其他會觸及 about 子樹的變更合併到 branches\c。回想一下老闆對合併到分支根目錄的抱怨,我們決定將 r20 合併到分支的根目錄。我們也選擇重新合併 r18 和 r19,知道合併追蹤邏輯應該會防止重複合併,並消除 branches\c\jobs 上的合併資訊
>svn merge %URL%/trunk wc\branches\c -c18,19,20
--- 將 r20 合併到 'wc\branches\c'
U wc\branches\c\about\index.html
在提交之前,我們會檢查分支\c 上的合併資訊。這是什麼?分支\c\jobs\index.html 仍然有合併資訊,而且看起來與分支\c 上的合併資訊完全相同。為什麼分支\c\jobs 上的合併資訊不會省略到分支\c?
>svn pg svn:mergeinfo -R wc\branches\c
wc\branches\c - /branches/a:3-11
/branches/b:10-13
/trunk:5-14,18-20
wc\branches\c\jobs - /branches/a/jobs:3-11
/branches/b/jobs:10-13
/trunk/jobs:5-14,18-20
問題出在我們混合修訂版的 working copy
>svn st -v wc\branches\c
M 17 16 cuser wc\branches\c
17 15 merger wc\branches\c\products
17 2 user wc\branches\c\products\little.html
17 15 merger wc\branches\c\products\medium.html
17 2 user wc\branches\c\products\big.html
17 15 merger wc\branches\c\products\roadmap.html
17 15 merger wc\branches\c\products\index.html
17 15 merger wc\branches\c\about
M 17 15 merger wc\branches\c\about\index.html
17 15 merger wc\branches\c\index.html
17 15 合併 wc\branches\c\news
17 15 合併 wc\branches\c\news\index.html
17 2 使用者 wc\branches\c\support
17 2 使用者 wc\branches\c\support\index.html
25 25 pburba wc\branches\c\jobs
25 25 pburba wc\branches\c\jobs\index.html
請注意,branches\c\jobs 處於工作版修訂版 25,而 branches\c 的其他部分則處於 r17。Subversions 省略邏輯不會嘗試將 branches\c\jobs 上的合併資訊省略到工作副本的 branches\c,因為兩者處於不同的工作版修訂版。為什麼不?因為無法得知在 branches\c@17 上找到的合併資訊是否與 branches\c@25 相同。我們的同事在 r21-r24 中所做的變更可能會變更 branches\c 的合併資訊。如果發生這種情況,而且我們嘗試提交此合併,我們將會收到過期錯誤,而且必須在提交前更新工作副本。
我們可以使用 --show-updates 狀態檢查 r21-r24 是否會影響 branches\c 分支
>svn st --show-updates --verbose wc\branches\c
17 2 user wc\branches\c\products\little.html
17 15 merger wc\branches\c\products\medium.html
17 2 user wc\branches\c\products\big.html
17 15 merger wc\branches\c\products\roadmap.html
17 15 merger wc\branches\c\products\index.html
17 15 merger wc\branches\c\products
M 17 15 merger wc\branches\c\about\index.html
17 15 merger wc\branches\c\about
17 15 merger wc\branches\c\index.html
17 15 合併 wc\branches\c\news\index.html
17 15 合併 wc\branches\c\news
17 2 使用者 wc\branches\c\support\index.html
17 2 使用者 wc\branches\c\support
25 25 pburba wc\branches\c\jobs\index.html
25 25 pburba wc\branches\c\jobs
M 17 16 cuser wc\branches\c
針對修訂版的狀態: 25
沒有,沒有人對 branches\c 進行變更,如果他們有變更,我們會在第 8 欄看到 '*' 過期標記。因此,我們決定更新 branches\c
>svn up wc\branches\c
於版本 25。
現在分支處於一致的工作版本(且仍有未提交合併的本地變更)
>svnversion wc\branches\c
25M
我們仍想移除 branches/c/jobs 上的冗餘明確合併資訊,因此我們重複合併,依賴合併追蹤邏輯來避免任何重複合併(我們也可以在此使用 --record-only
合併,在這種情況下,兩者之間沒有實際差異)
>svn merge %URL%/trunk wc\branches\c -c18,19,20
>
很好,沒有發生合併,但可以肯定的是,合併資訊省略,現在工作副本處於一致版本,已發生
>svn st wc\branches\c -v
M 25 25 pburba wc\branches\c
25 15 merger wc\branches\c\products
25 2 user wc\branches\c\products\little.html
25 15 merger wc\branches\c\products\medium.html
25 2 user wc\branches\c\products\big.html
25 15 merger wc\branches\c\products\roadmap.html
25 15 merger wc\branches\c\products\index.html
25 15 merger wc\branches\c\about
M 25 15 merger wc\branches\c\about\index.html
25 15 merger wc\branches\c\index.html
25 15 合併 wc\branches\c\news
25 15 合併 wc\branches\c\news\index.html
25 2 使用者 wc\branches\c\support
25 2 使用者 wc\branches\c\support\index.html
M 25 25 pburba wc\branches\c\jobs
25 25 pburba wc\branches\c\jobs\index.html
>svn pg svn:mergeinfo -R wc\branches\c
wc\branches\c - /branches/a:3-11
/branches/b:10-13
/trunk:5-14,18-20
此範例示範了合併資訊省略的問題。如先前所述,省略和繼承基本上是同一件事,因此您也可以看到涉及繼承的混合版本工作副本的問題。假設我們在將前一次合併提交為 r26 之後,在某個時間點有下列混合版本工作副本
>svn st -v wc\branches\c
26 26 pburba wc\branches\c
26 15 合併 wc\branches\c\products
26 2 使用者 wc\branches\c\products\little.html
26 15 合併 wc\branches\c\products\medium.html
26 2 使用者 wc\branches\c\products\big.html
26 15 合併 wc\branches\c\products\roadmap.html
26 15 merger wc\branches\c\products\index.html
25 15 merger wc\branches\c\about
26 26 pburba wc\branches\c\about\index.html
26 15 merger wc\branches\c\index.html
26 15 merger wc\branches\c\news
26 15 merger wc\branches\c\news\index.html
26 2 user wc\branches\c\support
26 2 user wc\branches\c\support\index.html
26 26 pburba wc\branches\c\jobs
26 25 pburba wc\branches\c\jobs\index.html
>svn pg svn:mergeinfo -R wc\branches\c
wc\branches\c - /branches/a:3-11
/branches/b:10-13
/trunk:2,5-14,18-20
如果我們嘗試將修訂版 17:20 直接合併到 branches\c\about,Subversion 會嘗試找出 branches\c\about 的合併資訊,以便避免重複合併。 很不幸的是,它無法繼承 branches\c 的明確合併資訊,因為那是在不同的工作修訂版中。 因此,Subversion 會詢問伺服器 branches\c\about@25 的明確或繼承合併資訊。 該路徑在該修訂版中沒有明確合併資訊,但確實繼承了 branches\c@25 的合併資訊,但這不包括 r18-20,因為那直到 r26 才提交! 所以 Subversion 認為 r18-20 沒有套用在以 branches/c/about 為根的樹狀結構中,並重複合併
>svn merge %URL%/trunk/about wc\branches\c\about -c18,19,20
--- 將 r20 合併到 'wc\branches\c\about'
U wc\branches\c\about\index.html
在這種情況下,發生的事情只有 branches\c\about 取得明確合併資訊,儘管重複合併的衝突很容易發生
>svn st wc\branches\c
M wc\branches\c\about
>svn diff wc\branches\c
wc\branches\c\about 的屬性變更:
___________________________________________________________________
已新增:svn:mergeinfo
已合併 /branches/b/about:r10-13
已合併 /trunk/about:r2,5-14,18-20
已合併 /branches/a/about:r3-11
如果我們還原該合併,並將合併目標更新為統一的工作副本修訂版,然後重複合併,我們會看到什麼事都沒有發生
>svn revert -R wc
已還原 'wc\branches\c\about'
>svn up wc
在修訂版 26。
>svn merge %URL%/trunk/about wc\branches\c\about -c18,19,20
>svn st wc
>
這是因為 branches\c\about 現在可以繼承 branches\c 的明確合併資訊,其中包含 r18-20。 看到這些修訂版已合併到 branches\c\about,Subversion 根本不會嘗試任何合併。
必須承認,這兩個範例都有些牽強,而且你可能永遠不會遇到類似的情況。 但是,如果你看到有關合併資訊的神秘行為,請務必檢查混合修訂版的工作副本是否為罪魁禍首。
作者羞怯地承認,曾幾何時,混和修訂工作副本和合併資訊繼承/省略的交集讓他感到困惑。甚至有一次,在理解發生什麼事之前,已經開始在 Subversion 問題追蹤器中撰寫新問題...
呼,涵蓋了許多內容!希望你對合併資訊運作方式有更深入的了解,特別是在一些非典型使用案例中。但是,如果你只是想避免所有這些複雜性呢?根據你的需求,這並不難做到。如果它們與你的開發流程相容,以下規則將讓你的合併資訊(和你的合併)盡可能直接
--reintegrate
。--record-only
選項。這足以解決大多數問題,而且不太可能讓你頭痛。 --depths
('empty
'、'files
' 或 'immediates
')。 是的,有很多禁止事項,再加一項:如果您需要,請不要猶豫執行所有這些操作。正如我在一開始所說,Subversion 始終會嘗試針對合併資訊為您「執行正確的動作」,希望這篇文章有助於您執行相同的動作。