JGIT基本CURD操作,获取提交记录,文件内容比对,获取暂存区文件,指定文件等操作
package utils;import org.eclipse.jgit.api.*;import org.eclipse.jgit.api.errors.GitAPIException;import org.eclipse.jgit.diff.DiffEntry;import org.eclipse.jgit.diff.DiffFormatter;import org.eclipse.jgit.diff.RenameDetector;import org.eclipse.jgit.lib.*;import org.eclipse.jgit.revwalk.RevCommit;import org.eclipse.jgit.revwalk.RevTree;import org.eclipse.jgit.revwalk.RevWalk;import org.eclipse.jgit.transport.URIish;import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;import org.eclipse.jgit.treewalk.AbstractTreeIterator;import org.eclipse.jgit.treewalk.CanonicalTreeParser;import org.eclipse.jgit.treewalk.TreeWalk;import org.eclipse.jgit.treewalk.filter.PathFilterGroup;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import java.io.ByteArrayOutputStream;import java.io.File;import java.io.IOException;import java.net.URISyntaxException;import java.text.DateFormat;import java.text.SimpleDateFormat;import java.util.*;import java.util.regex.Matcher;import java.util.regex.Pattern;/** *@author Mr.Ye *@ClassName GitUtils *@Date 2022/3/21 *@describe http://wiki.eclipse.org/JGit/User_Guide (eclipse-JGIT社区) *@describe https://github.com/centic9/jgit-cookbook (开源api) *@describe https://www.136.la/tech/show-875175.html (刚开始可以看这个,参考GitUtils.test()方法) * @describe 使用工具类以后需要主动调用GIT.close()方法,关闭git */public class GitUtils { private static final DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); private static final Logger logger = LoggerFactory.getLogger(GitUtils.class); private final static String REF_HEADS = "refs/heads/"; /** 本地远程仓库--用于文件比较 默认路径不对外展示*/ private static String localRemotePath = "/local-remote/"; /** * 初始化url * 默认远程仓库的项目名称为最后一个路径 */ public static HashMap<String, String> initUrl(String remotePath,String localPath){ String path =remotePath.split("\\.git")[0]; String[] split= path.split("/"); String ss = split[split.length-1]; //本地地址 localPath = localPath+ss; //本地远程地址 用于比较 localRemotePath = localRemotePath+ss; HashMap<String, String> map = new HashMap<>(); map.put("localPath",localPath); map.put("remotePath",remotePath); map.put("localRemotePath",localRemotePath); return map; } /** * 克隆远程仓库 * @param localPath 本地地址 -(本地地址不存在,使用这个方法) * @param remotePath 远程仓库 * @param provider 秘钥 * @param masterBranch 分支 * @return */ public static Git cloneRepository(String localPath, String remotePath, UsernamePasswordCredentialsProvider provider, String masterBranch) throws GitAPIException { Git git = Git.cloneRepository() .setCredentialsProvider(provider) .setURI(remotePath) .setRemote(masterBranch) .setDirectory(new File(localPath)) .call(); return git; } /** * 本地地址存在(使用这个方法) * @return */ public static Git initGit(String localPath) { File file = new File(localPath); logger.info("本地文件路径"+localPath); Git git = null; /*路径是否存在*/ if(file.exists()) { try { git = Git.open(new File(localPath)); } catch (IOException e) { e.printStackTrace(); git.close(); } } return git; } /** * 初始化 将本地代码关联远程仓库,并推送数据至远程仓库(每个仓库只需要使用一次) * @return */ public static Git onlyOnceGitInit(String localPath,String remotePath, UsernamePasswordCredentialsProvider provider,String banceName) throws GitAPIException, URISyntaxException { /*初始化*/ Git git = Git.init().setDirectory(new File(localPath)).call(); /*关联远程仓库*/ git.remoteAdd().setName(banceName).setUri(new URIish(remotePath)).call(); /*本地暂存*/ git.add().addFilepattern(".").call(); /*本地提交*/ git.commit().setAll(true).setMessage("Initialize commit").call(); /*拉取最新代码*/ git.pull().setRemote(banceName).call(); /*推送到远程仓库*/ git.push().setRemote(banceName).setCredentialsProvider(provider).call(); return git; } /** * 全部推送,返回提交的id * --url 为空则默认全部推送 * --不为空则可以选择地址推送 * @throws GitAPIException * @param git git * @param message 提交信息 */ public static String push(Git git,String message,List<String> url,UsernamePasswordCredentialsProvider provider,String banceName) throws GitAPIException{ /*路径为空则暂存所有文件*/ AddCommand addCmd = git.add(); if (url==null
url.size()<=0){ addCmd.addFilepattern("."); }else { for (String s : url) { addCmd.addFilepattern(s); } } addCmd.call(); /*提交到本地仓库*/ RevCommit revCommit = git.commit().setAll(true).setMessage(message).call(); /*推送到远程仓库*/ git.push().setRemote(banceName).setCredentialsProvider(provider).call(); logger.info("------ succeed ---------"); return revCommit.getName(); } /** * 拉取最新的代码 * @param git * @return */ public static boolean pullNew(Git git,UsernamePasswordCredentialsProvider provider,String branchname){ try { git.pull().setRemote(branchname).setCredentialsProvider(provider).call(); } catch (GitAPIException e) { e.printStackTrace(); return false; } return true; } /** * 放弃本地更改, * --只重置工作区work以及暂存index中的文件,Respository中的数据不会被重置,未被git跟踪的数据也不会被提交 * @param git * @return */ public static boolean reset(Git git,UsernamePasswordCredentialsProvider provider){ try { /*先暂存所有文件 --这段代码会让未被跟踪的文件也加入到提交队列,当执行丢弃操作时,当前所有更改都会被丢弃*/ git.add().addFilepattern(".").call(); git.reset().setMode(ResetCommand.ResetType.HARD).call(); } catch (GitAPIException e) { e.printStackTrace(); return false; } return true; } /** * git 认证对象 * @param username * @param password * @return */ public static UsernamePasswordCredentialsProvider provider(String username,String password){ return new UsernamePasswordCredentialsProvider(username,password); } /** * 根据branch 获取ref信息 * @param branchName * @return */ public static Ref getBranchRef(Git git,String branchName) { Ref result = null; try { result = git.getRepository().exactRef(REF_HEADS + branchName); } catch (IOException e) { e.printStackTrace(); } return result; } /** * 获取指定分支的指定文件内容 * @param branchName 分支名称 * @param javaPath 文件路径 * @return java类 * @throws IOException */ public static String getBranchSpecificFileContent(Git git,String branchName, String javaPath) throws IOException { Ref branch = git.getRepository().exactRef( REF_HEADS + branchName); ObjectId objId = branch.getObjectId(); RevWalk walk = new RevWalk( git.getRepository()); RevTree tree = walk.parseTree(objId); return getFileContent(git,javaPath,tree,walk); } /** * 获取指定分支指定的指定文件内容 * @param javaPath 件路径 * @param tree git RevTree * @param walk git RevWalk * @return java类 * @throws IOException */ private static String getFileContent(Git git, String javaPath, RevTree tree, RevWalk walk) throws IOException { TreeWalk treeWalk = TreeWalk.forPath(git.getRepository(), javaPath, tree); ObjectId blobId = treeWalk.getObjectId(0); ObjectLoader loader = git.getRepository().open(blobId); byte[] bytes = loader.getBytes(); walk.dispose(); return new String(bytes); } /** * 通过CommitId获取分支树结构信息 * 此方法是为了兼容 * 被比较的分支(一般为master分支)的commitId已知的情况下,无需在获取Ref直接通过commitId获取分支树结构 * @param commitId * @return * @throws IOException */ public static AbstractTreeIterator prepareTreeParser(Git git, String commitId) throws IOException { RevWalk walk = new RevWalk(git.getRepository()); RevCommit revCommit = walk.parseCommit(git.getRepository().resolve(commitId)); RevTree tree = walk.parseTree(revCommit.getTree().getId()); CanonicalTreeParser treeParser = new CanonicalTreeParser(); ObjectReader reader = git.getRepository().newObjectReader(); treeParser.reset(reader, tree.getId()); walk.dispose(); return treeParser; } /** * 删除本地仓库还未提交的文件 */ public static void deleteFile(Git git, List<String> urls) throws Exception { RmCommand addCommand = git.rm(); if (urls==null
urls.size()<=0){ addCommand.addFilepattern("."); }else { for (String s:urls){ addCommand.addFilepattern(s); } } addCommand.call(); } /** * 切换分支 * @param branchName 分支名称 * @throws GitAPIException GitAPIException */ public static void checkOut(Git git,String branchName) throws GitAPIException { // 切换分支 git.checkout().setCreateBranch(false).setName(branchName).call(); } /** * 更新分支代码 * @param branchName 分支名称 * @throws GitAPIException GitAPIException */ public static Ref checkOutAndPull(Git git,String branchName,UsernamePasswordCredentialsProvider provider) throws GitAPIException { // 1. 获取此分支的Ref信息 Ref branchRef = getBranchRef(git,branchName); boolean isCreateBranch = branchRef == null; /* TODO 这里可以增加判断 是否最新数据,如果是最新数据则后续两步操作可以省略*/ // 2. 切换分支 git.checkout().setCreateBranch(isCreateBranch).setName(branchName) .setStartPoint( "origin/"+branchName) .setUpstreamMode(CreateBranchCommand.SetupUpstreamMode.SET_UPSTREAM) .call(); // 3. 拉取最新代码 git.pull().setCredentialsProvider(provider).call(); branchRef = getBranchRef(git,branchName); return branchRef; } /** * 判断本地是否最新代码 * * @return boolean * @throws GitAPIException GitAPIException */ public static boolean checkBranchNewVersion(Git remoteGit, Git git, UsernamePasswordCredentialsProvider provider,String branceName) throws GitAPIException { boolean flag = false; try { String remote = LogRemote(remoteGit,provider,branceName); String log = Log(git); if (remote.equals(log)){ flag = true; } logger.info("比较版本信息,本地:{},远程:{},是否一致:{}",log,remote,flag); } catch (Exception e) { e.printStackTrace(); } return flag; } /** * 获取提交记录 * @return * @throws IOException * @throws GitAPIException * @param masterBranch 分支名称 */ public static List<CommitMessage> getCommitMessages(Git git,Integer num,String masterBranch) throws IOException, GitAPIException { List<CommitMessage> commitMessages = new ArrayList<>(); CommitMessage commitMessage =null; /** 获取最新的10条提交记录来展示*/ Iterable<RevCommit> commits ; if (StringUtils.isEmpty(masterBranch)){ /** 查所有分支记录*/ commits = git.log().setMaxCount(num).call(); }else { /** 查指定分支数据记录*/ commits = git.log().add(git.getRepository().resolve(masterBranch)).setMaxCount(num).call(); } for(RevCommit commit:commits) { commitMessage = new CommitMessage(); commitMessage.setCommitId(commit.getName()); commitMessage.setCommitIdent(commit.getAuthorIdent().getName()); commitMessage.setCommitMessage(commit.getFullMessage()); commitMessage.setCommitDate(df.format(new Date(commit.getCommitTime()*1000L))); if (commit.getParents().length!=0){ commitMessage.setLastCommitId(commit.getParent(0).getName()); commitMessage.setMergeBranchCommitId(commit.getParent(0).getName()); } commitMessage.setEmailAddress(commit.getAuthorIdent().getEmailAddress()); commitMessages.add(commitMessage); } return commitMessages; } /** * LogRemote 获取Git远程仓库版本号 * @param git * @param git * @return * @throws IOException * @throws GitAPIException */ public static String LogRemote(Git git,UsernamePasswordCredentialsProvider provider,String branceName) throws GitAPIException { String versionID = ""; pullNew(git,provider,branceName); Iterable<RevCommit> iterable = git.log().setMaxCount(1).call(); for (RevCommit u : iterable) { String ID = String.valueOf(u.getId()); //正则表达式 截取需要的部分 versionID = getMatcher("commit\\s(\\w+)\\s?", ID); break; } return versionID; } /** * getMatcher 正则表达式方法 * @param regex * @param source * @return */ public static String getMatcher(String regex, String source) { String str = ""; Pattern pattern = Pattern.compile(regex); Matcher matcher = pattern.matcher(source); while (matcher.find()) { // 只取第一组 str = matcher.group(1); } return str; } /** * 获取Git本地仓库版本号 * * @return versionID 版本号 * @throws IOException * @throws GitAPIException */ public static String Log(Git git) throws GitAPIException { String versionID = ""; Iterable<RevCommit> iterable = git.log().setMaxCount(1).call(); for (RevCommit u : iterable) { //版本id String ID = String.valueOf(u.getId()); versionID = getMatcher("commit\\s(\\w+)\\s?", ID); break; } git.close(); return versionID; } private final static String GIT = ".git"; /** * 指定文件提交到git仓库中 * @param git git * @param files 需要提交的文件列表(文件名) * @return 返回本次提交的版本号 * @throws IOException */ public static String commitToGitRepository(Git git, List<String> files,String message,String banceName, UsernamePasswordCredentialsProvider provider) throws Exception { //判断是否有被修改过的文件 List<DiffEntry>diffEntries = git.diff() .setDestinationPrefix("HEAD") .setPathFilter(PathFilterGroup.createFromStrings(files)) .call(); if (diffEntries == null
diffEntries.size() == 0) { throw new Exception("提交的文件内容都没有被修改,不能提交"); } //被修改过的文件 List<String> updateFiles=new ArrayList<String>(); DiffEntry.ChangeType changeType; for(DiffEntry entry : diffEntries){ changeType = entry.getChangeType(); switch (changeType) { case ADD: case COPY: case RENAME: updateFiles.add(entry.getNewPath()); break; case DELETE: case MODIFY: updateFiles.add(entry.getOldPath()); break; } } //将文件提交到git仓库中,并返回本次提交的版本号 AddCommand addCmd = git.add(); for (String file : updateFiles) { /*保存到 暂存区*/ addCmd.addFilepattern(file); } /*执行*/ addCmd.call(); CommitCommand commitCmd = git.commit(); for (String file : updateFiles) { commitCmd.setOnly(file); } RevCommit revCommit = commitCmd.setMessage(message).call(); //提交到远程分支 git.push().setRemote(banceName).setCredentialsProvider(provider).call(); return revCommit.getName(); } /** * 将git仓库内容回滚到指定版本的上一个版本 * @param git git * @param revision 指定的版本号 * @return true,回滚成功,否则flase * @throws IOException */ public static boolean rollBackPreRevision(Git git, String revision) throws IOException, GitAPIException { Repository repository = git.getRepository(); RevWalk walk = new RevWalk(repository); ObjectId objId = repository.resolve(revision); RevCommit revCommit = walk.parseCommit(objId); String preVision = revCommit.getParent(0).getName(); git.reset().setMode(ResetCommand.ResetType.HARD).setRef(preVision).call(); repository.close(); return true; } /** * 查询本次提交的日志 * @param git git * @param revision 版本号 * @return * @throws Exception */ public static List<DiffEntry> getLog(Git git , String revision) throws Exception { Repository repository = git.getRepository(); ObjectId objId = repository.resolve(revision); Iterable<RevCommit> allCommitsLater = git.log().add(objId).call(); Iterator<RevCommit> iter = allCommitsLater.iterator(); RevCommit commit = iter.next(); TreeWalk tw = new TreeWalk(repository); tw.addTree(commit.getTree()); commit = iter.next(); if (commit != null){ tw.addTree(commit.getTree()); } else{ return null; } tw.setRecursive(true); RenameDetector rd = new RenameDetector(repository); rd.addAll(DiffEntry.scan(tw)); return rd.compute(); } /** * 获取暂存区,以及工作区所有文件,部门文件无法获取差异内容,该方法暂不使用 */ public static List<ComparativeGit> status(Git git) throws Exception { /*TODO 问题1:新增文件,git没办法捕捉到文件索引,所以数据无法展示,会出现异常,这里解决方法是: 先将所有文件add添加索引,然后重置索引 如果比较暂存区域的数据,需要先将所有文件提交至暂存区域 建立索引*/ git.add().addFilepattern(".").call(); /*重置索引*/ git.reset().call(); List<ComparativeGit> comparativeGit= new ArrayList<>(); Status status = git.status().call();// //git add命令后会看到变化 (暂存区文件)// status.getAdded().forEach(it->{// logger.info("1");// compareInfo(git,it,comparativeGit);// });// //(暂存区文件)// status.getChanged().forEach(it->{// logger.info("2");// compareInfo(git,it,comparativeGit);// }); //git rm命令会看到变化,从暂存区删除的文件列表 status.getRemoved().forEach(it ->{ logger.info("6"); compareInfo(git,it,comparativeGit); }); //修改的文件列表 status.getModified().forEach(it -> { logger.info("7"); compareInfo(git,it,comparativeGit); }); //工作区新增的文件列表 status.getUntracked().forEach(it -> { logger.info("8"); compareInfo(git,it,comparativeGit); }); //冲突的文件列表 status.getConflicting().forEach(it -> { logger.info("9"); compareInfo(git,it,comparativeGit); }); //工作区删除的文件列表 status.getMissing().forEach(it -> { logger.info("10"); compareInfo(git,it,comparativeGit); }); System.out.println(comparativeGit); return comparativeGit; } public static void compareInfo(Git git,String url,List<ComparativeGit> comparativeGit){ /*获取指定文件内容,必须是版本库中已经存在的版本,新增文件不会打印文件内容*/ List<DiffEntry> diffEntries = null; ComparativeGit comparative = new ComparativeGit(); try { /** 获取差异文件, * git diff head 工作区域 local history比较 * git diff --cached 暂存区域与local history比较*/ /** 目前使用 git diff --cached 来比较工作区与local Res比较 */// diffEntries = git.diff().setCached(true)// .setPathFilter(PathFilterGroup.createFromStrings(url)).call(); /** 目前使用 git diff head 来比较工作区与local Res比较 */ diffEntries = git.diff() .setDestinationPrefix("HEAD") .setPathFilter(PathFilterGroup.createFromStrings(url)) .call(); System.out.println(diffEntries); ByteArrayOutputStream out = new ByteArrayOutputStream(); DiffFormatter formatter = new DiffFormatter(out); formatter.setRepository( git.getRepository() ); for( DiffEntry entry : diffEntries ) { String newPath = entry.getNewPath(); comparative.setUrl(newPath); comparative.setType(entry.getChangeType().name()); comparative.setTypeName(entry.getChangeType().toString()); formatter.format(entry); String diffText = out.toString("UTF-8"); /*截取内容*/ if (diffText!=null){ String[] split = diffText.split("@@"); diffText = split[split.length-1]; } System.out.println(diffText); comparative.setDiffText(diffText); out.reset(); comparativeGit.add(comparative); } } catch (Exception e) { e.printStackTrace(); } } /** * 测试api * @param git * @throws Exception */ public static void test(Git git) throws Exception { Status status = git.status().call(); //git add命令后会看到变化 status.getAdded().forEach(it -> System.out.println("Add File :" + it)); //git rm命令会看到变化,从暂存区删除的文件列表 status.getRemoved().forEach(it -> System.out.println("Remove File :" + it)); //修改的文件列表 status.getModified().forEach(it -> System.out.println("Modified File :" + it)); //工作区新增的文件列表 status.getUntracked().forEach(it -> System.out.println("Untracked File :" + it)); //冲突的文件列表 status.getConflicting().forEach(it -> System.out.println("Conflicting File :" + it)); //工作区删除的文件列表 status.getMissing().forEach(it -> System.out.println("Missing File :" + it)); /*获取指定文件内容,必须是版本库中已经存在的版本,新增文件不会打印文件内容*/ List<DiffEntry> diffEntries = git.diff() .setPathFilter(PathFilterGroup.createFromStrings(status.getModified())).call(); System.out.println(diffEntries); ByteArrayOutputStream out = new ByteArrayOutputStream(); DiffFormatter formatter = new DiffFormatter( System.out ); formatter.setRepository( git.getRepository() ); for( DiffEntry entry : diffEntries ) { System.out.println( "Entry: " + entry + ", from: " + entry.getOldId() + ", to: " + entry.getNewId() ); System.out.println(entry.getNewPath()); System.out.println(entry.getChangeType()); formatter.format(entry); String diffText = out.toString("UTF-8"); System.out.println(diffText); out.reset(); } } public static void main(String[] args) throws Exception { GitUtils utils = new GitUtils();// GitParser parser = new GitParser(); String local = "/local/test_03/";// String remotePath ="xxxx.git"; String remotePath ="xxx.git";// HashMap<String, String> map = initUrl(remotePath, local);// String localPath1 = map.get("localPath");// String localRemotePath = map.get("localRemotePath"); String localPath = local; UsernamePasswordCredentialsProvider provider = provider("账号", "密码");// Git localGit = utils.initUrl(localPath1); // 初始化adapter Git git = initGit(localPath); if (git==null){ git= cloneRepository(localPath, remotePath, provider, "master"); } pullNew(git,provider,"master");// Git remoteGit = utils.cloneRepository(localRemotePath,remotePath,provider,"master");// onlyOnceGitInit(localPath,remotePath,provider,"master"); status(git);// List<String> list = new ArrayList<>();// list.add("123.txt");// utils.deleteFile(localGit,list);// utils.reset(localGit,provider);// utils.reset(localGit,provider);// utils.pull(git);// Git git1 = utils.initLocalRemoteGit();// utils.gitDiff(git1,git);// boolean b = utils.checkBranchNewVersion(git1, git);// System.out.println(b);// parser.gitParser(git);// //远程的提交记录// List<CommitMessage> messages = utils.getCommitMessages(remoteGit,10,null);// System.out.println(messages);// Ref ref = utils.getBranchRef(git, MASTER_BRANCH);// System.out.println(ref); List<String> list = new ArrayList<>(); list.add("测试一下差异/1/测试.txt"); try { commitToGitRepository(git,list,"批量提交测试","master",provider); } catch (Exception e) { e.printStackTrace(); }// utils.push(git,".","本地删除测试提交",provider); }}