GitLab代码库提交量统计工具

news2024/9/21 20:46:45
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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1464602.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

软考29-上午题-【数据结构】-排序

一、排序的基本概念 1-1、稳定性 稳定性指的是相同的数据所在的位置经过排序后是否发生变化。若是排序后&#xff0c;次序不变&#xff0c;则是稳定的。 1-2、归位 每一趟排序能确定一个元素的最终位置。 1-3、内部排序 排序记录全部存放在内存中进行排序的过程。 1-4、外部…

TF-A之供应链威胁模型分析

目录 一、简介 二、TF-A 概述 2.1、TF-A 存储库 2.2、外部依赖 2.3、附加二进制文件 2.4、TF-A工具链 2.5、基础设施 三、TF-A数据流 四、攻击树 五、威胁评估与缓解 5.1、影响和可能性评级 5.2、威胁和缓解措施 六、附录 一、简介 软件供应链攻击旨在向软件产品…

《深入浅出 Spring Boot 3.x》预计3月份发版

各位&#xff0c;目前本来新书《深入浅出 Spring Boot 3.x》已经到了最后编辑排版阶段&#xff0c;即将在3月份发布。 目录&#xff1a; 现在把目录截取给大家&#xff1a; 主要内容&#xff1a; 本书内容安排如下。 ● 第 1 章和第 2 章讲解 Spring Boot 和传统 Spri…

IT资讯——全速推进“AI+鸿蒙”战略布局!

文章目录 每日一句正能量前言坚持长期研发投入全速推进“AI鸿蒙”战略 人才战略新章落地持续加码核心技术生态建设 后记 每日一句正能量 人总要咽下一些委屈&#xff0c;然后一字不提的擦干眼泪往前走&#xff0c;没有人能像白纸一样没有故事&#xff0c;成长的代价就是失去原来…

【东京都立大学主办多重会议奖项】第六届计算机通信与互联网国际会议

ICCCI 2024 - Hosted by Tokyo Metropolitan University, Japanhttps://www.iccci.org/ 会议简介 第六届计算机通信与互联网国际会议将于2024年6月14-16日在日本东京都立大学举行。ICCCI 2024由东京都立大学主办&#xff0c;华中师范大学和美国科学工程学会联合赞助、并得到了…

Curfew e-Pass 管理系统存在Sql注入漏洞 附源代码

免责声明&#xff1a;本文所涉及的信息安全技术知识仅供参考和学习之用&#xff0c;并不构成任何明示或暗示的保证。读者在使用本文提供的信息时&#xff0c;应自行判断其适用性&#xff0c;并承担由此产生的一切风险和责任。本文作者对于读者基于本文内容所做出的任何行为或决…

使用备份工具xtrabackup进行增量备份详细讲解

增量备份 第一次修改数据 mysql> insert into tb_user values (4,sxx,0); Query OK, 1 row affected (0.01 sec)mysql> select * from tb_user; ------------------- | id | name | sex | ------------------- | 1 | Tom | 1 | | 2 | Trigger | 0 | | …

深入学习TS的高阶语法(泛型、类型检测、内置工具)

文章目录 概要一.TS的类型检测1.鸭子类型2.严格的字面量类型检测 二.TS的泛型1.基本使用2.传递多个参数3.泛型接口4.泛型类5.泛型约束6.映射类型&#xff08;了解&#xff09; 三.TS的知识扩展1.模块的使用-- 内置类型导入 2.类型的查找3.第三方库的类型导入4.declare 声明文件…

深度学习中的样本分类:如何区分正样本、负样本、困难样本和简单样本?

深度学习中的样本分类&#xff1a;如何区分正样本、负样本、困难样本和简单样本&#xff1f; &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入…

思维导图教你工作日报怎么写?

工作日报撰写秘诀&#xff1a;轻松提升效率&#xff0c;让你的每一天都闪闪发光 在快节奏的现代工作中&#xff0c;如何高效地规划和管理工作任务成为了职场人士必须面对的挑战。思维导图作为一种强大的思维工具&#xff0c;正逐渐受到越来越多人的青睐。本文将探讨为什么使用…

MySQL 索引原理以及 SQL 优化

索引 索引&#xff1a;一种有序的存储结构&#xff0c;按照单个或者多个列的值进行排序。索引的目的&#xff1a;提升搜索效率。索引分类&#xff1a; 数据结构 B 树索引&#xff08;映射的是磁盘数据&#xff09;hash 索引&#xff08;快速锁定内存数据&#xff09;全文索引 …

opencv的图像上下、左右和对角线翻转—flip函数

在OpenCV中&#xff0c;flip函数用于翻转图像。你可以沿x轴、y轴或两者同时翻转图像。这个函数非常直接&#xff0c;可以用于创建镜像图像或旋转图像。 void flip(InputArray src, OutputArray dst, int flipCode);src&#xff1a;输入图像。 dst&#xff1a;翻转后的输出图像…

安卓系统和iOS系统的手机备忘录同步数据方法

在这个智能手机时代&#xff0c;安卓与iOS系统犹如两位王者&#xff0c;各自拥有庞大的用户群体。有人钟情于安卓的开放与多样&#xff0c;有人偏爱iOS的流畅与稳定。甚至&#xff0c;有些人为了满足不同需求&#xff0c;同时使用着两个系统的手机。我就是其中的一员。 工作中…

ELK 简介安装

1、概念介绍 日志介绍 日志就是程序产生的&#xff0c;遵循一定格式&#xff08;通常包含时间戳&#xff09;的文本数据。 通常日志由服务器生成&#xff0c;输出到不同的文件中&#xff0c;一般会有系统日志、 应用日志、安全日志。这些日志分散地存储在不同的机器上。 日志…

RAW 编程接口 TCP 简介

一、LWIP 中 中 RAW API 编程接口中与 TCP 相关的函数 二、LWIP TCP RAW API 函数 三、LwIP_Periodic_Handle函数 LwIP_Periodic_Handle 函数是一个必须被无限循环调用的 LwIP支持函数&#xff0c;一般在 main函数的无限循环中调用&#xff0c;主要功能是为 LwIP各个模块提供…

LeetCode | 整数反转 C语言

Problem: 7. 整数反转 文章目录 思路解题方法Code结果 思路 运算部分 while(x > 0) {y x % 10;y * 10;x / 10; } y / 10;对于大于32位的数要用long int类型的变量保存用pow算-2的31次方和2的31次方-1。 解题方法 由思路得 Code int reverse(long int x){long int y …

bat 查找文件所在

脚本 在批处理文件&#xff08;.bat&#xff09;中查找文件所在的目录&#xff0c;你可以使用dir命令结合循环和条件语句来实现。以下是一个简单的示例&#xff0c;演示如何在批处理文件中查找指定文件并输出其所在目录&#xff1a; echo off setlocal enabledelayedexpansio…

预训练大模型LLM的PEFT之——LORA

简介 LORA: LOW-RANK ADAPTATION OF LARGE LANGUAGE MODELS&#xff0c;直接翻译过来就是大模型的低秩适配 2021年微软提出的LoRA&#xff0c;它的灵感来自于 Li和 Aghajanyan等人的一些关于内在维度&#xff08;intrinsic dimension&#xff09;的发现&#xff1a;模型是过参…

[HTML]Web前端开发技术27(HTML5、CSS3、JavaScript )JavaScript基础——喵喵画网页

希望你开心&#xff0c;希望你健康&#xff0c;希望你幸福&#xff0c;希望你点赞&#xff01; 最后的最后&#xff0c;关注喵&#xff0c;关注喵&#xff0c;关注喵&#xff0c;佬佬会看到更多有趣的博客哦&#xff01;&#xff01;&#xff01; 喵喵喵&#xff0c;你对我真的…

大型语言模型的语义搜索(一):关键词搜索

关键词搜索(Keyword Search)是文本搜索种一种常用的技术&#xff0c;很多知名的应用app比如Spotify、YouTube 或 Google map等都会使用关键词搜索的算法来实现用户的搜索任务&#xff0c;关键词搜索是构建搜索系统最常用的方法&#xff0c;最常用的搜索算法是Okapi BM25&#x…