GitLab代码库提交量统计工具
1.说明
统计公司所有项目的提交情况,可指定分支和时间段,返回每个人的提交新增数、删除数和总数。
2.API
文档地址:http://公司gitlab域名/help/api/README.md
- 项目列表查询
返回示例:
[{"id": 1, //项目ID"http_url_to_repo": "http://git.xxx.com/a/saas-project-1.git","web_url": "http://git.xxx.com/a/saas-project-1","name": "saas-project-1", //项目名"name_with_namespace": "a / saas-project-1","path": "saas-project-1","path_with_namespace": "a/saas-project-1"}
]
- 提交记录查询
- 单次提交统计
3.PRIVATE-TOKEN
PRIVATE-TOKEN获取地址:http://公司gitlab域名/profile/account
查看Private token下面的值即可
4.代码
package com.visy.utils;import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.tuple.Triple;import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;/*** @author visy.wang*/
public class GitStatsUtil {private static final String PRIVATE_TOKEN = "你自己的Private token";private static final String BASE_URL = "http://公司gitlab域名/api/v4";private static <T> T doGet(String url, Function<String,T> respHandler, Supplier<T> defaultResp){HttpRequest request = HttpUtil.createGet(BASE_URL + url);request.header("PRIVATE-TOKEN", PRIVATE_TOKEN);HttpResponse response = request.execute();if(response.getStatus() == 200){return respHandler.apply(response.body());}else{return defaultResp.get();}}private static List<Map<String,Object>> listProjects(){int pageNo = 1, pageSize = 100;List<Map<String,Object>> allList = new ArrayList<>();boolean hasNext = true;while (hasNext){System.out.println("listProjects: pageNo=" + pageNo);List<Map<String,Object>> list = listProjects(pageNo, pageSize);allList.addAll(list);pageNo ++;hasNext = list.size() >= pageSize;}return allList;}private static List<Map<String,Object>> listProjects(int pageNo, int pageSize){String url = "/projects?order_by=name&sort=asc&simple=true&archived=false&owned=false&page="+pageNo+"&per_page="+pageSize;return doGet(url, body -> {JSONArray array = JSONArray.parseArray(body);return array.stream().map(item -> {Map<String,Object> mp = new HashMap<>();mp.put("id", ((JSONObject)item).getLong("id"));mp.put("name", ((JSONObject)item).getString("name"));return mp;}).collect(Collectors.toList());}, Collections::emptyList);}private static List<String> listCommitIds(Object projectId, String since, String until, String refName){int pageNo = 1, pageSize = 100;List<String> allList = new ArrayList<>();boolean hasNext = true;while (hasNext){System.out.println("listCommitIds: pageNo=" + pageNo+", projectId="+projectId);List<String> list = listCommitIds(projectId, since, until, refName, pageNo, pageSize);allList.addAll(list);pageNo ++;hasNext = list.size() >= pageSize;}return allList;}private static List<String> listCommitIds(Object projectId, String since, String until, String refName, int pageNo, int pageSize){String url = "/projects/" + projectId + "/repository/commits?ref_name=" + refName+ "&page=" + pageNo + "&per_page=" + pageSize+ "&since=" + since + "T00:00:00+08:00&until=" + until+"T23:59:59+08:00";return doGet(url, body -> {JSONArray array = JSONArray.parseArray(body);return array.stream().map(item -> ((JSONObject)item).getString("id")).collect(Collectors.toList());}, Collections::emptyList);}private static Map<String,Object> getCommitStats(Object projectId, Object commitId){String url = "/projects/" + projectId + "/repository/commits/" + commitId;return doGet(url, body -> {JSONObject data = JSONObject.parseObject(body);Map<String,Object> mp = new HashMap<>();mp.put("authorName", data.getString("author_name"));mp.put("authorEmail", data.getString("author_email"));data = data.getJSONObject("stats");mp.put("add", data.getInteger("additions"));mp.put("delete", data.getInteger("deletions"));mp.put("total", data.getInteger("total"));return mp;}, Collections::emptyMap);}public static void main(String[] args) {//指定时间段和分支名String since = "2024-01-01", until = "2024-01-31", branch = "branch1";List<Map<String, Object>> projects = listProjects();List<Map<String,Object>> allUserCommits = new ArrayList<>();projects.forEach(project -> {Object projectId = project.get("id");List<String> commitIds = listCommitIds(projectId, since, until, branch);commitIds.forEach(commitId -> allUserCommits.add(getCommitStats(projectId, commitId)));});Map<String, Triple<Integer,Integer,Integer>> userCommitsMap = new HashMap<>();allUserCommits.forEach(item -> {if(item==null || item.isEmpty()){return;}String userName = item.get("authorName").toString();Triple<Integer,Integer,Integer> triple = userCommitsMap.getOrDefault(userName, Triple.of(0,0,0));Integer add = Integer.valueOf(item.get("add").toString());Integer delete = Integer.valueOf(item.get("delete").toString());Integer total = Integer.valueOf(item.get("total").toString());triple = Triple.of(triple.getLeft()+add, triple.getMiddle()+delete, triple.getRight()+total);userCommitsMap.put(userName, triple);});System.out.println("涉及项目("+projects.size()+"):");projects.forEach(p -> System.out.println(p.get("name")+" [id="+p.get("id")+"]"));System.out.println("----------------------------------------------------------");System.out.println("分支:"+branch+", 统计周期: " + since+" ~ " + until);System.out.println("----------------------------------------------------------");AtomicInteger add = new AtomicInteger(0), delete = new AtomicInteger(0), total = new AtomicInteger(0);userCommitsMap.forEach((userName, triple) -> {add.getAndAdd(triple.getLeft());delete.getAndAdd(triple.getMiddle());total.getAndAdd(triple.getRight());System.out.println(userName + ": 新增=" + triple.getLeft()+", 删除="+triple.getMiddle()+", 总数="+triple.getRight());});System.out.println("总计: 新增=" + add.get() + ", 删除=" + delete.get() + ", 总数=" + total.get());}
}
5.输出示例:
涉及项目(3):
saas-project-1 [id=1]
saas-project-2 [id=2]
saas-project-3 [id=3]
----------------------------------------------------------
分支:branch1, 统计周期: 2024-01-01 ~ 2024-01-31
----------------------------------------------------------
张三: 新增=1, 删除=2, 总数=3
李四: 新增=4, 删除=5, 总数=9
王五: 新增=6, 删除=7, 总数=13
总计: 新增=11, 删除=14, 总数=25
相关文章:

GitLab代码库提交量统计工具
1.说明 统计公司所有项目的提交情况,可指定分支和时间段,返回每个人的提交新增数、删除数和总数。 2.API 文档地址:http://公司gitlab域名/help/api/README.md 项目列表查询 返回示例: [{"id": 1, //项目ID"http…...

Python爬虫技术详解:从基础到高级应用,实战与应对反爬虫策略【第93篇—Python爬虫】
前言 随着互联网的快速发展,网络上的信息爆炸式增长,而爬虫技术成为了获取和处理大量数据的重要手段之一。在Python中,requests模块是一个强大而灵活的工具,用于发送HTTP请求,获取网页内容。本文将介绍requests模块的…...
关于TypeReference的使用
关于TypeReference的使用 在项目中,有遇到TypeReference的使用,其主要在字符串转对象过程中,对于序列化和反序列化中也有效果,将字符串转换成自定义对象. 1 说明 以常见为例,在com.alibaba.fastjson包下面的TypeReference类,是指Type的Reference,表示某类型的一个指…...
阿里大文娱前端一面
引言 我目前本科大四,正在春招找前端,有大厂内推的友友可以聊一聊,球球给孩子的机会吧。 我整理了一份10w字的前端技术文档:https://qx8wba2yxsl.feishu.cn/docx/Vb5Zdq7CGoPAsZxMLztc53E1n0k?fromfrom_copylink,对…...

Clickhouse系列之连接工具连接、数据类型和数据库
基本操作 一、使用连接工具连接二、数据类型1、数字类型IntFloatDecimal 2、字符串类型StringFixedStringUUID 3、时间类型DateTimeDateTime64Date 4、复合类型ArrayEnum 5、特殊类型Nullable 三、数据库 一、使用连接工具连接 上一篇介绍了clickhouse的命令行登录,…...

【深入理解设计模式】原型设计模式
原型设计模式 原型设计模式(Prototype Pattern)是一种创建型设计模式,它允许通过复制已有对象来创建新对象,而无需直接依赖它们的具体类。这种模式通常用于需要频繁创建相似对象的场景,以避免昂贵的创建操作或初始化过…...

Python算法题集_图论(课程表)
Python算法题集_课程表 题207:课程表1. 示例说明2. 题目解析- 题意分解- 优化思路- 测量工具 3. 代码展开1) 标准求解【循环递归全算】2) 改进版一【循环递归缓存】3) 改进版二【循环递归缓存反向计算】4) 改进版三【迭代剥离计数器检测】 4. 最优算法5. 相关资源 本…...

视频评论挖掘软件|抖音视频下载工具
针对抖音视频下载的需求,我们开发了一款功能强大的工具,旨在解决用户在获取抖音视频时需要逐个复制链接、下载的繁琐问题。我们希望用户能够通过简单的关键词搜索,实现自动批量抓取视频,并根据需要进行选择性批量下载。因此&#…...

Linux学习方法-框架学习法——Linux驱动架构的演进
配套视频学习链接:https://www.bilibili.com/video/BV1HE411w7by?p4&vd_sourced488bc722b90657aaa06a1e8647eddfc 目录 Linux驱动演进的过程 Linux驱动的原始架构(Linux V2.4) 平台总线架构(platform) Linux设备树 Linux驱动演进的趋势 Linux驱动演进的过程…...
Spring Boot基础面试问题(一)
上篇文章中10个Spring Boot面试问题的标准答案: 什么是Spring Boot?它与Spring框架有什么区别? 标准回答:Spring Boot是基于Spring框架的快速开发框架,它简化了Spring应用程序的搭建和配置过程,提供了一套自…...

电路设计(28)——交通灯控制器的multisim仿真
1.功能设定 南北、东西两道的红灯时间、绿灯时间均为24S,数码管显示倒计时。在绿灯的最后5S内,黄灯闪烁。有夜间模式:按下按键进入夜间模式。在夜间模式下,数码管显示计数最大值,两个方向的黄灯不停闪烁。 2.电路设计 …...

【Docker】免费使用的腾讯云容器镜像服务
需要云服务器等云产品来学习Linux可以移步/-->腾讯云<--/官网,轻量型云服务器低至112元/年,新用户首次下单享超低折扣。 目录 1、设置密码 2、登录实例(sudo docker login xxxxxx) 3、新建命名空间(每个命名空…...
如何让qml使用opengl es
要让 QML 使用 OpenGL ES,您需要确保项目配置正确,并在应用程序中使用 QSurfaceFormat 来设置 OpenGL ES 渲染。 以下是一些步骤来配置 QML 使用 OpenGL ES: 1、项目配置:在您的项目配置文件(例如 .pro 文件…...

金航标电子位于广西柳州鹿寨县天线生产基地于大年正月初九开工了!!
金航标电子位于广西柳州鹿寨县天线生产基地于大年正月初九开工了!!!金航标kinghelm( http://www.kinghelm.com.cn )总部位于中国深圳市,兼顾技术、成本、管理、效率和可持续发展。东莞塘厦实验室全电波暗…...

FlinkCDC详解
1、FlinkCDC是什么 1.1 CDC是什么 CDC是Chanage Data Capture(数据变更捕获)的简称。其核心原理就是监测并捕获数据库的变动(例如增删改),将这些变更按照发生顺序捕获,将捕获到的数据,写入数据…...
力扣代码学习日记六
Problem: 66. 加一 思路 给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。 最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。 你可以假设除了整数 0 之外,这个整数不会以零开头。 示例 1: 输…...
「Python系列」Python标准库
文章目录 一、 os 模块:文件和目录操作二、 sys 模块:与Python解释器交互三、 datetime 模块:日期和时间处理四、 json 模块:处理JSON数据五、 re 模块:正则表达式六、 time模块1. 获取当前时间2. 延迟执行(…...

虚拟列表【vue】等高虚拟列表/非等高虚拟列表
文章目录 1、等高虚拟列表2、非等高虚拟列表 1、等高虚拟列表 参考文章1 参考文章2 <!-- eslint-disable vue/multi-word-component-names --> <template><divclass"waterfall-wrapper"ref"waterfallWrapperRef"scroll"handleScro…...

【MySQL】如何理解索引(高频面试点)
一、前言 首先这个博客会介绍一些关于MySQL中索引的基本内容以及一些基本的语法,当然里面也会有些常见的面试题的解答。 二、关于索引 1、概念 索引是一种能够帮助MySQL高效的去磁盘检索数据的一种数据结构。在MySQL的Innodb存储引擎中呢,采用的是B树的…...
NXP实战笔记(四):S32K3xx如何产生中心对称三相六路波形
目录 1、概述 1.1、理论基础 2、RTD实现 2.1、Emios时基配置 2.1.1、EmiosMcl 2.1.2、EmiosCommon 2.2、Emios PWM配置 2.3、TRGMUX 2.4、LCU 2.5、外设信号配置 3、代码实现 4、测试结果 1、概述 电机控制中需要产生三相六路SVPWM进行占空比与周期调制,怎么通过RT…...

【WiFi帧结构】
文章目录 帧结构MAC头部管理帧 帧结构 Wi-Fi的帧分为三部分组成:MAC头部frame bodyFCS,其中MAC是固定格式的,frame body是可变长度。 MAC头部有frame control,duration,address1,address2,addre…...

使用分级同态加密防御梯度泄漏
抽象 联邦学习 (FL) 支持跨分布式客户端进行协作模型训练,而无需共享原始数据,这使其成为在互联和自动驾驶汽车 (CAV) 等领域保护隐私的机器学习的一种很有前途的方法。然而,最近的研究表明&…...

MODBUS TCP转CANopen 技术赋能高效协同作业
在现代工业自动化领域,MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步,这两种通讯协议也正在被逐步融合,形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...

IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)
文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...

SpringCloudGateway 自定义局部过滤器
场景: 将所有请求转化为同一路径请求(方便穿网配置)在请求头内标识原来路径,然后在将请求分发给不同服务 AllToOneGatewayFilterFactory import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; impor…...
在QWebEngineView上实现鼠标、触摸等事件捕获的解决方案
这个问题我看其他博主也写了,要么要会员、要么写的乱七八糟。这里我整理一下,把问题说清楚并且给出代码,拿去用就行,照着葫芦画瓢。 问题 在继承QWebEngineView后,重写mousePressEvent或event函数无法捕获鼠标按下事…...

【C++进阶篇】智能指针
C内存管理终极指南:智能指针从入门到源码剖析 一. 智能指针1.1 auto_ptr1.2 unique_ptr1.3 shared_ptr1.4 make_shared 二. 原理三. shared_ptr循环引用问题三. 线程安全问题四. 内存泄漏4.1 什么是内存泄漏4.2 危害4.3 避免内存泄漏 五. 最后 一. 智能指针 智能指…...

如何应对敏捷转型中的团队阻力
应对敏捷转型中的团队阻力需要明确沟通敏捷转型目的、提升团队参与感、提供充分的培训与支持、逐步推进敏捷实践、建立清晰的奖励和反馈机制。其中,明确沟通敏捷转型目的尤为关键,团队成员只有清晰理解转型背后的原因和利益,才能降低对变化的…...
深度学习之模型压缩三驾马车:模型剪枝、模型量化、知识蒸馏
一、引言 在深度学习中,我们训练出的神经网络往往非常庞大(比如像 ResNet、YOLOv8、Vision Transformer),虽然精度很高,但“太重”了,运行起来很慢,占用内存大,不适合部署到手机、摄…...

nnUNet V2修改网络——暴力替换网络为UNet++
更换前,要用nnUNet V2跑通所用数据集,证明nnUNet V2、数据集、运行环境等没有问题 阅读nnU-Net V2 的 U-Net结构,初步了解要修改的网络,知己知彼,修改起来才能游刃有余。 U-Net存在两个局限,一是网络的最佳深度因应用场景而异,这取决于任务的难度和可用于训练的标注数…...