JGit克隆仓库报错:Cannot invoke SshSessionFactory.getSession因this.sch为null
JGit克隆特定域名仓库时出现SSH会话工厂空指针问题
问题背景
使用Java代码通过JGit克隆Git仓库,代码中创建UsernamePasswordCredentialsProvider传入HTTPS凭据,调用Git.cloneRepository()执行克隆。仅克隆特定域名的仓库时抛出异常,核心错误为:
Cannot invoke "org.eclipse.jgit.transport.SshSessionFactory.getSession" because "this.sch" is null
其他域名仓库可正常克隆。
代码示例
class Scratch { public static void main(String[] args) { String repoName = "https://github.somedomain.co/AppPipelines/some-project.git"; String username = "username"; String personalAccessToken = "some_personal_access_token"; String repoUrl = "https://github.somedomain.co/AppPipelines/some_repo"; String cloneDirectoryPath = "/Users/username/DSS/1223"; try { // 创建凭据提供者 UsernamePasswordCredentialsProvider credentialsProvider = new UsernamePasswordCredentialsProvider("token", personalAccessToken); Git.cloneRepository() .setURI(repoUrl) .setDirectory(new File(cloneDirectoryPath)) .setBranch("main") .setCredentialsProvider(credentialsProvider) .call(); } catch (GitAPIException e) { System.err.println("Error cloning repository: " + e.getMessage()); e.printStackTrace(); } } }
异常堆栈
Error cloning repository: git@github.bamtech.co:AppPipelines/nexus-rm: remote hung up unexpectedly org.eclipse.jgit.api.errors.TransportException: git@github.bamtech.co:AppPipelines/nexus-rm: remote hung up unexpectedly at org.eclipse.jgit.api.FetchCommand.call(FetchCommand.java:249) at org.eclipse.jgit.api.CloneCommand.fetch(CloneCommand.java:319) at org.eclipse.jgit.api.CloneCommand.call(CloneCommand.java:189) at Scratch.main(scratch_1.java:27) Caused by: org.eclipse.jgit.errors.TransportException: git@github.bamtech.co:AppPipelines/nexus-rm: remote hung up unexpectedly at org.eclipse.jgit.transport.TransportGitSsh$SshFetchConnection.<init>(TransportGitSsh.java:317) at org.eclipse.jgit.transport.TransportGitSsh.openFetch(TransportGitSsh.java:152) at org.eclipse.jgit.transport.FetchProcess.executeImp(FetchProcess.java:153) at org.eclipse.jgit.transport.FetchProcess.execute(FetchProcess.java:105) at org.eclipse.jgit.transport.Transport.fetch(Transport.java:1458) at org.eclipse.jgit.api.FetchCommand.call(FetchCommand.java:238) ... 3 more Caused by: java.lang.NullPointerException: Cannot invoke "org.eclipse.jgit.transport.SshSessionFactory.getSession(org.eclipse.jgit.transport.URIish, org.eclipse.jgit.transport.CredentialsProvider, org.eclipse.jgit.util.FS, int)" because "this.sch" is null at org.eclipse.jgit.transport.SshTransport.getSession(SshTransport.java:107) at org.eclipse.jgit.transport.TransportGitSsh$SshFetchConnection.<init>(TransportGitSsh.java:285) ... 8 more
Gradle依赖配置
plugins { id 'org.springframework.boot' version '3.4.1' id 'io.spring.dependency-management' version '1.1.0' id 'java' id 'jacoco' id 'com.diffplug.spotless' version '6.20.0' } dependencies { implementation "org.springframework.boot:spring-boot-starter-web" implementation "org.springdoc:springdoc-openapi-starter-webmvc-ui:${swaggerVersion}" testImplementation "org.springframework.boot:spring-boot-starter-test" compileOnly "org.projectlombok:lombok:${lombokVersion}" annotationProcessor "org.projectlombok:lombok:${lombokVersion}" implementation "org.springframework.boot:spring-boot-starter-validation" implementation "org.eclipse.jgit:org.eclipse.jgit:${eclipseJgitVersion}" implementation "org.eclipse.jgit.ssh.jsch:${eclipseJgitVersion}" implementation group: 'com.jcraft.jsch', name: 'com.jcraft.jsch', version: '0.1.37-20081006' implementation "com.google.code.gson:gson:${gsonVersion}" implementation platform("software.amazon.awssdk:bom:${awsSdk}") implementation 'software.amazon.awssdk:dynamodb' implementation 'software.amazon.awssdk:dynamodb-enhanced' implementation "com.github.derjust:spring-data-dynamodb:${springDataDynamoDB}" implementation "com.amazonaws:aws-java-sdk-sts:${awsSdk1Sts}" implementation "com.datadoghq:datadog-api-client:${ddClient}" implementation 'software.amazon.awssdk:sts' implementation 'software.amazon.awssdk:eks' implementation "software.amazon.awssdk:batch:${awsSdk}" implementation "software.amazon.awssdk:autoscaling:${awsSdk}" implementation "software.amazon.awssdk:netty-nio-client:${awsSdk}" implementation "software.amazon.awssdk:ec2:${awsSdk}" implementation "software.amazon.awssdk:ecs:${awsSdk}" implementation "com.datadoghq:java-dogstatsd-client:${dataDogStatSd}" implementation "org.springframework.boot:spring-boot-starter-actuator" implementation 'org.springframework.boot:spring-boot-starter-aop' implementation 'io.micrometer:micrometer-registry-datadog:1.14.3' implementation "org.springframework.security:spring-security-web:${springSecurityWebVersion}" implementation "com.disneystreaming.nexus:common-java-libs:0.0.2" }
JGit版本:eclipseJgitVersion=7.1.0.202411261347-r
问题分析与解决
1. 核心原因
从异常信息可见,代码传入的是HTTPS格式URL,但实际执行时被切换为SSH格式(git@github.bamtech.co:AppPipelines/nexus-rm)。可能的触发场景:
- 目标Git服务配置了强制SSH访问,自动重定向HTTPS请求
- 本地Git配置存在
url.<base>.insteadOf规则,将HTTPS URL替换为SSH - JGit内部的传输逻辑自动切换协议
此时代码仅配置了HTTPS的凭据提供者,未初始化SSH会话工厂,导致SshTransport的sch字段为空,触发空指针异常。
2. 解决方案
方案一:强制使用HTTPS协议
在克隆命令中添加传输配置回调,明确指定使用HTTPS传输,避免自动切换到SSH:
Git.cloneRepository() .setURI(repoUrl) .setDirectory(new File(cloneDirectoryPath)) .setBranch("main") .setCredentialsProvider(credentialsProvider) .setTransportConfigCallback(transport -> { if (transport instanceof HttpTransport) { HttpTransport httpTransport = (HttpTransport) transport; httpTransport.setCredentialsProvider(credentialsProvider); // 禁用HTTP到SSH的自动转换 httpTransport.getHttpConnectionFactory().setFollowRedirects(false); } }) .call();
同时修正凭据提供者的用户名参数(部分Git服务要求传入真实用户名而非"token"):
UsernamePasswordCredentialsProvider credentialsProvider = new UsernamePasswordCredentialsProvider(username, personalAccessToken);
方案二:修复SSH依赖并初始化会话工厂
如果确实需要使用SSH协议,需更新依赖并初始化SSH会话工厂:
- 更新Gradle依赖,替换旧版JSch:
// 保留JGit SSH适配包 implementation "org.eclipse.jgit:org.eclipse.jgit.ssh.jsch:${eclipseJgitVersion}" // 使用新版JSch替代旧版本 implementation "com.jcraft:jsch:0.1.55"
- 初始化SSH会话工厂并配置到克隆命令:
// 初始化JSch会话工厂 JschConfigSessionFactory sshSessionFactory = new JschConfigSessionFactory() { @Override protected void configure(OpenSshConfig.Host host, Session session) { // 设置SSH密码(此处使用个人访问令牌作为密码) session.setPassword(personalAccessToken); // 禁用严格主机密钥检查(可选,根据安全需求调整) session.setConfig("StrictHostKeyChecking", "no"); } }; // 执行克隆 Git.cloneRepository() .setURI(repoUrl) .setDirectory(new File(cloneDirectoryPath)) .setBranch("main") .setCredentialsProvider(credentialsProvider) .setTransportConfigCallback(transport -> { if (transport instanceof SshTransport) { ((SshTransport) transport).setSshSessionFactory(sshSessionFactory); } }) .call();
方案三:检查并清理本地Git配置
查看本地是否存在URL替换规则:
git config --global --get-regexp insteadOf
如果存在类似url.git@github.bamtech.co:.insteadOf https://github.bamtech.co/的配置,可删除该规则:
git config --global --unset url.git@github.bamtech.co:.insteadOf
或在代码中禁用读取本地Git配置:
Git.cloneRepository() .setURI(repoUrl) .setDirectory(new File(cloneDirectoryPath)) .setBranch("main") .setCredentialsProvider(credentialsProvider) .setGitConfig("core.autocrlf", "false") // 示例:禁用本地配置加载 .call();
内容的提问来源于stack exchange,提问作者Aravind S
相关产品推荐
相关产品推荐

