git工作流現(xiàn)在已經(jīng)成了大大小小科技型公司的標配举反,程序員日常擼代碼必備工具攒暇。不同的公司根據(jù)業(yè)務的展開及開發(fā)的進度都會制定最合適的git流程。上家公司開發(fā)人員少灌灾,流程基本就是develop->release->master
雕旨,有線上bug修復扮匠,就基于master
拉hotfix
分支,測完之后通過gitlab
合并回master
和develop
分支凡涩,基本上屬于單線操作棒搜,簡單清晰。到新公司后活箕,開發(fā)需求數(shù)倍增加力麸,開發(fā)人員也比較多,各種流程繁雜育韩,需要合并分支克蚂,處理沖突,多線開發(fā)的情況也越來越多筋讨。以往學習的那點git知識已經(jīng)不能滿足現(xiàn)在的開發(fā)了(其實就是git學的不精)埃叭,最近又重新把git流程過了一遍,今天來總結(jié)一下悉罕。
幾個基礎概念
工作區(qū)及文件狀態(tài)
git分為三個工作區(qū)域:工作區(qū) Working Directory
赤屋、暫存區(qū) Staging Area
立镶、GIT倉庫 repository
,git倉庫又有本地和遠程的概念类早,一般來講媚媒,本地倉庫會領先于遠程倉庫,不過也有例外情況涩僻,下面再具體講缭召。
git的文件狀態(tài)。git倉庫中的文件逆日,不外乎這幾種狀態(tài):
-
Untracked 未追蹤
嵌巷,即新建一個a.js
文件,還沒有被git追蹤屏富,不會到版本庫內(nèi)晴竞。 -
Unmodified 未修改
蛙卤,該文件在git版本庫內(nèi)狠半,但是還沒有被修改。 -
modified 已修改
颤难,該文件在git版本庫神年,已經(jīng)修改,但還沒有暫存行嗤。 -
Staged 已暫存
已日,有修改的文件已經(jīng)通過git add <file>
添加到暫存區(qū)
。
倉庫內(nèi)文件的狀態(tài)可以通過git status
查看栅屏。
a.js
是新增的文件飘千,還沒有被追蹤,c.js
修改了栈雳,但是還沒有添加到暫存區(qū)
护奈,b.js
修改后已經(jīng)添加到暫存區(qū)
,此時commit
將只會提交b.js
哥纫。
快照流
每次提交保存時霉旗,git主要對當時的全部文件制作一個快照并保存這個快照的索引。 為了高效蛀骇,如果文件沒有修改厌秒,Git 不再重新存儲該文件,而是只保留一個鏈接指向之前存儲的文件擅憔。
各種撤消
針對不同情況的撤消操作命令鸵闪,在git status
中都能看到,這里來總結(jié)一下:
-
git rm <file>
:將某個已被追蹤的文件刪除暑诸,提交之后該文件不會再被追蹤岛马,同時本地也會刪除該文件棉姐。 -
git rm --cached <file>
: 同上,不再追蹤某個文件啦逆,但是本地磁盤內(nèi)不會刪除該文件伞矩,回到Untracked
狀態(tài)。 -
git checkout -- <file>
: 將整個文件的修改恢復夏志,慎用乃坤,modified
->unmodified
,一般使用IDE
工具修改最好沟蔑。 -
git reset HEAD <file>
: 將暫存區(qū)的文件回退至已修改狀態(tài)湿诊,staged
->modified
,文件add
之后回退可用瘦材。 -
git reset --hard HEAD^
: 回退到上個版本厅须,commit
之后算一個版本。 -
git reset --hard commit_id
: 移動HEAD指針食棕,會退到指定commit_id的版本朗和。 -
git commit --amend
: 覆蓋之前的提交信息,運行此命令會將暫存區(qū)文件提交簿晓,同時啟動vim
編輯器眶拉,可以編輯上一次的提交信息,最終提交記錄里只會有這一次修改后的信息憔儿。
打標簽
Git 可以給歷史中的某一個提交打上標簽忆植,以示重要。 比較有代表性的是人們會使用這個功能來標記發(fā)布結(jié)點(v1.0 等等)谒臼。工作中可以給某次提交打上標簽朝刊,運維發(fā)布線上代碼時會以此為準。
-
git tag
:列出所有標簽蜈缤。 -
git tag -l release
:列出所有包含release字段的tag拾氓。 -
git tag -a v1.0 -m 'my v1.0 version'
: 創(chuàng)建附注標簽,使用git show v1.0
查看詳細信息劫樟。 -
git tag v1.0
:創(chuàng)建輕量標簽痪枫。 -
git tag v1.0 commit_id
: 給某個歷史提交commit_id
打標簽,可以使用git log --pretty=online
查看歷史請求叠艳。 -
git push origin [tagname]
: 推送單個標簽到遠端 -
git push origin --tags
: 推送所有本地標簽到遠端 -
git checkout -b [branchname] [tagname]
: 在特定的標簽上創(chuàng)建一個新分支
分支操作
分支操作比較常用奶陈,總結(jié)一下命令行:
-
git branch develop
: 新建develop分支,但是不切換附较。 -
git checkout master
: 切換至master分支吃粒。 -
git checkout -b develop
:基于當前分支新建develop分支,并切換至develop分支拒课。 -
git branch -d dev
:刪除dev
分支徐勃。 -
git branch -r
:查看遠程分支列表 -
git checkout -b iss53 origin/iss53
: 拉取遠端分支iss53并在本地新建該分支事示。 -
git checkout --track origin/iss53
: 同上,簡化版本僻肖。 -
git push -u origin iss54
: 推送本地分支iss54至遠程倉庫origin/iss54肖爵,同時建立追蹤關系。 -
git push origin --delete iss54
: 刪除遠程分支iss54
合并及處理沖突
合并
先來張合并圖:
上圖展示了將
iss53
分支合并至master
分支的過程臀脏。首先切換到master
分支劝堪,保證master
分支最新,然后git merge iss53
揉稚,此時Git 會使用兩個分支的末端所指的快照(commit6
和 commit4-hotfix
)以及這兩個分支的工作祖先(commit3
)秒啦,做一個簡單的三方合并。
注意觀察指針的位置搀玖,此時master
分支指向了commit7
余境,iss53
仍然指向commit6
的位置,這就是平時開發(fā)最常見的情況灌诅,保留自己的開發(fā)分支進度芳来,同時將自己的代碼合并回提測/上線分支。問題來了延塑,如果我反向合并呢绣张?即在iss53
分支上運行git merge master
呢答渔?
考慮一下這種場景:你的同事開發(fā)了一個需求A关带,并且已經(jīng)通過上面的方式將他的代碼合入了master
分支;你負責的需求B此時開發(fā)了一半沼撕,突然發(fā)現(xiàn)有一個地方需要基于需求A的代碼才能繼續(xù)進行宋雏。這時候就可以將master
上的代碼合入你自己的分支iss53
,此時你的代碼已經(jīng)包含了master
中commit的代碼务豺,流程圖如下:
如果之后
master
分支再沒有任何代碼合入磨总,你的需求開發(fā)完之后,合入master
分支時將會是一次fast-forward
合并笼沥,master
的指針會直接快進指向iss53
所在的位置蚪燕。
另一個需要注意的是:代碼合并時合并的只是你之前commit
提交的代碼修改,只對比這些文件奔浅。
解決沖突
代碼為什么沖突馆纳,用上圖舉例來說,自commit3
之后汹桦,master分支和iss53分支分別有commit4-hotfix
和commit5/6
提交鲁驶,如果這兩個分支提交中修改了同一個文件的相同位置,則會報文件有沖突舞骆,需要解決沖突才能繼續(xù)合并钥弯。解決流程就是:先手動解決文件a的沖突径荔,然后git add a.js
,接著git commit -m 'fix conflict'
即可脆霎,此時可以推送到遠端倉庫总处。可以通過git status
查看未解決沖突的文件睛蛛。
這個過程中可以通過
git merge --abort
來終止合并辨泳。
解決沖突最重要的是要細心,要仔細查看git status
未解決沖突的文件玖院,一一去解決菠红。如果不加思索的直接git add .
,則會將未解決沖突的文件移動到暫存區(qū)难菌,然后提交上去试溯。如果又推送到了遠程倉庫,處理起來會比較麻煩郊酒。如果遇到這種情況遇绞,可以用以下方法解決:
- 在本地
master
分支上git reset --hard HEAD^
,回退這次提交燎窘。 -
git push -f
摹闽,強制推送本地分支到遠端。因為這個時候你的本地分支已經(jīng)落后于遠端分支褐健,如果正常流程git pull
付鹿,你的代碼會回到?jīng)_突后的提交狀態(tài),相當于白白reset
了代碼蚜迅。如果直接使用git push
舵匾,git會提示你本地分支落后于遠端,必須git pull
才能推送谁不。使用git push -f
坐梯,強制將遠程倉庫的代碼替換為你當前本地分支的代碼,相當于遠端代碼reset
回退刹帕。 - 再次
git merge iss53
吵血,將代碼合并回master
,解決沖突偷溺,add -> commit -> push
蹋辅。
以上方法建立在你的沖突代碼推送到遠端后,你的隊友沒有人再次合并代碼到master
并且推送到遠端亡蓉。如果有這種情況晕翠,處理起來會更復雜,因為你的回退,然后強制推送淋肾,會將他們的代碼回退掉硫麻!這時候就需要每一個隊友的共同配合才能解決,異常復雜和麻煩樊卓。所以一定要細心細心再細心拿愧,盡量不要reset
。
儲藏與清理
有這樣一個場景碌尔,你在你的分支iss54上開發(fā)需求浇辜,改了好多文件代碼,這個時候老大來告訴你唾戚,develop分支上有個地方有問題柳洋,需要你去確認一下。一般這時候你切換分支是無法切換的叹坦,git會提示你先暫存才可以熊镣。但是你暫時又不想提交,因為提交過之后你的修改記錄就沒有了募书,下次再切換回來绪囱,哪些文件調(diào)整過,在IDE
里就無法清晰的看到莹捡。這個時候你可以使用git stash
相關命令:
-
git stash
: 將當前分支的修改先儲存到棧內(nèi)(清理一下工作區(qū))鬼吵,方便切換分支。 -
git stash list
: 查看棧內(nèi)儲存列表篮赢。 -
git stash apply
: 應用最近的一次儲存齿椅,之前暫存過的文件會回到modified狀態(tài)。 -
git stash apply --index
: 恢復到儲存時的狀態(tài)荷逞,包括已經(jīng)暫存的文件狀態(tài)媒咳。 -
git stash apply stash@{2}
: 應用指定的儲存粹排。 -
git stash drop stash@{0}
: 刪掉棧內(nèi)指定的儲存种远。
思維導圖總結(jié)
以上的命令及處理方法基本上已經(jīng)可以滿足日常的開發(fā)了。之前命令行用的少顽耳,都是使用IDE內(nèi)自帶的可視化工具來操作坠敷,雖然比較簡單明了,但速度是無法跟命令行比的射富。不過使用IDE可以方便的切換分支膝迎,拉取遠端分支,提示未解決的沖突等胰耗,日常使用要方便的多限次。推薦兩者結(jié)合著使用,畢竟我們的目的是把工作做好,把任務完成卖漫,在git上不必過分糾結(jié)于過程费尽。