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
相关推荐
公贵买其鹿4 分钟前
List深拷贝后,数据还是被串改
java
xlsw_3 小时前
java全栈day20--Web后端实战(Mybatis基础2)
java·开发语言·mybatis
神仙别闹4 小时前
基于java的改良版超级玛丽小游戏
java
黄油饼卷咖喱鸡就味增汤拌孜然羊肉炒饭4 小时前
SpringBoot如何实现缓存预热?
java·spring boot·spring·缓存·程序员
暮湫5 小时前
泛型(2)
java
超爱吃士力架5 小时前
邀请逻辑
java·linux·后端
南宫生5 小时前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论
转码的小石5 小时前
12/21java基础
java
李小白665 小时前
Spring MVC(上)
java·spring·mvc
GoodStudyAndDayDayUp5 小时前
IDEA能够从mapper跳转到xml的插件
xml·java·intellij-idea