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
相关推荐
XiaoLeisj1 小时前
【JavaEE初阶 — 多线程】单例模式 & 指令重排序问题
java·开发语言·java-ee
paopaokaka_luck1 小时前
【360】基于springboot的志愿服务管理系统
java·spring boot·后端·spring·毕业设计
dayouziei1 小时前
java的类加载机制的学习
java·学习
Yaml43 小时前
Spring Boot 与 Vue 共筑二手书籍交易卓越平台
java·spring boot·后端·mysql·spring·vue·二手书籍
小小小妮子~3 小时前
Spring Boot详解:从入门到精通
java·spring boot·后端
hong1616883 小时前
Spring Boot中实现多数据源连接和切换的方案
java·spring boot·后端
aloha_7893 小时前
从零记录搭建一个干净的mybatis环境
java·笔记·spring·spring cloud·maven·mybatis·springboot
记录成长java4 小时前
ServletContext,Cookie,HttpSession的使用
java·开发语言·servlet
睡觉谁叫~~~4 小时前
一文解秘Rust如何与Java互操作
java·开发语言·后端·rust
程序媛小果4 小时前
基于java+SpringBoot+Vue的旅游管理系统设计与实现
java·vue.js·spring boot