哈嘍,everybody,不知不覺8天的小長假也接近了尾聲,玩耍了這麼多天,今天也要收一收心,開始學習了呦~。明天就要上班啦,今天姐姐突然問我git-rebase指令是幹什麼的,怎麼用?其實我是不想給他講的,但是還是沒有逃過姐姐的軟磨硬泡,那麼我們就一起來看一看什麼是git-rebase吧!!!
緣起話說,我和姐姐的緣分是在那一個月黑風高的晚上,啪,姐姐一巴掌打在了我的臉上並說了一句:能不能講重點~~~。哈哈,不開玩笑了,直接說重點吧。我們先來看一個場景,我查看了一下我github上的個人倉庫,commit提交次數很多,提交內容如下:
這麼多的提交,有很多沒有規範的命名,因為是自己使用,就隨便開整了,這確實不好,還有一些沒有必要的提交,其實是可以合併到一起的,這樣會導致如下問題:
這一篇文章我們先不講git提交規範,我們先來解決一下如何合併多次提交記錄。
rebase作用一:合併提交記錄通過上面的場景,我們可以引申出git-rebase的第一個作用:合併提交記錄。現在我們想合併最近5次的提交記錄,執行:
$ git rebase -i HEAD~2執行該指令後會自動彈出vim編輯模式:
pick e2c71c6 update readme
pick 3d2c660 wip: merge`
# Rebase 5f47a82..3d2c660 onto 5f47a82 (2 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup <commit> = like "squash", but discard this commit's log message
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# . create a merge commit using the original merge commit's
# . message (or the oneline, if no original merge commit was
# . specified). Use -c <commit> to reword the commit message.
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out從這裡我們可以看出前面5行是我們要合併的記錄,不過前面都帶了一個相同的指令:pick,這是什麼指令呢,不要慌,這不,下面已經給出了commands:
pick:保留該commit(縮寫:p)
reword:保留該commit,但我需要修改該commit的注釋(縮寫:r)
edit:保留該commit, 但我要停下來修改該提交(不僅僅修改注釋)(縮寫:e)
squash:將該commit和前一個commit合併(縮寫:s)
fixup:將該commit和前一個commit合併,但我不要保留該提交的注釋信息(縮寫:f)
exec:執行shell命令(縮寫:x)
drop:我要丟棄該commit(縮寫:d)
label:用名稱標記當前HEAD(縮寫:l)
reset:將HEAD重置為標籤(縮寫:t)
merge:創建一個合併分支並使用原版分支的commit的注釋(縮寫:m)根據這些指令,我們可以進行修改,如下:
pick e2c71c6 update readme
s 3d2c660 wip: merge`修改好後,我們點擊保存退出,就會進入注釋界面:
# This is a combination of 2 commits.
# This is the 1st commit message:
update readme
# This is the commit message #2:
wip: merge`
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# Date: Thu Sep 17 22:03:52 2020 +0800
#
# interactive rebase in progress; onto 5f47a82
# Last commands done (2 commands done):
# pick e2c71c6 update readme
# squash 3d2c660 wip: merge`
# No commands remaining.
# You are currently rebasing branch 'master' on '5f47a82'.
#
# Changes to be committed:
# new file: hash/.idea/.gitignore
# new file: hash/.idea/hash.iml
# new file: hash/.idea/misc.xml
# new file: hash/.idea/modules.xml
# new file: hash/.idea/vcs.xml
# new file: hash/go.mod
# new file: hash/hash/main.go
# modified: snowFlake/Readme.md上面把每一次的提交的meassage都列出了,因為我們要合併這兩次的commit,所以提交注釋可以修改成一條,最終編輯如下:
# This is a combination of 2 commits.
# This is the 1st commit message:
fix: merge update and wip: merge`
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# Date: Thu Sep 17 22:03:52 2020 +0800
#
# interactive rebase in progress; onto 5f47a82
# Last commands done (2 commands done):
# pick e2c71c6 update readme
# squash 3d2c660 wip: merge`
# No commands remaining.
# You are currently rebasing branch 'master' on '5f47a82'.
#
# Changes to be committed:
# new file: hash/.idea/.gitignore
# new file: hash/.idea/hash.iml
# new file: hash/.idea/misc.xml
# new file: hash/.idea/modules.xml
# new file: hash/.idea/vcs.xml
# new file: hash/go.mod
# new file: hash/hash/main.go
# modified: snowFlake/Readme.md編輯好後,保存退出就可以了。這樣就完成了一次合併commit。我們來驗證一下:
$ git log
15ace34 (HEAD -> master) fix: merge update and wip: merge`
5f47a82 update snowFlake code從這裡我們可以看到,兩次提交變成了一次,減少了無用的提交信息。
作用二:分支合併這個作用我們使用的很少,但是還是要知道,下面我們一起來看一下使用場景。
假設我們現在有一個新項目,現在我們要從master分支切出來一個dev分支,進行開發:
$ git checkout -b dev這時候,你的同事完成了一次 hotfix,並合併入了 master 分支,此時 master 已經領先於你的 dev 分支了:
同事修復完事後,在群裡通知了一聲,正好是你需要的部分,所以我們現在要同步master分支的改動,使用merge進行合併:
$ git merge master圖中綠色的點就是我們合併之後的結果,執行git log就會在記錄裡發現一些 merge 的信息,但是我們覺得這樣汙染了 commit 記錄,想要保持一份乾淨的 commit,怎麼辦呢?這時候,git rebase 就派上用場了。
所以現在我們來試一試使用git rebase,我們先回退到同事 hotfix 後合併 master 的步驟,我現在不使用merge進行合併了,直接使用rebase指令
$ git rebase master這時,git會把dev分支裡面的每個commit取消掉,然後把上面的操作臨時保存成 patch 文件,存在 .git/rebase 目錄下;然後,把 dev 分支更新到最新的 master 分支;最後,把上面保存的 patch 文件應用到 dev 分支上;
從 commit 記錄我們可以看出來,dev 分支是基於 hotfix 合併後的 master ,自然而然的成為了最領先的分支,而且沒有 merge 的 commit 記錄,是不是感覺很舒服了。
我們在使用rebase合併分支時,也會出現conflict,在這種情況下,git 會停止 rebase 並會讓你去解決衝突。在解決完衝突後,用 git add 命令去更新這些內容。然後再次執行git rebase --continue,這樣git 會繼續應用餘下的 patch 補丁文件。
假如我們現在不想在執行這次rebase操作了,都可以通過--abort回到開始前狀態:
git rebase --abort
rebase是存在危險的操作 - 慎用我們現在使用rebase操作看起來是完美的,但是他也是存在一定危險的,下面我們就一起來看一看。
現在假設我們在dev分支進行開發,執行了rebase操作後,在提交代碼到遠程之前,是這樣的:
提交dev分支到遠程代碼倉庫後,就變成了這樣:
而此時你的同事也在 dev 上開發,他的分支依然還是以前的dev,並沒有進行同步master:
那麼當他 pull 遠程 master 的時候,就會有丟失提交記錄。這就是為什麼我們經常聽到有人說 git rebase 是一個危險命令,因為它改變了歷史,我們應該謹慎使用。
不過,如果你的分支上需要 rebase 的所有 commits 歷史還沒有被 push 過,就可以安全地使用 git-rebase來操作。
總結在asong的細心講解下,姐姐完全搞懂了怎麼使用git rebase,我們來看一下姐姐的總結:
當我們在一個過時的分支上面開發的時候,執行 rebase 以此同步 master 分支最新變動;假如我們要啟動一個放置了很久的並行工作,現在有時間來繼續這件事情,很顯然這個分支已經落後了。這時候需要在最新的基準上面開始工作,所以 rebase 是最合適的選擇。git-rebase 很完美,解決了我們的兩個問題:使用rebase操作要注意一個問題,如果你的分支上需要 rebase 的所有 commits 歷史還沒有被 push 過,就可以安全地使用 git-rebase來操作。看來姐姐是真的學會了,那你們呢?
沒有學會不要緊,親自試驗一下才能更好的理解呦~~~。
好啦這一篇文章到這裡就結束了,我們下期見。
參考文章連結:http://jartto.wang/2018/12/11/git-rebase/
結尾給大家發一個小福利吧,最近我在看[微服務架構設計模式]這一本書,講的很好,自己也收集了一本PDF,有需要的小夥可以到自行下載。獲取方式:關注公眾號:[Golang夢工廠],後臺回覆:[微服務],即可獲取。
我翻譯了一份GIN中文文檔,會定期進行維護,有需要的小夥伴後臺回復[gin]即可下載。
我是asong,一名普普通通的程序猿,讓我一起慢慢變強吧。我自己建了一個golang交流群,有需要的小夥伴加我vx,我拉你入群。歡迎各位的關注,我們下期見~~~
推薦往期文章: