Git 分支管理 1. 概念 我們來抽象的理解,分支就是科幻電影裡面的平行宇宙,當你正在電腦前努力學習Git的時候,另一個你正在另一個平行宇宙里努力學習SVN。如果兩個平行宇宙互不幹擾,那對現在的你也沒啥影響。不過,在某個時間點,兩個平行宇宙合併了,結果,你既學會了git又學會了SVN! 那麼分支在 ...
Git 分支管理
1. 概念
我們來抽象的理解,分支就是科幻電影裡面的平行宇宙,當你正在電腦前努力學習Git的時候,另一個你正在另一個平行宇宙里努力學習SVN。如果兩個平行宇宙互不幹擾,那對現在的你也沒啥影響。不過,在某個時間點,兩個平行宇宙合併了,結果,你既學會了git又學會了SVN!
那麼分支在實際中有什麼用呢?假設你準備開發一個新功能,但是需要兩周才能完成,第一周你寫了50%的代碼,如果立刻提交,由於代碼還沒寫完,不完整的代碼庫會導致別人不能幹活了。如果等代碼全部寫完再一次提交,又存在丟失每天進度的巨大風險。
現在有了分支,就不用怕了。你創建了一個屬於你自己的分支,別人看不到,還繼續在原來的分支上正常工作,而你在自己的分支上幹活,想提交就提交,直到開發完畢後,再一次性合併到原來的分支上,這樣,既安全,又不影響別人工作。
2.創建與合併分支
git把我們之前每次提交的版本串成一條時間線,這條時間線就是一個分支。截止到目前只有一條時間線,在git里,這個分支叫主分支,即master分支。HEAD嚴格來說不是指向提交,而是指向master,master才是指向提交的,所以,HEAD指向的就是當前分支。
1) 一開始的時候,master分支是一條線,git用master指向最新的提交,再用HEAD指向master,就能確定當前分支,以及當前分支的提交點:
每次提交,master分支都會向前移動一步,這樣,隨著你不斷提交,master分支的線也越來越長。
2)當我們創建新的分支,例如dev時,git新建了一個指針叫dev,指向master相同的提交,再把HEAD指向dev,就表示當前分支在dev上:
git創建一個分支很快,因為除了增加一個dev指針,改變HEAD的指向,工作區的文件都沒有任何變化。
3)不過,從現在開始,對工作區的修改和提交就是針對dev分支了,比如新提交一次後,dev指針往前移動一步,而master指針不變:
4)假如我們在dev上的工作完成了,就可以把dev合併到master上。git怎麼合併呢?最簡單的方法,就是直接把master指向dev的當前提交,就完成了合併:
git合併分支也很快,就改改指針,工作區內容也不變。
5)合併完分支後,甚至可以刪除dev分支。刪除dev分支就是把dev指針給刪掉,刪掉後,我們就剩下了一條master分支:
實戰案例演示:
執行如下命令可以查看當前有幾個分支,並且看到在哪個分支下工作
[root@kai git_test]# git branch * master [root@kai git_test]#
下麵創建一個分支dev並切換到其上進行工作。
[root@kai git_test]# git branch * master [root@kai git_test]# git checkout -b dev Switched to a new branch 'dev' [root@kai git_test]# git branch * dev master [root@kai git_test]#
下麵我們修改code.txt內容,在裡面添加一行,併進行提交。
[root@kai git_test]# echo "add one line" >> code.txt [root@kai git_test]# cat code.txt this is the first line this is the second line this is the third line this is the forth line add one line [root@kai git_test]# git add code.txt [root@kai git_test]# git commit -m "dev_branch_submit" [dev b63902b] dev_branch_submit 1 file changed, 1 insertion(+) [root@kai git_test]#
dev分支的工作完成,我們就可以切換回master分支:
[root@kai git_test]# git commit -m "dev_branch_submit" [dev b63902b] dev_branch_submit 1 file changed, 1 insertion(+) [root@kai git_test]# git checkout master Switched to branch 'master' [root@kai git_test]# git branch dev * master [root@kai git_test]#
master分支查看code.txt,發現添加的內容沒有了。因為那個提交是在dev分支上,而master分支此刻的提交點並沒有變:
[root@kai git_test]# cat code.txt this is the first line this is the second line this is the third line this is the forth line [root@kai git_test]#
現在,我們把dev分支的工作成果合併到master分支上:
[root@kai git_test]# git merge dev Updating f25e944..b63902b Fast-forward code.txt | 1 + 1 file changed, 1 insertion(+) [root@kai git_test]# cat code.txt this is the first line this is the second line this is the third line this is the forth line add one line [root@kai git_test]#
註意到上面的Fast-forward信息,Git告訴我們,這次合併是“快進模式”,也就是直接把master指向dev的當前提交,所以合併速度非常快。
合併完成後,就可以放心地刪除dev分支了,刪除後,查看branch,就只剩下master分支了。
[root@kai git_test]# git branch -d dev Deleted branch dev (was b63902b). [root@kai git_test]# git branch * master [root@kai git_test]#
小結:
查看分支:git branch
創建分支:git branch <name>
切換分支:git checkout <name>
創建+切換分支:git checkout -b <name>
合併某分支到當前分支:git merge <name>
刪除分支:git branch -d <name>
3. 解決衝突
合併分支往往也不是一帆風順的。
再創建並切換一個新分支dev,修改code.txt內容,併進行提交。
[root@kai git_test]# git checkout -b dev Switched to a new branch 'dev' [root@kai git_test]# git branch * dev master [root@kai git_test]# echo "add two line" >> code.txt [root@kai git_test]# cat code.txt this is the first line this is the second line this is the third line this is the forth line add one line add two line [root@kai git_test]# git add code.txt [root@kai git_test]# git commit -m "dev_branch_submit" [dev 7da47b4] dev_branch_submit 1 file changed, 1 insertion(+) [root@kai git_test]#
切換回master分支,在master的code.txt添加一行內容併進行提交。
[root@kai git_test]# git checkout master Switched to branch 'master' [root@kai git_test]# echo "add two line in master" >> code.txt [root@kai git_test]# cat code.txt this is the first line this is the second line this is the third line this is the forth line add one line add two line in master [root@kai git_test]# git add code.txt [root@kai git_test]# git commit -m "matser_branch_submit" [master e57c158] matser_branch_submit 1 file changed, 1 insertion(+) [root@kai git_test]#
現在,master分支和dev分支各自都分別有新的提交,變成了這個樣子:
這種情況下,git無法執行“快速合併”,只能試圖把各自的修改合併起來,但這種合併就可能會有衝突。
執行如下命令嘗試將dev分支合併到master分支上來。
[root@kai git_test]# git merge dev Auto-merging code.txt CONFLICT (content): Merge conflict in code.txt Automatic merge failed; fix conflicts and then commit the result. [root@kai git_test]#
git告訴我們,code.txt文件存在衝突,必須手動解決衝突後再提交。
git status也可以告訴我們衝突的文件:
[root@kai git_test]# git status # On branch master # You have unmerged paths. # (fix conflicts and run "git commit") # # Unmerged paths: # (use "git add <file>..." to mark resolution) # # both modified: code.txt # no changes added to commit (use "git add" and/or "git commit -a") [root@kai git_test]#
查看code.txt的內容。
[root@kai git_test]# cat code.txt this is the first line this is the second line this is the third line this is the forth line add one line <<<<<<< HEAD add two line in master ======= add two line >>>>>>> dev [root@kai git_test]#
git用<<<<<<<,=======,>>>>>>>標記出不同分支的內容,我們修改如下後保存,再次提交:
[root@kai git_test]# cat code.txt this is the first line this is the second line this is the third line this is the forth line add one line add two line in master add two line [root@kai git_test]# git add code.txt [root@kai git_test]# git commit -m "resolve_the_conflict" [master b46a32f] resolve_the_conflict [root@kai git_test]#
現在,master分支和dev分支變成了下圖所示:
用帶參數的git log也可以看到分支的合併情況:
[root@kai git_test]# git commit -m "resolve_the_conflict" [master b46a32f] resolve_the_conflict [root@kai git_test]# git log --graph --pretty=oneline * b46a32ff94fb7cf4b61ce9af466c5433b0469fb2 resolve_the_conflict |\ | * 7da47b4af8476b1c42b03bf04b7d3f103b16da0d dev_branch_submit * | e57c158c60697955a4179ab87244957967419f70 matser_branch_submit |/ * b63902b3874dd3e0a3e0b1ae3e0eb0f30cd616d4 dev_branch_submit * f25e944f3e23532ecc3d2837c32057826920474b delete_code2.txt * 66a9c996749285bdb5e2010b992483e8a1a1771c version4 * f18f0ccadc62b83fa4c6e2222956ba2f2a0e5230 version3 * 6280fa584403809ac2078a81120acf33e6bec836 version2 * 020bf021ec6d1b77836db4e96541d3659251714e version1 [root@kai git_test]#
最後工作完成,可以刪除dev分支。
[root@kai git_test]# git branch -d dev Deleted branch dev (was 7da47b4). [root@kai git_test]# git branch * master [root@kai git_test]#
4. 分支管理策略
通常,合併分支時,如果可能,git會用fast forward模式,但是有些快速合併不能成功而且合併時沒有衝突,這個時候會合併之後並做一次新的提交。但這種模式下,刪除分支後,會丟掉分支信息。
創建切換到dev分支下,新建一個文件code3.txt編輯內容如下,並提交一個commit。
[root@kai git_test]# git checkout -b dev Switched to a new branch 'dev' [root@kai git_test]# echo "this is the first line" >> code3.txt [root@kai git_test]# cat code3.txt this is the first line [root@kai git_test]# git add code3.txt [root@kai git_test]# git commit -m "create_new_code3" [dev a1f6ad6] create_new_code3 1 file changed, 1 insertion(+) create mode 100644 code3.txt [root@kai git_test]#
切換回master分支,編輯code.txt併進行一個提交。
[root@kai git_test]# git checkout master Switched to branch 'master' [root@kai git_test]# ls code.txt [root@kai git_test]# echo "add three line" >> code.txt [root@kai git_test]# git add code.txt [root@kai git_test]# git commit -m "add_new_line" [master f4f5dd0] add_new_line 1 file changed, 1 insertion(+) [root@kai git_test]#
合併dev分支的內容到master分支。出現如下提示,這是因為這次不能進行快速合併,所以git提示輸入合併說明信息,輸入之後合併內容之後git會自動創建一次新的提交。
[root@kai git_test]# git merge dev Merge branch 'dev' # Please enter a commit message to explain why this merge is necessary, # especially if it merges an updated upstream into a topic branch. # # Lines starting with '#' will be ignored, and an empty message aborts # the commit. ~ ~ ~ ".git/MERGE_MSG" 7L, 246C # 保存退出即可! Merge made by the 'recursive' strategy. code3.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 code3.txt [root@kai git_test]#
使用分支命令查看分支信息。
[root@kai git_test]# git log --graph --pretty=oneline * f6807c155cbb5aa59a04a6eb354d1df14d641c3c Merge branch 'dev' |\ | * a1f6ad699c62782ddf3cd2c6fc36773f7e55a365 create_new_code3 * | f4f5dd02154451492b799d9ce8346361e3898b00 add_new_line |/ * b46a32ff94fb7cf4b61ce9af466c5433b0469fb2 resolve_the_conflict |\ | * 7da47b4af8476b1c42b03bf04b7d3f103b16da0d dev_branch_submit * | e57c158c60697955a4179ab87244957967419f70 matser_branch_submit |/ * b63902b3874dd3e0a3e0b1ae3e0eb0f30cd616d4 dev_branch_submit * f25e944f3e23532ecc3d2837c32057826920474b delete_code2.txt * 66a9c996749285bdb5e2010b992483e8a1a1771c version4 * f18f0ccadc62b83fa4c6e2222956ba2f2a0e5230 version3 * 6280fa584403809ac2078a81120acf33e6bec836 version2 * 020bf021ec6d1b77836db4e96541d3659251714e version1 [root@kai git_test]#
刪除dev分支。
[root@kai git_test]# git branch -d dev Deleted branch dev (was a1f6ad6). [root@kai git_test]#
如果要強制禁用fast forward模式,git就會在merge時生成一個新的commit,這樣,從分支歷史上就可以看出分支信息。
禁用fast forward模式:
創建並切換到dev分支,修改code.txt內容,並提交一個commit。
[root@kai git_test]# git checkout -b dev Switched to a new branch 'dev' [root@kai git_test]# echo "add new line" >> code.txt [root@kai git_test]# git add code.txt [root@kai git_test]# git commit -m "add_nwe_line" [dev 5952b2f] add_nwe_line 1 file changed, 1 insertion(+) [root@kai git_test]#
切換回master分支,準備合併dev分支,請註意--no-ff參數,表示禁用Fast forward:
[root@kai git_test]# git merge --no-ff -m 'forbid_fastforward_merge' dev Merge made by the 'recursive' strategy. code.txt | 1 + 1 file changed, 1 insertion(+) [root@kai git_test]#
因為本次合併要創建一個新的commit,所以加上-m參數,把commit描述寫進去。
合併後,我們用git log看看分支歷史:
可以看到,不使用Fast forward模式,merge後就像這樣:
[root@kai git_test]# git log --graph --pretty=oneline * aa22465083e104bf61d7a5c1b8190fe48557adf7 forbid_fastforward_merge |\ | * 5952b2f6b216ca88484ab92aafc636077c30c9a3 add_nwe_line |/ * f6807c155cbb5aa59a04a6eb354d1df14d641c3c Merge branch 'dev' |\ | * a1f6ad699c62782ddf3cd2c6fc36773f7e55a365 create_new_code3 * | f4f5dd02154451492b799d9ce8346361e3898b00 add_new_line |/ * b46a32ff94fb7cf4b61ce9af466c5433b0469fb2 resolve_the_conflict |\ | * 7da47b4af8476b1c42b03bf04b7d3f103b16da0d dev_branch_submit * | e57c158c60697955a4179ab87244957967419f70 matser_branch_submit |/ * b63902b3874dd3e0a3e0b1ae3e0eb0f30cd616d4 dev_branch_submit * f25e944f3e23532ecc3d2837c32057826920474b delete_code2.txt * 66a9c996749285bdb5e2010b992483e8a1a1771c version4 * f18f0ccadc62b83fa4c6e2222956ba2f2a0e5230 version3 * 6280fa584403809ac2078a81120acf33e6bec836 version2 * 020bf021ec6d1b77836db4e96541d3659251714e version1 [root@kai git_test]#
5. Bug分支
軟體開發中,bug就像家常便飯一樣。有了bug就需要修複,在git中,由於分支是如此的強大,所以,每個bug都可以通過一個新的臨時分支來修複,修複後,合併分支,然後將臨時分支刪除。
當你接到一個修複一個代號001的bug的任務時,很自然地,你想創建一個分支bug-001來修複它,但是,等等,當前正在dev上進行的工作還沒有提交:
[root@kai git_test]# echo "this line is writing...." >> code.txt [root@kai git_test]# git status # On branch dev # 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: code.txt # no changes added to commit (use "git add" and/or "git commit -a") [root@kai git_test]#
並不是你不想提交,而是工作只進行到一半,還沒法提交,預計完成還需1天時間。但是,必須在兩個小時內修複該bug,怎麼辦?
git還提供了一個stash功能,可以把當前工作現場“儲藏”起來,等以後恢復現場後繼續工作:
[root@kai git_test]# git stash Saved working directory and index state WIP on dev: 5952b2f add_nwe_line HEAD is now at 5952b2f add_nwe_line [root@kai git_test]#
首先確定要在哪個分支上修複bug,假定需要在master分支上修複,就從master創建臨時分支:
[root@kai git_test]# git checkout master Switched to branch 'master' [root@kai git_test]# git checkout -b bug-001 Switched to a new branch 'bug-001' [root@kai git_test]#
現在模擬修複bug,把 add new line刪掉,然後提交。
[root@kai git_test]# vim code.txt [root@kai git_test]# cat code.txt this is the first line this is the second line this is the third line this is the forth line add one line add two line in master add two line add three line [root@kai git_test]# git add code.txt [root@kai git_test]# git commit -m "repair_bug" [bug-001 f52de9b] repair_bug 1 file changed, 1 deletion(-)
修複完成後,切換到master分支,並完成合併,最後刪除bug-001分支。
[root@kai git_test]# git checkout master Switched to branch 'master' [root@kai git_test]# git merge --no-ff -m "repair_bug" bug-001 Merge made by the 'recursive' strategy. code.txt | 1 - 1 file changed, 1 deletion(-) [root@kai git_test]# git branch -d bug-001 Deleted branch bug-001 (was f52de9b). [root@kai git_test]#
現在bug-001修複完成,是時候接著回到dev分支幹活了!
[root@kai git_test]# git checkout dev Switched to branch 'dev' [root@kai git_test]# git status # On branch dev nothing to commit, working directory clean [root@kai git_test]#
工作區是乾凈的,剛纔的工作現場存到哪去了?用git stash list命令看看:
[root@kai git_test]# git stash list stash@{0}: WIP on dev: 5952b2f add_nwe_line [root@kai git_test]#
工作現場還在,git把stash內容存在某個地方了,但是需要恢復一下
[root@kai git_test]# git stash pop # On branch dev # 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: code.txt # no changes added to commit (use "git add" and/or "git commit -a") Dropped refs/stash@{0} (ff39e6e47ea948b97d2587f2205c465fa789c5e9) [root@kai git_test]# git status # On branch dev # 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: code.txt # no changes added to commit (use "git add" and/or "git commit -a") [root@kai git_test]#
小結:
修複bug時,我們會通過創建新的bug分支進行修複,然後合併,最後刪除;
當手頭工作沒有完成時,先把工作現場git stash一下,然後去修複bug,修複後,再git stash pop,恢復工作現場。