有效代码行数预研方案

前言

公司这边其实已经使用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脚本注入,本地代码统计方案,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&param2=222 http://www.baidu.com
4.本地每次merge与本地commit只有hook存在,都能触发对应的脚本程序。

实验情况:
1.测试本地存在hook脚本的情况,在本地分支merge和commit两个关键动作都能触发执行脚本。

本地提交,成功触发pre-commit脚本:

本地分支merge,成功触发post-merge脚本:

2.测试目录文件数及总行数:
获取本地仓库目录下,所有文件书和行数:shell脚本测试ok

补充:
1)能否去除空行,注释行。(使用grep排除空行和注释行,sed和tee用法)
2)缓存对比数据方案(本地?远程?)
3)test.sh在统计文件数和行数,不包括.git目录的。

执行结果:

3.测试修改文件了,先merge再commit,确定行数获取时机。
答:merge之前,git的操作是必须需要先本地commit当前分支,否则合并失
败。(保证当前分支是最新的节点)
这里就解决了,统计merge之前和merge之后的代码行数。统一由commit时产
生的行数作为计算基准数。
当前分支未commit就执行merge动作:

当前分支已commit,执行merge动作,触发脚本计算行数:

脚本代码:统计总行数、历史删除、增加的总行数

1
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:

t1行数15:

2.上传t1分支,让用户tt克隆项目,tt用户在master的基础上新建分支t2,新增行数8,提交。分别获取hans提交行数,tt提交行数。

把t2合并进入master,再次统计master上不同用户提交的代码行数,hans行数10,tt行数8,符合预期有效行数。

补充:hans电脑上传master(已合并t2),下图是tt电脑上,拉取最新的master,然后再次分别获取对应行数。
对于tt用户来说,虽然master上行数变为18行,但统计tt的提交量还是8行,符合预期有效行数。

3.测试同一个文件下,不同用户操作,并统计行数。
在hans电脑上,如果发生冲突的部分,会导致tt用户提交的这部分冲突代码统计失败,还是原来的8行,只有hans的代码新增了,由原来的10行,新增了5行。

在tt用户电脑,master上pull回来,统计同hans电脑结果。

结论:
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=”$(git config –get user.name)” –pretty=tformat: –numstat | gawk ‘{ add += $1 ; subs += $2 ; loc += $1 - $2 } END { printf “added lines: %s removed lines : %s total lines: %s\n”,add,subs,loc }’ -
仓库提交者排名前 5(如果看全部,去掉 head 管道即可):
git log –pretty=’%aN’ | sort | uniq -c | sort -k1 -n -r | head -n 5
仓库提交者(邮箱)排名前 5:这个统计可能不会太准,因为很多人有不同的邮箱,但会使用相同的名字
git log –pretty=format:%ae | gawk – ‘{ ++c[$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 { print $c } $c += $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
坚持原创,您的支持是继续创作的动力!
0%