【JGit 】一个完整的使用案例

news2024/11/17 23:45:05

需求

生成一系列结构相同的项目代码,将这些项目的代码推送至一个指定的 Git 仓库,每个项目独占一个分支。

推送时若仓库不存在,则自动创建仓库。

分析

生成代码使用 Java 程序模拟,每个项目中模拟三个文件。Project.cppProject.hREADME

使用 JGit 实现代码版本管理与推送。

代码实现

以下代码包含了代码生成,Git 仓库初始化、代码克隆、分支检出、代码修改、暂存、提交及推送等操作。

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.apache.commons.lang.StringUtils;
import org.eclipse.jgit.api.CheckoutCommand;
import org.eclipse.jgit.api.CreateBranchCommand;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.ListBranchCommand;
import org.eclipse.jgit.api.PushCommand;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.transport.CredentialsProvider;
import org.eclipse.jgit.transport.PushResult;
import org.eclipse.jgit.transport.URIish;
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;

import cn.hutool.core.io.FileUtil;

public class JgitTest3 {

    private static String DEFAULE_REMOTE_NAME = "origin";
    private static String DEFAULE_BRANCH_NAME = "master";

    private static String gitUrl = "http://192.168.181.1:3000/root/a-test.git";

    private static String username = "root";
    private static String password = "123456";

    /**
     * 创建认证信息
     * 
     * @return
     */
    public static CredentialsProvider getCredentialsProvider() {
        return new UsernamePasswordCredentialsProvider(username, password);
    }

    public static void main(String[] args) throws Exception {

        List<String> branches = Stream.of("branchA", "branchB").collect(Collectors.toList());

        for (String branchName : branches) {

            System.out.println("\r\n ==========================<<" + branchName + ">>==========================\r\n");
            // 生成代码
            File codeDir = genCode(branchName);
            System.out.println(" >>>>>>>>>>>>>>> CodeDir: " + codeDir);

            // 判断仓库是否存在
            boolean remoteRepositoryExist = remoteRepositoryExist(gitUrl, username, password);
            if (remoteRepositoryExist) {
                pushOnRepoExist(branchName, codeDir);
            } else {
                pushOnRepoNotExist(branchName, codeDir);
            }

            // 清理生成的文件
            FileUtil.del(codeDir);
        }
    }

    private static void pushOnRepoNotExist(String branchName, File codeDir) throws Exception {
        // 将源码库初始化为Git仓库
        Git git = Git.init().setDirectory(codeDir).call();

        // 添加远程仓库
        git.remoteAdd()
                .setName(DEFAULE_REMOTE_NAME)
                .setUri(new URIish(gitUrl))
                .call();

        // 初始化提交
        git.add().addFilepattern(".").call();
        git.commit().setMessage("Initial commit").call();

        // 切换分支
        checkoutBranch(git, branchName);

        // 提交推送
        pushToRepo(git, branchName);

        // 关闭资源
        git.close();
    }

    private static void pushOnRepoExist(String branchName, File codeDir) throws Exception {

        // 创建临时工作目录
        File localDir = Files.createTempDirectory("Jgit-work-dir-").toFile();
        System.out.println("\r\n >>>>>>>>>>>>>>> Work dir is: " + localDir + "\r\n");

        // 克隆到本地
        Git git = Git.cloneRepository()
                .setDirectory(localDir)
                .setURI(gitUrl)
                .setCredentialsProvider(getCredentialsProvider())
                .call();

        // 切换到分支
        checkoutBranch(git, branchName);

        // 更新代码
        deleteContent(localDir.toPath(), ".git");
        // git.add().addFilepattern(".").setUpdate(true).call();
        // git.commit().setMessage("rm origin files").call();

        FileUtil.copyContent(codeDir, localDir, true);

        // 提交、推送
        pushToRepo(git, branchName);

        // 关闭资源
        git.close();

        FileUtil.del(localDir);
    }

    /**
     * 推送到远端分支
     * 
     * @param git
     * @param branchName 远端分支
     * @throws Exception
     */
    private static Iterable<PushResult> pushToRepo(Git git, String branchName) throws Exception {
        // 加入暂存区
        git.add().addFilepattern(".").call();
        git.add().addFilepattern(".").setUpdate(true).call();
        // 提交
        git.commit().setMessage(" Commit at : " + LocalDateTime.now()).call();

        // 构建推送命令
        PushCommand pushCmd = git.push()
                .setRemote(DEFAULE_REMOTE_NAME)
                .setCredentialsProvider(getCredentialsProvider());

        Ref remoteBranchRef = git.getRepository().findRef("refs/remotes/" + DEFAULE_REMOTE_NAME + "/" + branchName);
        if (Objects.isNull(remoteBranchRef)) {
            pushCmd.add(branchName);
        } else {
            pushCmd.setForce(true);
        }

        // 推送
        return pushCmd.call();
    }

    /**
     * 切换分支
     * <p>
     * <ul>
     * <li>先判断本地分支是否存在,存在则直接切换,不存在则下一步。</li>
     * <li>判断远程分支是否存在,存在则直接切换,不存在则在切换时创建分支。</li>
     * </ul>
     * 
     * </p>
     * 
     * @param git
     * @param branchName
     */
    private static void checkoutBranch(Git git, String branchName) throws Exception {

        CheckoutCommand checkoutCmd = git.checkout().setName(branchName);

        Repository repository = git.getRepository();
        Ref branchRef = repository.findRef("refs/heads/" + branchName);
        if (Objects.isNull(branchRef)) {
            Ref remoteBranchRef = repository.findRef("refs/remotes/" + DEFAULE_REMOTE_NAME + "/" + branchName);
            if (Objects.isNull(remoteBranchRef)) {
                CreateBranchCommand createBranchCmd = git.branchCreate().setName(branchName);
                // 先切换到已有分支,以获取提交记录,设置提交点辅助创建新的分支
                List<Ref> branches = git.branchList()
                        .setListMode(ListBranchCommand.ListMode.REMOTE)
                        .call();
                if (Objects.nonNull(branches) && branches.size() > 0) {
                    Ref remoteRef = branches.get(0);
                    String bName = Repository.shortenRefName(remoteRef.getName());
                    git.checkout().setName(bName).call();
                    RevCommit latestCommit = git.log().setMaxCount(1).call().iterator().next();
                    if (Objects.nonNull(latestCommit)) {
                        createBranchCmd.setStartPoint(latestCommit);
                    }
                }

                createBranchCmd.call();
            } else {
                checkoutCmd.setCreateBranch(true);
            }
        }

        checkoutCmd.call();
    }

    /**
     * 删除目录中的所有文件
     * <ul>
     * <li>文件</li>
     * <li>文件夹</li>
     * <li>子文件</li>
     * <li>子文件夹</li>
     * </ul>
     * 
     * @param folderPath  目标文件夹
     * @param ignoreFiles 忽略的文件或文件夹
     */
    public static void deleteContent(Path folderPath, String... ignoreFiles) {
        try {
            Files.walk(folderPath)
                    .filter(path -> !path.equals(folderPath))
                    .filter(path -> {
                        if (Objects.isNull(ignoreFiles) || ignoreFiles.length == 0) {
                            return false;
                        }
                        for (String ig : ignoreFiles) {
                            if (StringUtils.contains(path.toAbsolutePath().toString(), ig)) {
                                return false;
                            }
                        }
                        return true;
                    })
                    .forEach(path -> {
                        try {
                            if (path.toFile().isDirectory()) {
                                deleteContent(path, ignoreFiles);
                            } else {
                                Files.deleteIfExists(path);
                            }
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    });
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 生成代码
     * 
     * @param branchName
     * @return
     * @throws Exception
     */
    private static File genCode(String branchName) throws Exception {

        File codeDir = Files.createTempDirectory("Jgit-source-dir-").toFile();
        String codeDirAbsPath = codeDir.getAbsolutePath();

        // 生成 README
        File addNewFile = new File((codeDirAbsPath.concat(File.separator).concat("README.md")));
        String readmeContent = "Project for " + branchName + "\r\nWrite By Code JGitTest ";
        Files.write(addNewFile.toPath(), readmeContent.getBytes());

        // 生成文件
        File cppFile = new File(codeDirAbsPath.concat(File.separator).concat(branchName.concat(".cpp")));
        String cppContent = "Cpp Code for " + branchName + "\r\n Write By Code JGitTest ";
        Files.write(cppFile.toPath(), cppContent.getBytes());

        // 生成文件
        File hFile = new File(codeDirAbsPath.concat(File.separator).concat(branchName.concat(".h")));
        String hContent = "Header code for " + branchName + "\r\nWrite By Code JGitTest ";
        Files.write(hFile.toPath(), hContent.getBytes());

        return codeDir;
    }

    /**
     * 判断远程仓库是不是存在
     *
     * @param remoteUrl 远程仓库地址
     * @return true(存在)/false(不存在)
     */
    public static boolean remoteRepositoryExist(String remoteUrl, String username, String password) {
        try {
            Collection<Ref> refs = (Collection<Ref>) Git.lsRemoteRepository()
                    .setHeads(true)
                    .setTags(true)
                    .setCredentialsProvider(getCredentialsProvider(username, password))
                    .setRemote(remoteUrl)
                    .call();
            if (refs.isEmpty()) {
                return false;
            } else {
                return true;
            }
        } catch (Exception e) {
            log.warn("仓库{}不存在", remoteUrl);
            return false;
        }
    }

}

成果展示

在这里插入图片描述

1、JGit 版本

        <!-- https://mvnrepository.com/artifact/org.eclipse.jgit/org.eclipse.jgit -->
        <dependency>
            <groupId>org.eclipse.jgit</groupId>
            <artifactId>org.eclipse.jgit</artifactId>
            <version>5.1.3.201810200350-r</version>
        </dependency>

2、Gitea 安装

  • Win 下 Docker 安装 Gitea 实践:windows docker desktop部署gitea

3、JGit 资料

  • 【JGit】简述及学习资料整理
  • 【Gitea】Java 使用 JGit 创建 Git 代码仓库
  • 【Git】 删除远程分支
  • 【Gitea】配置 Push To Create

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

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

相关文章

总结 HashTable, HashMap, ConcurrentHashMap 之间的区别

1.多线程环境使用哈希表 HashMap 不行,线程不安全 更靠谱的,Hashtable,在关键方法上加了synchronized 后来标准库又引入了一个更好的解决方案;ConcurrentHashMap 2.HashMap 首先HashMap本身线程不安全其次HashMap的key值可以为空&#xff08;当key为空时&#xff0c;哈希会…

FNM和SFNM的区别

看图说话。 级联模式下&#xff0c;FNM模式&#xff0c;从片的中断都是同一个级别&#xff0c;因此从片如果有多个中断发生&#xff0c;中断之间不会抢占&#xff0c;只能按顺序处理。 级连模式下&#xff0c;SFNM模式&#xff0c;从片中断有优先级的区别&#xff0c;高优先级…

Qt外部调用进程类QProcess的使用

有的时候我们需要在自己程序运行过程中调用其他进程&#xff0c;那么就需要用到QProcess。 首先可以了解一些关于进程的相关知识&#xff1a;线程与进程&#xff0c;你真得理解了吗_进程和线程的区别-CSDN博客 进程是计算机中的程序关于某数据集合上的一次运行活动&#xff0…

7.1.1 selenium介绍及安装chromedriver

目录 1. Selenium的用途 2. 安装Selenium库 3. 安装chromedriver 1. 查看谷歌版本号​编辑 2. 找到最新版本及下载 3. 配置环境变量 4. 检测是否配置成功 5. 用python初始化浏览器对象检测&#xff1a; 6. 参考链接 1. Selenium的用途 在前面我们提到&#xff1a;在我…

NIO核心三:Selector

一、基本概念 选择器提供一种选择执行已经就绪的任务的能力。selector选择器可以让单线程处理多个通道。如果程序打开了多个连接通道&#xff0c;每个连接的流量都比较低&#xff0c;可以使用Selector对通道进行管理。 二、如何创建选择器 1.创建Selector Selector select…

ArduinoTFTLCD应用

ArduinoTFTLCD应用 ArduinoTFTLCD应用硬件连接软件导入库显示数字、字符显示汉字方案1方案2 显示图片 总结 ArduinoTFTLCD应用 对于手工喜欢DIY的人来说&#xff0c;Arduino驱动的TFTLCD被很多人使用&#xff0c;此处就总结一下&#xff0c;使用的是VScode的PlatformIO插件驱动…

Docusaurus框架——react+antd+echarts自定义mdx生成图表代码解释文档

文章目录 ⭐前言⭐Docusaurus框架渲染mdx内容&#x1f496; 创建一个mdx文件&#x1f496; 创建一个react jsx文件&#x1f496; mdx引入react的组件并渲染&#x1f496; mdx引入react的组件源代码内容 ⭐渲染一个echarts地图的代码解释文档&#x1f496; echarts 渲染地图&…

USLE模型-P因子的计算

首先需要下载土地利用类型数据集&#xff0c;查看我的相关文章 对于已有的10种土地类型代码&#xff0c;需要按水土保持措施P值表进行重分类。 10是耕地&#xff0c;且庆阳市坡度10-15度左右&#xff0c;所以赋给了3&#xff08;最好再下个DEM计算一下&#xff0c;这里就统一用…

WebServer -- 注册登录

目录 &#x1f349;整体内容 &#x1f33c;流程图 &#x1f382;载入数据库表 提取用户名和密码 &#x1f6a9;同步线程登录注册 补充解释 代码 &#x1f618;页面跳转 补充解释 代码 &#x1f349;整体内容 概述 TinyWebServer 中&#xff0c;使用数据库连接池实现…

C++指针(三)

个人主页:PingdiGuo_guo 收录专栏&#xff1a;C干货专栏 文章目录 前言 1.字符指针 1.1字符指针的概念 1.2字符指针的用处 1.3字符指针的操作 1.3.1定义 1.3.2初始化 1.4字符指针使用注意事项 2.数组参数&#xff0c;指针参数 2.1数组参数 2.1.1数组参数的概念 2.1…

NCT 全国青少年编程图形化编程(Scratch)等级考试(一级)模拟测试H

202312 青少年软件编程等级考试Scratch一级真题 第 1 题 【 单选题 】 以下说法合理的是( ) A :随意点开不明来源的邮件 B :把密码设置成 abc123 C :在虚拟社区上可以辱骂他人 D :在改编他人的作品前&#xff0c; 先征得他人同意 正确答案&#xff1a; D 试题解析&…

python模块和包概念与使用

python模块和包概念与使用 Python模块与包的关键概念 在Python编程中&#xff0c;模块和包是代码组织和管理的基石。以下是关于Python模块与包的核心要点&#xff1a; 模块&#xff1a; 模块是一个包含Python代码的.py文件&#xff0c;它可以定义函数、类、变量等。通过导入模…

水经微图Web版1.6.0发布

让每一个人都有自己的地图&#xff01; 水经微图&#xff08;简称“微图”&#xff09;新版已上线&#xff0c;在该版本中主要新增了点线面图层分组样式设置、图层排序并按序绘制、KML支持矢量符号的存储、KML支持态势标绘要素存储和新增历史地图文本样式等。 现在&#xff0…

Leetcoder Day27| 贪心算法part01

语言&#xff1a;Java/Go 理论 贪心的本质是选择每一阶段的局部最优&#xff0c;从而达到全局最优。 什么时候用贪心&#xff1f;可以用局部最优退出全局最优&#xff0c;并且想不到反例到情况 贪心的一般解题步骤 将问题分解为若干个子问题找出适合的贪心策略求解每一个子…

使用plasmo框架开发浏览器插件,注入contents脚本和给页面添加UI组件

plasmo&#xff1a;GitHub - PlasmoHQ/plasmo: &#x1f9e9; The Browser Extension Framework plasmo是一个开发浏览器插件的框架&#xff0c;支持使用react和vue等技术&#xff0c;而且不用手动管理manifest.json文件&#xff0c;框架会根据你在框架中的使用&#xff0c;自…

美团分布式 ID 框架 Leaf 介绍和使用

一、Leaf 在当今日益数字化的世界里&#xff0c;软件系统的开发已经成为了几乎所有行业的核心。然而&#xff0c;随着应用程序的规模不断扩大&#xff0c;以及对性能和可扩展性的需求不断增加&#xff0c;传统的软件架构和设计模式也在不断地面临挑战。其中一个主要挑战就是如…

SAP EC-CS如何实现自动抵消

SAP EC-CS 是SAP 比较早的合并方案&#xff0c;尽管后面有很多其他的方案作为替代&#xff0c;但 EC-CS 因为其成熟性&#xff0c;在集团合并单元不多的情况下&#xff0c;也可以作为一个不错的合并解决方案。可以说&#xff0c;会计报表合并一个核心就是实现抵消的处理&#x…

nginx------------缓存功能 ip透传 负载均衡 (六)

一、http 协议反向代理 &#xff08;一&#xff09;反向代理示例:缓存功能 缓存功能可以加速访问&#xff0c;如果没有缓存关闭后端服务器后&#xff0c;图片将无法访问&#xff0c;缓存功能默认关闭&#xff0c;需要开启。 ​ proxy_cache zone_name | off; 默认off #指明调…

2024全新手机软件下载应用排行、平台和最新发布网站,采用响应式织梦模板

这是一款简洁蓝色的手机软件下载应用排行、平台和最新发布网站&#xff0c;采用响应式织梦模板。 主要包括主页、APP列表页、APP详情介绍页、新闻资讯列表、新闻详情页、关于我们等模块页面。 地 址 &#xff1a; runruncode.com/php/19703.html 软件程序演示图&#xff1a;…

C语言中的字符魔法:大小写转换的艺术

引言 在C语言的世界里&#xff0c;字符处理是一项基础且重要的任务。字符作为编程中最基本的元素之一&#xff0c;承担着信息展示、数据交互等多重角色。特别是在处理文本信息时&#xff0c;字符的转换和识别显得尤为重要。大小写字母的转换就是其中一个常见的需求&#xff0c…