Git 沙盒模擬實戰(基礎篇)

Git 沙盒模擬實戰

分支

現有一個主分支前端

image-20200911190747381

建立分支

# 建立分支
$ git branch bugFix
# 切換到指定分支
$ git checkout bugFix

或者git

# 建立分支,並切換到該分支
$ git checkout -b bugFix

切換到指定分支後,工做區也會更新。好比在 IDEA 中工程文件會切換到該分支的保存的版本數據。shell

image-20200911190925836

合併分支

先要建立分支,切換到該分支,並進行提交ide

$ git checkout -b bugFix

$ git commit -m "c2"

image-20200911191243994

$ git checkout master

$ git commit -m "c3"

image-20200911191533239

# 合併指定分支到當前分支
$ git merge "須要合併的分支"

image-20200911191605939

Rebase

第二種合併分支的方法是 git rebase。Rebase 實際上就是取出一系列的提交記錄,「複製」它們,而後在另一個地方逐個的放下去。學習

Rebase 的優點就是能夠創造更線性的提交歷史,這聽上去有些難以理解。若是隻容許使用 Rebase 的話,代碼庫的提交歷史將會變得異常清晰。設計

image-20200911193322069

$ git rebase master

image-20200911193111298

其餘關於分支命令:調試

# 列出全部本地分支
$ git branch
 
# 列出全部遠程分支
$ git branch -r
 
# 列出全部本地分支和遠程分支
$ git branch -a

# 新建一個分支,指向指定commit
$ git branch [branch] [commit]

# 選擇一個commit,合併進當前分支
$ git cherry-pick [commit]

# 新建一個分支,與指定的遠程分支創建追蹤關係
$ git branch --track [branch] [remote-branch]

# 創建追蹤關係,在現有分支與指定的遠程分支之間
$ git branch --set-upstream [branch] [remote-branch]

# 刪除分支
$ git branch -d [branch-name]
 
# 刪除遠程分支
$ git push origin --delete [branch-name]
$ git branch -dr [remote/branch]

分離 HEAD

HEAD 是一個對當前檢出記錄的符號引用 —— 也就是指向你正在其基礎上進行工做的提交記錄。code

HEAD 老是指向當前分支上最近一次提交記錄。大多數修改提交樹的 Git 命令都是從改變 HEAD 的指向開始的。排序

HEAD 一般狀況下是指向分支名的(如 bugFix)。在你提交時,改變了 bugFix 的狀態,這一變化經過 HEAD 變得可見。three

分離的 HEAD 就是讓其指向了某個具體的提交記錄而不是分支名。在命令執行以前的狀態以下所示:

HEAD -> bugFix-> C4

HEAD 指向 bugFix, bugFix 指向 C4

image-20200911195102361

$ git checkout c4

image-20200911195202561

相對引用

經過指定提交記錄哈希值的方式在 Git 中移動不太方便。在實際應用時,並無像上圖中這麼漂亮的可視化提交樹供你參考,因此你就不得不用 git log 來查查看提交記錄的哈希值。

例如上面的介紹中的提交記錄的哈希值多是 fed2da64c0efc5293610bdd892f82a58e8cbc5d8。舌頭都快打結了吧...

比較使人欣慰的是,Git 對哈希的處理很智能。你只須要提供可以惟一標識提交記錄的前幾個字符便可。所以我能夠僅輸入fed2 而不是上面的一長串字符。

正如我前面所說,經過哈希值指定提交記錄很不方便,因此 Git 引入了相對引用。這個就很厲害了!

使用相對引用的話,你就能夠從一個易於記憶的地方(好比 bugFix 分支或 HEAD)開始計算。

相對引用很是給力,這裏我介紹兩個簡單的用法:

  • 使用 ^ 向上移動 1 個提交記錄
  • 使用 ~<num> 向上移動多個提交記錄,如 ~3

首先看看操做符 (^)。把這個符號加在引用名稱的後面,表示讓 Git 尋找指定提交記錄的父提交。

因此 master^ 至關於「master 的父節點」。

master^^master 的第二個父節點

image-20200911200338921

$ git checkout bugFix^

image-20200911200436148


若是你想在提交樹中向上移動不少步的話,敲那麼多 ^ 貌似也挺煩人的,Git 固然也考慮到了這一點,因而又引入了操做符 ~

該操做符後面能夠跟一個數字(可選,不跟數字時與 ^ 相同,向上移動一次),指定向上移動多少次。我們仍是經過實際操做看一下吧

我使用相對引用最多的就是移動分支。能夠直接使用 -f 選項讓分支指向另外一個提交。例如:

$ git branch -f master HEAD~3

上面的命令會將 master 分支強制指向 HEAD 的第 3 級父提交。

image-20200911201012151

移動 HEADbugFix 到目標所示的位置。

$ git checkout HEAD~1

image-20200911201156183

$ git branch -f bugFix HEAD~1

image-20200911201338403

撤銷變動

在 Git 裏撤銷變動的方法不少。和提交同樣,撤銷變動由底層部分(暫存區的獨立文件或者片斷)和上層部分(變動究竟是經過哪一種方式被撤銷的)組成。咱們這個應用主要關注的是後者。

主要有兩種方法用來撤銷變動 —— 一是 git reset,還有就是 git revert。接下來我們逐個進行講解。


git reset 經過把分支記錄回退幾個提交記錄來實現撤銷改動。你能夠將這想象成「改寫歷史」。git reset 向上移動分支,原來指向的提交記錄就跟歷來沒有提交過同樣。

雖然在你的本地分支中使用 git reset 很方便,可是這種「改寫歷史」的方法對你們一塊兒使用的遠程分支是無效的哦!

爲了撤銷更改並分享給別人,咱們須要使用 git revert

分別撤銷 local 分支和 pushed 分支上的最近一次提交。共須要撤銷兩個提交(每一個分支一個)。

記住 pushed 是遠程分支,local 是本地分支

image-20200911203311362

$ git reset master

image-20200911203637240

# 先切換到 pushed
$ git checkout pushed
$ git revert c2  # 須要用到提交的哈希碼

image-20200911203831371

移動(整理)提交記錄

到如今咱們已經學習了 Git 的基礎知識 —— 提交、分支以及在提交樹上移動。 這些概念涵蓋了 Git 90% 的功能,一樣也足夠知足開發者的平常需求

然而, 剩餘的 10% 在處理複雜的工做流時(或者當你陷入困惑時)可能就顯得尤其重要了。接下來要討論的這個話題是「整理提交記錄」 —— 開發人員有時會說「我想要把這個提交放到這裏, 那個提交放到剛纔那個提交的後面」, 而接下來就講的就是它的實現方式,很是清晰、靈活,還很生動。

看起來挺複雜, 實際上是個很簡單的概念。

Git Cherry-pick

本小節的第一個命令是 git cherry-pick, 命令形式爲:

  • git cherry-pick <提交號>...

若是你想將一些提交複製到當前所在的位置(HEAD)下面的話, Cherry-pick 是最直接的方式了。我我的很是喜歡 cherry-pick,由於它特別簡單。

我們仍是經過例子來看一下!

須要簡單的將三個分支中的提交記錄複製到 master 上就能夠了。

image-20200911205410850

$ git cherry-pick C3 C4 C7

image-20200911205504309

交互式 Rebase

當你知道你所須要的提交記錄(而且還知道這些提交記錄的哈希值)時, 用 cherry-pick 再好不過了 —— 沒有比這更簡單的方式了。

可是若是你不清楚你想要的提交記錄的哈希值呢? 幸虧 Git 幫你想到了這一點, 咱們能夠利用交互式的 rebase —— 若是你想從一系列的提交記錄中找到想要的記錄, 這就是最好的方法了

我們具體來看一下……

交互式 rebase 指的是使用帶參數 --interactive 的 rebase 命令, 簡寫爲 -i

若是你在命令後增長了這個選項, Git 會打開一個 UI 界面並列出將要被複制到目標分支的備選提交記錄,它還會顯示每一個提交記錄的哈希值和提交說明,提交說明有助於你理解這個提交進行了哪些更改。

當 rebase UI界面打開時, 你能作3件事:

  • 調整提交記錄的順序(經過鼠標拖放來完成)
  • 刪除你不想要的提交(經過切換 pick 的狀態來完成,關閉就意味着你不想要這個提交記錄)
  • 合併提交。 簡而言之,它容許你把多個提交記錄合併成一個。

作一次交互式的 rebase,整理成目標窗口中的提交順序。

image-20200911210955287

$ git rebase -i overHere

image-20200911211106726

image-20200911210919344

一些提交技巧

只去一個提交記錄

來看一個在開發中常常會遇到的狀況:我正在解決某個特別棘手的 Bug,爲了便於調試而在代碼中添加了一些調試命令並向控制檯打印了一些信息。

這些調試和打印語句都在它們各自的提交記錄裏。最後我終於找到了形成這個 Bug 的根本緣由,解決掉之後以爲沾沾自喜!

最後就差把 bugFix 分支裏的工做合併回 master 分支了。你能夠選擇經過 fast-forward 快速合併到 master 分支上,但這樣的話 master 分支就會包含我這些調試語句了。你確定不想這樣,應該還有更好的方式……


實際咱們只要讓 Git 複製解決問題的那一個提交記錄就能夠了。跟以前咱們在「整理提交記錄」中學到的同樣,咱們可使用

  • git rebase -i
  • git cherry-pick

來達到目的。

image-20200911212116887

$ git cherry-pick bugFix

image-20200911212058175

提交的技巧 #1

接下來這種狀況也是很常見的:你以前在 newImage 分支上進行了一次提交,而後又基於它建立了 caption 分支,而後又提交了一次。

此時你想對的某個之前的提交記錄進行一些小小的調整。好比設計師想修改一下 newImage 中圖片的分辨率,儘管那個提交記錄並非最新的了。


咱們能夠經過下面的方法來克服困難:

  • 先用 git rebase -i 將提交從新排序,而後把咱們想要修改的提交記錄挪到最前
  • 而後用 commit --amend 來進行一些小修改
  • 接着再用 git rebase -i 來將他們調回原來的順序
  • 最後咱們把 master 移到修改的最前端(用你本身喜歡的方法),就大功告成啦!

固然完成這個任務的方法不止上面提到的一種(我知道你在看 cherry-pick 啦),以後咱們會多點關注這些技巧啦,但如今暫時只專一上面這種方法。 最後有必要說明一下目標狀態中的那幾個' —— 咱們把這個提交移動了兩次,每移動一次會產生一個 ';而 C2 上多出來的那個是咱們在使用了 amend 參數提交時產生的,因此最終結果就是這樣了。

也就是說,我在對比結果的時候只會對比提交樹的結構,對於 ' 的數量上的不一樣,並不歸入對比範圍內。只要你的 master 分支結構與目標結構相同,我就算你經過。

image-20200911213636719

$ git rebase -i master
# 使用新的一次提交
$ git commit --amend

$ git rebase -i master

$ git checkout master

$ git merge caption

image-20200911213454999

提交的技巧 #2

正如你在上小節所見到的,咱們可使用 rebase -i 對提交記錄進行從新排序。只要把咱們想要的提交記錄挪到最前端,咱們就能夠很輕鬆的用 --amend 修改它,而後把它們從新排成咱們想要的順序。

但這樣作就惟一的問題就是要進行兩次排序,而這有可能形成由 rebase 而致使的衝突。下面仍是看看 git cherry-pick是怎麼作的吧。

要在內心牢記 cherry-pick 能夠將提交樹上任何地方的提交記錄取過來追加到 HEAD 上(只要不是 HEAD 上游的提交就沒問題)。

同上一小節同樣的問題,咱們是用 git cherry-pick來解決。

image-20200911214431155

$ git checkout master

$ git cherry-pick c2

$ git commit --amend

$ git cherry-pick c3

image-20200911214358004

打標籤

相信你已經發現了:分支很容易被人爲移動,而且當有新的提交時,它也會移動。分支很容易被改變,大部分分支還只是臨時的,而且還一直在變。

你可能會問了:有沒有什麼能夠永遠指向某個提交記錄的標識呢,好比軟件發佈新的大版本,或者是修正一些重要的 Bug 或是增長了某些新特性,有沒有比分支更好的能夠永遠指向這些提交的方法呢?

固然有了!Git 的 tag 就是幹這個用的啊,它們能夠(在某種程度上 —— 由於標籤能夠被刪除後從新在另一個位置建立同名的標籤)永久地將某個特定的提交命名爲里程碑,而後就能夠像分支同樣引用了。

更可貴的是,它們並不會隨着新的提交而移動。你也不能檢出到某個標籤上面進行修改提交,它就像是提交樹上的一個錨點,標識了某個特定的位置。

image-20200912075930584

$ git tag v1 c2

$ git tag v0 c1

$ git checkout v1

image-20200912075759033

若是你不指定提交記錄,Git 會用 HEAD所指向的位置。進行標記 Tag。

$ git tag v2
# 此時 HEAD 指向 c2 就會爲 c2增長一個名爲 v2 的tag

獲取描述

因爲標籤在代碼庫中起着「錨點」的做用,Git 還爲此專門設計了一個命令用來描述離你最近的錨點(也就是標籤),它就是 git describe

Git Describe 能幫你在提交歷史中移動了屢次之後找到方向;當你用 git bisect(一個查找產生 Bug 的提交記錄的指令)找到某個提交記錄時,或者是當你坐在你那剛剛度假回來的同事的電腦前時, 可能會用到這個命令。

git describe 的語法是:

git describe <ref>

<ref> 能夠是任何能被 Git 識別成提交記錄的引用,若是你沒有指定的話,Git 會以你目前所檢出的位置(HEAD)。

它輸出的結果是這樣的:

<tag>_<numCommits>_g<hash>
  • tag 表示的是往上追溯,離 ref 最近的標籤,
  • numCommits 是表示這個 reftag 相差有多少個提交記錄,
  • hash 表示的是你所給定的 ref 所表示的提交記錄哈希值的前幾位。

ref 提交記錄上有某個標籤時,則只輸出標籤名稱

image-20200912084711001

$ git describe side

v1_1_gC4

$ git describe master

v0_2_gC2

高級操做

多分支 rebase

哥們兒,咱們準備了不少分支!我們把這些分支 rebase 到 master 上吧。

可是你的領導給你提了點要求 —— 他們但願獲得有序的提交歷史,也就是咱們最終的結果應該是 C6'C7' 上面, C5'C6' 上面,依此類推。

即便你搞砸了也不要緊,用 reset 命令就能夠從新開始了。記得看看咱們提供的答案,看你可否使用更少的命令來完成任務!

image-20200912093804536

$ git rebase master bugFix

$ git rebase bugFix side

$ git rebase side another

$ git rebase another master

image-20200912093751409

選擇父提交記錄

操做符 ^~ 符同樣,後面也能夠跟一個數字。

可是該操做符後面的數字與 ~ 後面的不一樣,並非用來指定向上返回幾代,而是指定合併提交記錄的某個父提交。還記得前面提到過的一個合併提交有兩個父提交吧,因此遇到這樣的節點時該選擇哪條路徑就不是很清晰了。

Git 默認選擇合併提交的「第一個」父提交,在操做符 ^ 後跟一個數字能夠改變這一默認行爲。

使用 ^~ 能夠自由地在提交樹中移動,很是給力。

更厲害的是,這些操做符還支持鏈式操做!

image-20200912095014091

$ git branch bugWork master^^2^

image-20200912094954220

糾纏不清的分支

如今咱們的 master 分支是比 onetwothree 要多幾個提交。出於某種緣由,咱們須要把 master 分支上最近的幾回提交作不一樣的調整後,分別添加到各個的分支上。

one 須要從新排序並刪除 C5two 僅須要重排排序,而 three 只須要提交一次。

慢慢來,你會找到答案的 —— 記得通關以後用 show solution 看看咱們的答案哦。

image-20200912101754067

$ git checkout one

$ git cherry-pick C4 C3 C2

$ git checkout two

$ git cherry-pick C5 C4 C3 C2

$ git branch -f three C2

image-20200912101724049

相關文章
相關標籤/搜索