設定與配置
取得與建立專案
基本快照
分支與合併
分享與更新專案
檢視與比較
修補
除錯
電子郵件
外部系統
伺服器管理
指南
管理
底層命令
- 2.43.1 → 2.47.0 無變更
-
2.43.0
11/20/23
- 2.38.1 → 2.42.3 無變更
-
2.38.0
10/02/22
描述
Git 支援透過 ssh://、git://、http:// 和 file:// 傳輸方式以 packfile 格式傳輸資料。存在兩組協定,一組用於將資料從客戶端推送到伺服器,另一組用於將資料從伺服器提取到客戶端。三種傳輸方式(ssh、git、file)使用相同的協定來傳輸資料。http 的相關文件請見 gitprotocol-http[5]。
在標準 Git 實作中,伺服器端調用的程序是 *upload-pack*,而客戶端調用的程序是 *fetch-pack*,用於提取資料;然後伺服器端調用 *receive-pack*,客戶端調用 *send-pack*,用於推送資料。協定的功能是讓伺服器告知客戶端目前伺服器上的內容,然後讓雙方協商要傳送的最小資料量,以便完全更新其中一方。
pkt-line 格式
以下描述建立在 gitprotocol-common[5] 中描述的 pkt-line 格式之上。當語法指示 PKT-LINE(...)
時,除非另有說明,否則適用通常的 pkt-line LF 規則:傳送者應該包含 LF,但接收者如果沒有 LF 也不應該抱怨。
錯誤封包是一個特殊的 pkt-line,包含錯誤字串。
error-line = PKT-LINE("ERR" SP explanation-text)
在整個協定中,當預期出現 PKT-LINE(...)
時,可以傳送錯誤封包。一旦客戶端或伺服器傳送此封包,此協定中定義的資料傳輸程序即終止。
傳輸方式
共有三種傳輸方式用於啟動 packfile 協定。Git 傳輸是一個簡單、未經驗證的伺服器,它會接收命令(幾乎總是 *upload-pack*,儘管可以將 Git 伺服器設定為全域可寫,在這種情況下也允許啟動 *receive-pack*),並執行該命令,然後將其連接到請求的程序。
在 SSH 傳輸中,客戶端只是透過 SSH 協定在伺服器上執行 *upload-pack* 或 *receive-pack* 程序,然後透過 SSH 連線與調用的程序進行通訊。
file:// 傳輸會在本地執行 *upload-pack* 或 *receive-pack* 程序,並透過管道與其進行通訊。
額外參數
該協定提供了一種機制,客戶端可以在其發給伺服器的第一個訊息中傳送額外資訊。這些稱為「額外參數」,並且受到 Git、SSH 和 HTTP 協定的支援。
每個額外參數都採用 <key>=<value>
或 <key>
的形式。
接收任何此類額外參數的伺服器必須忽略所有無法識別的鍵。目前,唯一可識別的額外參數是「version」,其值為 *1* 或 *2*。有關協定版本 2 的更多資訊,請參閱 gitprotocol-v2[5]。
Git 傳輸
Git 傳輸首先使用 pkt-line 格式在網路上傳送命令和儲存庫,然後接著一個 NUL 位元組和一個主機名稱參數,最後以 NUL 位元組終止。
0033git-upload-pack /project.git\0host=myserver.com\0
傳輸可以透過新增一個額外的 NUL 位元組,然後新增一個或多個以 NUL 終止的字串來傳送額外參數。
003egit-upload-pack /project.git\0host=myserver.com\0\0version=1\0
git-proto-request = request-command SP pathname NUL [ host-parameter NUL ] [ NUL extra-parameters ] request-command = "git-upload-pack" / "git-receive-pack" / "git-upload-archive" ; case sensitive pathname = *( %x01-ff ) ; exclude NUL host-parameter = "host=" hostname [ ":" port ] extra-parameters = 1*extra-parameter extra-parameter = 1*( %x01-ff ) NUL
主機參數用於 git-daemon 基於名稱的虛擬主機。請參閱 git daemon 的 --interpolated-path 選項,其中包含 %H/%CH 格式字元。
基本上,Git 客戶端透過 Git 協定連線到伺服器端的 *upload-pack* 程序時所做的事情如下
$ echo -e -n \ "003agit-upload-pack /schacon/gitbook.git\0host=example.com\0" | nc -v example.com 9418
SSH 傳輸
透過 SSH 啟動 upload-pack 或 receive-pack 程序,實際上是透過 SSH 遠端執行在伺服器上執行二進制檔案。它基本上等效於執行以下命令
$ ssh git.example.com "git-upload-pack '/project.git'"
為了讓伺服器支援透過 SSH 為特定使用者推送和提取 Git,該使用者需要在登入時透過他們提供的 SSH shell 執行其中一個或兩個命令。在某些系統上,該 shell 存取權限僅限於執行這兩個命令中的一個,甚至只有一個。
在 ssh:// 格式的 URI 中,它是 URI 中的絕對路徑,因此主機名稱(或連接埠號碼)之後的 */* 會作為參數傳送,然後由遠端 git-upload-pack 按原樣讀取,因此它實際上是遠端檔案系統中的絕對路徑。
git clone ssh://user@example.com/project.git | v ssh user@example.com "git-upload-pack '/project.git'"
在 "user@host:path" 格式的 URI 中,它是相對於使用者家目錄的路徑,因為 Git 客戶端將執行
git clone user@example.com:project.git | v ssh user@example.com "git-upload-pack 'project.git'"
例外情況是使用了 *~*,在這種情況下,我們會執行它而不使用開頭的 */*。
ssh://user@example.com/~alice/project.git, | v ssh user@example.com "git-upload-pack '~alice/project.git'"
根據 protocol.version
設定變數的值,Git 可能會嘗試將額外參數作為以冒號分隔的字串傳送在 GIT_PROTOCOL 環境變數中。只有在 ssh.variant
設定變數指示 ssh 命令支援將環境變數作為參數傳遞時,才會執行此操作。
這裡有幾件事要記住
-
「命令名稱」以破折號拼寫(例如 git-upload-pack),但客戶端可以覆蓋此名稱;
-
儲存庫路徑始終以單引號引起來。
從伺服器提取資料
當一個 Git 儲存庫想要取得第二個儲存庫擁有的資料時,第一個儲存庫可以從第二個儲存庫*提取*。此操作會判斷伺服器擁有而客戶端沒有的資料,然後以 packfile 格式將該資料串流傳輸到客戶端。
參考探索
當客戶端最初連線時,伺服器會立即回應版本號碼(如果將「version=1」作為額外參數傳送),以及它擁有的每個參考(所有分支和標籤)的清單,以及每個參考目前指向的物件名稱。
$ echo -e -n "0045git-upload-pack /schacon/gitbook.git\0host=example.com\0\0version=1\0" | nc -v example.com 9418 000eversion 1 00887217a7c7e582c46cec22a130adf4b9d7d950fba0 HEAD\0multi_ack thin-pack side-band side-band-64k ofs-delta shallow no-progress include-tag 00441d3fcd5ced445d1abc402225c0b8a1299641f497 refs/heads/integration 003f7217a7c7e582c46cec22a130adf4b9d7d950fba0 refs/heads/master 003cb88d2441cac0977faf98efc80305012112238d9d refs/tags/v0.9 003c525128480b96c89e6418b1e40909bf6c5b2d580f refs/tags/v1.0 003fe92df48743b7bc7d26bcaabfddde0a1e20cae47c refs/tags/v1.0^{} 0000
傳回的回應是以 pkt-line 串流描述每個參考及其目前值。串流必須根據 C locale 排序,按名稱排序。
如果 HEAD 是有效的參考,則 HEAD 必須作為第一個宣告的參考出現。如果 HEAD 不是有效的參考,則 HEAD 必須完全不顯示在宣告清單中,但其他參考仍然可以顯示。
串流必須在第一個參考的 NUL 後面包含功能宣告。參考的去殼值(即「ref^{}」)必須緊接著參考本身,如果存在。如果參考是附註標籤,則符合規範的伺服器必須對該參考進行去殼。
advertised-refs = *1("version 1") (no-refs / list-of-refs) *shallow flush-pkt no-refs = PKT-LINE(zero-id SP "capabilities^{}" NUL capability-list) list-of-refs = first-ref *other-ref first-ref = PKT-LINE(obj-id SP refname NUL capability-list) other-ref = PKT-LINE(other-tip / other-peeled) other-tip = obj-id SP refname other-peeled = obj-id SP refname "^{}" shallow = PKT-LINE("shallow" SP obj-id) capability-list = capability *(SP capability) capability = 1*(LC_ALPHA / DIGIT / "-" / "_") LC_ALPHA = %x61-7A
伺服器和客戶端必須使用小寫的 obj-id,兩者都必須將 obj-id 視為不區分大小寫。
有關允許的伺服器功能和描述的清單,請參閱 protocol-capabilities.txt。
Packfile 協商
在參考和功能探索之後,如果客戶端不需要任何 pack 資料,它可以透過傳送 flush-pkt 來決定終止連線,告訴伺服器它現在可以優雅地終止並斷開連線。這可能會發生在 ls-remote 命令中,也可能發生在客戶端已經是最新的時候。
否則,它將進入協商階段,客戶端和伺服器在此階段決定傳輸所需的最小 packfile,方法是告訴伺服器它想要的物件、其淺層物件(如果有的話)以及它想要的最大提交深度(如果有的話)。客戶端還會傳送它想要生效的功能清單,這些功能是從伺服器在第一個 *want* 行中聲明的功能中選出的。
upload-request = want-list *shallow-line *1depth-request [filter-request] flush-pkt want-list = first-want *additional-want shallow-line = PKT-LINE("shallow" SP obj-id) depth-request = PKT-LINE("deepen" SP depth) / PKT-LINE("deepen-since" SP timestamp) / PKT-LINE("deepen-not" SP ref) first-want = PKT-LINE("want" SP obj-id SP capability-list) additional-want = PKT-LINE("want" SP obj-id) depth = 1*DIGIT filter-request = PKT-LINE("filter" SP filter-spec)
客戶端必須傳送它從參考探索階段想要的所有 obj-id 作為 *want* 行。客戶端必須在請求主體中傳送至少一個 *want* 命令。客戶端不得在 *want* 命令中提及未在透過參考探索獲得的回應中出現的 obj-id。
客戶端必須將它只有淺層副本的所有 obj-id(表示它沒有提交的父系)寫為 *shallow* 行,以便伺服器了解客戶端歷程記錄的限制。
客戶端現在會發送此交易需要的最大提交歷史深度,也就是它希望從歷史頂端取得的提交次數(如果有的話),以 deepen 行表示。深度為 0 等同於不發出深度請求。客戶端不希望收到超出此深度的任何提交,也不希望收到僅用於完成這些提交的物件。若提交的父提交未被收到,則會被定義為淺層提交,並在伺服器中標記為淺層。此資訊將在下一步回傳給客戶端。
客戶端可以選擇性地要求 pack-objects 使用數種篩選技術,從 packfile 中省略各種物件。這些技術旨在用於部分複製和部分提取操作。不符合篩選規格值的物件將被省略,除非在 want 行中明確請求。請參閱 rev-list
以了解可能的篩選規格值。
一旦所有 want 和 'shallow'(以及可選的 'deepen')都已傳輸完畢,客戶端必須發送一個 flush-pkt,告知伺服器端它已完成發送列表。
否則,如果客戶端發送了正數深度請求,伺服器將確定哪些提交將會是淺層提交,並將此資訊發送給客戶端。如果客戶端未請求正數深度,則會跳過此步驟。
shallow-update = *shallow-line *unshallow-line flush-pkt shallow-line = PKT-LINE("shallow" SP obj-id) unshallow-line = PKT-LINE("unshallow" SP obj-id)
如果客戶端請求了正數深度,伺服器將計算出不超過所需深度的提交集合。該提交集合從客戶端的 want 開始。
伺服器會為每個父提交不會被傳送的提交寫入 shallow 行。伺服器會為每個客戶端已指示為淺層提交,但目前請求的深度不再是淺層的提交(也就是說,它的父提交現在將會被傳送)寫入 unshallow 行。伺服器絕不能將客戶端未指示為淺層的任何提交標記為非淺層。
現在,客戶端將使用 have 行發送它擁有的 obj-id 列表,以便伺服器可以製作一個只包含客戶端需要的物件的 packfile。在 multi_ack 模式下,標準實作會一次發送最多 32 個,然後發送一個 flush-pkt。標準實作會跳過並立即發送下 32 個,以便始終有 32 個區塊「在網路上傳輸」。
upload-haves = have-list compute-end have-list = *have-line have-line = PKT-LINE("have" SP obj-id) compute-end = flush-pkt / PKT-LINE("done")
如果伺服器讀取到 have 行,它將回應 ACK,確認客戶端表示它擁有的 obj-id 伺服器也有。伺服器會根據客戶端選擇的 ack 模式,以不同的方式確認 obj-id。
在 multi_ack 模式下
-
伺服器將為任何常見的提交回應 ACK obj-id continue。
-
一旦伺服器找到可接受的共同基準提交,並準備好製作 packfile,它將盲目地將所有 have obj-id 回應 ACK 給客戶端。
-
然後伺服器將發送 NAK,然後等待客戶端的另一個回應 - 要么是 done,要么是另一個 have 行列表。
在 multi_ack_detailed 模式下
-
伺服器將區分 ACK,其中它會使用 ACK obj-id ready 行表示它已準備好傳送資料,並使用 ACK obj-id common 行表示已識別的共同提交。
在沒有 multi_ack 或 multi_ack_detailed 的情況下
-
upload-pack 會在它找到的第一個共同物件上發送 "ACK obj-id"。在那之後,它不會說任何話,直到客戶端給它 "done"。
-
如果尚未找到任何共同物件,upload-pack 會在 flush-pkt 上發送 "NAK"。如果已找到一個,因此已經發送了 ACK,則在 flush-pkt 上保持沉默。
在客戶端獲得足夠的 ACK 回應,足以判斷伺服器擁有足夠的資訊來傳送高效的 packfile 之後(在標準實作中,當它收到足夠的 ACK,可以將 --date-order 佇列中剩下的所有項目標記為與伺服器共有的,或者 --date-order 佇列為空時,就會判定),或者客戶端確定它想要放棄時(在標準實作中,當客戶端發送 256 個 have 行而沒有任何一行被伺服器 ACK 時,就會判定 - 這表示沒有任何共同之處,伺服器應該直接傳送它所有的物件),客戶端將會發送 done 指令。done 指令向伺服器發出訊號,表示客戶端已準備好接收其 packfile 資料。
但是,只有在先前的回合中收到至少一個 "ACK %s continue" 時,標準客戶端實作才會啟用 256 的限制。這有助於確保在完全放棄之前至少找到一個共同的祖先。
一旦從客戶端讀取到 done 行,伺服器將會發送最終的 ACK obj-id 或 NAK。obj-id 是最後一個被判定為共同的提交的物件名稱。只有在至少有一個共同基準且啟用 multi_ack 或 multi_ack_detailed 時,伺服器才會在 done 之後發送 ACK。如果沒有找到共同基準,伺服器始終會在 done 之後發送 NAK。
伺服器可能會發送錯誤訊息,而不是 ACK 或 NAK(例如,如果它無法辨識客戶端在 want 行中收到的物件)。
然後伺服器將開始傳送其 packfile 資料。
server-response = *ack_multi ack / nak ack_multi = PKT-LINE("ACK" SP obj-id ack_status) ack_status = "continue" / "common" / "ready" ack = PKT-LINE("ACK" SP obj-id) nak = PKT-LINE("NAK")
一個簡單的複製可能看起來像這樣(沒有 have 行)
C: 0054want 74730d410fcb6603ace96f1dc55ea6196122532d multi_ack \ side-band-64k ofs-delta\n C: 0032want 7d1665144a3a975c05f1f43902ddaf084e784dbe\n C: 0032want 5a3f6be755bbb7deae50065988cbfa1ffa9ab68a\n C: 0032want 7e47fe2bd8d01d481f44d7af0531bd93d3b21c01\n C: 0032want 74730d410fcb6603ace96f1dc55ea6196122532d\n C: 0000 C: 0009done\n S: 0008NAK\n S: [PACKFILE]
增量更新(提取)回應可能看起來像這樣
C: 0054want 74730d410fcb6603ace96f1dc55ea6196122532d multi_ack \ side-band-64k ofs-delta\n C: 0032want 7d1665144a3a975c05f1f43902ddaf084e784dbe\n C: 0032want 5a3f6be755bbb7deae50065988cbfa1ffa9ab68a\n C: 0000 C: 0032have 7e47fe2bd8d01d481f44d7af0531bd93d3b21c01\n C: [30 more have lines] C: 0032have 74730d410fcb6603ace96f1dc55ea6196122532d\n C: 0000 S: 003aACK 7e47fe2bd8d01d481f44d7af0531bd93d3b21c01 continue\n S: 003aACK 74730d410fcb6603ace96f1dc55ea6196122532d continue\n S: 0008NAK\n C: 0009done\n S: 0031ACK 74730d410fcb6603ace96f1dc55ea6196122532d\n S: [PACKFILE]
Packfile 資料
現在客戶端和伺服器已經完成關於需要傳送到客戶端的最小資料量的協商,伺服器將會建構並以 packfile 格式傳送所需的資料。
請參閱 gitformat-pack[5] 以了解 packfile 本身的實際樣子。
如果客戶端已指定 side-band 或 side-band-64k 功能,伺服器將會傳送多工的 packfile 資料。
每個封包都以隨後資料的封包行長度開始,然後是一個單一位元組,指定以下資料來自的側邊頻帶。
在 side-band 模式下,它將傳送最多 999 個資料位元組加上 1 個控制碼,總共在 pkt-line 中最多 1000 個位元組。在 side-band-64k 模式下,它將傳送最多 65519 個資料位元組加上 1 個控制碼,總共在 pkt-line 中最多 65520 個位元組。
側邊頻帶位元組將是 1、2 或 3。側邊頻帶 1 將包含 packfile 資料,側邊頻帶 2 將用於進度資訊,客戶端通常會將其列印到 stderr,側邊頻帶 3 用於錯誤資訊。
如果未指定 side-band 功能,伺服器將會串流傳輸整個 packfile 而不進行多工處理。
將資料推送到伺服器
將資料推送到伺服器將會在伺服器上調用 receive-pack 程序,這將允許客戶端告訴它應該更新哪些參考,然後傳送伺服器需要的所有資料,以完成這些新的參考。一旦收到並驗證所有資料,伺服器將會將其參考更新為客戶端指定的內容。
身分驗證
協定本身不包含身分驗證機制。這應該由傳輸層處理,例如 SSH,在調用 receive-pack 程序之前。如果在 Git 傳輸層上設定 receive-pack,則任何可以存取該連接埠(9418)的人都可以寫入這些儲存庫,因為該傳輸層未經身分驗證。
參考探索
參考探索階段的完成方式與提取協定中的方式幾乎相同。伺服器上的每個參考 obj-id 和名稱都以封包行格式傳送到客戶端,然後是一個 flush-pkt。唯一的真正區別是功能列表不同 - 唯一可能的值是 report-status、report-status-v2、delete-refs、ofs-delta、atomic 和 push-options。
參考更新請求和 Packfile 傳輸
一旦客戶端知道伺服器的參考狀態,它就可以傳送參考更新請求列表。對於它想要更新的伺服器上的每個參考,它會傳送一行列出伺服器上目前的 obj-id、客戶端想要將其更新為的 obj-id 以及參考的名稱。
此列表後面跟著一個 flush-pkt。
update-requests = *shallow ( command-list | push-cert ) shallow = PKT-LINE("shallow" SP obj-id) command-list = PKT-LINE(command NUL capability-list) *PKT-LINE(command) flush-pkt command = create / delete / update create = zero-id SP new-id SP name delete = old-id SP zero-id SP name update = old-id SP new-id SP name old-id = obj-id new-id = obj-id push-cert = PKT-LINE("push-cert" NUL capability-list LF) PKT-LINE("certificate version 0.1" LF) PKT-LINE("pusher" SP ident LF) PKT-LINE("pushee" SP url LF) PKT-LINE("nonce" SP nonce LF) *PKT-LINE("push-option" SP push-option LF) PKT-LINE(LF) *PKT-LINE(command LF) *PKT-LINE(gpg-signature-lines LF) PKT-LINE("push-cert-end" LF) push-option = 1*( VCHAR | SP )
如果伺服器已宣告 push-options 功能,且客戶端已將 push-options 指定為上述功能列表的一部分,則客戶端會接著發送其推送選項,然後是一個 flush-pkt。
push-options = *PKT-LINE(push-option) flush-pkt
為了與較舊的 Git 伺服器向後相容,如果客戶端傳送推送憑證和推送選項,它必須在推送憑證內和推送憑證後都嵌入推送選項。(請注意,憑證內的推送選項具有前綴,但憑證後的推送選項則沒有。)這兩個列表必須相同,但前綴除外。
在那之後,將會傳送應該包含伺服器完成新參考所需的所有物件的 packfile。
packfile = "PACK" 28*(OCTET)
如果接收端不支援 delete-refs,則發送端不得要求刪除命令。
如果接收端不支援 push-cert,則發送端不得傳送 push-cert 命令。當傳送 push-cert 命令時,不得傳送 command-list;而是使用 push 憑證中記錄的命令。
如果僅使用 delete 命令,則不得傳送 packfile。
如果使用建立或更新命令,則必須傳送 packfile,即使伺服器已擁有所有必要的物件。在這種情況下,客戶端必須傳送一個空的 packfile。只有在客戶端正在建立指向現有 obj-id 的新分支或標籤時,才可能發生這種情況。
伺服器將接收 packfile、解壓縮它,然後驗證正在更新的每個參考,該參考在處理請求時沒有變更(obj-id 與舊 id 仍然相同),並且它將執行任何更新掛鉤以確保更新可以接受。如果一切都沒問題,伺服器將會更新參考。
推送憑證
推送憑證以一組標頭行開始。在標頭和空行之後,協定命令會接在後面,每行一個。請注意,推送憑證 PKT-LINE 中的尾隨 LF 並非可選;它必須存在。
目前,定義了以下標頭欄位
GPG 簽名行是推送憑證中簽名區塊開始之前所記錄內容的獨立簽名。獨立簽名用於證明這些指令是由推送者發出的,而推送者必須是簽署人。
報告狀態
從發送方接收到打包資料後,如果啟用 report-status 或 report-status-v2 功能,則接收方會發送一份報告。這是一份簡短的更新內容清單。它會先列出解包檔的狀態,結果為 unpack ok 或 unpack [error]。然後,它會列出它嘗試更新的每個參考的狀態。每一行若更新成功則為 ok [refname],若更新失敗則為 ng [refname] [error]。
report-status = unpack-status 1*(command-status) flush-pkt unpack-status = PKT-LINE("unpack" SP unpack-result) unpack-result = "ok" / error-msg command-status = command-ok / command-fail command-ok = PKT-LINE("ok" SP refname) command-fail = PKT-LINE("ng" SP refname SP error-msg) error-msg = 1*(OCTET) ; where not "ok"
report-status-v2 功能通過新增選項行來擴展協定,以支援報告由 proc-receive hook 重寫的參考。proc-receive hook 可以處理用於偽參考的命令,該命令可能會建立或更新一個或多個參考,並且每個參考可能具有不同的名稱、不同的 new-oid 和不同的 old-oid。
report-status-v2 = unpack-status 1*(command-status-v2) flush-pkt unpack-status = PKT-LINE("unpack" SP unpack-result) unpack-result = "ok" / error-msg command-status-v2 = command-ok-v2 / command-fail command-ok-v2 = command-ok *option-line command-ok = PKT-LINE("ok" SP refname) command-fail = PKT-LINE("ng" SP refname SP error-msg) error-msg = 1*(OCTET) ; where not "ok" option-line = *1(option-refname) *1(option-old-oid) *1(option-new-oid) *1(option-forced-update) option-refname = PKT-LINE("option" SP "refname" SP refname) option-old-oid = PKT-LINE("option" SP "old-oid" SP obj-id) option-new-oid = PKT-LINE("option" SP "new-oid" SP obj-id) option-force = PKT-LINE("option" SP "forced-update")
更新可能會因多種原因而失敗。參考可能自最初發送參考發現階段以來已變更,這表示有人在這段時間內進行了推送。正在推送的參考可能是非快轉參考,並且更新 hook 或組態可能設定為不允許這樣做,等等。此外,某些參考可以更新,而其他參考可能會被拒絕。
一個範例的客戶端/伺服器通訊可能如下所示
S: 006274730d410fcb6603ace96f1dc55ea6196122532d refs/heads/local\0report-status delete-refs ofs-delta\n S: 003e7d1665144a3a975c05f1f43902ddaf084e784dbe refs/heads/debug\n S: 003f74730d410fcb6603ace96f1dc55ea6196122532d refs/heads/master\n S: 003d74730d410fcb6603ace96f1dc55ea6196122532d refs/heads/team\n S: 0000 C: 00677d1665144a3a975c05f1f43902ddaf084e784dbe 74730d410fcb6603ace96f1dc55ea6196122532d refs/heads/debug\n C: 006874730d410fcb6603ace96f1dc55ea6196122532d 5a3f6be755bbb7deae50065988cbfa1ffa9ab68a refs/heads/master\n C: 0000 C: [PACKDATA] S: 000eunpack ok\n S: 0018ok refs/heads/debug\n S: 002ang refs/heads/master non-fast-forward\n
GIT
屬於 git[1] 套件的一部分