Git 常用操作 | 重写 commit 历史
當我們修改完代碼,提交了一個 commit,然后發現改錯了,怎么修正?這種情況分為兩種:修正最近一次提交,和修正歷史多個提交。
修正最近一次提交
如果發現剛剛提交的內容有錯誤,當場再修改一下再提交一個新 commit 不就可以么?可以是可以,不過還有一個更加優雅和簡單的解決方法:
git?commit?--amend"amend" 是“修正”的意思。在提交時,如果加上 --amend 參數,Git 不會在當前 commit 上增加 commit,而是會把當前 commit 的內容和暫存區(stageing area)里的最近一次 commit 的內容合并起來后創建一個新的 commit,用這個新的 commit 把之前最新的 commit 替換掉。所以 commit --amend 做的事就是它的字面意思:對最新一條 commit 進行修正。
具體地,比如你發現剛剛的提交中 foo.txt 文件有錯別字,你就可以把文件中的錯別字修改好之后,輸入以下命令:
git?add?foo.txt git?commit?--amend此時 Git 會把你帶到提交信息編輯界面。提交信息默認是最近那次提交時填的信息。你可以修改或者保留它,然后保存退出。然后,你的最新 commit 就被更新了。
需要注意的有一點:commit --amend 并不是直接修改原 commit 的內容,而是如上面動圖所示生成一條新的 commit。
修正歷史多個提交
commit --amend 可以修正最新 commit 的錯誤,但如果是倒數第二個、第三個 commit 寫錯了,怎么辦?
如果不是最新的 commit 寫錯,就不能用 commit --amend 來修復了,而是要用 rebase。不過需要給 rebase 也加一個參數:-i。
rebase -i 是 rebase --interactive 的縮寫形式,意為“交互式變基”。
所謂交互式 rebase,就是在 rebase 的操作執行之前,你可以指定要 rebase 的 commit 鏈中的哪一個 commit 是否需要進一步修改。
那么你就可以利用這個特點,進行一次原地 rebase。
例如你是在寫錯了 commit 之后,又提交了一次才發現之前寫錯了。現在再用 commit --amend 已經晚了,此時就要用 rebase -i 命令了:
git?rebase?-i?HEAD^^在 Git 中,有兩個「偏移符號」:^ 和 ~。
^ 的用法:在 commit 的后面加一個或多個 ^ 號,可以把 commit 往回偏移,偏移的數量是 ^ 的數量。例如:master^ 表示 master 指向的 commit 之前的那個 commit;HEAD^^ 表示 HEAD 所指向的 commit 往前數兩個 commit。
~ 的用法:在 commit 的后面加上 ~ 號和一個數,可以把 commit 往回偏移,偏移的數量是 ~ 號后面的數。例如:HEAD~5 表示 HEAD 指向的 commit往前數 5 個 commit。
上面這行代碼表示,把當前 commit ( HEAD 所指向的 commit) rebase 到 HEAD 向前兩個的 commit 上:
如果沒有 -i 參數的話,這種原地 rebase 相當于空操作,會直接結束。而在加了 -i 后,就會跳到一個新的界面:
pick?310154e?第?N-2?次提交 pick?a5f4a0d?第?N-1?次提交#?Rebase?710f0f8..a5f4a0d?onto?710f0f8 # #?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 ...這個編輯界面的最頂部,列出了將要被 rebase 的所有 commits,也就是倒數第二個 commit “第 N-2 次提交”和最近的 commit “第 N-1 次提交”。需要注意,這個排列是正序的,舊的 commit 會排在上面,新的排在下面。
這兩行指示了兩個信息:需要處理哪些 commit 和如何處理它們。
你需要修改這兩行的內容來指定你需要的操作。每個 commit 默認的操作都是 pick,表示直接應用這個 commit。所以如果你現在直接退出編輯界面,那么結果仍然是空操作。
但你的目標是修改倒數第二個 commit,也就是上面的那個“第 N-2 次提交”,所以你需要把它的操作指令從 pick 改成 edit 。edit 的意思是應用這個 commit,然后停下來等待繼續修正。其他的操作指令,在這個界面里都已經列舉了出來(下面的 "Commands…" 部分文字),你可以自己研究一下。
edit?310154e?第?N-2?次提交 pick?a5f4a0d?第?N-1?次提交...把 pick 修改成 edit 后,就可以退出編輯界面了,并輸出以下信息:
$?git?rebase?-i?HEAD^^ Stopped?at?310154e...?第?N-2?次提交 You?can?amend?the?commit?now,?withgit?commit?--amendOnce?you're?satisfied?with?your?changes,?rungit?rebase?--continue上圖的提示信息說明,rebase 過程已經停在“第 N-2 次提交”的 commit 的位置,那么現在你就可以去修改你想修改的內容了。
修改完成后,和前文修改最近提交的方法一樣,用 commit --amend 把修改原地應用到一個新的 commit:
git?add?foo.txt git?commit?--amend這樣你的倒數第二個錯誤的 commit 就被修正了。在此過程中,把原來倒數第二個 commit 的內容和當前修改的內容合并在一起創建了一個新的 commit,并用此 commit 原地替換了原來的原來倒數第二個 commit:
然后,你可以用 rebase --continue 來繼續上述 rebase 過程。
git?rebase?--continue所有需要重寫的 commit 都修改完成后,這次交互式 rebase 的過程就完美結束了。
總結
只修正最近的錯誤提交,使用簡單的 commit --amend 即可。若修改歷史多個提交用交互式變基 rebase -i,它可以在 rebase 開始之前指定一些額外操作。通過交互式變基還可以實現其它歷史重寫操作,如“重新排序提交”、“壓縮提交”、“拆分提交”等,這些歷史重寫操作不常用,我個人從來沒用過,所以就不講了,你可以在實際有需要的時候自己再去研究一下。
-
精致碼農
帶你洞悉編程與架構
↑長按圖片識別二維碼關注,不要錯過網海相遇的緣分
總結
以上是生活随笔為你收集整理的Git 常用操作 | 重写 commit 历史的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 在家办公这半年,让我开始热爱生活
- 下一篇: 这些年我是怎么自学成架构师的(转自知乎)