一些常用的Git 知識(shí)點(diǎn)整理
1. Git基本概念
- repository
- config
- init
- clone
- fetch
- pull
- commit
- push
- branch
- head
- tag
- merge
- conflict
- diff
- log
- show
- status
2. Git工作空間和文件狀態(tài)
(1).工作空間
左側(cè)為工作區(qū),右側(cè)為版本庫(kù)。
- 工作區(qū)(Working Directory) 就是在電腦里能看到的目錄,比如learngit文件夾就是一個(gè)工作區(qū)。
- 版本庫(kù)(Repository)工作區(qū)有一個(gè)隱藏目錄.git,是Git的版本庫(kù)。
在版本庫(kù)中標(biāo)記為index的區(qū)域?yàn)闀捍鎱^(qū),標(biāo)記為master的是Git為我們自動(dòng)創(chuàng)建的第一個(gè)分支,代表的是目錄樹(shù)。此時(shí)HEAD實(shí)際是指向master分支的一個(gè)“游標(biāo)”,所以圖示的命令中出現(xiàn)HEAD的地方可以用master來(lái)替換。圖中的objects標(biāo)識(shí)的區(qū)域?yàn)間it的對(duì)象庫(kù),實(shí)際位于.git/objects目錄下。
- 當(dāng)對(duì)工作區(qū)修改(或新增)的文件執(zhí)行g(shù)it add命令時(shí),暫存區(qū)的目錄樹(shù)會(huì)被更新,同時(shí)工作區(qū)修改(或新增)的文件內(nèi)容會(huì)被寫入到對(duì)象庫(kù)中的一個(gè)新的對(duì)象中,而該對(duì)象的id被記錄在暫存區(qū)的文件索引中。
- 當(dāng)執(zhí)行提交操作git commit時(shí),暫存區(qū)的目錄樹(shù)會(huì)寫到版本庫(kù)(對(duì)象庫(kù))中,master分支會(huì)做相應(yīng)的更新,即master最新指向的目錄樹(shù)就是提交時(shí)原暫存區(qū)的目錄樹(shù)。
- 當(dāng)執(zhí)行g(shù)it reset HEAD命令時(shí),暫存區(qū)的目錄樹(shù)會(huì)被重寫,會(huì)被master分支指向的目錄樹(shù)所替換,但是工作區(qū)不受影響。
- 當(dāng)執(zhí)行g(shù)it rm --cached命令時(shí),會(huì)直接從暫存區(qū)刪除文件,工作區(qū)則不做出改變。
- 當(dāng)執(zhí)行g(shù)it checkout .或git checkout --命令時(shí),會(huì)用暫存區(qū)全部的文件或指定的文件替換工作區(qū)的文件。這個(gè)操作很危險(xiǎn),會(huì)清楚工作區(qū)中未添加到暫存區(qū)的改動(dòng)。
- 當(dāng)執(zhí)行g(shù)it checkout HEAD .或git checkout HEAD命令時(shí),會(huì)用HEAD指向的master分支中的全部或部分文件替換暫存區(qū)和工作區(qū)中的文件。這個(gè)命令也是極度危險(xiǎn)的。因?yàn)椴坏珪?huì)清楚工作區(qū)中未提交的改動(dòng),也會(huì)清楚暫存區(qū)中未提交的改動(dòng)。
(1).文件狀態(tài)
Git 有三種狀態(tài),你的文件可能處于其中之一:已提交(committed)、已修改(modified)和已暫存(staged)。
3. Git配置系統(tǒng)級(jí)、全局、當(dāng)前倉(cāng)庫(kù)用戶名、郵箱的命令
系統(tǒng)級(jí)、全局、當(dāng)前倉(cāng)庫(kù)選項(xiàng)分別是:倉(cāng)庫(kù)-system、-global、-local(或默認(rèn)不填)
- git config --global user.name "Jerry Mouse"
- git config --global user.email "jerry@yiibai.com"
列出Git設(shè)置
- git config --list
- git config -l
4. Git fetch和pull的區(qū)別
- git fetch:相當(dāng)于是從遠(yuǎn)程獲取最新版本到本地,不會(huì)自動(dòng)merge.
- git pull:相當(dāng)于是從遠(yuǎn)程獲取最新版本并merge到本地.
(1). git fetch示例:
- Git fetch origin master
- git log -p master..origin/master
- git merge origin/master
以上命令的含義:
- 首先從遠(yuǎn)程的origin的master主分支下載最新的版本到origin/master分支上
- 然后比較本地的master分支和origin/master分支的差別
- 最后進(jìn)行合并
- 上述過(guò)程其實(shí)可以用以下更清晰的方式來(lái)進(jìn)行:
(1). git pull示例:
- git pull origin master
上述命令其實(shí)相當(dāng)于git fetch和git merge。在實(shí)際使用中,git fetch更安全一些,因?yàn)樵趍erge前,我們可以查看更新情況,然后再?zèng)Q定是否合并。
5. Git reset和revert的卻別
- git revert是用一次新的commit來(lái)回滾之前的commit,git reset是直接刪除指定的commit。
- 在回滾這一操作上看,效果差不多。但是在日后繼續(xù)merge以前的老版本時(shí)有區(qū)別。因?yàn)間it revert是用一次逆向的commit“中和”之前的提交,因此日后合并老的branch時(shí),導(dǎo)致這部分改變不會(huì)再次出現(xiàn),但是git reset是之間把某些commit在某個(gè)branch上刪除,因而和老的branch再次merge時(shí),這些被回滾的commit應(yīng)該還會(huì)被引入。
- git reset是把HEAD向后移動(dòng)了一下,而git revert是HEAD繼續(xù)前進(jìn),只是新的commit的內(nèi)容和要revert的內(nèi)容正好相反,能夠抵消要被revert的內(nèi)容。
- git revert與git reset最大的不同是,git revert 僅僅是撤銷某次提交。
另外,說(shuō)一下git revert, git reset –hard和 –soft的區(qū)別
- git reset –mixed id: 是將git的HEAD變了(也就是提交記錄變了),但文件并沒(méi)有改變,(也就是working tree并沒(méi)有改變)。
- git reset –soft id: 實(shí)際上,是git reset –mixed id后,又做了一次git add。
- git reset –herd id: 是將git的HEAD變了,文件也變了。
6. Git merge和reabse的相同點(diǎn)和不同點(diǎn)
merge是合并的意思,rebase是復(fù)位基底的意思,相同點(diǎn)都是用來(lái)合并分支的。
不同點(diǎn):
- merge操作會(huì)生成一個(gè)新的節(jié)點(diǎn),之前的提交分開(kāi)顯示。而rebase操作不會(huì)生成新的節(jié)點(diǎn),是將兩個(gè)分支融合成一個(gè)線性的提交。
- 解決沖突時(shí)。merge操作遇到?jīng)_突的時(shí)候,當(dāng)前merge不能繼續(xù)進(jìn)行下去。手動(dòng)修改沖突內(nèi)容后,add 修改,commit就可以了。而rebase操作的話,會(huì)中斷rebase,同時(shí)會(huì)提示去解決沖突。解決沖突后,將修改add后執(zhí)行g(shù)it rebase –continue繼續(xù)操作,或者git rebase –skip忽略沖突。
- git pull和git pull --rebase區(qū)別:git pull做了兩個(gè)操作分別是”獲取”和”合并”。所以加了rebase就是以rebase的方式進(jìn)行合并分支,默認(rèn)為merge。
總結(jié):選擇 merge 還是 rebase?
- merge 是一個(gè)合并操作,會(huì)將兩個(gè)分支的修改合并在一起,默認(rèn)操作的情況下會(huì)提交合并中修改的內(nèi)容
- merge 的提交歷史忠實(shí)地記錄了實(shí)際發(fā)生過(guò)什么,關(guān)注點(diǎn)在真實(shí)的提交歷史上面
- rebase 并沒(méi)有進(jìn)行合并操作,只是提取了當(dāng)前分支的修改,將其復(fù)制在了目標(biāo)分支的最新提交后面
- rebase 的提交歷史反映了項(xiàng)目過(guò)程中發(fā)生了什么,關(guān)注點(diǎn)在開(kāi)發(fā)過(guò)程上面
- merge 與 rebase 都是非常強(qiáng)大的分支整合命令,沒(méi)有優(yōu)劣之分,使用哪一個(gè)應(yīng)由項(xiàng)目和團(tuán)隊(duì)的開(kāi)發(fā)需求決定
- merge 和 rebase 還有很多強(qiáng)大的選項(xiàng),可以使用 git help 查看
7. Git stash是什么?它的相關(guān)使用方式命令
- git stash: 備份當(dāng)前的工作區(qū)的內(nèi)容,從最近的一次提交中讀取相關(guān)內(nèi)容,讓工作區(qū)保證和上次提交的內(nèi)容一致。同時(shí),將當(dāng)前的工作區(qū)內(nèi)容保存到Git棧中。
- git stash pop: 從Git棧中讀取最近一次保存的內(nèi)容,恢復(fù)工作區(qū)的相關(guān)內(nèi)容。由于可能存在多個(gè)Stash的內(nèi)容,所以用棧來(lái)管理,pop會(huì)從最近的一個(gè)stash中讀取內(nèi)容并恢復(fù)。
- git stash pop –index stash@{0}: 恢復(fù)編號(hào)為0的進(jìn)度的工作區(qū)和暫存區(qū)。
- git stash apply stash@{1} 以將你指定版本號(hào)為stash@{1}的工作取出來(lái)
- git stash drop[] 刪除某一個(gè)進(jìn)度,默認(rèn)刪除最新進(jìn)度
- git stash list: 顯示Git棧內(nèi)的所有備份,可以利用這個(gè)列表來(lái)決定從那個(gè)地方恢復(fù)。
- git stash clear: 清空Git棧。此時(shí)使用gitg等圖形化工具會(huì)發(fā)現(xiàn),原來(lái)stash的哪些節(jié)點(diǎn)都消失了
- # 恢復(fù)工作進(jìn)度
- git stash pop [--index] [<stash>]
- --index 參數(shù):不僅恢復(fù)工作區(qū),還恢復(fù)暫存區(qū)
- <stash> 指定恢復(fù)某一個(gè)具體進(jìn)度。如果沒(méi)有這個(gè)參數(shù),默認(rèn)恢復(fù)最新進(jìn)度
- # 這是git stash保存進(jìn)度的完整命令形式
- git stash [save message] [-k|--no-keep-index] [--patch]
- -k和--no-keep-index指定保存進(jìn)度后,是否重置暫存區(qū)
- --patch 會(huì)顯示工作區(qū)和HEAD的差異,通過(guò)編輯差異文件,排除不需要保存的內(nèi)容。和git add -p命令類似
- 使用save可以對(duì)進(jìn)度添加備注
- # git stash save "這是保存的進(jìn)度"
8. Git只從暫存區(qū)刪除,從工作空間刪除的命令分別是什么?
- git rm --cached
- git rm
- git commit
9. Git標(biāo)簽的使用
- # 列出現(xiàn)有的標(biāo)簽
- git tag
- # 打標(biāo)簽
- git tag -a v1.01 -m "Relase version 1.01"
- # 查看相應(yīng)標(biāo)簽的版本信息
- git show v1.4
- -a 選項(xiàng),創(chuàng)建一個(gè)含附注類型的標(biāo)簽
- -m 選項(xiàng),指定了對(duì)應(yīng)的標(biāo)簽說(shuō)明
9. Git分支的使用
- # 查看本地分支
- git branch
- # 查看遠(yuǎn)程分支
- git branch -r
- # 創(chuàng)建本地分支(注意新分支創(chuàng)建后不會(huì)自動(dòng)切換為當(dāng)前分支)
- git branch [name]
- # 切換分支
- git checkout [name]
- # 創(chuàng)建新分支并立即切換到新分支
- git checkout -b [name]
- # 強(qiáng)制刪除一個(gè)分支
- git branch -D [name]
- # 合并分支(將名稱為[name]的分支與當(dāng)前分支合并)
- git merge [name]
- # 查看各個(gè)分支最后提交信息
- git br -v
- # 查看已經(jīng)被合并到當(dāng)前分支的分支
- git br --merged
- # 查看尚未被合并到當(dāng)前分支的分支
- git br --no-merged
10. 介紹Git沖突處理經(jīng)驗(yàn),以及merge和rebase中的ours和theirs的差別。
merge和rebase對(duì)于ours和theirs的定義是完全相反的。在merge時(shí),ours指代的是當(dāng)前分支,theirs代表需要被合并的分支。而在rebase過(guò)程中,ours指向了修改參考分支,theirs卻是當(dāng)前分支。因?yàn)閞ebase 隱含了一個(gè)git checkout upstream的過(guò)程,將HEAD從local分支變成了upstream分支。git會(huì)在rebase結(jié)束后撤銷這個(gè)改變,但它已經(jīng)不可避免地影響了沖突的狀態(tài),使rebase中ours和theirs的定義與merge 截然相反。因此,在使用ours與theirs時(shí)請(qǐng)格外小心。
11. Git遠(yuǎn)程操作相關(guān)
(1). clone
git clone <版本庫(kù)的網(wǎng)址>
git clone <版本庫(kù)的網(wǎng)址> <本地目錄名>
- # 克隆jQuery的版本庫(kù)
- git clone https://github.com/jquery/jquery.git
- git clone -o jQuery https://github.com/jquery/jquery.git
(2). remote
- # 列出所有遠(yuǎn)程主機(jī)
- git remote
- # 使用-v選項(xiàng),可以參看遠(yuǎn)程主機(jī)的網(wǎng)址
- git remote -v
- # 可以查看該主機(jī)的詳細(xì)信息
- git remote show <主機(jī)名>
- # 添加遠(yuǎn)程主機(jī)
- git remote add <主機(jī)名> <網(wǎng)址>
- # 刪除遠(yuǎn)程主機(jī)
- git remote rm <主機(jī)名>
- # 修改遠(yuǎn)程主機(jī)名稱
- git remote rename <原主機(jī)名> <新主機(jī)名>
(3). fetch
- # 取回所有分支(branch)的更新到本地
- git fetch <遠(yuǎn)程主機(jī)名>
- # 取回某的特定分支的更新
- git fetch <遠(yuǎn)程主機(jī)名> <分支名>
- # 取回origin主機(jī)的master分支的更新
- git fetch origin master
- # 所取回的更新,在本地主機(jī)上要用”遠(yuǎn)程主機(jī)名/分支名”的形式讀取。比如origin主機(jī)的master,就要用origin/master讀取。可以使用git merge命令或者git rebase命令,在本地分支上合并遠(yuǎn)程分支
- git merge origin/master
- git rebase origin/master
(4). pull
git pull <遠(yuǎn)程主機(jī)名> <遠(yuǎn)程分支名>:<本地分支名>
- # 取回origin主機(jī)的next分支,與本地的master分支合并
- git pull origin next:master
- # 如果遠(yuǎn)程分支是與當(dāng)前分支合并,則冒號(hào)后面的部分可以省略。
- git pull origin next
- # 上面的命令實(shí)質(zhì)上等同于先做git fetch,再做git merge。
- git fetch origin
- git merge origin/next
- # 合并需要采用rebase模式
- git pull --rebase <遠(yuǎn)程主機(jī)名> <遠(yuǎn)程分支名>:<本地分支名>
(5). push
git push <遠(yuǎn)程主機(jī)名> <本地分支名>:<遠(yuǎn)程分支名>
注意:分支推送順序的寫法是”<來(lái)源地>:<目的地>”,所以git pull是”<遠(yuǎn)程分支>:<本地分支>”,而git push是”<本地分支>:<遠(yuǎn)程分支>”。
- 如果省略遠(yuǎn)程分支名,則表示將本地分支推送與之存在”追蹤關(guān)系”的遠(yuǎn)程分支(通常兩者同名),如果該遠(yuǎn)程分支不存在,則會(huì)被新建。
- 如果省略本地分支名,則表示刪除指定的遠(yuǎn)程分支,因?yàn)檫@等同于推送一個(gè)空的本地分支到遠(yuǎn)程分支。
- # 將本地的master分支推送到origin主機(jī)的master分支。如果后者不存在,則會(huì)被新建
- git push origin master
- # 省略了本地分支,以下等同,刪除origin主機(jī)的master分支
- git push origin :master
- git push origin --delete master
- # 如果當(dāng)前分支與遠(yuǎn)程分支之間存在追蹤關(guān)系,則本地分支和遠(yuǎn)程分支都可以省略
- git push origin
- # 如果當(dāng)前分支只有一個(gè)追蹤分支,那么主機(jī)名都可以省略。
- git push
- # 如果當(dāng)前分支與多個(gè)主機(jī)存在追蹤關(guān)系,則可以使用-u選項(xiàng)指定一個(gè)默認(rèn)主機(jī),這樣后面就可以不加任何參數(shù)使用git push
- git push -u origin master
- # 不管是否存在對(duì)應(yīng)的遠(yuǎn)程分支,將本地的所有分支都推送到遠(yuǎn)程主機(jī)
- git push --all origin
- # 強(qiáng)制推送
- git push --force origin
- # git push不會(huì)推送標(biāo)簽(tag),除非使用–tags選項(xiàng)
- git push origin --tags
12. Git Flow使用簡(jiǎn)介
就像代碼需要代碼規(guī)范一樣,代碼管理同樣需要一個(gè)清晰的流程和規(guī)范。三種廣泛使用的工作流程:
- Git flow
- Github flow
- Gitlab flow
三種工作流程,有一個(gè)共同點(diǎn):都采用”功能驅(qū)動(dòng)式開(kāi)發(fā)”(Feature-driven development,簡(jiǎn)稱FDD)。它指的是,需求是開(kāi)發(fā)的起點(diǎn),先有需求再有功能分支(feature branch)或者補(bǔ)丁分支(hotfix branch)。完成開(kāi)發(fā)后,該分支就合并到主分支,然后被刪除。最早誕生、并得到廣泛采用的一種工作流程,就是Git flow。
它最主要的特點(diǎn)有兩個(gè)。首先,項(xiàng)目存在兩個(gè)長(zhǎng)期分支,分別是:主分支master、開(kāi)發(fā)分支develop。其次,項(xiàng)目存在三種短期分支,分別是:功能分支(feature branch)、補(bǔ)丁分支(hotfix branch)、預(yù)發(fā)分支(release branch),一旦完成開(kāi)發(fā),它們就會(huì)被合并進(jìn)develop或master,然后被刪除。
(1). Git Flow流程圖
(2). Git Flow常用的分支
- Production分支。也就是我們經(jīng)常使用的Master分支,這個(gè)分支最近發(fā)布到生產(chǎn)環(huán)境的代碼,最近發(fā)布的Release, 這個(gè)分支只能從其他分支合并,不能在這個(gè)分支直接修改。
- Develop分支。這個(gè)分支是我們是我們的主開(kāi)發(fā)分支,包含所有要發(fā)布到下一個(gè)Release的代碼,這個(gè)主要合并與其他分支,比如Feature分支。
- Feature分支。這個(gè)分支主要是用來(lái)開(kāi)發(fā)一個(gè)新的功能,一旦開(kāi)發(fā)完成,我們合并回Develop分支進(jìn)入下一個(gè)Release。
- Release分支。當(dāng)你需要一個(gè)發(fā)布一個(gè)新Release的時(shí)候,我們基于Develop分支創(chuàng)建一個(gè)Release分支,完成Release后,我們合并到Master和Develop分支。
- Hotfix分支。當(dāng)我們?cè)赑roduction發(fā)現(xiàn)新的Bug時(shí)候,我們需要?jiǎng)?chuàng)建一個(gè)Hotfix, 完成Hotfix后,我們合并回Master和Develop分支,所以Hotfix的改動(dòng)會(huì)進(jìn)入下一個(gè)Release。
(3). Git Flow代碼示例
a. 創(chuàng)建develop分支
- git branch develop
- git push -u origin develop
b. 開(kāi)始新Feature開(kāi)發(fā)
- git checkout -b some-feature develop
- # Optionally, push branch to origin:
- git push -u origin some-feature
- # 做一些改動(dòng)
- git status
- git add some-file
- git commit
c. 完成Feature
- git pull origin develop
- git checkout develop
- git merge --no-ff some-feature
- git push origin develop
- git branch -d some-feature
- # If you pushed branch to origin:
- git push origin --delete some-feature
d. 開(kāi)始Relase
- git checkout -b release-0.1.0 develop
- # Optional: Bump version number, commit
- # Prepare release, commit
e. 完成Release
- git checkout master
- git merge --no-ff release-0.1.0
- git push
- git checkout develop
- git merge --no-ff release-0.1.0
- git push
- git branch -d release-0.1.0
- # If you pushed branch to origin:
- git push origin --delete release-0.1.0
- git tag -a v0.1.0 master
- git push --tags
f. 開(kāi)始Hotfix
- git checkout -b hotfix-0.1.1 master
g. 完成Hotfix
- git checkout master
- git merge --no-ff hotfix-0.1.1
- git push
- git checkout develop
- git merge --no-ff hotfix-0.1.1
- git push
- git branch -d hotfix-0.1.1
- git tag -a v0.1.1 master
- git push --tags