說起 Git 分支的內容,咱們要先理解 Git 保存的並非文件的變化或者差別,而是一系列的不一樣時刻的文件快照。git
Git 在進行提交操做的時候,會保存一個提交對象(commit object)。這個提交對象包含了一個指向暫存內容快照的指針,還包括提交者的姓名,郵箱,提交的信息以及指向它的父對象的指針。固然,首次提交產生的提交對象沒有父對象。其餘普通提交操做產生的對象都有一個父對象,當多個分支合併產生的提交對象則有多個父對象。github
上面這篇文章中,咱們提到每次提交,Git 會將它們串成一條時間線,這時間線就是一個分支。未新建分支以前,這個就是主分支,便是 master
分支。segmentfault
Git 如何建立新分支?使用 git branch <branch_name>
,這裏它會建立一個能夠移動的新指針。好比建立一個 dev 分支:微信
$ git branch dev
這時,便會在提交對象上建立一個指針,以下圖:分佈式
如今建立了一個分支,可是 Git 如何分辨兩個分支,如何知道目前在哪一個分支?這裏要說起一個特殊指針 HEAD
。學習
HEAD
嚴格來講不是指向提交,默認狀況下,而是指向 master
,而 master
或者新建立的 dev
纔是指向提交,HEAD
指向的就是當前分支。剛纔 git branch dev
僅僅是建立了一個新分支,並不會自動切換到新分支上去。fetch
HEAD 指向的是當前所在的分支,可使用命令 git log
查看各個分支當前所指的對象。配合參數 --decorate
:spa
$ git log --oneline --decorate 861b17e (HEAD -> master, dev) Initial commit of project
此時,master
和 dev
分支均指向校驗和爲 861b17e
開頭的提交對象。.net
切換已經存在的分支,可使用 git checkout <branch>
或者 git switch <branch>
,這裏使用的是 git switch
更容易理解:
$ git switch dev Switched to branch 'dev'
這樣 HEAD 就指向了 dev
分支。
上面是將建立跟切換操做分開。Git 還提供一個命令可以建立的同時切換到分支:
$ git switch -c iss007
或者
$ git checkout -b iss007
使用帶 -c
參數的 git switch
命令,或者使用帶 -b
參數的 checkout
命令。
其實上面的命令能夠分解爲:
$ git branch iss007 $ git switch iss007 $ git branch iss007 $ git checkout iss007
假設如今在 iss007
分支上工做,並作了提交。在這個過程當中,iss007
分支會不斷向前推動。
$ vim ISSUE $ git commit -a -m "Fix issue 007 [issue 007]"
假設,這個時候有另一個緊急的問題須要解決。使用 Git 這個時候不須要將這個問題跟 issue 007 混合在一塊兒。也不用去還原關於 iss007 的修改,再添加關於這個緊急問題的修改,只須要須要回到 master
分支,建立新分支解決緊急問題。
這裏須要注意,切換到 master
分支以前,須要留意工做目錄和暫存區有沒有未提交的文件,它會檢測出分支衝突阻止切換分支(待會介紹這種狀況)。這裏假設工做目錄是乾淨的:
$ git switch master Switched to branch 'master' $ git switch -c hotfix Switched to a new branch 'hotfix' $ vi ISSUE $ git commit -a -m "fixed the hot issue"
如今緊急問題已經修復,這個時候就能夠合併回 master 分支部署到線上,以下:
$ git switch master $ git merge hotfix Updating 861b17e..48a6cfa Fast-forward ISSUE | 1 + 1 file changed, 1 insertion(+) create mode 100644 ISSUE
注意 Fast-forward
,這個表示的是「快速模式」,也就是直接將 master 指向 hotfix 的當前提交,合併速度快。
注意:,並非全部的合併都能 Fast-forward
。
同時,咱們能夠刪除 hotfix
分支,由於已經完成任務了:
$ git branch -d hotfix Deleted branch hotfix (was 48a6cfa).
如今緊急的問題已經修復部署上線,能夠回到 iss007
分支繼續修改文件。假設 iss007
分支也通過修改解決了問題。
這個時候也考慮合併會 master 部署到線上:
$ git switch master $ git merge iss007 CONFLICT (add/add): Merge conflict in ISSUE Auto-merging ISSUE Automatic merge failed; fix conflicts and then commit the result.
這個時候會出現合併衝突,由於 iss007 分支與 hotfix 分支修改的是同一個文件。Git 不會自動建立新的合併提交。須要手動解決衝突以後再提交。
使用 git status
查看處於未合併狀態的文件:
$ git status On branch master You have unmerged paths. (fix conflicts and run "git commit") (use "git merge --abort" to abort the merge) Unmerged paths: (use "git add <file>..." to mark resolution) both added: ISSUE no changes added to commit (use "git add" and/or "git commit -a")
如今查看衝突的文件:
<<<<<<< HEAD Fix hot issue. ======= Fix issue 007. Fix issue continue and done. >>>>>>> iss007
Git 用 <<<<<<<
,=======
,>>>>>>>
標記出不一樣分支的內容,修改內容以下:
Fix hot issue. Fix issue 007. Fix issue continue and done.
將標記的部分刪除,根據需求解決衝突,而後用 git add
將其標記爲衝突已解決。
$ git add ISSUE $ git status On branch master All conflicts fixed but you are still merging. (use "git commit" to conclude merge) Changes to be committed: modified: ISSUE
這個時候就能夠提交部署了:
git commit -a -m "Merge branch 'iss007'" [master 7059dd0] Merge branch 'iss007'
git branch
除了建立和刪除分支。不加參數時,命令可以獲得全部分支的列表:
$ git branch dev iss007 * master
*
號表示當前的分支(即便 HEAD
指針指向的分支)。
加上 -v
參數也能夠查看每一個分支最後的提交信息:
$ git branch -v dev 861b17e Initial commit of project iss007 5267039 Fix issue 007 again * master 7059dd0 Merge branch 'iss007'
git branch
命令還有兩個選項 --merged
和 --no-merged
。這兩個選項分別過濾合併或還沒有合併到當前的分支。
例如:查看已經合併到當前的分支:
$ git branch --merged iss007 * master
由於已經合併過 ·iss007`,因此這個時候也能夠刪除該分支,這裏並不會失去任何東西:
$ git branch -d iss007 Deleted branch iss007 (was 5267039).
查看全部包含未合併工做的分支,可使用 git branch --no-merged
:
$ git branch --no-merged dev
這裏顯示了 dev 分支,它包含了還未合併的工做,如今嘗試使用刪除命令會運行失敗:
$ git branch -d dev error: The branch 'dev' is not fully merged. If you are sure you want to delete it, run 'git branch -D dev'.
若是真的想要刪除這些未合併的工做,能夠根據下面的提示,使用 -D
選項強制刪除。
多人協做的狀況下,你們都會往 master
和 dev
分支推送各自的修改。
可是當協做者修改的是同個文件,且先一部提交到遠程倉庫時,這個時候,當你修改完問題提交的時候,就會提示衝突。
解決辦法:用 git pull
抓取文件,在本地合併解決衝突再推送便可。
這裏參考上面衝突解決的步驟就能夠。
每次合併再 push 後,分支會變得混亂。
Git 有一種成爲 rebase 的操做,可以讓 Git 的提交歷史變成直線。
這裏我用以前學習的例子來加以說明。
和遠程分支同步後,對文件作了兩次提交,用 git log
查看:
* 24be579 (HEAD -> master) add author * 94448fe add comment * 6a7291a store Git Learn * e36f4e9 (origin/master) add content about co-operative and update catalog * 6cd4087 add knowledge about feature branch
注意到 Git 用 (Head -> master)
和 (origin/master)
標識當前分支的 HEAD 和遠程 origin 的位置分別是 24be579 add author
和 e36f4e9 add content..
,本地比遠程分支快 3 個提交
如今嘗試推送本地分支:
$ git push origin master To github.com:username/git_learn.git ! [rejected] master -> master (fetch first) error: failed to push some refs to 'git@github.com:username/git_learn.git' hint: Updates were rejected because the remote contains work that you do hint: not have locally. This is usually caused by another repository pushing hint: to the same ref. You may want to first integrate the remote changes hint: (e.g., 'git pull ...') before pushing again. hint: See the 'Note about fast-forwards' in 'git push --help' for details.
失敗了,說明有人先往遠程庫推送了分支。先 pull 一下
$ git pull remote: Enumerating objects: 4, done. remote: Counting objects: 100% (4/4), done. remote: Compressing objects: 100% (1/1), done. remote: Total 3 (delta 1), reused 3 (delta 1), pack-reused 0 Unpacking objects: 100% (3/3), done. From github.com:damengsanqianqiu/git_learn e36f4e9..538bd7c master -> origin/master CONFLICT (add/add): Merge conflict in hello.py Auto-merging hello.py Automatic merge failed; fix conflicts and then commit the result.
這裏自動合併失敗,手動解決衝突(詳情略)。
解決衝突,再提交。再用 git status
查看狀態
$ git status On branch master Your branch is ahead of 'origin/master' by 4 commits. (use "git push" to publish your local commits)
加上剛纔合併的提交,如今咱們分支比遠程分支超前 4 個提交。
用 git log
看看
$ git log --graph --pretty=oneline --abbrev-commit * 2027993 (HEAD -> master) fix conflict of hello.py |\ | * 538bd7c (origin/master) set exit = 1 * | 24be579 add author * | 94448fe add comment * | 6a7291a store Git Learn |/ * e36f4e9 add content about co-operative and update catalog
如今分支比較亂,這個時候,rebase 就派上用場了,用 git rebase
試試:
$ git rebase First, rewinding head to replay your work on top of it... Applying: add comment Using index info to reconstruct a base tree... M hello.py Falling back to patching base and 3-way merge... Auto-merging hello.py Applying: add author Using index info to reconstruct a base tree... M hello.py Falling back to patching base and 3-way merge... Auto-merging hello.py
再用 git log
看看,
$ git log --graph --pretty=oneline --abbrev-commit * 2027993 (HEAD -> master) fix conflict of hello.py * 24be579 add author * 94448fe add comment * 6a7291a store Git Learn * 538bd7c (origin/master) set exit = 1
原來分叉的提交如今變成一條直線。咱們注意到 Git 把咱們本地的提交「挪動」了位置,放到了 538bd7c (origin/master) set exit = 1
以後,這樣整個提交歷史就成了一條直線。rebase 操做先後,最終的提交內容是一直的,可是,本地的 commit 修改內容已經變化了,他們的修改再也不基於 e36f4e9 add content...
,而是基於 538bd7c (origin/master) set exit = 1
,但最後的提交 2027993
內容是一致的。
這就是 rebase 操做的特色:把分叉的提交歷史「整理」成一條直線,看上去更直觀。缺點就是本地的分叉提交已經被修改過了。
關於變基與合併,這裏會有不一樣的觀點,有的以爲提交歷史就是記錄發生過什麼,使用變基會將痕跡抹除,另外的觀點會以爲提交歷史就是項目過程發生的事情,沒人在乎初版的手稿,大部分手冊也是屢次修訂後才能方便使用。
這裏並無必要追究一個完整簡單的答案。須要根據實際狀況去判斷做出選擇。
不過,整體的原則是,還沒有推送或分享的本地修改,能夠根據狀況執行變基清理歷史,已推送到別處的提交,不要執行變基操做。
以上就是本篇的主要內容。
目前,關於 Git 的介紹就已經算是更新完成。可是,這些內容還遠遠沒有將 Git 講透徹。可是這些內容可以讓你簡單瞭解 Git,也但願這些內容可以讓你上手學習,慢慢了解。
這裏如果但願更深刻了解 Git 的使用能夠查看官方文檔:
https://git-scm.com/book/en/v2
歡迎關注微信公衆號《書所集錄》