Java通过JGit操作Git的使用方法

2023-05-28 0 458

by zhimaxingzhe from Java透过Jgit操作形式Git选用形式热烈欢迎撷取镜像,转发请标明原文,认同著作权,若短萼请联络许可。 https://zhimaxingzhe.github.io

序言

Java 街道社区操作形式与GIT可视化的最合适模块应该就是Jgit了,目前没找出更快的,JGit是同时实现Git版掌控技术的纯Java库。这是一个Eclipse工程项目,起初是EGit的Git库,它提供了与Eclipse的Git软件系统。同时,JGit还有更多选用者,比如Gerrit,GitBlit,Jenkins的GitClient应用程序。上周在做命令行正式发布机能时加进了,自学了Jgit的选用,做呵呵撷取。若有助益,请全屏帕西基吧[gf]1f91d[/gf]。

Java通过JGit操作Git的使用方法

与GIT可视化怎样做?最先我想透过在伺服器上加装git应用程序,在Java标识符中执行shell命令的形式来同时实现对git的操作形式,如此一来圣索弗,标识符都写好了(见variations)。但这样与GIT可视化合作开发、增容工作效率非常大,且布署需要网络管理做非常多工作,且存有安全可靠问题,对职权的掌控明确要求严苛。而后找出Jgit,了解到Jgit 能不倚赖伺服器加装 git client 方可对 GIT 进行操作形式,所以对GIT的大部分操作形式都PCB好了API,实在太适宜我的情景了。

一、引入倚赖

现阶段新一代的版是6.4.0版,所以是JDK 11校对版,明确要求JDK 是11或其以上版才行,如果是JDK 8得找旧版的,比如说5.13版,可能有安全可靠漏洞信用风险,取决情况来选用。发稿前5.13版没看见有安全可靠漏洞信用风险,能选用。

org.eclipse.jgitorg.eclipse.jgit6.4.0.202210260700-m2org.eclipse.jgitorg.eclipse.jgit5.13.1.202206130422-r

二、此基础操作形式

1、建立库房发送到远距

邻近地区已存有产品目录调用为库房后发送到远程

cd “/Users/zhimaxingzhe/test”git initgit remote add origin https://github.com/zhimaxingzhe/test.gitgit add .git commit -m “init”git push -u origin master

//调用邻近地区库房,标记远距库房地址”https://github.com/zhimaxingzhe/test.git”Git git = Git.init().setDirectory(new File(“/Users/zhimaxingzhe/test”)).call();StoredConfig config = git.getRepository().getConfig();config.setString(“remote”,”origin”,”url”,”https://github.com/zhimaxingzhe/test.git”);config.save();git.commit().setMessage(“init”).call();//推到远距库房UsernamePasswordCredentialsProvider provider = new UsernamePasswordCredentialsProvider(“userName-zhimaxingzhe”, password);git.push().setRemote(“origin”).setCredentialsProvider(provider).setRefSpecs(new RefSpec(“master”)).call();

远距已存有的库房

git clone https://github.com/zhimaxingzhe/test.gitcd testtouch README.mdgit add README.mdgit commit -m add README.mdgit push -u origin master

对应的,透过Jgit是怎样操作形式的呢?直接上标识符

//克隆远距仓库到邻近地区Git git = Git.cloneRepository().setURI(“https://github.com/zhimaxingzhe/test.git”).setTimeout(90).setDirectory(new File(“/Users/zhimaxingzhe/test”)).setCredentialsProvider(provider).call();//建立邻近地区文件FileUtil.touch(“/Users/zhimaxingzhe/test”+”/README.md”);//添加全部文件git.add().addFilepattern(“README.md”).call();//提交git.commit().setMessage(“add README”).call();//推到远距库房UsernamePasswordCredentialsProvider provider = new UsernamePasswordCredentialsProvider(“userName-zhimaxingzhe”, password);git.push().setRemote(“origin”).setCredentialsProvider(provider).setRefSpecs(new RefSpec(“master”)).call();

2、 clone 远端库房到邻近地区

//提供用户名和密码的验证UsernamePasswordCredentialsProvider provider = new UsernamePasswordCredentialsProvider(“userName”,”password”);//判断邻近地区产品目录是否存有boolean exist = FileUtil.exist(“/Users/zhimaxingzhe/”);if (!exist){ FileUtil.mkdir(“/Users/zhimaxingzhe/”);}// clone 库房到指定产品目录Git git = Git.cloneRepository().setURI(GITURL).setDirectory(new File(“/Users/zhimaxingzhe/test”)).setCredentialsProvider(provider).call();

3、身份认证

上面的例子中都有加进用户名密码做身份验证,所以顺着讲呵呵身份认证。在做远距库房操作形式是需要身份认证,如执行pull、push、clone操作形式时。和git可视化一样,身份认证Jgit 支持两种形式,一种是HTTP(S)账号+密码的形式,一种是透过SSH协议选用公钥认证。HTTP(S)账号+密码:

//账号+ 密码UsernamePasswordCredentialsProvider provider = new UsernamePasswordCredentialsProvider(“userName-zhimaxingzhe”, password);//拉取远距更新 git.pull().setCredentialsProvider(provider).setRemoteBranchName(branchName).call();

SSH协议选用公钥认证:

SshSessionFactory sshSessionFactory = new JschConfigSessionFactory(){ @Override protected void configure(OpenSshConfig.Host host, Session session){ session.setConfig(“StrictHostKeyChecking”,”no”);} @Override protected JSch createDefaultJSch(FS fs) throws JSchException { JSch sch = super.createDefaultJSch(fs);//keyPath 私钥文件 path sch.addIdentity(keyPath); return sch;}};//打开邻近地区库房Git git = Git.open(new File(“/Users/zhimaxingzhe/test”));//执行远距拉取合并git.pull().setTransportConfigCallback( transport ->{ SshTransport sshTransport =( SshTransport )transport; sshTransport.setSshSessionFactory( sshSessionFactory );});git.call();// git pushgit.push().setRemote(“origin”).setTransportConfigCallback(transport ->{ SshTransport sshTransport =( SshTransport )transport; sshTransport.setSshSessionFactory( sshSessionFactory );}).setRefSpecs(new RefSpec(branchName)).call();

4、拉取更新 git pull

UsernamePasswordCredentialsProvider provider = new UsernamePasswordCredentialsProvider(“userName-zhimaxingzhe”, password);//拉取远距更新 git.pull().setCredentialsProvider(provider).setRemoteBranchName(branchName).call();

5、添加文件 git add

添加所有修改文件

//打开git库房 Git git = Git.open(new File(“/Users/zhimaxingzhe/test”));git.add().addFilepattern(“.”).call();

按文件名称添加文件

//打开git库房 Git git = Git.open(new File(“/Users/zhimaxingzhe/test”));git.add().addFilepattern(“README.md”).call();

如果知道要添加的文件列表,则能根据邻近地区库房文件状态按修改类型进行添加

//打开git库房 Git git = Git.open(new File(“/Users/zhimaxingzhe/test”));//判断是否有被修改过的文件 List diffEntries = git.diff().setPathFilter(PathFilterGroup.createFromStrings(files)).setShowNameAndStatusOnly(true).call(); if (diffEntries == null diffEntries.size()==0){ throw new Exception(“提交的文件内容都没有被修改,不能提交”);} //被修改过的文件 List updateFiles=new ArrayList(); ChangeType changeType; for(DiffEntry entry : diffEntries){ changeType = entry.getChangeType(); switch (changeType){ case ADD: updateFiles.add(entry.getNewPath()); break; case COPY: updateFiles.add(entry.getNewPath()); break; case DELETE: updateFiles.add(entry.getOldPath()); break; case MODIFY: updateFiles.add(entry.getOldPath()); break; case RENAME: updateFiles.add(entry.getNewPath()); break;} }//将修改类型的文件提交到git库房中,并返回本次提交的版号 AddCommand addCmd = git.add(); for (String file : updateFiles){ addCmd.addFilepattern(file);} addCmd.call(); CommitCommand commitCmd = git.commit();

6、提交修改 git commit

Status status = git.status().call();String commitId = null;if (!status.isClean()){//执行 git 提交 git.add().addFilepattern(“.”).call(); log.debug(“Git add all files success.”); CommitCommand commit = git.commit();//指定用户提交 RevCommit call = commit.setMessage(comment).setCommitter(“userName-zhimaxingzhe”, userName +”@github.com”).call(); commitId = call.getName();} else { log.debug(“Git status is clean,nothing to commit.”); Iterable call = git.log().setMaxCount(1).call(); for (RevCommit revCommit : call){ commitId = revCommit.getName();}}

7、推到远距库房 git push

git.push().setRemote(“origin”).setCredentialsProvider(provider).setRefSpecs(new RefSpec(branchName)).call();

8、切换分支 git checkout

Git git = Git.open(new File(“/Users/zhimaxingzhe/test”));UsernamePasswordCredentialsProvider provider = new UsernamePasswordCredentialsProvider(“userName-zhimaxingzhe”, password);//拉取远距更新 git.pull().setCredentialsProvider(provider).setRemoteBranchName(branchName).call();boolean existBranch = false;//列出所有的分支名称,判断分支是否存有List branchList = git.branchList().setListMode(ListBranchCommand.ListMode.ALL).call();String branchName = featureInfo.getBranchVersion();for (Ref ref : branchList){ if ((“refs/remotes/origin/”+ branchName).equals(ref.getName())){ existBranch = true;}}if (!existBranch){ log.error(“分支{}不存有,请确认”, branchName); throw new BusinessException(ExceptionEnum.BRANCHNOTEXIST.getCode(),”分支”+ branchName +”不存有,请确认”);}boolean existLocalBranch = false;List branchList = git.branchList().call();for (Ref ref : branchList){ if (ref.getName().equals(“refs/heads/”+ branchName)){ existLocalBranch = true;}}if (existLocalBranch){ //存有则切换邻近地区分支 git.checkout().setName(branchName).call();} else {//不存有则切换为远距分支 git.checkout().setCreateBranch(true).setName(branchName).setUpstreamMode(CreateBranchCommand.SetupUpstreamMode.TRACK).setStartPoint(“origin/”+ branchName).call();}

9、回滚到指定版 git reset

/*** 将git库房内容回滚到指定版,回退前要考虑版所处分支和现阶段库房分支是否一致,否则回退失败* *@param gitRoot 库房产品目录* @param commitId 指定的版号* @return true,回滚成功,否则flase *@throws IOException */ public boolean rollBackPreRevision(String gitRoot, String commitId) throws IOException {// Git同时实现了AutoCloseable,默认会关闭repository try (Git git = Git.open(new File(gitRoot))){ git.reset().setMode(ResetType.HARD).setRef(commitId).call();} catch (GitAPIException e){ log.error(“回退失败”,e); throw new BusinessException(“回退失败”);} return true;} /*** 将git库房内容回滚到指定版的上一个版,回退前要考虑版所处分支和现阶段库房分支是否一致,否则回退失败* *@param gitRoot 库房产品目录* @param commitId 指定的版号* @return true,回滚成功,否则flase *@throws IOException */ public boolean rollBackPreRevision(String gitRoot, String commitId) throws IOException {// Git同时实现了AutoCloseable,默认会关闭repository try (Git git = Git.open(new File(gitRoot))){ Repository reposito = repository.resolve(commitId); RevCommit revCommit = walk.parseCommit(objId); String preVision = revCommit.getParent(0).getName(); git.reset().setMode(ResetType.HARD).setRef(preVision).call();} catch (GitAPIException e){ log.error(“回退失败”,e); throw new BusinessException(“回退失败”);} repository.close(); return true;}

10、比较版差异 git diff

比较现阶段工作区和和最后一次提交的差异

List call = git.diff().call();

比较两个版之间的差异

// git diff HEAD HEAD^ByteArrayOutputStream outputStream = new ByteArrayOutputStream();AbstractTreeIterator newTreeIter = prepareTreeParser(git.getRepository(), git.getRepository().resolve(“HEAD”).getName());AbstractTreeIterator oldTreeIter = prepareTreeParser(git.getRepository(), git.getRepository().resolve(“HEAD^”).getName());git.diff().setNewTree(newTreeIter)//设置源,不设置则默认工作区和历史新一代commit版比较.setOldTree(oldTreeIter).setPathFilter(PathFilter.create(“README.dm”))//设置过滤.setOutputStream(outputStream)//输出流用outputStream,后面转成字符串.call();

如果只是想知道邻近地区工作区有没有可提交的东西,能选用git status,判断is clean方可。

Status status = git.status().call();if (!status.isClean()){ // do something}

查看源码能看见status().call()内部也是调用了diff.diff()来获得状态的。

11、合并分支 git merge [sourceBranchName]

Git git = Git.open(new File(“/Users/zhimaxingzhe/test”));Ref refranchName).call();//切换回被合并分支// git merge [sourceBranchName]MergeResult mergeResult = git.merge().include(refdev)//合并源头分支到目标分支.setCommit(true)//设置合并后同时提交.setFastForward(MergeCommand.FastForwardMode.NOFF)//分支合并策略,–ff代表快速合并,–no-ff代表普通合并.setMessage(“Merge sourceBranchName into targetBranchName.”)//设置提交信息.call();

三、源码自学

1、Jgit pull 源码阅读

pull 实际上是 fetch、merge两个操作形式的组合,那么在Jgit 源码中这也是执行了这两个操作形式,带着这样的预期进入形式中,看见关键步骤:

FetchCommand fetch = new FetchCommand(repo).setRemote(remote).setProgressMonitor(monitor).setTagOpt(tagOption).setRecurseSubmodules(submoduleRecurseMode);configure(fetch);fetchRes = fetch.call();

MergeCommand merge = new MergeCommand(repo);MergeResult mergeRes = merge.include(upstreamName, commitToMerge).setProgressMonitor(monitor).setStrategy(strategy).setContentMergeStrategy(contentStrategy).setFastForward(getFastForwardMode()).call();monitor.update(1);result = new PullResult(fetchRes, remote, mergeRes);

2、Jgit clone 源码阅读

Jgit 是能不倚赖伺服器加装 git client 方可对 GIT 进行操作形式的,是怎样做到的呢?带着这个疑问从 Jgit 的 clone()形式源码开始寻找答案。通常git库房的产品目录结构是这样的

Jgit也是建立了这些产品目录和文件。clone 的关键标识符步骤:1、建立各种产品目录和文件

refs.create();objectDatabase.create();FileUtils.mkdir(new File(getDirectory(),”branches”));//$NON-NLS-1$FileUtils.mkdir(new File(getDirectory(),”hooks”));//$NON-NLS-1$//……

2、执行 fetch

FetchResult result = transport.fetch(monitor,applyOptions(refSpecs), initialBranch);

3、执行 checkout()

DirCache dc = clonedRepo.lockDirCache();DirCacheCheckout co = new DirCacheCheckout(clonedRepo, dc,commit.getTree());co.setProgressMonitor(monitor);co.checkout();

3、Jgit push 源码阅读

地>,所以git pull是<远距分支>:<邻近地区分支>,而git push是<邻近地区分支>:<远距分支>。如果省略远距分支名,则表示将邻近地区分支发送与之存有”追踪关系”的远距分支(通常两者同名),如果该远程分支不存有,则会被新建。

s pushProcess = new PushProcess(this, toPush, prePush, out);return pushProcess.execute(monitor);

pushProcess.execute(monitor)形式这里面太复杂了,晚点再学[gf]1f926[/gf][gf]fe0f[/gf]。

其他的API还有很多值得研读的,比如说 commit 是怎样操作形式工作区和邻近地区库房的,很多命令带上参数在Jgit上能否对应同时实现,又是怎样同时实现对应操作形式的,比如说 checkout 带上 setCreateBranch是怎样同时实现分支的建立的,带着对git的理解看源码,既能了解Jgit的同时实现,透过Jgit API 中的标识符同时实现以及很多边界条件的考虑,还能加深对git本身的了解。

还有更多介绍能查看Jgit 官方主页Jgit官方API文档Jgit Support

附加信息

Java 执行 shell 命令

从接口职权、形式职权、启动应用用户拥有哪些命令职权都要掌控好,否则就是木马般的存有[gf]1f926[/gf][gf]1f3fb[/gf]。

org.apache.commonscommons-exec1.3

import java.io.ByteArrayOutputStream;import java.io.IOException;import java.nio.charset.StandardCharsets;import lombok.extern.slf4j.Slf4j;import org.apache.commons.exec.CommandLine;import org.apache.commons.exec.DefaultExecutor;import org.apache.commons.exec.ExecuteWatchdog;import org.apache.commons.exec.PumpStreamHandler;@Slf4jpublic class Test { private String executeCommand(String command, String params){ String line = String.format(“%s %s”, command, params); return execute(line);} private String executeCommand(String command){ return execute(command);} private String execute(String line){ CommandLine commandLine = CommandLine.parse(line); DefaultExecutor defaultExecutor = new DefaultExecutor(); defaultExecutor.setExitValues((int[]) null); ExecuteWatchdog watchdog = new ExecuteWatchdog(60000L);

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务