当前位置: 首页 > 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…...

ElevenLabs语音合成效果翻倍的秘密(行业未公开的声学参数调优矩阵)

更多请点击&#xff1a; https://intelliparadigm.com 第一章&#xff1a;ElevenLabs英文语音合成效果翻倍的核心洞察 关键瓶颈在于语音上下文建模粒度 ElevenLabs 的高质量语音合成并非单纯依赖更大模型参数量&#xff0c;而是通过细粒度的语义-韵律联合编码实现自然度跃升。…...

OpenCore Legacy Patcher终极指南:让老Mac免费运行最新macOS的完整教程

OpenCore Legacy Patcher终极指南&#xff1a;让老Mac免费运行最新macOS的完整教程 【免费下载链接】OpenCore-Legacy-Patcher Experience macOS just like before 项目地址: https://gitcode.com/GitHub_Trending/op/OpenCore-Legacy-Patcher OpenCore Legacy Patcher是…...

AI Agent执行链路的安全机制:权限控制与沙箱隔离方案

AI Agent执行链路安全深度解析:权限控制与沙箱隔离全栈落地方案 摘要/引言 你有没有遇到过这些场景:刚上线的企业内部运维Agent被恶意Prompt注入后,直接调用了删除生产库的工具;你做的数据分析Agent被诱导执行了恶意Python代码,把公司的用户隐私数据传到了境外黑客服务器…...

Godot游戏集成Discord状态:RPC插件原理与实战指南

1. 项目概述&#xff1a;在Godot引擎中点亮你的Discord状态 如果你是一名独立游戏开发者&#xff0c;或者正在用Godot引擎捣鼓一些有趣的个人项目&#xff0c;你可能会想让你的朋友或社区成员知道你现在正在“玩”什么。不是通过截图发到社交媒体&#xff0c;而是更实时、更优…...

Go语言缓存雪崩:防止缓存失效

Go语言缓存雪崩&#xff1a;防止缓存失效 1. 雪崩防护 type CacheWithProtection struct {cache *RedisCachemu sync.Mutexlocks map[string]*sync.Mutex }func NewCacheWithProtection(cache *RedisCache) *CacheWithProtection {return &CacheWithProtect…...

轻量级服务器监控面板:从原理到部署实战

1. 项目概述&#xff1a;一个开源监控面板的诞生最近在折腾服务器和容器化应用&#xff0c;发现一个挺普遍的需求&#xff1a;当你手头有几台服务器&#xff0c;上面跑着几个Docker容器&#xff0c;或者一些自己写的服务&#xff0c;你总想知道它们现在“活”得怎么样。CPU是不…...

从零部署开源语音助手:OpenClaw项目实战与二次开发指南

1. 项目概述&#xff1a;从开源代码到可用的语音助手看到leilei926524-tech/openclaw-voice-assistant这个项目标题&#xff0c;我的第一反应是&#xff1a;又一个基于开源代码的语音助手项目。在GitHub上&#xff0c;类似的项目多如牛毛&#xff0c;但真正能让一个普通开发者&…...

基于Claude的AI招聘系统:从简历解析到智能评估全流程实践

1. 项目概述&#xff1a;当Claude成为你的招聘官最近在GitHub上看到一个挺有意思的项目&#xff0c;叫“hire-from-claude”。光看名字&#xff0c;你可能会觉得有点玄乎&#xff0c;难道是要让AI来面试和招聘人类&#xff1f;其实&#xff0c;这个项目的核心思路&#xff0c;是…...

像素风格技能图标自动生成:Python+Pillow实现模板化设计

1. 项目概述与核心价值最近在和一些做独立开发者和内容创作者的朋友聊天时&#xff0c;发现一个普遍痛点&#xff1a;大家手头都有不少好想法&#xff0c;但一到具体执行&#xff0c;尤其是需要制作宣传素材时&#xff0c;就卡住了。比如&#xff0c;想给自己的新App做个宣传图…...

AI代码管理器:统一多模型编程助手,提升开发效率与代码质量

1. 项目概述&#xff1a;一个面向开发者的多模型代码管理技能最近在折腾AI编程助手&#xff0c;发现一个挺有意思的现象&#xff1a;很多开发者手头可能同时用着Claude、CodeGemini这类工具&#xff0c;但每次切换都得重新配置环境、调整提示词&#xff0c;甚至要处理不同模型输…...