当前位置: 首页 > news >正文

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.说明 统计公司所有项目的提交情况&#xff0c;可指定分支和时间段&#xff0c;返回每个人的提交新增数、删除数和总数。 2.API 文档地址&#xff1a;http://公司gitlab域名/help/api/README.md 项目列表查询 返回示例&#xff1a; [{"id": 1, //项目ID"http…...

Python爬虫技术详解:从基础到高级应用,实战与应对反爬虫策略【第93篇—Python爬虫】

前言 随着互联网的快速发展&#xff0c;网络上的信息爆炸式增长&#xff0c;而爬虫技术成为了获取和处理大量数据的重要手段之一。在Python中&#xff0c;requests模块是一个强大而灵活的工具&#xff0c;用于发送HTTP请求&#xff0c;获取网页内容。本文将介绍requests模块的…...

关于TypeReference的使用

关于TypeReference的使用 在项目中,有遇到TypeReference的使用,其主要在字符串转对象过程中,对于序列化和反序列化中也有效果,将字符串转换成自定义对象. 1 说明 以常见为例,在com.alibaba.fastjson包下面的TypeReference类,是指Type的Reference&#xff0c;表示某类型的一个指…...

阿里大文娱前端一面

引言 我目前本科大四&#xff0c;正在春招找前端&#xff0c;有大厂内推的友友可以聊一聊&#xff0c;球球给孩子的机会吧。 我整理了一份10w字的前端技术文档&#xff1a;https://qx8wba2yxsl.feishu.cn/docx/Vb5Zdq7CGoPAsZxMLztc53E1n0k?fromfrom_copylink&#xff0c;对…...

Clickhouse系列之连接工具连接、数据类型和数据库

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

【深入理解设计模式】原型设计模式

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

Python算法题集_图论(课程表)

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

视频评论挖掘软件|抖音视频下载工具

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

Linux学习方法-框架学习法——Linux驱动架构的演进

配套视频学习链接&#xff1a;https://www.bilibili.com/video/BV1HE411w7by?p4&vd_sourced488bc722b90657aaa06a1e8647eddfc 目录 Linux驱动演进的过程 Linux驱动的原始架构(Linux V2.4) 平台总线架构(platform) Linux设备树 Linux驱动演进的趋势 Linux驱动演进的过程…...

Spring Boot基础面试问题(一)

上篇文章中10个Spring Boot面试问题的标准答案&#xff1a; 什么是Spring Boot&#xff1f;它与Spring框架有什么区别&#xff1f; 标准回答&#xff1a;Spring Boot是基于Spring框架的快速开发框架&#xff0c;它简化了Spring应用程序的搭建和配置过程&#xff0c;提供了一套自…...

电路设计(28)——交通灯控制器的multisim仿真

1.功能设定 南北、东西两道的红灯时间、绿灯时间均为24S&#xff0c;数码管显示倒计时。在绿灯的最后5S内&#xff0c;黄灯闪烁。有夜间模式&#xff1a;按下按键进入夜间模式。在夜间模式下&#xff0c;数码管显示计数最大值&#xff0c;两个方向的黄灯不停闪烁。 2.电路设计 …...

【Docker】免费使用的腾讯云容器镜像服务

需要云服务器等云产品来学习Linux可以移步/-->腾讯云<--/官网&#xff0c;轻量型云服务器低至112元/年&#xff0c;新用户首次下单享超低折扣。 目录 1、设置密码 2、登录实例&#xff08;sudo docker login xxxxxx&#xff09; 3、新建命名空间&#xff08;每个命名空…...

如何让qml使用opengl es

要让 QML 使用 OpenGL ES&#xff0c;您需要确保项目配置正确&#xff0c;并在应用程序中使用 QSurfaceFormat 来设置 OpenGL ES 渲染。 以下是一些步骤来配置 QML 使用 OpenGL ES&#xff1a; 1、项目配置&#xff1a;在您的项目配置文件&#xff08;例如 .pro 文件&#xf…...

金航标电子位于广西柳州鹿寨县天线生产基地于大年正月初九开工了!!

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

FlinkCDC详解

1、FlinkCDC是什么 1.1 CDC是什么 CDC是Chanage Data Capture&#xff08;数据变更捕获&#xff09;的简称。其核心原理就是监测并捕获数据库的变动&#xff08;例如增删改&#xff09;&#xff0c;将这些变更按照发生顺序捕获&#xff0c;将捕获到的数据&#xff0c;写入数据…...

力扣代码学习日记六

Problem: 66. 加一 思路 给定一个由 整数 组成的 非空 数组所表示的非负整数&#xff0c;在该数的基础上加一。 最高位数字存放在数组的首位&#xff0c; 数组中每个元素只存储单个数字。 你可以假设除了整数 0 之外&#xff0c;这个整数不会以零开头。 示例 1&#xff1a; 输…...

「Python系列」Python标准库

文章目录 一、 os 模块&#xff1a;文件和目录操作二、 sys 模块&#xff1a;与Python解释器交互三、 datetime 模块&#xff1a;日期和时间处理四、 json 模块&#xff1a;处理JSON数据五、 re 模块&#xff1a;正则表达式六、 time模块1. 获取当前时间2. 延迟执行&#xff08…...

虚拟列表【vue】等高虚拟列表/非等高虚拟列表

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

【MySQL】如何理解索引(高频面试点)

一、前言 首先这个博客会介绍一些关于MySQL中索引的基本内容以及一些基本的语法&#xff0c;当然里面也会有些常见的面试题的解答。 二、关于索引 1、概念 索引是一种能够帮助MySQL高效的去磁盘检索数据的一种数据结构。在MySQL的Innodb存储引擎中呢&#xff0c;采用的是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…...

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...

java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别

UnsatisfiedLinkError 在对接硬件设备中&#xff0c;我们会遇到使用 java 调用 dll文件 的情况&#xff0c;此时大概率出现UnsatisfiedLinkError链接错误&#xff0c;原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用&#xff0c;结果 dll 未实现 JNI 协…...

Opencv中的addweighted函数

一.addweighted函数作用 addweighted&#xff08;&#xff09;是OpenCV库中用于图像处理的函数&#xff0c;主要功能是将两个输入图像&#xff08;尺寸和类型相同&#xff09;按照指定的权重进行加权叠加&#xff08;图像融合&#xff09;&#xff0c;并添加一个标量值&#x…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个生活电费的缴纳和查询小程序

一、项目初始化与配置 1. 创建项目 ohpm init harmony/utility-payment-app 2. 配置权限 // module.json5 {"requestPermissions": [{"name": "ohos.permission.INTERNET"},{"name": "ohos.permission.GET_NETWORK_INFO"…...

C++使用 new 来创建动态数组

问题&#xff1a; 不能使用变量定义数组大小 原因&#xff1a; 这是因为数组在内存中是连续存储的&#xff0c;编译器需要在编译阶段就确定数组的大小&#xff0c;以便正确地分配内存空间。如果允许使用变量来定义数组的大小&#xff0c;那么编译器就无法在编译时确定数组的大…...

Java + Spring Boot + Mybatis 实现批量插入

在 Java 中使用 Spring Boot 和 MyBatis 实现批量插入可以通过以下步骤完成。这里提供两种常用方法&#xff1a;使用 MyBatis 的 <foreach> 标签和批处理模式&#xff08;ExecutorType.BATCH&#xff09;。 方法一&#xff1a;使用 XML 的 <foreach> 标签&#xff…...

LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》

这段 Python 代码是一个完整的 知识库数据库操作模块&#xff0c;用于对本地知识库系统中的知识库进行增删改查&#xff08;CRUD&#xff09;操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 &#x1f4d8; 一、整体功能概述 该模块…...

PHP 8.5 即将发布:管道操作符、强力调试

前不久&#xff0c;PHP宣布了即将在 2025 年 11 月 20 日 正式发布的 PHP 8.5&#xff01;作为 PHP 语言的又一次重要迭代&#xff0c;PHP 8.5 承诺带来一系列旨在提升代码可读性、健壮性以及开发者效率的改进。而更令人兴奋的是&#xff0c;借助强大的本地开发环境 ServBay&am…...

【从零开始学习JVM | 第四篇】类加载器和双亲委派机制(高频面试题)

前言&#xff1a; 双亲委派机制对于面试这块来说非常重要&#xff0c;在实际开发中也是经常遇见需要打破双亲委派的需求&#xff0c;今天我们一起来探索一下什么是双亲委派机制&#xff0c;在此之前我们先介绍一下类的加载器。 目录 ​编辑 前言&#xff1a; 类加载器 1. …...

微服务通信安全:深入解析mTLS的原理与实践

&#x1f525;「炎码工坊」技术弹药已装填&#xff01; 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、引言&#xff1a;微服务时代的通信安全挑战 随着云原生和微服务架构的普及&#xff0c;服务间的通信安全成为系统设计的核心议题。传统的单体架构中&…...