首页 > 编程语言 >使用python统计git仓库中频繁修改的热点函数

使用python统计git仓库中频繁修改的热点函数

时间:2023-08-22 23:56:59浏览次数:55  
标签:git python void dfs times 仓库 nfs commit

本篇博客以开源代码RT-Thread为例,描述了如何使用python扫描统计代码中频繁修改的函数,帮助我们发现系统中需求变化和BUG制造的重灾区。

需求背景

最近在学习设计模式时,印象深刻的一句话就是“要将设计模式应用在不稳定、频繁修改的地方,在变化处应用招式”,那么什么样的地方是频繁变化的?找出变化点最好的办法就是模块或者系统的架构师可以根据经验预测识别出系统的需求热点、发展趋势,指导我们进行重构。但是我想有没有其他办法,统计出我们可能注意不到的潜在变化热点呢?这些函数可能预示着我们的代码质量不佳、频繁出现BUG,或者这块代码需求经常变更,面临剧烈的修改冲击。这样的代码,我们要把它找出来,主动思考进行合理的架构设计来抵御需求变化或者BUG产生。

关键技术

要实现上面的功能,我们要摸底下面的几个关键信息能不能搞定:

  1. 代码修改记录,有了修改记录才能统计热点。
  2. 代码修改记录筛选,我们只需要精确到函数级别,精确到某一行代码是否频繁修改没有意义,一般修改不会只改一行。
  3. 自动化统计手段,依靠人工统计是不现实的。

上面的三个问题,提前尝试了一下解决方法如下:

  1. 代码记录自然想到从git获取,git一般查询git log命令获取修改记录,尝试了一下如下:

    $ git log
    commit cc2d54ff9fa0b4e958d4a46dacc1106abac9432e (HEAD -> master, origin/master)
    Merge: 31b6533ba f94ffe28e
    Author: Bernard Xiong <[email protected]>
    Date:   Thu Jun 27 15:47:17 2019 +0800
    
        Merge pull request #2781 from jinsheng20/Timer
    
        增加基础定时器驱动
    
    commit 31b6533baa1d52f070ce43fecaf0183af9ef7299
    Merge: ef6a4aee9 7d0907185
    Author: Bernard Xiong <[email protected]>
    Date:   Thu Jun 27 13:49:16 2019 +0800
    
        Merge pull request #2811 from enkiller/nfs
    
        [components][dfs][nfs] 修复连接 Linux NFS服务器认证错误的问题
    
    

    可以看到,git log可以看到提交的修改记录,每个修改记录都有一个独一无二的commit id。但是不幸的是,我们并不能从这里面看出具体修改了什么,没关系,使用git show即可,git show commit_id filename还可以指定特定文件。

    $ git show 7d0907185837152b918bd4a4b749d453171f6a9f
    commit 7d0907185837152b918bd4a4b749d453171f6a9f
    Author: tangyuxin <[email protected]>
    Date:   Wed Jun 26 11:33:41 2019 +0800
    
        [components][dfs][nfs] ÐÞ¸´Linux NFS·þÎñÆ÷ÈÏÖ¤ÎÊÌâ
    
    diff --git a/components/dfs/filesystems/nfs/SConscript b/components/dfs/filesystems/nfs/SConscript
    ems/nfs/SConscript
    index 8f1e6defc..f830dfc75 100644
    --- a/components/dfs/filesystems/nfs/SConscript
    +++ b/components/dfs/filesystems/nfs/SConscript
    @@ -6,6 +6,8 @@ cwd = GetCurrentDir()
    src = Glob('*.c') + Glob('rpc/*.c')
    CPPPATH = [cwd]
    
    +SrcRemove(src, ['rpc/auth_none.c'])
    +
    group = DefineGroup('Filesystem', src, depend = ['RT_USING_DFS', 'RT_USING_DFS_NFS'], CPPPATH = CPPPATH)
    
    Return('group')
    diff --git a/components/dfs/filesystems/nfs/dfs_nfs.c b/components/dfs/filesystems/nfs/dfs_nfs.c
    index f36531456..e60f6f98c 100644
    --- a/components/dfs/filesystems/nfs/dfs_nfs.c
    +++ b/components/dfs/filesystems/nfs/dfs_nfs.c
    @@ -225,7 +225,7 @@ static nfs_fh3 *get_dir_handle(nfs_filesystem *nfs, const char *name)
            copy_handle(handle, &nfs->current_handle);
        }
    
    -    while ((file = strtok_r(NULL, "/", &path)) != NULL && path[0] != 0)
    +    while ((file = strtok_r(NULL, "/", &path)) != NULL && path && path[0] != 0)
    
    
  2. 有了记录之后,我们要从中筛选出我们感兴趣的函数名,准确从文本中识别出函数名个人感觉不是一件容易的事情,函数名字的形式和参数都多变,不过仔细观察上面的比较记录,会发现git自己能够识别出函数名,并专门使用@@标示出来了,如下的void SystemClock_Config(void)函数,我们只需要筛选符合@@的行就可以了。例子里给了一个@@.*@@\s((\w+)\s+)+[\*,&]*\s*(\w+)\s*\(,效果不是特别好,你可以自己根据函数特点修改,修改后可能需要重新改下最后拼接res_str的地方。

     @@ -160,14 +165,14 @@ void SystemClock_Config(void)
     RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
     RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
    
     -  /**Configure LSE Drive Capability
     +  /** Configure LSE Drive Capability
     */
     HAL_PWR_EnableBkUpAccess();
     -  /**Configure the main internal regulator output voltage
     +  /** Configure the main internal regulator output voltage
     */
     __HAL_RCC_PWR_CLK_ENABLE();
     __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
     -  /**Initializes the CPU, AHB and APB busses clocks
     +  /** Initializes the CPU, AHB and APB busses clocks
     */
     RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI|RCC_OSCILLATORTYPE_HSE;
     RCC_OscInitStruct.HSEState = RCC_HSE_ON;
    
  3. 自动化工具方面这种文本处理的肯定使用python是最快的,git可以使用gitpython库,使用pip install gitpython安装即可,避免使用原生git命令行的繁琐。文本筛选肯定使用正则表达式最方便,可以实现我们想要的功能。

具体脚本

# -- coding: utf-8 --

# author: tanxiaoyao
# date:2019.06.29
import thread

import git
import re
import sys
import threading
import time

reload(sys)
sys.setdefaultencoding("utf-8")

# 全局变量定义
thread_num = 10 # 使用的CPU线程数
commit_num = 1000 # 需要遍历的commit数量
repo = git.Repo.init("D:\\02.Code\\02.Reference\\02.OS\\rt-thread") # 仓库路径,根据自己的实际填写
folder_path = "." # 需要扫描的子文件夹路径
diff_regex = r"@@.*@@\s((\w+)\s+)+[\*,&]*\s*(\w+)\s*\(" # 修改函数名匹配正则表达式,根据自己的需要修改
max_modified_filenum = 30 # 允许的单次提交修改的最大文件数,排除分支合并的commit

# 正则匹配
commit_regex = r"commit\s(\w{40})" # commit匹配正则表达式,当前简单匹配40个hash码


# 全局线程互斥变量
fun_dict= {}
lock = threading.Lock()
global final_thread_count
final_thread_count = 0


# 获取正则匹配结果
def get_regex_match(src_str, regex_str):
    partten = re.compile(regex_str)
    match = partten.findall(src_str)
    return match

# 获取小于特定修改文件数的commit,支持多线程
def get_commit_lessthan(match, thread_id):
    fo = open("fo" + str(thread_id) + ".txt", "w")
    ok_list = []
    count = 0

    # 筛选修改文件数符合要求的commit
    for commit in match:
        filelist_str = repo.git.show(commit, stat=True)
        name_patt = re.compile(commit_regex)
        name_match = name_patt.findall(filelist_str)
        if len(name_match) <= max_modified_filenum:
            ok_list.append(commit)

    # 获取每个commit的具体修改内容
    for commit in ok_list:
        if commit is "":
            continue
        diff_str = get_commit_content(commit,"*")
        # 从内容中提取修改的函数
        change_fun_list = get_change_funcs(diff_str)
        fo.write(diff_str)
        count += 1

        lock.acquire()
        try:
            # 添加到全局数组
            add_funlist2dict(change_fun_list)
            print "Thd" + str(thread_id) + "\t" + commit + "_" + str(count) + "/" + str(len(ok_list))
        finally:
            lock.release()

    fo.close()
    global final_thread_count
    final_thread_count += 1

# 按指定数量拆分数组
def chunks(l, n):
    for i in xrange(0, len(l), n):
        yield l[i:i+n]

# 获取commit列表
def get_commit_list():
    log = repo.git.log(folder_path)
    all_commit = get_regex_match(log, commit_regex)
    commit_groups = list(chunks(all_commit, commit_num / thread_num))
    print commit_groups

    # 开始多个线程扫描修改commit,修改函数名
    try:
        for index in range(0, min(thread_num, len(commit_groups))):
            thread.start_new_thread(get_commit_lessthan, (commit_groups[index], index,) )
    except:
        print "start thread fail!"

    return

# 获取COMMIT中文件的修改内容
def get_commit_content(commit_id, file_name):
    return repo.git.show(commit_id, file_name)

# 提取差异中的函数
def get_change_funcs(diff_log):
    return get_regex_match(diff_log, diff_regex)


# 将函数加入全局dict
def add_funlist2dict(list):
    for fun in list:
        if fun_dict.has_key(fun):
            fun_dict[fun] = fun_dict[fun] + 1
        else:
            fun_dict[fun] = 1


# dict按照value值排序
def sort_funcdict(dict):
    items = dict.items()
    backitems = [[v[1], v[0]] for v in items]
    backitems.sort(None, None, True)
    return [[v[1], v[0]] for v in backitems]


if __name__ == '__main__':
    # 开始处理
    get_commit_list()
    # 等待所有线程处理完毕
    while(final_thread_count < thread_num):
        time.sleep(1)

    # 输出结果
    resfile = open("result.txt", "w")
    sorted_list = sort_funcdict(fun_dict)
    for func_name, times in sorted_list:
        res_str = " times:" + str(times) + "\t" + func_name[1].strip() + " " + func_name[2].strip() + "\n"
        resfile.write(res_str)
    resfile.close()

运行效果

提示:依赖gitpython库,使用pip install gitpython安装。运行后结果输出在脚本同级目录的result.txt中

 times:77	void SystemClock_Config
 times:50	if GetDepend
 times:36	int main
 times:19	void MX_GPIO_Init
 times:18	void stm32_dma_config
 times:18	typedef void
 times:18	HAL_StatusTypeDef HAL_RCC_OscConfig
 times:17	void HAL_MspInit
 times:16	typedef struct
 times:14	rt_err_t es32f0x_configure
 times:14	int rt_hw_spi_bus_init
 times:13	void rt_schedule
 times:13	void HAL_UART_MspDeInit
 times:13	void HAL_SMARTCARD_IRQHandler
 times:13	rt_err_t drv_control
 times:12	void uart_isr
 times:12	HAL_StatusTypeDef SMBUS_Slave_ISR
 times:11	void phy_monitor_thread_entry
 times:11	void MX_USART1_UART_Init
 times:11	def PrepareBuilding
 times:11	HAL_StatusTypeDef HAL_SPI_TransmitReceive
 times:10	void HAL_UART_MspInit
 times:10	void HAL_CAN_IRQHandler
 times:10	rt_err_t rt_sensor_open
 times:10	rt_err_t rt1052_pin_irq_enable

如果仓库修改记录很多,运行时间会比较长,可以在脚本中限制一下统计次数,运行速度主要消耗在git比较两个commit上。

标签:git,python,void,dfs,times,仓库,nfs,commit
From: https://www.cnblogs.com/tanxiaoyao/p/17649952.html

相关文章

  • python+selenium+pytest-(1)_8种元素定位方法
    方法一:元素ID定位username=driver.find_element(By.ID,"username")方法二:元素class定位login=driver.find_element(By.CLASS_NAME,"login")方法三:元素name定位password=driver.find_element(By.NAME,"password")方法四:元素tag定位p=driver.find_eleme......
  • python+selenium+pytest-(2)_访问百度
    UI自动化测试代码的执行顺序就是:加载驱动->访问链接->页面操作importtimefromseleniumimportwebdriverfromselenium.webdriver.common.byimportBy#加载驱动driver=webdriver.Chrome()#访问链接driver.get("http://www.baidu.com")#页面操作inputElement=dr......
  • git上传/下载代码到github
    安装git查看git版本   进入你想要将代码存放的本地文件夹,打开GitBash   https://www.php.cn/faq/505727.htmlhttp://www.taodudu.cc/news/show-3707188.html?action=onClick ......
  • Python 基础面试第二弹
    1. 解释下Python中的面向对象,以及面向对象的三大特点:在Python中,面向对象编程(Object-OrientedProgramming,简称OOP)是一种编程范式,它将数据和操作数据的方法组织在一起,形成对象。面向对象的编程主要围绕着类(Class)和对象(Object)展开,通过封装、继承和多态等概念,实现代码的重用性......
  • Git撤销错误commit
    1、背景新手程序员,对git一直玩不明白,一直会commit错自己的代码。有的时候写自己的东西要push代码回去在另一台电脑上继续写,但是又不想保留自己的中间那次没用的commit,所以就有了这篇文章。2、介绍对于我们本地的代码,在commit后发现自己commit操作有误:commit了不想提交的代码/.......
  • Hugging News #0821: 新的里程碑:一百万个代码仓库!
    每一周,我们的同事都会向社区的成员们发布一些关于HuggingFace相关的更新,包括我们的产品和平台更新、社区活动、学习资源和内容更新、开源库和模型更新等,我们将其称之为「HuggingNews」。本期HuggingNews有哪些有趣的消息,快来看看吧!......
  • docker上建一个jenkins容器 连gitee上代码 当更改代码后,浏览器页面更新
    1.dockerpulljenkinszh/jenkins-zh  2.设置端口  3访问本机的浏览器 跳转 4在cmd窗口输入命令 找到密码,输入,点击继续按钮 5下载默认推荐的插件 6填写账号和密码必须写自己设置的容器的账号和密码 7.  8进入主页面 9插件管理点击......
  • python:用Pandas实现笛卡尔积效果
       1importpandasaspd23456students=pd.DataFrame([[1,'Alice'],7[2,'Bob'],8[13,'John'],9[6,'Alex']],colu......
  • 20230822-github实现AB仓库通信全过程
    第一步注册github账号首先打开自己得github网址https://github.com/用自己常用得邮箱注册一个个人得github账号不多赘述第二步安装git工具账号注册完毕之后需要安装git工具紧接着就是下一步下一步程序得安装注意不要安装再c盘即可当本地出现以上图标window上是shift+右键则......
  • python截图、压缩、转base64,可以用2m压缩到100k,肉眼不失真
    1importwin32gui2importwin32ui3importwin32con4importnumpyasnp5importcv26importbase6478#通过句柄截取窗口内容9defcapture_window_by_handle(handle):10left,top,right,bottom=win32gui.GetWindowRect(handle)11width......