Git使用總結(jié)五:分支管理

前些時(shí)間,公司產(chǎn)品經(jīng)理針對(duì)司機(jī)端提出一個(gè)微信及支付寶二維碼掃碼支付需求,需求不急,但也是剛性任務(wù),于是我在會(huì)后便火急火燎開(kāi)干,期間碰到一些問(wèn)題獲取了一些經(jīng)驗(yàn),想分享給各位。

第一個(gè)問(wèn)題,期間,或許因?yàn)楣痉磸?fù)修網(wǎng)造成GitLab地址變動(dòng),管理GitLab的后臺(tái)并沒(méi)有多余的時(shí)間來(lái)維護(hù),所以短時(shí)間內(nèi),并不能將本地的修改 push 到遠(yuǎn)程。這種情況下,我將每次的修改 commit 到本地,等GitLab好了再 push,對(duì)此我深刻感受到Git的好處。

第二個(gè)問(wèn)題,在公司我負(fù)責(zé)iOS移動(dòng)端的獨(dú)立開(kāi)發(fā)與維護(hù)。由于是獨(dú)立開(kāi)發(fā),使用Git時(shí),我一般在 devfix bug 或者改需求。此次,我需求完成一半,測(cè)試那邊給出反饋,我放下手頭需求,緊急 fix bugcommit 到本地后,在無(wú)法 push 到遠(yuǎn)程的情況下,我需要緊急提交一個(gè)新版本。問(wèn)題來(lái)了,由于GitLab未修復(fù),我無(wú)法clone上個(gè)版本到本地來(lái)fix bug,又因?yàn)槲襠ev分支上同時(shí)fix bug和迭代需求,本地也不是上一個(gè)版本。

創(chuàng)建與合并分支

在Git里,每次提交,Git把提交串成一個(gè)分支,這個(gè)分支是主分支,即 master 分支。HEAD 不是指向提交,而是指向 master,master 才指向提交,所以,HEAD指向的是當(dāng)前分支。

Git創(chuàng)建一個(gè) dev 分支時(shí)很快,因?yàn)槌诵枰黾右粋€(gè) dev 指針,改變 HEAD 的指向,工作區(qū)的文件沒(méi)有變。

Git合并 dev 分支到 master 分支也很快,在 dev 上完成工作后,直接把 master 指向 dev 當(dāng)前的提交,就完成了合并。合并過(guò)程中,指針指向改變,工作區(qū)內(nèi)容同樣沒(méi)有變。

  • 創(chuàng)建 dev 分支,然后切換到 dev 分支
git checkout -b dev
  • 創(chuàng)建分支
git branch <name>
  • 切換分支
git checkout <name>
  • git branch 查看當(dāng)前分支
$ git branch
* dev
  master
  • git merge 命令用于合并指定分支到當(dāng)前分支
git merge <name>
  • 刪除分支
git branch -d <name>

解決沖突

  • 創(chuàng)建并切換 feature 分支
$ git checkout -b feature
Switched to a new branch feature
  • 修改工作區(qū)內(nèi)容后,在 feature 分支上提交修改并且將暫存區(qū)的修改 commit 到當(dāng)前分支
$ git add readme.txt
$ git commit -m "and simple"
[feature 75a857c] and simple
1 file changed, 1 insertion(+), 1 deletion(-)
  • 切換到 master 分支,系統(tǒng)將會(huì)提示當(dāng)前 master 分支彼遠(yuǎn)程的 master 分支要超前一個(gè)提交
$ git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 1 commit
  • master 分支上把工作區(qū)內(nèi)容進(jìn)行修改,并且提交
$ git add readme.txt
$ git commit -m "& simple"
[master 400b400] & simple
1 file changed, 1 insertion(+), 1 deletion(-)
  • 此刻在master分支和feature分支都要提交的情況下,Git無(wú)法進(jìn)行快速合并,如果試圖把各自的修改合并,將會(huì)造成沖突。
$ git merge feature
Auto-merging readme.txt
CONFLICT (content): Merge conflict in readme.txt
Automatic merge failed; fix conflicts and then commit the result.
  • Git將會(huì)提示,readme.txt文件存在沖突,必須手動(dòng)解決沖突后再提交,我們一般使用 git status 了解我們沖突的文件。
$ git status
# On branch master
# Your branch is ahead of 'origin/master' by 2 commits.
#
# Unmerged paths:
#   (use "git add/rm <file>..." as appropriate to mark resolution)
#
#       both modified:      readme.txt
#
no changes added to commit (use "git add" and/or "git commit -a")
  • 直接查看readme.txt文件夾下內(nèi)容,Git用 <<<<<<<=======,>>>>>>> 標(biāo)記出不同分支的內(nèi)容,我們修改文件夾下內(nèi)容使 feature 分支和 master 分支保持一致后,再提交。

  • 使用帶參數(shù)的 git log 也可以看到分支的合并情況

$ git log --graph --pretty=oneline --abbrev-commit
*   59bc1cb conflict fixed
|\
| * 75a857c AND simple
* | 400b400 & simple
|/
* fec145a branch test
...
  • 最后刪除分支
$ git branch -d feature
Deleted branch feature (was 75a857c).
  • 所以說(shuō),當(dāng)Git無(wú)法自動(dòng)合并分支時(shí),就必須首先解決沖突,解決沖突后,再提交,合并完成。使用 git log --graph 命令可以查看分支合并圖

分支管理策略

在解決沖突時(shí),我們提到了快速合并,通常,我們?cè)诤喜⒎种r(shí),Git會(huì)使用 Fast forward 模式,在 Fast forward 模式下,刪除分支后,會(huì)丟失分支信息。

所以,我們會(huì)強(qiáng)制禁用 Fast forward 模式,Git會(huì)在 merge 時(shí)生成一個(gè)新的 commit,這樣我們可以在分支歷史上查看分支信息。

--no-ff方式的git merge

  • 首先,創(chuàng)建并切換 dev 分支
$ git checkout -b dev 
Switched to a new branch dev
  • 修改工作區(qū)readme.txt文件,并提交一個(gè)新的 commit
$ git add readme.txt 
$ git commit -m "add merge"
[dev 6224937] add merge
 1 file changed, 1 insertion(+)
  • 切換回 master
$ git checkout master
Switched to branch 'master'
  • 合并 dev 分支時(shí),注意使用 --no-ff 參數(shù),表示禁用 Fast forward
$ git merge --no-ff -m "merge with --no-ff" dev
Merge made by the 'recursive' strategy.
 readme.txt |    1 +
 1 file changed, 1 insertion(+)
  • 合并后我們使用 git log 查看分支歷史
$ git log --graph --pretty=oneline --abbrev-commit
*   7825a50 merge with no-ff
|\
| * 6224937 add merge
|/
*   59bc1cb conflict fixed
...

分支策略

其實(shí),我們?cè)趯?shí)際開(kāi)發(fā)中,應(yīng)該按照幾個(gè)基本原則進(jìn)行分支管理:

  1. 首先,master 分支應(yīng)該是最穩(wěn)定的,也就是說(shuō),我們平時(shí)不能在 master 上干活,master 分支僅用來(lái)發(fā)布版本使用。

  2. 如果要干活,我們應(yīng)該創(chuàng)建并切換一個(gè)分支,這個(gè)分支就是 dev 分支。也就是說(shuō),dev 分支是不穩(wěn)定的,平時(shí)我們各自在 dev 分支上干活,每個(gè)人有自己的分支,不斷往 dev 分支上合并,等到版本發(fā)布時(shí),再把 dev 分支合并到 master 上。

Bug分支

當(dāng)前,正在 dev 上的工作還沒(méi)提交,我們需要經(jīng)緊急修復(fù)一個(gè)bug,很自然地,需要?jiǎng)?chuàng)建一個(gè) bug 分支來(lái)修復(fù)這個(gè)bug。有人會(huì)有疑問(wèn),為什么不把 dev 上工作區(qū)的修改先進(jìn)行提交,其實(shí)我們并不是不想提交,而是工作進(jìn)行到一半無(wú)法提交。那么,Git為我們提供一個(gè) stash 功能,先把當(dāng)前工作現(xiàn)場(chǎng)“儲(chǔ)藏”起來(lái),等恢復(fù)現(xiàn)場(chǎng)后繼續(xù)工作。

$ git stash
Saved working directory and index state WIP on dev: 6224937 add merge
HEAD is now at 6224937 add merge
  • git stash 后,我們用 git status 查看工作區(qū),工作區(qū)是干凈的,我們可以放心創(chuàng)建分支修復(fù)bug。

  • 確定在那個(gè)分支上修復(fù)bug,就在那個(gè)分支上創(chuàng)建bug分支。此次我們?cè)?master 上修復(fù),就從 master 上創(chuàng)建臨時(shí)分支。由于創(chuàng)建的 bug 分支是臨時(shí)分支,修復(fù)bug后,切換到 master 分支,完成合并,最后刪除 bug 分支。

  • 接著我們回到 dev 干活,使用 git status 查看工作區(qū)狀態(tài),發(fā)現(xiàn)工作區(qū)是干凈的,這是我們需要使用 git stash list 查看存的工作現(xiàn)場(chǎng)。

$ git stash list
stash@{0}: WIP on dev: 6224937 add merge
  • 使用 git stash apply 恢復(fù),但是恢復(fù)后,stash內(nèi)容不會(huì)刪除,需要使用 git stash drop 刪除stash內(nèi)容。

  • 或者,使用 git stash pop,恢復(fù)的同時(shí)把stash內(nèi)容也刪除了。

$ git stash pop
# On branch dev
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       new file:   hello.py
#
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   readme.txt
#
Dropped refs/stash@{0} (f624f8e5f082f2df2bed8a4e09c12fd2943bdd40)
  • 使用 git stash pop 后,使用 git stash list 查看,就看不到stash內(nèi)容了。

Feature分支

一般情況下,添加一個(gè)新功能,需要?jiǎng)?chuàng)建一個(gè) feature 分支,feature 分支作為臨時(shí)開(kāi)發(fā)新功能的分支最終要合并到 dev 分支,也就是說(shuō),我們添加一個(gè)新功能,并不是在 dev 上進(jìn)行開(kāi)發(fā),而是要?jiǎng)?chuàng)建一個(gè) feature 分支,而這個(gè) feature 分支用于臨時(shí)開(kāi)發(fā)一個(gè)新功能,是一個(gè)臨時(shí)分支。

  • 其實(shí),feature 分支和 bug 分支類似,創(chuàng)建切換,合并,然后刪除。

  • 取消新功能時(shí),使用 git branch -d <name> 刪除分支,但是會(huì)提示銷毀失敗,這時(shí)需要強(qiáng)行刪除分支,使用命令 git branch -D <name>。

$ git branch -D feature-vulcan
Deleted branch feature-vulcan (was 756d4af).
  • 所以,如果要丟棄一個(gè)沒(méi)有被合并的分支,可以通過(guò) git branch -D <name> 強(qiáng)行刪除這個(gè)分支。

多人協(xié)作

真正意義上,Git的價(jià)值還是體現(xiàn)在團(tuán)隊(duì)協(xié)作作用上,而那些命令其實(shí)僅僅起到輔助協(xié)作的作用,如果是獨(dú)立開(kāi)發(fā)并不能深刻體會(huì)到這種協(xié)作的作用,但是如果進(jìn)入純技術(shù)公司團(tuán)隊(duì),可能Git的這種協(xié)作價(jià)值才能真正的體現(xiàn)出來(lái)。

  • 從遠(yuǎn)程倉(cāng)庫(kù) clone 時(shí),實(shí)際上Git自動(dòng)把本地 master 分支和遠(yuǎn)程 master 分支對(duì)應(yīng)起來(lái)了,并且,遠(yuǎn)程倉(cāng)庫(kù)默認(rèn)的名稱是 origin。

  • 所以說(shuō),我們使用 git remote 查看遠(yuǎn)程倉(cāng)庫(kù)的信息

$ git remote
origin
  • 或者,用 git remote -v 查看遠(yuǎn)程庫(kù)詳細(xì)信息
$ git remote -v
origin  git@github.com:michaelliao/learngit.git (fetch)
origin  git@github.com:michaelliao/learngit.git (push)

推送分支

  • master 分支在本地作為主分支,要時(shí)刻與遠(yuǎn)程倉(cāng)庫(kù)同步。

  • dev 分支是開(kāi)發(fā)分支,團(tuán)隊(duì)所有成員都需要在 dev 分支上工作,所以 dev 分支同樣需要與遠(yuǎn)程同步。

  • bug 分支作為臨時(shí)分支,只在本地修復(fù)bug,沒(méi)有必要推送到遠(yuǎn)程,可能一周推送一次。

  • feature 分支同樣是臨時(shí)分支,但是,feature 分支是否推送到分支,取決于是否團(tuán)隊(duì)協(xié)作,如果是獨(dú)立開(kāi)發(fā),feature 分支沒(méi)有必要推送到遠(yuǎn)程,如果協(xié)作開(kāi)發(fā),需要推送到遠(yuǎn)程,與遠(yuǎn)程保持同步。

抓取分支

  • 從遠(yuǎn)程庫(kù) clone 時(shí)默認(rèn)只能看到本地的 master 分支,我們可以使用 git stauts 查看。
$ git branch
* master
  • 要在 dev 分支上開(kāi)發(fā),必須創(chuàng)建遠(yuǎn)程 origindev 分支到本地,可以使用 git checkout -b dev origin/dev 命令創(chuàng)建本地 dev 分支。
$ git checkout -b dev origin/dev
  • 可以在本地創(chuàng)建的這個(gè) dev 分支上修改,并且把修改 commitdev 分支,然后把 dev 分支 push 到遠(yuǎn)程。
$ git commit -m "add /usr/bin/env"
[dev 291bea8] add /usr/bin/env
 1 file changed, 1 insertion(+)
$ git push origin dev
Counting objects: 5, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 349 bytes, done.
Total 3 (delta 0), reused 0 (delta 0)
To git@github.com:michaelliao/learngit.git
   fc38031..291bea8  dev -> dev
  • 由于你已經(jīng)向 origin/dev分支 推送了提交,而devB碰巧對(duì)同樣的文件做了修改,并且試圖推送,由于我的最新提交和devB試圖推送的提交有沖突,所以,推送失敗。
$ git add hello.py 
$ git commit -m "add coding: utf-8"
[dev bd6ae48] add coding: utf-8
 1 file changed, 1 insertion(+)
$ git push origin dev
To git@github.com:michaelliao/learngit.git
 ! [rejected]        dev -> dev (non-fast-forward)
error: failed to push some refs to 'git@github.com:michaelliao/learngit.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Merge the remote changes (e.g. 'git pull')
hint: before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
  • 這時(shí),Git提醒,先用 git pull 把最新的提交從 origin/dev 抓取下來(lái),然后,在本地合并,先解決沖突,然后再推送到遠(yuǎn)程。
$ git pull
remote: Counting objects: 5, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 3 (delta 0)
Unpacking objects: 100% (3/3), done.
From github.com:michaelliao/learngit
   fc38031..291bea8  dev        -> origin/dev
There is no tracking information for the current branch.
Please specify which branch you want to merge with.
See git-pull(1) for details

    git pull <remote> <branch>

If you wish to set tracking information for this branch you can do so with:

    git branch --set-upstream dev origin/<branch>
  • git pull 失敗,因?yàn)槲粗付ū镜?dev 分支和遠(yuǎn)程 origin/dev 分支鏈接,所以,設(shè)置 dev 和遠(yuǎn)程 origin/dev 的鏈接。
$ git branch --set-upstream dev origin/dev
Branch dev set up to track remote branch dev from origin.
  • git pull 成功后,git merge 合并存在沖突,先手動(dòng)解決沖突,然后提交,最后 push 到遠(yuǎn)程。
$ git commit -m "merge & fix hello.py"
[dev adca45d] merge & fix hello.py
$ git push origin dev
Counting objects: 10, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (5/5), done.
Writing objects: 100% (6/6), 747 bytes, done.
Total 6 (delta 0), reused 0 (delta 0)
To git@github.com:michaelliao/learngit.git
   291bea8..adca45d  dev -> dev
  • 請(qǐng)記住,如果 git pull 提示“no tracking information“,則說(shuō)明本地分支和遠(yuǎn)程分支的鏈接關(guān)系沒(méi)有創(chuàng)建,使用命令 git branch --set-upstream branch-name origin/branch-name,以上就是多人協(xié)作的工作模式。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1.git的安裝 1.1 在Windows上安裝Git msysgit是Windows版的Git,從https:/...
    落魂灬閱讀 12,843評(píng)論 4 54
  • 聲明:這篇文章來(lái)源于廖雪峰老師的官方網(wǎng)站,我僅僅是作為學(xué)習(xí)之用 Git簡(jiǎn)介 Git是什么? Git是目前世界上最先...
    橫渡閱讀 4,141評(píng)論 3 27
  • 遠(yuǎn)程倉(cāng)庫(kù) 到目前為止,我們已經(jīng)掌握了如何在Git倉(cāng)庫(kù)里對(duì)一個(gè)文件進(jìn)行時(shí)光穿梭,你再也不用擔(dān)心文件備份或者丟失的問(wèn)題...
    歸云丶閱讀 2,040評(píng)論 0 5
  • 石頭小記 一塊奇異形狀的石頭碎了,散在桌子上,一陣悲傷。 它經(jīng)歷了從大石山到大石頭再到小石頭的過(guò)程,從遙遠(yuǎn)的...
    碧風(fēng)寒影閱讀 443評(píng)論 0 3
  • angularjs是由谷歌公司開(kāi)發(fā)并維護(hù)的一款開(kāi)源的javascript框架,其特點(diǎn)用于構(gòu)建單頁(yè)面應(yīng)用(SPA),...
    家娃閱讀 465評(píng)論 1 1

友情鏈接更多精彩內(nèi)容