考慮到cvs的一些局限性,最近和同僚在公司推行git。
其實,如果推行svn的化,可能推行的難度會降低很多。不過lark說既然推行一個新的版本管理工具,總要花費一定的時間進行教育訓練、部署、轉換。而推行git和svn的代價不如想象中差距那麼大。是以,不如就多花些精力推行git , 可以帶來更多的好處。 這個想法說服了我。 然後就開始籌備了。 我發現網上很多git教程對一些基礎指令(比如git-reset)的介紹還是不夠清楚。另外,介紹git1.5的少,介紹git1.4的多。此外,對于如何基于git合作開發,介紹的内容也是少之又少。是以,決定寫一份教程,以減少在公司推廣git的教育訓練代價。
其實我也是一個git的新手。 寫這份教程也是我自己學習和摸索git的過程,其中基于git進行合作開發的模式參考了cvs,應該是很初級的合作模式。但是目前自己也隻能做到這一步了。 教程所述都是自己通過試驗驗證的。至少可以滿足公司基本的合作開發。教程寫完後,謝欣說可以放到blog與大家共享。我覺得是個不錯的主意。一方面我覺得這個文檔應該可以給git的新手一些幫助,另一方面也歡迎git的大牛指點。 這裡要感謝《git 中文教程》的作者。還有概述中關于git的優點描述拷貝了網絡上某位大牛的原話,但是拷貝的出處也是轉載的,就在這裡謝謝那位我不知名大牛了。
下面就開始了。
【轉】 ubuntu git入門教程
原文參考:http://hi.baidu.com/04023081/blog/item/5205b1ec0f070e26279791c7.html
1. 概述
對于軟體版本管理工具,酷訊決定摒棄cvs而轉向git了。
為什麼要選擇git? 你真正學會使用git時, 你就會覺得這個問題的回答是非常自然的。然而當真正需要用文字來回答時,卻覺得文字好像不是那麼夠用。 咳,該則麼回答呢?
其實,關鍵的問題不在于如何回答這個問題。 問題的關鍵是公司已經決定使用它了。那麼,我們的程式員們! 請開動你們的浏覽器,請拿出你的搜尋引擎工具,去自己發掘答案吧。在這裡,我隻能給你們一個最朦胧的感覺。
git和 cvs、svn不同,是一個分布式的源代碼管理工具。linux核心的代碼就是用git管理的。它很強,也很快。它給我們帶來的直接好處有:
1. 傻瓜都會的初始化,git init, git commit -a, 就完了。對于随便寫兩行代碼就要放到代碼管理工具裡的人來說,再合适不過。也可以拿git做備份系統,或者同步兩台機器的文檔,都很友善。
2. 絕大部分操作在本地完成,不用和集中的代碼管理伺服器互動,終于可以随時随地大膽地check in代碼了。 隻有最終完成的版本才需要向一個中心的集中的代碼管理伺服器送出。
3. 每次送出都會對所有代碼建立一個唯一的commit id。不像cvs那樣都是對單個檔案分别進行版本的更改。是以你可以一次性将某次送出前的所有代碼check出來,而不用考慮到底送出過那些檔案。(其實svn也可以做到這點)
4. branch管理容易多了,無論是建立新的branch,還是在branch之間切換都一條指令完成,不需要建立多餘的目錄。
5. branch之間merge時,不僅代碼會merge在一起,check in曆史也會保留,這點非常重要。
6. … 太多了
當然,git也會帶給我們一些困難,首先,你想要使用好git,就要真正明白它的原理,了解它的觀念, 對以那些cvs的熟手來說,改變你已經固有的純集中式源代碼管理的觀念尤為重要,同時也會讓你覺得有些困難。在使用git的初期,你可能會覺得有些困難, 但等你逐漸明白它時,你絕對會喜歡上它。這是一定的,就像我問你“喜歡一個溫吞如水、毫無感覺的主婦,還是喜歡一個奔放如火,讓你愛的癡狂恨的牙癢的情 人”一樣毋庸置疑。
下面,就讓我們進入學習git之旅…
請記住,這隻是一個非常簡單而且初級的教程, 想要成為git的專家,需要各位同僚不斷的自己深入挖掘。
2. git基礎指令
2.1 建立git庫—git-init
你們曾經建立過cvs的庫麼?應該很少有人操作過吧?因為很多人都是從cvs庫裡checkout代碼。同樣,在合作開發中,如果你不是一個代碼子產品的發 起者,也不會使用到這個指令,更多的是使用git-clone(見2.7節)。 但是,如果你想個人開發一個小子產品,并暫時用代碼管理工具管理起來(其實我就常這麼做,至少很多個人開發過程都可以保留下來,以便備份和恢複),建立一個 git庫是很容易和友善的。
對于酷訊來說,當一個代碼的git庫建立後,會添加代碼檔案到庫裡,并将這個庫放到公司一個專門用來進行代碼管理的伺服器上,使大家可以在以後clone(不明白?沒關系,繼續往後看就明白了)它。對于個人來說,你可以随便将這個庫放到哪裡,隻要你能通路的到就行。
建立一個git庫是很容易和友善的,隻要用指令 git-init 就可以了。在git1.4之前(包括git1.4)的版本,這個指令是git-init。
a) $ mkdir dir
b) $ cd dir
c) $ git-init
這樣,一個空的版本庫就建立好了,并在目前目錄中建立一個叫 .git 的子目錄。以後,是以的檔案變化資訊都會儲存到這個目錄下,而不像cvs那樣,會在每個目錄和子目錄下都建立一個讨厭的cvs目錄。
在.git目錄下有一個config檔案, 需要我們添加一下個人資訊後才能使用。否則我們不能對其中添加和修改任何檔案。
原始的config檔案是這樣的,
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
我們需要加入
[user]
name = xxx
emai= [email protected]
現在已經建立好了一個 git 版本庫,但是它是空的,還不能做任何事情,下一步就是怎麼向版本庫中添加檔案了。如果希望忽略某些檔案,需要在git庫根目錄下添加. gitignore檔案。
2.2 一條重要的指令 -- git-update-index
在介紹如何向git庫中添加檔案前,不得不先介紹git-update-index指令。這條指令可能會使很多熟悉cvs的使用者疑惑, 一般來說,我們向一個源代碼管理庫送出代碼的更改,都會抽象為以下的動作:更改檔案;向源碼管理系統辨別變化;送出。比如從一個cvs庫裡删除一個檔案, 需要先删除檔案,然後cvs delete; 最後cvs commit。
是以, git-update-index就是向源碼管理系統辨別檔案變化的一個抽象操作。說的簡要一些,git-update-index指令就是通知git庫 有檔案的狀态發生了變化(新添、修改、删除等待)。這條指令在早期的git版本中是非常常用的。 在新的git版本(1.5版本及以後)已經被其它指令包裝起來,并且不推薦使用了。
git-update-index最常用的方式有以下兩種,更多功能請man git-update-index。
方法一:git-update-index --add 檔案名清單。 如果檔案存在,則這條指令是向git庫辨別該檔案發生過變化(無論是否該檔案确實被修改過),如果檔案不存在,則這條指令是向git庫表示需要加入一個新檔案。
方法二: git-update-index --force-remove 檔案名清單。 這表示向git庫表示喲啊從庫中删除檔案。無論該檔案是否已經被删除,這條指令僅僅是通知git庫要從庫中删除這些檔案。這些檔案都不會受影響。
是以,git-update-index僅僅是向git庫起到一個通知和辨別的作用,并不會操作具體的檔案。
2.3 向git庫中添加或删除檔案 – git-add、git-rm
其實,說使用git-add指令向git庫裡添加檔案是不對的, 或者說至少是不全面的。git-add 指令的本質是指令"git-update-index --add” 的一個包裝。是以,git-add除了可以添加檔案,還可以辨別檔案修改。在調用了git-add後,才可以做commit操作。git-rm 也是一樣, 它是git-update-index --force-remove的一個包裝。
對于git-add來說, 如果在一個目錄下調用了git-add * ,則預設是遞歸将子目錄中所有檔案都add到git庫中。對于git-rm來說,也是一樣。 這點和cvs有較大差別。
此外,我們還可以通過指令git-ls-files來檢視目前的git庫中有那些檔案。
2.4 檢視版本庫狀态—git-status
通過該指令,我們可以檢視版本庫的狀态。可以得知那些檔案發生了變化,那些檔案還沒有添加到git庫中等等。 建議每次commit前都要通過該指令确認庫狀态。以避免誤操作。
其總,最常見的誤操作是, 修改了一個檔案, 沒有調用git-add通知git庫該檔案已經發生了變化就直接調用commit操作, 進而導緻該檔案并沒有真正的送出。如果這時如果開發者以為已經送出了該檔案,就繼續修改甚至删除這個檔案,那麼修改的内容就沒有通過版本管理起來。如果每 次在送出前,使用git-status檢視一下,就可以發現這種錯誤。是以,如果調用了git-status指令,一定要格外注意那些提示為 “changed but not updated:”的檔案。 這些檔案都是與上次commit相比發生了變化,但是卻沒有通過git-add辨別的檔案。
2.5 向版本庫送出變化 – git-commit
直接調用git-commit指令,會提示填寫注釋。也可以通過如下方式在指令行就填寫送出注釋:git-commit -m "initial commit of gittutor reposistory"。 注意,和cvs不同,git的送出注釋必須不能為空。否則就會送出失敗。
git-commit還有一個 –a的參數,可以将那些沒有通過git-add辨別的變化一并強行送出,但是不建議使用這種方式。
每一次送出,git就會為全局代碼建立一個唯一的commit辨別代碼,使用者可以通過git-revert指令恢複到任意一次送出時的代碼。 這比cvs不同檔案有不同的版本呢号管理可友善多了。(和svn類似)
如果送出前,想看看具體那些檔案發生變化,可以通過git-diff來檢視, 不過這個指令的輸出并不友好。是以建議用别的工具來實作該功能。在送出後,還可以通過git-log指令來檢視送出記錄。
2.6 分支管理 – git-branch
我們迎來了git最強大,也是比cvs、svn強大的多的功能 — 分支管理。
大概每個程式員都會經常遇到這樣的情況:
1. 需要立刻放下手頭的工作,去修改曾經一個版本的bug并上線,然後再繼續當的工作。
2. 本想向中心庫commit一個重要修改,但是由于需要經常備份代碼,最終不得不頻繁的向中心庫commit。進而導緻大量無用的commit資訊被保留在中心庫中。
3. 将一次修改送出同僚進行code review, 但是由于同僚code review比較慢, 得到回報時,自己的代碼已經發生了變化,進而倒是合并異常困難
這些場景,如果用cvs或者svn來解決,雖說不一定解決不了,但過程之繁瑣,之複雜,肯定另所有人都有生不如死的感覺吧!究其關鍵,就是cvs或者snv的branch管理太複雜,基本不具可用性。
在 git 版本庫中建立分支的成本幾乎為零,是以,不必吝啬多建立幾個分支。當第一次執行git-init時,系統就會建立一個名為”master”的分支。 而其它分支則通過手工建立。下面列舉一些常見的分支政策,這些政策相信會對你的日常開發帶來很大的便利。
1.建立一個屬于自己的個人工作分支,以避免對主分支 master 造成太多的幹擾,也友善與他人交流協作。
2.當進行高風險的工作時,建立一個試驗性的分支,扔掉一個爛攤子總比收拾一個爛攤子好得多。
3.合并别人的工作的時候,最好是建立一個臨時的分支用來合并,合并完成後在“fatch”到自己的分支(合并和fatch後面有講述,不明白就繼續往下看好了)
2.6.1 檢視分支 – git-branch
調用git-branch可以檢視程式中已經存在的分支和目前分支
2.6.2 建立分支 – git-branch 分支名
要建立一個分支,可以使用如下方法:
1. git-branch 分支名稱
2. git-checout –b 分支名
使用第一種方法,雖然建立了分支,但是不會将目前工作分支切換到新建立的分支上,是以,還需要指令”git-checkout 分支名” 來切換, 而第二種方法不但建立了分支,還将目前工作分支切換到了該分支上。
另外,需要注意,分支名稱是有可能出現重名的情況的, 比如說,我在master分支下建立了a和b兩個分支, 然後切換到b分支,在b分支下又建立了a和c分支。 這種操作是可以進行的。 此時的a分支和master下的a分支實際上是兩個不同的分支。 是以,在實際使用時,不建議這樣的操作,這樣會帶來命名上的疑惑。
2.6.3 删除分支 – git-branch –d
git-branch –d 分支名可以删除分支,但是需要小心,删除後,發生在該分支的所有變化都無法恢複。
2.6.4 切換分支 – git-checkout 分支名
如果分支已經存在, 可以通過 git-checkout 分支名 來切換工作分支到該分支名
2.6.5 檢視分支曆史 –git-show-branch
調用該指令可以檢視分支曆史變化情況。 如:
* [dev1] d2
! [master] m2
--
* [dev1^] d1
* [dev1~2] d1
*+ [master] m2
在上述例子中, “--”之上的兩行表示有兩個分支dev1和master, 且dev分支上最後一次送出的日志是“d2”,master分支上最後一次送出的日志是”m2”。 “--”之下的幾行表示了分支演化的曆史,其中 dev1表示發生在dev分支上的最後一次送出,dev^表示發生在dev分支上的倒數第二次送出。dev1~2表示發生在dev分支上的倒數第三次提 交。
2.6.6 合并分支 – git-merge
git-merge的用法為:git-merge “some memo” 合并的目标分支 合并的來源分支。如:
git-merge master dev1~2
如果合并有沖突,git會由提示,目前,git-merge已經很少用了, 用git-pull來替代了。
用法為:git-pull 合并的目标分支 合并的來源分支。 如git-pull . dev1^
2.7 遠端擷取一個git庫 git-clone
在2.1節提到過,如果你不是一個代碼子產品的發起者,也不會使用到git-init指令,而是更多的是使用git-clone。通過這個指令,你可以從遠端完整擷取一個git庫,并可以通過一些指令和遠端的git互動。
基于git的代碼管理的組織結構,往往形成一個樹狀結構,開發者一般從某個代碼子產品的管理者的git庫通過git-clone取得開發環境,在本地疊代開 發後,再送出給該子產品的管理者,該子產品的管理者檢查這些送出并将代碼合并到自己的庫中,并向更高一級的代碼管理者送出自己的子產品代碼。
對于酷訊來說,公司會有一個中心的git庫, 大家在開發時,都是從中心庫git-clone擷取最新代碼。
git-clone的使用方法如下: git-clone [ssh://]username@ipaddr:path。 其中, “ssh://”可選,也有别的擷取方式,如rsync。 path是遠端git的根路徑,也叫repository。
通過git-clone擷取遠端git庫後,.git/config中的開發者資訊不會被一起clone過來。仍然需要為.git/config檔案添加開發者資訊。此外,開發者還需要自己添加. gitignore檔案
另外,通過git-clone擷取的遠端git庫,隻包含了遠端git庫的目前工作分支。如果想擷取其它分支資訊,需要使用”git-branch –r” 來檢視, 如果需要将遠端的其它分支代碼也擷取過來,可以使用指令” git checkout -b 本地分支名 遠端分支名”,其中,遠端分支名為git-branch –r所列出的分支名, 一般是諸如“origin/分支名”的樣子。如果本地分支名已經存在, 則不需要“-b”參數。
2.8 從遠端擷取一個git分支 – git-pull
與git-clone不同, git-pull可以從任意一個git庫擷取某個分支的内容。用法如下:
需要注意的是,git-pull也可以用來合并分支。 和git-merge的作用相同。 是以,如果你的本地分支已經有内容,則git-pull會合并這些檔案,如果有沖突會報警。
2.9 将本地分支内容送出到遠端分支 – git-push
git-push和git-pull正好想反,是将本地某個分支的内容送出到遠端某個分支上。用法:
需要格外注意的是,git-push好像不會自動合并檔案。這點我的試驗表明是這樣,但我不能确認是否是我用錯了。是以,如果git-push時,發生了沖突,就會被後push的檔案内容強行覆寫,而且沒有什麼提示。 這在合作開發時是很危險的事情。
2.10 庫的逆轉與恢複 – git-reset
庫的逆轉與恢複除了用來進行一些廢棄的研發代碼的重置外,還有一個重要的作用。比如我們從遠端clone了一個代碼庫,在本地開發後,準備送出回遠端。但 是本地代碼庫在開發時,有功能性的commit,也有出于備份目的的commit等等。總之,commit的日志中有大量無用log,我們并不想把這些 log在送出回遠端時也送出到庫中。 是以,就要用到git-reset。
git-reset的概念比較複雜。它的指令形式:git-reset [--mixed | --soft | --hard] [<commit-ish>]
指令的選項:
--mixed
這個是預設的選項。 如git-reset [--mixed] dev1^(dev1^的定義可以參見2.6.5)。它的作用僅是重置分支狀态到dev1^, 但是卻不改變任何工作檔案的内容。即,從dev1^到dev1的所有檔案變化都保留了,但是dev1^到dev1之間的所有commit日志都被清除了, 而且,發生變化的檔案内容也沒有通過git-add辨別,如果您要重新commit,還需要對變化的檔案做一次git-add。 這樣,commit後,就得到了一份非常幹淨的送出記錄。
--soft
相當于做了git-reset –mixed,後,又對變化的檔案做了git-add。如果用了該選項, 就可以直接commit了。
--hard
這個指令就會導緻所有資訊的回退, 包括檔案内容。 一般隻有在重置廢棄代碼時,才用它。 執行後,檔案内容也無法恢複回來了。
2.11 更多的操作
3. 基于git的合作開發
對于酷訊來說,當我們采用了git,如何進行合作開發呢? 具體步驟如下:
3.1 擷取最新代碼
酷訊會準備一個中心git代碼庫。首先,我們将整理好的代碼分子產品在git中心庫中建立git庫。并将檔案add到中心庫中。 接下來,開發者通過git-clone将代碼從中心庫clone到本地開發環境。
對于較大的項目,我們還建議每個組選擇一個負責人,由這個負責人負責從中心庫擷取和更新最新的代碼,其它開發者從這個負責人的git代碼庫中clone代碼。此時,對開發者來說,這個負責人的git庫就是中心庫了。
3.2 開發者在本地進行疊代開發
當使用者将代碼clone到本地後, 就可以進行本地的疊代開發,建議使用者不要在master分支上開發,而是建立一個開發分支進行開發。 在本地開發中,使用者可以随意的建立臨時分支,随意commit。
3.3 開發者請其它同僚進行code review
當本地開發完畢,可以請其它同僚進行code review。過程為:
1. user2通通過git-pull指令,将開發者(user1)的開發分支(dev)pull到user2本地的一個tmp分支,并切換工作分支到該分支上進行code review。
2. 完成code review後, user2切換回其原有開發分支繼續開發,并告知user1已經修改完畢。
3. user1将user2的tmp分支git-pull到本地tmp分支,并和dev分支進行merge。最終得到一個code review後的dev分支。
當然,user2也可以直接坐在user1旁邊在他的代碼上進行review。而不需要走上述步驟。(圖中第7步,不是git-pull,而是直接在dev分支上和user1邊review邊modify)
3.4 和中心庫進行代碼合并
使用過cvs的人都知道, 在commit之前,都要做一次cvs update,以避免和中心庫沖突。git也是如此。
現在我們已經經過了code review, 準備向中心庫送出變化了, 在開發的這段時間,也許中心庫發生了變化, 是以,我們需要在向中心庫送出前,再次将中心庫的master分支git-pull到本地的master分支上。并且和dev分支做合并。最終,将合并的 代碼放入master分支。
如果開發過程送出日志過多,可以考慮參照2.10節的介紹做一次git-reset。
此外,如果發現合并過程變化非常多, 出于代碼品質考慮,建議再做一次code review
3.5 送出代碼到中心庫
此時,已經完全準備好送出最終的代碼了。 通過git-push就可以了。
3.6 合作流程總結
大家可以看到,使用git進行合作開發,這一過程和cvs有很多相似性,同時,增強了以下幾個環節:
1. 開發者在本地進行疊代開發,可以經常的做commit操作且不會影響他人。 而且即使不線上也可以進行開發。隻需要最後向中心庫送出一次即可。
2. 大家都知道,如果cvs管理代碼,由于我們會常常做commit操作。但是在commit之前cvs update時常會遇到将中心庫上的其它最新代碼checkout下來的情況,此時,一旦出現問題,就很難确認到底是自己開發的bug還是其它使用者的代碼 帶來了影響。 而使用git則避免了使用者間的開發互相影響。
3. 更有利于在代碼送出前做code review。 以往用cvs, 都是代碼送出後才做code view。如果發生問題, 也無法避免伺服器上有不好的代碼。 但是用git, 真正向中心庫commit前,都是在本地開發,可以友善的進行code review, 然後才送出到中心庫。更有利于代碼品質。而且, 大家應該可以感到,使用git的過程中,更容易對代碼進行code review,因為影響因素更小。
4. 建立多分支,更容易在開發中進行多種工作,而使工作間不會互相影響。 比如user2對user1的代碼進行code review時,就可以非常友善的保留當時的開發現場,并切換到user1的代碼分支,在code review完畢後,也可以非常友善的切換會曾經被中斷的工作現場。
誠然,帶來這些好處的同時,确實也使得操作比cvs複雜了一些。但我們覺得和前面所能獲得的好處相比,這些麻煩是值得的。 當大家用慣了之後會發現,這并不增加多大的複雜性, 而且開發流程會更加自然。
請大家多動手,多嘗試! 去體驗git的魅力所在吧!let’s enjoy it!
轉載請注明出處:http://www.cnblogs.com/haochuang/ 8年it工作經驗,5年測試技術與管理,2年産品與項目管理,曾參與過雲計算\雲存儲\車聯網産品研發工作; 業餘自媒體人,有技術類垂直微信公衆号;如有招聘或求職方面需求,請mail to [email protected] ;或通過 qq:363573922 微網誌:@念槐聚 聯系;