1. 程式人生 > >Git tips: 合併 commit 保持分支幹淨整潔

Git tips: 合併 commit 保持分支幹淨整潔

本文的讀者需要已經瞭解 基本的 Git 操作和開發流程 。

在我們開發完分支後,一般分支上會有很多 commit —— 少不了諸如 “fix typo”, “sth wrong in the previous commit” 之類的 commit。在合併到主幹的時候,往往這類 commit 顯得臃腫多餘。為了方便別人做 code review,我們希望合併一些不必要的 commit 使我們的分支顯得乾淨一目瞭然,也方便管理。有 3 種方式可以做到。

一、git rebase

$ git rebase -i origin/master

-i 引數表示互動 interactive,這時 git 會使用你設定的編輯器,讓你對 git 歷史記錄做詳細修改。

下面以 Atlassian 的例子來說明

 
# 開始開發一個新 feature

$ git checkout -b new-feature master

# 改了一些程式碼

$ git commit -a -m "Start developing a feature"

# 剛剛的修改有點問題,再改一下

$ git commit -a -m "Fix something from the previous commit"


# 緊急修復,直接在 master 分支上改點東西

$ git checkout master

# 改了一些程式碼

$ git commit -a -m "Fix security hole"


# 開始互動式地 rebase 了

$ git checkout new-feature

$ git rebase -i master

這時 git 會開啟編輯器,你會看到 new-feature 分支上的 2 個最新 commit,以及一些指引提示

pick 32618c4 Start developing a feature
pick 62eed47 Fix something from the previous commit
 
# Commands:
#  p, pick = use commit
#  r, reword = use commit, but edit the commit message
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#  f, fixup = like "squash", but discard this commit's log message
#  x, exec = run command (the rest of the line) using shell
#
# 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

每個 commit 前有一個操作命令,預設是 pick ,表示該行被選中,需要進行 rebase 操作。下面一堆註釋的指引中還有幾個指令,我們常用到的是以下 2 個

  • squash:將這一行的 commit 與上一個 commit 進行合併
  • fixup:與 squash 相同,只是不會保留這行 commit 的提交 message 資訊

比如上面的例子,我們在編輯器中修改指令為

 

pick 32618c4 Start developing a feature

squash 62eed47 Fix something from the previous commit

這樣一改,儲存執行後,new-feature 分支就只剩下 1 個 commit 了,這個合併後的 commit 提交的資訊包含之前 2 個 commit 的資訊

 
Start developing a feature

Fix something from the previous commit

消除了一個多餘的不那麼重要的 commit,達到了我們的目的。最後,只需 fast-forward merge 到 master

 
$ git checkout master

$ git merge new-feature

在別人看來,你就是一個天才開發者,沒有出差錯地一次實現了 new-feature ,專案的 commit 歷史記錄顯得乾淨而有意義。

如果我們在編輯器中不用 squash 指令,而使用 fixup 指令,這樣改

 
pick 32618c4 Start developing a feature

fixup 62eed47 Fix something from the previous commit

則結果一樣,還是隻剩 1 個 commit,但是合併後的提交訊息,就只有之前第一個 commit 的訊息,第二個的 commit 訊息被註釋掉了

Start developing a feature

這樣也達到了我們的目的,消除了曾經發生過 2 個 commit 的痕跡。在別人看來,這個新功能的分支是按計劃一次性開發好的,中途並未發生 “錯別字” 之類的意外,簡直完美。

二、–fixup & –autosquash

 
# 自動標記這一次的 commit 為上一個 commit 的 fix

$ git commit --fixup <commit>

# 自動組織合併兩個 commit

$ git rebase -i --autosquash

這種方式和上面的第一種原理是一樣的,只是形式上不是在編輯器中調整歷史記錄,而是直接執行命令,結果等同。

三、撤銷過去的 commit 重建一個新的

 
$ git reset HEAD~2

$ git add .

$ git commit -am "This is the new feature"

$ git push --force

這種方式就比較暴力了,嗯。

說點題外話

修改上一次 commit 提交的 message

有時我們提交程式碼,message 寫的太匆忙有錯字(不是程式碼裡有錯字,而是 commit message 寫錯),就可以用下面的命令來修正

$ git commit --amend

不過只能修正上一次的 commit。如果很多個 commit 之前就有 message 寫錯,就得用上我們之前說的 git rebase 了

$ git rebase -i HEAD~4

和之前一樣,會開啟編輯器,顯示最近的 4 次提交

pick 07c5abd Introduce OpenPGP and teach basic usage
pick de9b1eb Fix PostChecker::Post#urls
pick 3e7ee36 Hey kids, stop all the highlighting
pick fa20af3 git interactive rebase, squash, amend

將 pick 指令改為 reword 指令,就可以修改這一行的 commit message

pick 07c5abd Introduce OpenPGP and teach basic usage
pick de9b1eb Fix PostChecker::Post#urls
reword 3e7ee36 Hey kids, stop all the highlighting
pick fa20af3 git interactive rebase, squash, amend

– EOF –

參考文件