第 4 章 分支巫術(shù)

2018-02-24 15:45 更新

即時分支合并是Git最給力的殺手锏。

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

打斷思維的連續(xù)性會使你的生產(chǎn)力大大降低,并且切換上下文也更麻煩,更大的損失。 使用中心版本控制我們必須從中心服務(wù)器下載一個新的工作拷貝。分布式系統(tǒng)的情況就 好多了,因為我們能夠在本地克隆所需要的版本。

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

方案?:Git有一個更好的工具對付這種情況,比克隆快多了而且節(jié)省空間:?git branch?。

使用這個魔咒,目錄里的文件突然從一個版本變到另一個。除了只是在歷史記錄里上跳 下竄外,這個轉(zhuǎn)換還可以做更多。你的文件可以從上一個發(fā)布版變到實驗版本到當前開 發(fā)版本到你朋友的版本等等。

老板鍵

曾經(jīng)玩過那樣的游戲嗎?按一個鍵(“老板鍵”),屏幕立即顯示一個電子表格或別的? 那么如果老板走進辦公室,而你正在玩游戲,就可以快速將游戲藏起來。

在某個目錄:

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

我們已經(jīng)創(chuàng)建了一個Git倉庫,該倉庫記錄一個包含特定信息的文件?,F(xiàn)在我們鍵入:

$ 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  # 切到適合老板看的版本

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

骯臟的工作

比如你正在開發(fā)某個特性,并且由于某種原因,你需要回退三個版本,臨時加進幾行打 印語句來,來看看一些東西是如何工作的。那么:

$ git commit -a
$ git checkout HEAD~3

現(xiàn)在你可以到處加丑陋的臨時代碼。你甚至可以提交這些改動。當你做完的時候,

$ git checkout master

來返回到你原來的工作??矗形刺峤蛔兏冀Y(jié)轉(zhuǎn)了。

如果你后來想保存臨時變更怎么辦?簡單:

$ git checkout -b dirty

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

$ git checkout dirty

我們在前面章節(jié)討論加載舊狀態(tài)的時候,曾經(jīng)接觸過這個命令。最終我們把故事說全: 文件改變成請求的狀態(tài),但我們必須離開主分支。從現(xiàn)在開始的任何提交都會將你的文 件提交到另一條不同的路,這個路可以之后命名。

換一個說法,在checkout一個舊狀態(tài)之后,Git自動把你放到一個新的,未命名的分支, 這個分支可以使用?git checkout -b?來命名和保存。

快速修訂

你正在做某件事的當間,被告知先停所有的事情,去修理一個新近發(fā)現(xiàn)的臭蟲,這個臭 蟲在提交 1b6d…

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

那么一旦你修正了這個臭蟲:

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

并可以繼續(xù)你原來的任務(wù)。你甚至可以“合并”到最新修訂:

$ git merge fixes

合并

一些版本控制系統(tǒng),創(chuàng)建分支很容易,但把分支合并回來很難。使用Git,合并簡直是家 常便飯,以至于甚至你可能對其發(fā)生沒有察覺。

我們很久之前就遇到合并了。?pull?命令取出提交并合并它們到你的當前分支。如果 你沒有本地變更,那這個合并就是一個“快進”,相當于中心式版本控制系統(tǒng)里的一個 弱化的獲取最新版本操作。但如有本地變更,Git將自動合并,并報告任何沖突。

通常,一個提交只有一個“父提交”,也叫前一個提交。合并分支到一起產(chǎn)生一個至少 有兩個父的提交。這就引出了問題:?HEAD~10?真正指哪個提交?一個提交可能有多個 父,那我們跟哪個呢?

原來這個表示每次選擇第一個父。這是可取的,因為在合并時候當前分支成了第一個父; 多數(shù)情況下我們只關(guān)注我們在當前分支都改了什么,而不是從其他分支合并來的變更。

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

$ git log HEAD^2

你可以忽略數(shù)字以指代第一個父。比如,顯示與第一個父的差別:

$ git diff HEAD^

你可以結(jié)合其他類型使用這個記號。比如:

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

開始一個新分支 “ancient” ,表示第一個父的第二個父的倒數(shù)第十次提交的狀態(tài)。

不間斷工作流

經(jīng)常在硬件項目里,計劃的第二步必須等第一步完成才能開始。待修的汽車傻等在車庫 里,直到特定的零件從工廠運來。一個原型在其可以構(gòu)建之前,可能苦等芯片成型。

軟件項目可能也類似。新功能的第二部分不得不等待,直到第一部分發(fā)布并通過測試。 一些項目要求你的代碼需要審批才能接受,因此你可能需要等待第一部分得到批準,才 能開始第二部分。

多虧了無痛分支合并,我們可以不必遵循這些規(guī)則,在第一部分正式準備好前開始第二 部分的工作。假設(shè)你已經(jīng)將第一部分提交并發(fā)去審批,比如說你現(xiàn)在在主分支。那么分 岔:

$ git checkout -b part2

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

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

最終,第一部分獲得批準:

$ git checkout master  # 回到第一部分
$ submit files         # 對世界發(fā)布
$ git merge part2      # 合并第二部分
$ git branch -d part2  # 刪除分支“part2”

現(xiàn)在你再次處在主分支,第二部分的代碼也在工作目錄。

很容易擴展這個技巧,應(yīng)用到任意數(shù)目的部分。它也很容易追溯分支:假如你很晚才意 識到你本應(yīng)在7次提交前就創(chuàng)建分支。那么鍵入:

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

分支?master?只有第一部分內(nèi)容,其他內(nèi)容在分支?part2?。 我們現(xiàn)在后一個分支; 我們創(chuàng)建了?master?分支還沒有切換過去,因為我們想繼續(xù)工作在?part2?。這是不 尋常的。直到現(xiàn)在,我們已經(jīng)在創(chuàng)建之后切換到分支,如:

$ git checkout HEAD~7 -b master  # 創(chuàng)建分支,并切換過去。

重組雜亂

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

$ git branch sanitized    # 為干凈提交創(chuàng)建分支
$ git checkout -b medley  # 創(chuàng)建并切換分支以進去工作

接下來,做任何事情:修臭蟲,加特性,加臨時代碼,諸如此類,經(jīng)常按這種方式提交。 然后:

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

應(yīng)用分支 “medley” 的祖父提交到分支 “sanitized” 。通過合適的挑選(像選櫻桃 那樣)你可以構(gòu)建一個只包含成熟代碼的分支,而且相關(guān)的提交也組織在一起。

管理分支

列出所有分支:

$ git branch

默認你從叫 “master” 的分支開始。一些人主張別碰“master”分支,而是創(chuàng)建你自 己版本的新分支。

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

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

臨時分支

很快你會發(fā)現(xiàn)你經(jīng)常會因為一些相似的原因創(chuàng)建短期的分支:每個其它分支只是為了保 存當前狀態(tài),那樣你就可以直接跳到較老狀態(tài)以修復高優(yōu)先級的臭蟲之類。

可以和電視的換臺做類比,臨時切到別的頻道,來看看其它臺那正放什么。但并不是簡 單地按幾個按鈕,你不得不創(chuàng)建,檢出,合并,以及刪除臨時分支。幸運的是,Git已經(jīng) 有了和電視機遙控器一樣方便的快捷方式:

$ git stash

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

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

你可以有多個隱藏,并用不同的方式來操作他們。參見?git help slash?。也許你已 經(jīng)猜到,Git維護在這個場景之后的分支以執(zhí)行魔法技巧.

按你希望的方式工作

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

考慮一下瀏覽器。為什么同時支持多標簽和多窗口?因為允許兩者同時接納了多種風 格的用戶。一些用戶喜歡只保持一個打開的窗口,然后用標簽瀏覽多個網(wǎng)頁。一些可能 堅持另一個極端:任何地方都沒有標簽的多窗口。一些喜好處在兩者之間。

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

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號