Git內(nèi)部培訓(xùn)課件
Git簡(jiǎn)介
什么是版本控制
版本控制系統(tǒng)(Version Control System离唐,簡(jiǎn)稱VCS)是一種記錄一個(gè)或若干文件內(nèi)容變化,以便將來(lái)查閱特定版本修訂情況的系統(tǒng)纽甘。
按類型可以分為:
-
本地版本控制系統(tǒng)
例如RCS(至少我是從來(lái)沒(méi)有用過(guò))
本地版本控制系統(tǒng)解決了版本的管理問(wèn)題肮韧,再也不用時(shí)不時(shí)的把工程目錄锐想,通過(guò)手工拷貝的方式來(lái)存檔了帮寻。但本地版本控制系統(tǒng)的缺點(diǎn)是,無(wú)法解決多人協(xié)作的問(wèn)題赠摇。 -
集中化的版本控制系統(tǒng)
例如CVS固逗,SVN等(公司中SVN應(yīng)該用的比較多)
有一個(gè)集中管理的服務(wù)器浅蚪,所有開(kāi)發(fā)人員通過(guò)客戶端連到這臺(tái)服務(wù)器,取出最新的 文件 或者提交更新烫罩。管理員可以掌控每個(gè)開(kāi)發(fā)者的權(quán)限惜傲。
集中化的VCS不但解決了版本控制問(wèn)題,還可以多人協(xié)作贝攒。但缺點(diǎn)也是有的盗誊,就是太依賴于遠(yuǎn)程服務(wù)器收叶,CVS服務(wù)器宕機(jī)后孵延,會(huì)影響所有人的工作柱查。版本記錄只保存在一臺(tái)服務(wù)器上怒见,會(huì)有數(shù)據(jù)丟失風(fēng)險(xiǎn)。 -
分布式版本控制系統(tǒng)
例如Git
客戶端并不只提取最新版本的文件佳谦,而是把 代碼倉(cāng)庫(kù) 完整地鏡像下來(lái)殴蹄。每一次的提取操作荚藻,實(shí)際上都是一次對(duì) 代碼倉(cāng)庫(kù) 的完整備份咽扇。
所以并沒(méi)有"中心服務(wù)器"的概念邪财,所謂的"Git服務(wù)器",也同每個(gè)人的電腦一樣质欲,只是為了多人協(xié)作時(shí)树埠,方便大家交換數(shù)據(jù)而已。
什么是Git
Git是目前世界上最先進(jìn)的分布式版本控制系統(tǒng)(沒(méi)有之一)
好不好用嘶伟,看看它的開(kāi)發(fā)者是誰(shuí)就知道了:Linux之父 Linus Torvalds
小歷史: Linux內(nèi)核社區(qū)原本使用的是名為BitKeeper的商業(yè)化版本控制工具怎憋,2005年,因?yàn)樯鐓^(qū)內(nèi)有人試圖破解BitKeeper的協(xié)議奋早,BitMover公司收回了免費(fèi)使用BitKeeper的權(quán)力盛霎。
Linus原本可以出面道個(gè)歉赠橙,繼續(xù)使用BitKeeper耽装,然而并沒(méi)有。期揪。掉奄。Linus大神僅用了兩周時(shí)間,自已用C寫(xiě)了一個(gè)分布式版本控制系統(tǒng)凤薛,于是Git誕生了姓建!
為什么要使用Git
為什么要使用Git,或者說(shuō)Git相比SVN有什么優(yōu)勢(shì)呢缤苫?
-
分布式
-
分支管理
-
GitHub
安裝Git
- 大多數(shù)Linux發(fā)行版已經(jīng)預(yù)裝了Git速兔,系統(tǒng)默認(rèn)自帶,如果不帶活玲。涣狗〉瘢可以源碼make安裝或使用yum/apt等直接安裝,過(guò)程不贅述了镀钓。
- macOS下穗熬,安裝Xcode后,它的CLI工具里應(yīng)該會(huì)包含Git了丁溅』秸幔或者使用brew手工安裝一下。
- Windows下窟赏,可以直接下載安裝 msysGit 妓柜。 或者如果你的機(jī)器上已經(jīng)有Cygwin,也可以直接用在它下面安裝Git饰序。
- 圖形工具推薦使用 SourceTree领虹,查看分支非常直觀 。IntelliJ IDEA等IDE也會(huì)自帶一些圖形化的工具求豫,在合并代碼時(shí)很高效塌衰。
學(xué)習(xí)路徑
- 首先,忘掉SVN/CVS蝠嘉,不要把Git的各種操作與它們做類比最疆,切記。
- 剛開(kāi)始不要依賴圖形客戶端蚤告。首先應(yīng)該將精力用在理解原理上 -> 然后掌握一些基本CLI命令努酸,動(dòng)手操作實(shí)踐 -> 最后在實(shí)際工作中使用GUI工具以提高效率。
- 重度Windows用戶使用Git時(shí)杜恰,與平時(shí)熟悉GUI的環(huán)境會(huì)有些違和感获诈,畢竟Git是Linux下的產(chǎn)物,Git遵循Linux的哲學(xué)心褐,Simple舔涎,簡(jiǎn)單直接,但Simple并不等于Easy逗爹。需要轉(zhuǎn)換一下思維亡嫌。
了解Git的工作原理
記錄文件整體快照
Git和其他版本控制系統(tǒng)的主要差別在于,Git只關(guān)心文件數(shù)據(jù)的 整體 是否發(fā)生變化掘而,而大多數(shù)其他系統(tǒng)則只關(guān)心 文件內(nèi)容 的具體差異挟冠。
SVN在每個(gè)版本中,以單一文件為單位袍睡,記錄各個(gè)文件的差異:
Git在每個(gè)版本中知染,以當(dāng)時(shí)的全部文件為單位,記錄一個(gè)快照:
大多數(shù)操作都在本地執(zhí)行
Git的絕大多數(shù)操作都只需要訪問(wèn)本地文件和資源斑胜,不用連網(wǎng)控淡。因?yàn)槟愕谋緳C(jī)上色瘩,就已經(jīng)是完整的代碼庫(kù)了。這樣一來(lái)逸寓,在無(wú)法連接公司內(nèi)網(wǎng)的環(huán)境中居兆,也可以愉快的寫(xiě)代碼了。
例如竹伸,如果想看當(dāng)前版本的文件和一個(gè)月前的版本之間有何差異泥栖,Git會(huì)取出一個(gè)月前的快照和當(dāng)前文件作一次差異運(yùn)算,而不用每次都請(qǐng)求遠(yuǎn)程服務(wù)器勋篓。
時(shí)刻保持?jǐn)?shù)據(jù)完整性
在保存到Git之前吧享,所有數(shù)據(jù)都要進(jìn)行內(nèi)容的校驗(yàn)和(checksum)計(jì)算,并將此結(jié)果作為數(shù)據(jù)的唯一標(biāo)識(shí)和索引譬嚣。
這項(xiàng)特性作為Git的設(shè)計(jì)哲學(xué)钢颂,建在整體架構(gòu)的最底層拜银。所以如果文件在傳輸時(shí)變得不完整殊鞭,或者磁盤損壞導(dǎo)致文件數(shù)據(jù)缺失,Git都能立即察覺(jué)尼桶。
Git使用SHA-1算法計(jì)算數(shù)據(jù)的校驗(yàn)和操灿,通過(guò)對(duì)文件的內(nèi)容或目錄的結(jié)構(gòu)計(jì)算出一個(gè)SHA-1哈希值,作為指紋字符串泵督。該字串由40個(gè)十六進(jìn)制字符組成趾盐,看起來(lái)就像是:
24b9da6552252987aa493b52f8696cd6d3b00373
Git的工作完全依賴于這類指紋字串,所以你會(huì)經(jīng)承±埃看到這樣的哈希值救鲤。實(shí)際上,所有保存在 Git數(shù)據(jù)庫(kù)中的東西都是用此哈希值來(lái)作索引的秩冈,而不是靠文件名本缠。
多數(shù)操作僅添加數(shù)據(jù)
常用的Git操作大多僅僅是把數(shù)據(jù)添加到數(shù)據(jù)庫(kù),很難讓Git執(zhí)行任何不可逆操作漩仙。在Git中一旦提交快照之后就完全不用擔(dān)心丟失數(shù)據(jù)搓茬,特別是養(yǎng)成定期推送到其他倉(cāng)庫(kù)的習(xí)慣的話犹赖。
文件的三種狀態(tài)
對(duì)于任何一個(gè)文件队他,在 Git 內(nèi)都只有三種狀態(tài):已提交(committed) 已修改(modified) 已暫存(staged)
已提交表示該文件已經(jīng)被安全地保存在本地?cái)?shù)據(jù)庫(kù)中了;
已修改表示修改了某個(gè)文件峻村,但還沒(méi)有提交保存麸折;
已暫存表示把已修改的文件放在下次提交時(shí)要保存的清單中。
由此我們看到 Git 管理項(xiàng)目時(shí)粘昨,文件流轉(zhuǎn)的三個(gè)工作區(qū)域:Git 的工作目錄垢啼,暫存區(qū)域窜锯,以及本地倉(cāng)庫(kù)。
每個(gè)項(xiàng)目都有一個(gè)名為.git的目錄芭析,它是 Git用來(lái)保存元數(shù)據(jù)和對(duì)象數(shù)據(jù)庫(kù)的地方锚扎。該目錄非常重要,每次克隆鏡像倉(cāng)庫(kù)的時(shí)候馁启,實(shí)際拷貝的就是這個(gè)目錄里面的數(shù)據(jù)驾孔。
從項(xiàng)目中取出某個(gè)版本的所有文件和目錄,用以開(kāi)始后續(xù)工作的叫做工作目錄惯疙。這些文件實(shí)際上都是從Git目錄中的壓縮對(duì)象數(shù)據(jù)庫(kù)中提取出來(lái)的翠勉,接下來(lái)就可以在工作目錄中對(duì)這些文件進(jìn)行編輯。
所謂的暫存區(qū)域只不過(guò)是個(gè)簡(jiǎn)單的文件霉颠,一般都放在 Git 目錄中对碌。有時(shí)候人們會(huì)把這個(gè)文件叫做索引文件,不過(guò)標(biāo)準(zhǔn)說(shuō)法還是叫暫存區(qū)域蒿偎。
基本的 Git 工作流程如下:
- 在工作目錄中修改某些文件朽们。
- 對(duì)修改后的文件進(jìn)行快照,然后保存到暫存區(qū)域诉位。
- 提交更新华坦,將保存在暫存區(qū)域的文件快照永久轉(zhuǎn)儲(chǔ)到 Git 目錄中。
所以不从,我們可以從文件所處的位置來(lái)判斷狀態(tài):如果是Git目錄中保存著的特定版本文件惜姐,就屬于已提交狀態(tài);如果作了修改并已放入暫存區(qū)域椿息,就屬于已暫存狀態(tài)歹袁;如果自上次取出后,作了修改但還沒(méi)有放到暫存區(qū)域寝优,就是已修改狀態(tài)条舔。
創(chuàng)建版本庫(kù)
有兩種取得Git項(xiàng)目倉(cāng)庫(kù)的方法。第一種是在現(xiàn)存的目錄下乏矾,通過(guò)導(dǎo)入所有文件來(lái)創(chuàng)建新的Git倉(cāng)庫(kù)孟抗。 第二種是從已有的Git倉(cāng)庫(kù)克隆出一個(gè)新的鏡像倉(cāng)庫(kù)來(lái)。
在目錄中創(chuàng)建新倉(cāng)庫(kù)
如果一個(gè)目錄還沒(méi)有使用Git進(jìn)行管理钻心,只需到此項(xiàng)目所在的目錄凄硼,執(zhí)行git init
,初始化后捷沸,在當(dāng)前目錄下會(huì)出現(xiàn)一個(gè)名為.git的目錄
$ mkdir learngit
$ cd learngit
$ git init
從已有的倉(cāng)庫(kù)克隆
如果Git項(xiàng)目已經(jīng)存在摊沉,可以使用git clone
從遠(yuǎn)程服務(wù)器上復(fù)制一份出來(lái),Git支持多種協(xié)議:
$ git clone mobgit@134.32.51.60:learngit.git #使用SSH傳輸協(xié)議
$ git clone git://134.32.51.60/learngit.git #使用Git傳輸協(xié)議
$ git clone https://134.32.51.60/learngit.git #使用HTTPS傳輸協(xié)議
版本庫(kù)基本操作
檢查當(dāng)前文件狀態(tài)
使用git status
命令可以查看文件的狀態(tài)
$ git status
On branch master
Initial commit
nothing to commit (create/copy files and use "git add" to track)
出現(xiàn)如上的提示痒给,說(shuō)明現(xiàn)在的工作目錄相當(dāng)干凈说墨,所有已跟蹤文件在上次提交后都未被更改過(guò)骏全。
現(xiàn)在我們做一些改動(dòng),添加一個(gè)readme.txt進(jìn)去尼斧,然后再看一下?tīng)顟B(tài)
$ cat>readme.txt
hello git
^C
git status
On branch master
Initial commit
Untracked files:
(use "git add <file>..." to include in what will be committed)
readme.txt
nothing added to commit but untracked files present (use "git add" to track)
Untracked files顯示了這個(gè)新創(chuàng)建的readme.txt處于未跟跟蹤狀態(tài)
跟蹤新文件
使用git add
命令開(kāi)始跟蹤一個(gè)新文件
$ git status
On branch master
Initial commit
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: readme.txt
readme.txt已 被跟蹤 姜贡,并處于 暫存狀態(tài)
將本次修改暫存
現(xiàn)在我們?cè)賹?duì)readme.txt進(jìn)行修改,添加一行棺棵,再執(zhí)行git status
查看狀態(tài)
$ git status
On branch master
Initial commit
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: readme.txt
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: readme.txt
可以看到readme.txt 不僅出現(xiàn)在了Changes to be committed鲁豪,還出現(xiàn)在了Changes not staged for commit
由此可見(jiàn),Git關(guān)心的是 Changes 律秃,而不是文件本身爬橡。
再次執(zhí)行
git add
,可以將 本次修改 提交到暫存區(qū)棒动,Changes not staged for commit提示消失提交更新
使用git commit
命令將暫存區(qū)中的內(nèi)容提交至版本庫(kù)糙申,工作區(qū)又是干凈的了
$ git commit -m "my first commit"
[master (root-commit) 6c8912a] my first commit
1 file changed, 2 insertions(+)
create mode 100644 readme.txt
$ git status
On branch master
Your branch is based on 'origin/master', but the upstream is gone.
(use "git branch --unset-upstream" to fixup)
nothing to commit, working tree clean
注意:一定要使用-m參數(shù)加入注釋,認(rèn)真描述本次的提交具體做了些什么船惨,這對(duì)于以后我們查詢歷史記錄非常重要柜裸。
如果覺(jué)得使用暫存區(qū)過(guò)于繁瑣,可以在commit時(shí)直接使用-a參數(shù)粱锐,Git就會(huì)自動(dòng)把所有已經(jīng)跟蹤過(guò)的文件暫存起來(lái)一并提交疙挺,從而跳過(guò)git add步驟。
$ git commit -a -m "my first commit"
查看歷史
使用git log
命令可以查看歷史記錄
$ git log
commit 43c5d337ffdd76f33ce5f5f90103d57e55474956
Author: BlueXIII <bluexiii@163.com>
Date: Thu Dec 8 14:45:59 2016 +0800
this is my second commit
commit 6c8912ad2a8e90a7ba32cc8578fd0069a205221b
Author: BlueXIII <bluexiii@163.com>
Date: Thu Dec 8 14:38:09 2016 +0800
my first commit
可以看到怜浅,每次更新都有一個(gè)SHA-1校驗(yàn)和铐然、作者的名字和電子郵件地址、提交時(shí)間恶座、提交說(shuō)明搀暑。
撤消操作
撤消操作在這里這里不做重點(diǎn)描述了,只列出幾個(gè)常用命令跨琳。
修改最后一次提交:
git commit --amend
取消已經(jīng)暫存的文件:
git reset HEAD readme.txt
取消對(duì)文件的修改:
git checkout -- readme.txt
遠(yuǎn)程倉(cāng)庫(kù)
之前介紹了在本地倉(cāng)庫(kù)的一些操作自点。但當(dāng)與他人協(xié)作開(kāi)發(fā)某個(gè)項(xiàng)目時(shí),需要至少使用一個(gè)遠(yuǎn)程倉(cāng)庫(kù)脉让,以便推送或拉取數(shù)據(jù)桂敛,分享各自的工作進(jìn)展。
克隆遠(yuǎn)程庫(kù)
之前已經(jīng)在講新建倉(cāng)庫(kù)時(shí)已經(jīng)提到溅潜,如何克隆遠(yuǎn)程庫(kù)术唬,這里再重復(fù)列一遍:
$ git clone mobgit@134.32.51.60:learngit.git #使用SSH傳輸協(xié)議
$ git clone git://134.32.51.60/learngit.git #使用Git傳輸協(xié)議
$ git clone https://134.32.51.60/learngit.git #使用HTTPS傳輸協(xié)議
查看綁定的遠(yuǎn)程庫(kù)
如果之前我們使用的git clone
命令直接克隆了一個(gè)遠(yuǎn)程倉(cāng)庫(kù)到本機(jī),Git就已經(jīng)默認(rèn)綁定了一個(gè)名為origin的遠(yuǎn)程庫(kù)伟恶。當(dāng)然我們還可以手工綁定其它遠(yuǎn)程庫(kù)碴开,遠(yuǎn)程倉(cāng)庫(kù)可以有多個(gè)毅该。
使用git remote -v
命令列出我們綁定了哪些遠(yuǎn)程庫(kù):
$ git remote -v
origin mobgit@134.32.51.60:learngit.git (fetch)
origin mobgit@134.32.51.60:learngit.git (push)
接下來(lái)還可以使用git remote show origin
來(lái)查看這個(gè)名為origin的遠(yuǎn)程庫(kù)的更詳細(xì)的信息博秫,這里先不細(xì)講
$ git remote show origin
* remote origin
Fetch URL: mobgit@134.32.51.60:learngit.git
Push URL: mobgit@134.32.51.60:learngit.git
HEAD branch (remote HEAD is ambiguous, may be one of the following):
dev
master
serverfix
serverfix2
Remote branches:
dev tracked
master tracked
serverfix tracked
serverfix2 tracked
Local branches configured for 'git pull':
dev merges with remote dev
master merges with remote master
serverfix merges with remote serverfix
serverfix2 merges with remote serverfix2
Local refs configured for 'git push':
dev pushes to dev (up to date)
master pushes to master (up to date)
serverfix pushes to serverfix (up to date)
serverfix2 pushes to serverfix2 (up to date)
手工添加一個(gè)遠(yuǎn)程倉(cāng)庫(kù)
我們先讓管理員新建一個(gè)名為learngit2的遠(yuǎn)程倉(cāng)庫(kù)潦牛,再使用remote add
命令將它添加進(jìn)來(lái),取名為repo2
$ git remote add repo2 mobgit@134.32.51.60:learngit2.git
$ git remote -v
origin mobgit@134.32.51.60:learngit.git (fetch)
origin mobgit@134.32.51.60:learngit.git (push)
repo2 mobgit@134.32.51.60:learngit2.git (fetch)
repo2 mobgit@134.32.51.60:learngit2.git (push)
現(xiàn)在我們有origin和repo2兩個(gè)遠(yuǎn)程倉(cāng)庫(kù)了
從遠(yuǎn)程倉(cāng)庫(kù)抓取數(shù)據(jù)
使用git fetch [remote-name]
從遠(yuǎn)程倉(cāng)庫(kù)抓取數(shù)據(jù)挡育,注意fetch命令只是將遠(yuǎn)端的數(shù)據(jù)拉到本地倉(cāng)庫(kù)巴碗,并不自動(dòng)合并到當(dāng)前工作分支(關(guān)于分支稍后講解)
例如要抓取名為origin遠(yuǎn)程倉(cāng)庫(kù):
$ git fetch origin
推送數(shù)據(jù)到遠(yuǎn)程倉(cāng)庫(kù)
使用git push [remote-name] [branch-name]
將本機(jī)的工作成果推送到遠(yuǎn)程倉(cāng)庫(kù)
例如要將本地的master分支推送到origin遠(yuǎn)程倉(cāng)庫(kù)上:
$ git push origin master
分支
也許到之前為止,大家會(huì)覺(jué)得Git和Svn除了實(shí)現(xiàn)原理不同以及實(shí)現(xiàn)了分布式之外即寒,在日常使用上并沒(méi)有什么太大的區(qū)別(甚至更繁瑣)橡淆。但接下來(lái)的分支,才是Git的精髓部分母赵。
為什么要使用分支
舉個(gè)簡(jiǎn)單的例子:假設(shè)你準(zhǔn)備開(kāi)發(fā)一個(gè)新功能逸爵,但是需要兩周才能完成,第一周你寫(xiě)了50%的代碼凹嘲,如果立刻提交师倔,由于代碼還沒(méi)寫(xiě)完,不完整的代碼庫(kù)會(huì)導(dǎo)致別人不能干活了周蹭。如果等代碼全部寫(xiě)完再一次提交趋艘,又存在丟失每天進(jìn)度的巨大風(fēng)險(xiǎn)。
于是你創(chuàng)建了一個(gè)屬于你自己的分支凶朗,別人看不到瓷胧,還繼續(xù)在原來(lái)的分支上正常工作,而你在自己的分支上干活棚愤,想提交就提交搓萧,直到開(kāi)發(fā)完畢后,再一次性合并到原來(lái)的分支上宛畦,這樣矛绘,既安全,又不影響別人工作刃永。
相比于Svn等工具货矮,Git創(chuàng)建、切換分支的開(kāi)銷是非常小的斯够,Git鼓勵(lì) 頻繁使用分支
分支的原理
要理解分支囚玫,需要繼續(xù)深入一下Git的工作原理
Git如何儲(chǔ)存數(shù)據(jù)
在Git中提交時(shí),會(huì)保存一個(gè)提交對(duì)象(commit object)读规,該對(duì)象包含一個(gè)指向暫存內(nèi)容快照的指針抓督,并同時(shí)包含本次提交的作者等相關(guān)附屬信息,包含零個(gè)或多個(gè)指向該提交對(duì)象的父對(duì)象指針(首次提交是沒(méi)有直接祖先的束亏,普通提交有一個(gè)祖先铃在,由兩個(gè)或多個(gè)分支合并產(chǎn)生的提交則有多個(gè)祖先)。
假設(shè)在工作目錄中有三個(gè)文件已經(jīng) 修改 過(guò),準(zhǔn)備將它們暫存后提交定铜。
git add
暫存操作時(shí)阳液,會(huì)對(duì) 每一個(gè)文件 計(jì)算校驗(yàn)和,然后把當(dāng)前版本的文件快照使用 blog對(duì)象 保存到Git倉(cāng)庫(kù)中(為提高性能揣炕,若文件沒(méi)有變化帘皿,Git不會(huì)再次保存)。將它們的SHA-1校驗(yàn)和加入到暫存區(qū)域等待提交畸陡。
git commit
提交操作鹰溜,時(shí),Git首先會(huì)計(jì)算 每一個(gè)子目錄 的校驗(yàn)和丁恭,然后將這些校驗(yàn)和保存為 tree對(duì)象 曹动。 然后Git會(huì)創(chuàng)建一個(gè) commit對(duì)象 ,它包含指向這個(gè)樹(shù)對(duì)象的指針及注釋牲览、提交人仁期、郵箱等信息。
現(xiàn)在竭恬,Git倉(cāng)庫(kù)中有五個(gè)對(duì)象:三個(gè)blob 對(duì)象(保存著文件快照);一個(gè)樹(shù)對(duì)象(記錄著目錄結(jié)構(gòu)和blob對(duì)象索引)以及一個(gè)提交對(duì)象(包含著指向前述樹(shù)對(duì)象的指針和所有提交信息)跛蛋。
單個(gè)提交對(duì)象在倉(cāng)庫(kù)中的數(shù)據(jù)結(jié)構(gòu):
多個(gè)提交對(duì)象之間的鏈接關(guān)系:
分支是什么
Git 中的分支,其實(shí)本質(zhì)上僅僅是個(gè)指向commit對(duì)象的可變指針痊硕。Git會(huì)使用master作為分支的默認(rèn)名字赊级。在若干次提交后,你其實(shí)已經(jīng)有了一個(gè)指向最后一次commit對(duì)象的master分支岔绸。它在每次提交的時(shí)候都會(huì)自動(dòng)向前移動(dòng)理逊。
創(chuàng)建名為testing的新的分支,本質(zhì)上就是創(chuàng)建一個(gè)指針盒揉,可以使用git branch
命令:
$ git branch testing
當(dāng)前工作在哪個(gè)分支
Git 是如何知道你當(dāng)前在哪個(gè)分支上工作的呢晋被?其實(shí)答案也很簡(jiǎn)單,它還保存著一個(gè)名為HEAD的特別指針刚盈。它是一個(gè)指向你正在工作中的本地分支的指針羡洛。
切換分支時(shí)發(fā)生了什么
切換分支,本質(zhì)上就是移動(dòng)HEAD指針藕漱。
要切換到其他分支欲侮,可以執(zhí)行git checkout
命令。我們現(xiàn)在轉(zhuǎn)換到剛才新建的testing分支:
$ git checkout testing
分支切換的實(shí)際操作
為了更好的理解分支肋联,我們接下來(lái)模擬實(shí)際工作中的場(chǎng)景威蕉,進(jìn)行一系列的切換操作。
現(xiàn)在我們已經(jīng)處于testing分支了橄仍,目前testing分支和master分支都是指向同一個(gè)commit韧涨,所以我們的工作區(qū)的內(nèi)容現(xiàn)在還沒(méi)有什么變化牍戚。
現(xiàn)在,我們要在testing分支上做一些文件修改虑粥,然后commit:
echo "testing branch">>readme.txt
git commit -a -m "modify on testing branch"
提交后如孝,產(chǎn)生了一個(gè)新的commit對(duì)象,并且HEAD隨著當(dāng)前testing分支一起向前移動(dòng)舀奶。而master分支則是停在原地不動(dòng)暑竟。
我們可以試著使用git checkout
命令切回master分支斋射,看看發(fā)生了什么:
$ git checkout master
這條命令做了兩件事:
- 它把HEAD指針移回到 master 分支育勺。
- 把工作目錄中的文件換成了master分支所指向的快照內(nèi)容。
我們?cè)囍趍aster上再做一些改動(dòng)并commit:
echo "testing master">>readme.txt
git commit -a -m "modify on master branch"
現(xiàn)在分支變成了上圖所示罗岖,我們可以在master與testing間隨時(shí)切換涧至,并修改工作區(qū)的文件內(nèi)容。必要時(shí)再將這兩個(gè)分支合并桑包。
分支新建與合并的實(shí)際操作
接下來(lái)南蓬,再以一個(gè)比較長(zhǎng)的真實(shí)的工作場(chǎng)景進(jìn)行舉例
我們首先在master分支上進(jìn)行工作,并提交了幾次更新哑了,測(cè)試無(wú)誤后編譯發(fā)布至生產(chǎn)系統(tǒng)赘方。
之后我們決定要修補(bǔ)問(wèn)題追蹤系統(tǒng)上的53號(hào)問(wèn)題,這時(shí)可以使用git checkout -b
命令快速創(chuàng)建一個(gè)分支并切換過(guò)去:
$ git checkout -b iss53
這相當(dāng)于執(zhí)行了下面這兩條命令:
$ git branch iss53
$ git checkout iss53
我們?cè)趇ss53分支上寫(xiě)了一些代碼弱左,并commit
$ vi index.html
$ git commit -a -m 'fixed the broken email address'
iss53上的工作還沒(méi)完成窄陡,突然接到通知,生產(chǎn)系統(tǒng)有一個(gè)緊急BUG需要立刻修復(fù)拆火。所以我們首先切回master分支跳夭,然后在master的基礎(chǔ)上,又新建出一個(gè)hotfix分支來(lái)修復(fù)BUG们镜。
$ git checkout master #回到master分支
$ git checkout -b hotfix #新建一個(gè)hotfix分支币叹,并切過(guò)去
$ vim index.html #修改一些東西,修復(fù)BUG
$ git commit -a -m 'fixed the broken email address' #提交hotfix
在hotfix分支上搞定BUG之后模狭,我們切回master分支颈抚,使用git merge
把剛才的hotfix合并進(jìn)來(lái)
$ git checkout master #切換回master分支
$ git merge hotfix #將hotfix分支的修改,合并到當(dāng)前master分支來(lái)(注意merge的方向嚼鹉,是從其它分支邪意,合到當(dāng)前分支)。
Updating f42c576..3a0874c
Fast-forward
README | 1 -
1 file changed, 1 deletion(-)
備注:本次合并時(shí)出現(xiàn)了“Fast forward”的提示反砌。由于當(dāng)前 master 分支所在的提交對(duì)象是要并入的 hotfix 分支的直接上游雾鬼,Git 只需把 master 分支指針直接右移。換句話說(shuō)宴树,如果順著一個(gè)分支走下去可以到達(dá)另一個(gè)分支的話策菜,那么 Git在合并兩者時(shí),只會(huì)簡(jiǎn)單地把指針右移,因?yàn)檫@種單線的歷史分支不存在任何需要解決的分歧又憨,所以這種合并過(guò)程可以稱為快進(jìn)(Fast forward)翠霍。
這時(shí)hotfix分支已經(jīng)沒(méi)用了,可以刪掉了
$ git branch -d hotfix #只是刪除了一個(gè)指針
現(xiàn)在回到之前未完成的53號(hào)問(wèn)題上蠢莺,繼續(xù)寫(xiě)一些代碼
$ git checkout iss53
$ vim index.html
$ git commit -a -m 'finished the new footer [issue 53]'
在問(wèn)題53相關(guān)的工作完成之后寒匙,可以合并回master分支。實(shí)際操作同前面合并hotfix分支差不多躏将,只需回到master分支锄弱,運(yùn)行g(shù)it merge命令指定要合并進(jìn)來(lái)的分支。
$ git checkout master
$ git merge iss53
Auto-merging README
Merge made by the 'recursive' strategy.
README | 1 +
1 file changed, 1 insertion(+)
請(qǐng)注意祸憋,這次合并操作的底層實(shí)現(xiàn)会宪,并不同于之前 hotfix 的并入方式。因?yàn)檫@次你的開(kāi)發(fā)歷史是從更早的地方開(kāi)始分叉的蚯窥。由于當(dāng)前 master 分支所指向的提交對(duì)象(C4)并不是 iss53 分支的直接祖先掸鹅,Git 不得不進(jìn)行一些額外處理。就此例而言拦赠,Git 會(huì)用兩個(gè)分支的末端(C4 和 C5)以及它們的共同祖先(C2)進(jìn)行一次簡(jiǎn)單的三方合并計(jì)算巍沙。
這次,Git 沒(méi)有簡(jiǎn)單地把分支指針右移荷鼠,而是對(duì)三方合并后的結(jié)果重新做一個(gè)新的快照句携,并自動(dòng)創(chuàng)建一個(gè)指向它的提交對(duì)象(C6)。這個(gè)提交對(duì)象比較特殊颊咬,它有兩個(gè)祖先(C4 和 C5)务甥。
有時(shí)候合并操作并不會(huì)如此順利。如果在不同的分支中都修改了同一個(gè)文件的同一部分喳篇,需要手工來(lái)處理沖突敞临。
$ git merge iss53
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
Automatic merge failed; fix conflicts and then commit the result.
Git作了合并,但沒(méi)有提交麸澜,它會(huì)停下來(lái)等你解決沖突挺尿。
$ git status
On branch master
You have unmerged paths.
(fix conflicts and run "git commit")
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: index.html
no changes added to commit (use "git add" and/or "git commit -a")
任何包含未解決沖突的文件都會(huì)以未合并(unmerged)的狀態(tài)列出。Git 會(huì)在有沖突的文件里加入標(biāo)準(zhǔn)的沖突解決標(biāo)記炊邦。
$ vi index.html
<<<<<<< HEAD
<div id="footer">contact : email.support@github.com</div>
=======
<div id="footer">
please contact us at support@github.com
</div>
>>>>>>> iss53
可以看到 ======= 隔開(kāi)的上半部分是 HEAD编矾,即master,下半部分是在iss53分支中的內(nèi)容馁害。
手工合并代碼后窄俏,把 <<<<<<<,======= 和 >>>>>>> 這些行也一并刪除碘菜。這時(shí)可以用git commit來(lái)提交了凹蜈。
分支策略
實(shí)際開(kāi)發(fā)中限寞,對(duì)于分支的管理,已經(jīng)有很多最佳實(shí)踐仰坦,大多數(shù)情況下履植,我們只需要遵守一些基本原則:
- 首先,master分支應(yīng)該是非常穩(wěn)定的悄晃,也就是僅用來(lái)發(fā)布新版本玫霎,平時(shí)不能在上面工作。
- 平時(shí)的開(kāi)發(fā)工作都放在dev分支上妈橄,也就是說(shuō)庶近,dev分支是不穩(wěn)定的。到某個(gè)時(shí)候眷细,比如測(cè)試通過(guò)拦盹,需要1.2版本發(fā)布時(shí)鹃祖,再把dev分支合并到master上溪椎,在master分支編譯發(fā)布1.2版本。
-
針對(duì)新需求恬口、修復(fù)等具體的任務(wù)校读,每次都在dev分支上開(kāi)一個(gè)新的任務(wù)分支出來(lái),工作完成后祖能,再向dev分支上合并就可以了歉秫。名稱沒(méi)有特別的規(guī)范,可以是人名养铸,例如:zhangsan雁芙,也可以是任務(wù)名、需求編號(hào)等钞螟,例如:iss03兔甘、feature04、hotfix鳞滨。
遠(yuǎn)程分支
之前討論過(guò)遠(yuǎn)程倉(cāng)庫(kù)洞焙,接著又學(xué)習(xí)了分支,當(dāng)二者結(jié)合到一起時(shí)拯啦,又會(huì)產(chǎn)生一些有趣的東西澡匪。
遠(yuǎn)程分支的概念
遠(yuǎn)程分支(remote branch),即遠(yuǎn)程倉(cāng)庫(kù)中的分支。同步到本地后褒链,與本地分支不同的是唁情,它們 無(wú)法移動(dòng) ;且只有在Git進(jìn)行網(wǎng)絡(luò)交互時(shí)才會(huì)更新甫匹。遠(yuǎn)程分支就像是書(shū)簽甸鸟,提醒著你上次連接遠(yuǎn)程倉(cāng)庫(kù)時(shí)上面各分支的位置夯巷。我們用 (遠(yuǎn)程倉(cāng)庫(kù)名)/(分支名) 這樣的形式表示遠(yuǎn)程分支(例如origin/master)。
如果我們?cè)诒镜豰aster分支做了些改動(dòng)哀墓,與此同時(shí)趁餐,其他人向遠(yuǎn)程倉(cāng)庫(kù)推送了他們的更新,那么服務(wù)器上的master分支就會(huì)向前推進(jìn)篮绰,而于此同時(shí)后雷,我們?cè)诒镜氐奶峤粴v史正朝向不同方向發(fā)展。(不過(guò)只要你不和服務(wù)器通訊吠各,你的 origin/master 指針仍然保持原位不會(huì)移動(dòng)臀突。)
可以運(yùn)行git fetch origin
來(lái)同步遠(yuǎn)程服務(wù)器上的數(shù)據(jù)到本地。該命令首先找到origin是哪個(gè)服務(wù)器贾漏,然后從上面獲取你尚未擁有的數(shù)據(jù)候学,更新你本地的數(shù)據(jù)庫(kù),然后把origin/master的指針移到它最新的位置上纵散。
可以使用git remote
命令查看遠(yuǎn)程倉(cāng)庫(kù)的詳情
$ git remote -v #列出遠(yuǎn)程服務(wù)器清單
origin mobgit@134.32.51.60:learngit.git (fetch)
origin mobgit@134.32.51.60:learngit.git (push)
$ git remote show origin #查詢某一個(gè)遠(yuǎn)程服務(wù)器的詳情
* remote origin
Fetch URL: mobgit@134.32.51.60:learngit.git
Push URL: mobgit@134.32.51.60:learngit.git
HEAD branch (remote HEAD is ambiguous, may be one of the following):
dev
master
Remote branches:
dev tracked
master tracked
Local branch configured for 'git pull':
master merges with remote master
Local ref configured for 'git push':
master pushes to master (up to date)
跟蹤遠(yuǎn)程分支
從遠(yuǎn)程分支checkout出來(lái)的本地分支梳码,稱為跟蹤分支 (tracking branch)。跟蹤分支是一種和某個(gè)遠(yuǎn)程分支有直接聯(lián)系的本地分支伍掀。
在跟蹤分支里輸入 git push
掰茶,Git 會(huì)自行推斷應(yīng)該向哪個(gè)服務(wù)器的哪個(gè)分支推送數(shù)據(jù)。同樣蜜笤,在這些分支里運(yùn)行 git pull
會(huì)獲取所有遠(yuǎn)程索引濒蒋,并把它們的數(shù)據(jù)都合并到本地分支中來(lái)。
在克隆倉(cāng)庫(kù)時(shí)把兔,Git 通常會(huì)自動(dòng)創(chuàng)建一個(gè)名為 master 的分支來(lái)跟蹤 origin/master沪伙。這正是 git push 和 git pull 一開(kāi)始就能正常工作的原因。
$ git checkout -b serverfix origin/serverfix
或簡(jiǎn)化為:
$ git checkout --track origin/serverfix
這會(huì)新建并切換到serverfix本地分支县好,其內(nèi)容同遠(yuǎn)程分支origin/serverfix一致围橡。
推送本地分支
要想和其他人分享某個(gè)本地分支,你需要把它推送到一個(gè)你擁有寫(xiě)權(quán)限的遠(yuǎn)程倉(cāng)庫(kù)聘惦。
例如本地有一個(gè)serverfix分支需要和他人一起開(kāi)發(fā)某饰,可以運(yùn)行 git push (遠(yuǎn)程倉(cāng)庫(kù)名) (分支名):
$ git push origin serverfix
Counting objects: 20, done.
Compressing objects: 100% (14/14), done.
Writing objects: 100% (15/15), 1.74 KiB, done.
Total 15 (delta 5), reused 0 (delta 0)
To git@github.com:schacon/simplegit.git
* [new branch] serverfix -> serverfix
或者加入--set-upstream設(shè)置跟蹤后,以后直接使用git push
就可以推送了:
git push --set-upstream origin serverfix
GitHub
[GitHub](https://github.com)是一個(gè)面向開(kāi)源及私有軟件項(xiàng)目的托管平臺(tái)善绎,因?yàn)橹恢С諫it作為唯一的版本庫(kù)格式進(jìn)行托管黔漂,故名GitHub。
GitHub本身沒(méi)有什么好學(xué)的禀酱,隨便看就知道怎么用了 知乎:怎樣使用GitHub
重點(diǎn)是炬守,GitHub上有非常多優(yōu)秀的個(gè)人項(xiàng)目值得我們學(xué)習(xí),我們也可以將自已的代碼發(fā)布上去剂跟〖跬荆可以看成是程序員的博客吧酣藻,只貼代碼,不廢話鳍置。
在GitHub上發(fā)布開(kāi)源項(xiàng)目是免費(fèi)的辽剧,但是私有項(xiàng)目收費(fèi)。
GitLab
GitLab是一個(gè)用Ruby on Rails寫(xiě)的開(kāi)源的版本管理系統(tǒng)税产,實(shí)現(xiàn)一個(gè)自托管的Git項(xiàng)目倉(cāng)庫(kù)怕轿,可通過(guò)Web界面進(jìn)行訪問(wèn)公開(kāi)的或者私人項(xiàng)目。它擁有與Github類似的功能辟拷,能夠?yàn)g覽源代碼撞羽,管理缺陷和注釋怯屉。
可以管理團(tuán)隊(duì)對(duì)倉(cāng)庫(kù)的訪問(wèn)射众,它非常易于瀏覽提交過(guò)的版本并提供一個(gè)文件歷史庫(kù)。團(tuán)隊(duì)成員可以利用內(nèi)置的簡(jiǎn)單聊天程序(Wall)進(jìn)行交流沪哺。它還提供一個(gè)代碼片段收集功能可以輕松實(shí)現(xiàn)代碼復(fù)用隅俘,便于日后有需要的時(shí)候進(jìn)行查找邻奠。
GitLab是目前搭建內(nèi)部Git服務(wù)器的首選,當(dāng)然如果要求不高的話考赛,我們也可以直接使用SSH協(xié)議來(lái)快速搭建Git服務(wù)端惕澎。
常用Git命令清單
更多內(nèi)容請(qǐng)直接參考 阮一峰的網(wǎng)絡(luò)日志
推薦文檔
不要指忘2小時(shí)的培訓(xùn)能帶來(lái)多大的收益莉测,最簡(jiǎn)單高效的方式颜骤,還是要多看優(yōu)秀的文檔。
本文大量參(chao)考(xi)了以下兩部文檔:
廖雪峰的在線教程 適合快速上手
Pro Git中文版 中文第一版