中文字幕理论片,69视频免费在线观看,亚洲成人app,国产1级毛片,刘涛最大尺度戏视频,欧美亚洲美女视频,2021韩国美女仙女屋vip视频

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費(fèi)電子書(shū)等14項(xiàng)超值服

開(kāi)通VIP
Git與Repo入門(mén)

Git與Repo入門(mén)

版本控制

  版本控制是什么已不用在說(shuō)了,就是記錄我們對(duì)文件、目錄或工程等的修改歷史,方便查看更改歷史,備份以便恢復(fù)以前的版本,多人協(xié)作。。。

一、原始版本控制

  最原始的版本控制是純手工的版本控制:修改文件,保存文件副本。有時(shí)候偷懶省事,保存副本時(shí)命名比較隨意,時(shí)間長(zhǎng)了就不知道哪個(gè)是新的,哪個(gè)是老的了,即使知道新舊,可能也不知道每個(gè)版本是什么內(nèi)容,相對(duì)上一版作了什么修改了,當(dāng)幾個(gè)版本過(guò)去后,很可能就是下面的樣子了:

  

二、本地版本控制

  手工管理比較麻煩且混亂,所以出現(xiàn)了本地版本控制系統(tǒng),記錄文件每次的更新,可以對(duì)每個(gè)版本做一個(gè)快照,或是記錄補(bǔ)丁文件。比如RCS。

  

三、集中版本控制

  但是本地版本控制系統(tǒng)偏向于個(gè)人使用,或者多個(gè)使用的人必須要使用相同的設(shè)備,如果需要多人協(xié)作就不好辦了,于是,集中化的版本控制系統(tǒng)( Centralized Version Control Systems,簡(jiǎn)稱 CVCS )應(yīng)運(yùn)而生,比如Subversion,Perforce。

  在CVCS中,所有的版本數(shù)據(jù)都保存在服務(wù)器上,一起工作的人從服務(wù)器上同步更新或上傳自己的修改。

   

  但是,所有的版本數(shù)據(jù)都存在服務(wù)器上,用戶的本地設(shè)備就只有自己以前所同步的版本,如果不連網(wǎng)的話,用戶就看不到歷史版本,也無(wú)法切換版本驗(yàn)證問(wèn)題,或在不同分支工作。。

  而且,所有數(shù)據(jù)都保存在單一的服務(wù)器上,有很大的風(fēng)險(xiǎn)這個(gè)服務(wù)器會(huì)損壞,這樣就會(huì)丟失所有的數(shù)據(jù),當(dāng)然可以定期備份。

四、分布式版本控制

  針對(duì)CVCS的以上缺點(diǎn),出現(xiàn)了分布式版本控制系統(tǒng)( Distributed Version Control System,簡(jiǎn)稱 DVCS ),如GIT,Mercurial。

  DVCS不是復(fù)制指定版本的快照,而是把所有的版本信息倉(cāng)庫(kù)全部同步到本地,這樣就可以在本地查看所有版本歷史,可以離線在本地提交,只需在連網(wǎng)時(shí)push到相應(yīng)的服務(wù)器或其他用戶那里。由于每個(gè)用戶那里保存的都是所有的版本數(shù)據(jù),所以,只要有一個(gè)用戶的設(shè)備沒(méi)有問(wèn)題就可以恢復(fù)所有的數(shù)據(jù)。

  當(dāng)然,這增加了本地存儲(chǔ)空間的占用。

  

GIT

  必須要了解GIT的原理,才能知道每個(gè)操作的意義是什么,才能更容易地理解在什么情況下用什么操作,而不是死記命令。當(dāng)然,第一步是要獲得一個(gè)GIT倉(cāng)庫(kù)。

一、獲得GIT倉(cāng)庫(kù)

  有兩種獲得GIT倉(cāng)庫(kù)的方法,一是在需要用GIT管理的項(xiàng)目的根目錄執(zhí)行:

git init

  執(zhí)行后可以看到,僅僅在項(xiàng)目目錄多出了一個(gè).git目錄,關(guān)于版本等的所有信息都在這個(gè)目錄里面。

  另一種方式是克隆遠(yuǎn)程目錄,由于是將遠(yuǎn)程服務(wù)器上的倉(cāng)庫(kù)完全鏡像一份至本地,而不是取某一個(gè)特定版本,所以用clone而不是checkout:

git clone <url>

二、GIT中版本的保存

  記錄版本信息的方式主要有兩種:

  1. 記錄文件每個(gè)版本的快照
  2. 記錄文件每個(gè)版本之間的差異

  GIT采用第一種方式。像Subversion和Perforce等版本控制系統(tǒng)都是記錄文件每個(gè)版本之間的差異,這就需要對(duì)比文件兩版本之間的具體差異,但是GIT不關(guān)心文件兩個(gè)版本之間的具體差別,而是關(guān)心文件的整體是否有改變,若文件被改變,在添加提交時(shí)就生成文件新版本的快照,而判斷文件整體是否改變的方法就是用SHA-1算法計(jì)算文件的校驗(yàn)和。

  GIT能正常工作完全信賴于這種SHA-1校驗(yàn)和,當(dāng)一個(gè)文件的某一個(gè)版本被記錄之后會(huì)生成這個(gè)版本的一個(gè)快照,但是一樣要能引用到這個(gè)快照,GIT中對(duì)快照的引用,對(duì)每個(gè)版本的記錄標(biāo)識(shí)全是通過(guò)SHA-1校驗(yàn)和來(lái)實(shí)現(xiàn)的。

  當(dāng)一個(gè)文件被改變時(shí),它的校驗(yàn)和一定會(huì)被改變(理論上存在兩個(gè)文件校驗(yàn)和相同,但機(jī)率小到可以忽略不計(jì)),GIT就以此判斷文件是否被修改,及以些記錄不同版本。

  在工作目錄的文件可以處于不同的狀態(tài),比如說(shuō)新添加了一個(gè)文件,GIT發(fā)覺(jué)了這個(gè)文件,但這個(gè)文件是否要納入GIT的版本控制還是要由我們自己決定,比如編譯生成的中間文件,我們肯定不想納入版本控制。下面就來(lái)看下文件狀態(tài)。

三、GIT文件操作

  版本控制就是對(duì)文件的版本控制,對(duì)于Linux來(lái)說(shuō),設(shè)備,目錄等全是文件,要對(duì)文件進(jìn)行修改、提交等操作,首先要知道文件當(dāng)前在什么狀態(tài),不然可能會(huì)提交了現(xiàn)在還不想提交的文件,或者要提交的文件沒(méi)提交上。

文件狀態(tài)

  GIT倉(cāng)庫(kù)所在的目錄稱為工作目錄,這個(gè)很好理解,我們的工程就在這里,工作時(shí)也是在這里做修改。

  在工作目錄中的文件被分為兩種狀態(tài),一種是已跟蹤狀態(tài)(tracked),另一種是未跟蹤狀態(tài)(untracked)。只有處于已跟蹤狀態(tài)的文件才被納入GIT的版本控制。如下圖:

  

  當(dāng)我們往工作目錄添加一個(gè)文件的時(shí)候,這個(gè)文件默認(rèn)是未跟蹤狀態(tài)的,我們肯定不希望編譯生成的一大堆臨時(shí)文件默認(rèn)被跟蹤還要我們每次手動(dòng)將這些文件清除出去。用以下命令可以跟蹤文件:

git add <file>

  上圖中右邊3個(gè)狀態(tài)都是已跟蹤狀態(tài),其中的灰色箭頭只表示untracked<-->tracked的轉(zhuǎn)換而不是untracked<-->unmodified的轉(zhuǎn)換,新添加的文件肯定算是被修改過(guò)的。那么,staged狀態(tài)又是什么呢?這就要搞清楚GIT的三個(gè)工作區(qū)域:本地?cái)?shù)據(jù)(倉(cāng)庫(kù))目錄,工作目錄,暫存區(qū),如下圖所示:

  

  git directory就是我們的本地倉(cāng)庫(kù).git目錄,里面保存了所有的版本信息等內(nèi)容。

  working driectory,工作目錄,就是我們的工作目錄,其中包括未跟蹤文件及已跟蹤文件,而已跟蹤文件都是從git directory取出來(lái)的文件的某一個(gè)版本或新跟蹤的文件。

  staging area,暫存區(qū),不對(duì)應(yīng)一個(gè)具體目錄,其時(shí)只是git directory中的一個(gè)特殊文件。

  當(dāng)我們修改了一些文件后,要將其放入暫存區(qū)然后才能提交,每次提交時(shí)其實(shí)都是提交暫存區(qū)的文件到git倉(cāng)庫(kù),然后清除暫存區(qū)。而checkout某一版本時(shí),這一版本的文件就從git倉(cāng)庫(kù)取出來(lái)放到了我們的工作目錄。

文件狀態(tài)的查看

  那么,我們?cè)趺粗喇?dāng)前工作目錄的狀態(tài)呢?哪些文件已被暫存?有哪些未跟蹤的文件?哪些文件被修改了?所有這些只需要一個(gè)命令,git status,如下圖所示:

  

  GIT在這一點(diǎn)做得很好,在輸出每個(gè)文件狀態(tài)的同時(shí)還說(shuō)明了怎么操作,像上圖就有怎么暫存、怎么跟蹤文件、怎么取消暫存的說(shuō)明。

文件暫存

  在上圖中我們可以很清楚地看到,filea未跟蹤,fileb已被暫存(changes to be committed),但是怎么還有一個(gè)fileb是modified但unstaged呢?這是因?yàn)楫?dāng)我們暫存一從此文件時(shí),暫存的是那一文件當(dāng)時(shí)的版本,當(dāng)暫存后再次修改了這個(gè)文件后就會(huì)提示這個(gè)文件暫存后的修改是未被暫存的。

  接下來(lái)我們就看怎么暫存文件,其實(shí)也很簡(jiǎn)單,從上圖中可以看到GIT已經(jīng)提示我們了:use "git add <file>..." to update what will be committed,通過(guò) 

git add <file>...

  就可以暫存文件,跟蹤文件同樣是這一個(gè)命令。在這個(gè)命令中可以使用glob模式匹配,比如"file[ab]",也可以使用"git add ."添加當(dāng)前目錄下的所有文件。

  取消暫存文件是 

git reset HEAD <file>...

   若修改了一個(gè)文件想還原修改可用

git checkout -- <file>... 

查看文件修改后的差異

  當(dāng)我們修改過(guò)一些文件之后,我們可能想查看我們都修改了什么東西,用"git status"只能查看對(duì)哪些文件做了改動(dòng),如果要看改動(dòng)了什么,可以用:

git diff

  比如下圖:

  

  ---a表示修改之前的文件,+++b表示修改后的文件,上圖表示在fileb的第一行后添加了一行"bb",原來(lái)文件的第一行擴(kuò)展為了修改后的1、2行。

  但是,前面我們明明用"git status"看到filesb做了一些修改后暫存了,然后又修改了fileb,理應(yīng)有兩次修改的,怎么只有一個(gè)?

  因?yàn)?git diff"顯示的是文件修改后還沒(méi)有暫存起來(lái)的內(nèi)容,那如果要比較暫存區(qū)的文件與之前已經(jīng)提交過(guò)的文件呢,畢竟實(shí)際提交的是暫存區(qū)的內(nèi)容,可以用以下命令:

  

  /dev/null表示之前沒(méi)有提交過(guò)這一個(gè)文件,這是將是第一次提交,用:

git diff --staged

  是等效的,但GIT的版本要大于1.6.1。

  再次執(zhí)行"git add"將覆蓋暫存區(qū)的內(nèi)容。

忽略一些文件

  如果有一些部件我們不想納入版本控制,也不想在每次"git status"時(shí)看到這些文件的提示,或者很多時(shí)候我們?yōu)榱朔奖銜?huì)使用"git add ."添加所有修改的文件,這時(shí)就會(huì)添加上一些我們不想添加的文件,怎么忽略這些文件呢?

  GIT當(dāng)然提供了方法,只需在主目錄下建立".gitignore"文件,此文件有如下規(guī)則:

  • 所有以#開(kāi)頭的行會(huì)被忽略
  • 可以使用glob模式匹配
  • 匹配模式后跟反斜杠(/)表示要忽略的是目錄
  • 如果不要忽略某模式的文件在模式前加"!"

  比如:

# 此為注釋 – 將被 Git 忽略
*.a # 忽略所有 .a 結(jié)尾的文件
!lib.a # 但 lib.a 除外
/TODO # 僅僅忽略項(xiàng)目根目錄下的 TODO 文件,不包括 subdir/TODO
build/ # 忽略 build/ 目錄下的所有文件
doc/*.txt # 會(huì)忽略 doc/notes.txt 但不包括 doc/server/arch.txt

移除文件

  當(dāng)我們要?jiǎng)h除一個(gè)文件時(shí),我們可能就直接用GUI刪除或者直接rm [file]了,但是看圖:

  

  我們需要將文件添加到暫存區(qū)才能提交,而移除文件后是無(wú)法添加到暫存區(qū)的,那么怎么移除一個(gè)文件讓GIT不再將其納入版本控制呢?上圖中GIT已經(jīng)給出了說(shuō)明:

git rm <file>...

  執(zhí)行以上命令后提交就可以了,有時(shí)我們只是想將一些文件從版本控制中剔除出去,但仍保留這些文件在工作目錄中,比如我們一不小心將編譯生成的中間文件納入了版本控制,想將其從版本控制中剔除出去但在工作目錄中保留這些文件(不然再次編譯可要花費(fèi)更多時(shí)間了),這時(shí)只需要添加"--cached"參數(shù)。

  如果我們之前不是通過(guò)"git rm"刪除了很多文件呢?比如說(shuō)通過(guò)patch或者通過(guò)GUI,如果這些文件命名沒(méi)有規(guī)則,一個(gè)一個(gè)地執(zhí)行"git rm"會(huì)搞死人的,這時(shí)可以用以下命令:

  

移動(dòng)文件

  和移除文件一樣,移動(dòng)文件不可以通過(guò)GUI直接重命令或用"mv"命令,而是要用"git mv",不然同移除文件一樣你會(huì)得到如下結(jié)果:

  

  如果要重命名文件可以使用

git mv old_name new_name

  這個(gè)命令等效于

mv old_name new_name

git rm old_name

git add new_name

交互式暫存

  使用git add -i可以開(kāi)啟交互式暫存,如圖所示,系統(tǒng)會(huì)列出一個(gè)功能菜單讓選擇將要執(zhí)行的操作。

  

移除所有未跟蹤文件

git clean [options]  一般會(huì)加上參數(shù)-df,-d表示包含目錄,-f表示強(qiáng)制清除。

儲(chǔ)藏-Stashing

  可能會(huì)遇到這樣的情況,你正在一個(gè)分支上進(jìn)行一個(gè)特性的開(kāi)發(fā),或者一個(gè)Bug的修正,但是這時(shí)突然有其他的事情急需處理,這時(shí)該怎么辦?不可能就在這個(gè)工作進(jìn)行到一半的分支上一起處理,先把修改的Copy出去?太麻煩了。這種情況下就要用到Stashing了。假如我們現(xiàn)在的工作目錄是這樣子的

$ git status# On branch master# Changes to be committed:#(use "git reset HEAD <file>..." to unstage)##modified:index.html## Changed but not updated:#(use "git add <file>..." to update what will be committed)##modified:lib/simplegit.rb

  此時(shí)如果想切換分支就可以執(zhí)行以下命令

$ git stashSaved working directory and index state "WIP on master: 049d078 added the index file"HEAD is now at 049d078 added the index file(To restore them type "git stash apply")

  這時(shí)你會(huì)發(fā)現(xiàn)你的工作目錄變得很干凈了,就可以隨意切分支進(jìn)行其他事情的處理了。

  我們可能不只一次進(jìn)行"git stash",通過(guò)以下命令可以查看所有stash列表

$ git stash liststash@{0}: WIP on master: 049d078 added the index filestash@{1}: WIP on master: c264051... Revert "added file_size"

  當(dāng)緊急事情處理完了,需要重新回來(lái)這里進(jìn)行原來(lái)的工作時(shí),只需把Stash區(qū)域的內(nèi)容取出來(lái)應(yīng)用到當(dāng)前工作目錄就行,命令就是

git stash apply

  如果不基參數(shù)就應(yīng)用最新的stash,或者可以指定stash的名字,如:stash@{1},可能通過(guò)

git stash show

  顯示stash的內(nèi)容具體是什么,同git stash apply一樣,可以選擇指定stash的名字。

  git stash apply之后再git stash list會(huì)發(fā)現(xiàn),apply后的stash還在stash列表中,如果要將其從stash列表中刪除可以用

git stash drop

  丟棄這個(gè)stash,stash的命令參數(shù)都可選擇指定stash名字,否則就是最新的stash。

  一般情況下apply stash后應(yīng)該就可以把它從stash列表刪除了,先apply再drop還是比較繁瑣的,使用以下一條命令就可以同時(shí)完成這兩個(gè)操作

git stash pop

  如果我們執(zhí)行g(shù)it stash時(shí)工作目錄的狀態(tài)是部分文件已經(jīng)加入了暫存區(qū),部分文件沒(méi)有,當(dāng)我們執(zhí)行g(shù)it stash apply之后會(huì)發(fā)現(xiàn)所有文件都變成了未暫存的,如果想維持原來(lái)的樣子操持原來(lái)暫存的文件仍然是暫存狀態(tài),可以加上--index參數(shù)

git stash apply --index

  還有這么一種情況,我們把原來(lái)的修改stash了,然后修復(fù)了其他一些東西并進(jìn)行了提交,但是,這些提交的文件有些在之前已經(jīng)被stash了,那么git stash apply時(shí)就很可能會(huì)遇到?jīng)_突,這種情況下就可以在stash時(shí)所以提交的基礎(chǔ)上新建一個(gè)分支,然后再apply stash,當(dāng)然,這兩個(gè)步驟有一人簡(jiǎn)單的完成方法

git stash branch <branch name>

四、提交與歷史

  了解了文件的狀態(tài),我們對(duì)文件進(jìn)行了必要的修改后,就要把我們所做的修改放入版本庫(kù)了,這樣以后我們就可以在需要的時(shí)候恢復(fù)到現(xiàn)在的版本,而要恢復(fù)到某一版,一般需要查看版本的歷史。

提交

  提交很簡(jiǎn)單,直接執(zhí)行"git commit"。執(zhí)行g(shù)it commit后會(huì)調(diào)用默認(rèn)的或我們?cè)O(shè)置的編譯器要我們填寫(xiě)提示說(shuō)明,但是提交說(shuō)明最好按GIT要求填寫(xiě):第一行填簡(jiǎn)單說(shuō)明,隔一行填寫(xiě)詳細(xì)說(shuō)明。因?yàn)榈谝恍性谝恍┣闆r下會(huì)被提取使用,比如查看簡(jiǎn)短提交歷史或向別人提交補(bǔ)丁,所以字符數(shù)不應(yīng)太多,40為好。下面看一下查看提交歷史。

查看提交歷史

  查看提交歷史使用如下圖的命令

  

  如圖所示,顯示了作者,作者郵箱,提交說(shuō)明與提交時(shí)間,"git log"可以使用放多參數(shù),比如:

  

  僅顯示最新的1個(gè)log,用"-n"表示。

  

  顯示簡(jiǎn)單的SHA-1值與簡(jiǎn)單提交說(shuō)明,oneline僅顯示提交說(shuō)明的第一行,所以第一行說(shuō)明最好簡(jiǎn)單點(diǎn)方便在一行顯示。

  "git log --graph"以圖形化的方式顯示提交歷史的關(guān)系,這就可以方便地查看提交歷史的分支信息,當(dāng)然是控制臺(tái)用字符畫(huà)出來(lái)的圖形。

  "git log"的更多參數(shù)可以查看命令幫助。

不經(jīng)過(guò)暫存的提交

 如果我們想跳過(guò)暫存區(qū)直接提交修改的文件,可以使用"-a"參數(shù),但要慎重,別一不小心提交了不想提交的文件

git commit -a

  如果需要快捷地填寫(xiě)提交說(shuō)明可使用"-m"參數(shù)

git commit -m 'commit message'

修訂提交

  如果我們提交過(guò)后發(fā)現(xiàn)有個(gè)文件改錯(cuò)了,或者只是想修改提交說(shuō)明,這時(shí)可以對(duì)相應(yīng)文件做出修改,將修改過(guò)的文件通過(guò)"git add"添加到暫存區(qū),然后執(zhí)行以下命令:

git commit --amend

  然后修改提交說(shuō)明覆蓋上次提交,但只能重寫(xiě)最后一次提交。

重排提交

  通過(guò)衍合(rebase)可以修改多個(gè)提交的說(shuō)明,并可以重排提交歷史,拆分、合并提交,關(guān)于rebase在講到分支時(shí)再說(shuō),這里先看一下重排提交。

  假設(shè)我們的提交歷史是這樣的:

  

  如果我們想重排最后兩個(gè)提交的提交歷史,可以借助交互式rebase命令: 

 git rebase -i HEAD~2 或 git rebase -i 3366e1123010e7d67620ff86040a061ae76de0c8

  HEAD~2表示倒數(shù)第三個(gè)提交,這條命令要指定要重排的最舊的提交的父提交,此處要重排Second commit與Third commit,所以要指定Initial commit的Commit ID。如圖所示:

  

  注釋部分詳細(xì)說(shuō)明了每個(gè)選項(xiàng)的作用,如果我們想交互這兩個(gè)提交,只需把開(kāi)頭的這兩行交換下位置就OK了,交換位置后保存,然后看下提交歷史:

  

  可以看到提交歷史已經(jīng)變了,而且最新的兩個(gè)提交的Commit ID變了,如果這些提交已經(jīng)push到了遠(yuǎn)程服務(wù)器,就不要用這個(gè)命令了。

刪除提交與修改提交說(shuō)明

  如果要?jiǎng)h除某個(gè)提交,只需要?jiǎng)h除相應(yīng)的行就可以了,而要修改某個(gè)提交的提交說(shuō)明的話,只需要把相應(yīng)行的pick改為reward。

合并提交-squashing

  合并提交也很簡(jiǎn)單,從注釋中的說(shuō)明看,只需要把相應(yīng)的行的pick改為squash就可以把這個(gè)提交全并到它上一行的提交中。

拆分提交

  至于拆分提交,由于Git不可能知道你要做哪里把某一提交拆分開(kāi),把以我們就需要讓Git在需要拆分的提交處停下來(lái),由我們手動(dòng)修改提交,這時(shí)要把pick改為edit,這樣Git在處理到這個(gè)提交時(shí)會(huì)停下來(lái),此時(shí)我們就可以進(jìn)行相應(yīng)的修改并多次提交來(lái)拆分提交。

撤銷(xiāo)提交

  前面說(shuō)了刪除提交的方法,但是如果是多人合作的話,如果某個(gè)提交已經(jīng)Push到遠(yuǎn)程倉(cāng)庫(kù),是不可以用那種方法刪除提交的,這時(shí)就要撤銷(xiāo)提交

git revert <commit-id>

  這條命令會(huì)把指定的提交的所有修改回滾,并同時(shí)生成一個(gè)新的提交。

Reset

  git reset會(huì)修改HEAD到指定的狀態(tài),用法為

git reset [options] <commit>

  這條命令會(huì)使HEAD提向指定的Commit,一般會(huì)用到3個(gè)參數(shù),這3個(gè)參數(shù)會(huì)影響到工作區(qū)與暫存區(qū)中的修改:

  • --soft: 只改變HEAD的State,不更改工作區(qū)與暫存區(qū)的內(nèi)容
  • --mixed(默認(rèn)): 撤銷(xiāo)暫存區(qū)的修改,暫存區(qū)的修改會(huì)轉(zhuǎn)移到工作區(qū)
  • --hard: 撤銷(xiāo)工作區(qū)與暫存區(qū)的修改

cherry-pick

  當(dāng)與別人和作開(kāi)發(fā)時(shí),會(huì)向別人貢獻(xiàn)代碼或者接收別人貢獻(xiàn)的代碼,有時(shí)候可能不想完全Merge別人貢獻(xiàn)的代碼,只想要其中的某一個(gè)提交,這時(shí)就可以使用cherry-pick了。就一個(gè)命令

 git cherry-pick <commit-id>

filter-branch

  這條命令可以修改整個(gè)歷史,如從所有歷史中刪除某個(gè)文件相關(guān)的信息,全局性地更換電子郵件地址。

五、GIT分支

   分支被稱之為GIT最強(qiáng)大的特性,因?yàn)樗浅5剌p量級(jí),如果用Perforce等工具應(yīng)該知道,創(chuàng)建分支就是克隆原目錄的一個(gè)完整副本,對(duì)于大型工程來(lái)說(shuō),太費(fèi)時(shí)費(fèi)力了,而對(duì)于GIT來(lái)說(shuō),可以在瞬間生成一個(gè)新的分支,無(wú)論工程的規(guī)模有多大,因?yàn)镚IT的分支其實(shí)就是一指針而已。在了解GIT分支之前,應(yīng)該先了解GIT是如何存儲(chǔ)數(shù)據(jù)的。

  前面說(shuō)過(guò),GIT存儲(chǔ)的不是文件各個(gè)版本的差異,而是文件的每一個(gè)版本存儲(chǔ)一個(gè)快照對(duì)象,然后通過(guò)SHA-1索引,不只是文件,包換每個(gè)提交都是一個(gè)對(duì)象并通過(guò)SHA-1索引。無(wú)論是文本文件,二進(jìn)制文件還是提交,都是GIT對(duì)象。

GIT對(duì)象

   每個(gè)對(duì)象(object) 包括三個(gè)部分:類型,大小內(nèi)容。大小就是指內(nèi)容的大小,內(nèi)容取決于對(duì)象的類型,有四種類型的對(duì)象:"blob"、"tree"、 "commit" 和"tag"。

  • “blob”用來(lái)存儲(chǔ)文件數(shù)據(jù),通常是一個(gè)文件。
  • “tree”有點(diǎn)像一個(gè)目錄,它管理一些“tree”或是 “blob”(就像文件和子目錄)
  • 一個(gè)“commit”指向一個(gè)"tree",它用來(lái)標(biāo)記項(xiàng)目某一個(gè)特定時(shí)間點(diǎn)的狀態(tài)。它包括一些關(guān)于時(shí)間點(diǎn)的元數(shù)據(jù),如提交時(shí)間、提交說(shuō)明、作者、提交者、指向上次提交(commits)的指針等等。
  • 一個(gè)“tag”是來(lái)標(biāo)記某一個(gè)提交(commit) 的方法。

  比如說(shuō)我們執(zhí)行了以下代碼進(jìn)行了一次提交:

$ git add README test.rb LICENSE2
$ git commit -m 'initial commit of my project'

  現(xiàn)在,Git 倉(cāng)庫(kù)中有五個(gè)對(duì)象:三個(gè)表示文件快照內(nèi)容的 blob 對(duì)象;一個(gè)記錄著目錄樹(shù)內(nèi)容及其中各個(gè)文件對(duì)應(yīng) blob 對(duì)象索引的 tree 對(duì)象;以及一個(gè)包含指向 tree 對(duì)象(根目錄)的索引和其他提交信息元數(shù)據(jù)的 commit 對(duì)象。概念上來(lái)說(shuō),倉(cāng)庫(kù)中的各個(gè)對(duì)象保存的數(shù)據(jù)和相互關(guān)系看起來(lái)如下圖:

  

  如果進(jìn)行多次提交,倉(cāng)庫(kù)的歷史會(huì)像這樣:

  

分支引用

  所謂的GIT分支,其實(shí)就是一個(gè)指向某一個(gè)Commit對(duì)象的指針,像下面這樣,有兩個(gè)分支,master與testing:

  

  而我們?cè)趺粗喇?dāng)前在哪一個(gè)分支呢?其實(shí)就是很簡(jiǎn)單地使用了一個(gè)名叫HEAD的指針,如上圖所示。HEAD指針的值可以為一個(gè)SHA-1值或是一個(gè)引用,看以下例子:

  

  git的所有版本信息都保存了Working Directory下的.git目錄,而HEAD指針就保存在.git目錄下,如上圖所有,目前為止已經(jīng)有3個(gè)提交,通過(guò)查看HEAD的值可以看到我們當(dāng)前在master分支:refs/heads/master,當(dāng)我們通過(guò)git checkout取出某一特定提交后,HEAD的值就是成了我們checkout的提交的SHA-1值。

  記錄我們當(dāng)前的位置很簡(jiǎn)單,就是能過(guò)HEAD指針,HEAD指向某一提交的SHA-1值或是某一分支的引用。

新建分支

git branch <branch-name>

  有時(shí)需要在新建分支后直接切換到新建的分支,可以直接用checkout的-b選項(xiàng)

git checkout -b <branch-name>

刪除分支

git branch -d <branch-name>

  如果在指定的分支有一些unmerged的提交,刪除分支會(huì)失敗,這里可以使用-D參數(shù)強(qiáng)制刪除分支。

git branch -D <branch-name>

檢出分支或提交

  檢出某一分支或某一提交是同一個(gè)命令

git checkout <branch-name> | <commit>

分支合并(merge)

  當(dāng)我們新建一個(gè)分支進(jìn)行開(kāi)發(fā),并提交了幾次更新后,感覺(jué)是時(shí)候?qū)⑦@個(gè)分支的內(nèi)容合回主線了,這是就可以取出主線分支,然后把分支的更新merge回來(lái):

git checkout master

git merge testing

  如果master分支是testing分支的直接上游,即從master延著testing分支的提交歷史往前走可以直接走到testing分支的最新提交,那么系統(tǒng)什么也不需要做,只需要改變master分支的指針即可,這被稱之為"Fast Forward"。

  但是,一般情況是這樣的,你取出了最新的master分支,比如說(shuō)master分支最新的提交是C2(假設(shè)共3次提交C0<-C1<-C2),在此基礎(chǔ)上你新建了分支,當(dāng)你在分支上提交了C3、C5后想將br1時(shí)merge回master時(shí),你發(fā)現(xiàn)已經(jīng)有其他人提交了C4,這時(shí)候就不能直接修改master的指針了,不然會(huì)丟失別人的提交,這個(gè)時(shí)候就需要將你新建分支時(shí)master所在的提交(C2)后的修改(C4),與你新建分支后在分支上的修改(C3、C5)做合并,將合并后的結(jié)果作為一個(gè)新的提交提交到master,GIT可以自動(dòng)推導(dǎo)出應(yīng)該基于哪個(gè)提交進(jìn)行合并(C2),如果沒(méi)有沖突,系統(tǒng)會(huì)自動(dòng)提交新的提交,如果有沖突,系統(tǒng)會(huì)提示你解決沖突,當(dāng)沖突解決后,你就可以將修改加入暫存區(qū)并提交。提交歷史類似下面這樣(圖來(lái)自Pro-Git):

  

  merge后的提交是按時(shí)間排序的,比如下圖,我們?cè)趓ename提交處新建分支test,在test上提交Commit from branch test,然后回到master提交commit in master after committing in branch,再將test分支merge進(jìn)master,這時(shí)看提交提交歷史,Commit from branch test是在commit in master...之前的,盡管在master上我們是在rename的基礎(chǔ)上提交的commit in master...而GIT會(huì)在最后添加一個(gè)新的提交(Merge branch 'test')表示我們?cè)诖颂帉⒁粋€(gè)分支merge進(jìn)來(lái)了。這種情況會(huì)有一個(gè)問(wèn)題,比如說(shuō)在rename提交處某人A從你這里Copy了一個(gè)GIT倉(cāng)庫(kù),然后你release了一個(gè)patch(通過(guò)git format-patch)給A,這時(shí)候test分支還沒(méi)有merge進(jìn)來(lái),所以patch中只包含提交:commit in master...然后你把test分支merge了進(jìn)來(lái)又給了A一個(gè)patch,這個(gè)patch會(huì)包含提交:Commit from branch test,而這個(gè)patch是以rename為base的,如果commit in master...和Commit from branch test修改了相同的文件,則第二次的patch可能會(huì)打不上去,因?yàn)橐詒ename為base的patch可能在新的Code上找不到在哪個(gè)位置應(yīng)用修改。

  

分支衍合(rebase)

  有兩種方法將一個(gè)分支的改動(dòng)合并進(jìn)另一個(gè)分支,一個(gè)就是前面所說(shuō)的分支合并,另一個(gè)就是分支衍合,這兩種方式有什么區(qū)別呢?

  分支合并(merge)是將兩個(gè)分支的改動(dòng)合并到一起,并生成一個(gè)新的提交,提交歷史是按時(shí)間排序的,即我們實(shí)際提交的順序,通過(guò)git log --graph或一些圖形化工具,可能很明顯地看到分支的合并歷史,如果分支比較多就很混亂,而且如果以功能點(diǎn)新建分支,等功能點(diǎn)完成后合回主線,由于merge后提交是按提交時(shí)間排序的,提交歷史就比較亂,各個(gè)功能點(diǎn)的提交混雜在一起,還可能遇到上面提到的patch問(wèn)題。

  而分支衍合(rebase)是找到兩個(gè)分支的共同祖先提交,將要被rebase進(jìn)來(lái)的分支的提交依次在要被rebase到的分支上重演一遍,即回到兩個(gè)分支的共同祖先,將branch(假如叫experiment)的每次提交的差異保存到臨時(shí)文件里,然后切換到要衍合入的分支(假如是master),依次應(yīng)用補(bǔ)丁文件。experiment上有幾次提交,在master就生成幾次新的提交,而且是連在一起的,這樣合進(jìn)主線后每個(gè)功能點(diǎn)的提交就都在一起,而且提交歷史是線性的

   對(duì)比merge與rebase的提交歷史會(huì)是下圖這樣的(圖來(lái)自Pro-GIt):

  

(merge)

  

(rebase)

 

  rebase后C3提交就不存在了,取而代之的是C3',而master也成為了experiment的直接上游,只需一次Fast Forward(git merge)后master就指向了最新的提交,就可以刪除experiment分支了。

衍合--onto

git rebase --onto master server client

  這條命令的意思是:檢出server分支與client分支共同祖先之后client上的變化,然后在master上重演一遍。

父提交

  HEAD表示當(dāng)前所在的提交,如果要查看當(dāng)前提交父提交呢?git log查看提交歷史,顯然太麻煩了,而且輸入一長(zhǎng)串的Commit-ID也不是一個(gè)令人愉悅的事。這時(shí)可借助兩個(gè)特殊的符號(hào):~與^。

  ^ 表示指定提交的父提交,這個(gè)提交可能由多個(gè)交提交,^之后跟上數(shù)字表示第幾個(gè)父提交,不跟數(shù)字等同于^1。

  ~n相當(dāng)于n個(gè)^,比如~3=^^^,表示第一個(gè)父提交的第一個(gè)父提交的第一個(gè)父提交。

遠(yuǎn)程分支

  遠(yuǎn)程分支以(遠(yuǎn)程倉(cāng)庫(kù)名)/(分支名)命令,遠(yuǎn)程分支在本地?zé)o法移動(dòng)修改,當(dāng)我們clone一個(gè)遠(yuǎn)程倉(cāng)庫(kù)時(shí)會(huì)自動(dòng)在本地生成一個(gè)名叫original的遠(yuǎn)程倉(cāng)庫(kù),下載遠(yuǎn)程倉(cāng)庫(kù)的所有數(shù)據(jù),并新建一個(gè)指向它的分支original/master,但這個(gè)分支我們是無(wú)法修改的,所以需要在本地重新一個(gè)分支,比如叫master,并跟蹤遠(yuǎn)程分支。

  Clone了遠(yuǎn)程倉(cāng)庫(kù)后,我們還會(huì)在本地新建其他分支,并且可能也想跟蹤遠(yuǎn)程分支,這時(shí)可以用以下命令:

git checkout -b [branch_name] --track|-t <remote>/<remote-banch>

  和新建分支的方法一樣,只是加了一個(gè)參數(shù)--track或其縮寫(xiě)形式-t,可以指定本地分支的名字,如果不指定就會(huì)被命名為remote-branch。

  要拉取某個(gè)遠(yuǎn)程倉(cāng)庫(kù)的數(shù)據(jù),可以用git fetch:

git fetch <remote>

  當(dāng)拉取到了遠(yuǎn)程倉(cāng)庫(kù)的數(shù)據(jù)后只是把數(shù)據(jù)保存到了一個(gè)遠(yuǎn)程分支中,如original/master,而這個(gè)分支的數(shù)據(jù)是無(wú)法修改的,此時(shí)我們可以把這個(gè)遠(yuǎn)程分支的數(shù)據(jù)合并到我們當(dāng)前分支

git merge <remote>/<remote-branch>

  如果當(dāng)前分支已經(jīng)跟蹤了遠(yuǎn)程分支,那么上述兩個(gè)部分就可以合并為一個(gè)

git pull

  當(dāng)在本地修改提交后,我們可能需要把這些本地的提交推送到遠(yuǎn)程倉(cāng)庫(kù),這里就可以用git push命令,由于本地可以由多個(gè)遠(yuǎn)程倉(cāng)庫(kù),所以需要指定遠(yuǎn)程倉(cāng)庫(kù)的名字,并同時(shí)指定需要推的本地分支及需要推送到遠(yuǎn)程倉(cāng)庫(kù)的哪一個(gè)分支

git push <remote> <local-branch>:<remote-branch>

  如果本地分支與遠(yuǎn)程分支同名,命令可以更簡(jiǎn)單

git push <remote> <branch-name> 等價(jià)于 git push <remote> refs/heads/<branch-name>:refs/for/<branch-name>

  如果本地分支的名字為空,可以刪除遠(yuǎn)程分支。

  前面說(shuō)過(guò)可以有不止一個(gè)遠(yuǎn)程分支f,添加遠(yuǎn)程分支的方法為

git remote add <short-name> <url>

六、標(biāo)簽-tag

  作為一個(gè)版本控制工具,針對(duì)某一時(shí)間點(diǎn)的某一版本打tag的功能是必不可少的,要查看tag也非常簡(jiǎn)單,查看tag使用如下命令

git tag

  參數(shù)"-l"可以對(duì)tag進(jìn)行過(guò)濾

git tag -l "v1.1.*"

  Git 使用的標(biāo)簽有兩種類型:輕量級(jí)的(lightweight)和含附注的(annotated)。輕量級(jí)標(biāo)簽就像是個(gè)不會(huì)變化的分支,實(shí)際上它就是個(gè)指向特定提交對(duì)象的引用。而含附注標(biāo)簽,實(shí)際上是存儲(chǔ)在倉(cāng)庫(kù)中的一個(gè)獨(dú)立對(duì)象,它有自身的校驗(yàn)和信息,包含著標(biāo)簽的名字,電子郵件地址和日期,以及標(biāo)簽說(shuō)明,標(biāo)簽本身也允許使用 GNU Privacy Guard (GPG) 來(lái)簽署或驗(yàn)證。

  輕量級(jí)標(biāo)簽只需在git tag后加上tag的名字,如果tag名字

git tag <tag_name>

  含附注的標(biāo)簽需要加上參數(shù)-a(annotated),同時(shí)加上-m跟上標(biāo)簽的說(shuō)明

git tag -a <tag_name> -m "<tag_description>"

  如果你有自己的私鑰,還可以用 GPG 來(lái)簽署標(biāo)簽,只需要把之前的 -a 改為 -s(signed)

  查看標(biāo)簽的內(nèi)容用

 git show <tag_name>

  驗(yàn)證已簽署的標(biāo)簽用-v(verify)

git tag -v <tag_name>

  有時(shí)在某一個(gè)版本忘記打tag了,可以在后期再補(bǔ)上,只需在打tag時(shí)加上commit-id

  要將tag推送到遠(yuǎn)程服務(wù)器上,可以用

git push <remote> <tag_name>

  或者可以用下面的命令推送所有的tag

git push <remote> --tags

七、Git配置

  使用"git config"可以配置Git的環(huán)境變量,這些變量可以存放在以下三個(gè)不同的地方:

  • /etc/gitconfig 文件:系統(tǒng)中對(duì)所有用戶都普遍適用的配置。若使用 git config 時(shí)用 --system選項(xiàng),讀寫(xiě)的就是這個(gè)文件。
  • ~/.gitconfig 文件:用戶目錄下的配置文件只適用于該用戶。若使用 git config 時(shí)用 --global選項(xiàng),讀寫(xiě)的就是這個(gè)文件。
  • 當(dāng)前項(xiàng)目的 git 目錄中的配置文件(也就是工作目錄中的 .git/config 文件):這里的配置僅僅針對(duì)當(dāng)前項(xiàng)目有效。每一個(gè)級(jí)別的配置都會(huì)覆蓋上層的相同配置,所以 .git/config 里的配置會(huì)覆蓋/etc/gitconfig 中的同名變量。

  在 Windows 系統(tǒng)上,Git 會(huì)找尋用戶主目錄下的 .gitconfig 文件。主目錄即 $HOME 變量指定的目錄,一般都是 C:\Documents and Settings\$USER。此外,Git 還會(huì)嘗試找尋 /etc/gitconfig 文件,只不過(guò)看當(dāng)初 Git 裝在什么目錄,就以此作為根目錄來(lái)定位。

  最基礎(chǔ)的配置是配置git的用戶,用來(lái)標(biāo)識(shí)作者的身份

git config --global user.name <name>

git config --global user.email <email>

  文本編輯器也可以配置,比如在git commit的時(shí)候就會(huì)調(diào)用我們?cè)O(shè)置的文本編輯器

git config --global core.editor vim

  另一個(gè)常用的是diff工具,比如我們想用可視化的對(duì)比工具

git config --global merge.tool meld

  要查看所有的配置,可以用

git config --list

  或者可以在git config后加上配置項(xiàng)的名字查看具體項(xiàng)的配置

git config user.name

  作為一個(gè)懶人,雖然checkout、status等命令只是一個(gè)單詞,但是還是嫌太長(zhǎng)了,我們還可以給命令設(shè)置別名如

git config --global alias.co checkout

  這樣git co就等于git checkout

  前面說(shuō)地,git配置項(xiàng)都保存在那3個(gè)文件里,可以直接打開(kāi)相應(yīng)的配置文件查看配置,也可以直接修改這些配置文件來(lái)配置git,想刪除某一個(gè)配置,直接刪除相應(yīng)的行就行了

  

八、其他

  關(guān)于GIT各命令的說(shuō)明可以查看相關(guān)幫助文檔,通過(guò)以下方法:

git help <command>或git <command> --help

 REPO

repo start <topic_name>

  開(kāi)啟一個(gè)新的主題,其實(shí)就是每個(gè)Project都新建一個(gè)分支。

repo init -u <url> [OPTIONS]

  在當(dāng)前目錄下初始化repo,會(huì)在當(dāng)前目錄生生成一個(gè).repo目錄,像Git Project下的.git一樣,-u指定url,可以加參數(shù)-m指定manifest文件,默認(rèn)是default.xml,.repo/manifests保存manifest文件。.repo/projects下有所有的project的數(shù)據(jù)信息,repo是一系列g(shù)it project的集合,每個(gè)git project下的.git目錄中的refs等目錄都是鏈接到.repo/manifests下的。

repo manifest

  可以根據(jù)當(dāng)前各Project的版本信息生成一個(gè)manifest文件

repo sync [PROJECT1...PROJECTN]

  同步Code。

repo status

  查看本地所有Project的修改,在每個(gè)修改的文件前有兩個(gè)字符,第一個(gè)字符表示暫存區(qū)的狀態(tài)。

-no changesame in HEAD and index
Aaddednot in HEAD, in index
Mmodifiedin HEAD, modified in index
Ddeletedin HEAD, not in index
Rrenamednot in HEAD, path changed in index
Ccopiednot in HEAD, copied from another in index
Tmode changedsame content in HEAD and index, mode changed
Uunmergedconflict between HEAD and index; resolution required

  每二個(gè)字符表示工作區(qū)的狀態(tài) 

lettermeaningdescription
-new/unknownnot in index, in work tree
mmodifiedin index, in work tree, modified
ddeletedin index, not in work tree

 

repo prune <topic> 

  刪除已經(jīng)merge的分支

repo abandon <topic>

  刪除分支,無(wú)論是否merged

repo branch或repo branches

  查看所有分支

repo diff

  查看修改

repo upload

  上傳本地提交至服務(wù)器

repo forall [PROJECT_LIST]-c COMMAND

  對(duì)指定的Project列表或所有Project執(zhí)行命令COMMAND,加上-p參數(shù)可打印出Project的路徑。

repo forall -c 'git reset --hard HEAD;git clean -df;git rebase --abort'

  這個(gè)命令可以撤銷(xiāo)整個(gè)工程的本地修改。

 

  說(shuō)明:文中關(guān)于Git的知識(shí)大多來(lái)自Pro-GIt,這本書(shū)感覺(jué)不錯(cuò),想學(xué)習(xí)的可以找來(lái)看:Pro-Git

Git與Repo入門(mén)

版本控制

  版本控制是什么已不用在說(shuō)了,就是記錄我們對(duì)文件、目錄或工程等的修改歷史,方便查看更改歷史,備份以便恢復(fù)以前的版本,多人協(xié)作。。。

一、原始版本控制

  最原始的版本控制是純手工的版本控制:修改文件,保存文件副本。有時(shí)候偷懶省事,保存副本時(shí)命名比較隨意,時(shí)間長(zhǎng)了就不知道哪個(gè)是新的,哪個(gè)是老的了,即使知道新舊,可能也不知道每個(gè)版本是什么內(nèi)容,相對(duì)上一版作了什么修改了,當(dāng)幾個(gè)版本過(guò)去后,很可能就是下面的樣子了:

  

二、本地版本控制

  手工管理比較麻煩且混亂,所以出現(xiàn)了本地版本控制系統(tǒng),記錄文件每次的更新,可以對(duì)每個(gè)版本做一個(gè)快照,或是記錄補(bǔ)丁文件。比如RCS。

  

三、集中版本控制

  但是本地版本控制系統(tǒng)偏向于個(gè)人使用,或者多個(gè)使用的人必須要使用相同的設(shè)備,如果需要多人協(xié)作就不好辦了,于是,集中化的版本控制系統(tǒng)( Centralized Version Control Systems,簡(jiǎn)稱 CVCS )應(yīng)運(yùn)而生,比如Subversion,Perforce。

  在CVCS中,所有的版本數(shù)據(jù)都保存在服務(wù)器上,一起工作的人從服務(wù)器上同步更新或上傳自己的修改。

   

  但是,所有的版本數(shù)據(jù)都存在服務(wù)器上,用戶的本地設(shè)備就只有自己以前所同步的版本,如果不連網(wǎng)的話,用戶就看不到歷史版本,也無(wú)法切換版本驗(yàn)證問(wèn)題,或在不同分支工作。。

  而且,所有數(shù)據(jù)都保存在單一的服務(wù)器上,有很大的風(fēng)險(xiǎn)這個(gè)服務(wù)器會(huì)損壞,這樣就會(huì)丟失所有的數(shù)據(jù),當(dāng)然可以定期備份。

四、分布式版本控制

  針對(duì)CVCS的以上缺點(diǎn),出現(xiàn)了分布式版本控制系統(tǒng)( Distributed Version Control System,簡(jiǎn)稱 DVCS ),如GIT,Mercurial。

  DVCS不是復(fù)制指定版本的快照,而是把所有的版本信息倉(cāng)庫(kù)全部同步到本地,這樣就可以在本地查看所有版本歷史,可以離線在本地提交,只需在連網(wǎng)時(shí)push到相應(yīng)的服務(wù)器或其他用戶那里。由于每個(gè)用戶那里保存的都是所有的版本數(shù)據(jù),所以,只要有一個(gè)用戶的設(shè)備沒(méi)有問(wèn)題就可以恢復(fù)所有的數(shù)據(jù)。

  當(dāng)然,這增加了本地存儲(chǔ)空間的占用。

  

GIT

  必須要了解GIT的原理,才能知道每個(gè)操作的意義是什么,才能更容易地理解在什么情況下用什么操作,而不是死記命令。當(dāng)然,第一步是要獲得一個(gè)GIT倉(cāng)庫(kù)。

一、獲得GIT倉(cāng)庫(kù)

  有兩種獲得GIT倉(cāng)庫(kù)的方法,一是在需要用GIT管理的項(xiàng)目的根目錄執(zhí)行:

git init

  執(zhí)行后可以看到,僅僅在項(xiàng)目目錄多出了一個(gè).git目錄,關(guān)于版本等的所有信息都在這個(gè)目錄里面。

  另一種方式是克隆遠(yuǎn)程目錄,由于是將遠(yuǎn)程服務(wù)器上的倉(cāng)庫(kù)完全鏡像一份至本地,而不是取某一個(gè)特定版本,所以用clone而不是checkout:

git clone <url>

二、GIT中版本的保存

  記錄版本信息的方式主要有兩種:

  1. 記錄文件每個(gè)版本的快照
  2. 記錄文件每個(gè)版本之間的差異

  GIT采用第一種方式。像Subversion和Perforce等版本控制系統(tǒng)都是記錄文件每個(gè)版本之間的差異,這就需要對(duì)比文件兩版本之間的具體差異,但是GIT不關(guān)心文件兩個(gè)版本之間的具體差別,而是關(guān)心文件的整體是否有改變,若文件被改變,在添加提交時(shí)就生成文件新版本的快照,而判斷文件整體是否改變的方法就是用SHA-1算法計(jì)算文件的校驗(yàn)和。

  GIT能正常工作完全信賴于這種SHA-1校驗(yàn)和,當(dāng)一個(gè)文件的某一個(gè)版本被記錄之后會(huì)生成這個(gè)版本的一個(gè)快照,但是一樣要能引用到這個(gè)快照,GIT中對(duì)快照的引用,對(duì)每個(gè)版本的記錄標(biāo)識(shí)全是通過(guò)SHA-1校驗(yàn)和來(lái)實(shí)現(xiàn)的。

  當(dāng)一個(gè)文件被改變時(shí),它的校驗(yàn)和一定會(huì)被改變(理論上存在兩個(gè)文件校驗(yàn)和相同,但機(jī)率小到可以忽略不計(jì)),GIT就以此判斷文件是否被修改,及以些記錄不同版本。

  在工作目錄的文件可以處于不同的狀態(tài),比如說(shuō)新添加了一個(gè)文件,GIT發(fā)覺(jué)了這個(gè)文件,但這個(gè)文件是否要納入GIT的版本控制還是要由我們自己決定,比如編譯生成的中間文件,我們肯定不想納入版本控制。下面就來(lái)看下文件狀態(tài)。

三、GIT文件操作

  版本控制就是對(duì)文件的版本控制,對(duì)于Linux來(lái)說(shuō),設(shè)備,目錄等全是文件,要對(duì)文件進(jìn)行修改、提交等操作,首先要知道文件當(dāng)前在什么狀態(tài),不然可能會(huì)提交了現(xiàn)在還不想提交的文件,或者要提交的文件沒(méi)提交上。

文件狀態(tài)

  GIT倉(cāng)庫(kù)所在的目錄稱為工作目錄,這個(gè)很好理解,我們的工程就在這里,工作時(shí)也是在這里做修改。

  在工作目錄中的文件被分為兩種狀態(tài),一種是已跟蹤狀態(tài)(tracked),另一種是未跟蹤狀態(tài)(untracked)。只有處于已跟蹤狀態(tài)的文件才被納入GIT的版本控制。如下圖:

  

  當(dāng)我們往工作目錄添加一個(gè)文件的時(shí)候,這個(gè)文件默認(rèn)是未跟蹤狀態(tài)的,我們肯定不希望編譯生成的一大堆臨時(shí)文件默認(rèn)被跟蹤還要我們每次手動(dòng)將這些文件清除出去。用以下命令可以跟蹤文件:

git add <file>

  上圖中右邊3個(gè)狀態(tài)都是已跟蹤狀態(tài),其中的灰色箭頭只表示untracked<-->tracked的轉(zhuǎn)換而不是untracked<-->unmodified的轉(zhuǎn)換,新添加的文件肯定算是被修改過(guò)的。那么,staged狀態(tài)又是什么呢?這就要搞清楚GIT的三個(gè)工作區(qū)域:本地?cái)?shù)據(jù)(倉(cāng)庫(kù))目錄,工作目錄,暫存區(qū),如下圖所示:

  

  git directory就是我們的本地倉(cāng)庫(kù).git目錄,里面保存了所有的版本信息等內(nèi)容。

  working driectory,工作目錄,就是我們的工作目錄,其中包括未跟蹤文件及已跟蹤文件,而已跟蹤文件都是從git directory取出來(lái)的文件的某一個(gè)版本或新跟蹤的文件。

  staging area,暫存區(qū),不對(duì)應(yīng)一個(gè)具體目錄,其時(shí)只是git directory中的一個(gè)特殊文件。

  當(dāng)我們修改了一些文件后,要將其放入暫存區(qū)然后才能提交,每次提交時(shí)其實(shí)都是提交暫存區(qū)的文件到git倉(cāng)庫(kù),然后清除暫存區(qū)。而checkout某一版本時(shí),這一版本的文件就從git倉(cāng)庫(kù)取出來(lái)放到了我們的工作目錄。

文件狀態(tài)的查看

  那么,我們?cè)趺粗喇?dāng)前工作目錄的狀態(tài)呢?哪些文件已被暫存?有哪些未跟蹤的文件?哪些文件被修改了?所有這些只需要一個(gè)命令,git status,如下圖所示:

  

  GIT在這一點(diǎn)做得很好,在輸出每個(gè)文件狀態(tài)的同時(shí)還說(shuō)明了怎么操作,像上圖就有怎么暫存、怎么跟蹤文件、怎么取消暫存的說(shuō)明。

文件暫存

  在上圖中我們可以很清楚地看到,filea未跟蹤,fileb已被暫存(changes to be committed),但是怎么還有一個(gè)fileb是modified但unstaged呢?這是因?yàn)楫?dāng)我們暫存一從此文件時(shí),暫存的是那一文件當(dāng)時(shí)的版本,當(dāng)暫存后再次修改了這個(gè)文件后就會(huì)提示這個(gè)文件暫存后的修改是未被暫存的。

  接下來(lái)我們就看怎么暫存文件,其實(shí)也很簡(jiǎn)單,從上圖中可以看到GIT已經(jīng)提示我們了:use "git add <file>..." to update what will be committed,通過(guò) 

git add <file>...

  就可以暫存文件,跟蹤文件同樣是這一個(gè)命令。在這個(gè)命令中可以使用glob模式匹配,比如"file[ab]",也可以使用"git add ."添加當(dāng)前目錄下的所有文件。

  取消暫存文件是 

git reset HEAD <file>...

   若修改了一個(gè)文件想還原修改可用

git checkout -- <file>... 

查看文件修改后的差異

  當(dāng)我們修改過(guò)一些文件之后,我們可能想查看我們都修改了什么東西,用"git status"只能查看對(duì)哪些文件做了改動(dòng),如果要看改動(dòng)了什么,可以用:

git diff

  比如下圖:

  

  ---a表示修改之前的文件,+++b表示修改后的文件,上圖表示在fileb的第一行后添加了一行"bb",原來(lái)文件的第一行擴(kuò)展為了修改后的1、2行。

  但是,前面我們明明用"git status"看到filesb做了一些修改后暫存了,然后又修改了fileb,理應(yīng)有兩次修改的,怎么只有一個(gè)?

  因?yàn)?git diff"顯示的是文件修改后還沒(méi)有暫存起來(lái)的內(nèi)容,那如果要比較暫存區(qū)的文件與之前已經(jīng)提交過(guò)的文件呢,畢竟實(shí)際提交的是暫存區(qū)的內(nèi)容,可以用以下命令:

  

  /dev/null表示之前沒(méi)有提交過(guò)這一個(gè)文件,這是將是第一次提交,用:

git diff --staged

  是等效的,但GIT的版本要大于1.6.1。

  再次執(zhí)行"git add"將覆蓋暫存區(qū)的內(nèi)容。

忽略一些文件

  如果有一些部件我們不想納入版本控制,也不想在每次"git status"時(shí)看到這些文件的提示,或者很多時(shí)候我們?yōu)榱朔奖銜?huì)使用"git add ."添加所有修改的文件,這時(shí)就會(huì)添加上一些我們不想添加的文件,怎么忽略這些文件呢?

  GIT當(dāng)然提供了方法,只需在主目錄下建立".gitignore"文件,此文件有如下規(guī)則:

  • 所有以#開(kāi)頭的行會(huì)被忽略
  • 可以使用glob模式匹配
  • 匹配模式后跟反斜杠(/)表示要忽略的是目錄
  • 如果不要忽略某模式的文件在模式前加"!"

  比如:

# 此為注釋 – 將被 Git 忽略
*.a # 忽略所有 .a 結(jié)尾的文件
!lib.a # 但 lib.a 除外
/TODO # 僅僅忽略項(xiàng)目根目錄下的 TODO 文件,不包括 subdir/TODO
build/ # 忽略 build/ 目錄下的所有文件
doc/*.txt # 會(huì)忽略 doc/notes.txt 但不包括 doc/server/arch.txt

移除文件

  當(dāng)我們要?jiǎng)h除一個(gè)文件時(shí),我們可能就直接用GUI刪除或者直接rm [file]了,但是看圖:

  

  我們需要將文件添加到暫存區(qū)才能提交,而移除文件后是無(wú)法添加到暫存區(qū)的,那么怎么移除一個(gè)文件讓GIT不再將其納入版本控制呢?上圖中GIT已經(jīng)給出了說(shuō)明:

git rm <file>...

  執(zhí)行以上命令后提交就可以了,有時(shí)我們只是想將一些文件從版本控制中剔除出去,但仍保留這些文件在工作目錄中,比如我們一不小心將編譯生成的中間文件納入了版本控制,想將其從版本控制中剔除出去但在工作目錄中保留這些文件(不然再次編譯可要花費(fèi)更多時(shí)間了),這時(shí)只需要添加"--cached"參數(shù)。

  如果我們之前不是通過(guò)"git rm"刪除了很多文件呢?比如說(shuō)通過(guò)patch或者通過(guò)GUI,如果這些文件命名沒(méi)有規(guī)則,一個(gè)一個(gè)地執(zhí)行"git rm"會(huì)搞死人的,這時(shí)可以用以下命令:

  

移動(dòng)文件

  和移除文件一樣,移動(dòng)文件不可以通過(guò)GUI直接重命令或用"mv"命令,而是要用"git mv",不然同移除文件一樣你會(huì)得到如下結(jié)果:

  

  如果要重命名文件可以使用

git mv old_name new_name

  這個(gè)命令等效于

mv old_name new_name

git rm old_name

git add new_name

交互式暫存

  使用git add -i可以開(kāi)啟交互式暫存,如圖所示,系統(tǒng)會(huì)列出一個(gè)功能菜單讓選擇將要執(zhí)行的操作。

  

移除所有未跟蹤文件

git clean [options]  一般會(huì)加上參數(shù)-df,-d表示包含目錄,-f表示強(qiáng)制清除。

儲(chǔ)藏-Stashing

  可能會(huì)遇到這樣的情況,你正在一個(gè)分支上進(jìn)行一個(gè)特性的開(kāi)發(fā),或者一個(gè)Bug的修正,但是這時(shí)突然有其他的事情急需處理,這時(shí)該怎么辦?不可能就在這個(gè)工作進(jìn)行到一半的分支上一起處理,先把修改的Copy出去?太麻煩了。這種情況下就要用到Stashing了。假如我們現(xiàn)在的工作目錄是這樣子的

$ git status# On branch master# Changes to be committed:#(use "git reset HEAD <file>..." to unstage)##modified:index.html## Changed but not updated:#(use "git add <file>..." to update what will be committed)##modified:lib/simplegit.rb

  此時(shí)如果想切換分支就可以執(zhí)行以下命令

$ git stashSaved working directory and index state "WIP on master: 049d078 added the index file"HEAD is now at 049d078 added the index file(To restore them type "git stash apply")

  這時(shí)你會(huì)發(fā)現(xiàn)你的工作目錄變得很干凈了,就可以隨意切分支進(jìn)行其他事情的處理了。

  我們可能不只一次進(jìn)行"git stash",通過(guò)以下命令可以查看所有stash列表

$ git stash liststash@{0}: WIP on master: 049d078 added the index filestash@{1}: WIP on master: c264051... Revert "added file_size"

  當(dāng)緊急事情處理完了,需要重新回來(lái)這里進(jìn)行原來(lái)的工作時(shí),只需把Stash區(qū)域的內(nèi)容取出來(lái)應(yīng)用到當(dāng)前工作目錄就行,命令就是

git stash apply

  如果不基參數(shù)就應(yīng)用最新的stash,或者可以指定stash的名字,如:stash@{1},可能通過(guò)

git stash show

  顯示stash的內(nèi)容具體是什么,同git stash apply一樣,可以選擇指定stash的名字。

  git stash apply之后再git stash list會(huì)發(fā)現(xiàn),apply后的stash還在stash列表中,如果要將其從stash列表中刪除可以用

git stash drop

  丟棄這個(gè)stash,stash的命令參數(shù)都可選擇指定stash名字,否則就是最新的stash。

  一般情況下apply stash后應(yīng)該就可以把它從stash列表刪除了,先apply再drop還是比較繁瑣的,使用以下一條命令就可以同時(shí)完成這兩個(gè)操作

git stash pop

  如果我們執(zhí)行g(shù)it stash時(shí)工作目錄的狀態(tài)是部分文件已經(jīng)加入了暫存區(qū),部分文件沒(méi)有,當(dāng)我們執(zhí)行g(shù)it stash apply之后會(huì)發(fā)現(xiàn)所有文件都變成了未暫存的,如果想維持原來(lái)的樣子操持原來(lái)暫存的文件仍然是暫存狀態(tài),可以加上--index參數(shù)

git stash apply --index

  還有這么一種情況,我們把原來(lái)的修改stash了,然后修復(fù)了其他一些東西并進(jìn)行了提交,但是,這些提交的文件有些在之前已經(jīng)被stash了,那么git stash apply時(shí)就很可能會(huì)遇到?jīng)_突,這種情況下就可以在stash時(shí)所以提交的基礎(chǔ)上新建一個(gè)分支,然后再apply stash,當(dāng)然,這兩個(gè)步驟有一人簡(jiǎn)單的完成方法

git stash branch <branch name>

四、提交與歷史

  了解了文件的狀態(tài),我們對(duì)文件進(jìn)行了必要的修改后,就要把我們所做的修改放入版本庫(kù)了,這樣以后我們就可以在需要的時(shí)候恢復(fù)到現(xiàn)在的版本,而要恢復(fù)到某一版,一般需要查看版本的歷史。

提交

  提交很簡(jiǎn)單,直接執(zhí)行"git commit"。執(zhí)行g(shù)it commit后會(huì)調(diào)用默認(rèn)的或我們?cè)O(shè)置的編譯器要我們填寫(xiě)提示說(shuō)明,但是提交說(shuō)明最好按GIT要求填寫(xiě):第一行填簡(jiǎn)單說(shuō)明,隔一行填寫(xiě)詳細(xì)說(shuō)明。因?yàn)榈谝恍性谝恍┣闆r下會(huì)被提取使用,比如查看簡(jiǎn)短提交歷史或向別人提交補(bǔ)丁,所以字符數(shù)不應(yīng)太多,40為好。下面看一下查看提交歷史。

查看提交歷史

  查看提交歷史使用如下圖的命令

  

  如圖所示,顯示了作者,作者郵箱,提交說(shuō)明與提交時(shí)間,"git log"可以使用放多參數(shù),比如:

  

  僅顯示最新的1個(gè)log,用"-n"表示。

  

  顯示簡(jiǎn)單的SHA-1值與簡(jiǎn)單提交說(shuō)明,oneline僅顯示提交說(shuō)明的第一行,所以第一行說(shuō)明最好簡(jiǎn)單點(diǎn)方便在一行顯示。

  "git log --graph"以圖形化的方式顯示提交歷史的關(guān)系,這就可以方便地查看提交歷史的分支信息,當(dāng)然是控制臺(tái)用字符畫(huà)出來(lái)的圖形。

  "git log"的更多參數(shù)可以查看命令幫助。

不經(jīng)過(guò)暫存的提交

 如果我們想跳過(guò)暫存區(qū)直接提交修改的文件,可以使用"-a"參數(shù),但要慎重,別一不小心提交了不想提交的文件

git commit -a

  如果需要快捷地填寫(xiě)提交說(shuō)明可使用"-m"參數(shù)

git commit -m 'commit message'

修訂提交

  如果我們提交過(guò)后發(fā)現(xiàn)有個(gè)文件改錯(cuò)了,或者只是想修改提交說(shuō)明,這時(shí)可以對(duì)相應(yīng)文件做出修改,將修改過(guò)的文件通過(guò)"git add"添加到暫存區(qū),然后執(zhí)行以下命令:

git commit --amend

  然后修改提交說(shuō)明覆蓋上次提交,但只能重寫(xiě)最后一次提交。

重排提交

  通過(guò)衍合(rebase)可以修改多個(gè)提交的說(shuō)明,并可以重排提交歷史,拆分、合并提交,關(guān)于rebase在講到分支時(shí)再說(shuō),這里先看一下重排提交。

  假設(shè)我們的提交歷史是這樣的:

  

  如果我們想重排最后兩個(gè)提交的提交歷史,可以借助交互式rebase命令: 

 git rebase -i HEAD~2 或 git rebase -i 3366e1123010e7d67620ff86040a061ae76de0c8

  HEAD~2表示倒數(shù)第三個(gè)提交,這條命令要指定要重排的最舊的提交的父提交,此處要重排Second commit與Third commit,所以要指定Initial commit的Commit ID。如圖所示:

  

  注釋部分詳細(xì)說(shuō)明了每個(gè)選項(xiàng)的作用,如果我們想交互這兩個(gè)提交,只需把開(kāi)頭的這兩行交換下位置就OK了,交換位置后保存,然后看下提交歷史:

  

  可以看到提交歷史已經(jīng)變了,而且最新的兩個(gè)提交的Commit ID變了,如果這些提交已經(jīng)push到了遠(yuǎn)程服務(wù)器,就不要用這個(gè)命令了。

刪除提交與修改提交說(shuō)明

  如果要?jiǎng)h除某個(gè)提交,只需要?jiǎng)h除相應(yīng)的行就可以了,而要修改某個(gè)提交的提交說(shuō)明的話,只需要把相應(yīng)行的pick改為reward。

合并提交-squashing

  合并提交也很簡(jiǎn)單,從注釋中的說(shuō)明看,只需要把相應(yīng)的行的pick改為squash就可以把這個(gè)提交全并到它上一行的提交中。

拆分提交

  至于拆分提交,由于Git不可能知道你要做哪里把某一提交拆分開(kāi),把以我們就需要讓Git在需要拆分的提交處停下來(lái),由我們手動(dòng)修改提交,這時(shí)要把pick改為edit,這樣Git在處理到這個(gè)提交時(shí)會(huì)停下來(lái),此時(shí)我們就可以進(jìn)行相應(yīng)的修改并多次提交來(lái)拆分提交。

撤銷(xiāo)提交

  前面說(shuō)了刪除提交的方法,但是如果是多人合作的話,如果某個(gè)提交已經(jīng)Push到遠(yuǎn)程倉(cāng)庫(kù),是不可以用那種方法刪除提交的,這時(shí)就要撤銷(xiāo)提交

git revert <commit-id>

  這條命令會(huì)把指定的提交的所有修改回滾,并同時(shí)生成一個(gè)新的提交。

Reset

  git reset會(huì)修改HEAD到指定的狀態(tài),用法為

git reset [options] <commit>

  這條命令會(huì)使HEAD提向指定的Commit,一般會(huì)用到3個(gè)參數(shù),這3個(gè)參數(shù)會(huì)影響到工作區(qū)與暫存區(qū)中的修改:

  • --soft: 只改變HEAD的State,不更改工作區(qū)與暫存區(qū)的內(nèi)容
  • --mixed(默認(rèn)): 撤銷(xiāo)暫存區(qū)的修改,暫存區(qū)的修改會(huì)轉(zhuǎn)移到工作區(qū)
  • --hard: 撤銷(xiāo)工作區(qū)與暫存區(qū)的修改

cherry-pick

  當(dāng)與別人和作開(kāi)發(fā)時(shí),會(huì)向別人貢獻(xiàn)代碼或者接收別人貢獻(xiàn)的代碼,有時(shí)候可能不想完全Merge別人貢獻(xiàn)的代碼,只想要其中的某一個(gè)提交,這時(shí)就可以使用cherry-pick了。就一個(gè)命令

 git cherry-pick <commit-id>

filter-branch

  這條命令可以修改整個(gè)歷史,如從所有歷史中刪除某個(gè)文件相關(guān)的信息,全局性地更換電子郵件地址。

五、GIT分支

   分支被稱之為GIT最強(qiáng)大的特性,因?yàn)樗浅5剌p量級(jí),如果用Perforce等工具應(yīng)該知道,創(chuàng)建分支就是克隆原目錄的一個(gè)完整副本,對(duì)于大型工程來(lái)說(shuō),太費(fèi)時(shí)費(fèi)力了,而對(duì)于GIT來(lái)說(shuō),可以在瞬間生成一個(gè)新的分支,無(wú)論工程的規(guī)模有多大,因?yàn)镚IT的分支其實(shí)就是一指針而已。在了解GIT分支之前,應(yīng)該先了解GIT是如何存儲(chǔ)數(shù)據(jù)的。

  前面說(shuō)過(guò),GIT存儲(chǔ)的不是文件各個(gè)版本的差異,而是文件的每一個(gè)版本存儲(chǔ)一個(gè)快照對(duì)象,然后通過(guò)SHA-1索引,不只是文件,包換每個(gè)提交都是一個(gè)對(duì)象并通過(guò)SHA-1索引。無(wú)論是文本文件,二進(jìn)制文件還是提交,都是GIT對(duì)象。

GIT對(duì)象

   每個(gè)對(duì)象(object) 包括三個(gè)部分:類型,大小內(nèi)容。大小就是指內(nèi)容的大小,內(nèi)容取決于對(duì)象的類型,有四種類型的對(duì)象:"blob"、"tree"、 "commit" 和"tag"。

  • “blob”用來(lái)存儲(chǔ)文件數(shù)據(jù),通常是一個(gè)文件。
  • “tree”有點(diǎn)像一個(gè)目錄,它管理一些“tree”或是 “blob”(就像文件和子目錄)
  • 一個(gè)“commit”指向一個(gè)"tree",它用來(lái)標(biāo)記項(xiàng)目某一個(gè)特定時(shí)間點(diǎn)的狀態(tài)。它包括一些關(guān)于時(shí)間點(diǎn)的元數(shù)據(jù),如提交時(shí)間、提交說(shuō)明、作者、提交者、指向上次提交(commits)的指針等等。
  • 一個(gè)“tag”是來(lái)標(biāo)記某一個(gè)提交(commit) 的方法。

  比如說(shuō)我們執(zhí)行了以下代碼進(jìn)行了一次提交:

$ git add README test.rb LICENSE2
$ git commit -m 'initial commit of my project'

  現(xiàn)在,Git 倉(cāng)庫(kù)中有五個(gè)對(duì)象:三個(gè)表示文件快照內(nèi)容的 blob 對(duì)象;一個(gè)記錄著目錄樹(shù)內(nèi)容及其中各個(gè)文件對(duì)應(yīng) blob 對(duì)象索引的 tree 對(duì)象;以及一個(gè)包含指向 tree 對(duì)象(根目錄)的索引和其他提交信息元數(shù)據(jù)的 commit 對(duì)象。概念上來(lái)說(shuō),倉(cāng)庫(kù)中的各個(gè)對(duì)象保存的數(shù)據(jù)和相互關(guān)系看起來(lái)如下圖:

  

  如果進(jìn)行多次提交,倉(cāng)庫(kù)的歷史會(huì)像這樣:

  

分支引用

  所謂的GIT分支,其實(shí)就是一個(gè)指向某一個(gè)Commit對(duì)象的指針,像下面這樣,有兩個(gè)分支,master與testing:

  

  而我們?cè)趺粗喇?dāng)前在哪一個(gè)分支呢?其實(shí)就是很簡(jiǎn)單地使用了一個(gè)名叫HEAD的指針,如上圖所示。HEAD指針的值可以為一個(gè)SHA-1值或是一個(gè)引用,看以下例子:

  

  git的所有版本信息都保存了Working Directory下的.git目錄,而HEAD指針就保存在.git目錄下,如上圖所有,目前為止已經(jīng)有3個(gè)提交,通過(guò)查看HEAD的值可以看到我們當(dāng)前在master分支:refs/heads/master,當(dāng)我們通過(guò)git checkout取出某一特定提交后,HEAD的值就是成了我們checkout的提交的SHA-1值。

  記錄我們當(dāng)前的位置很簡(jiǎn)單,就是能過(guò)HEAD指針,HEAD指向某一提交的SHA-1值或是某一分支的引用。

新建分支

git branch <branch-name>

  有時(shí)需要在新建分支后直接切換到新建的分支,可以直接用checkout的-b選項(xiàng)

git checkout -b <branch-name>

刪除分支

git branch -d <branch-name>

  如果在指定的分支有一些unmerged的提交,刪除分支會(huì)失敗,這里可以使用-D參數(shù)強(qiáng)制刪除分支。

git branch -D <branch-name>

檢出分支或提交

  檢出某一分支或某一提交是同一個(gè)命令

git checkout <branch-name> | <commit>

分支合并(merge)

  當(dāng)我們新建一個(gè)分支進(jìn)行開(kāi)發(fā),并提交了幾次更新后,感覺(jué)是時(shí)候?qū)⑦@個(gè)分支的內(nèi)容合回主線了,這是就可以取出主線分支,然后把分支的更新merge回來(lái):

git checkout master

git merge testing

  如果master分支是testing分支的直接上游,即從master延著testing分支的提交歷史往前走可以直接走到testing分支的最新提交,那么系統(tǒng)什么也不需要做,只需要改變master分支的指針即可,這被稱之為"Fast Forward"。

  但是,一般情況是這樣的,你取出了最新的master分支,比如說(shuō)master分支最新的提交是C2(假設(shè)共3次提交C0<-C1<-C2),在此基礎(chǔ)上你新建了分支,當(dāng)你在分支上提交了C3、C5后想將br1時(shí)merge回master時(shí),你發(fā)現(xiàn)已經(jīng)有其他人提交了C4,這時(shí)候就不能直接修改master的指針了,不然會(huì)丟失別人的提交,這個(gè)時(shí)候就需要將你新建分支時(shí)master所在的提交(C2)后的修改(C4),與你新建分支后在分支上的修改(C3、C5)做合并,將合并后的結(jié)果作為一個(gè)新的提交提交到master,GIT可以自動(dòng)推導(dǎo)出應(yīng)該基于哪個(gè)提交進(jìn)行合并(C2),如果沒(méi)有沖突,系統(tǒng)會(huì)自動(dòng)提交新的提交,如果有沖突,系統(tǒng)會(huì)提示你解決沖突,當(dāng)沖突解決后,你就可以將修改加入暫存區(qū)并提交。提交歷史類似下面這樣(圖來(lái)自Pro-Git):

  

  merge后的提交是按時(shí)間排序的,比如下圖,我們?cè)趓ename提交處新建分支test,在test上提交Commit from branch test,然后回到master提交commit in master after committing in branch,再將test分支merge進(jìn)master,這時(shí)看提交提交歷史,Commit from branch test是在commit in master...之前的,盡管在master上我們是在rename的基礎(chǔ)上提交的commit in master...而GIT會(huì)在最后添加一個(gè)新的提交(Merge branch 'test')表示我們?cè)诖颂帉⒁粋€(gè)分支merge進(jìn)來(lái)了。這種情況會(huì)有一個(gè)問(wèn)題,比如說(shuō)在rename提交處某人A從你這里Copy了一個(gè)GIT倉(cāng)庫(kù),然后你release了一個(gè)patch(通過(guò)git format-patch)給A,這時(shí)候test分支還沒(méi)有merge進(jìn)來(lái),所以patch中只包含提交:commit in master...然后你把test分支merge了進(jìn)來(lái)又給了A一個(gè)patch,這個(gè)patch會(huì)包含提交:Commit from branch test,而這個(gè)patch是以rename為base的,如果commit in master...和Commit from branch test修改了相同的文件,則第二次的patch可能會(huì)打不上去,因?yàn)橐詒ename為base的patch可能在新的Code上找不到在哪個(gè)位置應(yīng)用修改。

  

分支衍合(rebase)

  有兩種方法將一個(gè)分支的改動(dòng)合并進(jìn)另一個(gè)分支,一個(gè)就是前面所說(shuō)的分支合并,另一個(gè)就是分支衍合,這兩種方式有什么區(qū)別呢?

  分支合并(merge)是將兩個(gè)分支的改動(dòng)合并到一起,并生成一個(gè)新的提交,提交歷史是按時(shí)間排序的,即我們實(shí)際提交的順序,通過(guò)git log --graph或一些圖形化工具,可能很明顯地看到分支的合并歷史,如果分支比較多就很混亂,而且如果以功能點(diǎn)新建分支,等功能點(diǎn)完成后合回主線,由于merge后提交是按提交時(shí)間排序的,提交歷史就比較亂,各個(gè)功能點(diǎn)的提交混雜在一起,還可能遇到上面提到的patch問(wèn)題。

  而分支衍合(rebase)是找到兩個(gè)分支的共同祖先提交,將要被rebase進(jìn)來(lái)的分支的提交依次在要被rebase到的分支上重演一遍,即回到兩個(gè)分支的共同祖先,將branch(假如叫experiment)的每次提交的差異保存到臨時(shí)文件里,然后切換到要衍合入的分支(假如是master),依次應(yīng)用補(bǔ)丁文件。experiment上有幾次提交,在master就生成幾次新的提交,而且是連在一起的,這樣合進(jìn)主線后每個(gè)功能點(diǎn)的提交就都在一起,而且提交歷史是線性的

   對(duì)比merge與rebase的提交歷史會(huì)是下圖這樣的(圖來(lái)自Pro-GIt):

  

(merge)

  

(rebase)

 

  rebase后C3提交就不存在了,取而代之的是C3',而master也成為了experiment的直接上游,只需一次Fast Forward(git merge)后master就指向了最新的提交,就可以刪除experiment分支了。

衍合--onto

git rebase --onto master server client

  這條命令的意思是:檢出server分支與client分支共同祖先之后client上的變化,然后在master上重演一遍。

父提交

  HEAD表示當(dāng)前所在的提交,如果要查看當(dāng)前提交父提交呢?git log查看提交歷史,顯然太麻煩了,而且輸入一長(zhǎng)串的Commit-ID也不是一個(gè)令人愉悅的事。這時(shí)可借助兩個(gè)特殊的符號(hào):~與^。

  ^ 表示指定提交的父提交,這個(gè)提交可能由多個(gè)交提交,^之后跟上數(shù)字表示第幾個(gè)父提交,不跟數(shù)字等同于^1。

  ~n相當(dāng)于n個(gè)^,比如~3=^^^,表示第一個(gè)父提交的第一個(gè)父提交的第一個(gè)父提交。

遠(yuǎn)程分支

  遠(yuǎn)程分支以(遠(yuǎn)程倉(cāng)庫(kù)名)/(分支名)命令,遠(yuǎn)程分支在本地?zé)o法移動(dòng)修改,當(dāng)我們clone一個(gè)遠(yuǎn)程倉(cāng)庫(kù)時(shí)會(huì)自動(dòng)在本地生成一個(gè)名叫original的遠(yuǎn)程倉(cāng)庫(kù),下載遠(yuǎn)程倉(cāng)庫(kù)的所有數(shù)據(jù),并新建一個(gè)指向它的分支original/master,但這個(gè)分支我們是無(wú)法修改的,所以需要在本地重新一個(gè)分支,比如叫master,并跟蹤遠(yuǎn)程分支。

  Clone了遠(yuǎn)程倉(cāng)庫(kù)后,我們還會(huì)在本地新建其他分支,并且可能也想跟蹤遠(yuǎn)程分支,這時(shí)可以用以下命令:

git checkout -b [branch_name] --track|-t <remote>/<remote-banch>

  和新建分支的方法一樣,只是加了一個(gè)參數(shù)--track或其縮寫(xiě)形式-t,可以指定本地分支的名字,如果不指定就會(huì)被命名為remote-branch。

  要拉取某個(gè)遠(yuǎn)程倉(cāng)庫(kù)的數(shù)據(jù),可以用git fetch:

git fetch <remote>

  當(dāng)拉取到了遠(yuǎn)程倉(cāng)庫(kù)的數(shù)據(jù)后只是把數(shù)據(jù)保存到了一個(gè)遠(yuǎn)程分支中,如original/master,而這個(gè)分支的數(shù)據(jù)是無(wú)法修改的,此時(shí)我們可以把這個(gè)遠(yuǎn)程分支的數(shù)據(jù)合并到我們當(dāng)前分支

git merge <remote>/<remote-branch>

  如果當(dāng)前分支已經(jīng)跟蹤了遠(yuǎn)程分支,那么上述兩個(gè)部分就可以合并為一個(gè)

git pull

  當(dāng)在本地修改提交后,我們可能需要把這些本地的提交推送到遠(yuǎn)程倉(cāng)庫(kù),這里就可以用git push命令,由于本地可以由多個(gè)遠(yuǎn)程倉(cāng)庫(kù),所以需要指定遠(yuǎn)程倉(cāng)庫(kù)的名字,并同時(shí)指定需要推的本地分支及需要推送到遠(yuǎn)程倉(cāng)庫(kù)的哪一個(gè)分支

git push <remote> <local-branch>:<remote-branch>

  如果本地分支與遠(yuǎn)程分支同名,命令可以更簡(jiǎn)單

git push <remote> <branch-name> 等價(jià)于 git push <remote> refs/heads/<branch-name>:refs/for/<branch-name>

  如果本地分支的名字為空,可以刪除遠(yuǎn)程分支。

  前面說(shuō)過(guò)可以有不止一個(gè)遠(yuǎn)程分支f,添加遠(yuǎn)程分支的方法為

git remote add <short-name> <url>

六、標(biāo)簽-tag

  作為一個(gè)版本控制工具,針對(duì)某一時(shí)間點(diǎn)的某一版本打tag的功能是必不可少的,要查看tag也非常簡(jiǎn)單,查看tag使用如下命令

git tag

  參數(shù)"-l"可以對(duì)tag進(jìn)行過(guò)濾

git tag -l "v1.1.*"

  Git 使用的標(biāo)簽有兩種類型:輕量級(jí)的(lightweight)和含附注的(annotated)。輕量級(jí)標(biāo)簽就像是個(gè)不會(huì)變化的分支,實(shí)際上它就是個(gè)指向特定提交對(duì)象的引用。而含附注標(biāo)簽,實(shí)際上是存儲(chǔ)在倉(cāng)庫(kù)中的一個(gè)獨(dú)立對(duì)象,它有自身的校驗(yàn)和信息,包含著標(biāo)簽的名字,電子郵件地址和日期,以及標(biāo)簽說(shuō)明,標(biāo)簽本身也允許使用 GNU Privacy Guard (GPG) 來(lái)簽署或驗(yàn)證。

  輕量級(jí)標(biāo)簽只需在git tag后加上tag的名字,如果tag名字

git tag <tag_name>

  含附注的標(biāo)簽需要加上參數(shù)-a(annotated),同時(shí)加上-m跟上標(biāo)簽的說(shuō)明

git tag -a <tag_name> -m "<tag_description>"

  如果你有自己的私鑰,還可以用 GPG 來(lái)簽署標(biāo)簽,只需要把之前的 -a 改為 -s(signed)

  查看標(biāo)簽的內(nèi)容用

 git show <tag_name>

  驗(yàn)證已簽署的標(biāo)簽用-v(verify)

git tag -v <tag_name>

  有時(shí)在某一個(gè)版本忘記打tag了,可以在后期再補(bǔ)上,只需在打tag時(shí)加上commit-id

  要將tag推送到遠(yuǎn)程服務(wù)器上,可以用

git push <remote> <tag_name>

  或者可以用下面的命令推送所有的tag

git push <remote> --tags

七、Git配置

  使用"git config"可以配置Git的環(huán)境變量,這些變量可以存放在以下三個(gè)不同的地方:

  • /etc/gitconfig 文件:系統(tǒng)中對(duì)所有用戶都普遍適用的配置。若使用 git config 時(shí)用 --system選項(xiàng),讀寫(xiě)的就是這個(gè)文件。
  • ~/.gitconfig 文件:用戶目錄下的配置文件只適用于該用戶。若使用 git config 時(shí)用 --global選項(xiàng),讀寫(xiě)的就是這個(gè)文件。
  • 當(dāng)前項(xiàng)目的 git 目錄中的配置文件(也就是工作目錄中的 .git/config 文件):這里的配置僅僅針對(duì)當(dāng)前項(xiàng)目有效。每一個(gè)級(jí)別的配置都會(huì)覆蓋上層的相同配置,所以 .git/config 里的配置會(huì)覆蓋/etc/gitconfig 中的同名變量。

  在 Windows 系統(tǒng)上,Git 會(huì)找尋用戶主目錄下的 .gitconfig 文件。主目錄即 $HOME 變量指定的目錄,一般都是 C:\Documents and Settings\$USER。此外,Git 還會(huì)嘗試找尋 /etc/gitconfig 文件,只不過(guò)看當(dāng)初 Git 裝在什么目錄,就以此作為根目錄來(lái)定位。

  最基礎(chǔ)的配置是配置git的用戶,用來(lái)標(biāo)識(shí)作者的身份

git config --global user.name <name>

git config --global user.email <email>

  文本編輯器也可以配置,比如在git commit的時(shí)候就會(huì)調(diào)用我們?cè)O(shè)置的文本編輯器

git config --global core.editor vim

  另一個(gè)常用的是diff工具,比如我們想用可視化的對(duì)比工具

git config --global merge.tool meld

  要查看所有的配置,可以用

git config --list

  或者可以在git config后加上配置項(xiàng)的名字查看具體項(xiàng)的配置

git config user.name

  作為一個(gè)懶人,雖然checkout、status等命令只是一個(gè)單詞,但是還是嫌太長(zhǎng)了,我們還可以給命令設(shè)置別名如

git config --global alias.co checkout

  這樣git co就等于git checkout

  前面說(shuō)地,git配置項(xiàng)都保存在那3個(gè)文件里,可以直接打開(kāi)相應(yīng)的配置文件查看配置,也可以直接修改這些配置文件來(lái)配置git,想刪除某一個(gè)配置,直接刪除相應(yīng)的行就行了

  

八、其他

  關(guān)于GIT各命令的說(shuō)明可以查看相關(guān)幫助文檔,通過(guò)以下方法:

git help <command>或git <command> --help

 REPO

repo start <topic_name>

  開(kāi)啟一個(gè)新的主題,其實(shí)就是每個(gè)Project都新建一個(gè)分支。

repo init -u <url> [OPTIONS]

  在當(dāng)前目錄下初始化repo,會(huì)在當(dāng)前目錄生生成一個(gè).repo目錄,像Git Project下的.git一樣,-u指定url,可以加參數(shù)-m指定manifest文件,默認(rèn)是default.xml,.repo/manifests保存manifest文件。.repo/projects下有所有的project的數(shù)據(jù)信息,repo是一系列g(shù)it project的集合,每個(gè)git project下的.git目錄中的refs等目錄都是鏈接到.repo/manifests下的。

repo manifest

  可以根據(jù)當(dāng)前各Project的版本信息生成一個(gè)manifest文件

repo sync [PROJECT1...PROJECTN]

  同步Code。

repo status

  查看本地所有Project的修改,在每個(gè)修改的文件前有兩個(gè)字符,第一個(gè)字符表示暫存區(qū)的狀態(tài)。

-no changesame in HEAD and index
Aaddednot in HEAD, in index
Mmodifiedin HEAD, modified in index
Ddeletedin HEAD, not in index
Rrenamednot in HEAD, path changed in index
Ccopiednot in HEAD, copied from another in index
Tmode changedsame content in HEAD and index, mode changed
Uunmergedconflict between HEAD and index; resolution required

  每二個(gè)字符表示工作區(qū)的狀態(tài) 

lettermeaningdescription
-new/unknownnot in index, in work tree
mmodifiedin index, in work tree, modified
ddeletedin index, not in work tree

 

repo prune <topic> 

  刪除已經(jīng)merge的分支

repo abandon <topic>

  刪除分支,無(wú)論是否merged

repo branch或repo branches

  查看所有分支

repo diff

  查看修改

repo upload

  上傳本地提交至服務(wù)器

repo forall [PROJECT_LIST]-c COMMAND

  對(duì)指定的Project列表或所有Project執(zhí)行命令COMMAND,加上-p參數(shù)可打印出Project的路徑。

repo forall -c 'git reset --hard HEAD;git clean -df;git rebase --abort'

  這個(gè)命令可以撤銷(xiāo)整個(gè)工程的本地修改。

 

  說(shuō)明:文中關(guān)于Git的知識(shí)大多來(lái)自Pro-GIt,這本書(shū)感覺(jué)不錯(cuò),想學(xué)習(xí)的可以找來(lái)看:Pro-Git

作者:AngelDevil
出處:www.cnblogs.com/angeldevil

本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)
打開(kāi)APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
推薦!手把手教你使用Git | 互聯(lián)網(wǎng)的那點(diǎn)事
你需要知道的12個(gè)Git高級(jí)命令
git repo
TortoiseGit日常使用指南
GIT版本配置管理
常用Git命令使用教程
更多類似文章 >>
生活服務(wù)
熱點(diǎn)新聞
分享 收藏 導(dǎo)長(zhǎng)圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服