[TOC]
Git
@(24.1 Git)[git]
工作原理
直接記錄快照,而非差異比較
Git 只關(guān)心文件數(shù)據(jù)的整體是否發(fā)生變化钾虐,大多數(shù)其他系統(tǒng)則只關(guān)心文件內(nèi)容的具體差異噪窘,每次記錄有哪些文件作了更新,以及更新了什么內(nèi)容效扫,每個(gè)版本都是每個(gè)文件相對于上個(gè)版本更新了哪些內(nèi)容倔监。
Git 并不保存這些前后變化的差異數(shù)據(jù):實(shí)際上,Git 更像是把變化后的文件作快照菌仁,記錄在一個(gè)微型的文件系統(tǒng)中浩习。每次提交更新,都會縱覽一遍所有文件的指紋信息并對文件作一次快照济丘,然后保存一個(gè)指向這次快照的索引谱秽。為提高性能,若文件沒有變化摹迷,Git 不會再次保存快照怠苔,而只對上次保存的快照作一鏈接(指針)露氮。
Git 更像一個(gè)小型的文件系統(tǒng)偶房,同時(shí)提供許多以此為基礎(chǔ)的超強(qiáng)工具垃环。
時(shí)刻保持?jǐn)?shù)據(jù)完整性
Git 使用 SHA-1 算法計(jì)算數(shù)據(jù)的校驗(yàn)和,通過對文件的內(nèi)容或目錄的結(jié)構(gòu)計(jì)算出一個(gè) SHA-1 哈希值异赫,作為指紋字符串,Git 的工作完全依賴于這類指紋字串头岔,所有保存在 Git 數(shù)據(jù)庫中的東西都使用此哈希值作為索引塔拳,而不是靠文件名。
文件的三種狀態(tài)
- 工作目錄
- 暫存區(qū)域
- 本地倉庫
對于任何一個(gè)文件峡竣,在 Git 內(nèi)都只有三種狀態(tài):已提交(committed)靠抑,已修改(modified)和已暫存(staged)。已提交表示該文件已經(jīng)被安全地保存在本地?cái)?shù)據(jù)庫中了适掰;已修改表示修改了某個(gè)文件颂碧,但還沒有提交保存;已暫存表示把已修改的文件放在下次提交時(shí)要保存的清單中类浪。
從項(xiàng)目中取出某個(gè)版本的所有文件和目錄载城,用以開始后續(xù)工作的叫做工作目錄。這些文件實(shí)際上都是從 Git 目錄中的壓縮對象數(shù)據(jù)庫中提取出來的费就,接下來就可以在工作目錄中對這些文件進(jìn)行編輯诉瓦。
所謂的暫存區(qū)域只不過是個(gè)簡單的文件,一般都放在 Git 目錄中。有時(shí)候人們會把這個(gè)文件叫做索引文件睬澡,不過標(biāo)準(zhǔn)說法還是叫暫存區(qū)域固额。
基本的 Git 工作流程如下:
- 在工作目錄中修改某些文件。
- 對修改后的文件進(jìn)行快照煞聪,然后保存到暫存區(qū)域斗躏。
- 提交更新,將保存在暫存區(qū)域的文件快照永久轉(zhuǎn)儲到 Git 目錄中昔脯。
配置
用戶信息
第一個(gè)要配置的是你個(gè)人的用戶名稱和電子郵件地址瑟捣。這兩條配置很重要,每次 Git 提交時(shí)都會引用這兩條信息栅干,說明是誰提交了更新迈套,所以會隨更新內(nèi)容一起被永久納入歷史記錄:
$ git config --global user.name "John Doe"
$ git config --global user.email johndoe@example.com
如果用了 --global 選項(xiàng),那么更改的配置文件就是位于你用戶主目錄下的那個(gè)碱鳞,以后你所有的項(xiàng)目都會默認(rèn)使用這里配置的用戶信息桑李。如果要在某個(gè)特定的項(xiàng)目中使用其他名字或者電郵,只要去掉 --global 選項(xiàng)重新配置即可窿给,新的設(shè)定保存在當(dāng)前項(xiàng)目的 .git/config 文件里贵白。
文本編輯器
接下來要設(shè)置的是默認(rèn)使用的文本編輯器。Git 需要你輸入一些額外消息的時(shí)候崩泡,會自動調(diào)用一個(gè)外部文本編輯器給你用禁荒。默認(rèn)會使用操作系統(tǒng)指定的默認(rèn)編輯器,一般可能會是 Vi 或者 Vim角撞。如果你有其他偏好呛伴,比如 Emacs 的話,可以重新設(shè)置:
$ git config --global core.editor emacs
差異分析工具
還有一個(gè)比較常用的是谒所,在解決合并沖突時(shí)使用哪種差異分析工具热康。比如要改用 vimdiff 的話:
$ git config --global merge.tool vimdiff
查看配置信息
要檢查已有的配置信息,可以使用 git config --list 命令
基礎(chǔ)
初始化倉庫:git init
從現(xiàn)有倉庫克铝恿臁:git clone [url]
工作目錄操作
工作目錄下面的所有文件只有兩種狀態(tài):已跟蹤或未跟蹤姐军。
初次克隆某個(gè)倉庫時(shí),工作目錄中的所有文件都屬于已跟蹤文件尖淘,且狀態(tài)為未修改奕锌。
在編輯過某些文件之后,Git 將這些文件標(biāo)為已修改村生。我們逐步把這些修改過的文件放到暫存區(qū)域惊暴,直到最后一次性提交所有這些暫存起來的文件,如此重復(fù)梆造。
檢查當(dāng)前文件狀態(tài)
git status
跟蹤新文件&暫存已修改文件
使用命令 git add
開始跟蹤一個(gè)新文件:
git add README
此時(shí)再運(yùn)行 git status
命令缴守,會看到 README 文件已被跟蹤葬毫,并處于暫存狀態(tài):
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: README
只要在 “Changes to be committed” 這行下面的,就說明是已暫存狀態(tài)屡穗。如果此時(shí)提交贴捡,那么該文件此時(shí)此刻的版本將被留存在歷史記錄中。
git add
命令還可以將修改過的文件添加到暫存區(qū)村砂。
忽略某些文件
創(chuàng)建一個(gè)名為 .gitignore
的文件在根目錄下烂斋,在其中設(shè)置忽略文件的數(shù)據(jù):
- 所有空行或者以注釋符號 # 開頭的行都會被 Git 忽略。
- 可以使用標(biāo)準(zhǔn)的 glob 模式匹配础废。
- 匹配模式最后跟反斜杠(/)說明要忽略的是目錄汛骂。
- 要忽略指定模式以外的文件或目錄,可以在模式前加上驚嘆號(!)取反评腺。
# 此為注釋 – 將被 Git 忽略
# 忽略所有 .a 結(jié)尾的文件
*.a
# 但 lib.a 除外
!lib.a
# 僅僅忽略項(xiàng)目根目錄下的 TODO 文件帘瞭,不包括 subdir/TODO
/TODO
# 忽略 build/ 目錄下的所有文件
build/
# 會忽略 doc/notes.txt 但不包括 doc/server/arch.txt
doc/*.txt
# ignore all .txt files in the doc/ directory
doc/**/*.txt
所謂的 glob 模式是指 shell 所使用的簡化了的正則表達(dá)式。星號(*)匹配零個(gè)或多個(gè)任意字符蒿讥;[abc] 匹配任何一個(gè)列在方括號中的字符(這個(gè)例子要么匹配一個(gè) a蝶念,要么匹配一個(gè) b,要么匹配一個(gè) c)芋绸;問號(?)只匹配一個(gè)任意字符媒殉;如果在方括號中使用短劃線分隔兩個(gè)字符,表示所有在這兩個(gè)字符范圍內(nèi)的都可以匹配(比如 [0-9] 表示匹配所有 0 到 9 的數(shù)字)摔敛。
查看已暫存和未暫存的更新
git status
的顯示比較簡單廷蓉,僅僅是列出了修改過的文件,如果要查看具體修改了什么地方马昙,可以用git diff
命令桃犬。
此命令比較的是工作目錄中當(dāng)前文件和暫存區(qū)域快照之間的差異,也就是修改之后還沒有暫存起來的變化內(nèi)容给猾。
若要看已經(jīng)暫存起來的文件和上次提交時(shí)的快照之間的差異疫萤,可以用 git diff --cached
命令。
請注意敢伸,單單git diff
不過是顯示還沒有暫存起來的改動,而不是這次工作和上次提交之間的差異恒削。所以有時(shí)候你一下子暫存了所有更新過的文件后池颈,運(yùn)行git diff
后卻什么也沒有,就是這個(gè)原因钓丰。
提交更新
git commit -m "..."
提交時(shí)記錄的是放在暫存區(qū)域的快照躯砰,任何還未暫存的仍然保持已修改狀態(tài),可以在下次提交時(shí)納入版本管理携丁。每一次運(yùn)行提交操作琢歇,都是對你項(xiàng)目作一次快照兰怠,以后可以回到這個(gè)狀態(tài),或者進(jìn)行比較李茫。
跳過使用暫存區(qū)域
盡管使用暫存區(qū)域的方式可以精心準(zhǔn)備要提交的細(xì)節(jié)揭保,但有時(shí)候這么做略顯繁瑣。Git 提供了一個(gè)跳過使用暫存區(qū)域的方式魄宏,只要在提交的時(shí)候秸侣,給 git commit
加上 -a
選項(xiàng),Git 就會自動把所有已經(jīng)跟蹤過的文件暫存起來一并提交宠互,從而跳過git add
步驟:
git commit -a -m "..."
移除文件
要從 Git 中移除某個(gè)文件味榛,就必須要從已跟蹤文件清單中移除(確切地說,是從暫存區(qū)域移除)予跌,然后提交搏色。
先在文件系統(tǒng)中刪除該文件,然后在 Git 中移除 git rm log.txt
券册。
如果刪除之前修改過并且已經(jīng)放到暫存區(qū)域的話频轿,則必須要用強(qiáng)制刪除選項(xiàng) -f(即 force 的首字母),以防誤刪除文件后丟失修改的內(nèi)容汁掠。
另外一種情況是略吨,我們想把文件從 Git 倉庫中刪除(亦即從暫存區(qū)域移除),但仍然希望保留在當(dāng)前工作目錄中考阱。換句話說翠忠,僅是從跟蹤清單中刪除。比如一些大型日志文件或者一堆 .a 編譯文件乞榨,不小心納入倉庫后秽之,要移除跟蹤但不刪除文件,以便稍后在 .gitignore
文件中補(bǔ)上吃既,用 --cached
選項(xiàng)即可:
$ git rm --cached readme.txt
后面可以列出文件或者目錄的名字考榨,也可以使用 glob 模式。比方說:
$ git rm log/\*.log
注意到星號 * 之前的反斜杠 \鹦倚,因?yàn)?Git 有它自己的文件模式擴(kuò)展匹配方式河质,所以我們不用 shell 來幫忙展開。
$ git rm \*~
會遞歸刪除當(dāng)前目錄及其子目錄中所有 ~ 結(jié)尾的文件震叙。
移動文件
$ git mv file_from file_to
其實(shí)掀鹅,運(yùn)行 git mv 就相當(dāng)于運(yùn)行了下面三條命令:
$ mv README.txt README
$ git rm README.txt
$ git add README
查看提交歷史
git log
git log 有許多選項(xiàng)可以幫助你搜尋感興趣的提交:
git log -p -2
-p
選項(xiàng)展開顯示每次提交的內(nèi)容差異,用 -2
則僅顯示最近的兩次更新媒楼。
單詞層面的對比:--word-diff
上下文( context )行數(shù)從默認(rèn)的 3 行乐尊,減為 1 行:-U1
僅顯示簡要的增改行數(shù)統(tǒng)計(jì):--stat
指定使用完全不同于默認(rèn)格式的方式展示提交歷史:-pretty=oneline
還有 short,full 和 fuller 參數(shù)可以用
用 oneline 或 format 時(shí)結(jié)合 --graph
選項(xiàng)划址,可以看到開頭多出一些 ASCII 字符串表示的簡單圖形扔嵌。
撤銷操作
任何時(shí)候限府,都有可能需要撤消剛才所做的某些操作。請注意痢缎,有些撤銷操作是不可逆的胁勺,所以請務(wù)必謹(jǐn)慎小心,一旦失誤牺弄,就有可能丟失部分工作成果姻几。
修改最后一次提交
可以使用 --amend 選項(xiàng)重新提交:
$ git commit --amend
實(shí)例,如果剛才提交時(shí)忘了暫存某些修改势告,可以先補(bǔ)上暫存操作蛇捌,然后再運(yùn)行 --amend 提交:
$ git commit -m 'initial commit'
$ git add forgotten_file
$ git commit --amend
上面的三條命令最終只是產(chǎn)生一個(gè)提交,第二個(gè)提交命令修正了第一個(gè)的提交內(nèi)容咱台。
取消已經(jīng)暫存的文件
$ git reset HEAD <file>
文件回到已修改未暫存狀態(tài)
取消對文件的修改
$ git checkout -- <file>
遠(yuǎn)程倉庫使用
查看當(dāng)前遠(yuǎn)程倉庫
$ git remote
在克隆完某個(gè)項(xiàng)目后络拌,至少可以看到一個(gè)名為 origin
的遠(yuǎn)程庫,Git 默認(rèn)使用這個(gè)名字來標(biāo)識你所克隆的原始倉庫:
也可以加上 -v
選項(xiàng)(此為 --verbose
的簡寫回溺,取首字母)春贸,顯示對應(yīng)的克隆地址:
$ git remote -v
origin git://github.com/schacon/ticgit.git (fetch)
origin git://github.com/schacon/ticgit.git (push)
添加遠(yuǎn)程倉庫
git remote add [shortname] [url]
$ git remote add pb git://github.com/paulboone/ticgit.git
$ git remote -v
origin git://github.com/schacon/ticgit.git
pb git://github.com/paulboone/ticgit.git
現(xiàn)在可以用字符串 pb 指代對應(yīng)的倉庫地址了。比如說遗遵,要抓取所有 Paul 有的萍恕,但本地倉庫沒有的信息,可以運(yùn)行 git fetch pb
:
$ git fetch pb
remote: Counting objects: 58, done.
remote: Compressing objects: 100% (41/41), done.
remote: Total 44 (delta 24), reused 1 (delta 0)
Unpacking objects: 100% (44/44), done.
From git://github.com/paulboone/ticgit
* [new branch] master -> pb/master
* [new branch] ticgit -> pb/ticgit
現(xiàn)在车要,Paul 的主干分支(master)已經(jīng)完全可以在本地訪問了允粤,對應(yīng)的名字是 pb/master
,你可以將它合并到自己的某個(gè)分支翼岁,或者切換到這個(gè)分支类垫,看看有些什么有趣的更新。
從遠(yuǎn)程倉庫抓取數(shù)據(jù)
$ git fetch [remote-name]
此命令會到遠(yuǎn)程倉庫中拉取所有你本地倉庫中還沒有的數(shù)據(jù)琅坡。運(yùn)行完成后悉患,你就可以在本地訪問該遠(yuǎn)程倉庫中的所有分支,將其中某個(gè)分支合并到本地榆俺,或者取出某個(gè)分支售躁,一探究竟。
如果是克隆了一個(gè)倉庫茴晋,此命令會自動將遠(yuǎn)程倉庫歸于 origin 名下迂求。所以,git fetch origin 會抓取從你上次克隆以來別人上傳到此遠(yuǎn)程倉庫中的所有更新(或是上次 fetch 以來別人提交的更新)晃跺。有一點(diǎn)很重要,需要記住毫玖,fetch 命令只是將遠(yuǎn)端的數(shù)據(jù)拉到本地倉庫掀虎,并不自動合并到當(dāng)前工作分支凌盯,只有當(dāng)你確實(shí)準(zhǔn)備好了,才能手工合并烹玉。
如果設(shè)置了某個(gè)分支用于跟蹤某個(gè)遠(yuǎn)端倉庫的分支(參見下節(jié)及第三章的內(nèi)容)驰怎,可以使用 git pull
命令自動抓取數(shù)據(jù)下來,然后將遠(yuǎn)端分支自動合并到本地倉庫中當(dāng)前分支二打。在日常工作中我們經(jīng)常這么用县忌,既快且好。實(shí)際上继效,默認(rèn)情況下 git clone
命令本質(zhì)上就是自動創(chuàng)建了本地的 master 分支用于跟蹤遠(yuǎn)程倉庫中的 master 分支(假設(shè)遠(yuǎn)程倉庫確實(shí)有 master 分支)症杏。所以一般我們運(yùn)行 git pull
,目的都是要從原始克隆的遠(yuǎn)端倉庫中抓取數(shù)據(jù)后瑞信,合并到工作目錄中的當(dāng)前分支厉颤。
推送數(shù)據(jù)到遠(yuǎn)程倉庫
項(xiàng)目進(jìn)行到一個(gè)階段,要同別人分享目前的成果凡简,可以將本地倉庫中的數(shù)據(jù)推送到遠(yuǎn)程倉庫逼友。實(shí)現(xiàn)這個(gè)任務(wù)的命令很簡單: git push [remote-name] [branch-name]
。如果要把本地的 master 分支推送到 origin 服務(wù)器上(再次說明下秤涩,克隆操作會自動使用默認(rèn)的 master 和 origin 名字)帜乞,可以運(yùn)行下面的命令:
$ git push origin master
只有在所克隆的服務(wù)器上有寫權(quán)限,或者同一時(shí)刻沒有其他人在推數(shù)據(jù)筐眷,這條命令才會如期完成任務(wù)黎烈。如果在你推數(shù)據(jù)前,已經(jīng)有其他人推送了若干更新浊竟,那你的推送操作就會被駁回怨喘。必須先把他們的更新抓取到本地,合并到自己的項(xiàng)目中振定,然后才可以再次推送必怜。
查看遠(yuǎn)程倉庫信息
通過命令 git remote show [remote-name]
查看某個(gè)遠(yuǎn)程倉庫的詳細(xì)信息,比如要看所克隆的 origin 倉庫后频,可以運(yùn)行:
$ git remote show origin
* remote origin
URL: git://github.com/schacon/ticgit.git
Remote branch merged with 'git pull' while on branch master
master
Tracked remote branches
master
ticgit
除了對應(yīng)的克隆地址外梳庆,它還給出了許多額外的信息。它友善地告訴你如果是在 master
分支卑惜,就可以用 git pull
命令抓取數(shù)據(jù)合并到本地膏执。另外還列出了所有處于跟蹤狀態(tài)中的遠(yuǎn)端分支。
遠(yuǎn)程倉庫的刪除和重命名
git remote rename pb paul
git remote rm paul
Git 分支
為了理解 Git 分支的實(shí)現(xiàn)方式露久,我們需要理解 Git 是如何儲存數(shù)據(jù)的更米。
Git 保持的不是文件差異或者變化量,而是一系列文件快照
在 Git 中提交時(shí)毫痕,會保存一個(gè)提交(commit)對象征峦,該對象包含一個(gè)指向暫存內(nèi)容快照的指針迟几,包含本次提交的作者等相關(guān)附屬信息,包含零個(gè)或多個(gè)指向該提交對象的父對象指針:首次提交是沒有直接祖先的栏笆,普通提交有一個(gè)祖先类腮,由兩個(gè)或多個(gè)分支合并產(chǎn)生的提交則有多個(gè)祖先。
當(dāng)使用 git commit
新建一個(gè)提交對象時(shí)蛉加,Git 會先計(jì)算每一個(gè)子目錄的校驗(yàn)和蚜枢,然后在 Git 倉庫中將這些目錄保存為樹(tree)對象。之后 Git 創(chuàng)建的提交對象针饥,除了包含相關(guān)提交信息以外厂抽,還包含著指向這個(gè)樹對象(項(xiàng)目根目錄)的指針,如此它就可以在將來需要的時(shí)候打厘,重現(xiàn)此次快照的內(nèi)容了修肠。
單個(gè)提交對象在倉庫中的數(shù)據(jù)結(jié)構(gòu)
作些修改后再次提交,那么這次的提交對象會包含一個(gè)指向上次提交對象的指針户盯。
多個(gè)提交對象之間的鏈接關(guān)系
現(xiàn)在來談?wù)劮种妒珿it 中的分支,本質(zhì)上僅僅是個(gè)指向提交對象的可變指針莽鸭,默認(rèn)的分支名字為 master吗伤。
分支其實(shí)就是從某個(gè)提交對象往回看的歷史
創(chuàng)建分支
$ git branch testing
會在當(dāng)前提交對象上新建一個(gè)分支指針
Git 如何知道當(dāng)前在哪個(gè)分支上工作:很簡單,Git 內(nèi)部有個(gè)名為 HEAD 的特別指針硫眨,指向正在工作中的本地分支的指針足淆,也就是傳說中的指向指針的指針。
如何切換分支:
git checkout testing
這樣 HEAD 就指向了 testing 分支
在 testing 分支上的提交會創(chuàng)建一個(gè)新的提交對象礁阁,而 master 依舊指向原來的提交對象巧号。
由于 Git 中的分支實(shí)際上僅是一個(gè)包含所指對象校驗(yàn)和(40 個(gè)字符長度 SHA-1 字串)的文件,所以創(chuàng)建和銷毀一個(gè)分支就變得非常廉價(jià)姥闭。說白了丹鸿,新建一個(gè)分支就是向一個(gè)文件寫入 41 個(gè)字節(jié)(外加一個(gè)換行符)那么簡單,當(dāng)然也就很快了棚品。
這和大多數(shù)版本控制系統(tǒng)形成了鮮明對比靠欢,它們管理分支大多采取備份所有項(xiàng)目文件到特定目錄的方式,所以根據(jù)項(xiàng)目文件數(shù)量和大小不同铜跑,可能花費(fèi)的時(shí)間也會有相當(dāng)大的差別门怪,快則幾秒,慢則數(shù)分鐘锅纺。而 Git 的實(shí)現(xiàn)與項(xiàng)目復(fù)雜度無關(guān)掷空,它永遠(yuǎn)可以在幾毫秒的時(shí)間內(nèi)完成分支的創(chuàng)建和切換。同時(shí),因?yàn)槊看翁峤粫r(shí)都記錄了祖先信息(parent 指針)拣帽,將來要合并分支時(shí)疼电,尋找恰當(dāng)?shù)暮喜⒒A(chǔ)(共同祖先)的工作其實(shí)已經(jīng)自然而然地?cái)[在那里了,所以實(shí)現(xiàn)起來非常容易减拭。Git 鼓勵(lì)開發(fā)者頻繁使用分支,正是因?yàn)橛兄@些特性作保障区丑。
合并分支
一個(gè)簡單的分支與合并的例子拧粪,實(shí)際工作中也會用到這樣的工作流程:
- 開發(fā)某個(gè)網(wǎng)站;
- 為實(shí)現(xiàn)某個(gè)新需求沧侥,創(chuàng)建一個(gè)分支可霎;
- 在這個(gè)分支上開展工作。
假設(shè)此時(shí)宴杀,你突然接到一個(gè)電話說有個(gè)很嚴(yán)重的問題需要緊急修補(bǔ)癣朗,那么可以按照下面的方式處理: - 返回到原先已經(jīng)發(fā)布到生產(chǎn)服務(wù)器上到分支;
- 為這次緊急修補(bǔ)創(chuàng)建一個(gè)新分支旺罢,并在其中修復(fù)問題旷余;
- 通過測試后,回到生產(chǎn)服務(wù)器所在的分支扁达,將修補(bǔ)分支合并進(jìn)來正卧,然后再推送到生產(chǎn)服務(wù)器上;
- 切換到之前實(shí)現(xiàn)新需求的分支跪解,繼續(xù)工作炉旷。
$ git checkout -b iss53
相當(dāng)于執(zhí)行下面這兩條命令:
$ git branch iss53
$ git checkout iss53
$ git checkout master
切換到生產(chǎn)服務(wù)器分支
$ git checkout -b hotfix
$ git checkout master
回到master分支
$ git merge hotfix
合并進(jìn)來
Updating f42c576..3a0874c
Fast-forward
README | 1 -
1 file changed, 1 deletion(-)
合并時(shí)出現(xiàn)了“Fast forward”的提示。由于當(dāng)前 master 分支所在的提交對象是要并入的 hotfix 分支的直接上游叉讥,Git 只需把 master 分支指針直接右移窘行。換句話說,如果順著一個(gè)分支走下去可以到達(dá)另一個(gè)分支的話图仓,那么 Git 在合并兩者時(shí)罐盔,只會簡單地把指針右移,因?yàn)檫@種單線的歷史分支不存在任何需要解決的分歧透绩,所以這種合并過程可以稱為快進(jìn)(Fast forward)翘骂。
現(xiàn)在最新的修改已經(jīng)在當(dāng)前 master 分支所指向的提交對象中了,可以部署到生產(chǎn)服務(wù)器上去了
master 分支和 hotfix 分支指向同一位置
在那個(gè)超級重要的修補(bǔ)發(fā)布以后帚豪,你想要回到被打擾之前的工作碳竟。由于當(dāng)前 hotfix 分支和 master 都指向相同的提交對象,所以 hotfix 已經(jīng)完成了歷史使命狸臣,可以刪掉了莹桅。
$ git branch -d hotfix
Deleted branch hotfix (was 3a0874c).
現(xiàn)在回到之前未完成的 iss53 問題修復(fù)分支上繼續(xù)工作
$ git checkout iss53
Switched to branch 'iss53'
$ vim index.html
$ git commit -a -m 'finished the new footer [issue 53]'
[iss53 ad82d7a] finished the new footer [issue 53]
1 file changed, 1 insertion(+)
值得注意的是之前 hotfix 分支的修改內(nèi)容尚未包含到 iss53 中來。如果需要納入此次修補(bǔ),可以用 git merge master 把 master 分支合并到 iss53诈泼;或者等 iss53 完成之后懂拾,再將 iss53 分支中的更新并入 master。
$ git checkout master
$ git merge iss53
Auto-merging README
Merge made by the 'recursive' strategy.
README | 1 +
1 file changed, 1 insertion(+)
這次合并操作的底層實(shí)現(xiàn)铐达,并不同于之前 hotfix 的并入方式岖赋。因?yàn)檫@次你的開發(fā)歷史是從更早的地方開始分叉的。由于當(dāng)前 master 分支所指向的提交對象(C4)并不是 iss53 分支的直接祖先瓮孙,Git 不得不進(jìn)行一些額外處理唐断。就此例而言,Git 會用兩個(gè)分支的末端(C4 和 C5)以及它們的共同祖先(C2)進(jìn)行一次簡單的三方合并計(jì)算杭抠。
Git 為分支合并自動識別出最佳的同源合并點(diǎn)脸甘。
這次,Git 沒有簡單地把分支指針右移偏灿,而是對三方合并后的結(jié)果重新做一個(gè)新的快照丹诀,并自動創(chuàng)建一個(gè)指向它的提交對象(C6)。這個(gè)提交對象比較特殊翁垂,它有兩個(gè)祖先(C4 和 C5)铆遭。
既然之前的工作成果已經(jīng)合并到 master 了,那么 iss53 也就沒用了沮峡。你可以就此刪除它疚脐,并在問題追蹤系統(tǒng)里關(guān)閉該問題。
$ git branch -d iss53
遇到?jīng)_突時(shí)到分支合并
有時(shí)候合并操作并不會如此順利邢疙。如果在不同的分支中都修改了同一個(gè)文件的同一部分棍弄,Git 就無法干凈地把兩者合到一起。如果你在解決問題 #53 的過程中修改了 hotfix 中修改的部分疟游,將得到類似下面的結(jié)果:
$ 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 作了合并呼畸,但沒有提交,它會停下來等你解決沖突颁虐。要看看哪些文件在合并時(shí)發(fā)生沖突蛮原,可以用 git status
查閱:
$ 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")
任何包含未解決沖突的文件都會以未合并(unmerged)的狀態(tài)列出。Git 會在有沖突的文件里加入標(biāo)準(zhǔn)的沖突解決標(biāo)記另绩,可以通過它們來手工定位并解決這些沖突儒陨。可以看到此文件包含類似下面這樣的部分:
<<<<<<< HEAD
<div id="footer">contact : email.support@github.com</div>
=======
<div id="footer">
please contact us at support@github.com
</div>
>>>>>>> iss53
可以看到 ======= 隔開的上半部分笋籽,是 HEAD(即 master 分支蹦漠,在運(yùn)行 merge 命令時(shí)所切換到的分支)中的內(nèi)容,下半部分是在 iss53 分支中的內(nèi)容车海。解決沖突的辦法無非是二者選其一或者由你親自整合到一起笛园。比如你可以通過把這段內(nèi)容替換為下面這樣來解決:
<div id="footer">
please contact us at email.support@github.com
</div>
這個(gè)解決方案各采納了兩個(gè)分支中的一部分內(nèi)容,而且還刪除了 <<<<<<<,======= 和 >>>>>>> 這些行研铆。在解決了所有文件里的所有沖突后埋同,運(yùn)行 git add
將把它們標(biāo)記為已解決狀態(tài)(譯注:實(shí)際上就是來一次快照保存到暫存區(qū)域。)棵红。因?yàn)橐坏捍嫘琢蓿捅硎緵_突已經(jīng)解決。如果你想用一個(gè)有圖形界面的工具來解決這些問題窄赋,不妨運(yùn)行 git mergetool
哟冬,它會調(diào)用一個(gè)可視化的合并工具并引導(dǎo)你解決所有沖突,再運(yùn)行一次 git status 來確認(rèn)所有沖突都已解決:
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: index.html
如果覺得滿意了忆绰,并且確認(rèn)所有沖突都已解決,也就是進(jìn)入了暫存區(qū)可岂,就可以用 git commit
來完成這次合并提交错敢。
如果想給將來看這次合并的人一些方便,可以修改該信息缕粹,提供更多合并細(xì)節(jié)稚茅。比如你都作了哪些改動,以及這么做的原因平斩。有時(shí)候裁決沖突的理由并不直接或明顯亚享,有必要略加注解。
管理分支
列出當(dāng)前分支清單:git branch
查看各個(gè)分支最后一個(gè)提交對象的信息:git branch -v
查看哪些分支已被并入當(dāng)前分支:git branch --merged
分布式工作流程
一般這種情況有個(gè)官方發(fā)布的項(xiàng)目倉庫绘面,開發(fā)者由此倉庫克隆出一個(gè)自己的公共倉庫欺税,然后將自己的提交推送上去,請求官方倉庫的維護(hù)者拉取更新合并到主項(xiàng)目揭璃。維護(hù)者在自己本地也有個(gè)克隆倉庫晚凿,可以將你的公共倉庫作為遠(yuǎn)程倉庫添加進(jìn)來,經(jīng)過測試無誤后合并到主干分支瘦馍,在推送到官方倉庫歼秽。
- 項(xiàng)目維護(hù)者可以推送數(shù)據(jù)到公共倉庫 blessed repository。
- 貢獻(xiàn)者克隆此倉庫情组,修訂或編寫新代碼燥筷。
- 貢獻(xiàn)者推送數(shù)據(jù)到自己的公共倉庫 developer public。
- 貢獻(xiàn)者給維護(hù)者發(fā)送郵件院崇,請求拉取自己的最新修訂肆氓。
- 維護(hù)者在自己本地的 integration manger 倉庫中,將貢獻(xiàn)者的倉庫加為遠(yuǎn)程倉庫亚脆,合并更新并做測試做院。
- 維護(hù)者將合并后的更新推送到主倉庫 blessed repository。