<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="zh-Hant-TW">
	<id>https://jiva.dila.edu.tw/index.php?action=history&amp;feed=atom&amp;title=Pro_Git_5.3_%E5%B0%88%E6%A1%88%E7%AE%A1%E7%90%86</id>
	<title>Pro Git 5.3 專案管理 - 修訂歷史</title>
	<link rel="self" type="application/atom+xml" href="https://jiva.dila.edu.tw/index.php?action=history&amp;feed=atom&amp;title=Pro_Git_5.3_%E5%B0%88%E6%A1%88%E7%AE%A1%E7%90%86"/>
	<link rel="alternate" type="text/html" href="https://jiva.dila.edu.tw/index.php?title=Pro_Git_5.3_%E5%B0%88%E6%A1%88%E7%AE%A1%E7%90%86&amp;action=history"/>
	<updated>2026-05-05T15:31:30Z</updated>
	<subtitle>本 Wiki 上此頁面的修訂歷史</subtitle>
	<generator>MediaWiki 1.39.1</generator>
	<entry>
		<id>https://jiva.dila.edu.tw/index.php?title=Pro_Git_5.3_%E5%B0%88%E6%A1%88%E7%AE%A1%E7%90%86&amp;diff=554&amp;oldid=prev</id>
		<title>imported&gt;Ray：​新頁面: 既然是相互協作，在貢獻代碼的同時，也免不了要維護管理自己的專案。像是怎麼處理別人用 format-patch 生成的補丁，或是集成遠端倉庫上某...</title>
		<link rel="alternate" type="text/html" href="https://jiva.dila.edu.tw/index.php?title=Pro_Git_5.3_%E5%B0%88%E6%A1%88%E7%AE%A1%E7%90%86&amp;diff=554&amp;oldid=prev"/>
		<updated>2011-05-31T07:25:04Z</updated>

		<summary type="html">&lt;p&gt;新頁面: 既然是相互協作，在貢獻代碼的同時，也免不了要維護管理自己的專案。像是怎麼處理別人用 format-patch 生成的補丁，或是集成遠端倉庫上某...&lt;/p&gt;
&lt;p&gt;&lt;b&gt;新頁面&lt;/b&gt;&lt;/p&gt;&lt;div&gt;既然是相互協作，在貢獻代碼的同時，也免不了要維護管理自己的專案。像是怎麼處理別人用 format-patch 生成的補丁，或是集成遠端倉庫上某個分支上的變化等等。但無論是管理代碼倉庫，還是幫忙審核收到的補丁，都需要與貢獻者約定某種長期可持續的工作方式。&lt;br /&gt;
&lt;br /&gt;
=使用特性分支進行工作=&lt;br /&gt;
&lt;br /&gt;
如果想要集成新的代碼進來，最好局限在特性分支(topic branch)上做。臨時的特性分支可以讓你隨意嘗試，進退自如。比如碰上無法正常工作的補丁，可以先擱在那邊，直到有時間仔細核查修復為止。創建的分支可以用相關的主題關鍵字命名，比如 ruby_client 或者其它類似的描述性詞語，以幫助將來回憶。Git 專案本身還時常把分支名稱分置於不同命名空間下，比如 sc/ruby_client 就說明這是 sc 這個人貢獻的。現在從當前主幹分支為基礎，新建臨時分支：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;XML&amp;quot;&amp;gt;&lt;br /&gt;
$ git branch sc/ruby_client master&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
另外，如果你希望立即轉到分支上去工作，可以用 checkout -b：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;XML&amp;quot;&amp;gt;&lt;br /&gt;
$ git checkout -b sc/ruby_client master&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
好了，現在已經準備妥當，可以試著將別人貢獻的代碼合併進來了。之後評估一下有沒有問題，最後再決定是不是真的要併入主幹。&lt;br /&gt;
&lt;br /&gt;
=採納來自郵件的補丁=&lt;br /&gt;
&lt;br /&gt;
如果收到一個通過電郵發來的補丁，你應該先把它應用到特性分支上進行評估。有兩種應用補丁的方法：git apply 或者 git am。&lt;br /&gt;
&lt;br /&gt;
==使用 apply 命令應用補丁==&lt;br /&gt;
&lt;br /&gt;
如果收到的補丁文件是用 git diff 或由其它 Unix 的 diff 命令生成，就該用 git apply 命令來應用補丁。假設補丁文件存在 /tmp/patch-ruby-client.patch，可以這樣執行：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;XML&amp;quot;&amp;gt;&lt;br /&gt;
$ git apply /tmp/patch-ruby-client.patch&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
這會修改當前工作目錄下的檔，效果基本與執行 patch -p1 打補丁一樣，但它更為嚴格，且不會出現混亂。如果是 git diff 格式描述的補丁，此命令還會相應地添加、刪除、重命名檔案。當然，普通的 patch 命令是不會這麼做的。另外請注意，git apply 是一個事務性操作的命令，也就是說，要麼所有補丁都打上去，要麼全部放棄。所以不會出現 patch 命令那樣，一部分檔打上了補丁而另一部分卻沒有，這樣一種不上不下的修訂狀態。所以總的來說，git apply 要比 patch 嚴謹許多。因為僅僅是更新當前的檔案，所以此命令不會自動生成提交物件，你得手工緩存(stage)相應檔案的更新狀態並執行提交命令。&lt;br /&gt;
&lt;br /&gt;
在實際打補丁之前，可以先用 git apply --check 查看補丁是否能夠乾淨順利地應用到當前分支中：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;XML&amp;quot;&amp;gt;&lt;br /&gt;
$ git apply --check 0001-seeing-if-this-helps-the-gem.patch &lt;br /&gt;
error: patch failed: ticgit.gemspec:1&lt;br /&gt;
error: ticgit.gemspec: patch does not apply&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
如果沒有任何輸出，表示我們可以順利採納該補丁。如果有問題，除了報告錯誤資訊之外，該命令還會返回一個非零的狀態，所以在 shell 腳本裡可用於檢測狀態。&lt;br /&gt;
&lt;br /&gt;
==使用 am 命令應用補丁==&lt;br /&gt;
&lt;br /&gt;
如果貢獻者也用 Git，且擅於製作 format-patch 補丁，那你的合併工作將會非常輕鬆。因為這些補丁中除了檔案內容差異外，還包含了作者資訊和提交消息。所以請鼓勵貢獻者用 format-patch 生成補丁。對於傳統的 diff 命令生成的補丁，則只能用 git apply 處理。&lt;br /&gt;
&lt;br /&gt;
對於 format-patch 製作的新式補丁，應當使用 git am 命令。從技術上來說，git am 能夠讀取 mbox 格式的檔。這是種簡單的純文字檔，可以包含多封電郵，格式上用 From 加空格以及隨便什麼輔助資訊所組成的行作為分隔行，以區分每封郵件，就像這樣：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;XML&amp;quot;&amp;gt;&lt;br /&gt;
From 330090432754092d704da8e76ca5c05c198e71a8 Mon Sep 17 00:00:00 2001&lt;br /&gt;
From: Jessica Smith &amp;lt;jessica@example.com&amp;gt;&lt;br /&gt;
Date: Sun, 6 Apr 2008 10:17:23 -0700&lt;br /&gt;
Subject: [PATCH 1/2] add limit to log function&lt;br /&gt;
&lt;br /&gt;
Limit log functionality to the first 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
這是 format-patch 命令輸出的開頭幾行，也是一個有效的 mbox 檔案格式。如果有人用 git send-email 給你發了一個補丁，你可以將此郵件下載到本地，然後運行 git am 命令來應用這個補丁。如果你的郵件用戶端能將多封電郵匯出為 mbox 格式的檔，就可以用 git am 一次性應用所有匯出的補丁。&lt;br /&gt;
&lt;br /&gt;
如果貢獻者將 format-patch 生成的補丁檔上傳到類似 Request Ticket 一樣的任務處理系統，那麼可以先下載到本地，繼而使用 git am 應用該補丁：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;XML&amp;quot;&amp;gt;&lt;br /&gt;
$ git am 0001-limit-log-function.patch &lt;br /&gt;
Applying: add limit to log function&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
你會看到它被乾淨地應用到本地分支，並自動創建了新的提交物件。作者資訊取自郵件頭 From 和 Date，提交消息則取自 Subject 以及正文中補丁之前的內容。來看具體實例，採納之前展示的那個 mbox 電郵補丁後，最新的提交對象為：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;XML&amp;quot;&amp;gt;&lt;br /&gt;
$ git log --pretty=fuller -1&lt;br /&gt;
commit 6c5e70b984a60b3cecd395edd5b48a7575bf58e0&lt;br /&gt;
Author:     Jessica Smith &amp;lt;jessica@example.com&amp;gt;&lt;br /&gt;
AuthorDate: Sun Apr 6 10:17:23 2008 -0700&lt;br /&gt;
Commit:     Scott Chacon &amp;lt;schacon@gmail.com&amp;gt;&lt;br /&gt;
CommitDate: Thu Apr 9 09:19:06 2009 -0700&lt;br /&gt;
&lt;br /&gt;
   add limit to log function&lt;br /&gt;
&lt;br /&gt;
   Limit log functionality to the first 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Commit 部分顯示的是採納補丁的人，以及採納的時間。而 Author 部分則顯示的是原作者，以及創建補丁的時間。&lt;br /&gt;
&lt;br /&gt;
有時，我們也會遇到打不上補丁的情況。這多半是因為主幹分支和補丁的基礎分支相差太遠，但也可能是因為某些依賴補丁還未應用。這種情況下，git am 會報告錯誤並詢問該怎麼做：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;XML&amp;quot;&amp;gt;&lt;br /&gt;
$ git am 0001-seeing-if-this-helps-the-gem.patch &lt;br /&gt;
Applying: seeing if this helps the gem&lt;br /&gt;
error: patch failed: ticgit.gemspec:1&lt;br /&gt;
error: ticgit.gemspec: patch does not apply&lt;br /&gt;
Patch failed at 0001.&lt;br /&gt;
When you have resolved this problem run &amp;quot;git am --resolved&amp;quot;.&lt;br /&gt;
If you would prefer to skip this patch, instead run &amp;quot;git am --skip&amp;quot;.&lt;br /&gt;
To restore the original branch and stop patching run &amp;quot;git am --abort&amp;quot;.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Git 會在有衝突的檔裡加入衝突解決標記，這同合併或衍合操作一樣。解決的辦法也一樣，先編輯檔消除衝突，然後暫存檔案，最後運行 git am --resolved 提交修正結果：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;XML&amp;quot;&amp;gt;&lt;br /&gt;
$ (fix the file)&lt;br /&gt;
$ git add ticgit.gemspec &lt;br /&gt;
$ git am --resolved&lt;br /&gt;
Applying: seeing if this helps the gem&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
如果想讓 Git 更智慧地處理衝突，可以用 -3 選項進行三方合併。如果當前分支未包含該補丁的基礎代碼或其祖先，那麼三方合併就會失敗，所以該選項預設為關閉狀態。一般來說，如果該補丁是基於某個公開的提交製作而成的話，你總是可以通過同步來獲取這個共同祖先，所以用三方合併選項可以解決很多麻煩：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;XML&amp;quot;&amp;gt;&lt;br /&gt;
$ git am -3 0001-seeing-if-this-helps-the-gem.patch &lt;br /&gt;
Applying: seeing if this helps the gem&lt;br /&gt;
error: patch failed: ticgit.gemspec:1&lt;br /&gt;
error: ticgit.gemspec: patch does not apply&lt;br /&gt;
Using index info to reconstruct a base tree...&lt;br /&gt;
Falling back to patching base and 3-way merge...&lt;br /&gt;
No changes -- Patch already applied.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
像上面的例子，對於打過的補丁我又再打一遍，自然會產生衝突，但因為加上了 -3 選項，所以它很聰明地告訴我，無需更新，原有的補丁已經應用。&lt;br /&gt;
&lt;br /&gt;
對於一次應用多個補丁時所用的 mbox 格式檔，可以用 am 命令的交互模式選項 -i，這樣就會在打每個補丁前停住，詢問該如何操作：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;XML&amp;quot;&amp;gt;&lt;br /&gt;
$ git am -3 -i mbox&lt;br /&gt;
Commit Body is:&lt;br /&gt;
--------------------------&lt;br /&gt;
seeing if this helps the gem&lt;br /&gt;
--------------------------&lt;br /&gt;
Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
在多個補丁要打的情況下，這是個非常好的辦法，一方面可以預覽一下補丁內容，同時也可以有選擇性的接納或跳過某些補丁。&lt;br /&gt;
&lt;br /&gt;
打完所有補丁後，如果測試下來新特性可以正常工作，那就可以安心地將當前特性分支合併到長期分支中去了。&lt;br /&gt;
&lt;br /&gt;
=檢出(check out)遠端分支=&lt;br /&gt;
&lt;br /&gt;
如果貢獻者有自己的 Git 倉庫，並將修改推送到此倉庫中，那麼當你拿到倉庫的訪問位址和對應分支的名稱後，就可以加為遠端分支，然後在本地進行合併。&lt;br /&gt;
&lt;br /&gt;
比如，Jessica 發來一封郵件，說在她代碼庫中的 ruby-client 分支上已經實現了某個非常棒的新功能，希望我們能幫忙測試一下。我們可以先把她的倉庫加為遠端倉庫，然後抓取資料，完了再將她所說的分支檢出到本地來測試：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;XML&amp;quot;&amp;gt;&lt;br /&gt;
$ git remote add jessica git://github.com/jessica/myproject.git&lt;br /&gt;
$ git fetch jessica&lt;br /&gt;
$ git checkout -b rubyclient jessica/ruby-client&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
若是不久她又發來郵件，說還有個很棒的功能實現在另一分支上，那我們只需重新抓取下最新資料，然後檢出那個分支到本地就可以了，無需重複設置遠端倉庫。&lt;br /&gt;
&lt;br /&gt;
這種做法便於同別人保持長期的合作關係。但前提是要求貢獻者有自己的伺服器，而我們也需要為每個人建一個遠端分支。有些貢獻者提交代碼補丁並不是很頻繁，所以通過郵件接收補丁效率會更高。同時我們自己也不會希望建上百來個分支，卻只從每個分支取一兩個補丁。但若是用腳本程式來管理，或直接使用代碼倉庫託管服務，就可以簡化此過程。當然，選擇何種方式取決於你和貢獻者的喜好。&lt;br /&gt;
&lt;br /&gt;
使用遠端分支的另外一個好處是能夠得到提交歷史。不管代碼合併是不是會有問題，至少我們知道該分支的歷史分叉點，所以預設會從共同祖先開始自動進行三方合併，無需 -3 選項，也不用像打補丁那樣祈禱存在共同的基準點。&lt;br /&gt;
&lt;br /&gt;
如果只是臨時合作，只需用 git pull 命令抓取遠端倉庫上的資料，合併到本地臨時分支就可以了。一次性的抓取動作自然不會把該倉庫位址加為遠程倉庫。&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;XML&amp;quot;&amp;gt;&lt;br /&gt;
$ git pull git://github.com/onetimeguy/project.git&lt;br /&gt;
From git://github.com/onetimeguy/project&lt;br /&gt;
 * branch            HEAD       -&amp;gt; FETCH_HEAD&lt;br /&gt;
Merge made by recursive.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=決斷代碼取捨=&lt;br /&gt;
&lt;br /&gt;
現在特性分支上已合併好了貢獻者的代碼，是時候決斷取捨了。本節將回顧一些之前學過的命令，以看清將要合併到主幹的是哪些代碼，從而理解它們到底做了些什麼，是否真的要併入。&lt;br /&gt;
&lt;br /&gt;
一般我們會先看一下，特性分支上都有哪些新增的提交。比如在 contrib 特性分支上打了兩個補丁，僅查看這兩個補丁的提交資訊，可以用 --not 選項指定要遮罩的分支 master，這樣就會剔除重複的提交歷史：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;XML&amp;quot;&amp;gt;&lt;br /&gt;
$ git log contrib --not master&lt;br /&gt;
commit 5b6235bd297351589efc4d73316f0a68d484f118&lt;br /&gt;
Author: Scott Chacon &amp;lt;schacon@gmail.com&amp;gt;&lt;br /&gt;
Date:   Fri Oct 24 09:53:59 2008 -0700&lt;br /&gt;
&lt;br /&gt;
    seeing if this helps the gem&lt;br /&gt;
&lt;br /&gt;
commit 7482e0d16d04bea79d0dba8988cc78df655f16a0&lt;br /&gt;
Author: Scott Chacon &amp;lt;schacon@gmail.com&amp;gt;&lt;br /&gt;
Date:   Mon Oct 22 19:38:36 2008 -0700&lt;br /&gt;
&lt;br /&gt;
    updated the gemspec to hopefully work better&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
還可以查看每次提交的具體修改。請牢記，在 git log 後加 -p 選項將展示每次提交的內容差異。&lt;br /&gt;
&lt;br /&gt;
如果想看當前分支同其他分支合併時的完整內容差異，有個小竅門：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;XML&amp;quot;&amp;gt;&lt;br /&gt;
$ git diff master&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
雖然能得到差異內容，但請記住，結果有可能和我們的預期不同。一旦主幹 master 在特性分支創建之後有所修改，那麼通過 diff 命令來比較的，是最新主幹上的提交快照。顯然，這不是我們所要的。比方在 master 分支中某個檔裡添了一行，然後運行上面的命令，簡單的比較最新快照所得到的結論只能是，特性分支中刪除了這一行。&lt;br /&gt;
&lt;br /&gt;
這個很好理解：如果 master 是特性分支的直接祖先，不會產生任何問題；如果它們的提交歷史在不同的分叉上，那麼產生的內容差異，看起來就像是增加了特性分支上的新代碼，同時刪除了 master 分支上的新代碼。&lt;br /&gt;
&lt;br /&gt;
實際上我們真正想要看的，是新加入到特性分支的代碼，也就是合併時會併入主幹的代碼。所以，準確地講，我們應該比較特性分支和它同 master 分支的共同祖先之間的差異。&lt;br /&gt;
&lt;br /&gt;
我們可以手工定位它們的共同祖先，然後與之比較：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;XML&amp;quot;&amp;gt;&lt;br /&gt;
$ git merge-base contrib master&lt;br /&gt;
36c7dba2c95e6bbb78dfa822519ecfec6e1ca649&lt;br /&gt;
$ git diff 36c7db &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
但這麼做很麻煩，所以 Git 提供了便捷的 ... 語法。對於 diff 命令，可以把 ... 加在原始分支（擁有共同祖先）和當前分支之間：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;XML&amp;quot;&amp;gt;&lt;br /&gt;
$ git diff master...contrib&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
現在看到的，就是實際將要引入的新代碼。這是一個非常有用的命令，應該牢記。&lt;br /&gt;
&lt;br /&gt;
=代碼集成=&lt;br /&gt;
&lt;br /&gt;
一但特性分支準備妥當，接下來的問題就是如何集成到更靠近主線的分支中。此外還要考慮維護專案的總體步驟是什麼。雖然有很多選擇，不過我們這裡只介紹其中一部分。&lt;br /&gt;
&lt;br /&gt;
==合併流程==&lt;br /&gt;
&lt;br /&gt;
一般最簡單的情形，是在 master 分支中維護穩定代碼，然後在特性分支上開發新功能，或是審核測試別人貢獻的代碼，接著將它併入主幹，最後刪除這個特性分支，如此反復。來看示例，假設當前代碼庫中有兩個分支，分別為 ruby_client 和 php_client，如圖 5-19 所示。然後先把 ruby_client 合併進主幹，再合併 php_client，最後的提交歷史如圖 5-20 所示。&lt;br /&gt;
&lt;br /&gt;
[[圖片:pro-git-5-19.png]]&amp;lt;br&amp;gt;&lt;br /&gt;
圖 5-19. 多個特性分支&lt;br /&gt;
&lt;br /&gt;
[[圖片:pro-git-5-20.png]]&amp;lt;br&amp;gt;&lt;br /&gt;
圖 5-20. 合併特性分支之後&lt;br /&gt;
&lt;br /&gt;
這是最簡單的流程，所以在處理大一些的專案時可能會有問題。&lt;br /&gt;
&lt;br /&gt;
對於大型專案，至少需要維護兩個長期分支 master 和 develop。新代碼（圖 5-21 中的 ruby_client）將首先併入 develop 分支（圖 5-22 中的 C8），經過一個階段，確認 develop 中的代碼已穩定到可發行時，再將 master 分支快進到穩定點（圖 5-23 中的 C8）。而平時這兩個分支都會被推送到公開的代碼庫。&lt;br /&gt;
&lt;br /&gt;
[[圖片:pro-git-5-21.png]]&amp;lt;br&amp;gt;&lt;br /&gt;
圖 5-21. 特性分支合併前&lt;br /&gt;
&lt;br /&gt;
[[圖片:pro-git-5-22.png]]&amp;lt;br&amp;gt;&lt;br /&gt;
圖 5-22. 特性分支合併後&lt;br /&gt;
&lt;br /&gt;
[[圖片:pro-git-5-23.png]]&amp;lt;br&amp;gt;&lt;br /&gt;
圖 5-23. 特性分支發佈後&lt;br /&gt;
&lt;br /&gt;
這樣，在人們克隆倉庫時就有兩種選擇：既可檢出最新穩定版本，確保正常使用；也能檢出開發版本，試用最前沿的新特性。&lt;br /&gt;
&lt;br /&gt;
你也可以擴展這個概念，先將所有新代碼合併到臨時特性分支，等到該分支穩定下來並通過測試後，再併入 develop 分支。然後，讓時間檢驗一切，如果這些代碼確實可以正常工作相當長一段時間，那就有理由相信它已經足夠穩定，可以放心併入主幹分支發佈。&lt;br /&gt;
&lt;br /&gt;
==大專案的合併流程==&lt;br /&gt;
&lt;br /&gt;
Git 專案本身有四個長期分支：用於發佈的 master 分支、用於合併基本穩定特性的 next 分支、用於合併仍需改進特性的 pu 分支（pu 是 proposed updates 的縮寫），以及用於除錯維護的 maint 分支（maint 取自 maintenance）。維護者可以按照之前介紹的方法，將貢獻者的代碼引入為不同的特性分支（如圖 5-24 所示），然後測試評估，看哪些特性能穩定工作，哪些還需改進。穩定的特性可以併入 next 分支，然後再推送到公共倉庫，以供其他人試用。&lt;br /&gt;
&lt;br /&gt;
[[圖片:pro-git-5-24.png]]&amp;lt;br&amp;gt;&lt;br /&gt;
圖 5-24. 管理複雜的並行貢獻&lt;br /&gt;
&lt;br /&gt;
仍需改進的特性可以先併入 pu 分支。直到它們完全穩定後再併入 master。同時一併檢查下 next 分支，將足夠穩定的特性也併入 master。所以一般來說，master 始終是在快進，next 偶爾做下衍合，而 pu 則是頻繁衍合，如圖 5-25 所示：&lt;br /&gt;
&lt;br /&gt;
[[圖片:pro-git-5-25.png]]&amp;lt;br&amp;gt;&lt;br /&gt;
圖 5-25. 將特性併入長期分支&lt;br /&gt;
&lt;br /&gt;
併入 master 後的特性分支，已經無需保留分支索引，放心刪除好了。Git 專案還有一個 maint 分支，它是以最近一次發行版本為基礎分化而來的，用於維護除錯補丁。所以克隆 Git 項目倉庫後會得到這四個分支，通過檢出不同分支可以瞭解各自進展，或是試用前沿特性，或是貢獻代碼。而維護者則通過管理這些分支，逐步有序地併入協力廠商貢獻。&lt;br /&gt;
&lt;br /&gt;
==衍合與挑揀（cherry-pick）的流程==&lt;br /&gt;
&lt;br /&gt;
一些維護者更喜歡衍合或者挑揀貢獻者的代碼，而不是簡單的合併，因為這樣能夠保持線性的提交歷史。如果你完成了一個特性的開發，並決定將它引入到主幹代碼中，你可以轉到那個特性分支然後執行衍合命令，好在你的主幹分支上（也可能是develop分支之類的）重新提交這些修改。如果這些代碼工作得很好，你就可以快進master分支，得到一個線性的提交歷史。&lt;br /&gt;
&lt;br /&gt;
另一個引入代碼的方法是挑揀。挑揀類似於針對某次特定提交的衍合。它首先提取某次提交的補丁，然後試著應用在當前分支上。如果某個特性分支上有多個commits，但你只想引入其中之一就可以使用這種方法。也可能僅僅是因為你喜歡用挑揀，討厭衍合。假設你有一個類似圖 5-26的專案。&lt;br /&gt;
&lt;br /&gt;
[[圖片:pro-git-5-26.png]]&amp;lt;br&amp;gt;&lt;br /&gt;
Figure 5-26. 挑揀（cherry-pick）之前的歷史&lt;br /&gt;
&lt;br /&gt;
如果你希望拉取e43a6到你的主幹分支，可以這樣：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;XML&amp;quot;&amp;gt;&lt;br /&gt;
$ git cherry-pick e43a6fd3e94888d76779ad79fb568ed180e5fcdf&lt;br /&gt;
Finished one cherry-pick.&lt;br /&gt;
[master]: created a0a41a9: &amp;quot;More friendly message when locking the index fails.&amp;quot;&lt;br /&gt;
 3 files changed, 17 insertions(+), 3 deletions(-)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
這將會引入e43a6的代碼，但是會得到不同的SHA-1值，因為應用日期不同。現在你的歷史看起來像圖 5-27.&lt;br /&gt;
&lt;br /&gt;
[[圖片:pro-git-5-27.png]]&amp;lt;br&amp;gt;&lt;br /&gt;
Figure 5-27. 挑揀（cherry-pick）之後的歷史&lt;br /&gt;
&lt;br /&gt;
現在，你可以刪除這個特性分支並丟棄你不想引入的那些commit。&lt;br /&gt;
&lt;br /&gt;
=給發行版本貼標籤=&lt;br /&gt;
&lt;br /&gt;
你可以刪除上次發佈的版本並重新打標籤，也可以像第二章所說的那樣建立一個新的標籤。如果你決定以維護者的身份給發行版本簽名，應該這樣做：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;XML&amp;quot;&amp;gt;&lt;br /&gt;
$ git tag -s v1.5 -m 'my signed 1.5 tag'&lt;br /&gt;
You need a passphrase to unlock the secret key for&lt;br /&gt;
user: &amp;quot;Scott Chacon &amp;lt;schacon@gmail.com&amp;gt;&amp;quot;&lt;br /&gt;
1024-bit DSA key, ID F721C45A, created 2009-02-09&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
完成簽名之後，如何分發PGP公開金鑰（public key）是個問題。（譯者注：分發公開金鑰是為了驗證標籤）。還好，Git的設計者想到了解決辦法：可以把key（既公開金鑰）作為blob變數寫入Git庫，然後把它的內容直接寫在標籤裡。gpg --list-keys命令可以顯示出你所擁有的key：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;XML&amp;quot;&amp;gt;&lt;br /&gt;
$ gpg --list-keys&lt;br /&gt;
/Users/schacon/.gnupg/pubring.gpg&lt;br /&gt;
---------------------------------&lt;br /&gt;
pub   1024D/F721C45A 2009-02-09 [expires: 2010-02-09]&lt;br /&gt;
uid                  Scott Chacon &amp;lt;schacon@gmail.com&amp;gt;&lt;br /&gt;
sub   2048g/45D02282 2009-02-09 [expires: 2010-02-09]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
然後，匯出key的內容並經由管道符(pipe)傳遞給git hash-object，之後鑰匙會以blob類型寫入Git中，最後返回這個blob量的SHA-1值：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;XML&amp;quot;&amp;gt;&lt;br /&gt;
$ gpg -a --export F721C45A | git hash-object -w --stdin&lt;br /&gt;
659ef797d181633c87ec71ac3f9ba29fe5775b92&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
現在你的Git已經包含了這個key的內容了，可以通過不同的SHA-1值指定不同的key來創建標籤。&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;XML&amp;quot;&amp;gt;&lt;br /&gt;
$ git tag -a maintainer-pgp-pub 659ef797d181633c87ec71ac3f9ba29fe5775b92&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
在執行 git push --tags 命令之後，maintainer-pgp-pub 標籤就會公佈給所有人。如果有人想要校驗標籤，他可以使用如下命令匯入你的key：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;XML&amp;quot;&amp;gt;&lt;br /&gt;
$ git show maintainer-pgp-pub | gpg --import&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
人們可以用這個 key 校驗你簽名的所有標籤。另外，你也可以在標籤資訊裡寫入一個操作指引，用戶只需要執行 git show &amp;lt;tag&amp;gt; 查看標籤資訊，然後按照你的指引就能完成校驗。&lt;br /&gt;
&lt;br /&gt;
=產生內部版本號碼(build number)=&lt;br /&gt;
&lt;br /&gt;
因為 Git 不會為每次提交自動附加類似’v123’的遞增序列，所以如果你想要得到一個便於理解的提交號碼可以執行git describe 命令。Git 將會返回一個字串，由三部分組成：最近一次標定的版本號，加上自那次標定之後的提交次數，再加上一段該次提交的 SHA-1 值：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;XML&amp;quot;&amp;gt;&lt;br /&gt;
$ git describe master&lt;br /&gt;
v1.6.2-rc1-20-g8c5b85c&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
這個字串可以作為快照的名字，方便人們理解。如果你的Git是你自己下載源碼然後編譯安裝的，你會發現 git --version 命令的輸出和這個字串差不多。如果在一個剛剛打完標籤的提交上執行 describe 命令，只會得到這次標定的版本號，而沒有後面兩項資訊。&lt;br /&gt;
&lt;br /&gt;
git describe 命令只適用於有標注的標籤（通過-a或者-s選項創建的標籤），所以發行版本的標籤都應該是帶有標注的，以保證 git describe 能夠正確的執行。你也可以把這個字串作為 checkout 或者 show 命令的目標，因為他們最終都依賴於一個簡短的 SHA-1 值，當然如果這個 SHA-1 值失效他們也跟著失效。最近 Linux 內核為了保證 SHA-1 值的唯一性，將位數由8位擴展到10位，這就導致舊的 git describe 輸出完全失效了。&lt;br /&gt;
&lt;br /&gt;
=準備發佈=&lt;br /&gt;
&lt;br /&gt;
現在可以發佈一個新的版本了。首先要將代碼的壓縮包歸檔，方便那些可憐的還沒有使用Git的人們。可以使用git archive：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;XML&amp;quot;&amp;gt;&lt;br /&gt;
$ git archive master --prefix='project/' | gzip &amp;gt; `git describe master`.tar.gz&lt;br /&gt;
$ ls *.tar.gz&lt;br /&gt;
v1.6.2-rc1-20-g8c5b85c.tar.gz&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
這個壓縮包解壓出來的是一個資料夾，裡面是你專案的最新代碼快照。你也可以用類似的方法建立一個zip壓縮包，在git archive加上 --format=zip 選項：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;XML&amp;quot;&amp;gt;&lt;br /&gt;
$ git archive master --prefix='project/' --format=zip &amp;gt; `git describe master`.zip&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
現在你有了一個 tar.gz 壓縮包和一個 zip 壓縮包，可以把他們上傳到你網站上或者用e-mail發給別人。&lt;br /&gt;
&lt;br /&gt;
=製作簡報 (Shortlog)=&lt;br /&gt;
&lt;br /&gt;
是時候通知郵寄清單裡的朋友們來檢驗你的成果了。使用 git shortlog 命令可以方便快捷的製作一份修改日誌（changelog），告訴大家上次發佈之後又增加了哪些特性和修復了哪些 bug。實際上這個命令能夠統計給定範圍內的所有提交；假如你上一次發佈的版本是 v1.0.1，下面的命令將給出自從上次發佈之後的所有提交的摘要：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;XML&amp;quot;&amp;gt;&lt;br /&gt;
$ git shortlog --no-merges master --not v1.0.1&lt;br /&gt;
Chris Wanstrath (8):&lt;br /&gt;
      Add support for annotated tags to Grit::Tag&lt;br /&gt;
      Add packed-refs annotated tag support.&lt;br /&gt;
      Add Grit::Commit#to_patch&lt;br /&gt;
      Update version and History.txt&lt;br /&gt;
      Remove stray `puts`&lt;br /&gt;
      Make ls_tree ignore nils&lt;br /&gt;
&lt;br /&gt;
Tom Preston-Werner (4):&lt;br /&gt;
      fix dates in history&lt;br /&gt;
      dynamic version method&lt;br /&gt;
      Version bump to 1.0.2&lt;br /&gt;
      Regenerated gemspec for version 1.0.2&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
這就是自從 v1.0.1 版本以來的所有提交的摘要，內容按照作者分組，以便你能快速的發e-mail給他們。&lt;/div&gt;</summary>
		<author><name>imported&gt;Ray</name></author>
	</entry>
</feed>