Subversion 和 GIT 開發者演進 頂 原 薦

##前言 在開發軟件的過程中,往往是需要多個人參與,版本控制系統的協同工作的重要性不言而喻,除此之外, 版本控制軟件對整個開發流程的記錄對於缺陷追蹤也是非常重要的。版本控制系統也是軟件開發的基礎設施。

筆者開始接觸版本控制系統是大學的時候,最開始安裝了 TortoiseSVN ,然而 TortoiseSVN 僅僅是佔據了硬盤空間而沒有發揮作用,很多開發者在接觸新事物的時候,並不一定會有極大的熱情去了解, 有的走了很多彎路後返回到了原地,只有當深入瞭解以後,才覺得其中異常的精彩。當我在 Windows 下編譯 LLVM 的時候, Subversion 開始發揮作用,彼時,幾乎所有開源的大型軟件都是使用 Subversion 進行託管,當然還有部分 CVS。 GIT 遠遠沒有目前流行。後來參加工作後,就是代碼託管的工作,對 Subversion 和 Git 有了一定程度的瞭解, 逐漸有了自己的思考。

大多數人對版本控制系統的解讀都是站在使用者的角度,而本文是站在一個代碼託管的開發者立場。

##版本控制系統見聞 版本控制系統的歷史可以追溯到20世紀70年代,這是一個軍方開發的 CCC (變更和配置控制)系統,名字叫做 CA Software Change Manager 隨後,版本控制系統開始發展起來。

CVS 一度曾經是開源軟件的第一選擇,比如 GNOME、KDE、THE GIMP 和 Wine, 都曾使用過 CVS 來管理。這是一個集中式的版本控制系統,同樣是集中式的還有 Subversion, Visual SouceSafe Perforce,Team Foundation Server。

由於難以忍受 CVS,CollabNet 的開發者開發了著名的 Subversion(SVN) 來取代 CVS, Subversion 誕生於 2000 年, 時至今日,SVN 依然是最流行的集中式版本控制系統,GCC ,LLVM 等開源軟件都使用 SVN 管理,代碼託管網站方面, SourceForge 提供 SVN 的代碼託管。

Visual SouceSafe(VSS)是微軟開發的版本控制系統,到了 2008年,被 Team Foundation Server(TFS) 取代, TFS 並不是傳統意義的版本控制系統,而是雲開發協作平臺,支持 Team Foundation Version Control 和 Git, 像微軟這樣的企業,無論是 Windows 還是 Office 還是 其他軟件,代碼量都非常巨大,只有像 TFS 這樣量身定做的系統才合適。

Perforce 是一個商業的版本控制系統,在其官網 www.perfoce.com 介紹, 有着超過10000個用戶使用他們的服務,有 NVIDIA ,Sumsung,vmware,adidas 等著名企業,而我對他的印象在是 OpenWATCOM C/C++ 編譯器以及 p4merge 工具。p4merge 是 Perforce 提供的一個基於 Qt 開發的跨平臺比較工具。

與集中式版本控制系統對應的是分佈式版本控制系統 (Distribution Version Control System) 比較流行的有 git 和 Mercurial, 二者均誕生於 2005 年。

Git 由 Linux 之父, Linus Torvalds 爲了替代 BitKeeper 而開發的,關於 Git 的誕生,可以看對 Linus 本人的採訪: 10 Years of Git: An Interview with Git Creator Linus Torvalds Git 非常流行, Linux, FreeBSD, .NET Core CLR, .NET Core Fx, Minix, Android 等項目都使用 Git 來管理, Git 的社區非常成熟,有很多代碼託管網站提供託管服務,如 Github, Bitbucket, 國內有 OSC@GIT,coding,gitcafe, CSDN code, jd code 等等。

技術上同樣優秀的版本控制系統 Mercurial 的使用者少很多,也有著名的瀏覽器 Mozilla Firefox,服務器 Nginx,以及編程語言 Python。 Mercurial 使用 Python 實現,或許這一點也限制了 Mercurial 的發展。

在維基百科中有一個 VCS 列表: Template:Version control software 記錄了多種版本控制系統,誕生時間,分類。

大多數時候,開發者需要學習的版本控制系統爲 Subversion 或者是 GIT。這二者已然是兩個版本控制流派的代表。

##Git 技術內幕 本節主要介紹 Git 的存儲和傳輸

###Git 存儲
git 倉庫在磁盤上可以表現爲兩種形式,帶有工作目錄的普通倉庫和不帶工作目錄的裸倉庫。
我們可以創建一個標準倉庫:

mkdir gitrepo &&cd gitrepo &&git --init &&tree -a

目錄結構如下

.
├── .git
│   ├── branches
│   ├── COMMIT_EDITMSG
│   ├── config
│   ├── description
│   ├── HEAD
│   ├── hooks
│   │   ├── applypatch-msg.sample
│   │   ├── commit-msg.sample
│   │   ├── post-update.sample
│   │   ├── pre-applypatch.sample
│   │   ├── pre-commit.sample
│   │   ├── prepare-commit-msg.sample
│   │   ├── pre-push.sample
│   │   ├── pre-rebase.sample
│   │   └── update.sample
│   ├── index
│   ├── info
│   │   └── exclude
│   ├── logs
│   │   ├── HEAD
│   │   └── refs
│   │       └── heads
│   │           └── master
│   ├── objects
│   │   ├── 89
│   │   │   └── 50b8b1af3c4cc712edb5a995c83a53eb03e6be
│   │   ├── d0
│   │   │   └── 2d9281b58703d020c3afe3e2ace204d6d462ae
│   │   ├── e6
│   │   │   └── 9de29bb2d1d6434b8b29ae775ad8c2e48c5391
│   │   ├── info
│   │   └── pack
│   └── refs
│       ├── heads
│       │   └── master
│       └── tags
└── helloworld

實際上我們創建一個裸倉庫會發現和普通倉庫的 .git 目錄結構是一致的。

mkdir gitbare.git &&cd gitbare.git &&git init --bare &&tree -a

目錄結構:

.
├── branches
├── config
├── description
├── HEAD
├── hooks
│   ├── applypatch-msg.sample
│   ├── commit-msg.sample
│   ├── post-update.sample
│   ├── pre-applypatch.sample
│   ├── pre-commit.sample
│   ├── prepare-commit-msg.sample
│   ├── pre-push.sample
│   ├── pre-rebase.sample
│   └── update.sample
├── info
│   └── exclude
├── objects
│   ├── info
│   └── pack
└── refs
    ├── heads
    └── tags

9 directories, 13 files

當我們創建一個倉庫時,默認情況下會創建工作目錄,在工作目錄下有個 .git 的子目錄,這纔是存儲庫的目錄。 而我們通常修改代碼的目錄稱之爲工作目錄。

衆所周知,git 是分佈式版本控制系統,這就意味着,只要獲得了 .git 目錄的完整數據,就可以在任意位置恢復成一個帶有工作目錄的倉庫。而 GIT 克隆一個存儲庫也僅僅是將 .git/objects 目錄下的 object 和 .git/refs (.git/packed-refs|.git/info/refs) 所存儲的引用列表傳輸到本地,並應用。

對於 Subversion 一樣的集中式版本控制系統,就相當於 .git 目錄被託管在中央服務器上,而本地的 .svn 只是工作目錄的元數據。

二者不同的機制帶來的直接差別就是一旦中央服務器宕機,git 可以迅速的遷移到其他服務器,並且數據的丟失的可能性很小,而 Subversion 服務器就沒有這麼好的運氣了。

每一次提交,git 都會把修改的文件快照,還有更新的目錄結構,以及提交信息,打包成一個個 object,這些 object 被loose object, 所以 git 的 object 可能是 blob tree commit 等。打包的過程會使用 zip 壓縮,這種被廣泛運用的壓縮格式實上壓縮率較低,壓縮速度也慢,但好處有廣泛的支持,專利上比較友好。

如果調用 git gc 命令後,git-gc 會將這些 object 打包成 pack 文件,這些內容在 proGit 都有詳細說明。

###Git 傳輸協議 Git 支持多種協議 http, git , ssh, file ,以內部機制區分爲啞協議和智能協議,啞協議非常簡單,簡單的說, 客戶端通過 URL 直接拿取服務端的文件。
Git 智能協議實現了兩類 RPC 調用,一個是 fetch-pack<->upload-pack, 另一個是 send-pack<->receive-pack。

任何 Git 遠程操作都需要獲得遠程倉庫的引用列表,與自身的引用列表進行比對

這裏以 HTTP 爲例

1 Fetch-Upload
Step 1:
Request

C: GET $GIT_URL/info/refs?service=git-upload-pack HTTP/1.0

Response

S: 200 OK
S: Content-Type: application/x-git-upload-pack-advertisement
S: Cache-Control: no-cache
S:
S: 001e# service=git-upload-pack\n
S: 004895dcfa3633004da0049d3d0fa03f80589cbcaf31 refs/heads/maint\0multi_ack\n
S: 0042d049f6c27a2244e12041955e262a404c7faba355 refs/heads/master\n
S: 003c2cb58b79488a98d2721cea644875a8dd0026b115 refs/tags/v1.0\n
S: 003fa3c2e2402b99163d1d59756e5f207ae21cccba4c refs/tags/v1.0^{}\n

Step 2:
Request

C: POST $GIT_URL/git-upload-pack HTTP/1.0
C: Content-Type: application/x-git-upload-pack-request
C:
C: 0032want 0a53e9ddeaddad63ad106860237bbf53411d11a7\n
C: 0032have 441b40d833fdfa93eb2908e52742248faf0ee993\n
C: 0000

Response

S: 200 OK
S: Content-Type: application/x-git-upload-pack-result
S: Cache-Control: no-cache
S:
S: ....ACK %s, continue
S: ....NAK

2 Send-Receive 實際上 push 的過程也是 GET 和 POST, 只不過,git-upload-pack 要變成 git-receive-pack ,POST 時,後者請求體中包含有 差異 package。

對於 git HTTP 來說,權限驗證通常是 HTTP 的一套,也就是 WWW-Authenticate, 絕大多數的 HTTP 服務器也就支持 Basic。
即:

user:password ->Base64 encode -->dXNlcjpwYXNzd29yZA==

所以從安全上來說,如果使用 HTTP 而不是 HTTPS , 對 GIT 遠程倉庫進行寫操作簡直就是在裸奔。

git HTTP 支持的 HTTP 返回碼並不多,這些是返回碼是支持的: 200 30x 304 403 404 410

關於 HTTP 的更多文檔細節可以去這個地址查看: HTTP Protocol

基於 HTTP 的智能協議和基於 SSH,Git 協議本質上並無太大的不同,都是通過這兩類 RPC 調用,實現本地倉庫和遠程倉庫的數據交換。

HTTP 協議是通過 http smart server 運行 git-xxx-pack,對其輸入數據,然後讀取 git-xxx-pack 輸出。 SSH 則是通過 ssh 服務器在遠程機器上運行 git-xxx-pack ,數據傳輸的過程使用 SSH 加密。 而 GIT 協議 (git://) 協議則是 通過遠程服務器 git-daemon 運行 git-xxx-pack 實現數據的交互。通常來說 git:// 無法實現差異化的權限管理, 也就是要麼全部只讀,全部可寫。

查看 git daemon 程序幫助:

git help daemon

一些更多的技術內幕可以參考 社區大作 《Pro Git》

##Git 代碼託管平臺的開發演進
雖然 GIT 是分佈式版本控制,但是對於代碼託管平臺來說又是一回事了。對於 HTTP 協議來說,像 NGINX 一樣的服務器只需要實現動態 IP, 然後通過 proxy 或者是 upstream 的方式實現 GIT 代碼託管平臺的 分佈式就可以了。但是對於 SSH 來說比較麻煩。

###基於 RPC 的 GIT 分佈式設計
客戶端訪問倉庫時,路由智能到達 DNS 所記錄的機器或者是無差別代理的機器(前端機器),往往不能到達特定的存儲機器, 開發者使用分佈式文件系統或者 分佈式 RPC 或者代理等多種方案實現 前端到存儲的關鍵一步。這裏主要說分佈式 RPC 與 GIT smart 的應用。
分佈式 RPC 框架很多,其中著名的有 Apache Thrift ,此項目是 Facebook 開源並貢獻給 Apache 基金會的,支持多種語言。
對於 GIT 操作,只需要實現 4個函數。一下是 Thrift 接口文件的一部分:

service GitSmartService{
	i32 Checksum(1:i32 client);
	string FetchRemoteReferences(1:string repositoryPath);
	binary FetchRemoteDiffPackage(1:string repositoryPath, 2:string clientReferences)
	string PushRemoteRefereces(1:string repositoryPath);
	string PushRemoteDiffPackage(1:string repositoryPath, 2:binary clientPackage);
}

然後存儲服務器通過 pipe 讀取存儲機器上的 git-upload-pack /git-receive-pack 的輸入輸出。 在 Linux 上通過管道讀取 git upload-pack 的輸出:

int FetchRemoteReferencesCli(std::string &result,const std::string &path){
	result.clear();
	int pid,fd[2];
	if(pipe(fd)<0){
		printf("oops\n");
	}
	if((pid=fork())<0){
		printf("fork failed \n");
		return -1;
	}else if(pid==0){
		if(fd[1]!=STDOUT_FILENO){
			if (dup2(fd[1], STDOUT_FILENO) != STDOUT_FILENO){  
				return -1; 
			} 
            close(fd[1]);  
		}
		if(execlp("git","git","upload-pack","--stateless-rpc","--advertise-refs",path.c_str(),NULL)==-1){
			printf("execlp failed \n");
			exit(0);
		}
	}else{
		char buffer[4096]={0};
		close(fd[1]);
		int n=0;
		while((n=read(fd[0],buffer,4096))){
			result.append(buffer,n);
		}
		close(fd[0]);
	}
	return 0;
}

前端服務器上,編寫 模擬 git-upload-pack 或者是 git-receive-pack 的程序。用戶通過 ssh 訪問遠程倉庫時執行的 git 工具變成了模擬後的 git-upload-pack /git-receive-pack, 當使用 HTTP 訪問時,可以整合成 RPC 客戶端整合直接整合進 HTTP 服務器,比如 NGINX 模塊, 或者也可 使用 傳統的 Git Smart HTTP 庫的方式,總的來說 Thrift 有多種語言支持,Git Smart HTTP 整合 Thrift RPC 並不成問題。
這個唯一的問題是實現異步比較麻煩,兩者都需要實現異步模式,git 倉庫可能非常大,一次性克隆傳輸數據幾百 MB 或者上 GB, 這個時候 4nK 發送非常必要。

###基於 libgit2 的 smart 協議實現
GIT 除了 Linus 本人實現,kernel.org 託管的官方版本外,還有 jgit,libgit2 等,git 是一系列命令組成,幾乎沒有剝離出共享庫的能力, 這樣的後果導致其他語言使用 git 時,不得不使用管道等進程間通訊的模式與 git 工具交互。而 jgit 使用 Java 實現,基本上沒有其他流行語言的綁定能力。
libgit2 是一個 GIT 的兼容實現,基於 C89 開發,支持絕大多數 git 特性。開發非常活躍,有多種語言綁定,如 C# Ruby 等, 其中 C# 綁定 Libgit2Sharp 被 VisualStudio, Github for Windows 等使用,而 Ruby 綁定 Rugged ,被 Github, GIT@OSC 等代碼託管平臺使用。

libgit2 並沒有合適的 GIT smart 服務器後端實現,多數情況下,libgit2 主要面向的是客戶端,由於 git 是分佈式的,對於倉庫的讀寫也就客戶端 和服務器的行爲也是類似的。

##Subversion 內幕 此部分中 SVN 協議 指 Apache Subversion 程序 svn(以及兼容的客戶端) 與遠程服務器上的 Apache Subversion svnserve (以及兼容的服務器) 進程通訊的協議, 即 Subversion protocol,協議默認端口是 3690,基於 TCP, 傳輸數據使用 ABNF 範式。

在這裏指出,與 Git 完全不同的是,svn 的倉庫存儲在遠程中央服務器上,開發者檢出的代碼只是特定版本,特定目錄的代碼,本地爲工作拷貝。

###Subversion HTTP 協議實現 Subversion HTTP 協議是一種 基於 WebDAV/DeltaV 的協議,WebDAV 在 HTTP 1.1 的基礎上擴展了多個 Method, 絕大多數的服務器並不支持 WebDAV, 這樣的後果就是,除了 Apache 可以使用 mod_dav_svn 插件,基本上再也沒有其他的服務器能快速的支持 Subversion 的 HTTP 協議了。代理還是可以的。

WebDAV 協議在 HTTP 1.1 的基礎上 使用 XML 的方式呈現數據,對於 Subversion 這種集中式版本控制系統來說,絕大多數操作都是在線的, WebDAV 包裹這些操作就變得很繁瑣。

比如一個 update-report 請求:

  <S:update-report send-all="true" xmlns:S="svn:">
    <S:src-path>http://localhost:8080/repos/test/httpd/support</S:src-path>
    <S:target-revision>2</S:target-revision>
    <S:entry rev="2"  start-empty="true"></S:entry>
  </S:update-report>

然後服務器返回:

<S:update-report xmlns:S="svn:" xmlns:V="..." xmlns:D="DAV:" send-all="true">
  <S:target-revision rev="2"/>
  <S:open-directory rev="2">
    <D:checked-in>
      <D:href>/repos/test/!svn/ver/2/httpd/support</D:href>
    </D:checked-in>
    <S:set-prop name="svn:entry:committed-rev">2</S:set-prop>
    ... more set props ...
    <S:add-file name="ab.c">
      <D:checked-in>
        <D:href>/repos/test/!svn/ver/2/httpd/support/ab.c</D:href>
      </D:checked-in>
      <S:set-prop name="svn:entry:committed-rev">2</S:set-prop>
      ... more set props for the file ...
      <S:txdelta>...base64-encoded file content...</S:txdelta>
    </S:add-file>
    <S:add-directory name="os" bc-url="/repos/test/!svn/bc/2/httpd/os">
      <D:checked-in>
        <D:href>/repos/test/!svn/ver/2/httpd/os</D:href>
      </D:checked-in>
      ...directory contents...
    </S:add-directory>
  </S:open-directory>
</S:update-report>

不同的請求,xml 的內容也完全不同,Subversion HTTP 協議的複雜也讓很多開發者望而卻步。

在 Subversion 的路線圖中,基於 WebDAV/DeltaV 的 HTTP 接入將被 基於 HTTP v2 的實現取代。
A Streamlined HTTP Protocol for Subversion

###Subversion SVN 協議實現 與 HTTP 不同的是,一個完整的基於 SVN 協議的連接中,倉庫的操作是上下文相關的。
當客戶端的連接過來時,服務器,通常說的 svnservice 將發送一段信息給客戶端,告知服務器的能力。

S: ( minver:number maxver:number mechs:list ( cap:word ... ) )

Example:

( success ( 2 2 ( ) ( edit-pipeline svndiff1 absent-entries depth inherited-props log-revprops ) ) ) 

這個時候客戶端獲知了這些數據,如果無法兼容,服務器,那麼將斷開與服務器的連接,否則,將發送請求數據給服務器,格式如下:

C: response: ( version:number ( cap:word ... ) url:string
              ? ra-client:string ( ? client:string ) )

Example:

( 2 ( edit-pipeline svndiff1 absent-entries depth mergeinfo log-revprops ) 36:svn://subversion.io/subversion/trunk 53:SVN/1.8.13-SlikSvn-1.8.13-X64 (x64-microsoft-windows) ( ) )

與 GIT 數據包類似的地方有一點,git 每一行數據前 4 個16進制字符代表本行的長度,而 這裏的 10 進制字符代表 字符的長度,比如 URL 長度36,UA 53。

服務器此時的行爲就得通過解析 URL 獲得中央倉庫的位置,判斷協議是否兼容,而 UA 有可能爲空,格式並不是非常標準,所以這是值得注意的地方。

服務器將決定使用那種授權方式,MD5 一般是 Subversion 客戶端默認的,無法第三方庫支持,而 PLAIN 和 ANONYMOUS 需要 SASL 模塊的支持, 在 Ubuntu 上編譯 svn,先安裝 libsasl2-dev。

S: ( ( mech:word ... ) realm:string )

客戶端不支持此授權方式時,會輸出錯誤信息,“無法協商驗證方式”

這裏的 Realm 是 subversion 客戶端存儲用戶賬戶用戶名和密碼信息的一個 key,只要 realm 一致,就會取相同的 用戶名和密碼。 realm RFC2617

Example:

( success ( ( PLAIN ) 36:e967876f-5ea0-4ff2-9c55-ea2d1703221e ) ) 

如果是 MD5 ,驗證協商如下:

S: ( mech:word [ token:string ] )

這個 Token 是隨機生成的 UUID, C++ 可以使用 boost 生成,也可以使用平臺的 API 生成。

如果是 PLAIN 授權機制,這裏就是用戶名和密碼經 Base64 編碼了, 用 NUL(0) 分隔

usernameNULpassword --> Base64 Encoded

Example:

( PLAIN ( 44:YWRtaW5Ac3VidmVyc2lvbi5pbyU1QzBwYXNzd29yZA== ) ) 

對於純 svn 協議來說,使用 PLAIN 並不安全,且當 Subversion 只作爲 GIT 代碼託管平臺的一個服務來說, 使用 CRAM-MD5 並不利於服務整合,這也是一個缺陷了。

這是服務器的下一步驟:

S:  challenge: ( step ( token:string ) )
S:           | ( failure ( message:string ) )
S:           | ( success [ token:string ] )

Incorrect credentials:

( failure ( 21:incorrect credentials ) ) 

Success

( success ( ) )

隨後服務器再發送存儲庫 UUID, capabilities 給客戶端

S: ( uuid:string repos-url:string ( cap:word ... ) )

Example:

( ( 36:0f475597-c342-45b4-88c5-7dc0857b8ba4 36:svn://subversion.io/subversion/trunk ( edit-pipeline svndiff1 absent-entries depth inherited-props log-revprops ))

如果是 svn up/commit 或者其他的操作,這個時候會檢查 uuid 是否匹配,當然也會檢查 URL 是否匹配。

如果客戶端覺得一切都 OK 啦,那麼就會開始下一階段的操作,command 模式,這些規則可以從 Subversion 官方存儲庫查看 Subversion Protocol

與 GIT 或者 SVN HTTP 不同的是,一個完整的 基於 svn 協議的 SVN 操作,只需要建立一次 socket,Subversion 客戶端此時是阻塞的,並且屏蔽了 Ctrl+C 等 信號, 倉庫體積巨大時,這種對連接資源的佔用非常突出,因爲有數據讀取, socket 並不會超時。這樣的機制使得 svn 服務器的併發受到了限制。

###Subversion 兼容實現 Github 基於 HTTP 協議的方式實現了對 Subversion 的兼容,而 GIT@OSC 基於 svn 協議方式實現了對 Subversion 的不完全兼容。

基於 HTTP 協議實現的 Subversion 兼容服務和 基於 SVN 協議的 Subversion 兼容服務二者並不能說誰就一定好,HTTP 協議很容易導致網關超時, 多大數情況下,一次完整的操作時成千上萬的 HTTP 請求構成,HTTP 協議支持需要 HTTP 服務器能夠支持 WebDAV, XML 解析過程比較麻煩, Subversion 官方也計劃使用 HTTP v2 取代 WebDAV,但 HTTP 協議的好處還是有的,比如很多企業並不一定開放 SVN 端口 3690, 可以和 gitlab 之類的服務整合。

而 SVN 協議也有不好的地方,比如連接時間過長,服務器併發上不去,容易阻塞,與 HTTP 服務整合不便,但同時 SVN 協議能夠支持較大存儲庫。

實際上兼容實現 SVN 接入往往沒有原生的 SVN 服務好,這點事毋庸置疑的。

###Subversion 協議代理服務器的實現 前面並不完全的分析了 SVN 協議,但是那些協議內容足夠實現一個 SVN 協議動態代理服務器了。

在客戶端 C 和代理服務器 S 建立連接後, S 向 C 發送一個數據包:

  • 服務器頭
#S to C
( success ( 2 2 ( ) ( edit-pipeline svndiff1 absent-entries depth inherited-props log-revprops ) ) )

C 接收到 S 的數據後,必須做出選擇,併發送第一個請求給 S。

#C to S
( 2 ( edit-pipeline svndiff1 absent-entries depth mergeinfo log-revprops ) 43:svn://subversion.io/apache/subversion/trunk 53:SVN/1.8.13-SlikSvn-1.8.13-X64 (x64-microsoft-windows) ( ) )

S 接收到 C 的請求後,解析 數據包,提取到 URL 爲 svn://subversion.io/apache/subversion/trunk , 而 Gitlab 的規則是 host/user/repo, 如果不同用戶的存儲庫放在不同機器上,這個時候提取到用戶爲 apache, 交由路由選擇模塊去處理得到後端的地址,也就是真實 svnserve 的 IP 和端口。

建立與後端服務器 B 的連接。這個時候 S 讀取 B 的數據包,也就是前面的服務器頭,接收完畢直接丟棄即可,然後將客戶端 C 的頭請求轉發給後端服務器。

#S to B
( 2 ( edit-pipeline svndiff1 absent-entries depth mergeinfo log-revprops ) 43:svn://subversion.io/apache/subversion/trunk 53:SVN/1.8.13-SlikSvn-1.8.13-X64 (x64-microsoft-windows) ( ) )

這裏值得注意的是 svnkit,Subversion Javahl 並沒有添加 UA 字符串,所以解析時略過即可。

至此,代理服務器的後面就不必關係細節了,GIT@OSC 使用 Boost.ASIO 異步框架,

Client <---> Proxy Server <---> Backend Subversion Server

一個基本的 SVN 協議動態代理服務器就實現了。

##結尾 如果你不是專業的 Git 或者 Subversion 開發者,你可能會覺得上面的內容沒什麼用處,實際上也沒什麼技術難度。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章