
image.png
1. git commit --amend
有時(shí)你提交過(guò)代碼之后,發(fā)現(xiàn)一個(gè)地方改錯(cuò)了,
你下次提交時(shí)不想保留上一次的記錄;
或者你上一次的commit message的描述有誤,
這時(shí)候你可以使用接下來(lái)的這個(gè)命令:git commit --amend
該操作會(huì)改變你原來(lái)的commit id
2. cherry-pick vs rebase vs merge
2.1. merge

merge合并

image.jpeg
那么git merge topic命令將會(huì)把在master分支上二者共同的節(jié)點(diǎn)
(E節(jié)點(diǎn))之后分離的節(jié)點(diǎn)(即topic分支的A B C節(jié)點(diǎn))重現(xiàn)在master分支上,
直到topic分支當(dāng)前的commit節(jié)點(diǎn)(C節(jié)點(diǎn)),并位于master分支的頂部。
并且沿著master分支和topic分支創(chuàng)建一個(gè)記錄
合并結(jié)果的新節(jié)點(diǎn),該節(jié)點(diǎn)帶有用戶描述合并變化的信息
2.2. cherry-pick
對(duì)于多分支的代碼庫(kù),將代碼從一個(gè)分支轉(zhuǎn)移到另一個(gè)分支是常見(jiàn)需求。
這時(shí)分兩種情況。一種情況是,你需要另一個(gè)分支的所有代碼變動(dòng),那么就采用合并(git merge)。
另一種情況是,你只需要部分代碼變動(dòng)(某幾個(gè)提交),這時(shí)可以采用 Cherry pick。
a - b - c - d Master
\
e - f - g Feature
- git cherry-pick 分支
- git cherry-pick <commitHash>
- git cherry-pick <HashA> <HashB>
- git cherry-pick A..B
2.3. rebase
概念:git rebase你其實(shí)可以把它理解成是重新設(shè)置基線,將你的當(dāng)前分支重新設(shè)置開(kāi)始點(diǎn)。這個(gè)時(shí)候才能知道你當(dāng)前分支于你需要比較的分支之間的差異。

image.png
1. master 1-2-3 是現(xiàn)在的分支狀態(tài)
2. 這個(gè)時(shí)候從master ,checkout出來(lái)一個(gè)prod分支
3. 然后master提交了4.5,prod提交了6.7
4. 這個(gè)時(shí)候master分支狀態(tài)就是1-2-3-4-5,prod狀態(tài)變成1-2-3-6-7
5. 如果在prod上用rebase master ,prod分支狀態(tài)就成了1-2-3-4-5-6-7
6. 如果是merge 會(huì)出來(lái)一個(gè)8,這個(gè)8的提交就是把4-5合進(jìn)來(lái)的提交
好處:提交直觀 便于回退 拉取代碼有預(yù)處理階段 不用生成新的節(jié)點(diǎn)
注意:不要通過(guò)rebase對(duì)任何已經(jīng)提交到公共倉(cāng)庫(kù)中的commit進(jìn)行修改
- git pull --rebase
- git rebase -i [startpoint] [endpoint]
git rebase [startpoint] [endpoint] --onto [branchName] - git rebase -i HEAD~n
pick:保留該commit(縮寫:p)
reword:保留該commit,但我需要修改該commit的注釋(縮寫:r)
edit:保留該commit, 但我要停下來(lái)修改該提交(不僅僅修改注釋)(縮寫:e)
squash:將該commit和前一個(gè)commit合并(縮寫:s)
fixup:將該commit和前一個(gè)commit合并,但我不要保留該提交的注釋信息(縮寫:f)
exec:執(zhí)行shell命令(縮寫:x)
drop:我要丟棄該commit(縮寫:d)
3. 什么是 HEAD
3.1 概念:
Git 中的 HEAD 可以理解為一個(gè)指針,我們可以在命令行中輸入 cat .git/HEAD 查看當(dāng)前 HEAD指向哪兒,一般它指向當(dāng)前工作目錄所在分支的最新提交或者分支。

切換分支時(shí),HEAD 會(huì)移動(dòng)到指定分支
3.2 HEAD游離狀態(tài)
使用的是 git checkout < commit id>,即切換到指定的某一次提交,HEAD就會(huì)處于 detached狀態(tài)(游離狀態(tài))
HEAD 處于游離狀態(tài)時(shí),我們可以很方便地在歷史版本之間互相切換,比如需要回到
某次提交,直接 checkout 對(duì)應(yīng)的 commit id 或者 tag 名即可。
它的弊端就是:在這個(gè)基礎(chǔ)上的提交會(huì)新開(kāi)一個(gè)匿名分支!

HEAD 就會(huì)處于 detached 狀態(tài)
可以看到,我還沒(méi)有修改和提交的情況下,切換完成就給我新建了一個(gè)分支,并且指明 HEAD
正游離在 2772886 的 <commit id> 上。
如果不做任何修改,想回到 master 分支,直接 git checkout master 即可,
而不要 checkout master 主干所對(duì)應(yīng)的 <commit id>。
順利回到主干的話,HEAD 的游離狀態(tài)會(huì)取消,原臨時(shí)游離分支也會(huì)消失。
如果是在游離狀態(tài)做了修改和提交,則:
切換會(huì) master 分支時(shí),在游離狀態(tài)所做的修改和提交無(wú)法追溯
3.3 git reset [--soft | --mixed | --hard] HEAD^
-
--mixed為默認(rèn),可以不用帶該參數(shù),用于重置暫存區(qū)的文件與上一次的提交(commit)保持一致,工作區(qū)文件內(nèi)容保持不變。
$ git reset HEAD^ # 回退所有內(nèi)容到上一個(gè)版本
$ git reset HEAD^ hello.php # 回退 hello.php 文件的版本到上一個(gè)版本
$ git reset 052e # 回退到指定版本
-
--soft參數(shù)用于回退到某個(gè)版本 保留工作目錄,并把重置 HEAD 所帶來(lái)的新的差異放進(jìn)暫存區(qū)
image.png
什么是「重置 HEAD 所帶來(lái)的新的差異:
由于 HEAD 從 4 移動(dòng)了 3,而且在 reset 的過(guò)程中工作目錄和暫存區(qū)的內(nèi)容沒(méi)有被清理掉,
所以 4 中的改動(dòng)在 reset 后就也成了工作目錄新增的「工作目錄和 HEAD 的差異」。
這就是上面一段中所說(shuō)的「重置 HEAD 所帶來(lái)的差異」
-
--hard參數(shù)撤銷工作區(qū)中所有未提交的修改內(nèi)容,將暫存區(qū)與工作區(qū)都回到上一次版本,并刪除之前的所有信息提交:
$ git reset –hard HEAD~3 # 回退上上上一個(gè)版本
$ git reset –hard bae128 # 回退到某個(gè)版本回退點(diǎn)之前的所有信息。
$ git reset --hard origin/master # 將本地的狀態(tài)回退到和遠(yuǎn)程的一樣
注意:謹(jǐn)慎使用 –hard 參數(shù),它會(huì)刪除回退點(diǎn)之前的所有信息。
4 Gerrit 的 Change-Id
Change-Id 是 gerrit (代碼審核平臺(tái))的概念

image.png
-
Change-Id的生成
Gerrit 提供了標(biāo)準(zhǔn)的“commit-msg”鉤子來(lái)實(shí)現(xiàn)。
Git 提供了4個(gè)提交工作流鉤子:pre-commit、prepare-commit-msg、
commit-msg、post-commit。其中 commit-msg 鉤子,
會(huì)在我們執(zhí)行 git commit 時(shí)被執(zhí)行。
本質(zhì)上,commit-msg 鉤子是一段腳本程序,放在 .git/hooks 目錄下。
commit-msg 腳本可以使用 Shell、Ruby、Python 等語(yǔ)言實(shí)現(xiàn)。
- 案例1
### Squash the commits with the same Change-Id or ensure Change-Ids are unique for each commit
shell
Enumerating objects: 9, done.
Counting objects: 100% (9/9), done.
Delta compression using up to 4 threads
Compressing objects: 100% (5/5), done.
Writing objects: 100% (5/5), 473 bytes | 473.00 KiB/s, done.
Total 5 (delta 4), reused 0 (delta 0)
remote: Resolving deltas: 100% (4/4)
remote: Processing changes: refs: 1, done
To ssh://sg-8-130.hst.xxxxx.net:29418/xxxx/xxxxxx
! [remote rejected] HEAD -> refs/for/dev (same Change-Id in multiple changes.
Squash the commits with the same Change-Id or ensure Change-Ids are unique for each commit)
error: failed to push some refs to 'ssh://xxxxxxxx@sg-8-130.hst.xxxxxx.net:29418/xxxxx/xxxxx'
在dev分支上有重復(fù)的change-id,使用
shell
git commit --amend
- 案例2 缺失
Change-Id
- 如果缺失 Change-Id 的是最后一個(gè) (head) commit, 使用以下命令即可解決問(wèn)題
$ git commit --amend
- 如果缺失 Change-Id 的不是最后一個(gè) commit,第二個(gè)提交缺失 Change-Id, 可用 reset 方法:
$ git reset 1a9096a34322885ac101175ddcac7dab4c52665d
$ git commit --amend
$ git add ......
$ git commit -m "你的提交日志"
$ git push review HEAD:refs/for/dev
- 使用交互式rebase 找回
任意提交位置的 Change-Id 以及有多個(gè) commit缺失 Change-Id 的情況
// 找到缺失 Change-Id 的那個(gè) commit:
// 執(zhí)行 git rebase -i, 參數(shù)為 該提交的上一個(gè)提交的 commit-id (本例中為 "表單" 那個(gè)提交):
$ git rebase -I d714bcde0c14ba4622d28952c4b2a80882b19927
//將缺失了 Change-Id 的 commit 前面的 pick 改為 reword 即可
//逐個(gè)編輯 commit-msg 可以不需要修改 保存推出即可
$ git log
commit 8aaaa749db4a5b105aa746659c5cd266ac82fffe
Author: liux <liux@xxxx.cn>
Date: Mon Dec 19 17:43:24 2016 +0800
I am commit message 3
Change-Id: Ic89d5ce6ce4de70d1dcb315ce543c86a2b3ac003
commit 8e1cad33bcd98e175cba710b1eacfd631a5dda41
Author: liux <liux@xxxx.cn>
Date: Mon Dec 19 17:43:00 2016 +0800
I am commit message 2
Change-Id: I9d2af0cc31423cf808cd235de0ad02abf451937d
commit 1a9096a34322885ac101175ddcac7dab4c52665d
Author: liux <liux@xxxx.cn>
Date: Mon Dec 19 15:23:36 2016 +0800
I am commit message 1
commit d714bcde0c14ba4622d28952c4b2a80882b19927
Author: shangsb <shangsb@czfw.cn>
Date: Wed Dec 14 09:20:52 2016 +0800
這是一個(gè)提交
Change-Id: I629b2bedff95491875f63634ad3da199612735b6
$ git rebase -I d714bcde0c14ba4622d28952c4b2a80882b19927
這個(gè)命令會(huì)打開(kāi)默認(rèn)的編輯器,一般為 vi. 內(nèi)容如下:
pick 1a9096a I am commit message 1
pick 8e1cad3 I am commit message 2
pick 8aaaa74 I am commit message 3
# Rebase d714bcd..8aaaa74 onto d714bcd
#
# 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
reword 1a9096a I am commit message 1
pick 8e1cad3 I am commit message 2
pick 8aaaa74 I am commit message 3
# Rebase d714bcd..8aaaa74 onto d714bcd
#
# 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
疑問(wèn)
- A-B-C 應(yīng)該在最新節(jié)點(diǎn)C上追加 假如我想在B或者A上追加應(yīng)該怎么處理
-
消息分支已經(jīng)push 并且已經(jīng)合并到master-pre 最終合并到master 而我本地追蹤的是遠(yuǎn)程master但是還是顯示超前一個(gè)版本 - 錯(cuò)誤操作導(dǎo)致我的分支出現(xiàn)別人的代碼節(jié)點(diǎn) 并且都處于
待push的狀態(tài) 也就是超前遠(yuǎn)程倉(cāng)庫(kù)n個(gè)版本
參考文檔:
http://www.itdecent.cn/p/4079284dd970
http://www.itdecent.cn/p/4a8f4af4e803
