如何优化Git仓库月度Python/SAS提交统计的Bash函数?
优化Git仓库按月统计Python/SAS提交次数的性能问题
原循环方案需要执行72次git log调用(36个月×2种文件),性能瓶颈明显。无循环方案的核心问题是提交行与文件名的正则匹配冲突,以及未覆盖无提交的月份,以下是修正后的可行方案:
修正后的无循环函数
function get_commits() { local start_date start_date=$(date -d "$(date +%Y-%m-01) -36 months" +%Y-%m-01) local end_date end_date=$(date -d "$(date +%Y-%m-01) +1 month" +%Y-%m-01) # 生成需要统计的所有月份列表,确保覆盖36个月 local current_month="$start_date" declare -A all_months while [[ "$current_month" < "$end_date" ]]; do month_str=$(date -d "$current_month" +%Y-%m) all_months["$month_str"]=1 current_month=$(date -d "$current_month +1 month" +%Y-%m-01) done # 打印表头 printf "%-10s %-10s %-10s\n" "Month" "Python" "SAS" # 单次Git log获取全量数据,用COMMIT:前缀标记提交行,避免与文件名混淆 git log --no-merges --since="$start_date" --until="$end_date" --pretty=format:"COMMIT:%cd %h" --date=format:'%Y-%m' --name-only -- "*.py" "*.sas" | \ awk -v months="${!all_months[*]}" ' BEGIN { OFS = "\t" # 初始化所有月份的计数为0 split(months, month_arr, " ") for (i in month_arr) { py[month_arr[i]] = 0 sas[month_arr[i]] = 0 } current_commit = "" current_date = "" } # 识别提交行:以COMMIT:开头 /^COMMIT:/ { current_date = substr($0, 8, 7) # 提取YYYY-MM部分 current_commit = $2 seen_py[current_commit] = 0 seen_sas[current_commit] = 0 next } # 处理Python文件 /\.py$/ { if (current_commit != "" && !seen_py[current_commit]++) { py[current_date]++ } } # 处理SAS文件 /\.sas$/ { if (current_commit != "" && !seen_sas[current_commit]++) { sas[current_date]++ } } END { # 按月份升序输出 PROCINFO["sorted_in"] = "@ind_str_asc" for (date in py) { printf "%-10s %-10d %-10d\n", date, py[date], sas[date] } }' }
关键修正点
避免提交行与文件名混淆:
在git log的格式化字符串中添加COMMIT:前缀,通过/^COMMIT:/精准识别提交行,彻底解决文件名(如2024-05.py)被误判为提交日期的问题。覆盖所有目标月份:
提前生成过去36个月的完整列表并初始化计数为0,确保即使某月份没有相关提交,也会输出0值,与原循环函数的输出结果保持一致。准确关联提交与文件:
增加current_commit和current_date变量缓存当前提交的信息,避免处理文件时丢失上下文,确保每个提交仅被计数一次(无论修改多少个同类型文件)。使用提交日期而非作者日期:
将%ad(作者日期)改为%cd(提交日期),与原循环函数中--since/--until的时间判定逻辑保持统一,统计结果更准确。
内容的提问来源于stack exchange,提问作者intransigent_rocker
相关产品推荐
相关产品推荐

