GitLab代码库提交量统计工具

1.说明

统计公司所有项目的提交情况,可指定分支和时间段,返回每个人的提交新增数、删除数和总数。

2.API

文档地址:http://公司gitlab域名/help/api/README.md

  • 项目列表查询

    返回示例:
json 复制代码
[
    {
        "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.代码
java 复制代码
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.输出示例:
java 复制代码
涉及项目(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
相关推荐
转世成为计算机大神3 分钟前
易考八股文之Java中的设计模式?
java·开发语言·设计模式
qq_3273427324 分钟前
Java实现离线身份证号码OCR识别
java·开发语言
阿龟在奔跑2 小时前
引用类型的局部变量线程安全问题分析——以多线程对方法局部变量List类型对象实例的add、remove操作为例
java·jvm·安全·list
飞滕人生TYF2 小时前
m个数 生成n个数的所有组合 详解
java·递归
代码小鑫2 小时前
A043-基于Spring Boot的秒杀系统设计与实现
java·开发语言·数据库·spring boot·后端·spring·毕业设计
真心喜欢你吖2 小时前
SpringBoot与MongoDB深度整合及应用案例
java·spring boot·后端·mongodb·spring
激流丶2 小时前
【Kafka 实战】Kafka 如何保证消息的顺序性?
java·后端·kafka
周全全2 小时前
Spring Boot + Vue 基于 RSA 的用户身份认证加密机制实现
java·vue.js·spring boot·安全·php
Kkooe3 小时前
GitLab|GitLab报错:Restoring PostgreSQL database gitlabhq_production...
gitlab
uzong3 小时前
一个 IDEA 老鸟的 DEBUG 私货之多线程调试
java·后端