使用Git已有很長一段時間端逼,遇到一些痛點問題揖铜,而且大都是網(wǎng)上難以直接查到的,故總結(jié)于此困介。
1.兩幅重要的圖
以上兩幅圖對理解Git的原理十分重要。
2.基礎(chǔ)命令
首先是一些基礎(chǔ)命令蘸际,這些命令經(jīng)常使用座哩,而少為人知。
git add -A
或git add --all
把包括修改粮彤、增加根穷、刪除都全部加入暫存區(qū)姜骡。等同于git add .
和git add -u
兩個命令。git add -p
一段段地把修改提交到暫存區(qū)屿良,每一個修處改都會提示你是否加入暫存區(qū)圈澈。可以按照想要的方式把所有修改分成多個commit尘惧。git commit -am
把已經(jīng)加入暫存區(qū)(Stage)的文件的更改提交到本地版本庫康栈,無論這些更改是否已經(jīng)加入暫存區(qū)。與git commit -m
的區(qū)別在于后者只能向本地版本庫提交已經(jīng)加入暫存區(qū)的更改喷橙。如果只是改動某些代碼啥么,而沒有增加或者刪除文件,使用git commit -am
一個命令就可以替代git add --all
和git commit -m
兩個命令贰逾。git commit --amend
修改最后一次commit的提交信息悬荣。注意,是最后一次疙剑,也就是HEAD指向的commit氯迂。git branch -vv
顯示本地分支與遠(yuǎn)程分支的對應(yīng)關(guān)系。執(zhí)行git push
(沒有帶分支參數(shù))時代碼被提交到對應(yīng)遠(yuǎn)程分支言缤,就是依據(jù)該對應(yīng)關(guān)系囚戚。這里需要注意Git的simple和matching模式,后者會一次性提交所有的對應(yīng)關(guān)系的分支的代碼到遠(yuǎn)程分支轧简,前者只會提交當(dāng)前所在分支。git stash
和git statsh pop
主要用來暫存當(dāng)前的工作匾二。如果需要git pull
又不想把當(dāng)前的修改提交commit哮独,則可以先使用git stash
把修改暫存。該命令運(yùn)行完后工作區(qū)是干凈的察藐,此時再使用git pull
拉新代碼皮璧。完事了后使用git statsh pop
恢復(fù)之前的工作狀態(tài)。git reflog
顯示曾經(jīng)的commit分飞。如果我們使用git reset
將HEAD指向某個過去的commit了悴务,此時要想回到最開始的commit,使用git log
是無法查到最開始的commit的譬猫。但是也不要慌讯檐,git reflog
能給你回到最開始的commit的機(jī)會,它會顯示所有的提交記錄染服。
3.使用場景
根據(jù)一些常見的場景給出我自己的解決方法别洪。
3.1 基于遠(yuǎn)程分支建立本地分支
你可能剛?cè)肼殹D愕男峦峦蝗粊G過來一個地址A柳刮,讓你git clone
一下代碼挖垛。接著他又丟過來一個分支名dev0.1痒钝,讓你基于該分支進(jìn)行開發(fā)。如果之前只會在master上git pull
和git push
痢毒,此時就可能有點不知所措了送矩。
你可能首先想到如何把遠(yuǎn)程的dev0.1分支clone下來,因為執(zhí)行git clone A
之后哪替,你再用git branch
查一下栋荸,發(fā)現(xiàn)本地只有master分支。
這里需要糾正一個問題夷家。git clone A
實際上把所有的分支都從遠(yuǎn)程拉下來了蒸其,但是git branch
只顯示那些與遠(yuǎn)程保持了追蹤(tracked)的分支。并且git clone A
還會創(chuàng)建本地master分支并將其與遠(yuǎn)程(origin)的master建立追蹤(tracked)關(guān)系库快。于是我們最開始使用git branch
就只顯示master了摸袁。
那么,問題來了义屏。既然所有分支都已經(jīng)拉下來了靠汁,我們?nèi)绾吻腥雂ev0.1分支并進(jìn)行開發(fā)呢?其實很簡單:
git checkout dev0.1
這個命令的含義是將dev0.1分支從本地版本庫取到工作區(qū)來(參考上面的兩幅圖中的第二幅)闽铐,并將工作區(qū)切換到該分支蝶怔。此時一個本地名為dev0.1的分支與遠(yuǎn)程(origin)的dev0.1分支就建立了追蹤關(guān)系。我們就可以基于dev0.1進(jìn)行開發(fā)了兄墅。試試git branch
看看會不會顯示出dev0.1
踢星。
假如遠(yuǎn)程分支中并沒有一個叫dev0.1的分支,我們運(yùn)行以上命令隙咸,就會發(fā)現(xiàn)這樣的錯誤提示:
error: pathspec 'dev0.1' did not match any file(s) known to git.
此時我們可以看看到底有哪些遠(yuǎn)程分支沐悦,請使用git branch -r
。當(dāng)然git branch -a
顯示的信息就多一些五督,它顯示全部本地分支和遠(yuǎn)程分支藏否。
但我通常并不這樣做。而是用以下命令:
git checkout -b dev origin/dev0.1
它的含義是新建一個本地分支dev充包,且讓這個分支與遠(yuǎn)程dev0.1分支保持追蹤關(guān)系副签。這樣做的好處在于可以自己取一個本地分支名。
3.2 分支開發(fā)模式同步Master分支代碼
你可能基于本地的dev0.1分支寫了一些代碼基矮,測試后并提交到本地倉庫中了淆储。你們團(tuán)隊代碼主干分支是master,且基于這個分支進(jìn)行發(fā)布愈捅。這就面臨著一個問題遏考,你的新寫的代碼如何提交到主干分支上。
1.如果的本地dev0.1分支與遠(yuǎn)程(origin)dev0.1分支保持了追蹤關(guān)系蓝谨,且如果你們團(tuán)隊使用類似github/gitlab這樣的git倉庫托管服務(wù)灌具,則可以直接在本地dev0.1分支上操作:
- 先
git pull origin dev0.1
拉下遠(yuǎn)程代碼青团,以防有更新。 如果git是simple模式咖楣,則可以直接使用git pull
[目前git2.0及以上版本都默認(rèn)為simple模式]督笆。 - 如果有更新則會自動合并,合并如果失敗诱贿,則會要求手動處理沖突娃肿。處理完后將這次修改提交到本地倉庫。
- 使用
git push origin dev0.1
向遠(yuǎn)程倉庫dev0.1分支提交代碼珠十。git 2.0及以上版本直接git push
即可料扰。 - 在github/gitlab的界面上操作,由dev0.1分支向master分支提交merge請求焙蹭。負(fù)責(zé)master分支維護(hù)的同事合并你的分支代碼到master即可晒杈。
2.如果本地dev0.1分支是基于master分支新建的,即在本地master上使用git checkout -b dev0.1
命令---它會創(chuàng)建一個dev0.1的本地分支孔厉,并切換到該分支拯钻,但它不會設(shè)置與遠(yuǎn)程分支的追蹤關(guān)系。這時候如何將該分支的新代碼合并到master分支呢撰豺?
- 首先dev0.1分支要合并到本地master上去粪般。先使用
git checkout master
切換到主干分支,在主干分支上操作污桦。 - 合并dev0.1分支亩歹。使用
git merge dev0.1 --no-ff
或git merge dev0.1
命令。這兩者的區(qū)別可用以下圖片說明:
前者帶有分支記錄凡橱,后者沒有捆憎。
- 然后將master分支的代碼提交到遠(yuǎn)程master:
git pull
拉新代碼,解決沖突梭纹,git push
推到遠(yuǎn)程。 - 如果dev0.1分支沒用處了致份,就可以直接刪掉:
git branch -d dev0.1
变抽。
3.3 將工作區(qū)恢復(fù)成干凈的狀態(tài)
如果開發(fā)了一陣子,修改了一些代碼氮块,但沒控制好绍载,把工作區(qū)搞成了一堆亂麻。這時候就想滔蝉,要是能把工作區(qū)恢復(fù)成最開始的樣子就好了击儡。
首先確定要恢復(fù)成的最初狀態(tài)。一般來講就是將工作區(qū)恢復(fù)成當(dāng)前本地倉庫中HEAD所指向的commit蝠引。不過如果你之前提交到本地的一些commit你也不想要了阳谍,那么先用
git log
查一下你要恢復(fù)到的commitID蛀柴,復(fù)制下來。使用
git reset --hard HEAD/commitID
命令矫夯。運(yùn)行完畢之后鸽疾,用git status
查一下狀態(tài)。一般狀態(tài)下會顯示
nothing to commit, working directory clean
但是如果你最開始時新增了一些文件训貌,且沒有將其加入暫存區(qū)制肮,那么就不是這種提示了。你需要把你新增的那些文件刪掉递沪,git status
才會恢復(fù)成以上狀態(tài)豺鼻。主要原因是git reset
無法重置那些沒有加入暫存區(qū)的更改。
如果你只是需要把某個文件B恢復(fù)成倉庫里上一次提交的狀態(tài)款慨,那么有以下兩種可能:1.這個文件沒有任何修改提交到暫存區(qū)儒飒;2.這個文件有一部分修改提交到暫存區(qū)了,但是想把暫存區(qū)的修改也恢復(fù)成HEAD指向的版本樱调。
- 1情況下直接使用
git checkout B
恢復(fù)工作區(qū)的文件B约素。 - 2情況下先使用
git reset HEAD B
撤銷暫存區(qū)里面對B的修改,再使用git checkout B
恢復(fù)工作區(qū)中的修改笆凌。
3.4 修改某次的提交信息
這個場景并不常見圣猎,而有的時候又一定用的到。例如你開發(fā)一段時間了乞而,卻發(fā)現(xiàn)公司的gitlab要求你push的時候必須使用公司的郵箱送悔,而你之前的commit都用的是你自己的郵箱。我之前就遇到過這個問題爪模,在這兒記錄一下解決方案欠啤。
首先,git commit --amend
只能對最新提交的Comment內(nèi)容或郵箱的修改屋灌,并不能對中間提交的Comment或者郵箱進(jìn)行修改洁段。如果只是修改最新提交的信息:
-
git commit --amend
直接進(jìn)入修改提交信息的模式。 -
git commit --amend --author="chenyi <chenyi@xxx.com>"
可以修改作者信息共郭。
如果需要修改中間某一次的提交信息祠丝,則需要按照一定的方法操作。以下是一個修改示列除嘹。
如上圖写半,第二個提交的郵箱同其他提交不一樣,我們需要將它修改成與其他一樣的尉咕。
先進(jìn)入第二個提交:
git rebase -i preCommitID
叠蝇,這里preCommitID就是第二個提交的下面的一個ID,即ff4e24
年缎。
我們可以看到悔捶,列表里面顯示了我們想要修改的commit的ID铃慷,即
c1c6685
。將其前面的pick
更改為edit
炎功,保存并退出枚冗。
當(dāng)我們從第2步的編輯過程中退出后,git會提示我們可以用的兩個命令
git commit --amend
和git rebae --continue
蛇损。這兩個命令在后面都會用到赁温。
我們目前已經(jīng)處于c1c6685
commit上了。此時我們只需要使用git commit --amend --author="chyoo <chyoo1991@gmail.com>"
修改該提交的作者信息淤齐。上圖就是運(yùn)行該命令后進(jìn)入修改的界面股囊。
git rebase --continue
繼續(xù)rebase就可以完成目標(biāo)了。
3.5 將Commit記錄變成直線模式
通常很多人合作寫代碼時穗熬,希望將遠(yuǎn)程master分支維護(hù)成直線的形式祭务,這樣commit干凈明確内狗,檢查問題時能省不少事。而一般合并最新代碼時用的git pull
會將生成一個merge commit义锥,這將導(dǎo)致推送到遠(yuǎn)程分支的代碼也是各種分支交叉柳沙,一點也不干凈明確。
那么拌倍,該如何實現(xiàn)這個目的呢赂鲤?
首先這里要上一份干貨,就是git merge
與git rebase
的區(qū)別柱恤,需要注意到git pull
實際上是調(diào)用git merge
進(jìn)行代碼的合并数初。->干貨在此。
從干貨文章中我們可以發(fā)現(xiàn)梗顺,能夠使用git rebase
命令用打補(bǔ)丁的方式來實現(xiàn)commit保持直線的目的泡孩。我們這樣操作:
git fetch origin master
用這個命令將遠(yuǎn)程(origin)的master分支最新提交取到本地。git rebase origin/master
使用rebase 進(jìn)行pack打補(bǔ)丁寺谤,將我們自己提交的本地代碼以補(bǔ)丁的方式放在最新代碼的后面珍德。這個過程中可能會出現(xiàn)沖突,修改完了之后使用git rebase --continue
繼續(xù)pack矗漾,直到完成rebase。如果出現(xiàn)無法解決的問題薄料,想回到最開始敞贡,可以使用git rebase --abort
。git push
提交代碼到遠(yuǎn)程master分支摄职。
這里要求開發(fā)協(xié)作的每一個人都用這種方式操作和提交代碼誊役,這樣才能保持遠(yuǎn)程master分支的干凈整潔和直線性获列。
3.6 為一個本地倉庫設(shè)定多個遠(yuǎn)程倉庫
這個看起來是有點奇怪的需求蛔垢。不過前不久gitlab的員工刪除了它們的數(shù)據(jù)庫击孩,導(dǎo)致很大部分項目受影響,這讓我們覺得如果能多一份代碼保障或許更好鹏漆。
使用git clone
時巩梢,會自動設(shè)定遠(yuǎn)程地址,一般都是以origin
指代艺玲。如果我們有特殊需求括蝠,需要將代碼提交到另一個遠(yuǎn)程倉庫,這時就要設(shè)定新的遠(yuǎn)程倉庫地址了饭聚。
git remote add hegel http://xxxx.git
這個命令就可以給當(dāng)前的本地庫設(shè)置一個遠(yuǎn)程倉庫hegel
忌警。以后我們想推拉代碼到這個遠(yuǎn)程庫時,就需要用git push hegel master
或git pull hegel master
了秒梳。
4.快捷設(shè)定
有一些比較有用的設(shè)定法绵,能在寫命令時少了很多麻煩。
- 全局設(shè)置user和email
- git config --global user.name "chenyi"
- git config --global user.emali "chenyi@xxx.com"
- 給常用命令起一個簡單的名字
- git config --global alias.co checkout
- git config --global alias.st status
- git config --global alias.br branch
- git config --global alias.ci commit
后續(xù)就可以使用git co
/git ci
等命令了酪碘,是不是簡潔了很多朋譬?
3.更好看的git log
git config --global alias.lg "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"
配置了這個之后,使用git lg
簡直眼前一亮婆跑。