== 分支巫術 ==

即時分支合併是Git最給力的殺手鐧。

*問題* ：外部因素要求必須切換場景。在發佈版本中突然蹦出個嚴重缺陷。某個特性完
成的截至日期就要來臨。在項目關鍵部分可以提供幫助的一個開發正打算離職。所有情
況逼迫你停下所有手頭工作，全力撲到到這個完全不同的任務上。

打斷思維的連續性會使你的生產力大大降低，並且切換上下文也更麻煩，更大的損失。
使用中心版本控制我們必須從中心伺服器下載一個新的工作拷貝。分散式系統的情況就
好多了，因為我們能夠在本地克隆所需要的版本。

但是克隆仍然需要拷貝整個工作目錄，還有直到給定點的整個歷史記錄。儘管Git使用文
件共享和硬連結減少了花費，項目檔案自身還是必須在新的工作目錄裡重建。

*方案* ：Git有一個更好的工具對付這種情況，比克隆快多了而且節省空間： *git
 branch* 。

使用這個魔咒，目錄裡的檔案突然從一個版本變到另一個。除了只是在歷史記錄裡上跳
下竄外，這個轉換還可以做更多。你的檔案可以從上一個發佈版變到實驗版本到當前開
發版本到你朋友的版本等等。

=== 老闆鍵 ===

曾經玩過那樣的遊戲嗎？按一個鍵（“老闆鍵”），屏幕立即顯示一個電子表格或別的？
那麼如果老闆走進辦公室，而你正在玩遊戲，就可以快速將遊戲藏起來。

在某個目錄：

 $ echo "I'm smarter than my boss" > myfile.txt
 $ git init
 $ git add .
 $ git commit -m "Initial commit"

我們已經創建了一個Git倉庫，該倉庫記錄一個包含特定信息的檔案。現在我們鍵入：

 $ git checkout -b boss  # 之後似乎沒啥變化
 $ echo "My boss is smarter than me" > myfile.txt
 $ git commit -a -m "Another commit"

看起來我們剛剛只是覆蓋了原來的檔案並提交了它。但這是個錯覺。鍵入：

 $ git checkout master  # 切到檔案的原先版本

嘿真快！這個檔案就恢復了。並且如果老闆決定窺視這個目錄，鍵入：

 $ git checkout boss  # 切到適合老闆看的版本

你可以在兩個版本之間相切多少次就切多少次，而且每個版本都可以獨立提交。

=== 骯髒的工作 ===

[[branch]]

比如你正在開發某個特性，並且由於某種原因，你需要回退三個版本，臨時加進幾行打
印語句來，來看看一些東西是如何工作的。那麼：

 $ git commit -a
 $ git checkout HEAD~3

現在你可以到處加醜陋的臨時代碼。你甚至可以提交這些改動。當你做完的時候，

 $ git checkout master

來返回到你原來的工作。看，所有未提交變更都結轉了。

如果你後來想保存臨時變更怎麼辦？簡單：

 $ git checkout -b dirty

只要在切換到主分支之前提交就可以了。無論你什麼時候想回到髒的變更，只需鍵入：

 $ git checkout dirty

我們在前面章節討論加載舊狀態的時候，曾經接觸過這個命令。最終我們把故事說全：
檔案改變成請求的狀態，但我們必須離開主分支。從現在開始的任何提交都會將你的文
件提交到另一條不同的路，這個路可以之後命名。

換一個說法，在checkout一個舊狀態之後，Git自動把你放到一個新的，未命名的分支，
這個分支可以使用 *git checkout -b* 來命名和保存。

=== 快速修訂 ===

你正在做某件事的當間，被告知先停所有的事情，去修理一個新近發現的臭蟲，這個臭
蟲在提交 `1b6d...`：

 $ git commit -a
 $ git checkout -b fixes 1b6d

那麼一旦你修正了這個臭蟲：

 $ git commit -a -m "Bug fixed"
 $ git checkout master

並可以繼續你原來的任務。你甚至可以“合併”到最新修訂：

 $ git merge fixes

=== 合併 ===

一些版本控制系統，創建分支很容易，但把分支合併回來很難。使用Git，合併簡直是家
常便飯，以至于甚至你可能對其發生沒有察覺。

我們很久之前就遇到合併了。 *pull* 命令取出提交併合並它們到你的當前分支。如果
你沒有本地變更，那這個合併就是一個“快進”，相當於中心式版本控制系統裡的一個
弱化的獲取最新版本操作。但如有本地變更，Git將自動合併，並報告任何衝突。

通常，一個提交只有一個“父提交”，也叫前一個提交。合併分支到一起產生一個至少
有兩個父的提交。這就引出了問題： `HEAD~10` 真正指哪個提交？一個提交可能有多個
父，那我們跟哪個呢？

原來這個表示每次選擇第一個父。這是可取的，因為在合併時候當前分支成了第一個父；
多數情況下我們只關注我們在當前分支都改了什麼，而不是從其他分支合併來的變更。

你可以用插入符號來特別指定父。比如，顯示來自第二個父的日誌：

 $ git log HEAD^2

你可以忽略數字以指代第一個父。比如，顯示與第一個父的差別：

 $ git diff HEAD^

你可以結合其他類型使用這個記號。比如：

 $ git checkout 1b6d^^2~10 -b ancient

開始一個新分支 ``ancient'' ，表示第一個父的第二個父的倒數第十次提交的狀態。

=== 不間斷工作流 ===

經常在硬件項目裡，計劃的第二步必須等第一步完成才能開始。待修的汽車傻等在車庫
裡，直到特定的零件從工廠運來。一個原型在其可以構建之前，可能苦等晶片成型。

軟件項目可能也類似。新功能的第二部分不得不等待，直到第一部分發佈並通過測試。
一些項目要求你的代碼需要審批才能接受，因此你可能需要等待第一部分得到批准，才
能開始第二部分。

多虧了無痛分支合併，我們可以不必遵循這些規則，在第一部分正式準備好前開始第二
部分的工作。假設你已經將第一部分提交並發去審批，比如說你現在在主分支。那麼分
岔：

 $ git checkout -b part2

接下來，做第二部分，隨時可以提交變更。只要是人就可能犯錯誤，經常你將回到第一
部分在修修補補。如果你非常幸運，或者超級棒，你可能不必做這幾行：

 $ git checkout master  # 回到第一部分
 $ 修復問題
 $ git commit -a        # 提交變更
 $ git checkout part2   # 回到第二部分
 $ git merge master     # 合併這些改動

最終，第一部分獲得批准：

 $ git checkout master  # 回到第一部分
 $ submit files         # 對世界發佈
 $ git merge part2      # 合併第二部分
 $ git branch -d part2  # 刪除分支“part2”

現在你再次處在主分支，第二部分的代碼也在工作目錄。

很容易擴展這個技巧，應用到任意數目的部分。它也很容易追溯分支：假如你很晚才意
識到你本應在7次提交前就創建分支。那麼鍵入：

 $ git branch -m master part2  # 重命名“master”分支為“part2”。
 $ git branch master HEAD~7    # 以七次前提交建一個新的“master”。

分支 `master` 只有第一部分內容，其他內容在分支 `part2` 。 我們現在後一個分支；
我們創建了 `master` 分支還沒有切換過去，因為我們想繼續工作在 `part2` 。這是不
尋常的。直到現在，我們已經在創建之後切換到分支，如：

 $ git checkout HEAD~7 -b master  # 創建分支，並切換過去。

=== 重組雜亂 ===

或許你喜歡在同一個分支下完成工作的方方面面。你想為自己保留工作進度並希望其他
人只能看到你仔細整理過後的提交。開啟一對分支：

  $ git branch sanitized    # 為乾淨提交創建分支
  $ git checkout -b medley  # 創建並切換分支以進去工作

接下來，做任何事情：修臭蟲，加特性，加臨時代碼，諸如此類，經常按這種方式提交。
然後：

  $ git checkout sanitized
  $ git cherry-pick medley^^

應用分支 ``medley'' 的祖父提交到分支 ``sanitized'' 。通過合適的挑選（像選櫻桃
那樣）你可以構建一個只包含成熟代碼的分支，而且相關的提交也組織在一起。

=== 管理分支 ===

列出所有分支：

 $ git branch

預設你從叫 ``master'' 的分支開始。一些人主張別碰“master”分支，而是創建你自
己版本的新分支。

選項 *-d* 和 *-m* 允許你來刪除和移動（重命名）分支。參見 *git help branch* 。

分支``master'' 是一個有用的慣例。其他人可能假定你的倉庫有一個叫這個名字的分
支，並且該分支包含你項目的官方版本。儘管你可以重命名或抹殺 ``master'' 分支，
你最好還是尊重這個約定。

=== 臨時分支 ===

很快你會發現你經常會因為一些相似的原因創建短期的分支：每個其它分支只是為了保
存當前狀態，那樣你就可以直接跳到較老狀態以修復高優先順序的臭蟲之類。

可以和電視的換台做類比，臨時切到別的頻道，來看看其它台那正放什麼。但並不是簡
單地按幾個按鈕，你不得不創建，檢出，合併，以及刪除臨時分支。幸運的是，Git已經
有了和電視機遙控器一樣方便的快捷方式：

 $ git stash

這個命令保存當前狀態到一個臨時的地方（一個隱藏的地方）並且恢復之前狀態。你的
工作目錄看起來和你開始編輯之前一樣，並且你可以修復臭蟲，引入之前變更等。當你
想回到隱藏狀態的時候，鍵入：

 $ git stash apply  # 你可能需要解決一些衝突

你可以有多個隱藏，並用不同的方式來操作他們。參見 *git help slash* 。也許你已
經猜到，Git維護在這個場景之後的分支以執行魔法技巧.

=== 按你希望的方式工作 ===

你可能猶疑于分支是否值得一試。畢竟，克隆也几乎一樣快，並且你可以用 *cd* 來在
彼此之間切換，而不是用Git深奧的命令。

考慮一下瀏覽器。為什麼同時支持多標籤和多窗口？因為允許兩者同時接納納了多種風
格的用戶。一些用戶喜歡只保持一個打開的窗口，然後用標籤瀏覽多個網頁。一些可能
堅持另一個極端：任何地方都沒有標籤的多窗口。一些喜好處在兩者之間。

分支類似你工作目錄的標籤，克隆類似打開的瀏覽器新窗口。這些是本地操作很快，那
為什麼不試着找出最適合你的組合呢？Git讓你按你確實所希望的那樣工作。
