2013-01-18 102 views
9

我會先說這個問題在性質上與this類似。有一個關鍵的區別使得它獨一無二:我想使用原始的git協議(如果您不熟悉基本包網絡協議,請參閱herehere)。Git以編程方式從遠程存儲庫獲取單個文件

我正在寫一個應用程序,使用Scala和JGit將連接到匿名git存儲庫。我想請求一個blob(認爲「/path/to/file.txt」@「refs/heads/branch1」)。最終,我的目標是以編程方式從遠程存儲庫中檢索單個文件。似乎是一件非常有用的事情,能夠做到。

任何人,我一直在鑽研這個協議的內部。看起來,這個基本版本是「我想要這些對象,我有這些對象」 - 而巴姆,還有一個你沒有的東西的包文件。我的問題的核心是這樣的:我如何以非遞歸方式請求單個對象的git-upload-packfile?我可以下載一個提交對象,然後詢問樹,然後是子樹,然後是另一個子樹,最後是blob本身。這裏速度並不太重要,主要是我試圖節省帶寬。但似乎沒有辦法告訴git-upload-packfile,「請只給我一個我要求的對象」。

是的,這裏有「有」列表,它基本上排除了下來的對象,但是需要事先知道存儲庫內容(我沒有本地存儲庫,請記住)。我可以生成所有可能的sha1列表,併發送除了我想要的所有列表以外的所有列表,但這超出了可笑(耗費時間,帶寬消耗,並且對任何地方的程序員都是犯罪)

另一個可能的解決方案深入研究是在遠程端使用git-upload-archive,儘管我承認我還沒有花太多時間研究它。

如果涉及到,我更願意重寫JGit,所以請不要把它寫成「我該如何讓JGit做...」。我只是想知道協議本身是否能夠做到這一點。我覺得有一些非常聰明的方法來濫用協議來達到我想要的效果。有什麼想法嗎?

+0

我想任何答案都必須以「'git'不能這樣工作」... – twalberg

+0

@twalberg本地回購確實。只是好奇,如果我能通過電線做到這一點。 –

+0

是的,您可以從本地回購中提取單個對象。有線協議並沒有設計成這樣做。我認爲,通過克隆/獲取(即使使用淺克隆來避免完整的回購,但你仍然會獲得commit + trees + blob)到本地回購並提取對象,你可以完成最終目標,但是我不要以爲你可以通過網線來完成(雖然如果你有ssh訪問遠程的話,你可以將它僞造)。 – twalberg

回答

9

回答我自己的問題。我發現了一個可接受的(儘管幾乎沒有文件)的答案。我不得不挖掘很多C代碼來解決這個問題。

首先,使用git-upload-packfile無法實現上述要求,因爲這根本不是該程序設計的目的。我懷疑的正確答案是git-upload-archive。可悲的是,協議幾乎沒有記錄在ALL中。所以這裏是我的筆記,以防其他人有類似的要求。

基本上我想在這裏模擬(中階)是下面的命令:

git archive --format=tar --remote=ssh://[email protected]/cornballer.git \ 
    > master plans/documents/cornballer-blueprint.pdf | tar -x 

除了在軟件,希望用JGit。可悲的是JGit不支持(還)支持git歸檔命令。所以這裏有一個關於如何添加支持的非常高層次的概述(我可以分叉JGit並在稍後添加)。

讓我們來看看協議(來自Documentation/technical/pack-protocol。TXT):

git-proto-request = request-command SP pathname NUL [ host-parameter NUL ] 
request-command = "git-upload-pack"/"git-receive-pack"/
        "git-upload-archive" ; case sensitive 
pathname   = *(%x01-ff) ; exclude NUL 
host-parameter = "host=" hostname [ ":" port ] 

所以部分協議的一個是這樣的:

  1. 建立傳輸與遠程(或SSH,然後運行git-upload-archive或使用匿名Git協議)
  2. 發送git-upload-archive /cornballer.git\0host=ssh.mycompany.com\0(作爲分組行)

此時連接已建立。如果命令不受支持或者有任何問題,GIt可能會返回錯誤。我還沒有想出如何檢查這個。

接下來是未公開的部分。我們基本上通過電線發送git-archive的命令行參數。它們與git-archive命令完全相同,只有一個例外:它們的前綴都是argument[SPACE]。每個參數都被寫入(至少在參考實現中)作爲單獨的分組行。因此,對於上面的例子:

  1. 發送argument --format=tar(作爲數據包線)
  2. 發送argument master(作爲數據包線)
  3. 發送argument plans/documents/cornballer-blueprint.pdf(作爲數據包線)
  4. 寄沖洗分組( 0000

在這一點上,我們已經給出了遠程git-archive進程的整個命令。現在我們閱讀迴應。我們從服務器讀取,這將是下列響應一一包線回:

  1. ACK(意味着成功 - 準備發送存檔)
  2. NACK [message] - 某種錯誤,才發現「無法產卵子」
  3. ERR [message] - - 它的使用的一個實例出現錯誤

如果ACK被髮送時,它隨後將沖洗分組(0000),然後將原始t AR數據。此時您重複讀取邊帶#1(主數據通道)上的數據包行。當你到達一個沖洗包,你停止閱讀。很簡單。

所以,現在你有遠程文件,但如果你想做一些聰明的緩存呢?我使用git-upload-packfile的原因之一是,它允許我記錄提交ID,從而將其緩存在本地,並只根據需要進行刷新。 tar文件不會告訴我們這些信息是正確的嗎?錯誤!

從GIT-歸檔的手冊頁:

另外提交ID被存儲在全局擴展PAX頭如果使用焦油格式 ;它可以使用git get-tar-commit-id來提取。在ZIP文件中,它被存儲爲 文件評論。

那麼這是個好消息!這幾乎是我想要的一切。如果你想知道的標題是什麼樣子,下面是一個示例(不,我不打算解剖PAX頭):

pax_global_header00006660000000000000000000000064121002672560014513gustar00rootroot0000000000000052 comment=326756f834865880c9832b64238e7665632e9b67 
從我的角度

所以,我只需要自動建立一個管道運行上述步驟,通過untar步驟(編程方式)運行它以執行所需的「從git獲取單個文件」功能。

+0

太棒了!前些日子,我遇到了這個問題,並沒有走得太遠(但我只看着'git-upload-packfile')。 –

+0

@Greg謝謝:) –

相關問題