【轉】[gerrit場景教程] gerrit "missing Change-Id"

場景:

 

你用 git push 向 gerrit 提交了待審覈代碼,一切都很順利,你腦殼裏冒出了"代碼頭上加了'佛祖保佑'果真有效"的想法.git

此時 git 打印出以下提示,你的心裏OS同步打印 "心情 -5" :shell

remote: Resolving deltas: 100% (14 /14 )
remote: Processing changes: refs: 1, done   
remote: ERROR: missing Change-Id in commit message footer
remote:
remote: Hint: To automatically insert Change-Id, install the hook:
remote:   gitdir=$(git rev-parse --git- dir ); scp -p -P 29418 liux@gerrit.xxxxx.com:hooks /commit-msg ${gitdir} /hooks/
remote: And then amend the commit:
remote:   git commit --amend
remote:
To ssh : //liux @121.xx.xx.xx:29418 /kaiba_admin_yunying .git
  ! [remote rejected] HEAD -> refs /for/master (missing Change-Id in commit message footer)
error: failed to push some refs to 'ssh://liux@121.xx.xx.xx:29418/sample_project.git'

 


套路:

大前提: commit-msg 文件必須已經在該項目中存在.

 

使用ls命令檢查該文件是否存在:bash

$ cd project_dir
$ ls .git /hooks/commit-msg

若是該文件不存在,則按照 git push 時產生的提示信息,獲取該文件:服務器

$ gitdir=$(git rev-parse --git- dir ); scp -p -P 29418 liux@gerrit.xxxxx.com:hooks /commit-msg ${gitdir} /hooks/

上面的命令能夠直接從 git push 產生的錯誤信息中複製出來.app

若是要手敲該命令,別忘了把用戶名換成本身的.ssh

 

 

方法一: 使用 amend 選項生成 Change-Id:

 

若是缺失 Change-Id 的是最後一個 (head) commit, 使用如下命令便可解決問題:編輯器

$ git commit --amend

該命令會打開默認的 commit message 編輯器,通常是 vi.post

這時什麼都不用修改,直接保存退出便可 (:wq).網站

再次查看 git log,就會發現缺失的 Change-Id 已經被補上了. 再次 git push 便可.ui

 

方法二: 若是缺失 Change-Id 的不是最後一個 commit, 可用 reset 方法:

 

好比,若是缺失 Change-Id 的 commit 是 git log 中的第二個 commit, 則能夠用 git reset 命令將本地分支回退到該 commit.

(但其實用 git reset 找回 Change-Id 是普通青年才幹的事情,文藝青年有更優雅的辦法.見方法三)

首先執行 git log, 找出缺失了 Change-Id 的 commit,並複製其 commit-id:

$ git log
commit 8e1cad33bcd98e175cba710b1eacfd631a5dda41
Author: liux <liux@xxxx.cn>
Date:   Mon Dec 19 17:43:00 2016 +0800
 
     test commit "with amended commit message"
     
     Change-Id: I9d2af0cc31423cf808cd235de0ad02abf451937d
 
commit 1a9096a34322885ac101175ddcac7dab4c52665d
Author: liux <liux@xxxx.cn>
Date:   Mon Dec 19 15:23:36 2016 +0800
 
     test commit-msg hook
 
......

發現是第二個 commit 缺失 Change-Id. 將代碼 reset 到這個 commit, 並執行 amend:

$ git reset 1a9096a34322885ac101175ddcac7dab4c52665d
$ git commit --amend

注: 上面的 git reset 用法不會毀滅你寫的代碼,放心執行便可.

這時 git log 能夠發現該 commit 已經補全了 change-Id.

下一步是把 git reset 撤消掉的代碼從新 commit, 而後 push 便可:

$ git add ......
$ git commit -m "你的提交日誌"
$ git push review HEAD:refs /for/master

方法三: 使用交互式 rebase 找回任意提交位置的 Change-Id:

 

前面方法二中給出的例子是第二個提交缺失 Change-Id,這時用 git reset 還能夠解決問題.

但若是你在一個方案上已經工做了一個月,生成了100個本地 commit,提交時才發現 git log 中第99個 commit 缺失 Change-Id. 若是這時還用 git reset 來找回 Change-Id ......

不要香菇,不要藍瘦.文藝青年表示有辦法優雅的解決問題: 交互式 rebase.

 

第一步,找到缺失 Change-Id 的那個 commit:

$ 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
 
    這是一個提交
     
     Change-Id: I629b2bedff95491875f63634ad3da199612735b6
......

發現是 "I am commit message 1" 這個提交沒有 Change-Id.

 

第二步,編輯交互式 rebase 的命令文件:

執行 git rebase -i, 參數爲 該提交的上一個提交的 commit-id (本例中爲 "表單" 那個提交):

$ git rebase -i d714bcde0c14ba4622d28952c4b2a80882b19927

這個命令會打開默認的編輯器,通常爲 vi. 內容以下:

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

能夠將這個文件理解爲 git rebase 的內嵌腳本.其命令寫法已經在下面的註釋裏給出了.

這裏不贅述,僅給出最終要將該文件編輯成什麼樣子:

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

即: 將缺失了 Change-Id 的 commit 前面的 "pick" 改成 "reword" 便可. 保存退出 (:wq)

注1: 上述文件中 commit 的順序是和 git log 顯示的順序相反的: git log 爲最新的在最前; 上述文件爲 最新的在最後.

注2: 若是進入該模式後,卻不肯定該怎麼改,這時不要擔憂,直接退出編輯則什麼都不會發生 (:q!)

注3: 若是沒有搞清楚運做機制,就要注意,除了按需把 pick 改成 reword 外,不要作其餘改動.尤爲注意不要刪除任何行 (被刪除的那行對應的提交將丟失).

注4: 你應該已經發現,有多個 commit 缺失 Change-Id 的狀況也能夠用該方法一次性處理.

 

第三步,逐個編輯 commit-msg:

上一步打開的文件保存退出後,git會逐個打開被你標註了 reword 的提交日誌頁面.

不須要修改任何東西,逐個保存退出便可 (一路 :wq).

 

第四步,再次提交:

用 git log 查看提交日誌,會發現缺失的 Change-Id 都生成了. 愉快的提交代碼吧!

$ git push review HEAD:refs /for/master

 


心法:

gerrit 的 Change-Id 機制:

 

首先要明確, Change-Id 是 gerrit (代碼審覈平臺)的概念, 與 git (版本管理) 是沒有關係的.

簡單來講, Change-Id 是 gerrit 用以追蹤具體提交的機制. 這裏不貼網上已有的解釋,舉兩個栗子你們體會下:

1. 你已經用 git push 將代碼提交 gerrit 審覈了,這時你發現代碼中有疏漏,修改了一下,執行 git commit --amend, 再次推送還能夠成功. 這就是由於 gerrit 檢查到兩次 push 的 commit 有同一個 change-id, 就認爲是同一個提交,所以能夠 amend.

2. git push 將代碼提交到 gerrit 審覈,到 gerrit 網站一看,大紅字標着 Can Not Merge 字樣. 我想經常使用 gerrit 的同窗確定都遇到過這問題. 以前個人作法是, git reset 後,更新代碼,再從新提交. 如今的作法是,不用 git reset 了,直接 git commit --amend, 刪掉 commit log 中的 change-id 那行,而後wq保存退出.這時 gerrit 的那個鉤子腳本會再生成一個不一樣的 change-id ,這時再更新代碼,從新提交便可成功. 這裏只簡要介紹該方法,具體步驟將在 代碼衝突 場景中詳解.

Change-Id 的生成機制請繼續向下看.

 

git 的 hook 機制:


鉤子(hooks)是一些在$GIT-DIR/hooks目錄的腳本, 在被特定的事件(certain points)觸發後被調用。當git init命令被調用後, 一些很是有用的示例鉤子腳本被拷到新倉庫的hooks目錄中; 可是在默認狀況下它們是不生效的。 把這些鉤子文件的".sample"文件名後綴去掉就可使它們生效。

hook機制能夠理解爲回調.各個鉤子其實就是一段 bash 腳本,各鉤子腳本的名字都是固定的.能夠查看git項目根目錄下的 .git/hooks 這個文件夾,看看都有哪些可用的鉤子.

$ cd project_dir
$ ls .git /hooks/
applypatch-msg.sample  commit-msg.sample   pre-applypatch.sample  prepare-commit-msg.sample  pre-rebase.sample
commit-msg             post-update.sample  pre-commit.sample      pre-push.sample            update.sample

若是有本身感興趣的 git 事件要處理,修改相應的鉤子腳本羅輯便可.而後把 .sample 後綴去掉,鉤子就生效了.

在 gerrit 的 Change-Id 生成機制中,其實 gerrit 就是利用了 commit-msg 的鉤子,在咱們提交代碼後,按必定規則去修改了咱們的提交日誌,在其末尾添加了這麼一行:

 Change-Id: .......

這個鉤子腳本是何時被加入咱們的項目中的呢? 其實就是你在 git push 出錯時 gerrit 網站給你的提示中的那句命令:

$ gitdir=$(git rev-parse --git- dir ); scp -p -P 29418 liux@gerrit.kaiba315.com:hooks /commit-msg ${gitdir} /hooks/

執行該命令便可獲得生成 Change-Id 的鉤子腳本. 這條命令作了如下事情:

// git rev-parse --git- dir 這條命令將找到該項目的 git 目錄,並將其賦值給 gitdir 這個變量.
// 通常就是項目根目錄下的 .git/ 這個目錄.
$ gitdir=$(git rev-parse --git- dir )
 
// 執行 scp 命令,從 gerrit 代碼服務器將鉤子腳本文件 commit-msg 下載到項目的鉤子目錄下 (通常是 .git /hooks/ )
$ scp -p -P 29418 liux@gerrit.kaiba315.com:hooks /commit-msg ${gitdir} /hooks/

查看該腳本,會發現它是用 awk 命令處理了 .git/COMMIT_EDITMSG 這個文件.

因此若是想手動生成 Change-Id ,只要執行下面命令,就能夠生成一個可用的 Change-Id:

$ cd project_dir
$ echo "some commit" > /tmp/test_generate_change_id
$ .git /hooks/commit-msg /tmp/test_generate_change_id
$ cat /tmp/test_generate_change_id
some commit
 
Change-Id: Ic89d5ce6ce4de70d1dcb315ce543c86a2b3ac003

利用 git commit --amend 從新生成 Change-Id 的原理:

git commit --amend , 看名字就知道,是對某個 commit 作出修改的.這種修改既能夠包含文件修改,也能夠僅包含提交日誌修改.

咱們用 --amend 對 commit 作出修改後, commit-msg 的鉤子會被從新觸發, Change-Id 就會被生成出來.

用交互式 git rebase 來生成 Change-Id 也是同一個道理.

另:

經過總結歷次缺失 Change-Id 的例子,發現基本咱們本身經過 git commit 生成的提交都會很順利的生成 Change-Id.

經過 git merge, git revert 等命令由 git 本身生成的 commit 則有較高几率會缺失 Change-Id.

嗯,咱們發現了一個偉大的定律! 然並卵... 並不知道怎麼解決這個問題.

所以提倡儘可能用 git rebase 代替 git merge 來更新代碼.

事實上, git rebase 更新代碼 相較 git merge 更新代碼,有諸多優點,只是略複雜些.強烈建議用 git rebase 方式更新代碼.

相關文章
相關標籤/搜索