寫在前面
Git 的官網上有很詳細的使用教程(當然有翻譯版本),具體地址是 https://git-scm.com/book/zh/v2。唯一不足就是,很多講解並沒有實機演示。但是,毫無疑問,官網資料是最全面的!如果有任何疑問,可以去官網看看!
分支
使用分支意味着你可以把你的工作從開發主線上分離開來,以免影響開發主線。 Git 的一大特點就是對於分支的支持!Git 的分支可謂是難以置信的輕量級,它的新建操作幾乎可以在瞬間完成,並且在不同分支間切換起來也差不多一樣快。
遠程分支
在瞭解分支之前,必須要先了解遠程分支。因爲在分支管理命令 git branch
中很多參數都是與遠程分支有關係的。在之前的博文中說過,Git 也可以有一個服務器中心倉庫!自然,服務器中的中心倉庫也會有多個分支。其中,Git 引入了一個概念:upstream。一個分支的 upstream,其實就是與遠程分支做關聯,告訴 Git,默認此分支爲推送及拉取的遠程分支的信息。如下圖所示:
在 Git 中,建立本地分支與遠程分支的關聯是通過 git branch
命令的參數 -u <upstream> 或 --set-upstream-to=<upstream>
或者 git branch -t 或 --track
來設置。這個過程被稱爲爲當前分支設置 upstream。在建立了分支之間的關聯之後,我們提交代碼時就會自動使用關聯的分支。如果不設置,則在提交代碼時必須指定 git push -u origin master
(只需要指定一次即可)。否則將出現以下錯誤:
關於遠程分支的詳細介紹,參見博文Git 之四 通信協議(HTTPS、SSH、Git)、使用遠程倉庫(GitHub、GitLab、Gitee等)。
創建與管理
Git 分支創建與管理是通過命令 git branch
來進行處理(增、刪、改、查)。創建分支非常簡單,如下圖使用不帶任何參數的 git branch
命令來創建一個名爲 Dev 和 Feature 的分支:
下圖顯示了創建分支的示意圖
下圖顯示了創建了分支之後,當前工作目錄下 .git 目錄的情況
該命令實際上還有許多的參數,具體格式有如下這些:
git branch [--color[=<when>] | --no-color] [--show-current]
[-v [--abbrev=<length> | --no-abbrev]]
[--column[=<options>] | --no-column] [--sort=<key>]
[(--merged | --no-merged) [<commit>]]
[--contains [<commit]] [--no-contains [<commit>]]
[--points-at <object>] [--format=<format>]
[(-r | --remotes) | (-a | --all)]
[--list] [<pattern>…]
git branch [--track | --no-track] [-f] <branchname> [<start-point>] # 創建名爲 <branchname> 的分支
git branch (--set-upstream-to=<upstream> | -u <upstream>) [<branchname>]
git branch --unset-upstream [<branchname>]
git branch (-m | -M) [<oldbranch>] <newbranch>
git branch (-c | -C) [<oldbranch>] <newbranch>
git branch (-d | -D) [-r] <branchname>…
git branch --edit-description [<branchname>]
下面是常用的一些參數:
-d、--delete、-D
:刪除一個分支。 分支必須在其上游分支中完全合併,或者如果沒有使用--track
或--set-upstream-to
設置上游,則必須在 HEAD 中合併。其中,-D
表示--delete
和--force
的組合。
--create-reflog
:創建分支的 reflog。 這將激活對分支引用所做的所有更改的記錄,從而可以使用基於日期的 sha1 表達式,例如“<branchname> @ {yesterday}”。-f、--force
:將 <branchname> 重置爲 <startpoint>,即使 <branchname> 已存在。 沒有-f,git branch拒絕更改現有分支。 與-d(或–delete)組合使用,允許刪除分支,而不管其合併狀態如何。 與-m(或–move)結合使用,即使新分支名稱已存在,也允許重命名分支,這同樣適用於-c(或 --copy)。-m、--move、-M
:移動/重命名分支和相應的 reflog。其中,-M
表示--move
和--force
的組合。-c、--copy、-C
:複製分支和相應的 reflog。其中,-C
表示--copy
和--force
的組合。-r、--remotes
:列出或刪除(如果與-d一起使用)遠程跟蹤分支。 與--list
組合以匹配可選模式。-a、--all
:列出遠程跟蹤分支和本地分支。 與–list組合以匹配可選模式。
-l、--list
:列出分支。 使用可選的 <pattern> …,例如 git branch --list’maint- *’,僅列出與模式匹配的分支。該命令與 不帶任何參數的git branch
結果相同。
--show-current
:打印當前分支的名稱。 在分離的 HEAD 狀態下,不打印任何內容。
-v、-vv、--verbose
:在列表模式下,顯示每個頭的sha1和提交主題行,以及與上游分支(如果有)的關係。 如果給出兩次,則打印鏈接的工作樹的路徑(如果有的話)和上游分支的名稱(另請參閱git remote show <remote>)。 請注意,當前工作樹的HEAD將不會打印其路徑(它將始終是您當前的目錄)。
- :新的分支頭將指向此提交。 它可以作爲分支名稱,commit- 如果省略此選項,則將使用當前 HEAD。
-t、--track
:When creating a new branch, set upbranch.<name>.remote
andbranch.<name>.merge
configuration entries to mark the start-point branch as “upstream” from the new branch. This configuration will tell git to show the relationship between the two branches in git status and git branch -v. Furthermore, it directs git pull without arguments to pull from the upstream when the new branch is checked out.
This behavior is the default when the start point is a remote-tracking branch. Set the branch.autoSetupMerge configuration variable to false if you want git switch, git checkout and git branch to always behave as if --no-track were given. Set it to always if you want this behavior when the start-point is either a local or remote-tracking branch.--set-upstream
:該參數以棄用。使用--track
或--set-upstream-to
代替。-u <upstream>、--set-upstream-to=<upstream>
:設置本地分支<branchname> 的跟蹤信息,即將遠程倉庫分支 <upstream> 與本地分支 <branchname> 關聯。 如果未指定本地分支 <branchname>,則默認爲當前分支。--no-track
:Do not set up “upstream” configuration, even if the branch.autoSetupMerge configuration variable is true.--edit-description
:Open an editor and edit the text to explain what the branch is for, to be used by various other commands (e.g. format-patch, request-pull, and merge (if enabled)). Multi-line explanations may be used.--merged [<commit>]
:Only list branches whose tips are reachable from the specified commit (HEAD if not specified). Implies --list, incompatible with --no-merged.--no-merged [<commit>]
:Only list branches whose tips are not reachable from the specified commit (HEAD if not specified). Implies --list, incompatible with --merged.
切換分支
Git 中切換指定的分支是通過命令 git switch
和 git checkout
來實現的。其中,命令 git switch
爲新增的一個命令,從官網可能找不到該命令的介紹。
git switch
該命令用來切換到指定的分支。 更新工作樹和索引以匹配分支。 所有新提交都將添加到此分支。下圖顯示了分支切換前後,當前工作目錄下 .git 目錄的情況
該命令還支持很多參數,具有以下 4 種格式:
git switch [<options>] [--no-guess] <branch>
git switch [<options>] --detach [<start-point>]
git switch [<options>] (-c|-C) <new-branch> [<start-point>]
git switch [<options>] --orphan <new-branch>
下面是常用的一些參數:
<branch>
:要切換到的分支的名字。如下圖所示:
<new-branch>
:新分支的名字<start-point>
:新分支的起點。 指定 <start-point> 允許您基於歷史記錄中的某個其他點創建分支,而不是 HEAD 當前指向的分支。 (或者,在–detach的情況下,允許您檢查和從其他點分離。)-c <new-branch>、--create <new-branch>
:從 <start-point> 處先創建一個名爲 <new-branch> 的新分支,然後在切換到創建的分支。 這個參數用於簡化以下這個流程,相當於兩個命令合二爲一:$ git branch <new-branch> $ git switch <new-branch>
-C <new-branch>、--force-create <new-branch>
:與--create
類似,只是如果 <new-branch> 已經存在,它將被重置爲 <start-point>。 這個參數用於簡化以下這個流程,相當於兩個命令合二爲一:$ git branch -f <new-branch> $ git switch <new-branch>
--orphan <new-branch>
:創建一個名爲 <new-branch> 的新孤立分支。 刪除所有跟蹤的文件。-t、--track
:創建新分支時,設置 “upstream” 配置。 -c 是隱含的。 有關詳細信息,請參閱 git-branch 中的 --track。
If no -c option is given, the name of the new branch will be derived from the remote-tracking branch, by looking at the local part of the refspec configured for the corresponding remote, and then stripping the initial part up to the “*”. This would tell us to use hack as the local branch when branching off of origin/hack (or remotes/origin/hack, or even refs/remotes/origin/hack). If the given name has no slash, or the above guessing results in an empty name, the guessing is aborted. You can explicitly give a name with -c in such a case.--no-track
:Do not set up “upstream” configuration, even if the branch.autoSetupMerge configuration variable is true.
git checkout
該命令用來切換分支或還原工作樹文件。命令格式如下:
git checkout [-q] [-f] [-m] [<branch>]
git checkout [-q] [-f] [-m] --detach [<branch>]
git checkout [-q] [-f] [-m] [--detach] <commit>
git checkout [-q] [-f] [-m] [[-b|-B|--orphan] <new_branch>] [<start_point>]
git checkout [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <paths>…
git checkout [<tree-ish>] [--] <pathspec>…
git checkout (-p|--patch) [<tree-ish>] [--] [<paths>…]
合併分支
命令 git merge
將指定分支的提交的更改(自其歷史記錄與當前分支分開時)合併到當前分支中。 git pull 使用此命令來合併來自另一個存儲庫的更改,並且可以手動使用此命令將更改從一個分支合併到另一個分支。該命令還支持很多參數,具有如下 2 種格式:
git merge [-n] [--stat] [--no-commit] [--squash] [--[no-]edit]
[-s <strategy>] [-X <strategy-option>] [-S[<keyid>]]
[--[no-]allow-unrelated-histories]
[--[no-]rerere-autoupdate] [-m <msg>] [-F <file>] [<commit>…]
git merge (--continue | --abort | --quit)
下面是常用的一些參數:
--commit、--no-commit
:執行合併並提交結果。 使用 --no-commit 執行合併並在創建合併提交之前停止,以使用戶有機會在提交之前檢查並進一步調整合並結果。--edit、-e、--no-edit
:在提交成功的機械合併之前調用編輯器以進一步編輯自動生成的合併消息,以便用戶可以解釋並證明合併。 --no-edit選項可用於接受自動生成的消息(通常不鼓勵這樣做)。 如果從命令行使用-m選項提供草稿消息並希望在編輯器中編輯它,則–edit(或-e)選項仍然有用。
注意:該命令是指將指定分支合併到當前分支。因此,在合併前必須要切換當要合併的目的分支中執行該命令。 下面我們我們來實際操作一下:
ZCShou@ZCShou-PC MINGW64 /e/N_W_Z/N_W_Z_1 (master)
$ git switch Dev
Switched to branch 'Dev' # 切換到 Dev 分支
ZCShou@ZCShou-PC MINGW64 /e/N_W_Z/N_W_Z_1 (Dev)
$ vim a.txt # 編輯 a.txt
ZCShou@ZCShou-PC MINGW64 /e/N_W_Z/N_W_Z_1 (Dev)
$ git add a.txt
warning: LF will be replaced by CRLF in a.txt.
The file will have its original line endings in your working directory
ZCShou@ZCShou-PC MINGW64 /e/N_W_Z/N_W_Z_1 (Dev)
$ git commit -m "Dev 分支的修改"
[Dev a33bc75] Dev 分支的修改 # 提交修改(目前是 Dev 分支)
1 file changed, 1 insertion(+)
ZCShou@ZCShou-PC MINGW64 /e/N_W_Z/N_W_Z_1 (Dev)
$ git switch master
Switched to branch 'master' # 這裏 切換回 maser 分支
ZCShou@ZCShou-PC MINGW64 /e/N_W_Z/N_W_Z_1 (master)
$ git merge Dev # 將 Dev 分支合併到當前分支(當前分支爲 master)
Updating 7d6d9bc..a33bc75
Fast-forward
a.txt | 1 +
1 file changed, 1 insertion(+)
ZCShou@ZCShou-PC MINGW64 /e/N_W_Z/N_W_Z_1 (master)
$ git branch -v # 合併之後的分支情況
Dev a33bc75 Dev 分支的修改
Feature 7d6d9bc 第一次提交
* master a33bc75 Dev 分支的修改
ZCShou@ZCShou-PC MINGW64 /e/N_W_Z/N_W_Z_1 (master)
$
如下圖所示:
由於當前 master 分支所指向的提交是你當前提交(有關 hotfix 的提交)的直接上游,所以 Git 只是簡單的將指針向前移動。 換句話說,當你試圖合併兩個分支時,如果順着一個分支走下去能夠到達另一個分支,那麼 Git 在合併兩者的時候,只會簡單的將指針向前推進(指針右移),因爲這種情況下的合併操作沒有需要解決的分歧——這就叫做 “快進(fast-forward)”。
通常,在合併完成分支之後,我們要刪除已經被合併的分支。因爲分支已經被合併,我們一般不再需要它。有新需求時,我們會再次創建新的分支。
如上圖,如果我們要合併 Feature 分支,操作流程與上面合併 Dev 分支相同,但是 Git 的合併行爲卻不相同。因爲 master 指向的提交併不是 Feature 分支的直接上游。Git 不得不做一些額外的工作。 出現這種情況的時候,Git 會使用兩個分支的末端所指的快照(8 和 4)以及這兩個分支的工作祖先(2),做一個簡單的三方合併。
需要指出的是,Git 會自行決定選取哪一個提交作爲最優的共同祖先,並以此作爲合併的基礎;這和更加古老的 CVS 系統或者 Subversion (1.5 版本之前)不同,在這些古老的版本管理系統中,用戶需要自己選擇最佳的合併基礎。 Git 的這個優勢使其在合併操作上比其他系統要簡單很多。
遇到衝突時的分支合併
如果你在兩個不同的分支中,對同一個文件的同一個部分進行了不同的修改,Git 就沒法乾淨的合併它們,在合併它們的時候就會產生合併衝突:
此時 Git 做了合併,但是沒有自動地創建一個新的合併提交。 Git 會暫停下來,等待你去解決合併產生的衝突。 你可以在合併衝突後的任意時刻使用 git status 命令來查看那些因包含合併衝突而處於未合併(unmerged)狀態的文件
其他
用到時再添加!
參考
- https://git-scm.com/book/zh/v2/Git-%E5%88%86%E6%94%AF-%E5%88%86%E6%94%AF%E7%9A%84%E6%96%B0%E5%BB%BA%E4%B8%8E%E5%90%88%E5%B9%B6