理解Git的工作流程

作者: Sandofsky  來源: 譯言  發布時間: 2012-07-28 10:42  閱讀: 9704 次  推薦: 8   原文鏈接   [收藏]  

  英文原文:Understanding the Git Workflow

  如果你不理解Git的設計動機,那你就會處處碰壁。知道足夠多的命令和參數后,你就會強行讓Git按你想的來工作,而不是按Git自己的方式來。這就像把螺絲刀當錘子用,也能把活干完,但肯定干的差極了,花費很長時間,還會弄壞螺絲刀。

  想想常見的Git工作流程是怎么失效的吧。

  多數時候這樣做的效果會如你所愿,因為從你創建分支到合并回去之間,Master一般都會有些變動。然后,有一天當你想把一個功能(feature)分支合并進Master的時候,而Master并沒有像以往那樣有變動,問題來了:這時Git不會進行合并commit,而是將Master指向功能分支上的最新commit。(看圖

  不幸的是,你的功能分支有用來備份代碼的commit(作者稱之為checkpoint commit),這些經常進行的commit對應的代碼可能處于不穩定狀態!而這些commit現在沒法和Master上那些穩定的commit區分開來了。當你想回滾的時候,很容易發生災難性后果。

  于是你就記住了:“當合并功能分支的時候,加上 -no-ff 選項強制進行一次全新的commit。”嗯,這么做好像解決問題了,那么繼續。

  然后一天你在線上環境中發現了一個嚴重bug,這時你需要追溯下這個bug是什么時候引入的。你運行了bisect命令,但卻總是追溯到一些不穩定的commit。因此你不得不放棄,改用人肉檢查。

  最后你將bug范圍縮小到一個文件。你運行blame命令查看這個文件在過去48小時里的變動。然后blame告訴你這個文件已經好幾周沒有被修改過了 —— 你知道根本不可能沒有變動。哦,原來是因為blame計算變動是從第一次commit算起,而不是merge的時候。你在幾周前的一次commit中改動了這個文件,但這個變動今天才被merge回來。

  用no-ff來救急,bisect又臨時失效,blame的運作機制又那么模糊,所有這些現象都說明一件事兒,那就是你正在把螺絲刀當錘子用。

  反思版本控制

  版本控制的存在是因為兩個原因。

  首先,版本控制是用來輔助寫代碼的。因為你要和同事同步代碼,并經常備份自己的代碼。當然了,把文件壓縮后發郵件也行,不過工程大了大概就不好辦了。

  其次,就是輔助配置管理工作。其中就包括并行開發的管理,比如一邊給線上版本修復bug,一邊開發下一個版本。配置管理也可以幫助弄清楚變動發生的具體時間,在追溯bug中是一個很好的工具。

  一般說來,這兩個原因是沖突的。

  在開發一個功能的時候,你應該經常做備份性的commit。然而,這些commit經常會讓軟件沒法編譯。

  理想情況是,你的版本更新歷史中的每一次變化都是明確且穩定的,不會有備份性commit帶來的噪聲,也不會有超過一萬行代碼變動的超大commit。一個清晰的版本歷史讓回滾和選擇性merge都變得相當容易,而且也方便以后的檢查和分析。然而,要維護這樣一個干凈的歷史版本庫,也許意味著總是要等到代碼完善之后才能提交變動。

  那么,經常性的commit和干凈的歷史,你選擇哪一個?

  如果你是在剛起步的創業公司中,干凈的歷史沒有太大幫助。你可以放心地把所有東西都往Master中提交,感覺不錯的時候隨時發布。

  如果團隊規模變大或是用戶規模擴大了,你就需要些工具和技巧來做約束,包括自動化測試,代碼檢查,以及干凈的版本歷史。

  功能分支貌似是一個不錯的折中選擇,能夠解決基本的并行開發問題。當你寫代碼的時候,可以不用怎么在意集成的問題,但它總有煩到你的時候。

  當你的項目規模足夠大的時候,簡單的 branch/commit/merge 工作流程就出問題了。縫縫補補已經不行了。這時你需要一個干凈的版本歷史庫。

  Git之所以是革命性的,就是因為它能同時給你這兩方面的好處。你可以在原型開發過程中經常備份變動,而搞定后只需要交付一個干凈的版本歷史。

  工作流程

  考慮兩種分支:公共的和私有的。

  公共分支是項目的權威性歷史庫。在公共分支中,每一個commit都應該確保簡潔、原子性,并且有完善的提交信息。此分支應該盡可能線性,且不能更改。公共分支包括Master和發行版的分支。

  私有分支是供你自己使用的,就像解決問題時的草稿紙。

  安全起見,把私有分支只保存在本地。如果你確實需要push到服務器的話(比如要同步你在家和辦公室的電腦),最好告訴同事這是私有的,不要基于這個分支展開工作。

  絕不要直接用merge命令把私有分支合并到公共分支中。要先用reset、rebase、squash merges、commit amending等工具把你的分支清理一下。

  把你自己看做一個作者,每一次的commit視為書中的一章。作者不會出版最初的草稿,就像Michael Crichton說的,“偉大的書都不是寫出來——而是改出來的”(“Great books aren’t written – they’re rewritten.”)。

  如果你沒接觸過Git,那么修改歷史對你來說好像是種禁忌。你習慣于認為提交過的所有東西都應該像刻在石頭上一樣不能抹去。但如果按這種邏輯,我們在文本處理軟件器中也不應該使用“撤銷”功能了。

  實用主義者們直到變化變為噪音的時候才關注變化。對于配置管理來說,我們關注宏觀的變化。日常commit(checkpoint commits)只是備份于云端的用于“撤銷”的緩沖。

  如果你保持公共歷史版本庫的簡潔,那么所謂的fast-forward merge就不僅安全而且可取了,它能保證版本變更歷史的線性和易于追溯。

  關于 -no-ff 僅剩的爭論就只剩“文檔證明”了。人們可能會先merge再commit,以此代表最新的線上部署版本。不過,這是反模式的。用tag吧。

  規則和例子

  根據改變的多少、持續工作時間的長短,以及分支分叉了多遠,我使用三種基本的方法。

  1)短期工作

  絕大多數時間,我做清理時只用squash merge命令。

  假設我創建了一個功能分支,并且在接下來一個小時里進行了一系列的checkpoint commit。

git checkout -b private_feature_branch
touch file1.txt
git add file1.txt
git commit -am "WIP" 

  完成開發后,我不是直接執行git merge命令,而是這樣:

git checkout master
git merge --squash private_feature_branch
git commit -v 

  然后我會花一分鐘時間寫個詳細的commit日志。

  2)較大的工作

  有時候一個功能可以延續好幾天,伴有大量的小的commit。

  我認為這些改變應該被分解為一些更小粒度的變更,所以squash作為工具來說就有點兒太糙了。(根據經驗我一般會問,“這樣能讓閱讀代碼更容易嗎?”)

  如果我的checkpoint commits之后有合理的更新,我可以使用rebase的交互模式。

  交互模式很強大。你可以用它來編輯、分解、重新排序、合并以前的commit。

  在我的功能分支上:

git rebase --interactive master 

  然后會打開一個編輯器,里邊是commit列表。每一行上依次是:要執行的操作、commit的SHA1值、當前commit的注釋。并且提供了包含所有可用命令列表的圖例。

  默認情況下,每個commit的操作都是“pick”,即不會修改commit。

pick ccd6e62 Work on back button
pick 1c83feb Bug fixes
pick f9d0c33 Start work on toolbar 

  我把第二行修改為“squash”,這樣第二個commit就會合并到第一個上去。

pick ccd6e62 Work on back button
squash 1c83feb Bug fixes
pick f9d0c33 Start work on toolbar 

  保存并退出,會彈出一個新的編輯器窗口,讓我為本次合并commit做注釋。就這樣。

  舍棄分支

  也許我的功能分支已經存在了很久很久,我不得不將好幾個分支合并進這個功能分支中,以便當我寫代碼時這個分支是足夠新的的。版本歷史讓人費解。最簡單的辦法是創建一個新的分支。

git checkout master
git checkout -b cleaned_up_branch
git merge --squash private_feature_branch
git reset 

  現在,我就有了一個包含我所有修改且不含之前分支歷史的工作目錄。這樣我就可以手動添加和commit我的變更了。

  總結

  如果你在與Git的默認設置背道而馳,先問問為什么。  

  將公共分支歷史看做不可變的、原子性的、容易追溯的。將私有分支歷史看做一次性的、可編輯的。

  推薦的工作流程是:

  1. 基于公共分支創建一個私有分支。

  2. 經常向這個私有分支commit代碼。

  3. 一旦你的代碼完善了,就清理掉私有分支的歷史。

  4. 將干凈的私有分支merge到公共分支中。

  (作者:Benjamin Sandofsky,譯者:張重騏)

8
0
 
標簽:Git 版本控制
 
 

文章列表

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()