有效程式碼行數預研方案
ofollow,noindex">歡迎到個人部落格閱讀
前言
公司這邊其實已經使用sonar對程式碼進行了統計,但需求想要更加明確的統計每個user單獨的程式碼量,以便對該user的工作質量進行評估。sonar目前只有對整個目錄檔案的程式碼行數統計,對於git merge之類的程式碼沒有區別得很細,也就是說不斷merge別人的程式碼也會被統計到當前提交的user上,而我們是需要明確每個user具體提交了多少程式碼量,然後下面是針對有效程式碼行數預研的方案及試錯,有需要的朋友可以參閱,少走彎路。
已實現:
提交通過的程式碼可以用sonar統計行數。Num2-Num1=有效行數(實際開發行數)
存在問題:
如果過程中分支有從別的分支合併程式碼過來,會造成統計時混合了其他分支程式碼。
處理方案
方案一:(客戶端方案)
1.情況一,當前功能分支一直提交,並沒有merge其他分支來支援當前分支開發,那麼可以通過每次提交成功後統計該分支的有效行數,通過之前已實現的sonar方案。
2.情況二,f2分支在開發過程中,如需要引用f1分支的功能作為開發支撐,這種情況需要在客戶端的git倉庫中埋點,使用git hook(post-merge)監聽,當pull f1並merge成功後,觸發post-merge監聽,接著執行通知,告知接收者幾個關鍵引數(合併的分支、合併成功後總程式碼行數),接收者收到入參後,接著統計合併分支的程式碼行數,然後 合併後的總行數-當前統計分支行數=合併進來的行數,以後每次f1分支提交統計程式碼行數時,都減去“合併進來的行數”得到提交的有效行數。
方案二:(服務端方案)
拉取分支後,雲效在未合併完成前,不允許拉取合併其他分支。監聽使用者的在服務端的post-receive觸發,如果與第一次拉取做一個對應關係。
方案三:(本地客戶端hook,優化版本)
本地客戶端方案關鍵點:
hook指令碼注入,原生代碼統計方案,merge、commit觸發執行指令碼,shell指令碼post互動
(實現全域性hook,本質也是本地配置引用,需要配置干預。與直接copy都是需要人為本地干預 no)
1.hook指令碼注入:hook檔案自動copy暫時沒有觸發入口(方案:雲效流程干預)
2.shell指令碼post互動:雲效上有sonar資料的攔截處理層,這部分之前是我們自己處理,api互動補充邏輯即可。
方案步驟:
1.拉取分支,然後給一個勾選的觸發,彈窗,選擇遠端的hook和本地倉庫目錄(目錄/.git/hooks)
在雲效這裡,由於客戶端pull動作無法知道完成時機,要麼雲效定時迴圈檢測目錄存在,然後注入遠端hook,要麼培訓說明人為判斷拉取成功後必須手動勾選,然後注入遠端hook,兩個方式目的都是想辦法把遠端的hooks檔案注入到本地的hooks目錄中。
2.原生代碼統計(難點,在不安轉三方支援的情況下,怎麼統計分支除merge之外的純程式碼量,暫時只統計新增行)
那麼考慮能否從檔案本身的角度入手,監聽或合適時機主動獲取檔案數量和檔案總行數。把過程中產生純新增行數記錄到併合適時機告訴接收者。
3.shell通過curl傳送post請求:
curl -d parm1=111¶m2=222 http://www.baidu.com
4.本地每次merge與本地commit只有hook存在,都能觸發對應的指令碼程式。
實驗情況:
1.測試本地存在hook指令碼的情況,在本地分支merge和commit兩個關鍵動作都能觸發執行指令碼。

image
本地提交,成功觸發pre-commit指令碼:

image
本地分支merge,成功觸發post-merge指令碼:

image
2.測試目錄檔案數及總行數:
獲取本地倉庫目錄下,所有檔案書和行數:shell指令碼測試ok

image
補充:
1)能否去除空行,註釋行。(使用grep排除空行和註釋行,sed和tee用法)
2)快取對比資料方案(本地?遠端?)
3)test.sh在統計檔案數和行數,不包括.git目錄的。

image
執行結果:

image
3.測試修改檔案了,先merge再commit,確定行數獲取時機。
答:merge之前,git的操作是必須需要先本地commit當前分支,否則合併失
敗。(保證當前分支是最新的節點)
這裡就解決了,統計merge之前和merge之後的程式碼行數。統一由commit時產
生的行數作為計算基準數。
當前分支未commit就執行merge動作:

image
當前分支已commit,執行merge動作,觸發指令碼計算行數:

image
方案四:伺服器端,分析.git,分離每個user提交的程式碼資訊,分別獲取對應的行數。(選用方案)
指令碼程式碼:統計總行數、歷史刪除、增加的總行數
git log --author="hans" --pretty=tformat: --numstat | gawk '{ add += $1 ; subs += $2 ; loc += $1 - $2 } END { printf "增加的行數:%s 刪除的行數:%s 總行數: %s\n",add,subs,loc }'
注意:該方法統計,最後一行如果是空行,不納入行數計算,如果不是則納入。
測試場景:
使用者hans:
1.master分支有10行,在master上建立分支t1,t1分支新增5行。分別獲取master行數、t1行數
master行數10:

image
t1行數15:

image
2.上傳t1分支,讓使用者tt克隆專案,tt使用者在master的基礎上新建分支t2,新增行數8,提交。分別獲取hans提交行數,tt提交行數。

image
把t2合併進入master,再次統計master上不同使用者提交的程式碼行數,hans行數10,tt行數8,符合預期有效行數。

image
補充:hans電腦上傳master(已合併t2),下圖是tt電腦上,拉取最新的master,然後再次分別獲取對應行數。
對於tt使用者來說,雖然master上行數變為18行,但統計tt的提交量還是8行,符合預期有效行數。
[圖片上傳失敗...(image-dc6ef-1539014498277)]
3.測試同一個檔案下,不同使用者操作,並統計行數。
在hans電腦上,如果發生衝突的部分,會導致tt使用者提交的這部分衝突程式碼統計失敗,還是原來的8行,只有hans的程式碼新增了,由原來的10行,新增了5行。

image
在tt使用者電腦,master上pull回來,統計同hans電腦結果。
[圖片上傳失敗...(image-d1a9f6-1539014498277)]
結論:
1.為了統計使用者有效程式碼行數,建議每個使用者在commit的行都是新的行,如果在別的使用者行上修改,統計時是不納入你的程式碼行數。
2.每個分支上,使用者之間提交的程式碼行數都是相互隔離的,可以分別獲取對應使用者提交的程式碼行數,與merge無關。
3.在實際應用上,雖然shell指令碼已經編寫完畢,但是由於系統間在執行過程中會存在許可權問題,不保證每個系統都穩定能執行到指令碼。最終討論的方案是,通過Java呼叫git命令來執行git的資訊統計,後面會針對該內容專門開一篇文章分享。
相關資料
git hooks鉤子:
那麼鉤子什麼時候被執行,Git預定義了觸發時機:
ClientSide hooks:
1 pre-commit,當執行commit動作時先執行此hook,可以用此hook做一些檢查,比如程式碼風格檢查,或者先跑測試。
2 prepare-commit-msg, 當commit時需要輸入message前會觸發此hook,可以用此hook來定製自己的default message資訊。
3 commit-msg,當用戶輸入commit的message後被觸發,可以用此hook校驗message的資訊,比如是否符合規定,有沒有cr等。
4 post-commit, 當commit完成後被觸發,可以用此hook傳送notification等。
5 pre-rebase, rebase之前會被觸發,可以用此hook來拒絕所有的已經push的commits進行rebase操作。
6 post-merge, 當merge成功後,會觸發此hook。
7 pre-push, 當push時,remote refs被更新,但是在所有的objects傳輸前被觸發。
8 pre-auto-gc, 當git gc --auto執行前被觸發。在垃圾回收之前做一些驗證或備份是挺不錯的。
ServerSide hooks:
1 pre-receive, 當收到push動作之前會被執行。
2 update, 也是收到push動作之前被執行,但是有可能被執行多次,每個branch一次。
3 post-receive, 當push動作已經完成的時候會被觸發,可以用此hook來push notification等,比如發郵件,通知持續構建伺服器等。
git log相關資訊:
統計某人的程式碼提交量,包括增加,刪除:
git log --author="0]; } END { for(cc in c) printf "%5d %s\n",c[cc],cc; }' | sort -u -n -r | head -n 5
貢獻者統計:
git log --pretty='%aN' | sort -u | wc -l
提交數統計:
git log --oneline | wc -l
新增或修改的程式碼行數:
git log --stat|perl -ne 'END { printc += $1 if /(\d+) insertions/;
git log 引數說明:
--author 指定作者
--stat 顯示每次更新的檔案修改統計資訊,會列出具體檔案列表
--shortstat 統計每個commit 的檔案修改行數,包括增加,刪除,但不列出檔案列表:
--numstat 統計每個commit 的檔案修改行數,包括增加,刪除,並列出文件列表:
-p 選項展開顯示每次提交的內容差異,用 -2 則僅顯示最近的兩次更新
例如:git log -p -2
--name-only 僅在提交資訊後顯示已修改的檔案清單
--name-status 顯示新增、修改、刪除的檔案清單
--abbrev-commit 僅顯示 SHA-1 的前幾個字元,而非所有的 40 個字元
--relative-date 使用較短的相對時間顯示(比如,“2 weeks ago”)
--graph 顯示 ASCII 圖形表示的分支合併歷史
--pretty 使用其他格式顯示歷史提交資訊。可用的選項包括 oneline,short,full,fuller 和 format(後跟指定格式)
例如: git log --pretty=oneline ; git log --pretty=short ; git log --pretty=full ; git log --pretty=fuller
--pretty=tformat: 可以定製要顯示的記錄格式,這樣的輸出便於後期程式設計提取分析
例如:git log --pretty=format:""%h - %an, %ar : %s""
下面列出了常用的格式佔位符寫法及其代表的意義。
選項 說明
%H 提交物件(commit)的完整雜湊字串
%h 提交物件的簡短雜湊字串
%T 樹物件(tree)的完整雜湊字串
%t 樹物件的簡短雜湊字串
%P 父物件(parent)的完整雜湊字串
%p 父物件的簡短雜湊字串
%an 作者(author)的名字
%ae 作者的電子郵件地址
%ad 作者修訂日期(可以用 -date= 選項定製格式)
%ar 作者修訂日期,按多久以前的方式顯示
%cn 提交者(committer)的名字
%ce 提交者的電子郵件地址
%cd 提交日期
%cr 提交日期,按多久以前的方式顯示
%s 提交說明
--since 限制顯示輸出的範圍,
例如: git log --since=2.weeks 顯示最近兩週的提交
選項 說明
-(n) 僅顯示最近的 n 條提交
--since, --after 僅顯示指定時間之後的提交。
--until, --before 僅顯示指定時間之前的提交。
--author 僅顯示指定作者相關的提交。
--committer 僅顯示指定提交者相關的提交。
一些例子: // 一分鐘之前的所有 git log --until=1.minute.ago //一天之內的log log git log --since=1.day.ago //一個小時之內的log git log --since=1.hour.ago //一個月之前到半個月之前的log git log --since=`.month.ago --until=2.weeks.ago //某個時間段的log git log --since ==2013-08.01 --until=2013-09-07 //看看某一個檔案的相關歷史記錄 git blame 例如:git blame index.html --date short