外部儲存庫副本

本文經原始位置 http://blogs.collab.net/subversion/foreign-repository-copies 許可轉載。已移除或更新失效連結。

作者:C. Michael Pilato

張貼 2013-06-28

一段時間前,我在 此處寫道,關於我設法整合到 Subversion 中的一些新功能 — 從所謂的「外部儲存庫」(即與工作副本反映的不同儲存庫)將變更合併到工作副本中的功能。 我宣傳此功能為管理供應商分支的另一種絕佳方式。 我承認我可能在那篇文章中稍微忽略了細節,但這個想法看起來很簡單: 將第三方程式碼庫的基準副本匯入您的儲存庫,然後直接從其儲存庫將第三方所做的變更合併到您的本地基準副本中。 瞧! 供應商分支。

認識到外部儲存庫合併功能的潛力,我自己也開始使用這種方法。我有一些 CollabNet 相關和個人專案,使用第三方程式碼。例如,CollabNet 的 TeamForge 和 Subversion Edge 產品都使用 ViewVC。在許多情況下,需要對第三方程式碼庫進行一些小的自訂。供應商分支是管理這些變更的自然方式。

但是,當我開始將理論付諸實踐時,我在第一個分支初始化步驟中遇到了意外的問題:屬性。svn import 不會辨識或複製匯入來源中可能記錄的任何 Subversion 檔案或目錄屬性。因此,當我使用 svn import 以第三方程式碼的基線版本建立供應商分支時,我會取得混合資訊。當然,我在工作副本中完美複製了供應商的檔案內容和目錄結構。但是,元資料,例如 svn:ignoresvn:eol-stylesvn:keywords 等節點屬性,並非來自供應商的原始資料來源。它來自我自己的 自動屬性設定

請注意,在以下(有點牽強)範例中,svn import 實際上遺失了第三方程式庫中存在的 svn:ignore 屬性

$ svn co http://otherfolks.com/svn/trunk third-party-wc
A    third-party-wc/src
A    third-party-wc/src/source_file.c
A    third-party-wc/src/source_file.h
A    third-party-wc/notes
A    third-party-wc/notes/how_to_do_stuff.txt
A    third-party-wc/README
Checked out revision 61.
$ svn plist -vR third-party-wc/
Properties on 'third-party-wc/src':
  svn:ignore
    a.out
$ svn import -m "Initialize vendor branch." \
      third-party-wc https://127.0.0.1/svn/my-project/vendor
Skipped 'third-party-wc/.svn'
Adding         third-party-wc/src
Adding         third-party-wc/src/source_file.c
Adding         third-party-wc/src/source_file.h
Adding         third-party-wc/notes
Adding         third-party-wc/notes/how_to_do_stuff.txt
Adding         third-party-wc/README

Committed revision 13.
$ svn co https://127.0.0.1/svn/my-project/vendor vendor-wc
A    vendor-wc/src
A    vendor-wc/src/source_file.c
A    vendor-wc/src/source_file.h
A    vendor-wc/notes
A    vendor-wc/notes/how_to_do_stuff.txt
A    vendor-wc/Makefile
A    vendor-wc/README
Checked out revision 13.
$ svn plist -vR vendor-wc
$

糟糕!我們遺漏了供應商的 svn:ignore 屬性!

因此,供應商資料的初始匯入會進行得很順利,但之後 — 在執行包含屬性變更的外來儲存庫合併時 — 我會遇到屬性衝突。合併會顯示「將屬性 NAME 的值從 OLD_VALUE 變更為 NEW_VALUE」。我的工作副本會皺著眉頭回答:「但 NAME 的值根本不是 OLD_VALUE。它是 SOMETHING_ELSE」。衝突發生了。

$ svn merge -r61:62 http://otherfolks.com/svn/trunk vendor-wc
Conflict for property 'svn:ignore' discovered on '/home/cmpilato/vendor-wc/src'.
They want to change the property value to 'program', you want to delete the property.
Select: (p) postpone,
        (mf) mine-full, (tf) theirs-full,
        (s) show all options: p
--- Merging (from foreign repository) r62 into 'vendor-wc':
 C   vendor-wc/src
A    vendor-wc/Makefile
Summary of conflicts:
  Property conflicts: 1
$

唉。

在此範例中,解決衝突的方法很簡單。但重點不在此。顯然,我需要一個更好的方法來初始化我的供應商分支。我最後採用困難的方式進行一段時間。我不會使用 svn import,而是會使用 svn add,使用大量的 svn propset 呼叫手動模擬原始資料的屬性設定,然後 svn commit。這很麻煩,但有效。同時,我在 Apache Subversion 專案的錯誤追蹤器中提出功能要求,希望有更好的解決方案。

我很高興現在可以報告,由於 Subversion 的工作副本管理函式庫進行大規模改寫,這是 Subversion 1.7.0 的基石,再加上 CollabNet 同事 Bert Huijben 的額外努力,Subversion 1.8.0 提供對外來儲存庫拷貝的支援。現在,從外來儲存庫初始化供應商分支就像執行 svn copy 一樣簡單,外來儲存庫 URL 為來源,而本機工作副本目錄為目標。

$ svn cp http://otherfolks.com/svn/trunk my-project-wc/vendor
--- Copying from foreign repository URL 'http://otherfolks.com/svn/trunk':
A         my-project-wc/vendor
A         my-project-wc/vendor/src
A         my-project-wc/vendor/src/source_file.c
A         my-project-wc/vendor/src/source_file.h
A         my-project-wc/vendor/notes
A         my-project-wc/vendor/notes/how_to_do_stuff.txt
A         my-project-wc/vendor/README
$ svn ci -m "Initialize third-party vendor branch." my-project-wc
Adding         my-project-wc/vendor
Adding         my-project-wc/vendor/README
Adding         my-project-wc/vendor/notes
Adding         my-project-wc/vendor/notes/how_to_do_stuff.txt
Adding         my-project-wc/vendor/src
Adding         my-project-wc/vendor/src/source_file.c
Adding         my-project-wc/vendor/src/source_file.h
Transmitting file data ....
Committed revision 13.
$ svn plist -vR my-project-wc/vendor
Properties on 'my-project-wc/vendor/src':
  svn:ignore
    a.out
$

這次,我們的供應商 svn:ignore 屬性已保留!這表示稍後,當需要合併供應商的最新變更時,合併應該可以順利進行(當然,前提是我們沒有引入衝突的自訂設定)

$ svn merge -r61:62 http://otherfolks.com/svn/trunk my-project-wc/vendor
--- Merging (from foreign repository) r62 into 'my-project-wc/vendor':
 U   my-project-wc/vendor/src
A    my-project-wc/vendor/Makefile
$

太棒了!

為了完整性,我應該注意到複製過程中有一個例外,即外來儲存庫資料的完美保留:在其中找到的任何 svn:mergeinfo 屬性都會從在您的本地工作中建立的複製品中過濾出來。這樣做可以避免 Subversion 認為合併追蹤資訊套用於您自己的儲存庫記錄時可能會發生的許多問題。但一切都會好轉。由於外來儲存庫合併同樣會忽略 svn:mergeinfo 屬性,因此此例外情況不會阻止您使用此類合併來維護您的供應商分支!

此外,雖然 svn copy 命令語法允許複製的來源和目標分別是儲存庫 URL 或工作副本路徑,但只有 svn copy REPOS-URL WC-PATH 語法支援外來儲存庫複製。例如,您無法直接從一個儲存庫中的 URL 複製到另一個儲存庫中的 URL 目標。

有關使用此方法管理供應商分支的更多詳細資訊,請查看我重新編寫的使用 Subversion 進行版本控制一書章節。有關 Subversion 1.8 提供的更多資訊,請參閱官方發行說明

(感謝 Bert!)

關於作者

C. Michael Pilato 是 Subversion 核心開發人員,也是《使用 Subversion 進行版本控制》(O'Reilly Media)的共同作者,以及 ViewVC 的主要維護人員。他遠距在北卡羅來納州的家鄉為 CollabNet 擔任軟體工程師,並自 2001 年初以來一直是活躍的開源開發人員。Mike 是一位以身為丈夫和父親為傲的人,他熱愛旅行、足球、與家人共度美好時光,以及這些事物的任何組合。他也喜歡創作和演奏音樂,並懷抱著不那麼秘密的搖滾明星幻想。Mike 擁有北卡羅來納大學夏洛特分校的電腦科學和數學學位。