You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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会话工厂:

  1. 更新Gradle依赖,替换旧版JSch:
// 保留JGit SSH适配包
implementation "org.eclipse.jgit:org.eclipse.jgit.ssh.jsch:${eclipseJgitVersion}"
// 使用新版JSch替代旧版本
implementation "com.jcraft:jsch:0.1.55"
  1. 初始化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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 05:15:55