[TOC]
背景
以前協(xié)同修改文件的方法:
通過復(fù)制文件來備份不同版本,按照日期等命名規(guī)則來區(qū)分。
文件共享州刽,大家都能編輯,容易被別人的修改覆蓋浪箭,所以需要文件名加上編輯者的名字穗椅。
Git解決的問題
- 以前方式比較麻煩、容易出錯(cuò)奶栖。
Git
是一個(gè)分布式版本管理系統(tǒng)房待,是為了更好地管理Linux內(nèi)核
開發(fā)而創(chuàng)立的。
Git
可以在任何時(shí)間點(diǎn)驼抹,把文檔的狀態(tài)作為更新記錄保存起來桑孩。因此可以把編輯過的文檔復(fù)原到以前的狀態(tài),也可以顯示編輯前后的內(nèi)容差異框冀。
而且流椒,編輯舊文件后,試圖覆蓋較新的文件的時(shí)候(即上傳文件到服務(wù)器時(shí))明也,系統(tǒng)會(huì)發(fā)出警告宣虾,因此可以避免在無意中覆蓋了他人的編輯內(nèi)容。
管理歷史記錄的數(shù)據(jù)庫(Repository)
數(shù)據(jù)庫是記錄文件或目錄狀態(tài)
的地方温数,存儲(chǔ)著內(nèi)容修改的歷史記錄
绣硝。在數(shù)據(jù)庫的管理下,把文件和目錄修改的歷史記錄放在對(duì)應(yīng)的目錄下撑刺。
本地?cái)?shù)據(jù)庫
為了方便用戶個(gè)人使用鹉胖,在自己的機(jī)器上配置的數(shù)據(jù)庫。
遠(yuǎn)程數(shù)據(jù)庫(共享數(shù)據(jù)庫)
配有專用的服務(wù)器,為了多人共享而建立的數(shù)據(jù)庫甫菠。
如果想要公開在本地?cái)?shù)據(jù)庫中修改的內(nèi)容挠铲,把內(nèi)容上傳到遠(yuǎn)程數(shù)據(jù)庫就可以了。另外寂诱,通過遠(yuǎn)程數(shù)據(jù)庫還可以取得其他人修改的內(nèi)容拂苹。
創(chuàng)建數(shù)據(jù)庫的方法
有兩種方法:
- 創(chuàng)建全新的數(shù)據(jù)庫。
- 復(fù)制遠(yuǎn)程數(shù)據(jù)庫痰洒。
修改記錄的提交
若要把文件或目錄的添加和變更
保存到數(shù)據(jù)庫瓢棒,就需要進(jìn)行提交。
執(zhí)行提交后丘喻,數(shù)據(jù)庫中會(huì)生成上次提交的狀態(tài)與當(dāng)前狀態(tài)的差異記錄
(也被稱為revision
)脯宿。
系統(tǒng)會(huì)根據(jù)修改的內(nèi)容和目錄結(jié)構(gòu)
使用SHA1
哈希函數(shù)計(jì)算出沒有重復(fù)的40位16進(jìn)制的英文及數(shù)字
來給提交命名。指定這個(gè)命名仓犬,就可以在數(shù)據(jù)庫中找到對(duì)應(yīng)的提交嗅绰。
工作樹和索引
在Git管理下舍肠,我們實(shí)際操作的目錄就是工作樹
搀继。
在數(shù)據(jù)庫和工作樹之間有索引
,索引是為了向數(shù)據(jù)庫提交作準(zhǔn)備的區(qū)域翠语。
https://ws1.sinaimg.cn/large/006tKfTcgy1ftn4p78kw4j30fv07a0tg.jpg
基礎(chǔ)配置
三個(gè)配置文件
Git
工作環(huán)境變量三個(gè)存放位置:
-
/etc/gitconfig
:對(duì)系統(tǒng)所有用戶都普遍適用的配置叽躯。 -
~/.gitconfig
:只適用當(dāng)前用戶的配置。 -
your-project/.git/config
:只適用當(dāng)前項(xiàng)目的配置肌括。
重要配置1:用戶信息
$ git config --global user.name "dszkng"
$ git config --global user.email dszkng@outlook.com
這兩項(xiàng)代表著是哪個(gè)用戶提交的点骑。
使用--global
選項(xiàng)時(shí)說明配置的是當(dāng)前用戶主目錄下這個(gè)配置文件。
重要配置2:文本編輯器
Git
需要你輸入一些額外消息的時(shí)候谍夭,會(huì)自動(dòng)調(diào)用一個(gè)外部文本編輯器給你用黑滴。默認(rèn)會(huì)使用操作系統(tǒng)指定的默認(rèn)編輯器,一般可能會(huì)是Vi
或者Vim
紧索。如果你有其他偏好袁辈,可以重新設(shè)置:
$ git config --global core.editor emacs
重要配置3:差異分析工具
在解決合并沖突時(shí)使用哪種差異分析工具?
比如要改用vimdiff
的話:
$ git config --global merge.tool vimdiff
Git
可以理解kdiff3
珠漂,tkdiff
晚缩,meld
,xxdiff
媳危,emerge
荞彼,vimdiff
,gvimdiff
待笑,ecmerge
鸣皂,和 opendiff
等合并工具的輸出信息。當(dāng)然,你也可以指定使用自己開發(fā)的工具签夭。
查看配置信息
$ git config --list
credential.helper=osxkeychain
user.name=dszkng
user.email=dszkng@outlook.com
core.repositoryformatversion=0
core.filemode=true
core.bare=false
core.logallrefupdates=true
core.ignorecase=true
core.precomposeunicode=true
remote.origin.url=ssh://gogs@git.genecard.cn:2222/zhichun/zc-cms.git
remote.origin.fetch=+refs/heads/*:refs/remotes/origin/*
branch.master.remote=origin
branch.master.merge=refs/heads/master
$ git config user.email
dszkng@outlook.com
有時(shí)候會(huì)看到重復(fù)的變量名齐邦,那就說明它們來自不同的配置文件,不過最終Git
實(shí)際采用的是最后一個(gè)第租。
基本使用
Git思想及基本工作原理
只有掌握了基本的原理措拇,用起來才會(huì)知其所以然,游刃有余慎宾。
分支(branch)操作
使用場(chǎng)景
在開發(fā)軟件時(shí)丐吓,可能有多人同時(shí)為同一個(gè)軟件(并行)開發(fā)功能
或修復(fù)Bug
,可能存在多個(gè)Release
版本趟据,并且需要對(duì)各個(gè)版本進(jìn)行維護(hù)券犁。
所幸,Git的分支功能可以支持同時(shí)進(jìn)行多個(gè)功能的開發(fā)和版本管理汹碱。
什么是分支粘衬?
分支是為了將修改記錄的整體流程分叉保存
。分叉后的分支不受其他分支的影響咳促,所以在同一個(gè)數(shù)據(jù)庫里可以同時(shí)進(jìn)行多個(gè)修改稚新。
為了不受其他開發(fā)人員的影響,一般是在主分支上建立自己專用的分支跪腹。完成工作后褂删,將自己分支上的修改合并到主分支。因?yàn)槊恳淮翁峤坏臍v史記錄都會(huì)被保存冲茸,所以當(dāng)發(fā)生問題時(shí)屯阀,定位和修改造成問題的提交就容易多了。
創(chuàng)建分支
$ git branch branch_name
- 切換并創(chuàng)建分支
$ git checkout -b branch_name
- 查看創(chuàng)建過的分支
$ git branch
切換分支
$ git checkout branch_name
在切換工作分支時(shí)需要進(jìn)行checkout
操作轴术。Git
會(huì)從工作樹
還原向目標(biāo)分支提交的修改內(nèi)容难衰。checkout
之后的提交記錄將被追加
到目標(biāo)分支。
- HEAD
HEAD
指向的是現(xiàn)在使用中的分支的最后一次更新
逗栽。通常默認(rèn)指向master
分支的最后一次更新盖袭。通過移動(dòng)HEAD
,就可以變更使用的分支祭陷。
ps
:提交時(shí)使用~(tilde)
和^(caret)
就可以指定某個(gè)提交的相對(duì)位置
苍凛。
比如對(duì)于:HEAD3 -> HEAD2 -> HEAD1 -> HEAD(master)。
HEAD1
用HEAD~1
或HEAD~^
表示兵志。
HEAD2
用HEAD~2
或HEAD~1^1
表示醇蝴。
HEAD3
用HEAD~3
或HEAD~1^2
或HEAD~2^1
表示。
- stash(暫存區(qū))
stash
是臨時(shí)保存文件修改內(nèi)容的區(qū)域想罕。stash
可以暫時(shí)保存工作樹
和索引里還沒提交的修改內(nèi)容
悠栓,等事后再取出暫存的修改霉涨,應(yīng)用到原先的分支或其他的分支上。
ps
:如果在還未提交的修改內(nèi)容
以及新添加的文件
惭适,留在索引區(qū)域
或工作樹
的情況下切換到其他的分支時(shí)笙瑟,修改內(nèi)容會(huì)從原來的分支移動(dòng)
到目標(biāo)分支。
但是如果在checkout
的目標(biāo)分支中相同的文件
也有修改癞志,checkout
會(huì)失敗的往枷。這時(shí)要么先提交修改內(nèi)容
,要么用stash暫時(shí)保存修改內(nèi)容
后再checkout
凄杯。
合并分支
$ git merge branch_name
將完成作業(yè)的Topic
分支合并到Merge
分支有兩種方法错洁,合并后分支的歷史記錄
會(huì)有很大的差別。
比如bugfix
分支是從master
分支分叉出來的戒突,bugfix
合并到master
時(shí):
- merge
$ git merge branch_name
- 如果
master
分支狀態(tài)沒有被更改過屯碴,bugfix
分支的歷史記錄包含master
分支所有的歷史記錄,這時(shí)候合并是最簡(jiǎn)單的膊存,這就是快進(jìn)(fast-forward)合并
导而。
- 如果
master
分支在分叉之后又有了更新,這種情況下隔崎,要把master
分支的修改內(nèi)容和bugfix
分支的修改內(nèi)容匯合
起來今艺。因此,合并
兩個(gè)修改會(huì)生成一個(gè)提交仍稀。這時(shí)洼滚,master
分支的HEAD
會(huì)移動(dòng)到該提交上埂息。
ps
:執(zhí)行合并時(shí)技潘,如果設(shè)定了non fast-forward
選項(xiàng),即使在能夠fast-forward
合并的情況下也會(huì)生成新的提交并合并
千康。
- rebase
$ git init
$ vim a.txt # 新增a.txt享幽,內(nèi)容:create file
$ git commit -a -m 'first commit'
$ git branch issue # 新建issue分支
$ vim a.txt # 修改文件制造沖突條件,增加內(nèi)容:first edit
$ git commit -a -m 'update a.txt in master branch'
$ git checkout issue # 切換分支
$ vim a.txt # 修改文件拾弃,增加內(nèi)容:second edit
$ git commit -a -m 'update a.txt in issue branch'
$ git rebase master # 合并master和issue值桩,但是會(huì)沖突
$ vim a.txt # 修改沖突內(nèi)容
$ git add . # 不需要commit
$ git rebase --continue # 繼續(xù)修改沖突后的提交
$ git rebase --abort # 如果要取消rebase的話
$ git checkout master
$ git merge issue # 這時(shí)候執(zhí)行合并操作,實(shí)際進(jìn)行的是fast-forward合并
bugfix
分支的歷史記錄
會(huì)添加在master
分支的后面豪椿。歷史記錄
成一條線奔坟,相當(dāng)整潔。
rebase
操作可能會(huì)有沖突搭盾,需要修改各自產(chǎn)生沖突的部分咳秉。rebase
之后,master
的HEAD
位置不變鸯隅。因此澜建,要合并master
分支和bugfix
分支,即是將master
的HEAD
移動(dòng)到bugfix
的HEAD
這里。
ps
:merge
和rebase
都是合并歷史記錄
炕舵,但是各自的特征不同何之。
- merge
保持修改內(nèi)容的歷史記錄,但是歷史記錄
會(huì)很復(fù)雜咽筋。 - rebase
歷史記錄簡(jiǎn)單溶推,是在原有提交的基礎(chǔ)上將差異內(nèi)容反映進(jìn)去
。
因此奸攻,可能導(dǎo)致原本的提交內(nèi)容無法正常運(yùn)行悼潭。
刪除分支
$ git branch -d branch_name
分支實(shí)踐
兩種分支
分支可以被任意創(chuàng)建,但是舞箍,要先確定運(yùn)用規(guī)則
才可以有效地利用分支舰褪。
-
Merge
分支
Merge
分支是為了可以隨時(shí)發(fā)布release
而創(chuàng)建的分支,它還能作為Topic
分支的源分支
使用疏橄。保持分支穩(wěn)定
的狀態(tài)是很重要的占拍。如果要進(jìn)行更改,通常先創(chuàng)建Topic
分支捎迫,而針對(duì)該分支晃酒,可以使用Jenkins之類的CI工具進(jìn)行自動(dòng)化編譯以及測(cè)試。
通常窄绒,大家會(huì)將master
分支當(dāng)作Merge
分支使用贝次。
ps
:在數(shù)據(jù)庫初次提交后,Git
會(huì)默認(rèn)創(chuàng)建一個(gè)master分支彰导,不修改分支情況下蛔翅,每次修改提交都是在master
分支上。
-
Topic
分支
Topic
分支是為了開發(fā)新功能
或修復(fù)Bug
等任務(wù)而建立的分支位谋。若要同時(shí)進(jìn)行多個(gè)的任務(wù)山析,請(qǐng)創(chuàng)建多個(gè)的Topic
分支。
Topic
分支是從穩(wěn)定的Merge
分支創(chuàng)建的掏父。完成作業(yè)后笋轨,要把Topic
分支合并回Merge
分支。
常用分支
- 主分支
- master:只負(fù)責(zé)
管理發(fā)布的狀態(tài)
赊淑,在提交時(shí)使用標(biāo)簽
記錄發(fā)布版本號(hào)
爵政。
- master:只負(fù)責(zé)
- develop:日常開發(fā)分支。
- release分支
- release-2018.9:從
develop
分叉出陶缺,為release
作準(zhǔn)備的分支钾挟,做最后的調(diào)整,再合并到develop
分支组哩。
- release-2018.9:從
- release-2017.8
- 特性分支(topic分支)
- feature:日常新功能開發(fā)等龙。
- bugfix:從
release
分支分叉出处渣,解決完bug
問題,最后合并到release
分支蛛砰。
- bugfix:從
- hotfix分支
- hotfix-20180805:從
master
分支分叉出罐栈,解決完緊急bug
問題,最后合并到develop
分支泥畅。因?yàn)橹苯訌?code>develop分支創(chuàng)建可以發(fā)布的版本要花許多的時(shí)間荠诬,所以最好選擇從master
分支創(chuàng)建。
- hotfix-20180805:從
- hotfix-20180802
遠(yuǎn)程分支操作
fetch
- 四種用法
$ git fetch # 更新git remote中所有的遠(yuǎn)程repo所有branch的最新commit_id位仁,將其記錄到.git/FETCH_HEAD文件中柑贞。
$ git fetch remote_repo # 只更新名稱為remote_repo的遠(yuǎn)程repo上的所有branch的最新commit_id,將其記錄聂抢。
$ git fetch remote_repo remote_branch_name # 只更新名稱為remote_repo的遠(yuǎn)程repo上的remote_branch_name分支钧嘶。
$ git fetch remote_repo remote_branch_name:local_branch_name # 同上,并在本地創(chuàng)建local_branch_name分支來保存遠(yuǎn)端分支的所有數(shù)據(jù)琳疏。
ps
:FETCH_HEAD
是一個(gè)版本鏈接有决,記錄在本地的.git/FETCH_HEAD
文件中,指向目前已經(jīng)從遠(yuǎn)程倉庫取下來的分支的末端版本空盼。
pull
- 運(yùn)行過程
首先书幕,基于本地的FETCH_HEAD
記錄,比對(duì)本地的FETCH_HEAD
記錄與遠(yuǎn)程倉庫
的版本號(hào)揽趾,然后git fetch
獲得當(dāng)前指向的遠(yuǎn)程分支的后續(xù)版本的數(shù)據(jù)台汇,然后再利用git merge
將其與本地的當(dāng)前分支
合并。
push
標(biāo)簽(tag)操作
使用場(chǎng)景
通常在發(fā)版本時(shí)會(huì)給版本庫打個(gè)tag
篱瞎,這個(gè)tag
就是這次版本的快照苟呐,指向某個(gè)commit
的指針,類似于branch
奔缠,但是tag
不能像branch
一樣可以移動(dòng)掠抬。
好處是查找以前某個(gè)版本時(shí)不用記0348411ee60d4951aa53afb50bc7d6b1c6d5cdfc
這種長(zhǎng)串的commit_id
吼野,通過tag
來查找更清晰明了校哎。
兩種標(biāo)簽
-
輕標(biāo)簽
:只添加名稱。 -
注解標(biāo)簽
:添加名稱瞳步、注解和簽名闷哆。
打標(biāo)簽
$ git tag tag_name # 不帶注解
$ git tag -a tag_name # 調(diào)用編輯器來寫注解
$ git tag -am "注解" tag_name # 簡(jiǎn)單字符串注解
- 為
commit
的歷史記錄打上標(biāo)簽:
$ git tag v0.1 0348411
查看標(biāo)簽
$ git tag
$ git tag -n # 查看標(biāo)簽和注解信息
- 列出的
tag
是按名稱排序的,也可以查看某個(gè)tag
的詳細(xì)信息:
$ git show tag_name
刪除標(biāo)簽
- 本地刪除:
$ git tag -d tag_name
- 刪除遠(yuǎn)程標(biāo)簽:
$ git tag -d v1.0 # 先本地刪除
$ git push origin :refs/tags/v1.0 # 遠(yuǎn)程刪除
推送標(biāo)簽
創(chuàng)建的標(biāo)簽都是只存儲(chǔ)在本地的单起,不會(huì)自動(dòng)推送到遠(yuǎn)程抱怔。所以打錯(cuò)的標(biāo)簽可以在本地安全刪除。
- 推送某個(gè)標(biāo)簽到遠(yuǎn)程:
$ git push origin tag_name
- 一次性推送全部尚未推送到遠(yuǎn)程的本地標(biāo)簽
$ git push origin --tags
已提交(committed)操作
修改最后一次提交
使用選項(xiàng):git commit --amend
嘀倒。
$ git log
$ vim xxx # wq
$ git add xxx
$ git commit --amend # 本次修改并入最后一次提交屈留,進(jìn)入編輯局冰,修改提交說明
撤銷/反悔(revert)某次提交
$ git revert HEAD # 撤銷前一次commit
$ git revert HEAD^ # 撤銷前前一次commit
$ git revert commit_id
ps
:第三種方式撤銷,在沒有和后面提交相沖突的情況下是直接成功的灌危。
比如說:那次提交是新增了一個(gè)文件康二,后面的每次提交都沒有修改這個(gè)文件,這時(shí)候撤銷那次提交勇蝙,直接做相反的操作把新增的文件刪了沫勿,比較順利。
回退/重置(reset)到某次提交
$ git reset --hard # 最后一次commit
$ git reset --hard HEAD~~ # 回退到前前一次commit
$ git reset --hard ORIG_HEAD # 回退到某次commit
ps
:git revert
和git reset
的區(qū)別:
git revert
是用一次新的commit
來回滾之前的commit
味混,而git reset
是直接刪除指定commit
之后的所有commit
产雹。在回滾這一操作上看,效果差不多翁锡。但是在日后繼續(xù)
merge
以前的老版本時(shí)有區(qū)別蔓挖。因?yàn)?code>git 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)容。
導(dǎo)入提交
使用場(chǎng)景:
在branch1
開發(fā)時(shí)進(jìn)行了多次提交邪财,這時(shí)切換到branch2
陕壹,想把之前branch1
分支提交的commit
都copy
過來,怎么辦树埠?
做法
:首先切換到branch1
分支糠馆,然后查看提交歷史記錄,也可以用sourceTree
查看怎憋,也可以用命令git log
又碌。用法:
$ git checkout branch1
$ git log # 找到想要操作的commit
$ git checkout branch2 # 將commit導(dǎo)入branch2
$ git cherry-pick commit_id # 單個(gè)commit
$ git cherry-pick commit_id1..commit_id2 # commit_id1和commit_id2之間的commit
匯合(合并)提交
- 用法:
$ git rebase -i HEAD~~
自動(dòng)打開編輯器,HEAD
- HEAD~~
的commit
的都被列出來了绊袋,將最后一個(gè)pick
改成squash
毕匀,保存,進(jìn)入注釋說明編輯癌别,再保存退出皂岔,完成合并(兩個(gè)提交合并成了一個(gè))。
修改提交
參考資料
Git Community Book 中文版
廖雪峰的官方網(wǎng)站 - Git教程
猴子都能懂的GIT入門
Git Book