aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.mailmap28
-rw-r--r--WORKSPACE61
-rw-r--r--lib/BUILD44
-rw-r--r--org.eclipse.jgit.ant.test/.classpath6
-rw-r--r--org.eclipse.jgit.ant.test/.settings/org.eclipse.jdt.core.prefs2
-rw-r--r--org.eclipse.jgit.ant.test/.settings/org.eclipse.mylyn.team.ui.prefs2
-rw-r--r--org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF10
-rw-r--r--org.eclipse.jgit.ant.test/pom.xml4
-rw-r--r--org.eclipse.jgit.ant/.settings/org.eclipse.jdt.core.prefs2
-rw-r--r--org.eclipse.jgit.ant/.settings/org.eclipse.mylyn.team.ui.prefs2
-rw-r--r--org.eclipse.jgit.ant/META-INF/MANIFEST.MF6
-rw-r--r--org.eclipse.jgit.ant/pom.xml4
-rw-r--r--org.eclipse.jgit.archive/.settings/org.eclipse.jdt.core.prefs2
-rw-r--r--org.eclipse.jgit.archive/.settings/org.eclipse.mylyn.team.ui.prefs2
-rw-r--r--org.eclipse.jgit.archive/META-INF/MANIFEST.MF14
-rw-r--r--org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF4
-rw-r--r--org.eclipse.jgit.archive/pom.xml2
-rw-r--r--org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/TarFormat.java7
-rw-r--r--org.eclipse.jgit.http.apache/.settings/org.eclipse.jdt.core.prefs2
-rw-r--r--org.eclipse.jgit.http.apache/.settings/org.eclipse.mylyn.team.ui.prefs2
-rw-r--r--org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF11
-rw-r--r--org.eclipse.jgit.http.apache/pom.xml2
-rw-r--r--org.eclipse.jgit.http.apache/src/org/eclipse/jgit/transport/http/apache/HttpClientConnection.java12
-rw-r--r--org.eclipse.jgit.http.server/.settings/org.eclipse.jdt.core.prefs2
-rw-r--r--org.eclipse.jgit.http.server/.settings/org.eclipse.mylyn.team.ui.prefs2
-rw-r--r--org.eclipse.jgit.http.server/META-INF/MANIFEST.MF27
-rw-r--r--org.eclipse.jgit.http.server/pom.xml2
-rw-r--r--org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ClientVersionUtil.java22
-rw-r--r--org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitSmartHttpTools.java7
-rw-r--r--org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/InfoRefsServlet.java2
-rw-r--r--org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ReceivePackServlet.java12
-rw-r--r--org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ServletUtils.java5
-rw-r--r--org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/SmartOutputStream.java1
-rw-r--r--org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackServlet.java10
-rw-r--r--org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/ServletBinder.java4
-rw-r--r--org.eclipse.jgit.http.test/.classpath12
-rw-r--r--org.eclipse.jgit.http.test/.settings/org.eclipse.jdt.core.prefs2
-rw-r--r--org.eclipse.jgit.http.test/.settings/org.eclipse.mylyn.team.ui.prefs2
-rw-r--r--org.eclipse.jgit.http.test/META-INF/MANIFEST.MF40
-rw-r--r--org.eclipse.jgit.http.test/pom.xml4
-rw-r--r--org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/server/ClientVersionUtilTest.java15
-rw-r--r--org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/FileResolverTest.java1
-rw-r--r--org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/RegexPipelineTest.java24
-rw-r--r--org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java2
-rw-r--r--org.eclipse.jgit.http.test/tst/org/eclipse/jgit/transport/http/apache/HttpClientConnectionTest.java2
-rw-r--r--org.eclipse.jgit.junit.http/.classpath18
-rw-r--r--org.eclipse.jgit.junit.http/.settings/org.eclipse.jdt.core.prefs2
-rw-r--r--org.eclipse.jgit.junit.http/.settings/org.eclipse.mylyn.team.ui.prefs2
-rw-r--r--org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF20
-rw-r--r--org.eclipse.jgit.junit.http/pom.xml2
-rw-r--r--org.eclipse.jgit.junit.ssh/.classpath7
-rw-r--r--org.eclipse.jgit.junit.ssh/.gitignore2
-rw-r--r--org.eclipse.jgit.junit.ssh/.project34
-rw-r--r--org.eclipse.jgit.junit.ssh/.settings/org.eclipse.core.resources.prefs3
-rw-r--r--org.eclipse.jgit.junit.ssh/.settings/org.eclipse.core.runtime.prefs3
-rw-r--r--org.eclipse.jgit.junit.ssh/.settings/org.eclipse.jdt.core.prefs399
-rw-r--r--org.eclipse.jgit.junit.ssh/.settings/org.eclipse.jdt.ui.prefs66
-rw-r--r--org.eclipse.jgit.junit.ssh/.settings/org.eclipse.mylyn.tasks.ui.prefs4
-rw-r--r--org.eclipse.jgit.junit.ssh/.settings/org.eclipse.mylyn.team.ui.prefs3
-rw-r--r--org.eclipse.jgit.junit.ssh/.settings/org.eclipse.pde.api.tools.prefs104
-rw-r--r--org.eclipse.jgit.junit.ssh/.settings/org.eclipse.pde.core.prefs3
-rw-r--r--org.eclipse.jgit.junit.ssh/BUILD15
-rw-r--r--org.eclipse.jgit.junit.ssh/META-INF/MANIFEST.MF36
-rw-r--r--org.eclipse.jgit.junit.ssh/build.properties5
-rw-r--r--org.eclipse.jgit.junit.ssh/plugin.properties2
-rw-r--r--org.eclipse.jgit.junit.ssh/pom.xml116
-rw-r--r--org.eclipse.jgit.junit.ssh/src/org/eclipse/jgit/junit/ssh/SshTestGitServer.java392
-rw-r--r--org.eclipse.jgit.junit/.settings/.api_filters35
-rw-r--r--org.eclipse.jgit.junit/.settings/org.eclipse.jdt.core.prefs2
-rw-r--r--org.eclipse.jgit.junit/.settings/org.eclipse.mylyn.team.ui.prefs2
-rw-r--r--org.eclipse.jgit.junit/META-INF/MANIFEST.MF47
-rw-r--r--org.eclipse.jgit.junit/pom.xml2
-rw-r--r--org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/MockSystemReader.java2
-rw-r--r--org.eclipse.jgit.lfs.server.test/.classpath6
-rw-r--r--org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.jdt.core.prefs2
-rw-r--r--org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.mylyn.team.ui.prefs2
-rw-r--r--org.eclipse.jgit.lfs.server.test/META-INF/MANIFEST.MF38
-rw-r--r--org.eclipse.jgit.lfs.server.test/pom.xml4
-rw-r--r--org.eclipse.jgit.lfs.server.test/tst/org/eclipse/jgit/lfs/server/fs/PushTest.java3
-rw-r--r--org.eclipse.jgit.lfs.server/.settings/.api_filters4
-rw-r--r--org.eclipse.jgit.lfs.server/.settings/org.eclipse.jdt.core.prefs2
-rw-r--r--org.eclipse.jgit.lfs.server/.settings/org.eclipse.mylyn.team.ui.prefs2
-rw-r--r--org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF32
-rw-r--r--org.eclipse.jgit.lfs.server/pom.xml2
-rw-r--r--org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/LargeFileRepository.java9
-rw-r--r--org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/FileLfsRepository.java3
-rw-r--r--org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/s3/SignerV4.java3
-rw-r--r--org.eclipse.jgit.lfs.test/.classpath12
-rw-r--r--org.eclipse.jgit.lfs.test/.settings/org.eclipse.jdt.core.prefs2
-rw-r--r--org.eclipse.jgit.lfs.test/.settings/org.eclipse.mylyn.team.ui.prefs2
-rw-r--r--org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF24
-rw-r--r--org.eclipse.jgit.lfs.test/pom.xml4
-rw-r--r--org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/lib/LFSPointerTest.java4
-rw-r--r--org.eclipse.jgit.lfs/.settings/org.eclipse.jdt.core.prefs2
-rw-r--r--org.eclipse.jgit.lfs/.settings/org.eclipse.mylyn.team.ui.prefs2
-rw-r--r--org.eclipse.jgit.lfs/META-INF/MANIFEST.MF46
-rw-r--r--org.eclipse.jgit.lfs/pom.xml2
-rw-r--r--org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/BuiltinLFS.java3
-rw-r--r--org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/SmudgeFilter.java5
-rw-r--r--org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/AtomicObjectOutputStream.java3
-rw-r--r--org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/LfsConnectionFactory.java3
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.feature/.settings/org.eclipse.mylyn.team.ui.prefs2
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.xml2
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml2
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/.settings/org.eclipse.mylyn.team.ui.prefs2
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/feature.xml2
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/pom.xml2
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/.settings/org.eclipse.mylyn.team.ui.prefs2
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.xml23
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/pom.xml7
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/.settings/org.eclipse.mylyn.team.ui.prefs2
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/feature.xml2
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/pom.xml2
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/.settings/org.eclipse.mylyn.team.ui.prefs2
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.xml7
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/pom.xml2
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/.settings/org.eclipse.mylyn.team.ui.prefs2
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/feature.xml2
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/pom.xml2
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.repository/.settings/org.eclipse.mylyn.team.ui.prefs2
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.repository/category.xml6
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.repository/pom.xml12
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/.settings/org.eclipse.mylyn.team.ui.prefs2
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.xml2
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/pom.xml2
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/.gitignore1
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/.project17
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/.settings/org.eclipse.core.resources.prefs3
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/.settings/org.eclipse.core.runtime.prefs3
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/.settings/org.eclipse.mylyn.tasks.ui.prefs4
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/.settings/org.eclipse.mylyn.team.ui.prefs3
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/build.properties4
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/edl-v10.html59
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/feature.properties177
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/feature.xml57
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/license.html189
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/pom.xml77
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/.gitignore1
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/.project17
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/.settings/org.eclipse.core.resources.prefs3
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/.settings/org.eclipse.core.runtime.prefs3
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/.settings/org.eclipse.mylyn.tasks.ui.prefs4
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/.settings/org.eclipse.mylyn.team.ui.prefs3
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/build.properties4
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/edl-v10.html59
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/feature.properties178
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/feature.xml31
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/license.html189
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/pom.xml62
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/META-INF/MANIFEST.MF2
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.10-staging.target (renamed from org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.9-staging.target)34
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.10-staging.tpd (renamed from org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.9-staging.tpd)4
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.5.target32
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.5.tpd2
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.target32
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.tpd2
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.target32
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.tpd2
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.8.target32
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.8.tpd2
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.9.target86
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.9.tpd8
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20181128170323-2018-12.tpd58
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/pom.xml4
-rw-r--r--org.eclipse.jgit.packaging/pom.xml10
-rw-r--r--org.eclipse.jgit.pgm.test/.classpath12
-rw-r--r--org.eclipse.jgit.pgm.test/.settings/org.eclipse.jdt.core.prefs2
-rw-r--r--org.eclipse.jgit.pgm.test/.settings/org.eclipse.mylyn.team.ui.prefs2
-rw-r--r--org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF36
-rw-r--r--org.eclipse.jgit.pgm.test/pom.xml4
-rw-r--r--org.eclipse.jgit.pgm.test/src/org/eclipse/jgit/pgm/CLIGitCommand.java13
-rw-r--r--org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ProxyConfigTest.java4
-rw-r--r--org.eclipse.jgit.pgm/.settings/org.eclipse.jdt.core.prefs2
-rw-r--r--org.eclipse.jgit.pgm/.settings/org.eclipse.mylyn.team.ui.prefs2
-rw-r--r--org.eclipse.jgit.pgm/BUILD2
-rw-r--r--org.eclipse.jgit.pgm/META-INF/MANIFEST.MF87
-rw-r--r--org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF4
-rw-r--r--org.eclipse.jgit.pgm/pom.xml8
-rw-r--r--org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties5
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/TextBuiltin.java88
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/TextHashFunctions.java2
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/SshDriver.java56
-rw-r--r--org.eclipse.jgit.ssh.apache.test/.classpath11
-rw-r--r--org.eclipse.jgit.ssh.apache.test/.gitignore2
-rw-r--r--org.eclipse.jgit.ssh.apache.test/.project28
-rw-r--r--org.eclipse.jgit.ssh.apache.test/.settings/org.eclipse.core.resources.prefs3
-rw-r--r--org.eclipse.jgit.ssh.apache.test/.settings/org.eclipse.core.runtime.prefs3
-rw-r--r--org.eclipse.jgit.ssh.apache.test/.settings/org.eclipse.jdt.core.prefs399
-rw-r--r--org.eclipse.jgit.ssh.apache.test/.settings/org.eclipse.jdt.ui.prefs66
-rw-r--r--org.eclipse.jgit.ssh.apache.test/.settings/org.eclipse.mylyn.tasks.ui.prefs4
-rw-r--r--org.eclipse.jgit.ssh.apache.test/.settings/org.eclipse.mylyn.team.ui.prefs3
-rw-r--r--org.eclipse.jgit.ssh.apache.test/.settings/org.eclipse.pde.api.tools.prefs104
-rw-r--r--org.eclipse.jgit.ssh.apache.test/.settings/org.eclipse.pde.core.prefs3
-rw-r--r--org.eclipse.jgit.ssh.apache.test/BUILD19
-rw-r--r--org.eclipse.jgit.ssh.apache.test/META-INF/MANIFEST.MF19
-rw-r--r--org.eclipse.jgit.ssh.apache.test/about.html96
-rw-r--r--org.eclipse.jgit.ssh.apache.test/build.properties5
-rw-r--r--org.eclipse.jgit.ssh.apache.test/plugin.properties2
-rw-r--r--org.eclipse.jgit.ssh.apache.test/pom.xml145
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/transport/sshd/proxy/HttpParserTest.java146
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/transport/sshd/ApacheSshTest.java105
-rw-r--r--org.eclipse.jgit.ssh.apache/.classpath8
-rw-r--r--org.eclipse.jgit.ssh.apache/.fbprefs125
-rw-r--r--org.eclipse.jgit.ssh.apache/.gitignore2
-rw-r--r--org.eclipse.jgit.ssh.apache/.project34
-rw-r--r--org.eclipse.jgit.ssh.apache/.settings/org.eclipse.core.resources.prefs3
-rw-r--r--org.eclipse.jgit.ssh.apache/.settings/org.eclipse.core.runtime.prefs3
-rw-r--r--org.eclipse.jgit.ssh.apache/.settings/org.eclipse.jdt.core.prefs399
-rw-r--r--org.eclipse.jgit.ssh.apache/.settings/org.eclipse.jdt.ui.prefs66
-rw-r--r--org.eclipse.jgit.ssh.apache/.settings/org.eclipse.mylyn.tasks.ui.prefs4
-rw-r--r--org.eclipse.jgit.ssh.apache/.settings/org.eclipse.mylyn.team.ui.prefs3
-rw-r--r--org.eclipse.jgit.ssh.apache/.settings/org.eclipse.pde.api.tools.prefs104
-rw-r--r--org.eclipse.jgit.ssh.apache/.settings/org.eclipse.pde.core.prefs3
-rw-r--r--org.eclipse.jgit.ssh.apache/BUILD19
-rw-r--r--org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF83
-rw-r--r--org.eclipse.jgit.ssh.apache/META-INF/SOURCE-MANIFEST.MF7
-rw-r--r--org.eclipse.jgit.ssh.apache/about.html96
-rw-r--r--org.eclipse.jgit.ssh.apache/build.properties7
-rw-r--r--org.eclipse.jgit.ssh.apache/plugin.properties2
-rw-r--r--org.eclipse.jgit.ssh.apache/pom.xml256
-rw-r--r--org.eclipse.jgit.ssh.apache/resources/org/eclipse/jgit/internal/transport/sshd/SshdText.properties77
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/CachingKeyPairProvider.java168
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/EncryptedFileKeyPairProvider.java158
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/GssApiMechanisms.java231
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/GssApiWithMicAuthFactory.java68
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/GssApiWithMicAuthentication.java282
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitClientSession.java333
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitHostConfigEntry.java103
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPasswordAuthFactory.java66
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPasswordAuthentication.java100
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthFactory.java83
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthentication.java103
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyIterator.java275
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitSshClient.java442
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitSshConfig.java146
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitUserInteraction.java219
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/KnownHostEntryReader.java208
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/OpenSshServerKeyVerifier.java745
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/PasswordProviderWrapper.java137
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/RepeatingFilePasswordProvider.java116
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/ServerKeyLookup.java69
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/SshdText.java91
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/auth/AbstractAuthenticationHandler.java89
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/auth/AuthenticationHandler.java121
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/auth/BasicAuthentication.java167
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/auth/GssApiAuthentication.java147
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/AbstractClientProxyConnector.java213
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/AuthenticationChallenge.java123
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/HttpClientConnector.java403
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/HttpParser.java346
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/Socks5ClientConnector.java642
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/StatefulProxyConnector.java92
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/StatusLine.java99
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/DefaultProxyDataFactory.java100
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/IdentityPasswordProvider.java275
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/JGitKeyCache.java88
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/KeyCache.java74
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/KeyPasswordProvider.java117
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/ProxyData.java136
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/ProxyDataFactory.java66
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SessionCloseListener.java61
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSession.java485
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSessionFactory.java499
-rw-r--r--org.eclipse.jgit.test/.classpath19
-rw-r--r--org.eclipse.jgit.test/.gitignore1
-rw-r--r--org.eclipse.jgit.test/.settings/org.eclipse.jdt.core.prefs2
-rw-r--r--org.eclipse.jgit.test/.settings/org.eclipse.mylyn.team.ui.prefs2
-rw-r--r--org.eclipse.jgit.test/BUILD30
-rw-r--r--org.eclipse.jgit.test/META-INF/MANIFEST.MF102
-rw-r--r--org.eclipse.jgit.test/build.properties7
-rw-r--r--org.eclipse.jgit.test/pom.xml32
-rw-r--r--org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_dsa12
-rw-r--r--org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_dsa.pub1
-rw-r--r--org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_dsa_testpass15
-rw-r--r--org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_dsa_testpass.pub1
-rw-r--r--org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_2565
-rw-r--r--org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_256.pub1
-rw-r--r--org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_256_testpass8
-rw-r--r--org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_256_testpass.pub1
-rw-r--r--org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_3846
-rw-r--r--org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_384.pub1
-rw-r--r--org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_384_testpass9
-rw-r--r--org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_384_testpass.pub1
-rw-r--r--org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_5217
-rw-r--r--org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_521.pub1
-rw-r--r--org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_521_testpass10
-rw-r--r--org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_521_testpass.pub1
-rw-r--r--org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ed255197
-rw-r--r--org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ed25519.pub1
-rw-r--r--org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ed25519_testpass8
-rw-r--r--org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ed25519_testpass.pub1
-rw-r--r--org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_102415
-rw-r--r--org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_1024.pub1
-rw-r--r--org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_1024_testpass18
-rw-r--r--org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_1024_testpass.pub1
-rw-r--r--org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_204827
-rw-r--r--org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_2048.pub1
-rw-r--r--org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_2048_testpass30
-rw-r--r--org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_2048_testpass.pub1
-rw-r--r--org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_307239
-rw-r--r--org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_3072.pub1
-rw-r--r--org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_3072_testpass42
-rw-r--r--org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_3072_testpass.pub1
-rw-r--r--org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_409651
-rw-r--r--org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_4096.pub1
-rw-r--r--org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_4096_testpass54
-rw-r--r--org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_4096_testpass.pub1
-rw-r--r--org.eclipse.jgit.test/src/org/eclipse/jgit/transport/ssh/SshTestBase.java844
-rw-r--r--org.eclipse.jgit.test/src/org/eclipse/jgit/transport/ssh/SshTestHarness.java452
-rw-r--r--org.eclipse.jgit.test/tests.bzl13
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java68
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ArchiveCommandTest.java10
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/BlameCommandTest.java70
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java4
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CleanCommandTest.java21
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CloneCommandTest.java31
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitAndLogCommandTest.java17
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitOnlyTest.java3
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CrLfNativeTest.java181
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/DescribeCommandTest.java6
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/EolRepositoryTest.java5
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PushCommandTest.java2
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ResetCommandTest.java10
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesMatcherTest.java8
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeTest.java15
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffEntryTest.java63
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/RawTextTest.java9
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBuilderTest.java1
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandTest.java225
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/FastIgnoreRuleTest.java9
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsFsckTest.java55
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileRepositoryBuilderTest.java82
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ObjectDirectoryTest.java7
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogReaderTest.java17
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogWriterTest.java5
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/T0003_BasicTest.java2
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/WindowCacheGetTest.java2
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/transport/parser/FirstWantTest.java130
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java364
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutMaliciousPathTest.java7
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java8
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/GpgConfigTest.java147
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffSubmoduleTest.java85
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffTest.java22
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/MergeHeadMsgTest.java9
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectLoaderTest.java6
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RacyGitTests.java3
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefTest.java16
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/SquashCommitMsgTest.java3
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ValidRefNameTest.java4
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergeAlgorithmTest.java5
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergerTest.java6
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/revplot/PlotCommitListTest.java2
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/FileBasedConfigTest.java96
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleWalkTest.java22
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/JSchSshTest.java117
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ObjectIdMatcher.java100
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/OpenSshConfigTest.java31
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PacketLineOutTest.java6
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ProtocolV0ParserTest.java199
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ProtocolV2ParserTest.java116
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateParserTest.java3
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateStoreTest.java5
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ReceivePackAdvertiseRefsHookTest.java5
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/SideBandOutputStreamTest.java7
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TestProtocolTest.java3
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java420
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/WalkEncryptionTest.java6
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/http/JDKHttpConnectionTest.java123
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/AbstractTreeIteratorTest.java2
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/InterIndexDiffFilterTest.java3
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/util/BlockListTest.java13
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/util/HookTest.java7
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/util/IOReadLineTest.java3
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RawParseUtils_LineMapTest.java5
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RunExternalScriptTest.java52
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/AutoCRLFInputStreamTest.java6
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/AutoCRLFOutputStreamTest.java6
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/TimeoutOutputStreamTest.java2
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/UnionInputStreamTest.java7
-rw-r--r--org.eclipse.jgit.ui/.settings/org.eclipse.jdt.core.prefs2
-rw-r--r--org.eclipse.jgit.ui/.settings/org.eclipse.mylyn.team.ui.prefs2
-rw-r--r--org.eclipse.jgit.ui/META-INF/MANIFEST.MF18
-rw-r--r--org.eclipse.jgit.ui/pom.xml2
-rw-r--r--org.eclipse.jgit/.settings/.api_filters20
-rw-r--r--org.eclipse.jgit/.settings/org.eclipse.jdt.core.prefs2
-rw-r--r--org.eclipse.jgit/.settings/org.eclipse.mylyn.team.ui.prefs2
-rw-r--r--org.eclipse.jgit/META-INF/MANIFEST.MF104
-rw-r--r--org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF4
-rw-r--r--org.eclipse.jgit/pom.xml2
-rw-r--r--org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties10
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/ApplyCommand.java8
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java31
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/ListBranchCommand.java22
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java9
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleAddCommand.java15
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/TransportConfigCallback.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNodeProvider.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesProvider.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/attributes/FilterCommand.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/attributes/FilterCommandFactory.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java29
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheIterator.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/events/WorkingTreeModifiedEvent.java6
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/Head.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/ManifestParser.java3
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java145
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoProject.java1
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/hooks/PrePushHook.java6
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/Strings.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java5
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/fsck/FsckError.java11
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/fsck/FsckPackParser.java8
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsFsck.java35
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/ReadableChannel.java10
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java6
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexRemapper.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/ObjectReuseAsIs.java12
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackOutputStream.java1
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/submodule/SubmoduleValidator.java56
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/parser/FirstWant.java146
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/ssh/OpenSshConfigFile.java922
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/AsyncObjectLoaderQueue.java8
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/AsyncObjectSizeQueue.java8
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/AsyncOperation.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/BlobObjectChecker.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/CheckoutEntry.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java32
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java44
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigLine.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java10
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/DefaultTypedConfigGetter.java3
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgConfig.java121
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java44
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectChecker.java25
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/ProgressMonitor.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/Ref.java18
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java25
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/RefUpdate.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/ReflogEntry.java16
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/ReflogReader.java10
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeFormatter.java78
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeFormatterPass.java25
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/revwalk/AsyncRevObjectQueue.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthGenerator.java79
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthWalk.java125
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevCommit.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java42
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/AdvertiseRefsHook.java6
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/AmazonS3.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseReceivePack.java10
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/Connection.java12
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchConnection.java12
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java3
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchRequest.java194
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV0Request.java147
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV2Request.java247
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/FtpChannel.java246
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java14
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/InternalPushConnection.java12
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/JschConfigSessionFactory.java6
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/JschSession.java169
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/LsRefsV2Request.java84
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/NetRC.java8
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/NonceGenerator.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/OpenSshConfig.java831
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/PostReceiveHook.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/PostUploadHook.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/PreReceiveHook.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/PreUploadHook.java8
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV0Parser.java158
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2Parser.java116
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/PushConnection.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java48
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/RefAdvertiser.java13
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/RefFilter.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteRefUpdate.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteSession.java15
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/SideBandInputStream.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/SshConstants.java202
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/SshSessionFactory.java30
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/TransferConfig.java3
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportBundle.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportSftp.java222
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java17
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java488
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/http/HttpConnection.java89
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/http/HttpConnectionFactory.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/http/JDKHttpConnection.java25
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/ReceivePackFactory.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/RepositoryResolver.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/UploadPackFactory.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java11
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/HttpSupport.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/LfsFactory.java11
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/RawParseUtils.java20
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/io/EolStreamTypeUtil.java83
-rw-r--r--pom.xml79
-rw-r--r--tools/BUILD107
-rwxr-xr-xtools/maven-central/deploy.rb2
-rwxr-xr-xtools/maven-central/download.rb2
507 files changed, 23256 insertions, 2814 deletions
diff --git a/.mailmap b/.mailmap
index 7c9dc3d7ce..f0bc990d8d 100644
--- a/.mailmap
+++ b/.mailmap
@@ -1,10 +1,18 @@
-Han-Wen Nienhuys <hanwen@google.com> Han-Wen NIenhuys <hanwen@google.com>
-Mark Ingram <markdingram@gmail.com> markdingram <markdingram@gmail.com>
-Roberto Tyley <roberto.tyley@guardian.co.uk> roberto <roberto.tyley@guardian.co.uk>
-Saša Živkov <sasa.zivkov@sap.com> Sasa Zivkov <sasa.zivkov@sap.com>
-Saša Živkov <sasa.zivkov@sap.com> Saša Živkov <zivkov@gmail.com>
-Saša Živkov <sasa.zivkov@sap.com> Sasa Zivkov <zivkov@gmail.com>
-Shawn Pearce <spearce@spearce.org> Shawn O. Pearce <sop@google.com>
-Shawn Pearce <spearce@spearce.org> Shawn Pearce <sop@google.com>
-Shawn Pearce <spearce@spearce.org> Shawn O. Pearce <spearce@spearce.org>
-Terry Parker <tparker@google.com> tparker <tparker@google.com>
+Chris Aniszczyk <caniszczyk@gmail.com> Chris Aniszczyk <zx@eclipsesource.com>
+Christian Halstrick <christian.halstrick@sap.com> Christian Halstrick <christian.halstrick@gmail.com>
+Dani Megert <Daniel_Megert@ch.ibm.com> Daniel Megert <daniel_megert@ch.ibm.com>
+David Pursehouse <david.pursehouse@gmail.com> David Pursehouse <david.pursehouse@sonymobile.com>
+Han-Wen Nienhuys <hanwen@google.com> Han-Wen NIenhuys <hanwen@google.com>
+Hector Oswaldo Caballero <hector.caballero@ericsson.com> Hector Caballero <hector.caballero@ericsson.com>
+Lars Vogel <Lars.Vogel@vogella.com> Lars Vogel <Lars.Vogel@gmail.com>
+Mark Ingram <markdingram@gmail.com> markdingram <markdingram@gmail.com>
+Markus Duft <markus.duft@ssi-schaefer.com> Markus Duft <markus.duft@salomon.at>
+Michael Keppler <michael.keppler@gmx.de> Michael Keppler <Michael.Keppler@gmx.de>
+Roberto Tyley <roberto.tyley@guardian.co.uk> roberto <roberto.tyley@guardian.co.uk>
+Saša Živkov <sasa.zivkov@sap.com> Sasa Zivkov <sasa.zivkov@sap.com>
+Saša Živkov <sasa.zivkov@sap.com> Saša Živkov <zivkov@gmail.com>
+Saša Živkov <sasa.zivkov@sap.com> Sasa Zivkov <zivkov@gmail.com>
+Shawn Pearce <spearce@spearce.org> Shawn O. Pearce <sop@google.com>
+Shawn Pearce <spearce@spearce.org> Shawn Pearce <sop@google.com>
+Shawn Pearce <spearce@spearce.org> Shawn O. Pearce <spearce@spearce.org>
+Terry Parker <tparker@google.com> tparker <tparker@google.com>
diff --git a/WORKSPACE b/WORKSPACE
index 9d723d28e1..0eabeccbc5 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -1,5 +1,18 @@
workspace(name = "jgit")
+load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
+
+http_archive(
+ name = "bazel_skylib",
+ sha256 = "bbccf674aa441c266df9894182d80de104cabd19be98be002f6d478aaa31574d",
+ strip_prefix = "bazel-skylib-2169ae1c374aab4a09aa90e65efe1a3aad4e279b",
+ urls = ["https://github.com/bazelbuild/bazel-skylib/archive/2169ae1c374aab4a09aa90e65efe1a3aad4e279b.tar.gz"],
+)
+
+load("@bazel_skylib//lib:versions.bzl", "versions")
+
+versions.check(minimum_bazel_version = "0.19.0")
+
load("//tools:bazlets.bzl", "load_bazlets")
load_bazlets(commit = "3afbeab55ece585dbfc7a980bf7214b24ddbbe86")
@@ -10,6 +23,12 @@ load(
)
maven_jar(
+ name = "eddsa",
+ artifact = "net.i2p.crypto:eddsa:0.3.0",
+ sha1 = "1901c8d4d8bffb7d79027686cfb91e704217c3e1",
+)
+
+maven_jar(
name = "jsch",
artifact = "com.jcraft:jsch:0.1.54",
sha1 = "da3584329a263616e277e15462b387addd1b208d",
@@ -40,6 +59,18 @@ maven_jar(
)
maven_jar(
+ name = "sshd-core",
+ artifact = "org.apache.sshd:sshd-core:2.0.0",
+ sha1 = "f4275079a2463cfd2bf1548a80e1683288a8e86b",
+)
+
+maven_jar(
+ name = "sshd-sftp",
+ artifact = "org.apache.sshd:sshd-sftp:2.0.0",
+ sha1 = "a12d64dc2d5d23271a4dc58075e55f9c64a68494",
+)
+
+maven_jar(
name = "commons-codec",
artifact = "commons-codec:commons-codec:1.10",
sha1 = "4b95f4897fa13f2cd904aee711aeafc0c5295cd8",
@@ -71,8 +102,8 @@ maven_jar(
maven_jar(
name = "commons-compress",
- artifact = "org.apache.commons:commons-compress:1.15",
- sha1 = "b686cd04abaef1ea7bc5e143c080563668eec17e",
+ artifact = "org.apache.commons:commons-compress:1.18",
+ sha1 = "1191f9f2bc0c47a8cce69193feb1ff0a8bcb37d5",
)
maven_jar(
@@ -106,6 +137,32 @@ maven_jar(
)
maven_jar(
+ name = "mockito",
+ artifact = "org.mockito:mockito-core:2.13.0",
+ sha1 = "8e372943974e4a121fb8617baced8ebfe46d54f0",
+)
+
+BYTE_BUDDY_VERSION = "1.7.9"
+
+maven_jar(
+ name = "byte_buddy",
+ artifact = "net.bytebuddy:byte-buddy:" + BYTE_BUDDY_VERSION,
+ sha1 = "51218a01a882c04d0aba8c028179cce488bbcb58",
+)
+
+maven_jar(
+ name = "byte_buddy_agent",
+ artifact = "net.bytebuddy:byte-buddy-agent:" + BYTE_BUDDY_VERSION,
+ sha1 = "a6c65f9da7f467ee1f02ff2841ffd3155aee2fc9",
+)
+
+maven_jar(
+ name = "objenesis",
+ artifact = "org.objenesis:objenesis:2.6",
+ sha1 = "639033469776fd37c08358c6b92a4761feb2af4b",
+)
+
+maven_jar(
name = "gson",
artifact = "com.google.code.gson:gson:2.8.2",
sha1 = "3edcfe49d2c6053a70a2a47e4e1c2f94998a49cf",
diff --git a/lib/BUILD b/lib/BUILD
index ebba0daf49..0c5c224262 100644
--- a/lib/BUILD
+++ b/lib/BUILD
@@ -23,12 +23,20 @@ java_library(
java_library(
name = "commons-logging",
- testonly = 1,
visibility = ["//visibility:public"],
exports = ["@commons-logging//jar"],
)
java_library(
+ name = "eddsa",
+ visibility = [
+ "//org.eclipse.jgit.ssh.apache:__pkg__",
+ "//org.eclipse.jgit.ssh.apache.test:__pkg__",
+ ],
+ exports = ["@eddsa//jar"],
+)
+
+java_library(
name = "gson",
visibility = [
"//org.eclipse.jgit.lfs:__pkg__",
@@ -60,6 +68,28 @@ java_library(
)
java_library(
+ name = "sshd-core",
+ visibility = [
+ "//org.eclipse.jgit.junit.ssh:__pkg__",
+ "//org.eclipse.jgit.ssh.apache:__pkg__",
+ "//org.eclipse.jgit.ssh.apache.test:__pkg__",
+ "//org.eclipse.jgit.test:__pkg__",
+ ],
+ exports = ["@sshd-core//jar"],
+)
+
+java_library(
+ name = "sshd-sftp",
+ visibility = [
+ "//org.eclipse.jgit.junit.ssh:__pkg__",
+ "//org.eclipse.jgit.ssh.apache:__pkg__",
+ "//org.eclipse.jgit.ssh.apache.test:__pkg__",
+ "//org.eclipse.jgit.test:__pkg__",
+ ],
+ exports = ["@sshd-sftp//jar"],
+)
+
+java_library(
name = "javaewah",
visibility = ["//visibility:public"],
exports = ["@javaewah//jar"],
@@ -138,6 +168,18 @@ java_library(
)
java_library(
+ name = "mockito",
+ testonly = 1,
+ visibility = ["//visibility:public"],
+ exports = [
+ "@byte_buddy//jar",
+ "@byte_buddy_agent//jar",
+ "@mockito//jar",
+ "@objenesis//jar",
+ ],
+)
+
+java_library(
name = "servlet-api",
visibility = [
"//org.eclipse.jgit.http.apache:__pkg__",
diff --git a/org.eclipse.jgit.ant.test/.classpath b/org.eclipse.jgit.ant.test/.classpath
index eca7bdba8f..3e5654f17e 100644
--- a/org.eclipse.jgit.ant.test/.classpath
+++ b/org.eclipse.jgit.ant.test/.classpath
@@ -2,6 +2,10 @@
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
- <classpathentry kind="src" path="src"/>
+ <classpathentry kind="src" path="src">
+ <attributes>
+ <attribute name="test" value="true"/>
+ </attributes>
+ </classpathentry>
<classpathentry kind="output" path="bin"/>
</classpath>
diff --git a/org.eclipse.jgit.ant.test/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.ant.test/.settings/org.eclipse.jdt.core.prefs
index 794592dee1..2ca78ff2d0 100644
--- a/org.eclipse.jgit.ant.test/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.ant.test/.settings/org.eclipse.jdt.core.prefs
@@ -91,7 +91,7 @@ org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled
org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning
org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning
-org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=ignore
org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error
org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
diff --git a/org.eclipse.jgit.ant.test/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.ant.test/.settings/org.eclipse.mylyn.team.ui.prefs
index 0cba949fb7..2fca432276 100644
--- a/org.eclipse.jgit.ant.test/.settings/org.eclipse.mylyn.team.ui.prefs
+++ b/org.eclipse.jgit.ant.test/.settings/org.eclipse.mylyn.team.ui.prefs
@@ -1,3 +1,3 @@
#Tue Jul 19 20:11:28 CEST 2011
-commit.comment.template=${task.description} \n\nBug\: ${task.key}
+commit.comment.template=${task.description}\n\nBug\: ${task.key}
eclipse.preferences.version=1
diff --git a/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF
index 76770f3f58..ad6407874d 100644
--- a/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF
@@ -4,13 +4,13 @@ Bundle-ManifestVersion: 2
Bundle-Name: %plugin_name
Automatic-Module-Name: org.eclipse.jgit.ant.test
Bundle-SymbolicName: org.eclipse.jgit.ant.test
-Bundle-Version: 5.1.9.qualifier
+Bundle-Version: 5.2.3.qualifier
Bundle-ActivationPolicy: lazy
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Import-Package: org.apache.tools.ant,
- org.eclipse.jgit.ant.tasks;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.junit;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.lib;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.util;version="[5.1.9,5.2.0)",
+ org.eclipse.jgit.ant.tasks;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.junit;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.lib;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.util;version="[5.2.3,5.3.0)",
org.hamcrest.core;version="[1.1.0,2.0.0)",
org.junit;version="[4.12,5.0.0)"
diff --git a/org.eclipse.jgit.ant.test/pom.xml b/org.eclipse.jgit.ant.test/pom.xml
index 3c133833d6..d75acdba28 100644
--- a/org.eclipse.jgit.ant.test/pom.xml
+++ b/org.eclipse.jgit.ant.test/pom.xml
@@ -50,7 +50,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>5.1.9-SNAPSHOT</version>
+ <version>5.2.3-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.ant.test</artifactId>
@@ -105,7 +105,7 @@
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
- <argLine>@{argLine} -Xmx512m -Dfile.encoding=UTF-8 -Djava.io.tmpdir=${project.build.directory}</argLine>
+ <argLine>-Xmx256m -Dfile.encoding=UTF-8 -Djava.io.tmpdir=${project.build.directory}</argLine>
</configuration>
</plugin>
</plugins>
diff --git a/org.eclipse.jgit.ant/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.ant/.settings/org.eclipse.jdt.core.prefs
index 565b75c658..94a1c4f10b 100644
--- a/org.eclipse.jgit.ant/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.ant/.settings/org.eclipse.jdt.core.prefs
@@ -91,7 +91,7 @@ org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled
org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning
org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning
-org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=ignore
org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error
org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
diff --git a/org.eclipse.jgit.ant/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.ant/.settings/org.eclipse.mylyn.team.ui.prefs
index 0cba949fb7..2fca432276 100644
--- a/org.eclipse.jgit.ant/.settings/org.eclipse.mylyn.team.ui.prefs
+++ b/org.eclipse.jgit.ant/.settings/org.eclipse.mylyn.team.ui.prefs
@@ -1,3 +1,3 @@
#Tue Jul 19 20:11:28 CEST 2011
-commit.comment.template=${task.description} \n\nBug\: ${task.key}
+commit.comment.template=${task.description}\n\nBug\: ${task.key}
eclipse.preferences.version=1
diff --git a/org.eclipse.jgit.ant/META-INF/MANIFEST.MF b/org.eclipse.jgit.ant/META-INF/MANIFEST.MF
index 99ca3b2c85..f0bbb7ef95 100644
--- a/org.eclipse.jgit.ant/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ant/META-INF/MANIFEST.MF
@@ -3,11 +3,11 @@ Bundle-ManifestVersion: 2
Bundle-Name: %Bundle-Name
Automatic-Module-Name: org.eclipse.jgit.ant
Bundle-SymbolicName: org.eclipse.jgit.ant
-Bundle-Version: 5.1.9.qualifier
+Bundle-Version: 5.2.3.qualifier
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Import-Package: org.apache.tools.ant,
- org.eclipse.jgit.storage.file;version="[5.1.9,5.2.0)"
+ org.eclipse.jgit.storage.file;version="[5.2.3,5.3.0)"
Bundle-Localization: plugin
Bundle-Vendor: %Provider-Name
-Export-Package: org.eclipse.jgit.ant.tasks;version="5.1.9";
+Export-Package: org.eclipse.jgit.ant.tasks;version="5.2.3";
uses:="org.apache.tools.ant.types,org.apache.tools.ant"
diff --git a/org.eclipse.jgit.ant/pom.xml b/org.eclipse.jgit.ant/pom.xml
index f12d4173de..f68a0152d7 100644
--- a/org.eclipse.jgit.ant/pom.xml
+++ b/org.eclipse.jgit.ant/pom.xml
@@ -48,7 +48,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>5.1.9-SNAPSHOT</version>
+ <version>5.2.3-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.ant</artifactId>
@@ -72,7 +72,7 @@
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant</artifactId>
- <version>1.9.6</version>
+ <version>1.10.5</version>
</dependency>
</dependencies>
diff --git a/org.eclipse.jgit.archive/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.archive/.settings/org.eclipse.jdt.core.prefs
index 13c32a6d94..ef6f5e732f 100644
--- a/org.eclipse.jgit.archive/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.archive/.settings/org.eclipse.jdt.core.prefs
@@ -91,7 +91,7 @@ org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled
org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning
org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning
-org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=ignore
org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error
org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
diff --git a/org.eclipse.jgit.archive/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.archive/.settings/org.eclipse.mylyn.team.ui.prefs
index 0cba949fb7..2fca432276 100644
--- a/org.eclipse.jgit.archive/.settings/org.eclipse.mylyn.team.ui.prefs
+++ b/org.eclipse.jgit.archive/.settings/org.eclipse.mylyn.team.ui.prefs
@@ -1,3 +1,3 @@
#Tue Jul 19 20:11:28 CEST 2011
-commit.comment.template=${task.description} \n\nBug\: ${task.key}
+commit.comment.template=${task.description}\n\nBug\: ${task.key}
eclipse.preferences.version=1
diff --git a/org.eclipse.jgit.archive/META-INF/MANIFEST.MF b/org.eclipse.jgit.archive/META-INF/MANIFEST.MF
index d1ed4da662..8c854b8d93 100644
--- a/org.eclipse.jgit.archive/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.archive/META-INF/MANIFEST.MF
@@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2
Bundle-Name: %plugin_name
Automatic-Module-Name: org.eclipse.jgit.archive
Bundle-SymbolicName: org.eclipse.jgit.archive
-Bundle-Version: 5.1.9.qualifier
+Bundle-Version: 5.2.3.qualifier
Bundle-Vendor: %provider_name
Bundle-Localization: plugin
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
@@ -13,15 +13,15 @@ Import-Package: org.apache.commons.compress.archivers;version="[1.4,2.0)",
org.apache.commons.compress.compressors.bzip2;version="[1.4,2.0)",
org.apache.commons.compress.compressors.gzip;version="[1.4,2.0)",
org.apache.commons.compress.compressors.xz;version="[1.4,2.0)",
- org.eclipse.jgit.api;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.lib;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.nls;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.revwalk;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.util;version="[5.1.9,5.2.0)",
+ org.eclipse.jgit.api;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.lib;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.nls;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.revwalk;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.util;version="[5.2.3,5.3.0)",
org.osgi.framework;version="[1.3.0,2.0.0)"
Bundle-ActivationPolicy: lazy
Bundle-Activator: org.eclipse.jgit.archive.FormatActivator
-Export-Package: org.eclipse.jgit.archive;version="5.1.9";
+Export-Package: org.eclipse.jgit.archive;version="5.2.3";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.api,
org.apache.commons.compress.archivers,
diff --git a/org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF
index 5c19758097..aab58a6300 100644
--- a/org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@ Bundle-ManifestVersion: 2
Bundle-Name: org.eclipse.jgit.archive - Sources
Bundle-SymbolicName: org.eclipse.jgit.archive.source
Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 5.1.9.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.archive;version="5.1.9.qualifier";roots="."
+Bundle-Version: 5.2.3.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.archive;version="5.2.3.qualifier";roots="."
diff --git a/org.eclipse.jgit.archive/pom.xml b/org.eclipse.jgit.archive/pom.xml
index cb1e66b08a..f36a3a6587 100644
--- a/org.eclipse.jgit.archive/pom.xml
+++ b/org.eclipse.jgit.archive/pom.xml
@@ -50,7 +50,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>5.1.9-SNAPSHOT</version>
+ <version>5.2.3-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.archive</artifactId>
diff --git a/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/TarFormat.java b/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/TarFormat.java
index 9ed60d941a..aee80d8307 100644
--- a/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/TarFormat.java
+++ b/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/TarFormat.java
@@ -42,7 +42,7 @@
*/
package org.eclipse.jgit.archive;
-import static org.eclipse.jgit.lib.Constants.CHARACTER_ENCODING;
+import static java.nio.charset.StandardCharsets.UTF_8;
import java.io.IOException;
import java.io.OutputStream;
@@ -85,7 +85,7 @@ public final class TarFormat extends BaseFormat implements
public ArchiveOutputStream createArchiveOutputStream(OutputStream s,
Map<String, Object> o) throws IOException {
TarArchiveOutputStream out = new TarArchiveOutputStream(s,
- CHARACTER_ENCODING);
+ UTF_8.name());
out.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX);
out.setBigNumberMode(TarArchiveOutputStream.BIGNUMBER_POSIX);
return applyFormatOptions(out, o);
@@ -99,8 +99,7 @@ public final class TarFormat extends BaseFormat implements
if (mode == FileMode.SYMLINK) {
final TarArchiveEntry entry = new TarArchiveEntry(
path, TarConstants.LF_SYMLINK);
- entry.setLinkName(new String(
- loader.getCachedBytes(100), CHARACTER_ENCODING));
+ entry.setLinkName(new String(loader.getCachedBytes(100), UTF_8));
out.putArchiveEntry(entry);
out.closeArchiveEntry();
return;
diff --git a/org.eclipse.jgit.http.apache/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.http.apache/.settings/org.eclipse.jdt.core.prefs
index 565b75c658..94a1c4f10b 100644
--- a/org.eclipse.jgit.http.apache/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.http.apache/.settings/org.eclipse.jdt.core.prefs
@@ -91,7 +91,7 @@ org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled
org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning
org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning
-org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=ignore
org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error
org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
diff --git a/org.eclipse.jgit.http.apache/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.http.apache/.settings/org.eclipse.mylyn.team.ui.prefs
index 0cba949fb7..2fca432276 100644
--- a/org.eclipse.jgit.http.apache/.settings/org.eclipse.mylyn.team.ui.prefs
+++ b/org.eclipse.jgit.http.apache/.settings/org.eclipse.mylyn.team.ui.prefs
@@ -1,3 +1,3 @@
#Tue Jul 19 20:11:28 CEST 2011
-commit.comment.template=${task.description} \n\nBug\: ${task.key}
+commit.comment.template=${task.description}\n\nBug\: ${task.key}
eclipse.preferences.version=1
diff --git a/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF
index 5177a604c9..fb3dec75ca 100644
--- a/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF
@@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2
Bundle-Name: %Bundle-Name
Automatic-Module-Name: org.eclipse.jgit.http.apache
Bundle-SymbolicName: org.eclipse.jgit.http.apache
-Bundle-Version: 5.1.9.qualifier
+Bundle-Version: 5.2.3.qualifier
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Bundle-Localization: plugin
Bundle-Vendor: %Provider-Name
@@ -23,10 +23,11 @@ Import-Package: org.apache.http;version="[4.3.0,5.0.0)",
org.apache.http.impl.client;version="[4.3.0,5.0.0)",
org.apache.http.impl.conn;version="[4.3.0,5.0.0)",
org.apache.http.params;version="[4.3.0,5.0.0)",
- org.eclipse.jgit.nls;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.transport.http;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.util;version="[5.1.9,5.2.0)"
-Export-Package: org.eclipse.jgit.transport.http.apache;version="5.1.9";
+ org.eclipse.jgit.annotations;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.nls;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.transport.http;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.util;version="[5.2.3,5.3.0)"
+Export-Package: org.eclipse.jgit.transport.http.apache;version="5.2.3";
uses:="org.apache.http.client,
org.eclipse.jgit.transport.http,
org.apache.http.entity,
diff --git a/org.eclipse.jgit.http.apache/pom.xml b/org.eclipse.jgit.http.apache/pom.xml
index b9e11f7e97..a1cb74c008 100644
--- a/org.eclipse.jgit.http.apache/pom.xml
+++ b/org.eclipse.jgit.http.apache/pom.xml
@@ -48,7 +48,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>5.1.9-SNAPSHOT</version>
+ <version>5.2.3-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.http.apache</artifactId>
diff --git a/org.eclipse.jgit.http.apache/src/org/eclipse/jgit/transport/http/apache/HttpClientConnection.java b/org.eclipse.jgit.http.apache/src/org/eclipse/jgit/transport/http/apache/HttpClientConnection.java
index 77c5dc0f3e..4ac81a54df 100644
--- a/org.eclipse.jgit.http.apache/src/org/eclipse/jgit/transport/http/apache/HttpClientConnection.java
+++ b/org.eclipse.jgit.http.apache/src/org/eclipse/jgit/transport/http/apache/HttpClientConnection.java
@@ -58,10 +58,13 @@ import java.net.URL;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.stream.Collectors;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.KeyManager;
@@ -90,6 +93,7 @@ import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.BasicHttpClientConnectionManager;
+import org.eclipse.jgit.annotations.NonNull;
import org.eclipse.jgit.transport.http.HttpConnection;
import org.eclipse.jgit.transport.http.apache.internal.HttpApacheText;
import org.eclipse.jgit.util.TemporaryBuffer;
@@ -347,11 +351,17 @@ public class HttpClientConnection implements HttpConnection {
// will return only the first field
/** {@inheritDoc} */
@Override
- public String getHeaderField(String name) {
+ public String getHeaderField(@NonNull String name) {
Header header = resp.getFirstHeader(name);
return (header == null) ? null : header.getValue();
}
+ @Override
+ public List<String> getHeaderFields(@NonNull String name) {
+ return Collections.unmodifiableList(Arrays.asList(resp.getHeaders(name))
+ .stream().map(Header::getValue).collect(Collectors.toList()));
+ }
+
/** {@inheritDoc} */
@Override
public int getContentLength() {
diff --git a/org.eclipse.jgit.http.server/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.http.server/.settings/org.eclipse.jdt.core.prefs
index 565b75c658..94a1c4f10b 100644
--- a/org.eclipse.jgit.http.server/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.http.server/.settings/org.eclipse.jdt.core.prefs
@@ -91,7 +91,7 @@ org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled
org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning
org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning
-org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=ignore
org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error
org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
diff --git a/org.eclipse.jgit.http.server/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.http.server/.settings/org.eclipse.mylyn.team.ui.prefs
index 0cba949fb7..2fca432276 100644
--- a/org.eclipse.jgit.http.server/.settings/org.eclipse.mylyn.team.ui.prefs
+++ b/org.eclipse.jgit.http.server/.settings/org.eclipse.mylyn.team.ui.prefs
@@ -1,3 +1,3 @@
#Tue Jul 19 20:11:28 CEST 2011
-commit.comment.template=${task.description} \n\nBug\: ${task.key}
+commit.comment.template=${task.description}\n\nBug\: ${task.key}
eclipse.preferences.version=1
diff --git a/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF
index dd469806e2..b65ee844d5 100644
--- a/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF
@@ -3,13 +3,13 @@ Bundle-ManifestVersion: 2
Bundle-Name: %plugin_name
Automatic-Module-Name: org.eclipse.jgit.http.server
Bundle-SymbolicName: org.eclipse.jgit.http.server
-Bundle-Version: 5.1.9.qualifier
+Bundle-Version: 5.2.3.qualifier
Bundle-Localization: plugin
Bundle-Vendor: %provider_name
-Export-Package: org.eclipse.jgit.http.server;version="5.1.9",
- org.eclipse.jgit.http.server.glue;version="5.1.9";
+Export-Package: org.eclipse.jgit.http.server;version="5.2.3",
+ org.eclipse.jgit.http.server.glue;version="5.2.3";
uses:="javax.servlet,javax.servlet.http",
- org.eclipse.jgit.http.server.resolver;version="5.1.9";
+ org.eclipse.jgit.http.server.resolver;version="5.2.3";
uses:="org.eclipse.jgit.transport.resolver,
org.eclipse.jgit.lib,
org.eclipse.jgit.transport,
@@ -18,12 +18,13 @@ Bundle-ActivationPolicy: lazy
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Import-Package: javax.servlet;version="[2.5.0,3.2.0)",
javax.servlet.http;version="[2.5.0,3.2.0)",
- org.eclipse.jgit.errors;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.internal.storage.dfs;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.internal.storage.file;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.lib;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.nls;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.revwalk;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.transport;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.transport.resolver;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.util;version="[5.1.9,5.2.0)"
+ org.eclipse.jgit.errors;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.internal.storage.dfs;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.internal.storage.file;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.internal.transport.parser;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.lib;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.nls;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.revwalk;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.transport;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.transport.resolver;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.util;version="[5.2.3,5.3.0)"
diff --git a/org.eclipse.jgit.http.server/pom.xml b/org.eclipse.jgit.http.server/pom.xml
index 472c942507..78191ef3cf 100644
--- a/org.eclipse.jgit.http.server/pom.xml
+++ b/org.eclipse.jgit.http.server/pom.xml
@@ -52,7 +52,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>5.1.9-SNAPSHOT</version>
+ <version>5.2.3-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.http.server</artifactId>
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ClientVersionUtil.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ClientVersionUtil.java
index 38a9ea75e7..18b9b2a484 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ClientVersionUtil.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ClientVersionUtil.java
@@ -43,18 +43,12 @@
package org.eclipse.jgit.http.server;
-import static org.eclipse.jgit.http.server.ServletUtils.isChunked;
-
import javax.servlet.http.HttpServletRequest;
/**
* Parses Git client User-Agent strings.
*/
public class ClientVersionUtil {
- private static final int[] v1_7_5 = { 1, 7, 5 };
- private static final int[] v1_7_8_6 = { 1, 7, 8, 6 };
- private static final int[] v1_7_9 = { 1, 7, 9 };
-
/**
* An invalid version of Git
*
@@ -174,17 +168,11 @@ public class ClientVersionUtil {
* @param version
* parsed version of the Git client software.
* @return true if the bug is present.
+ * @deprecated no widely used Git versions need this any more
*/
+ @Deprecated
public static boolean hasPushStatusBug(int[] version) {
- int cmp = compare(version, v1_7_8_6);
- if (cmp < 0)
- return true; // Everything before 1.7.8.6 is known broken.
- else if (cmp == 0)
- return false; // 1.7.8.6 contained the bug fix.
-
- if (compare(version, v1_7_9) <= 0)
- return true; // 1.7.9 shipped before 1.7.8.6 and has the bug.
- return false; // 1.7.9.1 and later are fixed.
+ return false;
}
/**
@@ -198,10 +186,12 @@ public class ClientVersionUtil {
* @param request
* incoming HTTP request.
* @return true if the client has the chunked encoding bug.
+ * @deprecated no widely used Git versions need this any more
*/
+ @Deprecated
public static boolean hasChunkedEncodingRequestBug(
int[] version, HttpServletRequest request) {
- return compare(version, v1_7_5) == 0 && isChunked(request);
+ return false;
}
private ClientVersionUtil() {
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitSmartHttpTools.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitSmartHttpTools.java
index b9ca12bf3d..ee4b32efb7 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitSmartHttpTools.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitSmartHttpTools.java
@@ -63,6 +63,7 @@ import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import org.eclipse.jgit.internal.transport.parser.FirstWant;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.transport.PacketLineIn;
import org.eclipse.jgit.transport.PacketLineOut;
@@ -246,9 +247,9 @@ public class GitSmartHttpTools {
// not have an UploadPack, or it might not have read any of the request.
// So, cheat and read the first line.
String line = new PacketLineIn(req.getInputStream()).readString();
- UploadPack.FirstLine parsed = new UploadPack.FirstLine(line);
- return (parsed.getOptions().contains(OPTION_SIDE_BAND)
- || parsed.getOptions().contains(OPTION_SIDE_BAND_64K));
+ FirstWant parsed = FirstWant.fromLine(line);
+ return (parsed.getCapabilities().contains(OPTION_SIDE_BAND)
+ || parsed.getCapabilities().contains(OPTION_SIDE_BAND_64K));
} catch (IOException e) {
// Probably the connection is closed and a subsequent write will fail, but
// try it just in case.
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/InfoRefsServlet.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/InfoRefsServlet.java
index 1a9d192450..b084b0db54 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/InfoRefsServlet.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/InfoRefsServlet.java
@@ -69,7 +69,7 @@ class InfoRefsServlet extends HttpServlet {
// Assume a dumb client and send back the dumb client
// version of the info/refs file.
rsp.setContentType(HttpSupport.TEXT_PLAIN);
- rsp.setCharacterEncoding(Constants.CHARACTER_ENCODING);
+ rsp.setCharacterEncoding(UTF_8.name());
final Repository db = getRepository(req);
try (OutputStreamWriter out = new OutputStreamWriter(
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ReceivePackServlet.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ReceivePackServlet.java
index a46652ee42..aed36560a8 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ReceivePackServlet.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ReceivePackServlet.java
@@ -43,14 +43,10 @@
package org.eclipse.jgit.http.server;
-import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN;
import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED;
import static javax.servlet.http.HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE;
-import static org.eclipse.jgit.http.server.ClientVersionUtil.hasChunkedEncodingRequestBug;
-import static org.eclipse.jgit.http.server.ClientVersionUtil.hasPushStatusBug;
-import static org.eclipse.jgit.http.server.ClientVersionUtil.parseVersion;
import static org.eclipse.jgit.http.server.GitSmartHttpTools.RECEIVE_PACK;
import static org.eclipse.jgit.http.server.GitSmartHttpTools.RECEIVE_PACK_REQUEST_TYPE;
import static org.eclipse.jgit.http.server.GitSmartHttpTools.RECEIVE_PACK_RESULT_TYPE;
@@ -174,13 +170,6 @@ class ReceivePackServlet extends HttpServlet {
return;
}
- int[] version = parseVersion(req.getHeader(HDR_USER_AGENT));
- if (hasChunkedEncodingRequestBug(version, req)) {
- GitSmartHttpTools.sendError(req, rsp, SC_BAD_REQUEST, "\n\n"
- + HttpServerText.get().clientHas175ChunkedEncodingBug);
- return;
- }
-
SmartOutputStream out = new SmartOutputStream(req, rsp, false) {
@Override
public void flush() throws IOException {
@@ -191,7 +180,6 @@ class ReceivePackServlet extends HttpServlet {
ReceivePack rp = (ReceivePack) req.getAttribute(ATTRIBUTE_HANDLER);
try {
rp.setBiDirectionalPipe(false);
- rp.setEchoCommandFailures(hasPushStatusBug(version));
rsp.setContentType(RECEIVE_PACK_RESULT_TYPE);
rp.receive(getInputStream(req), out, null);
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ServletUtils.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ServletUtils.java
index 9601c8cafa..b6d73b5591 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ServletUtils.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ServletUtils.java
@@ -43,6 +43,7 @@
package org.eclipse.jgit.http.server;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static org.eclipse.jgit.util.HttpSupport.ENCODING_GZIP;
import static org.eclipse.jgit.util.HttpSupport.ENCODING_X_GZIP;
import static org.eclipse.jgit.util.HttpSupport.HDR_ACCEPT_ENCODING;
@@ -191,9 +192,9 @@ public final class ServletUtils {
public static void sendPlainText(final String content,
final HttpServletRequest req, final HttpServletResponse rsp)
throws IOException {
- final byte[] raw = content.getBytes(Constants.CHARACTER_ENCODING);
+ final byte[] raw = content.getBytes(UTF_8);
rsp.setContentType(TEXT_PLAIN);
- rsp.setCharacterEncoding(Constants.CHARACTER_ENCODING);
+ rsp.setCharacterEncoding(UTF_8.name());
send(raw, req, rsp);
}
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/SmartOutputStream.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/SmartOutputStream.java
index ad5e8d479e..06bdce679d 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/SmartOutputStream.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/SmartOutputStream.java
@@ -105,6 +105,7 @@ class SmartOutputStream extends TemporaryBuffer {
// If output hasn't started yet, the entire thing fit into our
// buffer. Try to use a proper Content-Length header, and also
// deflate the response with gzip if it will be smaller.
+ @SuppressWarnings("resource")
TemporaryBuffer out = this;
if (256 < out.length() && acceptsGzipEncoding(req)) {
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackServlet.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackServlet.java
index ca6b749e75..0f4037144a 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackServlet.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackServlet.java
@@ -43,13 +43,10 @@
package org.eclipse.jgit.http.server;
-import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN;
import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED;
import static javax.servlet.http.HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE;
-import static org.eclipse.jgit.http.server.ClientVersionUtil.hasChunkedEncodingRequestBug;
-import static org.eclipse.jgit.http.server.ClientVersionUtil.parseVersion;
import static org.eclipse.jgit.http.server.GitSmartHttpTools.UPLOAD_PACK;
import static org.eclipse.jgit.http.server.GitSmartHttpTools.UPLOAD_PACK_REQUEST_TYPE;
import static org.eclipse.jgit.http.server.GitSmartHttpTools.UPLOAD_PACK_RESULT_TYPE;
@@ -193,13 +190,6 @@ class UploadPackServlet extends HttpServlet {
return;
}
- int[] version = parseVersion(req.getHeader(HDR_USER_AGENT));
- if (hasChunkedEncodingRequestBug(version, req)) {
- GitSmartHttpTools.sendError(req, rsp, SC_BAD_REQUEST, "\n\n"
- + HttpServerText.get().clientHas175ChunkedEncodingBug);
- return;
- }
-
SmartOutputStream out = new SmartOutputStream(req, rsp, false) {
@Override
public void flush() throws IOException {
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/ServletBinder.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/ServletBinder.java
index b2b4748989..2a03633580 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/ServletBinder.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/ServletBinder.java
@@ -57,7 +57,7 @@ public interface ServletBinder {
* the filter to trigger while processing the path.
* @return {@code this}.
*/
- public ServletBinder through(Filter filter);
+ ServletBinder through(Filter filter);
/**
* Set the servlet to execute on this path
@@ -65,5 +65,5 @@ public interface ServletBinder {
* @param servlet
* the servlet to execute on this path.
*/
- public void with(HttpServlet servlet);
+ void with(HttpServlet servlet);
}
diff --git a/org.eclipse.jgit.http.test/.classpath b/org.eclipse.jgit.http.test/.classpath
index e6014c7122..b25838024a 100644
--- a/org.eclipse.jgit.http.test/.classpath
+++ b/org.eclipse.jgit.http.test/.classpath
@@ -2,7 +2,15 @@
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
- <classpathentry kind="src" path="tst"/>
- <classpathentry kind="src" path="src"/>
+ <classpathentry kind="src" path="tst">
+ <attributes>
+ <attribute name="test" value="true"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry kind="src" path="src">
+ <attributes>
+ <attribute name="test" value="true"/>
+ </attributes>
+ </classpathentry>
<classpathentry kind="output" path="bin"/>
</classpath>
diff --git a/org.eclipse.jgit.http.test/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.http.test/.settings/org.eclipse.jdt.core.prefs
index 794592dee1..2ca78ff2d0 100644
--- a/org.eclipse.jgit.http.test/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.http.test/.settings/org.eclipse.jdt.core.prefs
@@ -91,7 +91,7 @@ org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled
org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning
org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning
-org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=ignore
org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error
org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
diff --git a/org.eclipse.jgit.http.test/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.http.test/.settings/org.eclipse.mylyn.team.ui.prefs
index 0cba949fb7..2fca432276 100644
--- a/org.eclipse.jgit.http.test/.settings/org.eclipse.mylyn.team.ui.prefs
+++ b/org.eclipse.jgit.http.test/.settings/org.eclipse.mylyn.team.ui.prefs
@@ -1,3 +1,3 @@
#Tue Jul 19 20:11:28 CEST 2011
-commit.comment.template=${task.description} \n\nBug\: ${task.key}
+commit.comment.template=${task.description}\n\nBug\: ${task.key}
eclipse.preferences.version=1
diff --git a/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF
index 4b8bff81b6..495d36d6b5 100644
--- a/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF
@@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2
Bundle-Name: %plugin_name
Automatic-Module-Name: org.eclipse.jgit.http.test
Bundle-SymbolicName: org.eclipse.jgit.http.test
-Bundle-Version: 5.1.9.qualifier
+Bundle-Version: 5.2.3.qualifier
Bundle-Vendor: %provider_name
Bundle-Localization: plugin
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
@@ -28,25 +28,25 @@ Import-Package: javax.servlet;version="[2.5.0,3.2.0)",
org.eclipse.jetty.util.log;version="[9.4.5,10.0.0)",
org.eclipse.jetty.util.security;version="[9.4.5,10.0.0)",
org.eclipse.jetty.util.thread;version="[9.4.5,10.0.0)",
- org.eclipse.jgit.errors;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.http.server;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.http.server.glue;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.http.server.resolver;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.internal;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.internal.storage.dfs;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.internal.storage.file;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.internal.storage.reftable;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.junit;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.junit.http;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.lib;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.nls;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.revwalk;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.storage.file;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.transport;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.transport.http;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.transport.http.apache;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.transport.resolver;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.util;version="[5.1.9,5.2.0)",
+ org.eclipse.jgit.errors;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.http.server;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.http.server.glue;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.http.server.resolver;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.internal;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.internal.storage.dfs;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.internal.storage.file;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.internal.storage.reftable;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.junit;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.junit.http;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.lib;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.nls;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.revwalk;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.storage.file;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.transport;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.transport.http;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.transport.http.apache;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.transport.resolver;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.util;version="[5.2.3,5.3.0)",
org.hamcrest;version="[1.1.0,2.0.0)",
org.hamcrest.core;version="[1.1.0,2.0.0)",
org.junit;version="[4.12,5.0.0)",
diff --git a/org.eclipse.jgit.http.test/pom.xml b/org.eclipse.jgit.http.test/pom.xml
index fa0d8ea217..e8e697e1ac 100644
--- a/org.eclipse.jgit.http.test/pom.xml
+++ b/org.eclipse.jgit.http.test/pom.xml
@@ -51,7 +51,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>5.1.9-SNAPSHOT</version>
+ <version>5.2.3-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.http.test</artifactId>
@@ -139,7 +139,7 @@
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
- <argLine>@{argLine} -Djava.io.tmpdir=${project.build.directory} -Xmx512m</argLine>
+ <argLine>-Djava.io.tmpdir=${project.build.directory} -Xmx300m</argLine>
<includes>
<include>**/*Test.java</include>
<include>**/*Tests.java</include>
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/server/ClientVersionUtilTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/server/ClientVersionUtilTest.java
index c7260e325b..9dca279912 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/server/ClientVersionUtilTest.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/server/ClientVersionUtilTest.java
@@ -43,11 +43,8 @@
package org.eclipse.jgit.http.server;
-import static org.eclipse.jgit.http.server.ClientVersionUtil.hasPushStatusBug;
import static org.eclipse.jgit.http.server.ClientVersionUtil.invalidVersion;
import static org.eclipse.jgit.http.server.ClientVersionUtil.parseVersion;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
import org.junit.Assert;
import org.junit.Test;
@@ -67,18 +64,6 @@ public class ClientVersionUtilTest {
assertEquals(ClientVersionUtil.toString(invalidVersion()), parseVersion("foo"));
}
- @Test
- public void testPushStatusBug() {
- assertTrue(hasPushStatusBug(parseVersion("git/1.6.6")));
- assertTrue(hasPushStatusBug(parseVersion("git/1.6.6.1")));
- assertTrue(hasPushStatusBug(parseVersion("git/1.7.9")));
-
- assertFalse(hasPushStatusBug(parseVersion("git/1.7.8.6")));
- assertFalse(hasPushStatusBug(parseVersion("git/1.7.9.1")));
- assertFalse(hasPushStatusBug(parseVersion("git/1.7.9.2")));
- assertFalse(hasPushStatusBug(parseVersion("git/1.7.10")));
- }
-
private static void assertEquals(String exp, int[] act) {
Assert.assertEquals(exp, ClientVersionUtil.toString(act));
}
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/FileResolverTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/FileResolverTest.java
index 82e79b8e50..553c340a73 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/FileResolverTest.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/FileResolverTest.java
@@ -147,6 +147,7 @@ public class FileResolverTest extends LocalDiskRepositoryTestCase {
try {
resolver.open(null, name);
+ fail("opened non-git repository");
} catch (RepositoryNotFoundException e) {
assertEquals("repository not found: " + name, e.getMessage());
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/RegexPipelineTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/RegexPipelineTest.java
index 725a5902df..59c2b4e677 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/RegexPipelineTest.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/RegexPipelineTest.java
@@ -43,11 +43,14 @@
package org.eclipse.jgit.http.test;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertEquals;
import java.io.BufferedReader;
+import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.URI;
@@ -82,7 +85,8 @@ public class RegexPipelineTest extends HttpTestCase {
protected void doGet(HttpServletRequest req, HttpServletResponse res)
throws IOException {
res.setStatus(200);
- PrintWriter out = new PrintWriter(res.getOutputStream());
+ PrintWriter out = new PrintWriter(new BufferedWriter(
+ new OutputStreamWriter(res.getOutputStream(), UTF_8)));
out.write(name);
out.write("\n");
out.write(String.valueOf(req.getServletPath()));
@@ -120,7 +124,8 @@ public class RegexPipelineTest extends HttpTestCase {
c = ((HttpURLConnection) uri.resolve("/a").toURL()
.openConnection());
assertEquals(200, c.getResponseCode());
- r = new BufferedReader(new InputStreamReader(c.getInputStream()));
+ r = new BufferedReader(
+ new InputStreamReader(c.getInputStream(), UTF_8));
assertEquals("test", r.readLine());
assertEquals("", r.readLine());
assertEquals("/a", r.readLine());
@@ -129,7 +134,8 @@ public class RegexPipelineTest extends HttpTestCase {
c = ((HttpURLConnection) uri.resolve("/b").toURL()
.openConnection());
assertEquals(200, c.getResponseCode());
- r = new BufferedReader(new InputStreamReader(c.getInputStream()));
+ r = new BufferedReader(
+ new InputStreamReader(c.getInputStream(), UTF_8));
assertEquals("test", r.readLine());
assertEquals("", r.readLine());
assertEquals("/b", r.readLine());
@@ -155,7 +161,8 @@ public class RegexPipelineTest extends HttpTestCase {
c = ((HttpURLConnection) uri.resolve("/a").toURL()
.openConnection());
assertEquals(200, c.getResponseCode());
- r = new BufferedReader(new InputStreamReader(c.getInputStream()));
+ r = new BufferedReader(
+ new InputStreamReader(c.getInputStream(), UTF_8));
assertEquals("test1", r.readLine());
assertEquals("", r.readLine());
assertEquals("/a", r.readLine());
@@ -183,7 +190,8 @@ public class RegexPipelineTest extends HttpTestCase {
c = ((HttpURLConnection) uri.resolve("/a/b").toURL()
.openConnection());
assertEquals(200, c.getResponseCode());
- r = new BufferedReader(new InputStreamReader(c.getInputStream()));
+ r = new BufferedReader(
+ new InputStreamReader(c.getInputStream(), UTF_8));
assertEquals("test1", r.readLine());
assertEquals("", r.readLine());
// No RegexGroupFilter defaults to first group.
@@ -193,7 +201,8 @@ public class RegexPipelineTest extends HttpTestCase {
c = ((HttpURLConnection) uri.resolve("/c/d").toURL()
.openConnection());
assertEquals(200, c.getResponseCode());
- r = new BufferedReader(new InputStreamReader(c.getInputStream()));
+ r = new BufferedReader(
+ new InputStreamReader(c.getInputStream(), UTF_8));
assertEquals("test2", r.readLine());
assertEquals("", r.readLine());
assertEquals("/c", r.readLine());
@@ -202,7 +211,8 @@ public class RegexPipelineTest extends HttpTestCase {
c = ((HttpURLConnection) uri.resolve("/e/f/g").toURL()
.openConnection());
assertEquals(200, c.getResponseCode());
- r = new BufferedReader(new InputStreamReader(c.getInputStream()));
+ r = new BufferedReader(
+ new InputStreamReader(c.getInputStream(), UTF_8));
assertEquals("test3", r.readLine());
assertEquals("/e/f", r.readLine());
assertEquals("/g", r.readLine());
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java
index 2b4a2511ed..b26324d4f2 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java
@@ -298,7 +298,7 @@ public class SmartClientSmartServerTest extends HttpTestCase {
throws IOException, ServletException {
final HttpServletResponse r = (HttpServletResponse) response;
r.setContentType("text/plain");
- r.setCharacterEncoding(Constants.CHARACTER_ENCODING);
+ r.setCharacterEncoding(UTF_8.name());
try (PrintWriter w = r.getWriter()) {
w.print("OK");
}
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/transport/http/apache/HttpClientConnectionTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/transport/http/apache/HttpClientConnectionTest.java
index 72c4921de6..8a0d59c748 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/transport/http/apache/HttpClientConnectionTest.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/transport/http/apache/HttpClientConnectionTest.java
@@ -76,7 +76,7 @@ public class HttpClientConnectionTest {
assertTrue(headerValues.contains("NTLM"));
}
- private class HttpResponseMock extends AbstractHttpMessage
+ private static class HttpResponseMock extends AbstractHttpMessage
implements HttpResponse {
@Override
public StatusLine getStatusLine() {
diff --git a/org.eclipse.jgit.junit.http/.classpath b/org.eclipse.jgit.junit.http/.classpath
index b862a296d3..3e5654f17e 100644
--- a/org.eclipse.jgit.junit.http/.classpath
+++ b/org.eclipse.jgit.junit.http/.classpath
@@ -1,7 +1,11 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
- <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
- <classpathentry kind="src" path="src"/>
- <classpathentry kind="output" path="bin"/>
-</classpath>
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src">
+ <attributes>
+ <attribute name="test" value="true"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/org.eclipse.jgit.junit.http/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.junit.http/.settings/org.eclipse.jdt.core.prefs
index 794592dee1..2ca78ff2d0 100644
--- a/org.eclipse.jgit.junit.http/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.junit.http/.settings/org.eclipse.jdt.core.prefs
@@ -91,7 +91,7 @@ org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled
org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning
org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning
-org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=ignore
org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error
org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
diff --git a/org.eclipse.jgit.junit.http/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.junit.http/.settings/org.eclipse.mylyn.team.ui.prefs
index 0cba949fb7..2fca432276 100644
--- a/org.eclipse.jgit.junit.http/.settings/org.eclipse.mylyn.team.ui.prefs
+++ b/org.eclipse.jgit.junit.http/.settings/org.eclipse.mylyn.team.ui.prefs
@@ -1,3 +1,3 @@
#Tue Jul 19 20:11:28 CEST 2011
-commit.comment.template=${task.description} \n\nBug\: ${task.key}
+commit.comment.template=${task.description}\n\nBug\: ${task.key}
eclipse.preferences.version=1
diff --git a/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF
index 8d6357e792..8432c451bc 100644
--- a/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF
@@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2
Bundle-Name: %plugin_name
Automatic-Module-Name: org.eclipse.jgit.junit.http
Bundle-SymbolicName: org.eclipse.jgit.junit.http
-Bundle-Version: 5.1.9.qualifier
+Bundle-Version: 5.2.3.qualifier
Bundle-Localization: plugin
Bundle-Vendor: %provider_name
Bundle-ActivationPolicy: lazy
@@ -22,16 +22,16 @@ Import-Package: javax.servlet;version="[2.5.0,3.2.0)",
org.eclipse.jetty.util.log;version="[9.4.5,10.0.0)",
org.eclipse.jetty.util.security;version="[9.4.5,10.0.0)",
org.eclipse.jetty.util.ssl;version="[9.4.5,10.0.0)",
- org.eclipse.jgit.errors;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.http.server;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.internal.storage.file;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.junit;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.lib;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.revwalk;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.transport;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.transport.resolver;version="[5.1.9,5.2.0)",
+ org.eclipse.jgit.errors;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.http.server;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.internal.storage.file;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.junit;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.lib;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.revwalk;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.transport;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.transport.resolver;version="[5.2.3,5.3.0)",
org.junit;version="[4.12,5.0.0)"
-Export-Package: org.eclipse.jgit.junit.http;version="5.1.9";
+Export-Package: org.eclipse.jgit.junit.http;version="5.2.3";
uses:="org.eclipse.jgit.transport,
org.eclipse.jgit.junit,
javax.servlet.http,
diff --git a/org.eclipse.jgit.junit.http/pom.xml b/org.eclipse.jgit.junit.http/pom.xml
index cda2f66236..b766974442 100644
--- a/org.eclipse.jgit.junit.http/pom.xml
+++ b/org.eclipse.jgit.junit.http/pom.xml
@@ -50,7 +50,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>5.1.9-SNAPSHOT</version>
+ <version>5.2.3-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.junit.http</artifactId>
diff --git a/org.eclipse.jgit.junit.ssh/.classpath b/org.eclipse.jgit.junit.ssh/.classpath
new file mode 100644
index 0000000000..eca7bdba8f
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/org.eclipse.jgit.junit.ssh/.gitignore b/org.eclipse.jgit.junit.ssh/.gitignore
new file mode 100644
index 0000000000..934e0e06ff
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/.gitignore
@@ -0,0 +1,2 @@
+/bin
+/target
diff --git a/org.eclipse.jgit.junit.ssh/.project b/org.eclipse.jgit.junit.ssh/.project
new file mode 100644
index 0000000000..3a0c494155
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/.project
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.eclipse.jgit.junit.ssh</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.api.tools.apiAnalysisBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ <nature>org.eclipse.pde.api.tools.apiAnalysisNature</nature>
+ </natures>
+</projectDescription>
diff --git a/org.eclipse.jgit.junit.ssh/.settings/org.eclipse.core.resources.prefs b/org.eclipse.jgit.junit.ssh/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000000..f77db3b723
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,3 @@
+#Sat Dec 20 21:21:24 CET 2008
+eclipse.preferences.version=1
+encoding/<project>=UTF-8
diff --git a/org.eclipse.jgit.junit.ssh/.settings/org.eclipse.core.runtime.prefs b/org.eclipse.jgit.junit.ssh/.settings/org.eclipse.core.runtime.prefs
new file mode 100644
index 0000000000..9f733eeea7
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/.settings/org.eclipse.core.runtime.prefs
@@ -0,0 +1,3 @@
+#Mon Mar 24 18:55:56 EDT 2008
+eclipse.preferences.version=1
+line.separator=\n
diff --git a/org.eclipse.jgit.junit.ssh/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.junit.ssh/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000000..2ca78ff2d0
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,399 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=disabled
+org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore
+org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull
+org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault
+org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable
+org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.8
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.doc.comment.support=enabled
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.autoboxing=warning
+org.eclipse.jdt.core.compiler.problem.comparingIdentical=error
+org.eclipse.jdt.core.compiler.problem.deadCode=error
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
+org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=warning
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=error
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=error
+org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=enabled
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=warning
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=error
+org.eclipse.jdt.core.compiler.problem.invalidJavadoc=error
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=private
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=error
+org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled
+org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=error
+org.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=protected
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag
+org.eclipse.jdt.core.compiler.problem.missingJavadocTags=error
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
+org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=error
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=error
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
+org.eclipse.jdt.core.compiler.problem.nonnullParameterAnnotationDropped=warning
+org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error
+org.eclipse.jdt.core.compiler.problem.nullReference=error
+org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error
+org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=warning
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=error
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning
+org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=ignore
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=ignore
+org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning
+org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=warning
+org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=error
+org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
+org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=error
+org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=disabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
+org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=error
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.unusedExceptionParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedImport=error
+org.eclipse.jdt.core.compiler.problem.unusedLabel=error
+org.eclipse.jdt.core.compiler.problem.unusedLocal=error
+org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameter=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=error
+org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=error
+org.eclipse.jdt.core.compiler.source=1.8
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=0
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=1
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=false
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert
+org.eclipse.jdt.core.formatter.comment.line_length=80
+org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true
+org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true
+org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off
+org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.join_lines_in_comments=true
+org.eclipse.jdt.core.formatter.join_wrapped_lines=true
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=80
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
+org.eclipse.jdt.core.formatter.tabulation.char=tab
+org.eclipse.jdt.core.formatter.tabulation.size=4
+org.eclipse.jdt.core.formatter.use_on_off_tags=true
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true
+org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true
diff --git a/org.eclipse.jgit.junit.ssh/.settings/org.eclipse.jdt.ui.prefs b/org.eclipse.jgit.junit.ssh/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 0000000000..fef3713825
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,66 @@
+eclipse.preferences.version=1
+editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
+formatter_profile=_JGit Format
+formatter_settings_version=12
+org.eclipse.jdt.ui.ignorelowercasenames=true
+org.eclipse.jdt.ui.importorder=java;javax;org;com;
+org.eclipse.jdt.ui.ondemandthreshold=99
+org.eclipse.jdt.ui.staticondemandthreshold=99
+org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8"?><templates/>
+sp_cleanup.add_default_serial_version_id=true
+sp_cleanup.add_generated_serial_version_id=false
+sp_cleanup.add_missing_annotations=true
+sp_cleanup.add_missing_deprecated_annotations=true
+sp_cleanup.add_missing_methods=false
+sp_cleanup.add_missing_nls_tags=false
+sp_cleanup.add_missing_override_annotations=true
+sp_cleanup.add_missing_override_annotations_interface_methods=true
+sp_cleanup.add_serial_version_id=false
+sp_cleanup.always_use_blocks=true
+sp_cleanup.always_use_parentheses_in_expressions=false
+sp_cleanup.always_use_this_for_non_static_field_access=false
+sp_cleanup.always_use_this_for_non_static_method_access=false
+sp_cleanup.convert_functional_interfaces=false
+sp_cleanup.convert_to_enhanced_for_loop=false
+sp_cleanup.correct_indentation=false
+sp_cleanup.format_source_code=true
+sp_cleanup.format_source_code_changes_only=true
+sp_cleanup.insert_inferred_type_arguments=false
+sp_cleanup.make_local_variable_final=false
+sp_cleanup.make_parameters_final=false
+sp_cleanup.make_private_fields_final=true
+sp_cleanup.make_type_abstract_if_missing_method=false
+sp_cleanup.make_variable_declarations_final=false
+sp_cleanup.never_use_blocks=false
+sp_cleanup.never_use_parentheses_in_expressions=true
+sp_cleanup.on_save_use_additional_actions=true
+sp_cleanup.organize_imports=false
+sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
+sp_cleanup.remove_private_constructors=true
+sp_cleanup.remove_redundant_type_arguments=true
+sp_cleanup.remove_trailing_whitespaces=true
+sp_cleanup.remove_trailing_whitespaces_all=true
+sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
+sp_cleanup.remove_unnecessary_casts=true
+sp_cleanup.remove_unnecessary_nls_tags=true
+sp_cleanup.remove_unused_imports=false
+sp_cleanup.remove_unused_local_variables=false
+sp_cleanup.remove_unused_private_fields=true
+sp_cleanup.remove_unused_private_members=false
+sp_cleanup.remove_unused_private_methods=true
+sp_cleanup.remove_unused_private_types=true
+sp_cleanup.sort_members=false
+sp_cleanup.sort_members_all=false
+sp_cleanup.use_anonymous_class_creation=false
+sp_cleanup.use_blocks=false
+sp_cleanup.use_blocks_only_for_return_and_throw=false
+sp_cleanup.use_lambda=false
+sp_cleanup.use_parentheses_in_expressions=false
+sp_cleanup.use_this_for_non_static_field_access=false
+sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+sp_cleanup.use_this_for_non_static_method_access=false
+sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
diff --git a/org.eclipse.jgit.junit.ssh/.settings/org.eclipse.mylyn.tasks.ui.prefs b/org.eclipse.jgit.junit.ssh/.settings/org.eclipse.mylyn.tasks.ui.prefs
new file mode 100644
index 0000000000..823c0f56ae
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/.settings/org.eclipse.mylyn.tasks.ui.prefs
@@ -0,0 +1,4 @@
+#Tue Jul 19 20:11:28 CEST 2011
+eclipse.preferences.version=1
+project.repository.kind=bugzilla
+project.repository.url=https\://bugs.eclipse.org/bugs
diff --git a/org.eclipse.jgit.junit.ssh/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.junit.ssh/.settings/org.eclipse.mylyn.team.ui.prefs
new file mode 100644
index 0000000000..2fca432276
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/.settings/org.eclipse.mylyn.team.ui.prefs
@@ -0,0 +1,3 @@
+#Tue Jul 19 20:11:28 CEST 2011
+commit.comment.template=${task.description}\n\nBug\: ${task.key}
+eclipse.preferences.version=1
diff --git a/org.eclipse.jgit.junit.ssh/.settings/org.eclipse.pde.api.tools.prefs b/org.eclipse.jgit.junit.ssh/.settings/org.eclipse.pde.api.tools.prefs
new file mode 100644
index 0000000000..c0030ded71
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/.settings/org.eclipse.pde.api.tools.prefs
@@ -0,0 +1,104 @@
+ANNOTATION_ELEMENT_TYPE_ADDED_FIELD=Error
+ANNOTATION_ELEMENT_TYPE_ADDED_METHOD_WITHOUT_DEFAULT_VALUE=Error
+ANNOTATION_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_FIELD=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_METHOD=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_API_TYPE=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_REEXPORTED_API_TYPE=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_REEXPORTED_TYPE=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_TYPE=Error
+API_USE_SCAN_FIELD_SEVERITY=Error
+API_USE_SCAN_METHOD_SEVERITY=Error
+API_USE_SCAN_TYPE_SEVERITY=Error
+CLASS_ELEMENT_TYPE_ADDED_FIELD=Error
+CLASS_ELEMENT_TYPE_ADDED_METHOD=Error
+CLASS_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error
+CLASS_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+CLASS_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error
+CLASS_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+CLASS_ELEMENT_TYPE_CHANGED_NON_ABSTRACT_TO_ABSTRACT=Error
+CLASS_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
+CLASS_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
+CLASS_ELEMENT_TYPE_REMOVED_CONSTRUCTOR=Error
+CLASS_ELEMENT_TYPE_REMOVED_FIELD=Error
+CLASS_ELEMENT_TYPE_REMOVED_METHOD=Error
+CLASS_ELEMENT_TYPE_REMOVED_SUPERCLASS=Error
+CLASS_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+CLASS_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+CONSTRUCTOR_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+CONSTRUCTOR_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+CONSTRUCTOR_ELEMENT_TYPE_CHANGED_VARARGS_TO_ARRAY=Error
+CONSTRUCTOR_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+ENUM_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error
+ENUM_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
+ENUM_ELEMENT_TYPE_REMOVED_ENUM_CONSTANT=Error
+ENUM_ELEMENT_TYPE_REMOVED_FIELD=Error
+ENUM_ELEMENT_TYPE_REMOVED_METHOD=Error
+ENUM_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+FIELD_ELEMENT_TYPE_ADDED_VALUE=Error
+FIELD_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+FIELD_ELEMENT_TYPE_CHANGED_FINAL_TO_NON_FINAL_STATIC_CONSTANT=Error
+FIELD_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
+FIELD_ELEMENT_TYPE_CHANGED_NON_STATIC_TO_STATIC=Error
+FIELD_ELEMENT_TYPE_CHANGED_STATIC_TO_NON_STATIC=Error
+FIELD_ELEMENT_TYPE_CHANGED_TYPE=Error
+FIELD_ELEMENT_TYPE_CHANGED_VALUE=Error
+FIELD_ELEMENT_TYPE_REMOVED_TYPE_ARGUMENT=Error
+FIELD_ELEMENT_TYPE_REMOVED_VALUE=Error
+ILLEGAL_EXTEND=Warning
+ILLEGAL_IMPLEMENT=Warning
+ILLEGAL_INSTANTIATE=Warning
+ILLEGAL_OVERRIDE=Warning
+ILLEGAL_REFERENCE=Warning
+INTERFACE_ELEMENT_TYPE_ADDED_DEFAULT_METHOD=Error
+INTERFACE_ELEMENT_TYPE_ADDED_FIELD=Error
+INTERFACE_ELEMENT_TYPE_ADDED_METHOD=Error
+INTERFACE_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error
+INTERFACE_ELEMENT_TYPE_ADDED_SUPER_INTERFACE_WITH_METHODS=Error
+INTERFACE_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+INTERFACE_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error
+INTERFACE_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_FIELD=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_METHOD=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+INVALID_ANNOTATION=Ignore
+INVALID_JAVADOC_TAG=Ignore
+INVALID_REFERENCE_IN_SYSTEM_LIBRARIES=Error
+LEAK_EXTEND=Warning
+LEAK_FIELD_DECL=Warning
+LEAK_IMPLEMENT=Warning
+LEAK_METHOD_PARAM=Warning
+LEAK_METHOD_RETURN_TYPE=Warning
+METHOD_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error
+METHOD_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+METHOD_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+METHOD_ELEMENT_TYPE_CHANGED_NON_ABSTRACT_TO_ABSTRACT=Error
+METHOD_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
+METHOD_ELEMENT_TYPE_CHANGED_NON_STATIC_TO_STATIC=Error
+METHOD_ELEMENT_TYPE_CHANGED_STATIC_TO_NON_STATIC=Error
+METHOD_ELEMENT_TYPE_CHANGED_VARARGS_TO_ARRAY=Error
+METHOD_ELEMENT_TYPE_REMOVED_ANNOTATION_DEFAULT_VALUE=Error
+METHOD_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+MISSING_EE_DESCRIPTIONS=Warning
+TYPE_PARAMETER_ELEMENT_TYPE_ADDED_CLASS_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_ADDED_INTERFACE_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_CHANGED_CLASS_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_CHANGED_INTERFACE_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_REMOVED_CLASS_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_REMOVED_INTERFACE_BOUND=Error
+UNUSED_PROBLEM_FILTERS=Warning
+automatically_removed_unused_problem_filters=false
+changed_execution_env=Error
+eclipse.preferences.version=1
+incompatible_api_component_version=Error
+incompatible_api_component_version_include_major_without_breaking_change=Disabled
+incompatible_api_component_version_include_minor_without_api_change=Disabled
+incompatible_api_component_version_report_major_without_breaking_change=Warning
+incompatible_api_component_version_report_minor_without_api_change=Ignore
+invalid_since_tag_version=Error
+malformed_since_tag=Error
+missing_since_tag=Error
+report_api_breakage_when_major_version_incremented=Disabled
+report_resolution_errors_api_component=Warning
diff --git a/org.eclipse.jgit.junit.ssh/.settings/org.eclipse.pde.core.prefs b/org.eclipse.jgit.junit.ssh/.settings/org.eclipse.pde.core.prefs
new file mode 100644
index 0000000000..82793f2d27
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/.settings/org.eclipse.pde.core.prefs
@@ -0,0 +1,3 @@
+#Thu Jan 14 14:34:32 CST 2010
+eclipse.preferences.version=1
+resolve.requirebundle=false
diff --git a/org.eclipse.jgit.junit.ssh/BUILD b/org.eclipse.jgit.junit.ssh/BUILD
new file mode 100644
index 0000000000..e9a04c7173
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/BUILD
@@ -0,0 +1,15 @@
+package(default_visibility = ["//visibility:public"])
+
+java_library(
+ name = "junit-ssh",
+ testonly = 1,
+ srcs = glob(["src/**/*.java"]),
+ resource_strip_prefix = "org.eclipse.jgit.junit.ssh/resources",
+ resources = glob(["resources/**"]),
+ deps = [
+ "//lib:sshd-core",
+ "//lib:sshd-sftp",
+ # We want these deps to be provided_deps
+ "//org.eclipse.jgit:jgit",
+ ],
+)
diff --git a/org.eclipse.jgit.junit.ssh/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit.ssh/META-INF/MANIFEST.MF
new file mode 100644
index 0000000000..14674996cd
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/META-INF/MANIFEST.MF
@@ -0,0 +1,36 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %plugin_name
+Automatic-Module-Name: org.eclipse.jgit.junit.ssh
+Bundle-SymbolicName: org.eclipse.jgit.junit.ssh
+Bundle-Version: 5.2.3.qualifier
+Bundle-Localization: plugin
+Bundle-Vendor: %provider_name
+Bundle-ActivationPolicy: lazy
+Bundle-RequiredExecutionEnvironment: JavaSE-1.8
+Import-Package: org.apache.sshd.common;version="[2.0.0,2.1.0)",
+ org.apache.sshd.common.config.keys;version="[2.0.0,2.1.0)",
+ org.apache.sshd.common.file.virtualfs;version="[2.0.0,2.1.0)",
+ org.apache.sshd.common.helpers;version="[2.0.0,2.1.0)",
+ org.apache.sshd.common.io;version="[2.0.0,2.1.0)",
+ org.apache.sshd.common.kex;version="[2.0.0,2.1.0)",
+ org.apache.sshd.common.keyprovider;version="[2.0.0,2.1.0)",
+ org.apache.sshd.common.session;version="[2.0.0,2.1.0)",
+ org.apache.sshd.common.util.buffer;version="[2.0.0,2.1.0)",
+ org.apache.sshd.common.util.logging;version="[2.0.0,2.1.0)",
+ org.apache.sshd.common.util.security;version="[2.0.0,2.1.0)",
+ org.apache.sshd.server;version="[2.0.0,2.1.0)",
+ org.apache.sshd.server.auth;version="[2.0.0,2.1.0)",
+ org.apache.sshd.server.auth.gss;version="[2.0.0,2.1.0)",
+ org.apache.sshd.server.auth.keyboard;version="[2.0.0,2.1.0)",
+ org.apache.sshd.server.auth.password;version="[2.0.0,2.1.0)",
+ org.apache.sshd.server.command;version="[2.0.0,2.1.0)",
+ org.apache.sshd.server.session;version="[2.0.0,2.1.0)",
+ org.apache.sshd.server.shell;version="[2.0.0,2.1.0)",
+ org.apache.sshd.server.subsystem;version="[2.0.0,2.1.0)",
+ org.apache.sshd.server.subsystem.sftp;version="[2.0.0,2.1.0)",
+ org.eclipse.jgit.annotations;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.lib;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.transport;version="[5.2.3,5.3.0)",
+ org.slf4j;version="[1.7.0,2.0.0)"
+Export-Package: org.eclipse.jgit.junit.ssh;version="5.2.3"
diff --git a/org.eclipse.jgit.junit.ssh/build.properties b/org.eclipse.jgit.junit.ssh/build.properties
new file mode 100644
index 0000000000..aa1a008269
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/build.properties
@@ -0,0 +1,5 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ plugin.properties
diff --git a/org.eclipse.jgit.junit.ssh/plugin.properties b/org.eclipse.jgit.junit.ssh/plugin.properties
new file mode 100644
index 0000000000..5689b9472f
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/plugin.properties
@@ -0,0 +1,2 @@
+plugin_name=JGit JUnit Ssh Utility Classes
+provider_name=Eclipse JGit
diff --git a/org.eclipse.jgit.junit.ssh/pom.xml b/org.eclipse.jgit.junit.ssh/pom.xml
new file mode 100644
index 0000000000..4cd68eb255
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/pom.xml
@@ -0,0 +1,116 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2010, Jens Baumgart <jens.baumgart@sap.com>
+ and other copyright owners as documented in the project's IP log.
+
+ This program and the accompanying materials are made available
+ under the terms of the Eclipse Distribution License v1.0 which
+ accompanies this distribution, is reproduced below, and is
+ available at http://www.eclipse.org/org/documents/edl-v10.php
+
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or
+ without modification, are permitted provided that the following
+ conditions are met:
+
+ - Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ - Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+ - Neither the name of the Eclipse Foundation, Inc. nor the
+ names of its contributors may be used to endorse or promote
+ products derived from this software without specific prior
+ written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.eclipse.jgit</groupId>
+ <artifactId>org.eclipse.jgit-parent</artifactId>
+ <version>5.2.3-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>org.eclipse.jgit.junit.ssh</artifactId>
+ <name>JGit - JUnit Ssh Utility Classes</name>
+
+ <description>
+ Utility classes to support Ssh based JUnit testing of JGit applications.
+ </description>
+
+ <properties>
+ <translate-qualifier/>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.eclipse.jgit</groupId>
+ <artifactId>org.eclipse.jgit</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-core</artifactId>
+ <version>${apache-sshd-version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-sftp</artifactId>
+ <version>${apache-sshd-version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <sourceDirectory>src/</sourceDirectory>
+
+ <resources>
+ <resource>
+ <directory>.</directory>
+ <includes>
+ <include>plugin.properties</include>
+ </includes>
+ </resource>
+ </resources>
+
+ <plugins>
+ <plugin>
+ <artifactId>maven-jar-plugin</artifactId>
+ <configuration>
+ <archive>
+ <manifestFile>${bundle-manifest}</manifestFile>
+ </archive>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/org.eclipse.jgit.junit.ssh/src/org/eclipse/jgit/junit/ssh/SshTestGitServer.java b/org.eclipse.jgit.junit.ssh/src/org/eclipse/jgit/junit/ssh/SshTestGitServer.java
new file mode 100644
index 0000000000..f5af2e5ce1
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/src/org/eclipse/jgit/junit/ssh/SshTestGitServer.java
@@ -0,0 +1,392 @@
+/*
+ * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.junit.ssh;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.security.PublicKey;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.SshConstants;
+import org.apache.sshd.common.config.keys.AuthorizedKeyEntry;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.config.keys.PublicKeyEntryResolver;
+import org.apache.sshd.common.file.virtualfs.VirtualFileSystemFactory;
+import org.apache.sshd.common.session.Session;
+import org.apache.sshd.common.util.buffer.Buffer;
+import org.apache.sshd.common.util.security.SecurityUtils;
+import org.apache.sshd.server.ServerAuthenticationManager;
+import org.apache.sshd.server.SshServer;
+import org.apache.sshd.server.auth.UserAuth;
+import org.apache.sshd.server.auth.gss.GSSAuthenticator;
+import org.apache.sshd.server.auth.gss.UserAuthGSS;
+import org.apache.sshd.server.auth.gss.UserAuthGSSFactory;
+import org.apache.sshd.server.auth.keyboard.DefaultKeyboardInteractiveAuthenticator;
+import org.apache.sshd.server.command.AbstractCommandSupport;
+import org.apache.sshd.server.command.Command;
+import org.apache.sshd.server.session.ServerSession;
+import org.apache.sshd.server.shell.UnknownCommand;
+import org.apache.sshd.server.subsystem.sftp.SftpSubsystemFactory;
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.transport.ReceivePack;
+import org.eclipse.jgit.transport.RemoteConfig;
+import org.eclipse.jgit.transport.UploadPack;
+
+/**
+ * A simple ssh/sftp git <em>test</em> server based on Apache MINA sshd.
+ * <p>
+ * Supports only a single repository. Authenticates only the given test user
+ * against his given test public key. Supports fetch and push.
+ * </p>
+ *
+ * @since 5.2
+ */
+public class SshTestGitServer {
+
+ @NonNull
+ protected final String testUser;
+
+ @NonNull
+ protected final Repository repository;
+
+ @NonNull
+ protected final List<KeyPair> hostKeys = new ArrayList<>();
+
+ protected final SshServer server;
+
+ @NonNull
+ protected PublicKey testKey;
+
+ private final ExecutorService executorService = Executors
+ .newFixedThreadPool(2);
+
+ /**
+ * Creates a ssh git <em>test</em> server. It serves one single repository,
+ * and accepts public-key authentication for exactly one test user.
+ *
+ * @param testUser
+ * user name of the test user
+ * @param testKey
+ * <em>private</em> key file of the test user; the server will
+ * only user the public key from it
+ * @param repository
+ * to serve
+ * @param hostKey
+ * the unencrypted private key to use as host key
+ * @throws IOException
+ * @throws GeneralSecurityException
+ */
+ public SshTestGitServer(@NonNull String testUser, @NonNull Path testKey,
+ @NonNull Repository repository, @NonNull byte[] hostKey)
+ throws IOException, GeneralSecurityException {
+ this.testUser = testUser;
+ setTestUserPublicKey(testKey);
+ this.repository = repository;
+ server = SshServer.setUpDefaultServer();
+ // Set host key
+ try (ByteArrayInputStream in = new ByteArrayInputStream(hostKey)) {
+ hostKeys.add(SecurityUtils.loadKeyPairIdentity("", in, null));
+ } catch (IOException | GeneralSecurityException e) {
+ // Ignore.
+ }
+ server.setKeyPairProvider(() -> hostKeys);
+
+ configureAuthentication();
+
+ List<NamedFactory<Command>> subsystems = configureSubsystems();
+ if (!subsystems.isEmpty()) {
+ server.setSubsystemFactories(subsystems);
+ }
+
+ configureShell();
+
+ server.setCommandFactory(command -> {
+ if (command.startsWith(RemoteConfig.DEFAULT_UPLOAD_PACK)) {
+ return new GitUploadPackCommand(command, executorService);
+ } else if (command.startsWith(RemoteConfig.DEFAULT_RECEIVE_PACK)) {
+ return new GitReceivePackCommand(command, executorService);
+ }
+ return new UnknownCommand(command);
+ });
+ }
+
+ private static class FakeUserAuthGSS extends UserAuthGSS {
+ @Override
+ protected Boolean doAuth(Buffer buffer, boolean initial)
+ throws Exception {
+ // We always reply that we did do this, but then we fail at the
+ // first token message. That way we can test that the client-side
+ // sends the correct initial request and then is skipped correctly,
+ // even if it causes a GSSException if Kerberos isn't configured at
+ // all.
+ if (initial) {
+ ServerSession session = getServerSession();
+ Buffer b = session.createBuffer(
+ SshConstants.SSH_MSG_USERAUTH_INFO_REQUEST);
+ b.putBytes(KRB5_MECH.getDER());
+ session.writePacket(b);
+ return null;
+ }
+ return Boolean.FALSE;
+ }
+ }
+
+ private List<NamedFactory<UserAuth>> getAuthFactories() {
+ List<NamedFactory<UserAuth>> authentications = new ArrayList<>();
+ authentications.add(new UserAuthGSSFactory() {
+ @Override
+ public UserAuth create() {
+ return new FakeUserAuthGSS();
+ }
+ });
+ authentications.add(
+ ServerAuthenticationManager.DEFAULT_USER_AUTH_PUBLIC_KEY_FACTORY);
+ authentications.add(
+ ServerAuthenticationManager.DEFAULT_USER_AUTH_KB_INTERACTIVE_FACTORY);
+ authentications.add(
+ ServerAuthenticationManager.DEFAULT_USER_AUTH_PASSWORD_FACTORY);
+ return authentications;
+ }
+
+ /**
+ * Configures the authentication mechanisms of this test server. Invoked
+ * from the constructor. The default sets up public key authentication for
+ * the test user, and a gssapi-with-mic authenticator that pretends to
+ * support this mechanism, but that then refuses to authenticate anyone.
+ */
+ protected void configureAuthentication() {
+ server.setUserAuthFactories(getAuthFactories());
+ // Disable some authentications
+ server.setPasswordAuthenticator(null);
+ server.setKeyboardInteractiveAuthenticator(null);
+ server.setHostBasedAuthenticator(null);
+ // Pretend we did gssapi-with-mic.
+ server.setGSSAuthenticator(new GSSAuthenticator() {
+ @Override
+ public boolean validateInitialUser(ServerSession session,
+ String user) {
+ return false;
+ }
+ });
+ // Accept only the test user/public key
+ server.setPublickeyAuthenticator((userName, publicKey, session) -> {
+ return SshTestGitServer.this.testUser.equals(userName) && KeyUtils
+ .compareKeys(SshTestGitServer.this.testKey, publicKey);
+ });
+ }
+
+ /**
+ * Configures the test server's subsystems (sftp, scp). Invoked from the
+ * constructor. The default provides a simple SFTP setup with the root
+ * directory as the given repository's .git directory's parent. (I.e., at
+ * the directory containing the .git directory.)
+ *
+ * @return A possibly empty collection of subsystems.
+ */
+ @NonNull
+ protected List<NamedFactory<Command>> configureSubsystems() {
+ // SFTP.
+ server.setFileSystemFactory(new VirtualFileSystemFactory() {
+
+ @Override
+ protected Path computeRootDir(Session session) throws IOException {
+ return SshTestGitServer.this.repository.getDirectory()
+ .getParentFile().getAbsoluteFile().toPath();
+ }
+ });
+ return Collections
+ .singletonList((new SftpSubsystemFactory.Builder()).build());
+ }
+
+ /**
+ * Configures shell access for the test server. The default provides no
+ * shell at all.
+ */
+ protected void configureShell() {
+ // No shell
+ server.setShellFactory(null);
+ }
+
+ /**
+ * Adds an additional host key to the server.
+ *
+ * @param key
+ * path to the private key file; should not be encrypted
+ * @param inFront
+ * whether to add the new key before other existing keys
+ * @throws IOException
+ * if the file denoted by the {@link Path} {@code key} cannot be
+ * read
+ * @throws GeneralSecurityException
+ * if the key contained in the file cannot be read
+ */
+ public void addHostKey(@NonNull Path key, boolean inFront)
+ throws IOException, GeneralSecurityException {
+ try (InputStream in = Files.newInputStream(key)) {
+ KeyPair pair = SecurityUtils.loadKeyPairIdentity(key.toString(), in,
+ null);
+ if (inFront) {
+ hostKeys.add(0, pair);
+ } else {
+ hostKeys.add(pair);
+ }
+ }
+ }
+
+ /**
+ * Enable password authentication. The server will accept the test user's
+ * name, converted to all upper-case, as password.
+ */
+ public void enablePasswordAuthentication() {
+ server.setPasswordAuthenticator((user, pwd, session) -> {
+ return testUser.equals(user)
+ && testUser.toUpperCase(Locale.ROOT).equals(pwd);
+ });
+ }
+
+ /**
+ * Enable keyboard-interactive authentication. The server will accept the
+ * test user's name, converted to all upper-case, as password.
+ */
+ public void enableKeyboardInteractiveAuthentication() {
+ server.setPasswordAuthenticator((user, pwd, session) -> {
+ return testUser.equals(user)
+ && testUser.toUpperCase(Locale.ROOT).equals(pwd);
+ });
+ server.setKeyboardInteractiveAuthenticator(
+ DefaultKeyboardInteractiveAuthenticator.INSTANCE);
+ }
+
+ /**
+ * Starts the test server, listening on a random port.
+ *
+ * @return the port the server listens on; test clients should connect to
+ * that port
+ * @throws IOException
+ */
+ public int start() throws IOException {
+ server.start();
+ return server.getPort();
+ }
+
+ /**
+ * Stops the test server.
+ *
+ * @throws IOException
+ */
+ public void stop() throws IOException {
+ executorService.shutdownNow();
+ server.stop(true);
+ }
+
+ public void setTestUserPublicKey(Path key)
+ throws IOException, GeneralSecurityException {
+ this.testKey = AuthorizedKeyEntry.readAuthorizedKeys(key).get(0)
+ .resolvePublicKey(PublicKeyEntryResolver.IGNORING);
+ }
+
+ private class GitUploadPackCommand extends AbstractCommandSupport {
+
+ protected GitUploadPackCommand(String command,
+ ExecutorService executorService) {
+ super(command, executorService, false);
+ }
+
+ @Override
+ public void run() {
+ UploadPack uploadPack = new UploadPack(repository);
+ String gitProtocol = getEnvironment().getEnv().get("GIT_PROTOCOL");
+ if (gitProtocol != null) {
+ uploadPack
+ .setExtraParameters(Collections.singleton(gitProtocol));
+ }
+ try {
+ uploadPack.upload(getInputStream(), getOutputStream(),
+ getErrorStream());
+ onExit(0);
+ } catch (IOException e) {
+ log.warn(
+ MessageFormat.format("Could not run {0}", getCommand()),
+ e);
+ onExit(-1, e.toString());
+ }
+ }
+
+ }
+
+ private class GitReceivePackCommand extends AbstractCommandSupport {
+
+ protected GitReceivePackCommand(String command,
+ ExecutorService executorService) {
+ super(command, executorService, false);
+ }
+
+ @Override
+ public void run() {
+ try {
+ new ReceivePack(repository).receive(getInputStream(),
+ getOutputStream(), getErrorStream());
+ onExit(0);
+ } catch (IOException e) {
+ log.warn(
+ MessageFormat.format("Could not run {0}", getCommand()),
+ e);
+ onExit(-1, e.toString());
+ }
+ }
+
+ }
+}
diff --git a/org.eclipse.jgit.junit/.settings/.api_filters b/org.eclipse.jgit.junit/.settings/.api_filters
deleted file mode 100644
index e5de787fa6..0000000000
--- a/org.eclipse.jgit.junit/.settings/.api_filters
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<component id="org.eclipse.jgit.junit" version="2">
- <resource path="src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java" type="org.eclipse.jgit.junit.LocalDiskRepositoryTestCase">
- <filter comment="Don't care about non-API in tests." id="643842064">
- <message_arguments>
- <message_argument value="FileRepository"/>
- <message_argument value="LocalDiskRepositoryTestCase"/>
- <message_argument value="createBareRepository()"/>
- </message_arguments>
- </filter>
- <filter comment="Don't care about non-API in tests." id="643842064">
- <message_arguments>
- <message_argument value="FileRepository"/>
- <message_argument value="LocalDiskRepositoryTestCase"/>
- <message_argument value="createRepository(boolean, boolean)"/>
- </message_arguments>
- </filter>
- <filter comment="Don't care about non-API in tests." id="643842064">
- <message_arguments>
- <message_argument value="FileRepository"/>
- <message_argument value="LocalDiskRepositoryTestCase"/>
- <message_argument value="createWorkRepository()"/>
- </message_arguments>
- </filter>
- </resource>
- <resource path="src/org/eclipse/jgit/junit/RepositoryTestCase.java" type="org.eclipse.jgit.junit.RepositoryTestCase">
- <filter comment="Don't care about non-API in tests." id="627060751">
- <message_arguments>
- <message_argument value="FileRepository"/>
- <message_argument value="RepositoryTestCase"/>
- <message_argument value="db"/>
- </message_arguments>
- </filter>
- </resource>
-</component>
diff --git a/org.eclipse.jgit.junit/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.junit/.settings/org.eclipse.jdt.core.prefs
index 794592dee1..2ca78ff2d0 100644
--- a/org.eclipse.jgit.junit/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.junit/.settings/org.eclipse.jdt.core.prefs
@@ -91,7 +91,7 @@ org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled
org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning
org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning
-org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=ignore
org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error
org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
diff --git a/org.eclipse.jgit.junit/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.junit/.settings/org.eclipse.mylyn.team.ui.prefs
index 0cba949fb7..2fca432276 100644
--- a/org.eclipse.jgit.junit/.settings/org.eclipse.mylyn.team.ui.prefs
+++ b/org.eclipse.jgit.junit/.settings/org.eclipse.mylyn.team.ui.prefs
@@ -1,3 +1,3 @@
#Tue Jul 19 20:11:28 CEST 2011
-commit.comment.template=${task.description} \n\nBug\: ${task.key}
+commit.comment.template=${task.description}\n\nBug\: ${task.key}
eclipse.preferences.version=1
diff --git a/org.eclipse.jgit.junit/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit/META-INF/MANIFEST.MF
index b9cd4a3f29..a416ae760c 100644
--- a/org.eclipse.jgit.junit/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.junit/META-INF/MANIFEST.MF
@@ -3,31 +3,34 @@ Bundle-ManifestVersion: 2
Bundle-Name: %plugin_name
Automatic-Module-Name: org.eclipse.jgit.junit
Bundle-SymbolicName: org.eclipse.jgit.junit
-Bundle-Version: 5.1.9.qualifier
+Bundle-Version: 5.2.3.qualifier
Bundle-Localization: plugin
Bundle-Vendor: %provider_name
Bundle-ActivationPolicy: lazy
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
-Import-Package: org.eclipse.jgit.api;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.api.errors;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.dircache;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.errors;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.internal.storage.file;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.internal.storage.pack;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.lib;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.merge;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.revwalk;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.storage.file;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.treewalk;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.treewalk.filter;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.util;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.util.io;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.util.time;version="[5.1.9,5.2.0)",
+Import-Package: org.eclipse.jgit.annotations;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.api;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.api.errors;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.dircache;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.errors;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.internal.storage.file;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.internal.storage.pack;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.lib;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.merge;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.revwalk;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.storage.file;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.transport;version="5.2.3",
+ org.eclipse.jgit.treewalk;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.treewalk.filter;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.util;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.util.io;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.util.time;version="[5.2.3,5.3.0)",
org.junit;version="[4.12,5.0.0)",
org.junit.rules;version="[4.12,5.0.0)",
org.junit.runner;version="[4.12,5.0.0)",
- org.junit.runners.model;version="[4.12,5.0.0)"
-Export-Package: org.eclipse.jgit.junit;version="5.1.9";
+ org.junit.runners.model;version="[4.12,5.0.0)",
+ org.slf4j;version="[1.7.0,2.0.0)"
+Export-Package: org.eclipse.jgit.junit;version="5.2.3";
uses:="org.eclipse.jgit.dircache,
org.eclipse.jgit.lib,
org.eclipse.jgit.revwalk,
@@ -35,5 +38,9 @@ Export-Package: org.eclipse.jgit.junit;version="5.1.9";
org.eclipse.jgit.treewalk,
org.eclipse.jgit.util,
org.eclipse.jgit.storage.file,
- org.eclipse.jgit.api",
- org.eclipse.jgit.junit.time;version="5.1.9"
+ org.eclipse.jgit.api,
+ org.junit.rules,
+ org.junit.runners.model,
+ org.junit.runner,
+ org.eclipse.jgit.util.time",
+ org.eclipse.jgit.junit.time;version="5.2.3";uses:="org.eclipse.jgit.util.time"
diff --git a/org.eclipse.jgit.junit/pom.xml b/org.eclipse.jgit.junit/pom.xml
index ab2000204d..e198193f5e 100644
--- a/org.eclipse.jgit.junit/pom.xml
+++ b/org.eclipse.jgit.junit/pom.xml
@@ -52,7 +52,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>5.1.9-SNAPSHOT</version>
+ <version>5.2.3-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.junit</artifactId>
diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/MockSystemReader.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/MockSystemReader.java
index d3d7d682e8..8de386e344 100644
--- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/MockSystemReader.java
+++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/MockSystemReader.java
@@ -70,7 +70,7 @@ import org.eclipse.jgit.util.time.ProposedTimestamp;
* Mock {@link org.eclipse.jgit.util.SystemReader} for tests.
*/
public class MockSystemReader extends SystemReader {
- private final class MockConfig extends FileBasedConfig {
+ private static final class MockConfig extends FileBasedConfig {
private MockConfig(File cfgLocation, FS fs) {
super(cfgLocation, fs);
}
diff --git a/org.eclipse.jgit.lfs.server.test/.classpath b/org.eclipse.jgit.lfs.server.test/.classpath
index 4f9f6bf22f..f08af0a4e9 100644
--- a/org.eclipse.jgit.lfs.server.test/.classpath
+++ b/org.eclipse.jgit.lfs.server.test/.classpath
@@ -2,6 +2,10 @@
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
- <classpathentry kind="src" path="tst"/>
+ <classpathentry kind="src" path="tst">
+ <attributes>
+ <attribute name="test" value="true"/>
+ </attributes>
+ </classpathentry>
<classpathentry kind="output" path="bin"/>
</classpath>
diff --git a/org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.jdt.core.prefs
index 794592dee1..2ca78ff2d0 100644
--- a/org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.jdt.core.prefs
@@ -91,7 +91,7 @@ org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled
org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning
org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning
-org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=ignore
org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error
org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
diff --git a/org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.mylyn.team.ui.prefs
index ce7a0f0478..984263dd94 100644
--- a/org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.mylyn.team.ui.prefs
+++ b/org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.mylyn.team.ui.prefs
@@ -1,2 +1,2 @@
-commit.comment.template=${task.description} \n\nBug\: ${task.key}
+commit.comment.template=${task.description}\n\nBug\: ${task.key}
eclipse.preferences.version=1
diff --git a/org.eclipse.jgit.lfs.server.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs.server.test/META-INF/MANIFEST.MF
index 4f05e9da38..32bdf98b41 100644
--- a/org.eclipse.jgit.lfs.server.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.lfs.server.test/META-INF/MANIFEST.MF
@@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2
Bundle-Name: %plugin_name
Automatic-Module-Name: org.eclipse.jgit.lfs.server.test
Bundle-SymbolicName: org.eclipse.jgit.lfs.server.test
-Bundle-Version: 5.1.9.qualifier
+Bundle-Version: 5.2.3.qualifier
Bundle-Vendor: %provider_name
Bundle-Localization: plugin
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
@@ -28,24 +28,24 @@ Import-Package: javax.servlet;version="[3.1.0,4.0.0)",
org.eclipse.jetty.util.log;version="[9.4.5,10.0.0)",
org.eclipse.jetty.util.security;version="[9.4.5,10.0.0)",
org.eclipse.jetty.util.thread;version="[9.4.5,10.0.0)",
- org.eclipse.jgit.api;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.api.errors;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.internal.storage.file;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.junit;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.junit.http;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.lfs;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.lfs.errors;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.lfs.lib;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.lfs.server;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.lfs.server.fs;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.lfs.test;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.lib;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.revwalk;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.storage.file;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.transport;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.treewalk;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.treewalk.filter;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.util;version="[5.1.9,5.2.0)",
+ org.eclipse.jgit.api;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.api.errors;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.internal.storage.file;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.junit;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.junit.http;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.lfs;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.lfs.errors;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.lfs.lib;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.lfs.server;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.lfs.server.fs;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.lfs.test;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.lib;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.revwalk;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.storage.file;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.transport;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.treewalk;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.treewalk.filter;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.util;version="[5.2.3,5.3.0)",
org.hamcrest.core;version="[1.1.0,2.0.0)",
org.junit;version="[4.12,5.0.0)",
org.junit.rules;version="[4.12,5.0.0)",
diff --git a/org.eclipse.jgit.lfs.server.test/pom.xml b/org.eclipse.jgit.lfs.server.test/pom.xml
index b2cb9cbb05..1e26b0a919 100644
--- a/org.eclipse.jgit.lfs.server.test/pom.xml
+++ b/org.eclipse.jgit.lfs.server.test/pom.xml
@@ -50,7 +50,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>5.1.9-SNAPSHOT</version>
+ <version>5.2.3-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.lfs.server.test</artifactId>
@@ -137,7 +137,7 @@
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
- <argLine>@{argLine} -Djava.io.tmpdir=${project.build.directory} -Xmx512m</argLine>
+ <argLine>-Djava.io.tmpdir=${project.build.directory} -Xmx300m</argLine>
</configuration>
</plugin>
</plugins>
diff --git a/org.eclipse.jgit.lfs.server.test/tst/org/eclipse/jgit/lfs/server/fs/PushTest.java b/org.eclipse.jgit.lfs.server.test/tst/org/eclipse/jgit/lfs/server/fs/PushTest.java
index b081a8ef73..4eb4a0da54 100644
--- a/org.eclipse.jgit.lfs.server.test/tst/org/eclipse/jgit/lfs/server/fs/PushTest.java
+++ b/org.eclipse.jgit.lfs.server.test/tst/org/eclipse/jgit/lfs/server/fs/PushTest.java
@@ -42,6 +42,7 @@
*/
package org.eclipse.jgit.lfs.server.fs;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertEquals;
import java.io.InputStream;
@@ -149,7 +150,7 @@ public class PushTest extends LfsServerTest {
new String(IO
.readWholeStream(is,
(int) ldr.getSize())
- .array()));
+ .array(), UTF_8));
}
}
diff --git a/org.eclipse.jgit.lfs.server/.settings/.api_filters b/org.eclipse.jgit.lfs.server/.settings/.api_filters
index 84e6b19b61..3e60680cf7 100644
--- a/org.eclipse.jgit.lfs.server/.settings/.api_filters
+++ b/org.eclipse.jgit.lfs.server/.settings/.api_filters
@@ -3,8 +3,8 @@
<resource path="META-INF/MANIFEST.MF">
<filter id="924844039">
<message_arguments>
- <message_argument value="5.1.8"/>
- <message_argument value="5.1.0"/>
+ <message_argument value="5.2.3"/>
+ <message_argument value="5.2.0"/>
</message_arguments>
</filter>
</resource>
diff --git a/org.eclipse.jgit.lfs.server/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.lfs.server/.settings/org.eclipse.jdt.core.prefs
index 89394eca41..525ac67142 100644
--- a/org.eclipse.jgit.lfs.server/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.lfs.server/.settings/org.eclipse.jdt.core.prefs
@@ -91,7 +91,7 @@ org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled
org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning
org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning
-org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=ignore
org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error
org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
diff --git a/org.eclipse.jgit.lfs.server/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.lfs.server/.settings/org.eclipse.mylyn.team.ui.prefs
index ce7a0f0478..984263dd94 100644
--- a/org.eclipse.jgit.lfs.server/.settings/org.eclipse.mylyn.team.ui.prefs
+++ b/org.eclipse.jgit.lfs.server/.settings/org.eclipse.mylyn.team.ui.prefs
@@ -1,2 +1,2 @@
-commit.comment.template=${task.description} \n\nBug\: ${task.key}
+commit.comment.template=${task.description}\n\nBug\: ${task.key}
eclipse.preferences.version=1
diff --git a/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF
index 0dec217147..7bed13371b 100644
--- a/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF
@@ -3,19 +3,19 @@ Bundle-ManifestVersion: 2
Bundle-Name: %plugin_name
Automatic-Module-Name: org.eclipse.jgit.lfs.server
Bundle-SymbolicName: org.eclipse.jgit.lfs.server
-Bundle-Version: 5.1.9.qualifier
+Bundle-Version: 5.2.3.qualifier
Bundle-Localization: plugin
Bundle-Vendor: %provider_name
-Export-Package: org.eclipse.jgit.lfs.server;version="5.1.9";
+Export-Package: org.eclipse.jgit.lfs.server;version="5.2.3";
uses:="javax.servlet.http,
org.eclipse.jgit.lfs.lib",
- org.eclipse.jgit.lfs.server.fs;version="5.1.9";
+ org.eclipse.jgit.lfs.server.fs;version="5.2.3";
uses:="javax.servlet,
javax.servlet.http,
org.eclipse.jgit.lfs.server,
org.eclipse.jgit.lfs.lib",
- org.eclipse.jgit.lfs.server.internal;version="5.1.9";x-internal:=true,
- org.eclipse.jgit.lfs.server.s3;version="5.1.9";
+ org.eclipse.jgit.lfs.server.internal;version="5.2.3";x-internal:=true,
+ org.eclipse.jgit.lfs.server.s3;version="5.2.3";
uses:="org.eclipse.jgit.lfs.server,
org.eclipse.jgit.lfs.lib"
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
@@ -25,15 +25,15 @@ Import-Package: com.google.gson;version="[2.8.0,3.0.0)",
javax.servlet.http;version="[3.1.0,4.0.0)",
org.apache.http;version="[4.3.0,5.0.0)",
org.apache.http.client;version="[4.3.0,5.0.0)",
- org.eclipse.jgit.annotations;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.internal;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.internal.storage.file;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.lfs.errors;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.lfs.internal;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.lfs.lib;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.lib;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.nls;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.transport.http;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.transport.http.apache;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.util;version="[5.1.9,5.2.0)",
+ org.eclipse.jgit.annotations;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.internal;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.internal.storage.file;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.lfs.errors;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.lfs.internal;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.lfs.lib;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.lib;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.nls;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.transport.http;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.transport.http.apache;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.util;version="[5.2.3,5.3.0)",
org.slf4j;version="[1.7.0,2.0.0)"
diff --git a/org.eclipse.jgit.lfs.server/pom.xml b/org.eclipse.jgit.lfs.server/pom.xml
index 543420c776..4a2b38ac18 100644
--- a/org.eclipse.jgit.lfs.server/pom.xml
+++ b/org.eclipse.jgit.lfs.server/pom.xml
@@ -50,7 +50,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>5.1.9-SNAPSHOT</version>
+ <version>5.2.3-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.lfs.server</artifactId>
diff --git a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/LargeFileRepository.java b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/LargeFileRepository.java
index cfa53af9cd..c2439adf62 100644
--- a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/LargeFileRepository.java
+++ b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/LargeFileRepository.java
@@ -61,7 +61,7 @@ public interface LargeFileRepository {
* id of the object to download
* @return Action for downloading the object
*/
- public Response.Action getDownloadAction(AnyLongObjectId id);
+ Response.Action getDownloadAction(AnyLongObjectId id);
/**
* Get upload action
@@ -72,7 +72,7 @@ public interface LargeFileRepository {
* size of the object to be uploaded
* @return Action for uploading the object
*/
- public Response.Action getUploadAction(AnyLongObjectId id, long size);
+ Response.Action getUploadAction(AnyLongObjectId id, long size);
/**
* Get verify action
@@ -82,7 +82,8 @@ public interface LargeFileRepository {
* @return Action for verifying the object, or {@code null} if the server
* doesn't support or require verification
*/
- public @Nullable Response.Action getVerifyAction(AnyLongObjectId id);
+ @Nullable
+ Response.Action getVerifyAction(AnyLongObjectId id);
/**
* Get size of an object
@@ -93,5 +94,5 @@ public interface LargeFileRepository {
* exist
* @throws java.io.IOException
*/
- public long getSize(AnyLongObjectId id) throws IOException;
+ long getSize(AnyLongObjectId id) throws IOException;
}
diff --git a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/FileLfsRepository.java b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/FileLfsRepository.java
index 55d9093241..0a7c37ca55 100644
--- a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/FileLfsRepository.java
+++ b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/FileLfsRepository.java
@@ -99,7 +99,8 @@ public class FileLfsRepository implements LargeFileRepository {
/** {@inheritDoc} */
@Override
- public @Nullable Action getVerifyAction(AnyLongObjectId id) {
+ @Nullable
+ public Action getVerifyAction(AnyLongObjectId id) {
return null;
}
diff --git a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/s3/SignerV4.java b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/s3/SignerV4.java
index b21c94e4e6..7b76cecf0c 100644
--- a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/s3/SignerV4.java
+++ b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/s3/SignerV4.java
@@ -411,7 +411,8 @@ class SignerV4 {
String stringToSign = stringToSign(SCHEME, ALGORITHM, dateTimeStamp,
scope, canonicalRequest);
- byte[] signature = (SCHEME + bucketConfig.getSecretKey()).getBytes();
+ byte[] signature = (SCHEME + bucketConfig.getSecretKey())
+ .getBytes(UTF_8);
signature = sign(dateStamp, signature);
signature = sign(bucketConfig.getRegion(), signature);
signature = sign(S3, signature);
diff --git a/org.eclipse.jgit.lfs.test/.classpath b/org.eclipse.jgit.lfs.test/.classpath
index e43ae76c50..e79b7c763a 100644
--- a/org.eclipse.jgit.lfs.test/.classpath
+++ b/org.eclipse.jgit.lfs.test/.classpath
@@ -2,7 +2,15 @@
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
- <classpathentry kind="src" path="src"/>
- <classpathentry kind="src" path="tst"/>
+ <classpathentry kind="src" path="src">
+ <attributes>
+ <attribute name="test" value="true"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry kind="src" path="tst">
+ <attributes>
+ <attribute name="test" value="true"/>
+ </attributes>
+ </classpathentry>
<classpathentry kind="output" path="bin"/>
</classpath>
diff --git a/org.eclipse.jgit.lfs.test/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.lfs.test/.settings/org.eclipse.jdt.core.prefs
index 794592dee1..2ca78ff2d0 100644
--- a/org.eclipse.jgit.lfs.test/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.lfs.test/.settings/org.eclipse.jdt.core.prefs
@@ -91,7 +91,7 @@ org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled
org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning
org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning
-org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=ignore
org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error
org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
diff --git a/org.eclipse.jgit.lfs.test/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.lfs.test/.settings/org.eclipse.mylyn.team.ui.prefs
index ce7a0f0478..984263dd94 100644
--- a/org.eclipse.jgit.lfs.test/.settings/org.eclipse.mylyn.team.ui.prefs
+++ b/org.eclipse.jgit.lfs.test/.settings/org.eclipse.mylyn.team.ui.prefs
@@ -1,2 +1,2 @@
-commit.comment.template=${task.description} \n\nBug\: ${task.key}
+commit.comment.template=${task.description}\n\nBug\: ${task.key}
eclipse.preferences.version=1
diff --git a/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF
index 8e03dd84d0..2863967868 100644
--- a/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF
@@ -3,23 +3,23 @@ Bundle-ManifestVersion: 2
Bundle-Name: %plugin_name
Automatic-Module-Name: org.eclipse.jgit.lfs.test
Bundle-SymbolicName: org.eclipse.jgit.lfs.test
-Bundle-Version: 5.1.9.qualifier
+Bundle-Version: 5.2.3.qualifier
Bundle-Vendor: %provider_name
Bundle-Localization: plugin
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
-Import-Package: org.eclipse.jgit.internal.storage.dfs;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.junit;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.lfs;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.lfs.errors;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.lfs.lib;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.lib;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.revwalk;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.treewalk;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.treewalk.filter;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.util;version="[5.1.9,5.2.0)",
+Import-Package: org.eclipse.jgit.internal.storage.dfs;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.junit;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.lfs;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.lfs.errors;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.lfs.lib;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.lib;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.revwalk;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.treewalk;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.treewalk.filter;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.util;version="[5.2.3,5.3.0)",
org.hamcrest.core;version="[1.1.0,2.0.0)",
org.junit;version="[4.12,5.0.0)",
org.junit.runner;version="[4.12,5.0.0)",
org.junit.runners;version="[4.12,5.0.0)"
-Export-Package: org.eclipse.jgit.lfs.test;version="5.1.9";x-friends:="org.eclipse.jgit.lfs.server.test"
+Export-Package: org.eclipse.jgit.lfs.test;version="5.2.3";x-friends:="org.eclipse.jgit.lfs.server.test"
diff --git a/org.eclipse.jgit.lfs.test/pom.xml b/org.eclipse.jgit.lfs.test/pom.xml
index c7734f2579..37d11f91ce 100644
--- a/org.eclipse.jgit.lfs.test/pom.xml
+++ b/org.eclipse.jgit.lfs.test/pom.xml
@@ -50,7 +50,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>5.1.9-SNAPSHOT</version>
+ <version>5.2.3-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.lfs.test</artifactId>
@@ -111,7 +111,7 @@
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
- <argLine>@{argLine} -Djava.io.tmpdir=${project.build.directory} -Xmx512m</argLine>
+ <argLine>-Djava.io.tmpdir=${project.build.directory} -Xmx300m</argLine>
</configuration>
</plugin>
</plugins>
diff --git a/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/lib/LFSPointerTest.java b/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/lib/LFSPointerTest.java
index a1283ddf41..146a25ed3d 100644
--- a/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/lib/LFSPointerTest.java
+++ b/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/lib/LFSPointerTest.java
@@ -43,7 +43,7 @@
package org.eclipse.jgit.lfs.lib;
-import static org.eclipse.jgit.lib.Constants.CHARACTER_ENCODING;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertEquals;
import java.io.ByteArrayOutputStream;
@@ -66,7 +66,7 @@ public class LFSPointerTest {
assertEquals(
"version https://git-lfs.github.com/spec/v1\noid sha256:"
+ s + "\nsize 4\n",
- baos.toString(CHARACTER_ENCODING));
+ baos.toString(UTF_8.name()));
}
}
}
diff --git a/org.eclipse.jgit.lfs/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.lfs/.settings/org.eclipse.jdt.core.prefs
index 89394eca41..525ac67142 100644
--- a/org.eclipse.jgit.lfs/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.lfs/.settings/org.eclipse.jdt.core.prefs
@@ -91,7 +91,7 @@ org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled
org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning
org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning
-org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=ignore
org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error
org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
diff --git a/org.eclipse.jgit.lfs/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.lfs/.settings/org.eclipse.mylyn.team.ui.prefs
index ce7a0f0478..984263dd94 100644
--- a/org.eclipse.jgit.lfs/.settings/org.eclipse.mylyn.team.ui.prefs
+++ b/org.eclipse.jgit.lfs/.settings/org.eclipse.mylyn.team.ui.prefs
@@ -1,2 +1,2 @@
-commit.comment.template=${task.description} \n\nBug\: ${task.key}
+commit.comment.template=${task.description}\n\nBug\: ${task.key}
eclipse.preferences.version=1
diff --git a/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF
index f731c54919..209b2b2e52 100644
--- a/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF
@@ -3,33 +3,33 @@ Bundle-ManifestVersion: 2
Bundle-Name: %plugin_name
Automatic-Module-Name: org.eclipse.jgit.lfs
Bundle-SymbolicName: org.eclipse.jgit.lfs
-Bundle-Version: 5.1.9.qualifier
+Bundle-Version: 5.2.3.qualifier
Bundle-Localization: plugin
Bundle-Vendor: %provider_name
-Export-Package: org.eclipse.jgit.lfs;version="5.1.9",
- org.eclipse.jgit.lfs.errors;version="5.1.9",
- org.eclipse.jgit.lfs.internal;version="5.1.9";x-friends:="org.eclipse.jgit.lfs.test,org.eclipse.jgit.lfs.server.fs,org.eclipse.jgit.lfs.server",
- org.eclipse.jgit.lfs.lib;version="5.1.9"
+Export-Package: org.eclipse.jgit.lfs;version="5.2.3",
+ org.eclipse.jgit.lfs.errors;version="5.2.3",
+ org.eclipse.jgit.lfs.internal;version="5.2.3";x-friends:="org.eclipse.jgit.lfs.test,org.eclipse.jgit.lfs.server.fs,org.eclipse.jgit.lfs.server",
+ org.eclipse.jgit.lfs.lib;version="5.2.3"
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Import-Package: com.google.gson;version="[2.8.2,3.0.0)",
com.google.gson.stream;version="[2.8.2,3.0.0)",
org.apache.http.impl.client;version="[4.2.6,5.0.0)",
org.apache.http.impl.conn;version="[4.2.6,5.0.0)",
- org.eclipse.jgit.annotations;version="[5.1.9,5.2.0)";resolution:=optional,
- org.eclipse.jgit.api.errors;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.attributes;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.diff;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.errors;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.hooks;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.internal.storage.file;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.lib;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.nls;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.revwalk;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.storage.file;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.storage.pack;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.transport;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.transport.http;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.treewalk;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.treewalk.filter;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.util;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.util.io;version="[5.1.9,5.2.0)"
+ org.eclipse.jgit.annotations;version="[5.2.3,5.3.0)";resolution:=optional,
+ org.eclipse.jgit.api.errors;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.attributes;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.diff;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.errors;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.hooks;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.internal.storage.file;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.lib;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.nls;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.revwalk;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.storage.file;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.storage.pack;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.transport;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.transport.http;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.treewalk;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.treewalk.filter;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.util;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.util.io;version="[5.2.3,5.3.0)"
diff --git a/org.eclipse.jgit.lfs/pom.xml b/org.eclipse.jgit.lfs/pom.xml
index 311b6ceead..f7cad33523 100644
--- a/org.eclipse.jgit.lfs/pom.xml
+++ b/org.eclipse.jgit.lfs/pom.xml
@@ -50,7 +50,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>5.1.9-SNAPSHOT</version>
+ <version>5.2.3-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.lfs</artifactId>
diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/BuiltinLFS.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/BuiltinLFS.java
index 415caa9859..56e3a12ddc 100644
--- a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/BuiltinLFS.java
+++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/BuiltinLFS.java
@@ -99,7 +99,8 @@ public class BuiltinLFS extends LfsFactory {
}
@Override
- public @Nullable PrePushHook getPrePushHook(Repository repo,
+ @Nullable
+ public PrePushHook getPrePushHook(Repository repo,
PrintStream outputStream) {
if (isEnabled(repo)) {
return new LfsPrePushHook(repo, outputStream);
diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/SmudgeFilter.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/SmudgeFilter.java
index fac87c177e..7bacf49269 100644
--- a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/SmudgeFilter.java
+++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/SmudgeFilter.java
@@ -222,7 +222,10 @@ public class SmudgeFilter extends FilterCommand {
Integer.valueOf(responseCode)));
}
Path path = lfs.getMediaFile(ptr.getOid());
- path.getParent().toFile().mkdirs();
+ Path parent = path.getParent();
+ if (parent != null) {
+ parent.toFile().mkdirs();
+ }
try (InputStream contentIn = contentServerConn
.getInputStream()) {
long bytesCopied = Files.copy(contentIn, path);
diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/AtomicObjectOutputStream.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/AtomicObjectOutputStream.java
index 0762ac5f14..317d68a980 100644
--- a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/AtomicObjectOutputStream.java
+++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/AtomicObjectOutputStream.java
@@ -106,7 +106,8 @@ public class AtomicObjectOutputStream extends OutputStream {
* stream. May return {@code null} if called before closing this
* stream.
*/
- public @Nullable AnyLongObjectId getId() {
+ @Nullable
+ public AnyLongObjectId getId() {
return id;
}
diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/LfsConnectionFactory.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/LfsConnectionFactory.java
index 955eca0c3f..feb8b4ae5a 100644
--- a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/LfsConnectionFactory.java
+++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/LfsConnectionFactory.java
@@ -227,7 +227,8 @@ public class LfsConnectionFactory {
* @throws IOException
* in case of any error.
*/
- public static @NonNull HttpConnection getLfsContentConnection(
+ @NonNull
+ public static HttpConnection getLfsContentConnection(
Repository repo, Protocol.Action action, String method)
throws IOException {
URL contentUrl = new URL(action.href);
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/.settings/org.eclipse.mylyn.team.ui.prefs
index 0cba949fb7..2fca432276 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/.settings/org.eclipse.mylyn.team.ui.prefs
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/.settings/org.eclipse.mylyn.team.ui.prefs
@@ -1,3 +1,3 @@
#Tue Jul 19 20:11:28 CEST 2011
-commit.comment.template=${task.description} \n\nBug\: ${task.key}
+commit.comment.template=${task.description}\n\nBug\: ${task.key}
eclipse.preferences.version=1
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.xml
index 2a25885802..ad4da8cdf0 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.xml
@@ -2,7 +2,7 @@
<feature
id="org.eclipse.jgit"
label="%featureName"
- version="5.1.9.qualifier"
+ version="5.2.3.qualifier"
provider-name="%providerName">
<description url="http://www.eclipse.org/jgit/">
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml
index cfbdee70c5..576756bf99 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml
@@ -50,7 +50,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>jgit.tycho.parent</artifactId>
- <version>5.1.9-SNAPSHOT</version>
+ <version>5.2.3-SNAPSHOT</version>
</parent>
<groupId>org.eclipse.jgit.feature</groupId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/.settings/org.eclipse.mylyn.team.ui.prefs
index 0cba949fb7..2fca432276 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/.settings/org.eclipse.mylyn.team.ui.prefs
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/.settings/org.eclipse.mylyn.team.ui.prefs
@@ -1,3 +1,3 @@
#Tue Jul 19 20:11:28 CEST 2011
-commit.comment.template=${task.description} \n\nBug\: ${task.key}
+commit.comment.template=${task.description}\n\nBug\: ${task.key}
eclipse.preferences.version=1
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/feature.xml
index d58fc2aae5..e721ddcebf 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/feature.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/feature.xml
@@ -2,7 +2,7 @@
<feature
id="org.eclipse.jgit.http.apache"
label="%featureName"
- version="5.1.9.qualifier"
+ version="5.2.3.qualifier"
provider-name="%providerName">
<description url="http://www.eclipse.org/jgit/">
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/pom.xml
index 81ffd3a789..c356b34869 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/pom.xml
@@ -50,7 +50,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>jgit.tycho.parent</artifactId>
- <version>5.1.9-SNAPSHOT</version>
+ <version>5.2.3-SNAPSHOT</version>
</parent>
<groupId>org.eclipse.jgit.feature</groupId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/.settings/org.eclipse.mylyn.team.ui.prefs
index 0cba949fb7..2fca432276 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/.settings/org.eclipse.mylyn.team.ui.prefs
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/.settings/org.eclipse.mylyn.team.ui.prefs
@@ -1,3 +1,3 @@
#Tue Jul 19 20:11:28 CEST 2011
-commit.comment.template=${task.description} \n\nBug\: ${task.key}
+commit.comment.template=${task.description}\n\nBug\: ${task.key}
eclipse.preferences.version=1
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.xml
index 3edf069777..0abc1acd17 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.xml
@@ -2,7 +2,7 @@
<feature
id="org.eclipse.jgit.junit"
label="%featureName"
- version="5.1.9.qualifier"
+ version="5.2.3.qualifier"
provider-name="%providerName">
<description url="http://www.eclipse.org/jgit/">
@@ -41,6 +41,13 @@
unpack="false"/>
<plugin
+ id="org.eclipse.jgit.junit.ssh"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
+ <plugin
id="org.eclipse.jgit.http.server"
download-size="0"
install-size="0"
@@ -54,4 +61,18 @@
version="0.0.0"
unpack="false"/>
+ <plugin
+ id="org.apache.sshd.core"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
+ <plugin
+ id="org.apache.sshd.sftp"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
</feature>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/pom.xml
index 783af2d24c..da11f324b9 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/pom.xml
@@ -50,7 +50,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>jgit.tycho.parent</artifactId>
- <version>5.1.9-SNAPSHOT</version>
+ <version>5.2.3-SNAPSHOT</version>
</parent>
<groupId>org.eclipse.jgit.feature</groupId>
@@ -72,6 +72,11 @@
</dependency>
<dependency>
<groupId>org.eclipse.jgit</groupId>
+ <artifactId>org.eclipse.jgit.junit.ssh</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit.http.server</artifactId>
<version>${project.version}</version>
</dependency>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/.settings/org.eclipse.mylyn.team.ui.prefs
index 0cba949fb7..2fca432276 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/.settings/org.eclipse.mylyn.team.ui.prefs
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/.settings/org.eclipse.mylyn.team.ui.prefs
@@ -1,3 +1,3 @@
#Tue Jul 19 20:11:28 CEST 2011
-commit.comment.template=${task.description} \n\nBug\: ${task.key}
+commit.comment.template=${task.description}\n\nBug\: ${task.key}
eclipse.preferences.version=1
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/feature.xml
index bfb95c3e45..d287505f28 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/feature.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/feature.xml
@@ -2,7 +2,7 @@
<feature
id="org.eclipse.jgit.lfs"
label="%featureName"
- version="5.1.9.qualifier"
+ version="5.2.3.qualifier"
provider-name="%providerName">
<description url="http://www.eclipse.org/jgit/">
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/pom.xml
index 57ad03113b..79576005eb 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/pom.xml
@@ -50,7 +50,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>jgit.tycho.parent</artifactId>
- <version>5.1.9-SNAPSHOT</version>
+ <version>5.2.3-SNAPSHOT</version>
</parent>
<groupId>org.eclipse.jgit.feature</groupId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/.settings/org.eclipse.mylyn.team.ui.prefs
index 0cba949fb7..2fca432276 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/.settings/org.eclipse.mylyn.team.ui.prefs
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/.settings/org.eclipse.mylyn.team.ui.prefs
@@ -1,3 +1,3 @@
#Tue Jul 19 20:11:28 CEST 2011
-commit.comment.template=${task.description} \n\nBug\: ${task.key}
+commit.comment.template=${task.description}\n\nBug\: ${task.key}
eclipse.preferences.version=1
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.xml
index b5dddaf5fe..c74326e3a6 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.xml
@@ -2,7 +2,7 @@
<feature
id="org.eclipse.jgit.pgm"
label="%featureName"
- version="5.1.9.qualifier"
+ version="5.2.3.qualifier"
provider-name="%providerName">
<description url="http://www.eclipse.org/jgit/">
@@ -31,8 +31,9 @@
version="0.0.0"/>
<requires>
- <import feature="org.eclipse.jgit" version="5.1.9" match="equivalent"/>
- <import feature="org.eclipse.jgit.lfs" version="5.1.9" match="equivalent"/>
+ <import feature="org.eclipse.jgit" version="5.2.3" match="equivalent"/>
+ <import feature="org.eclipse.jgit.lfs" version="5.2.3" match="equivalent"/>
+ <import feature="org.eclipse.jgit.ssh.apache" version="5.2.3" match="equivalent"/>
</requires>
<plugin
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/pom.xml
index 80424c11f7..e45d79dbef 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/pom.xml
@@ -50,7 +50,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>jgit.tycho.parent</artifactId>
- <version>5.1.9-SNAPSHOT</version>
+ <version>5.2.3-SNAPSHOT</version>
</parent>
<groupId>org.eclipse.jgit.feature</groupId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/.settings/org.eclipse.mylyn.team.ui.prefs
index 0cba949fb7..2fca432276 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/.settings/org.eclipse.mylyn.team.ui.prefs
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/.settings/org.eclipse.mylyn.team.ui.prefs
@@ -1,3 +1,3 @@
#Tue Jul 19 20:11:28 CEST 2011
-commit.comment.template=${task.description} \n\nBug\: ${task.key}
+commit.comment.template=${task.description}\n\nBug\: ${task.key}
eclipse.preferences.version=1
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/feature.xml
index 902a904d27..2179699abf 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/feature.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/feature.xml
@@ -2,7 +2,7 @@
<feature
id="org.eclipse.jgit.pgm.source"
label="%featureName"
- version="5.1.9.qualifier"
+ version="5.2.3.qualifier"
provider-name="%providerName">
<description url="http://www.eclipse.org/jgit/">
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/pom.xml
index 2af5eb824b..221f0316ae 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/pom.xml
@@ -50,7 +50,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>jgit.tycho.parent</artifactId>
- <version>5.1.9-SNAPSHOT</version>
+ <version>5.2.3-SNAPSHOT</version>
</parent>
<groupId>org.eclipse.jgit.feature</groupId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/.settings/org.eclipse.mylyn.team.ui.prefs
index 0cba949fb7..2fca432276 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/.settings/org.eclipse.mylyn.team.ui.prefs
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/.settings/org.eclipse.mylyn.team.ui.prefs
@@ -1,3 +1,3 @@
#Tue Jul 19 20:11:28 CEST 2011
-commit.comment.template=${task.description} \n\nBug\: ${task.key}
+commit.comment.template=${task.description}\n\nBug\: ${task.key}
eclipse.preferences.version=1
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/category.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/category.xml
index 01490295ae..995bd21b18 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/category.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/category.xml
@@ -9,12 +9,18 @@
<feature url="features/org.eclipse.jgit.pgm_0.0.0.qualifier.jar" id="org.eclipse.jgit.pgm" version="0.0.0" patch="true">
<category name="JGit"/>
</feature>
+ <feature url="features/org.eclipse.jgit.ssh.apache_0.0.0.qualifier.jar" id="org.eclipse.jgit.ssh.apache" version="0.0.0" patch="true">
+ <category name="JGit"/>
+ </feature>
<feature url="features/org.eclipse.jgit.source_0.0.0.qualifier.jar" id="org.eclipse.jgit.source" version="0.0.0" patch="true">
<category name="JGit"/>
</feature>
<feature url="features/org.eclipse.jgit.pgm.source_0.0.0.qualifier.jar" id="org.eclipse.jgit.pgm.source" version="0.0.0" patch="true">
<category name="JGit"/>
</feature>
+ <feature url="features/org.eclipse.jgit.ssh.apache.source_0.0.0.qualifier.jar" id="org.eclipse.jgit.ssh.apache.source" version="0.0.0" patch="true">
+ <category name="JGit"/>
+ </feature>
<feature url="features/org.eclipse.jgit.junit_0.0.0.qualifier.jar" id="org.eclipse.jgit.junit" version="0.0.0" patch="true">
<category name="JGit"/>
</feature>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/pom.xml
index fb1a55627f..e258eb253e 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/pom.xml
@@ -50,7 +50,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>jgit.tycho.parent</artifactId>
- <version>5.1.9-SNAPSHOT</version>
+ <version>5.2.3-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.repository</artifactId>
@@ -96,8 +96,18 @@
</dependency>
<dependency>
<groupId>org.eclipse.jgit</groupId>
+ <artifactId>org.eclipse.jgit.junit.ssh</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit.http.server</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.eclipse.jgit</groupId>
+ <artifactId>org.eclipse.jgit.ssh.apache</artifactId>
+ <version>${project.version}</version>
+ </dependency>
</dependencies>
</project>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/.settings/org.eclipse.mylyn.team.ui.prefs
index 0cba949fb7..2fca432276 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/.settings/org.eclipse.mylyn.team.ui.prefs
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/.settings/org.eclipse.mylyn.team.ui.prefs
@@ -1,3 +1,3 @@
#Tue Jul 19 20:11:28 CEST 2011
-commit.comment.template=${task.description} \n\nBug\: ${task.key}
+commit.comment.template=${task.description}\n\nBug\: ${task.key}
eclipse.preferences.version=1
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.xml
index f9ae9d0a35..cee8946e06 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.xml
@@ -2,7 +2,7 @@
<feature
id="org.eclipse.jgit.source"
label="%featureName"
- version="5.1.9.qualifier"
+ version="5.2.3.qualifier"
provider-name="%providerName">
<description url="http://www.eclipse.org/jgit/">
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/pom.xml
index 4621e1bbe3..60b2c5e28b 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/pom.xml
@@ -50,7 +50,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>jgit.tycho.parent</artifactId>
- <version>5.1.9-SNAPSHOT</version>
+ <version>5.2.3-SNAPSHOT</version>
</parent>
<groupId>org.eclipse.jgit.feature</groupId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/.gitignore b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/.gitignore
new file mode 100644
index 0000000000..2f7896d1d1
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/.gitignore
@@ -0,0 +1 @@
+target/
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/.project b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/.project
new file mode 100644
index 0000000000..77876e13b2
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.eclipse.jgit.ssh.apache.feature</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.pde.FeatureBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.FeatureNature</nature>
+ </natures>
+</projectDescription>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/.settings/org.eclipse.core.resources.prefs b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000000..14bdc2c705
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,3 @@
+#Fri Jun 18 23:33:45 CEST 2010
+eclipse.preferences.version=1
+encoding/<project>=UTF-8
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/.settings/org.eclipse.core.runtime.prefs b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/.settings/org.eclipse.core.runtime.prefs
new file mode 100644
index 0000000000..898252b4d6
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/.settings/org.eclipse.core.runtime.prefs
@@ -0,0 +1,3 @@
+#Fri Jun 18 23:33:45 CEST 2010
+eclipse.preferences.version=1
+line.separator=\n
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/.settings/org.eclipse.mylyn.tasks.ui.prefs b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/.settings/org.eclipse.mylyn.tasks.ui.prefs
new file mode 100644
index 0000000000..823c0f56ae
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/.settings/org.eclipse.mylyn.tasks.ui.prefs
@@ -0,0 +1,4 @@
+#Tue Jul 19 20:11:28 CEST 2011
+eclipse.preferences.version=1
+project.repository.kind=bugzilla
+project.repository.url=https\://bugs.eclipse.org/bugs
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/.settings/org.eclipse.mylyn.team.ui.prefs
new file mode 100644
index 0000000000..2fca432276
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/.settings/org.eclipse.mylyn.team.ui.prefs
@@ -0,0 +1,3 @@
+#Tue Jul 19 20:11:28 CEST 2011
+commit.comment.template=${task.description}\n\nBug\: ${task.key}
+eclipse.preferences.version=1
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/build.properties b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/build.properties
new file mode 100644
index 0000000000..b4a8dde9e5
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/build.properties
@@ -0,0 +1,4 @@
+bin.includes = feature.xml,\
+ edl-v10.html,\
+ feature.properties,\
+ license.html
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/edl-v10.html b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/edl-v10.html
new file mode 100644
index 0000000000..1826b47af8
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/edl-v10.html
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
+<title>Eclipse Distribution License - Version 1.0</title>
+<style type="text/css">
+ body {
+ size: 8.5in 11.0in;
+ margin: 0.25in 0.5in 0.25in 0.5in;
+ tab-interval: 0.5in;
+ }
+ p {
+ margin-left: auto;
+ margin-top: 0.5em;
+ margin-bottom: 0.5em;
+ }
+ p.list {
+ margin-left: 0.5in;
+ margin-top: 0.05em;
+ margin-bottom: 0.05em;
+ }
+ </style>
+
+</head>
+
+<body lang="EN-US">
+
+<p><b>Eclipse Distribution License - v 1.0</b></p>
+
+<p>Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors. </p>
+
+<p>All rights reserved.</p>
+<p>Redistribution and use in source and binary forms, with or without modification,
+ are permitted provided that the following conditions are met:
+<ul><li>Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.</li>
+<li>Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.</li>
+<li>Neither the name of the Eclipse Foundation, Inc. nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.</li></ul>
+</p>
+<p>THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.</p>
+
+</body>
+
+</html>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/feature.properties b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/feature.properties
new file mode 100644
index 0000000000..2b086129ff
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/feature.properties
@@ -0,0 +1,177 @@
+###############################################################################
+# Copyright (c) 2000, 2010 IBM Corporation and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+###############################################################################
+
+featureName=Java implementation of Git - ssh support using Apache MINA sshd
+providerName=Eclipse JGit
+
+updateSiteName=Eclipse JGit Update Site
+
+# description property - text of the "Feature Description"
+description=\
+Ssh support using Apache MINA sshd.\n
+################ end of description property ##################################
+
+# "copyright" property - text of the "Feature Update Copyright"
+copyright=\
+Copyright (c) 2018 Thomas Wolf and others.\n\
+All rights reserved. This program and the accompanying materials\n\
+are made available under the terms of the Eclipse Distribution License v1.0\n\
+which accompanies this distribution, and is available at\n\
+http://www.eclipse.org/org/documents/edl-v10.html\n
+################ end of copyright property ####################################
+
+# "licenseURL" property - URL of the "Feature License"
+# do not translate value - just change to point to a locale-specific HTML page
+licenseURL=license.html
+
+# "license" property - text of the "Feature Update License"
+# should be plain text version of license agreement pointed to be "licenseURL"
+license=\
+Eclipse Foundation Software User Agreement\n\
+\n\
+November 22, 2017\n\
+\n\
+Usage Of Content\n\
+\n\
+THE ECLIPSE FOUNDATION MAKES AVAILABLE SOFTWARE, DOCUMENTATION, INFORMATION\n\
+AND/OR OTHER MATERIALS FOR OPEN SOURCE PROJECTS (COLLECTIVELY "CONTENT"). USE OF\n\
+THE CONTENT IS GOVERNED BY THE TERMS AND CONDITIONS OF THIS AGREEMENT AND/OR THE\n\
+TERMS AND CONDITIONS OF LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED\n\
+BELOW. BY USING THE CONTENT, YOU AGREE THAT YOUR USE OF THE CONTENT IS GOVERNED\n\
+BY THIS AGREEMENT AND/OR THE TERMS AND CONDITIONS OF ANY APPLICABLE LICENSE\n\
+AGREEMENTS OR NOTICES INDICATED OR REFERENCED BELOW. IF YOU DO NOT AGREE TO THE\n\
+TERMS AND CONDITIONS OF THIS AGREEMENT AND THE TERMS AND CONDITIONS OF ANY\n\
+APPLICABLE LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED BELOW, THEN YOU\n\
+MAY NOT USE THE CONTENT.\n\
+\n\
+Applicable Licenses\n\
+\n\
+Unless otherwise indicated, all Content made available by the Eclipse Foundation\n\
+is provided to you under the terms and conditions of the Eclipse Public License\n\
+Version 2.0 ("EPL"). A copy of the EPL is provided with this Content and is also\n\
+available at http://www.eclipse.org/legal/epl-2.0. For purposes of the EPL,\n\
+"Program" will mean the Content.\n\
+\n\
+Content includes, but is not limited to, source code, object code, documentation\n\
+and other files maintained in the Eclipse Foundation source code repository\n\
+("Repository") in software modules ("Modules") and made available as\n\
+downloadable archives ("Downloads").\n\
+\n\
+- Content may be structured and packaged into modules to facilitate\n\
+ delivering, extending, and upgrading the Content. Typical modules may\n\
+ include plug-ins ("Plug-ins"), plug-in fragments ("Fragments"), and\n\
+ features ("Features").\n\
+- Each Plug-in or Fragment may be packaged as a sub-directory or JAR\n\
+ (Javaâ„¢ ARchive) in a directory named "plugins".\n\
+- A Feature is a bundle of one or more Plug-ins and/or Fragments and\n\
+ associated material. Each Feature may be packaged as a sub-directory in a\n\
+ directory named "features". Within a Feature, files named "feature.xml" may\n\
+ contain a list of the names and version numbers of the Plug-ins and/or\n\
+ Fragments associated with that Feature.\n\
+- Features may also include other Features ("Included Features"). Within a\n\
+ Feature, files named "feature.xml" may contain a list of the names and\n\
+ version numbers of Included Features.\n\
+\n\
+The terms and conditions governing Plug-ins and Fragments should be contained in\n\
+files named "about.html" ("Abouts"). The terms and conditions governing Features\n\
+and Included Features should be contained in files named "license.html"\n\
+("Feature Licenses"). Abouts and Feature Licenses may be located in any\n\
+directory of a Download or Module including, but not limited to the following\n\
+locations:\n\
+\n\
+- The top-level (root) directory\n\
+- Plug-in and Fragment directories\n\
+- Inside Plug-ins and Fragments packaged as JARs\n\
+- Sub-directories of the directory named "src" of certain Plug-ins\n\
+- Feature directories\n\
+\n\
+Note: if a Feature made available by the Eclipse Foundation is installed using\n\
+the Provisioning Technology (as defined below), you must agree to a license\n\
+("Feature Update License") during the installation process. If the Feature\n\
+contains Included Features, the Feature Update License should either provide you\n\
+with the terms and conditions governing the Included Features or inform you\n\
+where you can locate them. Feature Update Licenses may be found in the "license"\n\
+property of files named "feature.properties" found within a Feature. Such\n\
+Abouts, Feature Licenses, and Feature Update Licenses contain the terms and\n\
+conditions (or references to such terms and conditions) that govern your use of\n\
+the associated Content in that directory.\n\
+\n\
+THE ABOUTS, FEATURE LICENSES, AND FEATURE UPDATE LICENSES MAY REFER TO THE EPL\n\
+OR OTHER LICENSE AGREEMENTS, NOTICES OR TERMS AND CONDITIONS. SOME OF THESE\n\
+OTHER LICENSE AGREEMENTS MAY INCLUDE (BUT ARE NOT LIMITED TO):\n\
+\n\
+- Eclipse Public License Version 1.0 (available at\n\
+ http://www.eclipse.org/legal/epl-v10.html)\n\
+- Eclipse Distribution License Version 1.0 (available at\n\
+ http://www.eclipse.org/licenses/edl-v1.0.html)\n\
+- Common Public License Version 1.0 (available at\n\
+ http://www.eclipse.org/legal/cpl-v10.html)\n\
+- Apache Software License 1.1 (available at\n\
+ http://www.apache.org/licenses/LICENSE)\n\
+- Apache Software License 2.0 (available at\n\
+ http://www.apache.org/licenses/LICENSE-2.0)\n\
+- Mozilla Public License Version 1.1 (available at\n\
+ http://www.mozilla.org/MPL/MPL-1.1.html)\n\
+\n\
+IT IS YOUR OBLIGATION TO READ AND ACCEPT ALL SUCH TERMS AND CONDITIONS PRIOR TO\n\
+USE OF THE CONTENT. If no About, Feature License, or Feature Update License is\n\
+provided, please contact the Eclipse Foundation to determine what terms and\n\
+conditions govern that particular Content.\n\
+\n\
+Use of Provisioning Technology\n\
+\n\
+The Eclipse Foundation makes available provisioning software, examples of which\n\
+include, but are not limited to, p2 and the Eclipse Update Manager\n\
+("Provisioning Technology") for the purpose of allowing users to install\n\
+software, documentation, information and/or other materials (collectively\n\
+"Installable Software"). This capability is provided with the intent of allowing\n\
+such users to install, extend and update Eclipse-based products. Information\n\
+about packaging Installable Software is available at\n\
+http://eclipse.org/equinox/p2/repository_packaging.html ("Specification").\n\
+\n\
+You may use Provisioning Technology to allow other parties to install\n\
+Installable Software. You shall be responsible for enabling the applicable\n\
+license agreements relating to the Installable Software to be presented to, and\n\
+accepted by, the users of the Provisioning Technology in accordance with the\n\
+Specification. By using Provisioning Technology in such a manner and making it\n\
+available in accordance with the Specification, you further acknowledge your\n\
+agreement to, and the acquisition of all necessary rights to permit the\n\
+following:\n\
+\n\
+1. A series of actions may occur ("Provisioning Process") in which a user may\n\
+ execute the Provisioning Technology on a machine ("Target Machine") with the\n\
+ intent of installing, extending or updating the functionality of an\n\
+ Eclipse-based product.\n\
+2. During the Provisioning Process, the Provisioning Technology may cause third\n\
+ party Installable Software or a portion thereof to be accessed and copied to\n\
+ the Target Machine.\n\
+3. Pursuant to the Specification, you will provide to the user the terms and\n\
+ conditions that govern the use of the Installable Software ("Installable\n\
+ Software Agreement") and such Installable Software Agreement shall be\n\
+ accessed from the Target Machine in accordance with the Specification. Such\n\
+ Installable Software Agreement must inform the user of the terms and\n\
+ conditions that govern the Installable Software and must solicit acceptance\n\
+ by the end user in the manner prescribed in such Installable\n\
+ Software Agreement. Upon such indication of agreement by the user, the\n\
+ provisioning Technology will complete installation of the\n\
+ Installable Software.\n\
+\n\
+Cryptography\n\
+\n\
+Content may contain encryption software. The country in which you are currently\n\
+may have restrictions on the import, possession, and use, and/or re-export to\n\
+another country, of encryption software. BEFORE using any encryption software,\n\
+please check the country's laws, regulations and policies concerning the import,\n\
+possession, or use, and re-export of encryption software, to see if this is\n\
+permitted.\n\
+\n\
+Java and all Java-based trademarks are trademarks of Oracle Corporation in the\n\
+United States, other countries, or both.\n
+########### end of license property ##########################################
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/feature.xml
new file mode 100644
index 0000000000..2ce712ccd0
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/feature.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<feature
+ id="org.eclipse.jgit.ssh.apache"
+ label="%featureName"
+ version="5.2.3.qualifier"
+ provider-name="%providerName">
+
+ <description url="http://www.eclipse.org/jgit/">
+ %description
+ </description>
+
+ <copyright>
+ %copyright
+ </copyright>
+
+ <license url="%licenseURL">
+ %license
+ </license>
+
+ <url>
+ <update label="%updateSiteName" url="http://download.eclipse.org/egit/updates"/>
+ <discovery label="%updateSiteName" url="http://download.eclipse.org/egit/updates"/>
+ </url>
+
+ <requires>
+ <import plugin="org.eclipse.jgit"/>
+ </requires>
+
+ <plugin
+ id="org.eclipse.jgit.ssh.apache"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
+ <plugin
+ id="org.apache.sshd.core"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
+ <plugin
+ id="org.apache.sshd.sftp"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
+ <plugin
+ id="net.i2p.crypto.eddsa"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
+</feature>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/license.html b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/license.html
new file mode 100644
index 0000000000..008b8018db
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/license.html
@@ -0,0 +1,189 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
+<title>Eclipse Foundation Software User Agreement</title>
+</head>
+
+<body lang="EN-US">
+ <h2>Eclipse Foundation Software User Agreement</h2>
+ <p>November 22, 2017</p>
+
+ <h3>Usage Of Content</h3>
+
+ <p>THE ECLIPSE FOUNDATION MAKES AVAILABLE SOFTWARE, DOCUMENTATION,
+ INFORMATION AND/OR OTHER MATERIALS FOR OPEN SOURCE PROJECTS
+ (COLLECTIVELY &quot;CONTENT&quot;). USE OF THE CONTENT IS GOVERNED BY
+ THE TERMS AND CONDITIONS OF THIS AGREEMENT AND/OR THE TERMS AND
+ CONDITIONS OF LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED
+ BELOW. BY USING THE CONTENT, YOU AGREE THAT YOUR USE OF THE CONTENT IS
+ GOVERNED BY THIS AGREEMENT AND/OR THE TERMS AND CONDITIONS OF ANY
+ APPLICABLE LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED
+ BELOW. IF YOU DO NOT AGREE TO THE TERMS AND CONDITIONS OF THIS
+ AGREEMENT AND THE TERMS AND CONDITIONS OF ANY APPLICABLE LICENSE
+ AGREEMENTS OR NOTICES INDICATED OR REFERENCED BELOW, THEN YOU MAY NOT
+ USE THE CONTENT.</p>
+
+ <h3>Applicable Licenses</h3>
+
+ <p>
+ Unless otherwise indicated, all Content made available by the Eclipse
+ Foundation is provided to you under the terms and conditions of the
+ Eclipse Public License Version 2.0 (&quot;EPL&quot;). A copy of the
+ EPL is provided with this Content and is also available at <a
+ href="http://www.eclipse.org/legal/epl-2.0">http://www.eclipse.org/legal/epl-2.0</a>.
+ For purposes of the EPL, &quot;Program&quot; will mean the Content.
+ </p>
+
+ <p>Content includes, but is not limited to, source code, object
+ code, documentation and other files maintained in the Eclipse
+ Foundation source code repository (&quot;Repository&quot;) in software
+ modules (&quot;Modules&quot;) and made available as downloadable
+ archives (&quot;Downloads&quot;).</p>
+
+ <ul>
+ <li>Content may be structured and packaged into modules to
+ facilitate delivering, extending, and upgrading the Content. Typical
+ modules may include plug-ins (&quot;Plug-ins&quot;), plug-in
+ fragments (&quot;Fragments&quot;), and features
+ (&quot;Features&quot;).</li>
+ <li>Each Plug-in or Fragment may be packaged as a sub-directory
+ or JAR (Java&trade; ARchive) in a directory named
+ &quot;plugins&quot;.</li>
+ <li>A Feature is a bundle of one or more Plug-ins and/or
+ Fragments and associated material. Each Feature may be packaged as a
+ sub-directory in a directory named &quot;features&quot;. Within a
+ Feature, files named &quot;feature.xml&quot; may contain a list of
+ the names and version numbers of the Plug-ins and/or Fragments
+ associated with that Feature.</li>
+ <li>Features may also include other Features (&quot;Included
+ Features&quot;). Within a Feature, files named
+ &quot;feature.xml&quot; may contain a list of the names and version
+ numbers of Included Features.</li>
+ </ul>
+
+ <p>The terms and conditions governing Plug-ins and Fragments should
+ be contained in files named &quot;about.html&quot;
+ (&quot;Abouts&quot;). The terms and conditions governing Features and
+ Included Features should be contained in files named
+ &quot;license.html&quot; (&quot;Feature Licenses&quot;). Abouts and
+ Feature Licenses may be located in any directory of a Download or
+ Module including, but not limited to the following locations:</p>
+
+ <ul>
+ <li>The top-level (root) directory</li>
+ <li>Plug-in and Fragment directories</li>
+ <li>Inside Plug-ins and Fragments packaged as JARs</li>
+ <li>Sub-directories of the directory named &quot;src&quot; of
+ certain Plug-ins</li>
+ <li>Feature directories</li>
+ </ul>
+
+ <p>Note: if a Feature made available by the Eclipse Foundation is
+ installed using the Provisioning Technology (as defined below), you
+ must agree to a license (&quot;Feature Update License&quot;) during
+ the installation process. If the Feature contains Included Features,
+ the Feature Update License should either provide you with the terms
+ and conditions governing the Included Features or inform you where you
+ can locate them. Feature Update Licenses may be found in the
+ &quot;license&quot; property of files named
+ &quot;feature.properties&quot; found within a Feature. Such Abouts,
+ Feature Licenses, and Feature Update Licenses contain the terms and
+ conditions (or references to such terms and conditions) that govern
+ your use of the associated Content in that directory.</p>
+
+ <p>THE ABOUTS, FEATURE LICENSES, AND FEATURE UPDATE LICENSES MAY
+ REFER TO THE EPL OR OTHER LICENSE AGREEMENTS, NOTICES OR TERMS AND
+ CONDITIONS. SOME OF THESE OTHER LICENSE AGREEMENTS MAY INCLUDE (BUT
+ ARE NOT LIMITED TO):</p>
+
+ <ul>
+ <li>Eclipse Public License Version 1.0 (available at <a
+ href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>)
+ </li>
+ <li>Eclipse Distribution License Version 1.0 (available at <a
+ href="http://www.eclipse.org/licenses/edl-v10.html">http://www.eclipse.org/licenses/edl-v1.0.html</a>)
+ </li>
+ <li>Common Public License Version 1.0 (available at <a
+ href="http://www.eclipse.org/legal/cpl-v10.html">http://www.eclipse.org/legal/cpl-v10.html</a>)
+ </li>
+ <li>Apache Software License 1.1 (available at <a
+ href="http://www.apache.org/licenses/LICENSE">http://www.apache.org/licenses/LICENSE</a>)
+ </li>
+ <li>Apache Software License 2.0 (available at <a
+ href="http://www.apache.org/licenses/LICENSE-2.0">http://www.apache.org/licenses/LICENSE-2.0</a>)
+ </li>
+ <li>Mozilla Public License Version 1.1 (available at <a
+ href="http://www.mozilla.org/MPL/MPL-1.1.html">http://www.mozilla.org/MPL/MPL-1.1.html</a>)
+ </li>
+ </ul>
+
+ <p>IT IS YOUR OBLIGATION TO READ AND ACCEPT ALL SUCH TERMS AND
+ CONDITIONS PRIOR TO USE OF THE CONTENT. If no About, Feature License,
+ or Feature Update License is provided, please contact the Eclipse
+ Foundation to determine what terms and conditions govern that
+ particular Content.</p>
+
+
+ <h3>Use of Provisioning Technology</h3>
+
+ <p>
+ The Eclipse Foundation makes available provisioning software, examples
+ of which include, but are not limited to, p2 and the Eclipse Update
+ Manager (&quot;Provisioning Technology&quot;) for the purpose of
+ allowing users to install software, documentation, information and/or
+ other materials (collectively &quot;Installable Software&quot;). This
+ capability is provided with the intent of allowing such users to
+ install, extend and update Eclipse-based products. Information about
+ packaging Installable Software is available at <a
+ href="http://eclipse.org/equinox/p2/repository_packaging.html">http://eclipse.org/equinox/p2/repository_packaging.html</a>
+ (&quot;Specification&quot;).
+ </p>
+
+ <p>You may use Provisioning Technology to allow other parties to
+ install Installable Software. You shall be responsible for enabling
+ the applicable license agreements relating to the Installable Software
+ to be presented to, and accepted by, the users of the Provisioning
+ Technology in accordance with the Specification. By using Provisioning
+ Technology in such a manner and making it available in accordance with
+ the Specification, you further acknowledge your agreement to, and the
+ acquisition of all necessary rights to permit the following:</p>
+
+ <ol>
+ <li>A series of actions may occur (&quot;Provisioning
+ Process&quot;) in which a user may execute the Provisioning
+ Technology on a machine (&quot;Target Machine&quot;) with the intent
+ of installing, extending or updating the functionality of an
+ Eclipse-based product.</li>
+ <li>During the Provisioning Process, the Provisioning Technology
+ may cause third party Installable Software or a portion thereof to be
+ accessed and copied to the Target Machine.</li>
+ <li>Pursuant to the Specification, you will provide to the user
+ the terms and conditions that govern the use of the Installable
+ Software (&quot;Installable Software Agreement&quot;) and such
+ Installable Software Agreement shall be accessed from the Target
+ Machine in accordance with the Specification. Such Installable
+ Software Agreement must inform the user of the terms and conditions
+ that govern the Installable Software and must solicit acceptance by
+ the end user in the manner prescribed in such Installable Software
+ Agreement. Upon such indication of agreement by the user, the
+ provisioning Technology will complete installation of the Installable
+ Software.</li>
+ </ol>
+
+ <h3>Cryptography</h3>
+
+ <p>Content may contain encryption software. The country in which
+ you are currently may have restrictions on the import, possession, and
+ use, and/or re-export to another country, of encryption software.
+ BEFORE using any encryption software, please check the country's laws,
+ regulations and policies concerning the import, possession, or use,
+ and re-export of encryption software, to see if this is permitted.</p>
+
+ <p>
+ <small>Java and all Java-based trademarks are trademarks of
+ Oracle Corporation in the United States, other countries, or both.</small>
+ </p>
+</body>
+</html>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/pom.xml
new file mode 100644
index 0000000000..869fb171c3
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/pom.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 201x84, Thomas Wolf <thomas.wolf@paranor.ch>
+ and other copyright owners as documented in the project's IP log.
+
+ This program and the accompanying materials are made available
+ under the terms of the Eclipse Distribution License v1.0 which
+ accompanies this distribution, is reproduced below, and is
+ available at http://www.eclipse.org/org/documents/edl-v10.php
+
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or
+ without modification, are permitted provided that the following
+ conditions are met:
+
+ - Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ - Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+ - Neither the name of the Eclipse Foundation, Inc. nor the
+ names of its contributors may be used to endorse or promote
+ products derived from this software without specific prior
+ written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.eclipse.jgit</groupId>
+ <artifactId>jgit.tycho.parent</artifactId>
+ <version>5.2.3-SNAPSHOT</version>
+ </parent>
+
+ <groupId>org.eclipse.jgit.feature</groupId>
+ <artifactId>org.eclipse.jgit.ssh.apache</artifactId>
+ <packaging>eclipse-feature</packaging>
+
+ <name>JGit - Ssh support using Apache MINA sshd</name>
+ <dependencies>
+
+ <dependency>
+ <groupId>org.eclipse.jgit</groupId>
+ <artifactId>org.eclipse.jgit</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.eclipse.jgit</groupId>
+ <artifactId>org.eclipse.jgit.ssh.apache</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ </dependencies>
+
+</project>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/.gitignore b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/.gitignore
new file mode 100644
index 0000000000..eb5a316cbd
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/.gitignore
@@ -0,0 +1 @@
+target
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/.project b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/.project
new file mode 100644
index 0000000000..55620844ff
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.eclipse.jgit.ssh.apache.source.feature</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.pde.FeatureBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.FeatureNature</nature>
+ </natures>
+</projectDescription>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/.settings/org.eclipse.core.resources.prefs b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000000..6f96ce809a
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,3 @@
+#Fri Jun 18 23:33:45 CEST 2010
+eclipse.preferences.version=1
+encoding/<project>=UTF-8
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/.settings/org.eclipse.core.runtime.prefs b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/.settings/org.eclipse.core.runtime.prefs
new file mode 100644
index 0000000000..1a32dba177
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/.settings/org.eclipse.core.runtime.prefs
@@ -0,0 +1,3 @@
+#Fri Jun 18 23:33:45 CEST 2010
+eclipse.preferences.version=1
+line.separator=\n
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/.settings/org.eclipse.mylyn.tasks.ui.prefs b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/.settings/org.eclipse.mylyn.tasks.ui.prefs
new file mode 100644
index 0000000000..823c0f56ae
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/.settings/org.eclipse.mylyn.tasks.ui.prefs
@@ -0,0 +1,4 @@
+#Tue Jul 19 20:11:28 CEST 2011
+eclipse.preferences.version=1
+project.repository.kind=bugzilla
+project.repository.url=https\://bugs.eclipse.org/bugs
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/.settings/org.eclipse.mylyn.team.ui.prefs
new file mode 100644
index 0000000000..2fca432276
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/.settings/org.eclipse.mylyn.team.ui.prefs
@@ -0,0 +1,3 @@
+#Tue Jul 19 20:11:28 CEST 2011
+commit.comment.template=${task.description}\n\nBug\: ${task.key}
+eclipse.preferences.version=1
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/build.properties b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/build.properties
new file mode 100644
index 0000000000..b4a8dde9e5
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/build.properties
@@ -0,0 +1,4 @@
+bin.includes = feature.xml,\
+ edl-v10.html,\
+ feature.properties,\
+ license.html
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/edl-v10.html b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/edl-v10.html
new file mode 100644
index 0000000000..8caddac569
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/edl-v10.html
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
+<title>Eclipse Distribution License - Version 1.0</title>
+<style type="text/css">
+ body {
+ size: 8.5in 11.0in;
+ margin: 0.25in 0.5in 0.25in 0.5in;
+ tab-interval: 0.5in;
+ }
+ p {
+ margin-left: auto;
+ margin-top: 0.5em;
+ margin-bottom: 0.5em;
+ }
+ p.list {
+ margin-left: 0.5in;
+ margin-top: 0.05em;
+ margin-bottom: 0.05em;
+ }
+</style>
+
+</head>
+
+<body lang="EN-US">
+
+<p><b>Eclipse Distribution License - v 1.0</b></p>
+
+<p>Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors. </p>
+
+<p>All rights reserved.</p>
+<p>Redistribution and use in source and binary forms, with or without modification,
+ are permitted provided that the following conditions are met:
+<ul><li>Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer. </li>
+<li>Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution. </li>
+<li>Neither the name of the Eclipse Foundation, Inc. nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission. </li></ul>
+</p>
+<p>THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.</p>
+
+</body>
+
+</html>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/feature.properties b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/feature.properties
new file mode 100644
index 0000000000..9935431824
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/feature.properties
@@ -0,0 +1,178 @@
+###############################################################################
+# Copyright (c) 2000, 2010 IBM Corporation and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+###############################################################################
+
+featureName=Java implementation of Git - ssh support using Apache MINA sshd - Source Code
+providerName=Eclipse JGit
+
+updateSiteName=Eclipse JGit Update Site
+
+# description property - text of the "Feature Description"
+description=\
+Do not install in your IDE: this feature is meant to provision Target Platforms.\n\
+Source code for the JGit ssh support using Apache MINA sshd.\n
+################ end of description property ##################################
+
+# "copyright" property - text of the "Feature Update Copyright"
+copyright=\
+Copyright (c) 2018 Thomas Wolf and others\n\
+All rights reserved. This program and the accompanying materials\n\
+are made available under the terms of the Eclipse Distribution License v1.0\n\
+which accompanies this distribution, and is available at\n\
+http://www.eclipse.org/org/documents/edl-v10.html\n
+################ end of copyright property ####################################
+
+# "licenseURL" property - URL of the "Feature License"
+# do not translate value - just change to point to a locale-specific HTML page
+licenseURL=license.html
+
+# "license" property - text of the "Feature Update License"
+# should be plain text version of license agreement pointed to be "licenseURL"
+license=\
+Eclipse Foundation Software User Agreement\n\
+\n\
+November 22, 2017\n\
+\n\
+Usage Of Content\n\
+\n\
+THE ECLIPSE FOUNDATION MAKES AVAILABLE SOFTWARE, DOCUMENTATION, INFORMATION\n\
+AND/OR OTHER MATERIALS FOR OPEN SOURCE PROJECTS (COLLECTIVELY "CONTENT"). USE OF\n\
+THE CONTENT IS GOVERNED BY THE TERMS AND CONDITIONS OF THIS AGREEMENT AND/OR THE\n\
+TERMS AND CONDITIONS OF LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED\n\
+BELOW. BY USING THE CONTENT, YOU AGREE THAT YOUR USE OF THE CONTENT IS GOVERNED\n\
+BY THIS AGREEMENT AND/OR THE TERMS AND CONDITIONS OF ANY APPLICABLE LICENSE\n\
+AGREEMENTS OR NOTICES INDICATED OR REFERENCED BELOW. IF YOU DO NOT AGREE TO THE\n\
+TERMS AND CONDITIONS OF THIS AGREEMENT AND THE TERMS AND CONDITIONS OF ANY\n\
+APPLICABLE LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED BELOW, THEN YOU\n\
+MAY NOT USE THE CONTENT.\n\
+\n\
+Applicable Licenses\n\
+\n\
+Unless otherwise indicated, all Content made available by the Eclipse Foundation\n\
+is provided to you under the terms and conditions of the Eclipse Public License\n\
+Version 2.0 ("EPL"). A copy of the EPL is provided with this Content and is also\n\
+available at http://www.eclipse.org/legal/epl-2.0. For purposes of the EPL,\n\
+"Program" will mean the Content.\n\
+\n\
+Content includes, but is not limited to, source code, object code, documentation\n\
+and other files maintained in the Eclipse Foundation source code repository\n\
+("Repository") in software modules ("Modules") and made available as\n\
+downloadable archives ("Downloads").\n\
+\n\
+- Content may be structured and packaged into modules to facilitate\n\
+ delivering, extending, and upgrading the Content. Typical modules may\n\
+ include plug-ins ("Plug-ins"), plug-in fragments ("Fragments"), and\n\
+ features ("Features").\n\
+- Each Plug-in or Fragment may be packaged as a sub-directory or JAR\n\
+ (Javaâ„¢ ARchive) in a directory named "plugins".\n\
+- A Feature is a bundle of one or more Plug-ins and/or Fragments and\n\
+ associated material. Each Feature may be packaged as a sub-directory in a\n\
+ directory named "features". Within a Feature, files named "feature.xml" may\n\
+ contain a list of the names and version numbers of the Plug-ins and/or\n\
+ Fragments associated with that Feature.\n\
+- Features may also include other Features ("Included Features"). Within a\n\
+ Feature, files named "feature.xml" may contain a list of the names and\n\
+ version numbers of Included Features.\n\
+\n\
+The terms and conditions governing Plug-ins and Fragments should be contained in\n\
+files named "about.html" ("Abouts"). The terms and conditions governing Features\n\
+and Included Features should be contained in files named "license.html"\n\
+("Feature Licenses"). Abouts and Feature Licenses may be located in any\n\
+directory of a Download or Module including, but not limited to the following\n\
+locations:\n\
+\n\
+- The top-level (root) directory\n\
+- Plug-in and Fragment directories\n\
+- Inside Plug-ins and Fragments packaged as JARs\n\
+- Sub-directories of the directory named "src" of certain Plug-ins\n\
+- Feature directories\n\
+\n\
+Note: if a Feature made available by the Eclipse Foundation is installed using\n\
+the Provisioning Technology (as defined below), you must agree to a license\n\
+("Feature Update License") during the installation process. If the Feature\n\
+contains Included Features, the Feature Update License should either provide you\n\
+with the terms and conditions governing the Included Features or inform you\n\
+where you can locate them. Feature Update Licenses may be found in the "license"\n\
+property of files named "feature.properties" found within a Feature. Such\n\
+Abouts, Feature Licenses, and Feature Update Licenses contain the terms and\n\
+conditions (or references to such terms and conditions) that govern your use of\n\
+the associated Content in that directory.\n\
+\n\
+THE ABOUTS, FEATURE LICENSES, AND FEATURE UPDATE LICENSES MAY REFER TO THE EPL\n\
+OR OTHER LICENSE AGREEMENTS, NOTICES OR TERMS AND CONDITIONS. SOME OF THESE\n\
+OTHER LICENSE AGREEMENTS MAY INCLUDE (BUT ARE NOT LIMITED TO):\n\
+\n\
+- Eclipse Public License Version 1.0 (available at\n\
+ http://www.eclipse.org/legal/epl-v10.html)\n\
+- Eclipse Distribution License Version 1.0 (available at\n\
+ http://www.eclipse.org/licenses/edl-v1.0.html)\n\
+- Common Public License Version 1.0 (available at\n\
+ http://www.eclipse.org/legal/cpl-v10.html)\n\
+- Apache Software License 1.1 (available at\n\
+ http://www.apache.org/licenses/LICENSE)\n\
+- Apache Software License 2.0 (available at\n\
+ http://www.apache.org/licenses/LICENSE-2.0)\n\
+- Mozilla Public License Version 1.1 (available at\n\
+ http://www.mozilla.org/MPL/MPL-1.1.html)\n\
+\n\
+IT IS YOUR OBLIGATION TO READ AND ACCEPT ALL SUCH TERMS AND CONDITIONS PRIOR TO\n\
+USE OF THE CONTENT. If no About, Feature License, or Feature Update License is\n\
+provided, please contact the Eclipse Foundation to determine what terms and\n\
+conditions govern that particular Content.\n\
+\n\
+Use of Provisioning Technology\n\
+\n\
+The Eclipse Foundation makes available provisioning software, examples of which\n\
+include, but are not limited to, p2 and the Eclipse Update Manager\n\
+("Provisioning Technology") for the purpose of allowing users to install\n\
+software, documentation, information and/or other materials (collectively\n\
+"Installable Software"). This capability is provided with the intent of allowing\n\
+such users to install, extend and update Eclipse-based products. Information\n\
+about packaging Installable Software is available at\n\
+http://eclipse.org/equinox/p2/repository_packaging.html ("Specification").\n\
+\n\
+You may use Provisioning Technology to allow other parties to install\n\
+Installable Software. You shall be responsible for enabling the applicable\n\
+license agreements relating to the Installable Software to be presented to, and\n\
+accepted by, the users of the Provisioning Technology in accordance with the\n\
+Specification. By using Provisioning Technology in such a manner and making it\n\
+available in accordance with the Specification, you further acknowledge your\n\
+agreement to, and the acquisition of all necessary rights to permit the\n\
+following:\n\
+\n\
+1. A series of actions may occur ("Provisioning Process") in which a user may\n\
+ execute the Provisioning Technology on a machine ("Target Machine") with the\n\
+ intent of installing, extending or updating the functionality of an\n\
+ Eclipse-based product.\n\
+2. During the Provisioning Process, the Provisioning Technology may cause third\n\
+ party Installable Software or a portion thereof to be accessed and copied to\n\
+ the Target Machine.\n\
+3. Pursuant to the Specification, you will provide to the user the terms and\n\
+ conditions that govern the use of the Installable Software ("Installable\n\
+ Software Agreement") and such Installable Software Agreement shall be\n\
+ accessed from the Target Machine in accordance with the Specification. Such\n\
+ Installable Software Agreement must inform the user of the terms and\n\
+ conditions that govern the Installable Software and must solicit acceptance\n\
+ by the end user in the manner prescribed in such Installable\n\
+ Software Agreement. Upon such indication of agreement by the user, the\n\
+ provisioning Technology will complete installation of the\n\
+ Installable Software.\n\
+\n\
+Cryptography\n\
+\n\
+Content may contain encryption software. The country in which you are currently\n\
+may have restrictions on the import, possession, and use, and/or re-export to\n\
+another country, of encryption software. BEFORE using any encryption software,\n\
+please check the country's laws, regulations and policies concerning the import,\n\
+possession, or use, and re-export of encryption software, to see if this is\n\
+permitted.\n\
+\n\
+Java and all Java-based trademarks are trademarks of Oracle Corporation in the\n\
+United States, other countries, or both.\n
+########### end of license property ##########################################
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/feature.xml
new file mode 100644
index 0000000000..08777443a4
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/feature.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<feature
+ id="org.eclipse.jgit.ssh.apache.source"
+ label="%featureName"
+ version="5.2.3.qualifier"
+ provider-name="%providerName">
+
+ <description url="http://www.eclipse.org/jgit/">
+ %description
+ </description>
+
+ <copyright>
+ %copyright
+ </copyright>
+
+ <license url="%licenseURL">
+ %license
+ </license>
+
+ <url>
+ <update label="%updateSiteName" url="http://download.eclipse.org/egit/updates"/>
+ <discovery label="%updateSiteName" url="http://download.eclipse.org/egit/updates"/>
+ </url>
+
+ <plugin
+ id="org.eclipse.jgit.ssh.apache.source"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+</feature>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/license.html b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/license.html
new file mode 100644
index 0000000000..008b8018db
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/license.html
@@ -0,0 +1,189 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
+<title>Eclipse Foundation Software User Agreement</title>
+</head>
+
+<body lang="EN-US">
+ <h2>Eclipse Foundation Software User Agreement</h2>
+ <p>November 22, 2017</p>
+
+ <h3>Usage Of Content</h3>
+
+ <p>THE ECLIPSE FOUNDATION MAKES AVAILABLE SOFTWARE, DOCUMENTATION,
+ INFORMATION AND/OR OTHER MATERIALS FOR OPEN SOURCE PROJECTS
+ (COLLECTIVELY &quot;CONTENT&quot;). USE OF THE CONTENT IS GOVERNED BY
+ THE TERMS AND CONDITIONS OF THIS AGREEMENT AND/OR THE TERMS AND
+ CONDITIONS OF LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED
+ BELOW. BY USING THE CONTENT, YOU AGREE THAT YOUR USE OF THE CONTENT IS
+ GOVERNED BY THIS AGREEMENT AND/OR THE TERMS AND CONDITIONS OF ANY
+ APPLICABLE LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED
+ BELOW. IF YOU DO NOT AGREE TO THE TERMS AND CONDITIONS OF THIS
+ AGREEMENT AND THE TERMS AND CONDITIONS OF ANY APPLICABLE LICENSE
+ AGREEMENTS OR NOTICES INDICATED OR REFERENCED BELOW, THEN YOU MAY NOT
+ USE THE CONTENT.</p>
+
+ <h3>Applicable Licenses</h3>
+
+ <p>
+ Unless otherwise indicated, all Content made available by the Eclipse
+ Foundation is provided to you under the terms and conditions of the
+ Eclipse Public License Version 2.0 (&quot;EPL&quot;). A copy of the
+ EPL is provided with this Content and is also available at <a
+ href="http://www.eclipse.org/legal/epl-2.0">http://www.eclipse.org/legal/epl-2.0</a>.
+ For purposes of the EPL, &quot;Program&quot; will mean the Content.
+ </p>
+
+ <p>Content includes, but is not limited to, source code, object
+ code, documentation and other files maintained in the Eclipse
+ Foundation source code repository (&quot;Repository&quot;) in software
+ modules (&quot;Modules&quot;) and made available as downloadable
+ archives (&quot;Downloads&quot;).</p>
+
+ <ul>
+ <li>Content may be structured and packaged into modules to
+ facilitate delivering, extending, and upgrading the Content. Typical
+ modules may include plug-ins (&quot;Plug-ins&quot;), plug-in
+ fragments (&quot;Fragments&quot;), and features
+ (&quot;Features&quot;).</li>
+ <li>Each Plug-in or Fragment may be packaged as a sub-directory
+ or JAR (Java&trade; ARchive) in a directory named
+ &quot;plugins&quot;.</li>
+ <li>A Feature is a bundle of one or more Plug-ins and/or
+ Fragments and associated material. Each Feature may be packaged as a
+ sub-directory in a directory named &quot;features&quot;. Within a
+ Feature, files named &quot;feature.xml&quot; may contain a list of
+ the names and version numbers of the Plug-ins and/or Fragments
+ associated with that Feature.</li>
+ <li>Features may also include other Features (&quot;Included
+ Features&quot;). Within a Feature, files named
+ &quot;feature.xml&quot; may contain a list of the names and version
+ numbers of Included Features.</li>
+ </ul>
+
+ <p>The terms and conditions governing Plug-ins and Fragments should
+ be contained in files named &quot;about.html&quot;
+ (&quot;Abouts&quot;). The terms and conditions governing Features and
+ Included Features should be contained in files named
+ &quot;license.html&quot; (&quot;Feature Licenses&quot;). Abouts and
+ Feature Licenses may be located in any directory of a Download or
+ Module including, but not limited to the following locations:</p>
+
+ <ul>
+ <li>The top-level (root) directory</li>
+ <li>Plug-in and Fragment directories</li>
+ <li>Inside Plug-ins and Fragments packaged as JARs</li>
+ <li>Sub-directories of the directory named &quot;src&quot; of
+ certain Plug-ins</li>
+ <li>Feature directories</li>
+ </ul>
+
+ <p>Note: if a Feature made available by the Eclipse Foundation is
+ installed using the Provisioning Technology (as defined below), you
+ must agree to a license (&quot;Feature Update License&quot;) during
+ the installation process. If the Feature contains Included Features,
+ the Feature Update License should either provide you with the terms
+ and conditions governing the Included Features or inform you where you
+ can locate them. Feature Update Licenses may be found in the
+ &quot;license&quot; property of files named
+ &quot;feature.properties&quot; found within a Feature. Such Abouts,
+ Feature Licenses, and Feature Update Licenses contain the terms and
+ conditions (or references to such terms and conditions) that govern
+ your use of the associated Content in that directory.</p>
+
+ <p>THE ABOUTS, FEATURE LICENSES, AND FEATURE UPDATE LICENSES MAY
+ REFER TO THE EPL OR OTHER LICENSE AGREEMENTS, NOTICES OR TERMS AND
+ CONDITIONS. SOME OF THESE OTHER LICENSE AGREEMENTS MAY INCLUDE (BUT
+ ARE NOT LIMITED TO):</p>
+
+ <ul>
+ <li>Eclipse Public License Version 1.0 (available at <a
+ href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>)
+ </li>
+ <li>Eclipse Distribution License Version 1.0 (available at <a
+ href="http://www.eclipse.org/licenses/edl-v10.html">http://www.eclipse.org/licenses/edl-v1.0.html</a>)
+ </li>
+ <li>Common Public License Version 1.0 (available at <a
+ href="http://www.eclipse.org/legal/cpl-v10.html">http://www.eclipse.org/legal/cpl-v10.html</a>)
+ </li>
+ <li>Apache Software License 1.1 (available at <a
+ href="http://www.apache.org/licenses/LICENSE">http://www.apache.org/licenses/LICENSE</a>)
+ </li>
+ <li>Apache Software License 2.0 (available at <a
+ href="http://www.apache.org/licenses/LICENSE-2.0">http://www.apache.org/licenses/LICENSE-2.0</a>)
+ </li>
+ <li>Mozilla Public License Version 1.1 (available at <a
+ href="http://www.mozilla.org/MPL/MPL-1.1.html">http://www.mozilla.org/MPL/MPL-1.1.html</a>)
+ </li>
+ </ul>
+
+ <p>IT IS YOUR OBLIGATION TO READ AND ACCEPT ALL SUCH TERMS AND
+ CONDITIONS PRIOR TO USE OF THE CONTENT. If no About, Feature License,
+ or Feature Update License is provided, please contact the Eclipse
+ Foundation to determine what terms and conditions govern that
+ particular Content.</p>
+
+
+ <h3>Use of Provisioning Technology</h3>
+
+ <p>
+ The Eclipse Foundation makes available provisioning software, examples
+ of which include, but are not limited to, p2 and the Eclipse Update
+ Manager (&quot;Provisioning Technology&quot;) for the purpose of
+ allowing users to install software, documentation, information and/or
+ other materials (collectively &quot;Installable Software&quot;). This
+ capability is provided with the intent of allowing such users to
+ install, extend and update Eclipse-based products. Information about
+ packaging Installable Software is available at <a
+ href="http://eclipse.org/equinox/p2/repository_packaging.html">http://eclipse.org/equinox/p2/repository_packaging.html</a>
+ (&quot;Specification&quot;).
+ </p>
+
+ <p>You may use Provisioning Technology to allow other parties to
+ install Installable Software. You shall be responsible for enabling
+ the applicable license agreements relating to the Installable Software
+ to be presented to, and accepted by, the users of the Provisioning
+ Technology in accordance with the Specification. By using Provisioning
+ Technology in such a manner and making it available in accordance with
+ the Specification, you further acknowledge your agreement to, and the
+ acquisition of all necessary rights to permit the following:</p>
+
+ <ol>
+ <li>A series of actions may occur (&quot;Provisioning
+ Process&quot;) in which a user may execute the Provisioning
+ Technology on a machine (&quot;Target Machine&quot;) with the intent
+ of installing, extending or updating the functionality of an
+ Eclipse-based product.</li>
+ <li>During the Provisioning Process, the Provisioning Technology
+ may cause third party Installable Software or a portion thereof to be
+ accessed and copied to the Target Machine.</li>
+ <li>Pursuant to the Specification, you will provide to the user
+ the terms and conditions that govern the use of the Installable
+ Software (&quot;Installable Software Agreement&quot;) and such
+ Installable Software Agreement shall be accessed from the Target
+ Machine in accordance with the Specification. Such Installable
+ Software Agreement must inform the user of the terms and conditions
+ that govern the Installable Software and must solicit acceptance by
+ the end user in the manner prescribed in such Installable Software
+ Agreement. Upon such indication of agreement by the user, the
+ provisioning Technology will complete installation of the Installable
+ Software.</li>
+ </ol>
+
+ <h3>Cryptography</h3>
+
+ <p>Content may contain encryption software. The country in which
+ you are currently may have restrictions on the import, possession, and
+ use, and/or re-export to another country, of encryption software.
+ BEFORE using any encryption software, please check the country's laws,
+ regulations and policies concerning the import, possession, or use,
+ and re-export of encryption software, to see if this is permitted.</p>
+
+ <p>
+ <small>Java and all Java-based trademarks are trademarks of
+ Oracle Corporation in the United States, other countries, or both.</small>
+ </p>
+</body>
+</html>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/pom.xml
new file mode 100644
index 0000000000..6fd255a9b0
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/pom.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 Thomas Wolf <thomas.wolf@paranor.ch>
+ and other copyright owners as documented in the project's IP log.
+
+ This program and the accompanying materials are made available
+ under the terms of the Eclipse Distribution License v1.0 which
+ accompanies this distribution, is reproduced below, and is
+ available at http://www.eclipse.org/org/documents/edl-v10.php
+
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or
+ without modification, are permitted provided that the following
+ conditions are met:
+
+ - Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ - Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+ - Neither the name of the Eclipse Foundation, Inc. nor the
+ names of its contributors may be used to endorse or promote
+ products derived from this software without specific prior
+ written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.eclipse.jgit</groupId>
+ <artifactId>jgit.tycho.parent</artifactId>
+ <version>5.2.3-SNAPSHOT</version>
+ </parent>
+
+ <groupId>org.eclipse.jgit.feature</groupId>
+ <artifactId>org.eclipse.jgit.ssh.apache.source</artifactId>
+ <packaging>eclipse-feature</packaging>
+
+ <name>JGit Apache MINA ssh Source Feature</name>
+
+</project>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/META-INF/MANIFEST.MF b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/META-INF/MANIFEST.MF
index d6787015bb..b869d8d716 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/META-INF/MANIFEST.MF
@@ -2,4 +2,4 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: JGit Target Platform Bundle
Bundle-SymbolicName: org.eclipse.jgit.target
-Bundle-Version: 5.1.9.qualifier
+Bundle-Version: 5.2.3.qualifier
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.9-staging.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.10-staging.target
index bdabdffa28..98a2a6b0c0 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.9-staging.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.10-staging.target
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?pde?>
-<!-- generated with https://github.com/mbarbero/fr.obeo.releng.targetplatform -->
-<target name="jgit-4.9-staging" sequenceNumber="1536522948">
+<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
+<target name="jgit-4.9-staging" sequenceNumber="1544018574">
<locations>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
<unit id="org.eclipse.jetty.client" version="9.4.11.v20180605"/>
@@ -23,12 +23,12 @@
<repository id="jetty-9.4.11" location="http://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.11.v20180605"/>
</location>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="org.apache.ant" version="1.9.6.v201510161327"/>
- <unit id="org.apache.ant.source" version="1.9.6.v201510161327"/>
+ <unit id="org.apache.ant" version="1.10.5.v20180808-0324"/>
+ <unit id="org.apache.ant.source" version="1.10.5.v20180808-0324"/>
<unit id="org.apache.commons.codec" version="1.10.0.v20180409-1845"/>
<unit id="org.apache.commons.codec.source" version="1.10.0.v20180409-1845"/>
- <unit id="org.apache.commons.compress" version="1.15.0.v20180119-1613"/>
- <unit id="org.apache.commons.compress.source" version="1.15.0.v20180119-1613"/>
+ <unit id="org.apache.commons.compress" version="1.18.0.v20181121-2221"/>
+ <unit id="org.apache.commons.compress.source" version="1.18.0.v20181121-2221"/>
<unit id="org.apache.commons.logging" version="1.2.0.v20180409-1502"/>
<unit id="org.apache.commons.logging.source" version="1.2.0.v20180409-1502"/>
<unit id="org.apache.httpcomponents.httpclient" version="4.5.5.v20180409-1525"/>
@@ -46,10 +46,14 @@
<unit id="org.hamcrest.library.source" version="1.3.0.v20180524-2246"/>
<unit id="javaewah" version="1.1.6.v20160919-1400"/>
<unit id="javaewah.source" version="1.1.6.v20160919-1400"/>
- <unit id="org.objenesis" version="1.0.0.v201505121915"/>
- <unit id="org.objenesis.source" version="1.0.0.v201505121915"/>
- <unit id="org.mockito" version="1.8.4.v201303031500"/>
- <unit id="org.mockito.source" version="1.8.4.v201303031500"/>
+ <unit id="org.objenesis" version="2.6.0.v20180420-1519"/>
+ <unit id="org.objenesis.source" version="2.6.0.v20180420-1519"/>
+ <unit id="org.mockito" version="2.13.0.v20180426-1843"/>
+ <unit id="org.mockito.source" version="2.13.0.v20180426-1843"/>
+ <unit id="net.bytebuddy.byte-buddy" version="1.7.9.v20180420-1519"/>
+ <unit id="net.bytebuddy.byte-buddy.source" version="1.7.9.v20180420-1519"/>
+ <unit id="net.bytebuddy.byte-buddy-agent" version="1.7.9.v20180420-1519"/>
+ <unit id="net.bytebuddy.byte-buddy-agent.source" version="1.7.9.v20180420-1519"/>
<unit id="com.google.gson" version="2.8.2.v20180104-1110"/>
<unit id="com.google.gson.source" version="2.8.2.v20180104-1110"/>
<unit id="com.jcraft.jsch" version="0.1.54.v20170116-1932"/>
@@ -66,11 +70,17 @@
<unit id="org.slf4j.impl.log4j12.source" version="1.7.2.v20131105-2200"/>
<unit id="com.jcraft.jzlib" version="1.1.1.v201205102305"/>
<unit id="com.jcraft.jzlib.source" version="1.1.1.v201205102305"/>
- <repository location="http://download.eclipse.org/tools/orbit/downloads/drops/R20180905201904/repository"/>
+ <unit id="net.i2p.crypto.eddsa" version="0.3.0.v20181102-1323"/>
+ <unit id="net.i2p.crypto.eddsa.source" version="0.3.0.v20181102-1323"/>
+ <unit id="org.apache.sshd.core" version="2.0.0.v20181102-1323"/>
+ <unit id="org.apache.sshd.core.source" version="2.0.0.v20181102-1323"/>
+ <unit id="org.apache.sshd.sftp" version="2.0.0.v20181102-1323"/>
+ <unit id="org.apache.sshd.sftp.source" version="2.0.0.v20181102-1323"/>
+ <repository location="http://download.eclipse.org/tools/orbit/downloads/drops/R20181128170323/repository"/>
</location>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
<unit id="org.eclipse.osgi" version="0.0.0"/>
- <repository location="http://download.eclipse.org/staging/2018-09/"/>
+ <repository location="http://download.eclipse.org/staging/2018-12/"/>
</location>
</locations>
</target>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.9-staging.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.10-staging.tpd
index 6f963c7ceb..07594eafd7 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.9-staging.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.10-staging.tpd
@@ -1,8 +1,8 @@
target "jgit-4.9-staging" with source configurePhase
include "projects/jetty-9.4.11.tpd"
-include "orbit/R20180905201904-2018-09.tpd"
+include "orbit/R20181128170323-2018-12.tpd"
-location "http://download.eclipse.org/staging/2018-09/" {
+location "http://download.eclipse.org/staging/2018-12/" {
org.eclipse.osgi lazy
} \ No newline at end of file
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.5.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.5.target
index 28ebdbf703..10ff3bea0d 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.5.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.5.target
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?pde?>
-<!-- generated with https://github.com/mbarbero/fr.obeo.releng.targetplatform -->
-<target name="jgit-4.5" sequenceNumber="1536523000">
+<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
+<target name="jgit-4.5" sequenceNumber="1544018556">
<locations>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
<unit id="org.eclipse.jetty.client" version="9.4.11.v20180605"/>
@@ -23,12 +23,12 @@
<repository id="jetty-9.4.11" location="http://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.11.v20180605"/>
</location>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="org.apache.ant" version="1.9.6.v201510161327"/>
- <unit id="org.apache.ant.source" version="1.9.6.v201510161327"/>
+ <unit id="org.apache.ant" version="1.10.5.v20180808-0324"/>
+ <unit id="org.apache.ant.source" version="1.10.5.v20180808-0324"/>
<unit id="org.apache.commons.codec" version="1.10.0.v20180409-1845"/>
<unit id="org.apache.commons.codec.source" version="1.10.0.v20180409-1845"/>
- <unit id="org.apache.commons.compress" version="1.15.0.v20180119-1613"/>
- <unit id="org.apache.commons.compress.source" version="1.15.0.v20180119-1613"/>
+ <unit id="org.apache.commons.compress" version="1.18.0.v20181121-2221"/>
+ <unit id="org.apache.commons.compress.source" version="1.18.0.v20181121-2221"/>
<unit id="org.apache.commons.logging" version="1.2.0.v20180409-1502"/>
<unit id="org.apache.commons.logging.source" version="1.2.0.v20180409-1502"/>
<unit id="org.apache.httpcomponents.httpclient" version="4.5.5.v20180409-1525"/>
@@ -46,10 +46,14 @@
<unit id="org.hamcrest.library.source" version="1.3.0.v20180524-2246"/>
<unit id="javaewah" version="1.1.6.v20160919-1400"/>
<unit id="javaewah.source" version="1.1.6.v20160919-1400"/>
- <unit id="org.objenesis" version="1.0.0.v201505121915"/>
- <unit id="org.objenesis.source" version="1.0.0.v201505121915"/>
- <unit id="org.mockito" version="1.8.4.v201303031500"/>
- <unit id="org.mockito.source" version="1.8.4.v201303031500"/>
+ <unit id="org.objenesis" version="2.6.0.v20180420-1519"/>
+ <unit id="org.objenesis.source" version="2.6.0.v20180420-1519"/>
+ <unit id="org.mockito" version="2.13.0.v20180426-1843"/>
+ <unit id="org.mockito.source" version="2.13.0.v20180426-1843"/>
+ <unit id="net.bytebuddy.byte-buddy" version="1.7.9.v20180420-1519"/>
+ <unit id="net.bytebuddy.byte-buddy.source" version="1.7.9.v20180420-1519"/>
+ <unit id="net.bytebuddy.byte-buddy-agent" version="1.7.9.v20180420-1519"/>
+ <unit id="net.bytebuddy.byte-buddy-agent.source" version="1.7.9.v20180420-1519"/>
<unit id="com.google.gson" version="2.8.2.v20180104-1110"/>
<unit id="com.google.gson.source" version="2.8.2.v20180104-1110"/>
<unit id="com.jcraft.jsch" version="0.1.54.v20170116-1932"/>
@@ -66,7 +70,13 @@
<unit id="org.slf4j.impl.log4j12.source" version="1.7.2.v20131105-2200"/>
<unit id="com.jcraft.jzlib" version="1.1.1.v201205102305"/>
<unit id="com.jcraft.jzlib.source" version="1.1.1.v201205102305"/>
- <repository location="http://download.eclipse.org/tools/orbit/downloads/drops/R20180905201904/repository"/>
+ <unit id="net.i2p.crypto.eddsa" version="0.3.0.v20181102-1323"/>
+ <unit id="net.i2p.crypto.eddsa.source" version="0.3.0.v20181102-1323"/>
+ <unit id="org.apache.sshd.core" version="2.0.0.v20181102-1323"/>
+ <unit id="org.apache.sshd.core.source" version="2.0.0.v20181102-1323"/>
+ <unit id="org.apache.sshd.sftp" version="2.0.0.v20181102-1323"/>
+ <unit id="org.apache.sshd.sftp.source" version="2.0.0.v20181102-1323"/>
+ <repository location="http://download.eclipse.org/tools/orbit/downloads/drops/R20181128170323/repository"/>
</location>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
<unit id="org.eclipse.osgi" version="0.0.0"/>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.5.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.5.tpd
index 8e65c9820e..f099b34998 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.5.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.5.tpd
@@ -1,7 +1,7 @@
target "jgit-4.5" with source configurePhase
include "projects/jetty-9.4.11.tpd"
-include "orbit/R20180905201904-2018-09.tpd"
+include "orbit/R20181128170323-2018-12.tpd"
location "http://download.eclipse.org/releases/mars/" {
org.eclipse.osgi lazy
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.target
index 8490e55b27..11fd7fd69d 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.target
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?pde?>
-<!-- generated with https://github.com/mbarbero/fr.obeo.releng.targetplatform -->
-<target name="jgit-4.6" sequenceNumber="1536522989">
+<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
+<target name="jgit-4.6" sequenceNumber="1544018561">
<locations>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
<unit id="org.eclipse.jetty.client" version="9.4.11.v20180605"/>
@@ -23,12 +23,12 @@
<repository id="jetty-9.4.11" location="http://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.11.v20180605"/>
</location>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="org.apache.ant" version="1.9.6.v201510161327"/>
- <unit id="org.apache.ant.source" version="1.9.6.v201510161327"/>
+ <unit id="org.apache.ant" version="1.10.5.v20180808-0324"/>
+ <unit id="org.apache.ant.source" version="1.10.5.v20180808-0324"/>
<unit id="org.apache.commons.codec" version="1.10.0.v20180409-1845"/>
<unit id="org.apache.commons.codec.source" version="1.10.0.v20180409-1845"/>
- <unit id="org.apache.commons.compress" version="1.15.0.v20180119-1613"/>
- <unit id="org.apache.commons.compress.source" version="1.15.0.v20180119-1613"/>
+ <unit id="org.apache.commons.compress" version="1.18.0.v20181121-2221"/>
+ <unit id="org.apache.commons.compress.source" version="1.18.0.v20181121-2221"/>
<unit id="org.apache.commons.logging" version="1.2.0.v20180409-1502"/>
<unit id="org.apache.commons.logging.source" version="1.2.0.v20180409-1502"/>
<unit id="org.apache.httpcomponents.httpclient" version="4.5.5.v20180409-1525"/>
@@ -46,10 +46,14 @@
<unit id="org.hamcrest.library.source" version="1.3.0.v20180524-2246"/>
<unit id="javaewah" version="1.1.6.v20160919-1400"/>
<unit id="javaewah.source" version="1.1.6.v20160919-1400"/>
- <unit id="org.objenesis" version="1.0.0.v201505121915"/>
- <unit id="org.objenesis.source" version="1.0.0.v201505121915"/>
- <unit id="org.mockito" version="1.8.4.v201303031500"/>
- <unit id="org.mockito.source" version="1.8.4.v201303031500"/>
+ <unit id="org.objenesis" version="2.6.0.v20180420-1519"/>
+ <unit id="org.objenesis.source" version="2.6.0.v20180420-1519"/>
+ <unit id="org.mockito" version="2.13.0.v20180426-1843"/>
+ <unit id="org.mockito.source" version="2.13.0.v20180426-1843"/>
+ <unit id="net.bytebuddy.byte-buddy" version="1.7.9.v20180420-1519"/>
+ <unit id="net.bytebuddy.byte-buddy.source" version="1.7.9.v20180420-1519"/>
+ <unit id="net.bytebuddy.byte-buddy-agent" version="1.7.9.v20180420-1519"/>
+ <unit id="net.bytebuddy.byte-buddy-agent.source" version="1.7.9.v20180420-1519"/>
<unit id="com.google.gson" version="2.8.2.v20180104-1110"/>
<unit id="com.google.gson.source" version="2.8.2.v20180104-1110"/>
<unit id="com.jcraft.jsch" version="0.1.54.v20170116-1932"/>
@@ -66,7 +70,13 @@
<unit id="org.slf4j.impl.log4j12.source" version="1.7.2.v20131105-2200"/>
<unit id="com.jcraft.jzlib" version="1.1.1.v201205102305"/>
<unit id="com.jcraft.jzlib.source" version="1.1.1.v201205102305"/>
- <repository location="http://download.eclipse.org/tools/orbit/downloads/drops/R20180905201904/repository"/>
+ <unit id="net.i2p.crypto.eddsa" version="0.3.0.v20181102-1323"/>
+ <unit id="net.i2p.crypto.eddsa.source" version="0.3.0.v20181102-1323"/>
+ <unit id="org.apache.sshd.core" version="2.0.0.v20181102-1323"/>
+ <unit id="org.apache.sshd.core.source" version="2.0.0.v20181102-1323"/>
+ <unit id="org.apache.sshd.sftp" version="2.0.0.v20181102-1323"/>
+ <unit id="org.apache.sshd.sftp.source" version="2.0.0.v20181102-1323"/>
+ <repository location="http://download.eclipse.org/tools/orbit/downloads/drops/R20181128170323/repository"/>
</location>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
<unit id="org.eclipse.osgi" version="0.0.0"/>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.tpd
index 44c846e0bf..39f166cc1c 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.tpd
@@ -1,7 +1,7 @@
target "jgit-4.6" with source configurePhase
include "projects/jetty-9.4.11.tpd"
-include "orbit/R20180905201904-2018-09.tpd"
+include "orbit/R20181128170323-2018-12.tpd"
location "http://download.eclipse.org/releases/neon/" {
org.eclipse.osgi lazy
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.target
index a6651b837b..061439cd93 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.target
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?pde?>
-<!-- generated with https://github.com/mbarbero/fr.obeo.releng.targetplatform -->
-<target name="jgit-4.7" sequenceNumber="1536522970">
+<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
+<target name="jgit-4.7" sequenceNumber="1544018548">
<locations>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
<unit id="org.eclipse.jetty.client" version="9.4.11.v20180605"/>
@@ -23,12 +23,12 @@
<repository id="jetty-9.4.11" location="http://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.11.v20180605"/>
</location>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="org.apache.ant" version="1.9.6.v201510161327"/>
- <unit id="org.apache.ant.source" version="1.9.6.v201510161327"/>
+ <unit id="org.apache.ant" version="1.10.5.v20180808-0324"/>
+ <unit id="org.apache.ant.source" version="1.10.5.v20180808-0324"/>
<unit id="org.apache.commons.codec" version="1.10.0.v20180409-1845"/>
<unit id="org.apache.commons.codec.source" version="1.10.0.v20180409-1845"/>
- <unit id="org.apache.commons.compress" version="1.15.0.v20180119-1613"/>
- <unit id="org.apache.commons.compress.source" version="1.15.0.v20180119-1613"/>
+ <unit id="org.apache.commons.compress" version="1.18.0.v20181121-2221"/>
+ <unit id="org.apache.commons.compress.source" version="1.18.0.v20181121-2221"/>
<unit id="org.apache.commons.logging" version="1.2.0.v20180409-1502"/>
<unit id="org.apache.commons.logging.source" version="1.2.0.v20180409-1502"/>
<unit id="org.apache.httpcomponents.httpclient" version="4.5.5.v20180409-1525"/>
@@ -46,10 +46,14 @@
<unit id="org.hamcrest.library.source" version="1.3.0.v20180524-2246"/>
<unit id="javaewah" version="1.1.6.v20160919-1400"/>
<unit id="javaewah.source" version="1.1.6.v20160919-1400"/>
- <unit id="org.objenesis" version="1.0.0.v201505121915"/>
- <unit id="org.objenesis.source" version="1.0.0.v201505121915"/>
- <unit id="org.mockito" version="1.8.4.v201303031500"/>
- <unit id="org.mockito.source" version="1.8.4.v201303031500"/>
+ <unit id="org.objenesis" version="2.6.0.v20180420-1519"/>
+ <unit id="org.objenesis.source" version="2.6.0.v20180420-1519"/>
+ <unit id="org.mockito" version="2.13.0.v20180426-1843"/>
+ <unit id="org.mockito.source" version="2.13.0.v20180426-1843"/>
+ <unit id="net.bytebuddy.byte-buddy" version="1.7.9.v20180420-1519"/>
+ <unit id="net.bytebuddy.byte-buddy.source" version="1.7.9.v20180420-1519"/>
+ <unit id="net.bytebuddy.byte-buddy-agent" version="1.7.9.v20180420-1519"/>
+ <unit id="net.bytebuddy.byte-buddy-agent.source" version="1.7.9.v20180420-1519"/>
<unit id="com.google.gson" version="2.8.2.v20180104-1110"/>
<unit id="com.google.gson.source" version="2.8.2.v20180104-1110"/>
<unit id="com.jcraft.jsch" version="0.1.54.v20170116-1932"/>
@@ -66,7 +70,13 @@
<unit id="org.slf4j.impl.log4j12.source" version="1.7.2.v20131105-2200"/>
<unit id="com.jcraft.jzlib" version="1.1.1.v201205102305"/>
<unit id="com.jcraft.jzlib.source" version="1.1.1.v201205102305"/>
- <repository location="http://download.eclipse.org/tools/orbit/downloads/drops/R20180905201904/repository"/>
+ <unit id="net.i2p.crypto.eddsa" version="0.3.0.v20181102-1323"/>
+ <unit id="net.i2p.crypto.eddsa.source" version="0.3.0.v20181102-1323"/>
+ <unit id="org.apache.sshd.core" version="2.0.0.v20181102-1323"/>
+ <unit id="org.apache.sshd.core.source" version="2.0.0.v20181102-1323"/>
+ <unit id="org.apache.sshd.sftp" version="2.0.0.v20181102-1323"/>
+ <unit id="org.apache.sshd.sftp.source" version="2.0.0.v20181102-1323"/>
+ <repository location="http://download.eclipse.org/tools/orbit/downloads/drops/R20181128170323/repository"/>
</location>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
<unit id="org.eclipse.osgi" version="0.0.0"/>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.tpd
index b63955f2af..b1f05680bc 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.tpd
@@ -1,7 +1,7 @@
target "jgit-4.7" with source configurePhase
include "projects/jetty-9.4.11.tpd"
-include "orbit/R20180905201904-2018-09.tpd"
+include "orbit/R20181128170323-2018-12.tpd"
location "http://download.eclipse.org/releases/oxygen/" {
org.eclipse.osgi lazy
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.8.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.8.target
index 7adb7dafd6..da66fb9475 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.8.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.8.target
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?pde?>
-<!-- generated with https://github.com/mbarbero/fr.obeo.releng.targetplatform -->
-<target name="jgit-4.8" sequenceNumber="1536522959">
+<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
+<target name="jgit-4.8" sequenceNumber="1544018536">
<locations>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
<unit id="org.eclipse.jetty.client" version="9.4.11.v20180605"/>
@@ -23,12 +23,12 @@
<repository id="jetty-9.4.11" location="http://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.11.v20180605"/>
</location>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="org.apache.ant" version="1.9.6.v201510161327"/>
- <unit id="org.apache.ant.source" version="1.9.6.v201510161327"/>
+ <unit id="org.apache.ant" version="1.10.5.v20180808-0324"/>
+ <unit id="org.apache.ant.source" version="1.10.5.v20180808-0324"/>
<unit id="org.apache.commons.codec" version="1.10.0.v20180409-1845"/>
<unit id="org.apache.commons.codec.source" version="1.10.0.v20180409-1845"/>
- <unit id="org.apache.commons.compress" version="1.15.0.v20180119-1613"/>
- <unit id="org.apache.commons.compress.source" version="1.15.0.v20180119-1613"/>
+ <unit id="org.apache.commons.compress" version="1.18.0.v20181121-2221"/>
+ <unit id="org.apache.commons.compress.source" version="1.18.0.v20181121-2221"/>
<unit id="org.apache.commons.logging" version="1.2.0.v20180409-1502"/>
<unit id="org.apache.commons.logging.source" version="1.2.0.v20180409-1502"/>
<unit id="org.apache.httpcomponents.httpclient" version="4.5.5.v20180409-1525"/>
@@ -46,10 +46,14 @@
<unit id="org.hamcrest.library.source" version="1.3.0.v20180524-2246"/>
<unit id="javaewah" version="1.1.6.v20160919-1400"/>
<unit id="javaewah.source" version="1.1.6.v20160919-1400"/>
- <unit id="org.objenesis" version="1.0.0.v201505121915"/>
- <unit id="org.objenesis.source" version="1.0.0.v201505121915"/>
- <unit id="org.mockito" version="1.8.4.v201303031500"/>
- <unit id="org.mockito.source" version="1.8.4.v201303031500"/>
+ <unit id="org.objenesis" version="2.6.0.v20180420-1519"/>
+ <unit id="org.objenesis.source" version="2.6.0.v20180420-1519"/>
+ <unit id="org.mockito" version="2.13.0.v20180426-1843"/>
+ <unit id="org.mockito.source" version="2.13.0.v20180426-1843"/>
+ <unit id="net.bytebuddy.byte-buddy" version="1.7.9.v20180420-1519"/>
+ <unit id="net.bytebuddy.byte-buddy.source" version="1.7.9.v20180420-1519"/>
+ <unit id="net.bytebuddy.byte-buddy-agent" version="1.7.9.v20180420-1519"/>
+ <unit id="net.bytebuddy.byte-buddy-agent.source" version="1.7.9.v20180420-1519"/>
<unit id="com.google.gson" version="2.8.2.v20180104-1110"/>
<unit id="com.google.gson.source" version="2.8.2.v20180104-1110"/>
<unit id="com.jcraft.jsch" version="0.1.54.v20170116-1932"/>
@@ -66,7 +70,13 @@
<unit id="org.slf4j.impl.log4j12.source" version="1.7.2.v20131105-2200"/>
<unit id="com.jcraft.jzlib" version="1.1.1.v201205102305"/>
<unit id="com.jcraft.jzlib.source" version="1.1.1.v201205102305"/>
- <repository location="http://download.eclipse.org/tools/orbit/downloads/drops/R20180905201904/repository"/>
+ <unit id="net.i2p.crypto.eddsa" version="0.3.0.v20181102-1323"/>
+ <unit id="net.i2p.crypto.eddsa.source" version="0.3.0.v20181102-1323"/>
+ <unit id="org.apache.sshd.core" version="2.0.0.v20181102-1323"/>
+ <unit id="org.apache.sshd.core.source" version="2.0.0.v20181102-1323"/>
+ <unit id="org.apache.sshd.sftp" version="2.0.0.v20181102-1323"/>
+ <unit id="org.apache.sshd.sftp.source" version="2.0.0.v20181102-1323"/>
+ <repository location="http://download.eclipse.org/tools/orbit/downloads/drops/R20181128170323/repository"/>
</location>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
<unit id="org.eclipse.osgi" version="0.0.0"/>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.8.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.8.tpd
index 4e41ad9a04..959abaeb56 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.8.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.8.tpd
@@ -1,7 +1,7 @@
target "jgit-4.8" with source configurePhase
include "projects/jetty-9.4.11.tpd"
-include "orbit/R20180905201904-2018-09.tpd"
+include "orbit/R20181128170323-2018-12.tpd"
location "http://download.eclipse.org/releases/photon/" {
org.eclipse.osgi lazy
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.9.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.9.target
new file mode 100644
index 0000000000..025bb1fa1c
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.9.target
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<?pde?>
+<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
+<target name="jgit-4.9" sequenceNumber="1544017335">
+ <locations>
+ <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
+ <unit id="org.eclipse.jetty.client" version="9.4.11.v20180605"/>
+ <unit id="org.eclipse.jetty.client.source" version="9.4.11.v20180605"/>
+ <unit id="org.eclipse.jetty.continuation" version="9.4.11.v20180605"/>
+ <unit id="org.eclipse.jetty.continuation.source" version="9.4.11.v20180605"/>
+ <unit id="org.eclipse.jetty.http" version="9.4.11.v20180605"/>
+ <unit id="org.eclipse.jetty.http.source" version="9.4.11.v20180605"/>
+ <unit id="org.eclipse.jetty.io" version="9.4.11.v20180605"/>
+ <unit id="org.eclipse.jetty.io.source" version="9.4.11.v20180605"/>
+ <unit id="org.eclipse.jetty.security" version="9.4.11.v20180605"/>
+ <unit id="org.eclipse.jetty.security.source" version="9.4.11.v20180605"/>
+ <unit id="org.eclipse.jetty.server" version="9.4.11.v20180605"/>
+ <unit id="org.eclipse.jetty.server.source" version="9.4.11.v20180605"/>
+ <unit id="org.eclipse.jetty.servlet" version="9.4.11.v20180605"/>
+ <unit id="org.eclipse.jetty.servlet.source" version="9.4.11.v20180605"/>
+ <unit id="org.eclipse.jetty.util" version="9.4.11.v20180605"/>
+ <unit id="org.eclipse.jetty.util.source" version="9.4.11.v20180605"/>
+ <repository id="jetty-9.4.11" location="http://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.11.v20180605"/>
+ </location>
+ <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
+ <unit id="org.apache.ant" version="1.10.5.v20180808-0324"/>
+ <unit id="org.apache.ant.source" version="1.10.5.v20180808-0324"/>
+ <unit id="org.apache.commons.codec" version="1.10.0.v20180409-1845"/>
+ <unit id="org.apache.commons.codec.source" version="1.10.0.v20180409-1845"/>
+ <unit id="org.apache.commons.compress" version="1.18.0.v20181121-2221"/>
+ <unit id="org.apache.commons.compress.source" version="1.18.0.v20181121-2221"/>
+ <unit id="org.apache.commons.logging" version="1.2.0.v20180409-1502"/>
+ <unit id="org.apache.commons.logging.source" version="1.2.0.v20180409-1502"/>
+ <unit id="org.apache.httpcomponents.httpclient" version="4.5.5.v20180409-1525"/>
+ <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.5.v20180409-1525"/>
+ <unit id="org.apache.httpcomponents.httpcore" version="4.4.9.v20180409-1525"/>
+ <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.9.v20180409-1525"/>
+ <unit id="org.apache.log4j" version="1.2.15.v201012070815"/>
+ <unit id="org.apache.log4j.source" version="1.2.15.v201012070815"/>
+ <unit id="org.kohsuke.args4j" version="2.33.0.v20160323-2218"/>
+ <unit id="org.kohsuke.args4j.source" version="2.33.0.v20160323-2218"/>
+ <unit id="org.hamcrest" version="1.1.0.v20090501071000"/>
+ <unit id="org.hamcrest.core" version="1.3.0.v20180420-1519"/>
+ <unit id="org.hamcrest.core.source" version="1.3.0.v20180420-1519"/>
+ <unit id="org.hamcrest.library" version="1.3.0.v20180524-2246"/>
+ <unit id="org.hamcrest.library.source" version="1.3.0.v20180524-2246"/>
+ <unit id="javaewah" version="1.1.6.v20160919-1400"/>
+ <unit id="javaewah.source" version="1.1.6.v20160919-1400"/>
+ <unit id="org.objenesis" version="2.6.0.v20180420-1519"/>
+ <unit id="org.objenesis.source" version="2.6.0.v20180420-1519"/>
+ <unit id="org.mockito" version="2.13.0.v20180426-1843"/>
+ <unit id="org.mockito.source" version="2.13.0.v20180426-1843"/>
+ <unit id="net.bytebuddy.byte-buddy" version="1.7.9.v20180420-1519"/>
+ <unit id="net.bytebuddy.byte-buddy.source" version="1.7.9.v20180420-1519"/>
+ <unit id="net.bytebuddy.byte-buddy-agent" version="1.7.9.v20180420-1519"/>
+ <unit id="net.bytebuddy.byte-buddy-agent.source" version="1.7.9.v20180420-1519"/>
+ <unit id="com.google.gson" version="2.8.2.v20180104-1110"/>
+ <unit id="com.google.gson.source" version="2.8.2.v20180104-1110"/>
+ <unit id="com.jcraft.jsch" version="0.1.54.v20170116-1932"/>
+ <unit id="com.jcraft.jsch.source" version="0.1.54.v20170116-1932"/>
+ <unit id="org.junit" version="4.12.0.v201504281640"/>
+ <unit id="org.junit.source" version="4.12.0.v201504281640"/>
+ <unit id="javax.servlet" version="3.1.0.v201410161800"/>
+ <unit id="javax.servlet.source" version="3.1.0.v201410161800"/>
+ <unit id="org.tukaani.xz" version="1.6.0.v20170629-1752"/>
+ <unit id="org.tukaani.xz.source" version="1.6.0.v20170629-1752"/>
+ <unit id="org.slf4j.api" version="1.7.2.v20121108-1250"/>
+ <unit id="org.slf4j.api.source" version="1.7.2.v20121108-1250"/>
+ <unit id="org.slf4j.impl.log4j12" version="1.7.2.v20131105-2200"/>
+ <unit id="org.slf4j.impl.log4j12.source" version="1.7.2.v20131105-2200"/>
+ <unit id="com.jcraft.jzlib" version="1.1.1.v201205102305"/>
+ <unit id="com.jcraft.jzlib.source" version="1.1.1.v201205102305"/>
+ <unit id="net.i2p.crypto.eddsa" version="0.3.0.v20181102-1323"/>
+ <unit id="net.i2p.crypto.eddsa.source" version="0.3.0.v20181102-1323"/>
+ <unit id="org.apache.sshd.core" version="2.0.0.v20181102-1323"/>
+ <unit id="org.apache.sshd.core.source" version="2.0.0.v20181102-1323"/>
+ <unit id="org.apache.sshd.sftp" version="2.0.0.v20181102-1323"/>
+ <unit id="org.apache.sshd.sftp.source" version="2.0.0.v20181102-1323"/>
+ <repository location="http://download.eclipse.org/tools/orbit/downloads/drops/R20181128170323/repository"/>
+ </location>
+ <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
+ <unit id="org.eclipse.osgi" version="0.0.0"/>
+ <repository location="http://download.eclipse.org/releases/2018-09/"/>
+ </location>
+ </locations>
+</target>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.9.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.9.tpd
new file mode 100644
index 0000000000..0b6277d271
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.9.tpd
@@ -0,0 +1,8 @@
+target "jgit-4.9" with source configurePhase
+
+include "projects/jetty-9.4.11.tpd"
+include "orbit/R20181128170323-2018-12.tpd"
+
+location "http://download.eclipse.org/releases/2018-09/" {
+ org.eclipse.osgi lazy
+} \ No newline at end of file
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20181128170323-2018-12.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20181128170323-2018-12.tpd
new file mode 100644
index 0000000000..d5a257d1df
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20181128170323-2018-12.tpd
@@ -0,0 +1,58 @@
+target "R20181128170323-2018-12" with source configurePhase
+// see http://download.eclipse.org/tools/orbit/downloads/
+
+location "http://download.eclipse.org/tools/orbit/downloads/drops/R20181128170323/repository" {
+ org.apache.ant [1.10.5.v20180808-0324,1.10.5.v20180808-0324]
+ org.apache.ant.source [1.10.5.v20180808-0324,1.10.5.v20180808-0324]
+ org.apache.commons.codec [1.10.0.v20180409-1845,1.10.0.v20180409-1845]
+ org.apache.commons.codec.source [1.10.0.v20180409-1845,1.10.0.v20180409-1845]
+ org.apache.commons.compress [1.18.0.v20181121-2221,1.18.0.v20181121-2221]
+ org.apache.commons.compress.source [1.18.0.v20181121-2221,1.18.0.v20181121-2221]
+ org.apache.commons.logging [1.2.0.v20180409-1502,1.2.0.v20180409-1502]
+ org.apache.commons.logging.source [1.2.0.v20180409-1502,1.2.0.v20180409-1502]
+ org.apache.httpcomponents.httpclient [4.5.5.v20180409-1525,4.5.5.v20180409-1525]
+ org.apache.httpcomponents.httpclient.source [4.5.5.v20180409-1525,4.5.5.v20180409-1525]
+ org.apache.httpcomponents.httpcore [4.4.9.v20180409-1525,4.4.9.v20180409-1525]
+ org.apache.httpcomponents.httpcore.source [4.4.9.v20180409-1525,4.4.9.v20180409-1525]
+ org.apache.log4j [1.2.15.v201012070815,1.2.15.v201012070815]
+ org.apache.log4j.source [1.2.15.v201012070815,1.2.15.v201012070815]
+ org.kohsuke.args4j [2.33.0.v20160323-2218,2.33.0.v20160323-2218]
+ org.kohsuke.args4j.source [2.33.0.v20160323-2218,2.33.0.v20160323-2218]
+ org.hamcrest [1.1.0.v20090501071000,1.1.0.v20090501071000]
+ org.hamcrest.core [1.3.0.v20180420-1519,1.3.0.v20180420-1519]
+ org.hamcrest.core.source [1.3.0.v20180420-1519,1.3.0.v20180420-1519]
+ org.hamcrest.library [1.3.0.v20180524-2246,1.3.0.v20180524-2246]
+ org.hamcrest.library.source [1.3.0.v20180524-2246,1.3.0.v20180524-2246]
+ javaewah [1.1.6.v20160919-1400,1.1.6.v20160919-1400]
+ javaewah.source [1.1.6.v20160919-1400,1.1.6.v20160919-1400]
+ org.objenesis [2.6.0.v20180420-1519,2.6.0.v20180420-1519]
+ org.objenesis.source [2.6.0.v20180420-1519,2.6.0.v20180420-1519]
+ org.mockito [2.13.0.v20180426-1843,2.13.0.v20180426-1843]
+ org.mockito.source [2.13.0.v20180426-1843,2.13.0.v20180426-1843]
+ net.bytebuddy.byte-buddy [1.7.9.v20180420-1519,1.7.9.v20180420-1519]
+ net.bytebuddy.byte-buddy.source [1.7.9.v20180420-1519,1.7.9.v20180420-1519]
+ net.bytebuddy.byte-buddy-agent [1.7.9.v20180420-1519,1.7.9.v20180420-1519]
+ net.bytebuddy.byte-buddy-agent.source [1.7.9.v20180420-1519,1.7.9.v20180420-1519]
+ com.google.gson [2.8.2.v20180104-1110,2.8.2.v20180104-1110]
+ com.google.gson.source [2.8.2.v20180104-1110,2.8.2.v20180104-1110]
+ com.jcraft.jsch [0.1.54.v20170116-1932,0.1.54.v20170116-1932]
+ com.jcraft.jsch.source [0.1.54.v20170116-1932,0.1.54.v20170116-1932]
+ org.junit [4.12.0.v201504281640,4.12.0.v201504281640]
+ org.junit.source [4.12.0.v201504281640,4.12.0.v201504281640]
+ javax.servlet [3.1.0.v201410161800,3.1.0.v201410161800]
+ javax.servlet.source [3.1.0.v201410161800,3.1.0.v201410161800]
+ org.tukaani.xz [1.6.0.v20170629-1752,1.6.0.v20170629-1752]
+ org.tukaani.xz.source [1.6.0.v20170629-1752,1.6.0.v20170629-1752]
+ org.slf4j.api [1.7.2.v20121108-1250,1.7.2.v20121108-1250]
+ org.slf4j.api.source [1.7.2.v20121108-1250,1.7.2.v20121108-1250]
+ org.slf4j.impl.log4j12 [1.7.2.v20131105-2200,1.7.2.v20131105-2200]
+ org.slf4j.impl.log4j12.source [1.7.2.v20131105-2200,1.7.2.v20131105-2200]
+ com.jcraft.jzlib [1.1.1.v201205102305,1.1.1.v201205102305]
+ com.jcraft.jzlib.source [1.1.1.v201205102305,1.1.1.v201205102305]
+ net.i2p.crypto.eddsa [0.3.0.v20181102-1323,0.3.0.v20181102-1323]
+ net.i2p.crypto.eddsa.source [0.3.0.v20181102-1323,0.3.0.v20181102-1323]
+ org.apache.sshd.core [2.0.0.v20181102-1323,2.0.0.v20181102-1323]
+ org.apache.sshd.core.source [2.0.0.v20181102-1323,2.0.0.v20181102-1323]
+ org.apache.sshd.sftp [2.0.0.v20181102-1323,2.0.0.v20181102-1323]
+ org.apache.sshd.sftp.source [2.0.0.v20181102-1323,2.0.0.v20181102-1323]
+}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/pom.xml
index f25796f13d..dba70d3238 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/pom.xml
@@ -49,7 +49,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>jgit.tycho.parent</artifactId>
- <version>5.1.9-SNAPSHOT</version>
+ <version>5.2.3-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.target</artifactId>
@@ -82,4 +82,4 @@
</plugin>
</plugins>
</build>
-</project> \ No newline at end of file
+</project>
diff --git a/org.eclipse.jgit.packaging/pom.xml b/org.eclipse.jgit.packaging/pom.xml
index d763cb03b9..bc069c6265 100644
--- a/org.eclipse.jgit.packaging/pom.xml
+++ b/org.eclipse.jgit.packaging/pom.xml
@@ -53,7 +53,7 @@
<groupId>org.eclipse.jgit</groupId>
<artifactId>jgit.tycho.parent</artifactId>
- <version>5.1.9-SNAPSHOT</version>
+ <version>5.2.3-SNAPSHOT</version>
<packaging>pom</packaging>
<name>JGit Tycho Parent</name>
@@ -79,10 +79,12 @@
<module>org.eclipse.jgit.target</module>
<module>org.eclipse.jgit.feature</module>
<module>org.eclipse.jgit.http.apache.feature</module>
+ <module>org.eclipse.jgit.ssh.apache.feature</module>
<module>org.eclipse.jgit.lfs.feature</module>
<module>org.eclipse.jgit.pgm.feature</module>
<module>org.eclipse.jgit.source.feature</module>
<module>org.eclipse.jgit.pgm.source.feature</module>
+ <module>org.eclipse.jgit.ssh.apache.source.feature</module>
<module>org.eclipse.jgit.junit.feature</module>
<module>org.eclipse.jgit.repository</module>
</modules>
@@ -115,6 +117,12 @@
<version>${project.version}</version>
<classifier>sources</classifier>
</dependency>
+ <dependency>
+ <groupId>org.eclipse.jgit</groupId>
+ <artifactId>org.eclipse.jgit.ssh.apache</artifactId>
+ <version>${project.version}</version>
+ <classifier>sources</classifier>
+ </dependency>
</dependencies>
<build>
diff --git a/org.eclipse.jgit.pgm.test/.classpath b/org.eclipse.jgit.pgm.test/.classpath
index b26f4c45d1..1334739b76 100644
--- a/org.eclipse.jgit.pgm.test/.classpath
+++ b/org.eclipse.jgit.pgm.test/.classpath
@@ -1,7 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
- <classpathentry kind="src" path="tst"/>
- <classpathentry kind="src" path="src"/>
+ <classpathentry kind="src" path="tst">
+ <attributes>
+ <attribute name="test" value="true"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry kind="src" path="src">
+ <attributes>
+ <attribute name="test" value="true"/>
+ </attributes>
+ </classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="output" path="bin"/>
diff --git a/org.eclipse.jgit.pgm.test/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.pgm.test/.settings/org.eclipse.jdt.core.prefs
index 794592dee1..2ca78ff2d0 100644
--- a/org.eclipse.jgit.pgm.test/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.pgm.test/.settings/org.eclipse.jdt.core.prefs
@@ -91,7 +91,7 @@ org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled
org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning
org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning
-org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=ignore
org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error
org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
diff --git a/org.eclipse.jgit.pgm.test/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.pgm.test/.settings/org.eclipse.mylyn.team.ui.prefs
index 0cba949fb7..2fca432276 100644
--- a/org.eclipse.jgit.pgm.test/.settings/org.eclipse.mylyn.team.ui.prefs
+++ b/org.eclipse.jgit.pgm.test/.settings/org.eclipse.mylyn.team.ui.prefs
@@ -1,3 +1,3 @@
#Tue Jul 19 20:11:28 CEST 2011
-commit.comment.template=${task.description} \n\nBug\: ${task.key}
+commit.comment.template=${task.description}\n\nBug\: ${task.key}
eclipse.preferences.version=1
diff --git a/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF
index 97a7ba22d8..90398a956c 100644
--- a/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF
@@ -3,28 +3,28 @@ Bundle-ManifestVersion: 2
Bundle-Name: %plugin_name
Automatic-Module-Name: org.eclipse.jgit.pgm.test
Bundle-SymbolicName: org.eclipse.jgit.pgm.test
-Bundle-Version: 5.1.9.qualifier
+Bundle-Version: 5.2.3.qualifier
Bundle-Vendor: %provider_name
Bundle-Localization: plugin
Bundle-ActivationPolicy: lazy
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
-Import-Package: org.eclipse.jgit.api;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.api.errors;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.diff;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.dircache;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.internal.storage.file;version="5.1.9",
- org.eclipse.jgit.junit;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.lib;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.merge;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.pgm;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.pgm.internal;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.pgm.opt;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.revwalk;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.storage.file;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.transport;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.treewalk;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.util;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.util.io;version="[5.1.9,5.2.0)",
+Import-Package: org.eclipse.jgit.api;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.api.errors;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.diff;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.dircache;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.internal.storage.file;version="5.2.3",
+ org.eclipse.jgit.junit;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.lib;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.merge;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.pgm;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.pgm.internal;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.pgm.opt;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.revwalk;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.storage.file;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.transport;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.treewalk;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.util;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.util.io;version="[5.2.3,5.3.0)",
org.hamcrest.core;bundle-version="[1.1.0,2.0.0)",
org.junit;version="[4.12,5.0.0)",
org.junit.rules;version="[4.12,5.0.0)",
diff --git a/org.eclipse.jgit.pgm.test/pom.xml b/org.eclipse.jgit.pgm.test/pom.xml
index d5641bec1c..ced7168068 100644
--- a/org.eclipse.jgit.pgm.test/pom.xml
+++ b/org.eclipse.jgit.pgm.test/pom.xml
@@ -50,7 +50,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>5.1.9-SNAPSHOT</version>
+ <version>5.2.3-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.pgm.test</artifactId>
@@ -109,7 +109,7 @@
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
- <argLine>@{argLine} -Xmx512m -Djava.io.tmpdir=${project.build.directory}</argLine>
+ <argLine>-Djava.io.tmpdir=${project.build.directory}</argLine>
</configuration>
</plugin>
</plugins>
diff --git a/org.eclipse.jgit.pgm.test/src/org/eclipse/jgit/pgm/CLIGitCommand.java b/org.eclipse.jgit.pgm.test/src/org/eclipse/jgit/pgm/CLIGitCommand.java
index 81875f11bc..9ad22ddf2e 100644
--- a/org.eclipse.jgit.pgm.test/src/org/eclipse/jgit/pgm/CLIGitCommand.java
+++ b/org.eclipse.jgit.pgm.test/src/org/eclipse/jgit/pgm/CLIGitCommand.java
@@ -42,11 +42,13 @@
*/
package org.eclipse.jgit.pgm;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertNull;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
+import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
@@ -153,7 +155,8 @@ public class CLIGitCommand extends Main {
@Override
PrintWriter createErrorWriter() {
- return new PrintWriter(result.err);
+ return new PrintWriter(new OutputStreamWriter(
+ result.err, UTF_8));
}
@Override
@@ -249,19 +252,19 @@ public class CLIGitCommand extends Main {
}
public String outString() {
- return out.toString();
+ return new String(out.toByteArray(), UTF_8);
}
public List<String> outLines() {
- return IO.readLines(out.toString());
+ return IO.readLines(outString());
}
public String errString() {
- return err.toString();
+ return new String(err.toByteArray(), UTF_8);
}
public List<String> errLines() {
- return IO.readLines(err.toString());
+ return IO.readLines(errString());
}
}
diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ProxyConfigTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ProxyConfigTest.java
index 40a223d92f..42530f3a34 100644
--- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ProxyConfigTest.java
+++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ProxyConfigTest.java
@@ -37,6 +37,7 @@
*/
package org.eclipse.jgit.pgm;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertEquals;
import java.io.ByteArrayOutputStream;
@@ -47,7 +48,6 @@ import java.net.MalformedURLException;
import java.util.List;
import java.util.Map;
-import org.eclipse.jgit.lib.Constants;
import org.junit.Before;
import org.junit.Test;
@@ -204,7 +204,7 @@ public class ProxyConfigTest {
while ((length = inputStream.read(buffer)) != -1) {
result.write(buffer, 0, length);
}
- return result.toString(Constants.CHARACTER_ENCODING);
+ return result.toString(UTF_8.name());
}
}
}
diff --git a/org.eclipse.jgit.pgm/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.pgm/.settings/org.eclipse.jdt.core.prefs
index 13c32a6d94..ef6f5e732f 100644
--- a/org.eclipse.jgit.pgm/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.pgm/.settings/org.eclipse.jdt.core.prefs
@@ -91,7 +91,7 @@ org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled
org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning
org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning
-org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=ignore
org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error
org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
diff --git a/org.eclipse.jgit.pgm/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.pgm/.settings/org.eclipse.mylyn.team.ui.prefs
index 0cba949fb7..2fca432276 100644
--- a/org.eclipse.jgit.pgm/.settings/org.eclipse.mylyn.team.ui.prefs
+++ b/org.eclipse.jgit.pgm/.settings/org.eclipse.mylyn.team.ui.prefs
@@ -1,3 +1,3 @@
#Tue Jul 19 20:11:28 CEST 2011
-commit.comment.template=${task.description} \n\nBug\: ${task.key}
+commit.comment.template=${task.description}\n\nBug\: ${task.key}
eclipse.preferences.version=1
diff --git a/org.eclipse.jgit.pgm/BUILD b/org.eclipse.jgit.pgm/BUILD
index ddc84bea06..1a115dd427 100644
--- a/org.eclipse.jgit.pgm/BUILD
+++ b/org.eclipse.jgit.pgm/BUILD
@@ -7,6 +7,7 @@ java_library(
deps = [
":services",
"//lib:args4j",
+ "//lib:commons-logging",
"//lib:httpclient",
"//lib:httpcore",
"//lib:jetty-http",
@@ -21,6 +22,7 @@ java_library(
"//org.eclipse.jgit:jgit",
"//org.eclipse.jgit.lfs:jgit-lfs",
"//org.eclipse.jgit.lfs.server:jgit-lfs-server",
+ "//org.eclipse.jgit.ssh.apache:ssh-apache",
"//org.eclipse.jgit.ui:ui",
],
)
diff --git a/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF b/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF
index ce4d6efc75..e10a772a8c 100644
--- a/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF
@@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2
Bundle-Name: %plugin_name
Automatic-Module-Name: org.eclipse.jgit.pgm
Bundle-SymbolicName: org.eclipse.jgit.pgm
-Bundle-Version: 5.1.9.qualifier
+Bundle-Version: 5.2.3.qualifier
Bundle-Vendor: %provider_name
Bundle-ActivationPolicy: lazy
Bundle-Localization: plugin
@@ -28,49 +28,50 @@ Import-Package: javax.servlet;version="[3.1.0,4.0.0)",
org.eclipse.jetty.util.log;version="[9.4.5,10.0.0)",
org.eclipse.jetty.util.security;version="[9.4.5,10.0.0)",
org.eclipse.jetty.util.thread;version="[9.4.5,10.0.0)",
- org.eclipse.jgit.api;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.api.errors;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.archive;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.awtui;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.blame;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.diff;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.dircache;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.errors;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.gitrepo;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.internal.ketch;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.internal.storage.dfs;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.internal.storage.file;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.internal.storage.io;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.internal.storage.pack;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.internal.storage.reftable;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.internal.storage.reftree;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.lfs;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.lfs.lib;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.lfs.server;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.lfs.server.fs;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.lfs.server.s3;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.lib;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.merge;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.nls;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.notes;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.revplot;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.revwalk;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.revwalk.filter;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.storage.file;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.storage.pack;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.transport;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.transport.http.apache;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.transport.resolver;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.treewalk;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.treewalk.filter;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.util;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.util.io;version="[5.1.9,5.2.0)",
+ org.eclipse.jgit.api;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.api.errors;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.archive;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.awtui;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.blame;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.diff;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.dircache;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.errors;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.gitrepo;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.internal.ketch;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.internal.storage.dfs;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.internal.storage.file;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.internal.storage.io;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.internal.storage.pack;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.internal.storage.reftable;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.internal.storage.reftree;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.lfs;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.lfs.lib;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.lfs.server;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.lfs.server.fs;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.lfs.server.s3;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.lib;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.merge;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.nls;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.notes;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.revplot;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.revwalk;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.revwalk.filter;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.storage.file;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.storage.pack;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.transport;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.transport.http.apache;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.transport.resolver;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.transport.sshd;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.treewalk;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.treewalk.filter;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.util;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.util.io;version="[5.2.3,5.3.0)",
org.kohsuke.args4j;version="[2.33.0,3.0.0)",
org.kohsuke.args4j.spi;version="[2.33.0,3.0.0)"
-Export-Package: org.eclipse.jgit.console;version="5.1.9";
+Export-Package: org.eclipse.jgit.console;version="5.2.3";
uses:="org.eclipse.jgit.transport,
org.eclipse.jgit.util",
- org.eclipse.jgit.pgm;version="5.1.9";
+ org.eclipse.jgit.pgm;version="5.2.3";
uses:="org.eclipse.jgit.revwalk,
org.eclipse.jgit.treewalk.filter,
org.eclipse.jgit.pgm.opt,
@@ -81,11 +82,11 @@ Export-Package: org.eclipse.jgit.console;version="5.1.9";
org.eclipse.jgit.treewalk,
javax.swing,
org.eclipse.jgit.transport",
- org.eclipse.jgit.pgm.debug;version="5.1.9";
+ org.eclipse.jgit.pgm.debug;version="5.2.3";
uses:="org.eclipse.jgit.util.io,
org.eclipse.jgit.pgm",
- org.eclipse.jgit.pgm.internal;version="5.1.9";x-friends:="org.eclipse.jgit.pgm.test,org.eclipse.jgit.test",
- org.eclipse.jgit.pgm.opt;version="5.1.9";
+ org.eclipse.jgit.pgm.internal;version="5.2.3";x-friends:="org.eclipse.jgit.pgm.test,org.eclipse.jgit.test",
+ org.eclipse.jgit.pgm.opt;version="5.2.3";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.revwalk,
org.kohsuke.args4j.spi,
diff --git a/org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF
index 2c4fba8083..9831e1347e 100644
--- a/org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@ Bundle-ManifestVersion: 2
Bundle-Name: org.eclipse.jgit.pgm - Sources
Bundle-SymbolicName: org.eclipse.jgit.pgm.source
Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 5.1.9.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.pgm;version="5.1.9.qualifier";roots="."
+Bundle-Version: 5.2.3.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.pgm;version="5.2.3.qualifier";roots="."
diff --git a/org.eclipse.jgit.pgm/pom.xml b/org.eclipse.jgit.pgm/pom.xml
index 98f579a9fb..81ef1b3f70 100644
--- a/org.eclipse.jgit.pgm/pom.xml
+++ b/org.eclipse.jgit.pgm/pom.xml
@@ -50,7 +50,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>5.1.9-SNAPSHOT</version>
+ <version>5.2.3-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.pgm</artifactId>
@@ -101,6 +101,12 @@
</dependency>
<dependency>
+ <groupId>org.eclipse.jgit</groupId>
+ <artifactId>org.eclipse.jgit.ssh.apache</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
diff --git a/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties b/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties
index 8ff9c6bb10..d7d895ab31 100644
--- a/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties
+++ b/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties
@@ -402,6 +402,7 @@ usage_showNotes=Add this ref to the list of note branches from which notes are d
usage_showTimeInMilliseconds=Show mtime in milliseconds
usage_squash=Squash commits as if a real merge happened, but do not make a commit or move the HEAD.
usage_srcPrefix=show the source prefix instead of "a/"
+usage_sshDriver=Selects the built-in ssh library to use, JSch or Apache MINA sshd.
usage_symbolicVersionForTheProject=Symbolic version for the project
usage_tags=fetch all tags
usage_notags=do not fetch tags
@@ -411,7 +412,7 @@ usage_untrackedFilesMode=show untracked files
usage_updateRef=reference to update
usage_updateRemoteRefsFromAnotherRepository=Update remote refs from another repository
usage_useNameInsteadOfOriginToTrackUpstream=use <name> instead of 'origin' to track upstream
-usage_checkoutBranchAfterClone=checkout named branch instead of remotes's HEAD
+usage_checkoutBranchAfterClone=checkout named branch instead of remote's HEAD
usage_viewCommitHistory=View commit history
-usage_orphan=Create a new orphan branch. The first commit made on this new branch will have no parents amd it will be the root of a new history totally disconnected from other branches and commits.
+usage_orphan=Create a new orphan branch. The first commit made on this new branch will have no parents and it will be the root of a new history totally disconnected from other branches and commits.
usernameFor=Username for {0}:
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/TextBuiltin.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/TextBuiltin.java
index 7e5b5451b6..c4b4018b8f 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/TextBuiltin.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/TextBuiltin.java
@@ -44,6 +44,9 @@
package org.eclipse.jgit.pgm;
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_SECTION_I18N;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_LOG_OUTPUT_ENCODING;
import static org.eclipse.jgit.lib.Constants.R_HEADS;
import static org.eclipse.jgit.lib.Constants.R_REMOTES;
import static org.eclipse.jgit.lib.Constants.R_TAGS;
@@ -56,14 +59,20 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
+import java.nio.charset.Charset;
import java.text.MessageFormat;
import java.util.ResourceBundle;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.pgm.internal.CLIText;
+import org.eclipse.jgit.pgm.internal.SshDriver;
import org.eclipse.jgit.pgm.opt.CmdLineParser;
import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.transport.SshSessionFactory;
+import org.eclipse.jgit.transport.sshd.DefaultProxyDataFactory;
+import org.eclipse.jgit.transport.sshd.JGitKeyCache;
+import org.eclipse.jgit.transport.sshd.SshdSessionFactory;
import org.eclipse.jgit.util.io.ThrowingPrintWriter;
import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.args4j.Option;
@@ -85,6 +94,9 @@ public abstract class TextBuiltin {
@Option(name = "--help", usage = "usage_displayThisHelpText", aliases = { "-h" })
private boolean help;
+ @Option(name = "--ssh", usage = "usage_sshDriver")
+ private SshDriver sshDriver = SshDriver.JSCH;
+
/**
* Input stream, typically this is standard input.
*
@@ -168,6 +180,30 @@ public abstract class TextBuiltin {
}
/**
+ * Get the log output encoding specified in the repository's
+ * {@code i18n.logOutputEncoding} configuration.
+ *
+ * @param repository
+ * the repository.
+ * @return Charset corresponding to {@code i18n.logOutputEncoding}, or
+ * {@code UTF_8}.
+ */
+ private Charset getLogOutputEncodingCharset(Repository repository) {
+ if (repository != null) {
+ String logOutputEncoding = repository.getConfig().getString(
+ CONFIG_SECTION_I18N, null, CONFIG_KEY_LOG_OUTPUT_ENCODING);
+ if (logOutputEncoding != null) {
+ try {
+ return Charset.forName(logOutputEncoding);
+ } catch (IllegalArgumentException e) {
+ throw die(CLIText.get().cannotCreateOutputStream);
+ }
+ }
+ }
+ return UTF_8;
+ }
+
+ /**
* Initialize the command to work with a repository.
*
* @param repository
@@ -177,32 +213,18 @@ public abstract class TextBuiltin {
* {@code repository} is null.
*/
protected void init(Repository repository, String gitDir) {
- try {
- final String outputEncoding = repository != null ? repository
- .getConfig().getString("i18n", null, "logOutputEncoding") : null; //$NON-NLS-1$ //$NON-NLS-2$
- if (ins == null)
- ins = new FileInputStream(FileDescriptor.in);
- if (outs == null)
- outs = new FileOutputStream(FileDescriptor.out);
- if (errs == null)
- errs = new FileOutputStream(FileDescriptor.err);
- BufferedWriter outbufw;
- if (outputEncoding != null)
- outbufw = new BufferedWriter(new OutputStreamWriter(outs,
- outputEncoding));
- else
- outbufw = new BufferedWriter(new OutputStreamWriter(outs));
- outw = new ThrowingPrintWriter(outbufw);
- BufferedWriter errbufw;
- if (outputEncoding != null)
- errbufw = new BufferedWriter(new OutputStreamWriter(errs,
- outputEncoding));
- else
- errbufw = new BufferedWriter(new OutputStreamWriter(errs));
- errw = new ThrowingPrintWriter(errbufw);
- } catch (IOException e) {
- throw die(CLIText.get().cannotCreateOutputStream);
- }
+ Charset charset = getLogOutputEncodingCharset(repository);
+
+ if (ins == null)
+ ins = new FileInputStream(FileDescriptor.in);
+ if (outs == null)
+ outs = new FileOutputStream(FileDescriptor.out);
+ if (errs == null)
+ errs = new FileOutputStream(FileDescriptor.err);
+ outw = new ThrowingPrintWriter(new BufferedWriter(
+ new OutputStreamWriter(outs, charset)));
+ errw = new ThrowingPrintWriter(new BufferedWriter(
+ new OutputStreamWriter(errs, charset)));
if (repository != null && repository.getDirectory() != null) {
db = repository;
@@ -225,6 +247,20 @@ public abstract class TextBuiltin {
*/
public final void execute(String[] args) throws Exception {
parseArguments(args);
+ switch (sshDriver) {
+ case APACHE: {
+ SshdSessionFactory factory = new SshdSessionFactory(
+ new JGitKeyCache(), new DefaultProxyDataFactory());
+ Runtime.getRuntime()
+ .addShutdownHook(new Thread(() -> factory.close()));
+ SshSessionFactory.setInstance(factory);
+ break;
+ }
+ case JSCH:
+ default:
+ SshSessionFactory.setInstance(null);
+ break;
+ }
run();
}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/TextHashFunctions.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/TextHashFunctions.java
index bb51b50487..300a01d246 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/TextHashFunctions.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/TextHashFunctions.java
@@ -474,7 +474,7 @@ class TextHashFunctions extends TextBuiltin {
}
/** Utility to help us identify unique lines in a file. */
- private class Line {
+ private static class Line {
private final RawText txt;
private final int pos;
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/SshDriver.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/SshDriver.java
new file mode 100644
index 0000000000..7d0423b014
--- /dev/null
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/SshDriver.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.pgm.internal;
+
+/**
+ * Simple enumeration for the available built-in ssh clients.
+ */
+public enum SshDriver {
+
+ /** Default client: use JSch. */
+ JSCH,
+
+ /** Use the Apache MINA sshd client from org.eclipse.jgit.ssh.apache. */
+ APACHE;
+
+}
diff --git a/org.eclipse.jgit.ssh.apache.test/.classpath b/org.eclipse.jgit.ssh.apache.test/.classpath
new file mode 100644
index 0000000000..f08af0a4e9
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/.classpath
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="tst">
+ <attributes>
+ <attribute name="test" value="true"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/org.eclipse.jgit.ssh.apache.test/.gitignore b/org.eclipse.jgit.ssh.apache.test/.gitignore
new file mode 100644
index 0000000000..934e0e06ff
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/.gitignore
@@ -0,0 +1,2 @@
+/bin
+/target
diff --git a/org.eclipse.jgit.ssh.apache.test/.project b/org.eclipse.jgit.ssh.apache.test/.project
new file mode 100644
index 0000000000..0aafb730fb
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.eclipse.jgit.ssh.apache.test</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/org.eclipse.jgit.ssh.apache.test/.settings/org.eclipse.core.resources.prefs b/org.eclipse.jgit.ssh.apache.test/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000000..f77db3b723
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,3 @@
+#Sat Dec 20 21:21:24 CET 2008
+eclipse.preferences.version=1
+encoding/<project>=UTF-8
diff --git a/org.eclipse.jgit.ssh.apache.test/.settings/org.eclipse.core.runtime.prefs b/org.eclipse.jgit.ssh.apache.test/.settings/org.eclipse.core.runtime.prefs
new file mode 100644
index 0000000000..9f733eeea7
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/.settings/org.eclipse.core.runtime.prefs
@@ -0,0 +1,3 @@
+#Mon Mar 24 18:55:56 EDT 2008
+eclipse.preferences.version=1
+line.separator=\n
diff --git a/org.eclipse.jgit.ssh.apache.test/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.ssh.apache.test/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000000..794592dee1
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,399 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=disabled
+org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore
+org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull
+org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault
+org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable
+org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.8
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.doc.comment.support=enabled
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.autoboxing=warning
+org.eclipse.jdt.core.compiler.problem.comparingIdentical=error
+org.eclipse.jdt.core.compiler.problem.deadCode=error
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
+org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=warning
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=error
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=error
+org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=enabled
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=warning
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=error
+org.eclipse.jdt.core.compiler.problem.invalidJavadoc=error
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=private
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=error
+org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled
+org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=error
+org.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=protected
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag
+org.eclipse.jdt.core.compiler.problem.missingJavadocTags=error
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
+org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=error
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=error
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
+org.eclipse.jdt.core.compiler.problem.nonnullParameterAnnotationDropped=warning
+org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error
+org.eclipse.jdt.core.compiler.problem.nullReference=error
+org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error
+org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=warning
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=error
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning
+org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=ignore
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=ignore
+org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning
+org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=warning
+org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=error
+org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
+org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=error
+org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=disabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
+org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=error
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.unusedExceptionParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedImport=error
+org.eclipse.jdt.core.compiler.problem.unusedLabel=error
+org.eclipse.jdt.core.compiler.problem.unusedLocal=error
+org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameter=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=error
+org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=error
+org.eclipse.jdt.core.compiler.source=1.8
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=0
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=1
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=false
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert
+org.eclipse.jdt.core.formatter.comment.line_length=80
+org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true
+org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true
+org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off
+org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.join_lines_in_comments=true
+org.eclipse.jdt.core.formatter.join_wrapped_lines=true
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=80
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
+org.eclipse.jdt.core.formatter.tabulation.char=tab
+org.eclipse.jdt.core.formatter.tabulation.size=4
+org.eclipse.jdt.core.formatter.use_on_off_tags=true
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true
+org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true
diff --git a/org.eclipse.jgit.ssh.apache.test/.settings/org.eclipse.jdt.ui.prefs b/org.eclipse.jgit.ssh.apache.test/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 0000000000..fef3713825
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,66 @@
+eclipse.preferences.version=1
+editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
+formatter_profile=_JGit Format
+formatter_settings_version=12
+org.eclipse.jdt.ui.ignorelowercasenames=true
+org.eclipse.jdt.ui.importorder=java;javax;org;com;
+org.eclipse.jdt.ui.ondemandthreshold=99
+org.eclipse.jdt.ui.staticondemandthreshold=99
+org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8"?><templates/>
+sp_cleanup.add_default_serial_version_id=true
+sp_cleanup.add_generated_serial_version_id=false
+sp_cleanup.add_missing_annotations=true
+sp_cleanup.add_missing_deprecated_annotations=true
+sp_cleanup.add_missing_methods=false
+sp_cleanup.add_missing_nls_tags=false
+sp_cleanup.add_missing_override_annotations=true
+sp_cleanup.add_missing_override_annotations_interface_methods=true
+sp_cleanup.add_serial_version_id=false
+sp_cleanup.always_use_blocks=true
+sp_cleanup.always_use_parentheses_in_expressions=false
+sp_cleanup.always_use_this_for_non_static_field_access=false
+sp_cleanup.always_use_this_for_non_static_method_access=false
+sp_cleanup.convert_functional_interfaces=false
+sp_cleanup.convert_to_enhanced_for_loop=false
+sp_cleanup.correct_indentation=false
+sp_cleanup.format_source_code=true
+sp_cleanup.format_source_code_changes_only=true
+sp_cleanup.insert_inferred_type_arguments=false
+sp_cleanup.make_local_variable_final=false
+sp_cleanup.make_parameters_final=false
+sp_cleanup.make_private_fields_final=true
+sp_cleanup.make_type_abstract_if_missing_method=false
+sp_cleanup.make_variable_declarations_final=false
+sp_cleanup.never_use_blocks=false
+sp_cleanup.never_use_parentheses_in_expressions=true
+sp_cleanup.on_save_use_additional_actions=true
+sp_cleanup.organize_imports=false
+sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
+sp_cleanup.remove_private_constructors=true
+sp_cleanup.remove_redundant_type_arguments=true
+sp_cleanup.remove_trailing_whitespaces=true
+sp_cleanup.remove_trailing_whitespaces_all=true
+sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
+sp_cleanup.remove_unnecessary_casts=true
+sp_cleanup.remove_unnecessary_nls_tags=true
+sp_cleanup.remove_unused_imports=false
+sp_cleanup.remove_unused_local_variables=false
+sp_cleanup.remove_unused_private_fields=true
+sp_cleanup.remove_unused_private_members=false
+sp_cleanup.remove_unused_private_methods=true
+sp_cleanup.remove_unused_private_types=true
+sp_cleanup.sort_members=false
+sp_cleanup.sort_members_all=false
+sp_cleanup.use_anonymous_class_creation=false
+sp_cleanup.use_blocks=false
+sp_cleanup.use_blocks_only_for_return_and_throw=false
+sp_cleanup.use_lambda=false
+sp_cleanup.use_parentheses_in_expressions=false
+sp_cleanup.use_this_for_non_static_field_access=false
+sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+sp_cleanup.use_this_for_non_static_method_access=false
+sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
diff --git a/org.eclipse.jgit.ssh.apache.test/.settings/org.eclipse.mylyn.tasks.ui.prefs b/org.eclipse.jgit.ssh.apache.test/.settings/org.eclipse.mylyn.tasks.ui.prefs
new file mode 100644
index 0000000000..823c0f56ae
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/.settings/org.eclipse.mylyn.tasks.ui.prefs
@@ -0,0 +1,4 @@
+#Tue Jul 19 20:11:28 CEST 2011
+eclipse.preferences.version=1
+project.repository.kind=bugzilla
+project.repository.url=https\://bugs.eclipse.org/bugs
diff --git a/org.eclipse.jgit.ssh.apache.test/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.ssh.apache.test/.settings/org.eclipse.mylyn.team.ui.prefs
new file mode 100644
index 0000000000..0cba949fb7
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/.settings/org.eclipse.mylyn.team.ui.prefs
@@ -0,0 +1,3 @@
+#Tue Jul 19 20:11:28 CEST 2011
+commit.comment.template=${task.description} \n\nBug\: ${task.key}
+eclipse.preferences.version=1
diff --git a/org.eclipse.jgit.ssh.apache.test/.settings/org.eclipse.pde.api.tools.prefs b/org.eclipse.jgit.ssh.apache.test/.settings/org.eclipse.pde.api.tools.prefs
new file mode 100644
index 0000000000..c0030ded71
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/.settings/org.eclipse.pde.api.tools.prefs
@@ -0,0 +1,104 @@
+ANNOTATION_ELEMENT_TYPE_ADDED_FIELD=Error
+ANNOTATION_ELEMENT_TYPE_ADDED_METHOD_WITHOUT_DEFAULT_VALUE=Error
+ANNOTATION_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_FIELD=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_METHOD=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_API_TYPE=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_REEXPORTED_API_TYPE=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_REEXPORTED_TYPE=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_TYPE=Error
+API_USE_SCAN_FIELD_SEVERITY=Error
+API_USE_SCAN_METHOD_SEVERITY=Error
+API_USE_SCAN_TYPE_SEVERITY=Error
+CLASS_ELEMENT_TYPE_ADDED_FIELD=Error
+CLASS_ELEMENT_TYPE_ADDED_METHOD=Error
+CLASS_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error
+CLASS_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+CLASS_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error
+CLASS_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+CLASS_ELEMENT_TYPE_CHANGED_NON_ABSTRACT_TO_ABSTRACT=Error
+CLASS_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
+CLASS_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
+CLASS_ELEMENT_TYPE_REMOVED_CONSTRUCTOR=Error
+CLASS_ELEMENT_TYPE_REMOVED_FIELD=Error
+CLASS_ELEMENT_TYPE_REMOVED_METHOD=Error
+CLASS_ELEMENT_TYPE_REMOVED_SUPERCLASS=Error
+CLASS_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+CLASS_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+CONSTRUCTOR_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+CONSTRUCTOR_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+CONSTRUCTOR_ELEMENT_TYPE_CHANGED_VARARGS_TO_ARRAY=Error
+CONSTRUCTOR_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+ENUM_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error
+ENUM_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
+ENUM_ELEMENT_TYPE_REMOVED_ENUM_CONSTANT=Error
+ENUM_ELEMENT_TYPE_REMOVED_FIELD=Error
+ENUM_ELEMENT_TYPE_REMOVED_METHOD=Error
+ENUM_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+FIELD_ELEMENT_TYPE_ADDED_VALUE=Error
+FIELD_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+FIELD_ELEMENT_TYPE_CHANGED_FINAL_TO_NON_FINAL_STATIC_CONSTANT=Error
+FIELD_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
+FIELD_ELEMENT_TYPE_CHANGED_NON_STATIC_TO_STATIC=Error
+FIELD_ELEMENT_TYPE_CHANGED_STATIC_TO_NON_STATIC=Error
+FIELD_ELEMENT_TYPE_CHANGED_TYPE=Error
+FIELD_ELEMENT_TYPE_CHANGED_VALUE=Error
+FIELD_ELEMENT_TYPE_REMOVED_TYPE_ARGUMENT=Error
+FIELD_ELEMENT_TYPE_REMOVED_VALUE=Error
+ILLEGAL_EXTEND=Warning
+ILLEGAL_IMPLEMENT=Warning
+ILLEGAL_INSTANTIATE=Warning
+ILLEGAL_OVERRIDE=Warning
+ILLEGAL_REFERENCE=Warning
+INTERFACE_ELEMENT_TYPE_ADDED_DEFAULT_METHOD=Error
+INTERFACE_ELEMENT_TYPE_ADDED_FIELD=Error
+INTERFACE_ELEMENT_TYPE_ADDED_METHOD=Error
+INTERFACE_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error
+INTERFACE_ELEMENT_TYPE_ADDED_SUPER_INTERFACE_WITH_METHODS=Error
+INTERFACE_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+INTERFACE_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error
+INTERFACE_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_FIELD=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_METHOD=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+INVALID_ANNOTATION=Ignore
+INVALID_JAVADOC_TAG=Ignore
+INVALID_REFERENCE_IN_SYSTEM_LIBRARIES=Error
+LEAK_EXTEND=Warning
+LEAK_FIELD_DECL=Warning
+LEAK_IMPLEMENT=Warning
+LEAK_METHOD_PARAM=Warning
+LEAK_METHOD_RETURN_TYPE=Warning
+METHOD_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error
+METHOD_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+METHOD_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+METHOD_ELEMENT_TYPE_CHANGED_NON_ABSTRACT_TO_ABSTRACT=Error
+METHOD_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
+METHOD_ELEMENT_TYPE_CHANGED_NON_STATIC_TO_STATIC=Error
+METHOD_ELEMENT_TYPE_CHANGED_STATIC_TO_NON_STATIC=Error
+METHOD_ELEMENT_TYPE_CHANGED_VARARGS_TO_ARRAY=Error
+METHOD_ELEMENT_TYPE_REMOVED_ANNOTATION_DEFAULT_VALUE=Error
+METHOD_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+MISSING_EE_DESCRIPTIONS=Warning
+TYPE_PARAMETER_ELEMENT_TYPE_ADDED_CLASS_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_ADDED_INTERFACE_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_CHANGED_CLASS_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_CHANGED_INTERFACE_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_REMOVED_CLASS_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_REMOVED_INTERFACE_BOUND=Error
+UNUSED_PROBLEM_FILTERS=Warning
+automatically_removed_unused_problem_filters=false
+changed_execution_env=Error
+eclipse.preferences.version=1
+incompatible_api_component_version=Error
+incompatible_api_component_version_include_major_without_breaking_change=Disabled
+incompatible_api_component_version_include_minor_without_api_change=Disabled
+incompatible_api_component_version_report_major_without_breaking_change=Warning
+incompatible_api_component_version_report_minor_without_api_change=Ignore
+invalid_since_tag_version=Error
+malformed_since_tag=Error
+missing_since_tag=Error
+report_api_breakage_when_major_version_incremented=Disabled
+report_resolution_errors_api_component=Warning
diff --git a/org.eclipse.jgit.ssh.apache.test/.settings/org.eclipse.pde.core.prefs b/org.eclipse.jgit.ssh.apache.test/.settings/org.eclipse.pde.core.prefs
new file mode 100644
index 0000000000..82793f2d27
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/.settings/org.eclipse.pde.core.prefs
@@ -0,0 +1,3 @@
+#Thu Jan 14 14:34:32 CST 2010
+eclipse.preferences.version=1
+resolve.requirebundle=false
diff --git a/org.eclipse.jgit.ssh.apache.test/BUILD b/org.eclipse.jgit.ssh.apache.test/BUILD
new file mode 100644
index 0000000000..a13cf0b30f
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/BUILD
@@ -0,0 +1,19 @@
+load(
+ "@com_googlesource_gerrit_bazlets//tools:junit.bzl",
+ "junit_tests",
+)
+
+junit_tests(
+ name = "sshd_apache",
+ srcs = glob(["tst/**/*.java"]),
+ tags = ["sshd"],
+ deps = [
+ "//lib:eddsa",
+ "//lib:junit",
+ "//lib:sshd-core",
+ "//lib:sshd-sftp",
+ "//org.eclipse.jgit:jgit",
+ "//org.eclipse.jgit.ssh.apache:ssh-apache",
+ "//org.eclipse.jgit.test:sshd-helpers",
+ ],
+)
diff --git a/org.eclipse.jgit.ssh.apache.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.ssh.apache.test/META-INF/MANIFEST.MF
new file mode 100644
index 0000000000..5456949947
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/META-INF/MANIFEST.MF
@@ -0,0 +1,19 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %Bundle-Name
+Automatic-Module-Name: org.eclipse.jgit.ssh.apache.test
+Bundle-SymbolicName: org.eclipse.jgit.ssh.apache.test
+Bundle-Version: 5.2.3.qualifier
+Bundle-Vendor: %Provider-Name
+Bundle-RequiredExecutionEnvironment: JavaSE-1.8
+Import-Package: org.eclipse.jgit.internal.transport.sshd.proxy;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.junit;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.junit.ssh;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.lib;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.transport;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.transport.ssh;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.transport.sshd;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.util;version="[5.2.3,5.3.0)",
+ org.junit;version="[4.12,5.0.0)",
+ org.junit.experimental.theories;version="[4.12,5.0.0)",
+ org.junit.runner;version="[4.12,5.0.0)"
diff --git a/org.eclipse.jgit.ssh.apache.test/about.html b/org.eclipse.jgit.ssh.apache.test/about.html
new file mode 100644
index 0000000000..f971af18d0
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/about.html
@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
+<title>Eclipse Distribution License - Version 1.0</title>
+<style type="text/css">
+ body {
+ size: 8.5in 11.0in;
+ margin: 0.25in 0.5in 0.25in 0.5in;
+ tab-interval: 0.5in;
+ }
+ p {
+ margin-left: auto;
+ margin-top: 0.5em;
+ margin-bottom: 0.5em;
+ }
+ p.list {
+ margin-left: 0.5in;
+ margin-top: 0.05em;
+ margin-bottom: 0.05em;
+ }
+ .ubc-name {
+ margin-left: 0.5in;
+ white-space: pre;
+ }
+ </style>
+
+</head>
+
+<body lang="EN-US">
+
+<p><b>Eclipse Distribution License - v 1.0</b></p>
+
+<p>Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors. </p>
+
+<p>All rights reserved.</p>
+<p>Redistribution and use in source and binary forms, with or without modification,
+ are permitted provided that the following conditions are met:
+<ul><li>Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer. </li>
+<li>Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution. </li>
+<li>Neither the name of the Eclipse Foundation, Inc. nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission. </li></ul>
+</p>
+<p>THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.</p>
+
+<hr>
+<p><b>SHA-1 UbcCheck - MIT</b></p>
+
+<p>Copyright (c) 2017:</p>
+<div class="ubc-name">
+Marc Stevens
+Cryptology Group
+Centrum Wiskunde & Informatica
+P.O. Box 94079, 1090 GB Amsterdam, Netherlands
+marc@marc-stevens.nl
+</div>
+<div class="ubc-name">
+Dan Shumow
+Microsoft Research
+danshu@microsoft.com
+</div>
+<p>Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+</p>
+<ul><li>The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.</li></ul>
+<p>THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.</p>
+
+</body>
+
+</html>
diff --git a/org.eclipse.jgit.ssh.apache.test/build.properties b/org.eclipse.jgit.ssh.apache.test/build.properties
new file mode 100644
index 0000000000..9ffa0caf78
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/build.properties
@@ -0,0 +1,5 @@
+source.. = tst/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ plugin.properties
diff --git a/org.eclipse.jgit.ssh.apache.test/plugin.properties b/org.eclipse.jgit.ssh.apache.test/plugin.properties
new file mode 100644
index 0000000000..67c296d9e5
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/plugin.properties
@@ -0,0 +1,2 @@
+plugin_name=JGit Tests for SSH with Apache MINA sshd
+provider_name=Eclipse JGit
diff --git a/org.eclipse.jgit.ssh.apache.test/pom.xml b/org.eclipse.jgit.ssh.apache.test/pom.xml
new file mode 100644
index 0000000000..a03f4ececd
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/pom.xml
@@ -0,0 +1,145 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
+ and other copyright owners as documented in the project's IP log.
+
+ This program and the accompanying materials are made available
+ under the terms of the Eclipse Distribution License v1.0 which
+ accompanies this distribution, is reproduced below, and is
+ available at http://www.eclipse.org/org/documents/edl-v10.php
+
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or
+ without modification, are permitted provided that the following
+ conditions are met:
+
+ - Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ - Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+ - Neither the name of the Eclipse Foundation, Inc. nor the
+ names of its contributors may be used to endorse or promote
+ products derived from this software without specific prior
+ written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.eclipse.jgit</groupId>
+ <artifactId>org.eclipse.jgit-parent</artifactId>
+ <version>5.2.3-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>org.eclipse.jgit.ssh.apache.test</artifactId>
+ <name>JGit - Apache sshd SSH Tests</name>
+
+ <description>
+ JUnit tests for the JGit SSH support based on Apache MINA sshd.
+ </description>
+
+ <properties>
+ <maven.javadoc.skip>true</maven.javadoc.skip>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.eclipse.jgit</groupId>
+ <artifactId>org.eclipse.jgit</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.eclipse.jgit</groupId>
+ <artifactId>org.eclipse.jgit.junit</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.eclipse.jgit</groupId>
+ <artifactId>org.eclipse.jgit.ssh.apache</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.eclipse.jgit</groupId>
+ <artifactId>org.eclipse.jgit.test</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ </dependencies>
+
+ <profiles>
+ <!-- Profile provides a property which enables long running tests. -->
+ <profile>
+ <id>test.long</id>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <argLine>-Djgit.test.long=true</argLine>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+
+ <build>
+ <sourceDirectory>src/</sourceDirectory>
+ <testSourceDirectory>tst/</testSourceDirectory>
+
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <executions>
+ <execution>
+ <goals>
+ <goal>test-jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <argLine>-Xmx1024m -Dfile.encoding=UTF-8 -Djava.io.tmpdir=${project.build.directory}</argLine>
+ <includes>
+ <include>**/*Test.java</include>
+ <include>**/*Tests.java</include>
+ </includes>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/transport/sshd/proxy/HttpParserTest.java b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/transport/sshd/proxy/HttpParserTest.java
new file mode 100644
index 0000000000..b8e85493aa
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/transport/sshd/proxy/HttpParserTest.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.internal.transport.sshd.proxy;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Test;
+
+public class HttpParserTest {
+
+ private static final String STATUS_LINE = "HTTP/1.1. 407 Authentication required";
+
+ @Test
+ public void testEmpty() throws Exception {
+ String[] lines = { STATUS_LINE };
+ List<AuthenticationChallenge> challenges = HttpParser
+ .getAuthenticationHeaders(Arrays.asList(lines),
+ "WWW-Authenticate:");
+ assertTrue("No challenges expected", challenges.isEmpty());
+ }
+
+ @Test
+ public void testRFC7235Example() throws Exception {
+ // The example from RFC 7235, sec. 4.1, slightly modified ("kind"
+ // argument with whitespace around '=')
+ String[] lines = { STATUS_LINE,
+ "WWW-Authenticate: Newauth realm=\"apps\", type=1 , kind = \t2 ",
+ " \t title=\"Login to \\\"apps\\\"\", Basic realm=\"simple\"" };
+ List<AuthenticationChallenge> challenges = HttpParser
+ .getAuthenticationHeaders(Arrays.asList(lines),
+ "WWW-Authenticate:");
+ assertEquals("Unexpected number of challenges", 2, challenges.size());
+ assertNull("No token expected", challenges.get(0).getToken());
+ assertNull("No token expected", challenges.get(1).getToken());
+ assertEquals("Unexpected mechanism", "Newauth",
+ challenges.get(0).getMechanism());
+ assertEquals("Unexpected mechanism", "Basic",
+ challenges.get(1).getMechanism());
+ Map<String, String> expectedArguments = new LinkedHashMap<>();
+ expectedArguments.put("realm", "apps");
+ expectedArguments.put("type", "1");
+ expectedArguments.put("kind", "2");
+ expectedArguments.put("title", "Login to \"apps\"");
+ assertEquals("Unexpected arguments", expectedArguments,
+ challenges.get(0).getArguments());
+ expectedArguments.clear();
+ expectedArguments.put("realm", "simple");
+ assertEquals("Unexpected arguments", expectedArguments,
+ challenges.get(1).getArguments());
+ }
+
+ @Test
+ public void testMultipleHeaders() {
+ String[] lines = { STATUS_LINE,
+ "Server: Apache",
+ "WWW-Authenticate: Newauth realm=\"apps\", type=1 , kind = \t2 ",
+ " \t title=\"Login to \\\"apps\\\"\", Basic realm=\"simple\"",
+ "Content-Type: text/plain",
+ "WWW-Authenticate: Other 0123456789=== , YetAnother, ",
+ "WWW-Authenticate: Negotiate ",
+ "WWW-Authenticate: Negotiate a87421000492aa874209af8bc028" };
+ List<AuthenticationChallenge> challenges = HttpParser
+ .getAuthenticationHeaders(Arrays.asList(lines),
+ "WWW-Authenticate:");
+ assertEquals("Unexpected number of challenges", 6, challenges.size());
+ assertEquals("Mismatched challenge", "Other",
+ challenges.get(2).getMechanism());
+ assertEquals("Token expected", "0123456789===",
+ challenges.get(2).getToken());
+ assertEquals("Mismatched challenge", "YetAnother",
+ challenges.get(3).getMechanism());
+ assertNull("No token expected", challenges.get(3).getToken());
+ assertTrue("No arguments expected",
+ challenges.get(3).getArguments().isEmpty());
+ assertEquals("Mismatched challenge", "Negotiate",
+ challenges.get(4).getMechanism());
+ assertNull("No token expected", challenges.get(4).getToken());
+ assertEquals("Mismatched challenge", "Negotiate",
+ challenges.get(5).getMechanism());
+ assertEquals("Token expected", "a87421000492aa874209af8bc028",
+ challenges.get(5).getToken());
+ }
+
+ @Test
+ public void testStopOnEmptyLine() {
+ String[] lines = { STATUS_LINE, "Server: Apache",
+ "WWW-Authenticate: Newauth realm=\"apps\", type=1 , kind = \t2 ",
+ " \t title=\"Login to \\\"apps\\\"\", Basic realm=\"simple\"",
+ "Content-Type: text/plain",
+ "WWW-Authenticate: Other 0123456789===", "",
+ // Not headers anymore; this would be the body
+ "WWW-Authenticate: Negotiate ",
+ "WWW-Authenticate: Negotiate a87421000492aa874209af8bc028" };
+ List<AuthenticationChallenge> challenges = HttpParser
+ .getAuthenticationHeaders(Arrays.asList(lines),
+ "WWW-Authenticate:");
+ assertEquals("Unexpected number of challenges", 3, challenges.size());
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/transport/sshd/ApacheSshTest.java b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/transport/sshd/ApacheSshTest.java
new file mode 100644
index 0000000000..ee58083a5a
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/transport/sshd/ApacheSshTest.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.transport.sshd;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.nio.file.Files;
+import java.util.Arrays;
+
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.transport.SshSessionFactory;
+import org.eclipse.jgit.transport.ssh.SshTestBase;
+import org.eclipse.jgit.transport.sshd.SshdSessionFactory;
+import org.eclipse.jgit.util.FS;
+import org.junit.Test;
+import org.junit.experimental.theories.Theories;
+import org.junit.runner.RunWith;
+
+@RunWith(Theories.class)
+public class ApacheSshTest extends SshTestBase {
+
+ @Override
+ protected SshSessionFactory createSessionFactory() {
+ SshdSessionFactory result = new SshdSessionFactory(new JGitKeyCache(),
+ null);
+ // The home directory is mocked at this point!
+ result.setHomeDirectory(FS.DETECTED.userHome());
+ result.setSshDirectory(sshDir);
+ return result;
+ }
+
+ @Override
+ protected void installConfig(String... config) {
+ File configFile = new File(sshDir, Constants.CONFIG);
+ if (config != null) {
+ try {
+ Files.write(configFile.toPath(), Arrays.asList(config));
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+ }
+
+ // Using an ed25519 (unencrypted) user key is tested in the super class in
+ // testSshKeys(). sshd 2.0.0 cannot yet read encrypted ed25519 keys.
+
+ @Test
+ public void testEd25519HostKey() throws Exception {
+ File newHostKey = new File(getTemporaryDirectory(), "newhostkey");
+ copyTestResource("id_ed25519", newHostKey);
+ server.addHostKey(newHostKey.toPath(), true);
+ File newHostKeyPub = new File(getTemporaryDirectory(),
+ "newhostkey.pub");
+ copyTestResource("id_ed25519.pub", newHostKeyPub);
+ createKnownHostsFile(knownHosts, "localhost", testPort, newHostKeyPub);
+ cloneWith("ssh://git/doesntmatter", defaultCloneDir, null, //
+ "Host git", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey1.getAbsolutePath());
+ }
+
+}
diff --git a/org.eclipse.jgit.ssh.apache/.classpath b/org.eclipse.jgit.ssh.apache/.classpath
new file mode 100644
index 0000000000..110168ffa1
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/.classpath
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="src" path="resources"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/org.eclipse.jgit.ssh.apache/.fbprefs b/org.eclipse.jgit.ssh.apache/.fbprefs
new file mode 100644
index 0000000000..81a0767ff6
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/.fbprefs
@@ -0,0 +1,125 @@
+#FindBugs User Preferences
+#Mon May 04 16:24:13 PDT 2009
+detectorAppendingToAnObjectOutputStream=AppendingToAnObjectOutputStream|true
+detectorBadAppletConstructor=BadAppletConstructor|false
+detectorBadResultSetAccess=BadResultSetAccess|true
+detectorBadSyntaxForRegularExpression=BadSyntaxForRegularExpression|true
+detectorBadUseOfReturnValue=BadUseOfReturnValue|true
+detectorBadlyOverriddenAdapter=BadlyOverriddenAdapter|true
+detectorBooleanReturnNull=BooleanReturnNull|true
+detectorCallToUnsupportedMethod=CallToUnsupportedMethod|true
+detectorCheckImmutableAnnotation=CheckImmutableAnnotation|true
+detectorCheckTypeQualifiers=CheckTypeQualifiers|true
+detectorCloneIdiom=CloneIdiom|false
+detectorComparatorIdiom=ComparatorIdiom|true
+detectorConfusedInheritance=ConfusedInheritance|true
+detectorConfusionBetweenInheritedAndOuterMethod=ConfusionBetweenInheritedAndOuterMethod|true
+detectorCrossSiteScripting=CrossSiteScripting|true
+detectorDoInsideDoPrivileged=DoInsideDoPrivileged|true
+detectorDontCatchIllegalMonitorStateException=DontCatchIllegalMonitorStateException|true
+detectorDontUseEnum=DontUseEnum|true
+detectorDroppedException=DroppedException|true
+detectorDumbMethodInvocations=DumbMethodInvocations|true
+detectorDumbMethods=DumbMethods|true
+detectorDuplicateBranches=DuplicateBranches|true
+detectorEmptyZipFileEntry=EmptyZipFileEntry|true
+detectorEqualsOperandShouldHaveClassCompatibleWithThis=EqualsOperandShouldHaveClassCompatibleWithThis|true
+detectorFinalizerNullsFields=FinalizerNullsFields|true
+detectorFindBadCast2=FindBadCast2|true
+detectorFindBadForLoop=FindBadForLoop|true
+detectorFindCircularDependencies=FindCircularDependencies|false
+detectorFindDeadLocalStores=FindDeadLocalStores|true
+detectorFindDoubleCheck=FindDoubleCheck|true
+detectorFindEmptySynchronizedBlock=FindEmptySynchronizedBlock|true
+detectorFindFieldSelfAssignment=FindFieldSelfAssignment|true
+detectorFindFinalizeInvocations=FindFinalizeInvocations|true
+detectorFindFloatEquality=FindFloatEquality|true
+detectorFindHEmismatch=FindHEmismatch|true
+detectorFindInconsistentSync2=FindInconsistentSync2|true
+detectorFindJSR166LockMonitorenter=FindJSR166LockMonitorenter|true
+detectorFindLocalSelfAssignment2=FindLocalSelfAssignment2|true
+detectorFindMaskedFields=FindMaskedFields|true
+detectorFindMismatchedWaitOrNotify=FindMismatchedWaitOrNotify|true
+detectorFindNakedNotify=FindNakedNotify|true
+detectorFindNonSerializableStoreIntoSession=FindNonSerializableStoreIntoSession|true
+detectorFindNonSerializableValuePassedToWriteObject=FindNonSerializableValuePassedToWriteObject|true
+detectorFindNonShortCircuit=FindNonShortCircuit|true
+detectorFindNullDeref=FindNullDeref|true
+detectorFindNullDerefsInvolvingNonShortCircuitEvaluation=FindNullDerefsInvolvingNonShortCircuitEvaluation|true
+detectorFindOpenStream=FindOpenStream|true
+detectorFindPuzzlers=FindPuzzlers|true
+detectorFindRefComparison=FindRefComparison|true
+detectorFindReturnRef=FindReturnRef|true
+detectorFindRunInvocations=FindRunInvocations|true
+detectorFindSelfComparison=FindSelfComparison|true
+detectorFindSelfComparison2=FindSelfComparison2|true
+detectorFindSleepWithLockHeld=FindSleepWithLockHeld|true
+detectorFindSpinLoop=FindSpinLoop|true
+detectorFindSqlInjection=FindSqlInjection|true
+detectorFindTwoLockWait=FindTwoLockWait|true
+detectorFindUncalledPrivateMethods=FindUncalledPrivateMethods|true
+detectorFindUnconditionalWait=FindUnconditionalWait|true
+detectorFindUninitializedGet=FindUninitializedGet|true
+detectorFindUnrelatedTypesInGenericContainer=FindUnrelatedTypesInGenericContainer|true
+detectorFindUnreleasedLock=FindUnreleasedLock|true
+detectorFindUnsatisfiedObligation=FindUnsatisfiedObligation|true
+detectorFindUnsyncGet=FindUnsyncGet|true
+detectorFindUselessControlFlow=FindUselessControlFlow|true
+detectorFormatStringChecker=FormatStringChecker|true
+detectorHugeSharedStringConstants=HugeSharedStringConstants|true
+detectorIDivResultCastToDouble=IDivResultCastToDouble|true
+detectorIncompatMask=IncompatMask|true
+detectorInconsistentAnnotations=InconsistentAnnotations|true
+detectorInefficientMemberAccess=InefficientMemberAccess|false
+detectorInefficientToArray=InefficientToArray|true
+detectorInfiniteLoop=InfiniteLoop|true
+detectorInfiniteRecursiveLoop=InfiniteRecursiveLoop|true
+detectorInfiniteRecursiveLoop2=InfiniteRecursiveLoop2|false
+detectorInheritanceUnsafeGetResource=InheritanceUnsafeGetResource|true
+detectorInitializationChain=InitializationChain|true
+detectorInstantiateStaticClass=InstantiateStaticClass|true
+detectorInvalidJUnitTest=InvalidJUnitTest|true
+detectorIteratorIdioms=IteratorIdioms|true
+detectorLazyInit=LazyInit|true
+detectorLoadOfKnownNullValue=LoadOfKnownNullValue|true
+detectorMethodReturnCheck=MethodReturnCheck|true
+detectorMultithreadedInstanceAccess=MultithreadedInstanceAccess|true
+detectorMutableLock=MutableLock|true
+detectorMutableStaticFields=MutableStaticFields|true
+detectorNaming=Naming|true
+detectorNumberConstructor=NumberConstructor|true
+detectorOverridingEqualsNotSymmetrical=OverridingEqualsNotSymmetrical|true
+detectorPreferZeroLengthArrays=PreferZeroLengthArrays|true
+detectorPublicSemaphores=PublicSemaphores|false
+detectorQuestionableBooleanAssignment=QuestionableBooleanAssignment|true
+detectorReadReturnShouldBeChecked=ReadReturnShouldBeChecked|true
+detectorRedundantInterfaces=RedundantInterfaces|true
+detectorRepeatedConditionals=RepeatedConditionals|true
+detectorRuntimeExceptionCapture=RuntimeExceptionCapture|true
+detectorSerializableIdiom=SerializableIdiom|true
+detectorStartInConstructor=StartInConstructor|true
+detectorStaticCalendarDetector=StaticCalendarDetector|true
+detectorStringConcatenation=StringConcatenation|true
+detectorSuperfluousInstanceOf=SuperfluousInstanceOf|true
+detectorSuspiciousThreadInterrupted=SuspiciousThreadInterrupted|true
+detectorSwitchFallthrough=SwitchFallthrough|true
+detectorSynchronizeAndNullCheckField=SynchronizeAndNullCheckField|true
+detectorSynchronizeOnClassLiteralNotGetClass=SynchronizeOnClassLiteralNotGetClass|true
+detectorSynchronizingOnContentsOfFieldToProtectField=SynchronizingOnContentsOfFieldToProtectField|true
+detectorURLProblems=URLProblems|true
+detectorUncallableMethodOfAnonymousClass=UncallableMethodOfAnonymousClass|true
+detectorUnnecessaryMath=UnnecessaryMath|true
+detectorUnreadFields=UnreadFields|true
+detectorUseObjectEquals=UseObjectEquals|false
+detectorUselessSubclassMethod=UselessSubclassMethod|false
+detectorVarArgsProblems=VarArgsProblems|true
+detectorVolatileUsage=VolatileUsage|true
+detectorWaitInLoop=WaitInLoop|true
+detectorWrongMapIterator=WrongMapIterator|true
+detectorXMLFactoryBypass=XMLFactoryBypass|true
+detector_threshold=2
+effort=default
+excludefilter0=findBugs/FindBugsExcludeFilter.xml
+filter_settings=Medium|BAD_PRACTICE,CORRECTNESS,MT_CORRECTNESS,PERFORMANCE,STYLE|false
+filter_settings_neg=MALICIOUS_CODE,NOISE,I18N,SECURITY,EXPERIMENTAL|
+run_at_full_build=true
diff --git a/org.eclipse.jgit.ssh.apache/.gitignore b/org.eclipse.jgit.ssh.apache/.gitignore
new file mode 100644
index 0000000000..934e0e06ff
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/.gitignore
@@ -0,0 +1,2 @@
+/bin
+/target
diff --git a/org.eclipse.jgit.ssh.apache/.project b/org.eclipse.jgit.ssh.apache/.project
new file mode 100644
index 0000000000..a7bbd6bafd
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/.project
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.eclipse.jgit.ssh.apache</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.api.tools.apiAnalysisBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ <nature>org.eclipse.pde.api.tools.apiAnalysisNature</nature>
+ </natures>
+</projectDescription>
diff --git a/org.eclipse.jgit.ssh.apache/.settings/org.eclipse.core.resources.prefs b/org.eclipse.jgit.ssh.apache/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000000..66ac15c47c
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,3 @@
+#Mon Aug 11 16:46:12 PDT 2008
+eclipse.preferences.version=1
+encoding/<project>=UTF-8
diff --git a/org.eclipse.jgit.ssh.apache/.settings/org.eclipse.core.runtime.prefs b/org.eclipse.jgit.ssh.apache/.settings/org.eclipse.core.runtime.prefs
new file mode 100644
index 0000000000..006e07ede5
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/.settings/org.eclipse.core.runtime.prefs
@@ -0,0 +1,3 @@
+#Mon Mar 24 18:55:50 EDT 2008
+eclipse.preferences.version=1
+line.separator=\n
diff --git a/org.eclipse.jgit.ssh.apache/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.ssh.apache/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000000..13c32a6d94
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,399 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=enabled
+org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore
+org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jgit.annotations.NonNull
+org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jgit.annotations.NonNullByDefault
+org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jgit.annotations.Nullable
+org.eclipse.jdt.core.compiler.annotation.nullanalysis=enabled
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.8
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.doc.comment.support=enabled
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.autoboxing=warning
+org.eclipse.jdt.core.compiler.problem.comparingIdentical=error
+org.eclipse.jdt.core.compiler.problem.deadCode=error
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
+org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=warning
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=error
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=error
+org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=enabled
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=warning
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=error
+org.eclipse.jdt.core.compiler.problem.invalidJavadoc=error
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=private
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=error
+org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled
+org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=error
+org.eclipse.jdt.core.compiler.problem.missingJavadocComments=error
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=protected
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag
+org.eclipse.jdt.core.compiler.problem.missingJavadocTags=error
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
+org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=error
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=error
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=warning
+org.eclipse.jdt.core.compiler.problem.nonnullParameterAnnotationDropped=warning
+org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error
+org.eclipse.jdt.core.compiler.problem.nullReference=error
+org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error
+org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=ignore
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=error
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=error
+org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=ignore
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=ignore
+org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning
+org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=warning
+org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=error
+org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
+org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=error
+org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=disabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
+org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.unusedExceptionParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedImport=error
+org.eclipse.jdt.core.compiler.problem.unusedLabel=error
+org.eclipse.jdt.core.compiler.problem.unusedLocal=error
+org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameter=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=error
+org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=error
+org.eclipse.jdt.core.compiler.source=1.8
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=0
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=1
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=false
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert
+org.eclipse.jdt.core.formatter.comment.line_length=80
+org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true
+org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true
+org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off
+org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.join_lines_in_comments=true
+org.eclipse.jdt.core.formatter.join_wrapped_lines=true
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=80
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
+org.eclipse.jdt.core.formatter.tabulation.char=tab
+org.eclipse.jdt.core.formatter.tabulation.size=4
+org.eclipse.jdt.core.formatter.use_on_off_tags=true
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true
+org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true
diff --git a/org.eclipse.jgit.ssh.apache/.settings/org.eclipse.jdt.ui.prefs b/org.eclipse.jgit.ssh.apache/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 0000000000..fef3713825
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,66 @@
+eclipse.preferences.version=1
+editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
+formatter_profile=_JGit Format
+formatter_settings_version=12
+org.eclipse.jdt.ui.ignorelowercasenames=true
+org.eclipse.jdt.ui.importorder=java;javax;org;com;
+org.eclipse.jdt.ui.ondemandthreshold=99
+org.eclipse.jdt.ui.staticondemandthreshold=99
+org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8"?><templates/>
+sp_cleanup.add_default_serial_version_id=true
+sp_cleanup.add_generated_serial_version_id=false
+sp_cleanup.add_missing_annotations=true
+sp_cleanup.add_missing_deprecated_annotations=true
+sp_cleanup.add_missing_methods=false
+sp_cleanup.add_missing_nls_tags=false
+sp_cleanup.add_missing_override_annotations=true
+sp_cleanup.add_missing_override_annotations_interface_methods=true
+sp_cleanup.add_serial_version_id=false
+sp_cleanup.always_use_blocks=true
+sp_cleanup.always_use_parentheses_in_expressions=false
+sp_cleanup.always_use_this_for_non_static_field_access=false
+sp_cleanup.always_use_this_for_non_static_method_access=false
+sp_cleanup.convert_functional_interfaces=false
+sp_cleanup.convert_to_enhanced_for_loop=false
+sp_cleanup.correct_indentation=false
+sp_cleanup.format_source_code=true
+sp_cleanup.format_source_code_changes_only=true
+sp_cleanup.insert_inferred_type_arguments=false
+sp_cleanup.make_local_variable_final=false
+sp_cleanup.make_parameters_final=false
+sp_cleanup.make_private_fields_final=true
+sp_cleanup.make_type_abstract_if_missing_method=false
+sp_cleanup.make_variable_declarations_final=false
+sp_cleanup.never_use_blocks=false
+sp_cleanup.never_use_parentheses_in_expressions=true
+sp_cleanup.on_save_use_additional_actions=true
+sp_cleanup.organize_imports=false
+sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
+sp_cleanup.remove_private_constructors=true
+sp_cleanup.remove_redundant_type_arguments=true
+sp_cleanup.remove_trailing_whitespaces=true
+sp_cleanup.remove_trailing_whitespaces_all=true
+sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
+sp_cleanup.remove_unnecessary_casts=true
+sp_cleanup.remove_unnecessary_nls_tags=true
+sp_cleanup.remove_unused_imports=false
+sp_cleanup.remove_unused_local_variables=false
+sp_cleanup.remove_unused_private_fields=true
+sp_cleanup.remove_unused_private_members=false
+sp_cleanup.remove_unused_private_methods=true
+sp_cleanup.remove_unused_private_types=true
+sp_cleanup.sort_members=false
+sp_cleanup.sort_members_all=false
+sp_cleanup.use_anonymous_class_creation=false
+sp_cleanup.use_blocks=false
+sp_cleanup.use_blocks_only_for_return_and_throw=false
+sp_cleanup.use_lambda=false
+sp_cleanup.use_parentheses_in_expressions=false
+sp_cleanup.use_this_for_non_static_field_access=false
+sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+sp_cleanup.use_this_for_non_static_method_access=false
+sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
diff --git a/org.eclipse.jgit.ssh.apache/.settings/org.eclipse.mylyn.tasks.ui.prefs b/org.eclipse.jgit.ssh.apache/.settings/org.eclipse.mylyn.tasks.ui.prefs
new file mode 100644
index 0000000000..823c0f56ae
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/.settings/org.eclipse.mylyn.tasks.ui.prefs
@@ -0,0 +1,4 @@
+#Tue Jul 19 20:11:28 CEST 2011
+eclipse.preferences.version=1
+project.repository.kind=bugzilla
+project.repository.url=https\://bugs.eclipse.org/bugs
diff --git a/org.eclipse.jgit.ssh.apache/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.ssh.apache/.settings/org.eclipse.mylyn.team.ui.prefs
new file mode 100644
index 0000000000..0cba949fb7
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/.settings/org.eclipse.mylyn.team.ui.prefs
@@ -0,0 +1,3 @@
+#Tue Jul 19 20:11:28 CEST 2011
+commit.comment.template=${task.description} \n\nBug\: ${task.key}
+eclipse.preferences.version=1
diff --git a/org.eclipse.jgit.ssh.apache/.settings/org.eclipse.pde.api.tools.prefs b/org.eclipse.jgit.ssh.apache/.settings/org.eclipse.pde.api.tools.prefs
new file mode 100644
index 0000000000..c0030ded71
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/.settings/org.eclipse.pde.api.tools.prefs
@@ -0,0 +1,104 @@
+ANNOTATION_ELEMENT_TYPE_ADDED_FIELD=Error
+ANNOTATION_ELEMENT_TYPE_ADDED_METHOD_WITHOUT_DEFAULT_VALUE=Error
+ANNOTATION_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_FIELD=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_METHOD=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_API_TYPE=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_REEXPORTED_API_TYPE=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_REEXPORTED_TYPE=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_TYPE=Error
+API_USE_SCAN_FIELD_SEVERITY=Error
+API_USE_SCAN_METHOD_SEVERITY=Error
+API_USE_SCAN_TYPE_SEVERITY=Error
+CLASS_ELEMENT_TYPE_ADDED_FIELD=Error
+CLASS_ELEMENT_TYPE_ADDED_METHOD=Error
+CLASS_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error
+CLASS_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+CLASS_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error
+CLASS_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+CLASS_ELEMENT_TYPE_CHANGED_NON_ABSTRACT_TO_ABSTRACT=Error
+CLASS_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
+CLASS_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
+CLASS_ELEMENT_TYPE_REMOVED_CONSTRUCTOR=Error
+CLASS_ELEMENT_TYPE_REMOVED_FIELD=Error
+CLASS_ELEMENT_TYPE_REMOVED_METHOD=Error
+CLASS_ELEMENT_TYPE_REMOVED_SUPERCLASS=Error
+CLASS_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+CLASS_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+CONSTRUCTOR_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+CONSTRUCTOR_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+CONSTRUCTOR_ELEMENT_TYPE_CHANGED_VARARGS_TO_ARRAY=Error
+CONSTRUCTOR_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+ENUM_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error
+ENUM_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
+ENUM_ELEMENT_TYPE_REMOVED_ENUM_CONSTANT=Error
+ENUM_ELEMENT_TYPE_REMOVED_FIELD=Error
+ENUM_ELEMENT_TYPE_REMOVED_METHOD=Error
+ENUM_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+FIELD_ELEMENT_TYPE_ADDED_VALUE=Error
+FIELD_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+FIELD_ELEMENT_TYPE_CHANGED_FINAL_TO_NON_FINAL_STATIC_CONSTANT=Error
+FIELD_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
+FIELD_ELEMENT_TYPE_CHANGED_NON_STATIC_TO_STATIC=Error
+FIELD_ELEMENT_TYPE_CHANGED_STATIC_TO_NON_STATIC=Error
+FIELD_ELEMENT_TYPE_CHANGED_TYPE=Error
+FIELD_ELEMENT_TYPE_CHANGED_VALUE=Error
+FIELD_ELEMENT_TYPE_REMOVED_TYPE_ARGUMENT=Error
+FIELD_ELEMENT_TYPE_REMOVED_VALUE=Error
+ILLEGAL_EXTEND=Warning
+ILLEGAL_IMPLEMENT=Warning
+ILLEGAL_INSTANTIATE=Warning
+ILLEGAL_OVERRIDE=Warning
+ILLEGAL_REFERENCE=Warning
+INTERFACE_ELEMENT_TYPE_ADDED_DEFAULT_METHOD=Error
+INTERFACE_ELEMENT_TYPE_ADDED_FIELD=Error
+INTERFACE_ELEMENT_TYPE_ADDED_METHOD=Error
+INTERFACE_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error
+INTERFACE_ELEMENT_TYPE_ADDED_SUPER_INTERFACE_WITH_METHODS=Error
+INTERFACE_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+INTERFACE_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error
+INTERFACE_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_FIELD=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_METHOD=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+INVALID_ANNOTATION=Ignore
+INVALID_JAVADOC_TAG=Ignore
+INVALID_REFERENCE_IN_SYSTEM_LIBRARIES=Error
+LEAK_EXTEND=Warning
+LEAK_FIELD_DECL=Warning
+LEAK_IMPLEMENT=Warning
+LEAK_METHOD_PARAM=Warning
+LEAK_METHOD_RETURN_TYPE=Warning
+METHOD_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error
+METHOD_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+METHOD_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+METHOD_ELEMENT_TYPE_CHANGED_NON_ABSTRACT_TO_ABSTRACT=Error
+METHOD_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
+METHOD_ELEMENT_TYPE_CHANGED_NON_STATIC_TO_STATIC=Error
+METHOD_ELEMENT_TYPE_CHANGED_STATIC_TO_NON_STATIC=Error
+METHOD_ELEMENT_TYPE_CHANGED_VARARGS_TO_ARRAY=Error
+METHOD_ELEMENT_TYPE_REMOVED_ANNOTATION_DEFAULT_VALUE=Error
+METHOD_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+MISSING_EE_DESCRIPTIONS=Warning
+TYPE_PARAMETER_ELEMENT_TYPE_ADDED_CLASS_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_ADDED_INTERFACE_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_CHANGED_CLASS_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_CHANGED_INTERFACE_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_REMOVED_CLASS_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_REMOVED_INTERFACE_BOUND=Error
+UNUSED_PROBLEM_FILTERS=Warning
+automatically_removed_unused_problem_filters=false
+changed_execution_env=Error
+eclipse.preferences.version=1
+incompatible_api_component_version=Error
+incompatible_api_component_version_include_major_without_breaking_change=Disabled
+incompatible_api_component_version_include_minor_without_api_change=Disabled
+incompatible_api_component_version_report_major_without_breaking_change=Warning
+incompatible_api_component_version_report_minor_without_api_change=Ignore
+invalid_since_tag_version=Error
+malformed_since_tag=Error
+missing_since_tag=Error
+report_api_breakage_when_major_version_incremented=Disabled
+report_resolution_errors_api_component=Warning
diff --git a/org.eclipse.jgit.ssh.apache/.settings/org.eclipse.pde.core.prefs b/org.eclipse.jgit.ssh.apache/.settings/org.eclipse.pde.core.prefs
new file mode 100644
index 0000000000..82793f2d27
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/.settings/org.eclipse.pde.core.prefs
@@ -0,0 +1,3 @@
+#Thu Jan 14 14:34:32 CST 2010
+eclipse.preferences.version=1
+resolve.requirebundle=false
diff --git a/org.eclipse.jgit.ssh.apache/BUILD b/org.eclipse.jgit.ssh.apache/BUILD
new file mode 100644
index 0000000000..a1a6c8e24c
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/BUILD
@@ -0,0 +1,19 @@
+package(default_visibility = ["//visibility:public"])
+
+SRCS = glob(["src/**/*.java"])
+
+RESOURCES = glob(["resources/**"])
+
+java_library(
+ name = "ssh-apache",
+ srcs = SRCS,
+ resource_strip_prefix = "org.eclipse.jgit.ssh.apache/resources",
+ resources = RESOURCES,
+ deps = [
+ "//lib:eddsa",
+ "//lib:slf4j-api",
+ "//lib:sshd-core",
+ "//lib:sshd-sftp",
+ "//org.eclipse.jgit:jgit",
+ ],
+)
diff --git a/org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF b/org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF
new file mode 100644
index 0000000000..29a12bbbe9
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF
@@ -0,0 +1,83 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %Bundle-Name
+Automatic-Module-Name: org.eclipse.jgit.ssh.apache
+Bundle-SymbolicName: org.eclipse.jgit.ssh.apache
+Bundle-Vendor: %Provider-Name
+Bundle-ActivationPolicy: lazy
+Bundle-Version: 5.2.3.qualifier
+Bundle-RequiredExecutionEnvironment: JavaSE-1.8
+Export-Package: org.eclipse.jgit.internal.transport.sshd;version="5.2.3";x-internal:=true;
+ uses:="org.apache.sshd.client,
+ org.apache.sshd.client.auth,
+ org.apache.sshd.client.auth.keyboard,
+ org.apache.sshd.client.auth.pubkey,
+ org.apache.sshd.client.config.hosts,
+ org.apache.sshd.client.future,
+ org.apache.sshd.client.keyverifier,
+ org.apache.sshd.client.session,
+ org.apache.sshd.common.config.keys,
+ org.apache.sshd.common.io,
+ org.apache.sshd.common.keyprovider,
+ org.apache.sshd.common.signature,
+ org.apache.sshd.common.util.buffer,
+ org.eclipse.jgit.transport",
+ org.eclipse.jgit.internal.transport.sshd.auth;version="5.2.3";x-internal:=true,
+ org.eclipse.jgit.internal.transport.sshd.proxy;version="5.2.3";x-friends:="org.eclipse.jgit.ssh.apache.test",
+ org.eclipse.jgit.transport.sshd;version="5.2.3";
+ uses:="org.eclipse.jgit.transport,
+ org.apache.sshd.client.config.hosts,
+ org.apache.sshd.common.keyprovider,
+ org.eclipse.jgit.util,
+ org.apache.sshd.client.session,
+ org.apache.sshd.client.keyverifier"
+Import-Package: net.i2p.crypto.eddsa;version="[0.3.0,0.4.0)",
+ org.apache.sshd.agent;version="[2.0.0,2.1.0)",
+ org.apache.sshd.client;version="[2.0.0,2.1.0)",
+ org.apache.sshd.client.auth;version="[2.0.0,2.1.0)",
+ org.apache.sshd.client.auth.keyboard;version="[2.0.0,2.1.0)",
+ org.apache.sshd.client.auth.password;version="[2.0.0,2.1.0)",
+ org.apache.sshd.client.auth.pubkey;version="[2.0.0,2.1.0)",
+ org.apache.sshd.client.channel;version="[2.0.0,2.1.0)",
+ org.apache.sshd.client.config.hosts;version="[2.0.0,2.1.0)",
+ org.apache.sshd.client.config.keys;version="[2.0.0,2.1.0)",
+ org.apache.sshd.client.future;version="[2.0.0,2.1.0)",
+ org.apache.sshd.client.keyverifier;version="[2.0.0,2.1.0)",
+ org.apache.sshd.client.session;version="[2.0.0,2.1.0)",
+ org.apache.sshd.client.subsystem.sftp;version="[2.0.0,2.1.0)",
+ org.apache.sshd.common;version="[2.0.0,2.1.0)",
+ org.apache.sshd.common.auth;version="[2.0.0,2.1.0)",
+ org.apache.sshd.common.channel;version="[2.0.0,2.1.0)",
+ org.apache.sshd.common.compression;version="[2.0.0,2.1.0)",
+ org.apache.sshd.common.config.keys;version="[2.0.0,2.1.0)",
+ org.apache.sshd.common.config.keys.loader;version="[2.0.0,2.1.0)",
+ org.apache.sshd.common.digest;version="[2.0.0,2.1.0)",
+ org.apache.sshd.common.forward;version="[2.0.0,2.1.0)",
+ org.apache.sshd.common.future;version="[2.0.0,2.1.0)",
+ org.apache.sshd.common.helpers;version="[2.0.0,2.1.0)",
+ org.apache.sshd.common.io;version="[2.0.0,2.1.0)",
+ org.apache.sshd.common.kex;version="[2.0.0,2.1.0)",
+ org.apache.sshd.common.keyprovider;version="[2.0.0,2.1.0)",
+ org.apache.sshd.common.mac;version="[2.0.0,2.1.0)",
+ org.apache.sshd.common.random;version="[2.0.0,2.1.0)",
+ org.apache.sshd.common.session;version="[2.0.0,2.1.0)",
+ org.apache.sshd.common.session.helpers;version="[2.0.0,2.1.0)",
+ org.apache.sshd.common.signature;version="[2.0.0,2.1.0)",
+ org.apache.sshd.common.subsystem.sftp;version="[2.0.0,2.1.0)",
+ org.apache.sshd.common.util;version="[2.0.0,2.1.0)",
+ org.apache.sshd.common.util.buffer;version="[2.0.0,2.1.0)",
+ org.apache.sshd.common.util.closeable;version="[2.0.0,2.1.0)",
+ org.apache.sshd.common.util.io;version="[2.0.0,2.1.0)",
+ org.apache.sshd.common.util.logging;version="[2.0.0,2.1.0)",
+ org.apache.sshd.common.util.net;version="[2.0.0,2.1.0)",
+ org.apache.sshd.common.util.security;version="[2.0.0,2.1.0)",
+ org.apache.sshd.server.auth;version="[2.0.0,2.1.0)",
+ org.eclipse.jgit.annotations;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.errors;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.fnmatch;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.internal.storage.file;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.internal.transport.ssh;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.nls;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.transport;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.util;version="[5.2.3,5.3.0)",
+ org.slf4j;version="[1.7.0,2.0.0)"
diff --git a/org.eclipse.jgit.ssh.apache/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.ssh.apache/META-INF/SOURCE-MANIFEST.MF
new file mode 100644
index 0000000000..a16ebd3f97
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/META-INF/SOURCE-MANIFEST.MF
@@ -0,0 +1,7 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: org.eclipse.jgit.ssh.apache - Sources
+Bundle-SymbolicName: org.eclipse.jgit.ssh.apache.source
+Bundle-Vendor: Eclipse.org - JGit
+Bundle-Version: 5.2.3.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.ssh.apache;version="5.2.3.qualifier";roots="."
diff --git a/org.eclipse.jgit.ssh.apache/about.html b/org.eclipse.jgit.ssh.apache/about.html
new file mode 100644
index 0000000000..f971af18d0
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/about.html
@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
+<title>Eclipse Distribution License - Version 1.0</title>
+<style type="text/css">
+ body {
+ size: 8.5in 11.0in;
+ margin: 0.25in 0.5in 0.25in 0.5in;
+ tab-interval: 0.5in;
+ }
+ p {
+ margin-left: auto;
+ margin-top: 0.5em;
+ margin-bottom: 0.5em;
+ }
+ p.list {
+ margin-left: 0.5in;
+ margin-top: 0.05em;
+ margin-bottom: 0.05em;
+ }
+ .ubc-name {
+ margin-left: 0.5in;
+ white-space: pre;
+ }
+ </style>
+
+</head>
+
+<body lang="EN-US">
+
+<p><b>Eclipse Distribution License - v 1.0</b></p>
+
+<p>Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors. </p>
+
+<p>All rights reserved.</p>
+<p>Redistribution and use in source and binary forms, with or without modification,
+ are permitted provided that the following conditions are met:
+<ul><li>Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer. </li>
+<li>Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution. </li>
+<li>Neither the name of the Eclipse Foundation, Inc. nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission. </li></ul>
+</p>
+<p>THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.</p>
+
+<hr>
+<p><b>SHA-1 UbcCheck - MIT</b></p>
+
+<p>Copyright (c) 2017:</p>
+<div class="ubc-name">
+Marc Stevens
+Cryptology Group
+Centrum Wiskunde & Informatica
+P.O. Box 94079, 1090 GB Amsterdam, Netherlands
+marc@marc-stevens.nl
+</div>
+<div class="ubc-name">
+Dan Shumow
+Microsoft Research
+danshu@microsoft.com
+</div>
+<p>Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+</p>
+<ul><li>The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.</li></ul>
+<p>THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.</p>
+
+</body>
+
+</html>
diff --git a/org.eclipse.jgit.ssh.apache/build.properties b/org.eclipse.jgit.ssh.apache/build.properties
new file mode 100644
index 0000000000..8148271ef3
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/build.properties
@@ -0,0 +1,7 @@
+source.. = src/,\
+ resources/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ plugin.properties,\
+ about.html
diff --git a/org.eclipse.jgit.ssh.apache/plugin.properties b/org.eclipse.jgit.ssh.apache/plugin.properties
new file mode 100644
index 0000000000..8f3540c0f0
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/plugin.properties
@@ -0,0 +1,2 @@
+plugin_name=JGit SSH support based on Apache MINA sshd
+provider_name=Eclipse JGit
diff --git a/org.eclipse.jgit.ssh.apache/pom.xml b/org.eclipse.jgit.ssh.apache/pom.xml
new file mode 100644
index 0000000000..22541339a5
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/pom.xml
@@ -0,0 +1,256 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
+ and other copyright owners as documented in the project's IP log.
+
+ This program and the accompanying materials are made available
+ under the terms of the Eclipse Distribution License v1.0 which
+ accompanies this distribution, is reproduced below, and is
+ available at http://www.eclipse.org/org/documents/edl-v10.php
+
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or
+ without modification, are permitted provided that the following
+ conditions are met:
+
+ - Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ - Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+ - Neither the name of the Eclipse Foundation, Inc. nor the
+ names of its contributors may be used to endorse or promote
+ products derived from this software without specific prior
+ written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.eclipse.jgit</groupId>
+ <artifactId>org.eclipse.jgit-parent</artifactId>
+ <version>5.2.3-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>org.eclipse.jgit.ssh.apache</artifactId>
+ <name>JGit - Apache sshd-based SSH support</name>
+
+ <description>
+ SSH support for JGit based on Apache MINA sshd
+ </description>
+
+ <properties>
+ <translate-qualifier/>
+ <source-bundle-manifest>${project.build.directory}/META-INF/SOURCE-MANIFEST.MF</source-bundle-manifest>
+ <eddsa-version>0.3.0</eddsa-version>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.eclipse.jgit</groupId>
+ <artifactId>org.eclipse.jgit</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-core</artifactId>
+ <version>${apache-sshd-version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-sftp</artifactId>
+ <version>${apache-sshd-version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>net.i2p.crypto</groupId>
+ <artifactId>eddsa</artifactId>
+ <version>${eddsa-version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <sourceDirectory>src/</sourceDirectory>
+
+ <resources>
+ <resource>
+ <directory>.</directory>
+ <includes>
+ <include>plugin.properties</include>
+ <include>about.html</include>
+ </includes>
+ </resource>
+ <resource>
+ <directory>resources/</directory>
+ </resource>
+ </resources>
+
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-antrun-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>translate-source-qualifier</id>
+ <phase>generate-resources</phase>
+ <configuration>
+ <target>
+ <copy file="META-INF/SOURCE-MANIFEST.MF" tofile="${source-bundle-manifest}" overwrite="true" />
+ <replace file="${source-bundle-manifest}">
+ <replacefilter token=".qualifier" value=".${maven.build.timestamp}" />
+ </replace>
+ </target>
+ </configuration>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-source-plugin</artifactId>
+ <inherited>true</inherited>
+ <executions>
+ <execution>
+ <id>attach-sources</id>
+ <phase>process-classes</phase>
+ <goals>
+ <goal>jar</goal>
+ </goals>
+ <configuration>
+ <archive>
+ <manifestFile>${source-bundle-manifest}</manifestFile>
+ </archive>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+
+ <plugin>
+ <artifactId>maven-jar-plugin</artifactId>
+ <configuration>
+ <archive>
+ <manifestFile>${bundle-manifest}</manifestFile>
+ </archive>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>com.github.siom79.japicmp</groupId>
+ <artifactId>japicmp-maven-plugin</artifactId>
+ <version>${japicmp-version}</version>
+ <configuration>
+ <oldVersion>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>${project.artifactId}</artifactId>
+ <version>${jgit-last-release-version}</version>
+ </dependency>
+ </oldVersion>
+ <newVersion>
+ <file>
+ <path>${project.build.directory}/${project.artifactId}-${project.version}.jar</path>
+ </file>
+ </newVersion>
+ <parameter>
+ <onlyModified>true</onlyModified>
+ <includes>
+ <include>org.eclipse.jgit.*</include>
+ </includes>
+ <accessModifier>public</accessModifier>
+ <breakBuildOnModifications>false</breakBuildOnModifications>
+ <breakBuildOnBinaryIncompatibleModifications>false</breakBuildOnBinaryIncompatibleModifications>
+ <onlyBinaryIncompatible>false</onlyBinaryIncompatible>
+ <includeSynthetic>false</includeSynthetic>
+ <ignoreMissingClasses>false</ignoreMissingClasses>
+ <skipPomModules>true</skipPomModules>
+ </parameter>
+ <skip>true</skip><!-- TODO: Enable after the first release -->
+ </configuration>
+ <executions>
+ <execution>
+ <phase>verify</phase>
+ <goals>
+ <goal>cmp</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+ <reporting>
+ <plugins>
+ <plugin>
+ <groupId>com.github.siom79.japicmp</groupId>
+ <artifactId>japicmp-maven-plugin</artifactId>
+ <version>${japicmp-version}</version>
+ <reportSets>
+ <reportSet>
+ <reports>
+ <report>cmp-report</report>
+ </reports>
+ </reportSet>
+ </reportSets>
+ <configuration>
+ <oldVersion>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>${project.artifactId}</artifactId>
+ <version>${jgit-last-release-version}</version>
+ </dependency>
+ </oldVersion>
+ <newVersion>
+ <file>
+ <path>${project.build.directory}/${project.artifactId}-${project.version}.jar</path>
+ </file>
+ </newVersion>
+ <parameter>
+ <onlyModified>true</onlyModified>
+ <includes>
+ <include>org.eclipse.jgit.*</include>
+ </includes>
+ <accessModifier>public</accessModifier>
+ <breakBuildOnModifications>false</breakBuildOnModifications>
+ <breakBuildOnBinaryIncompatibleModifications>false</breakBuildOnBinaryIncompatibleModifications>
+ <onlyBinaryIncompatible>false</onlyBinaryIncompatible>
+ <includeSynthetic>false</includeSynthetic>
+ <ignoreMissingClasses>false</ignoreMissingClasses>
+ <skipPomModules>true</skipPomModules>
+ </parameter>
+ <skip>true</skip><!-- TODO: Enable after the first release -->
+ </configuration>
+ </plugin>
+ </plugins>
+ </reporting>
+</project>
diff --git a/org.eclipse.jgit.ssh.apache/resources/org/eclipse/jgit/internal/transport/sshd/SshdText.properties b/org.eclipse.jgit.ssh.apache/resources/org/eclipse/jgit/internal/transport/sshd/SshdText.properties
new file mode 100644
index 0000000000..aa4e4ccec3
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/resources/org/eclipse/jgit/internal/transport/sshd/SshdText.properties
@@ -0,0 +1,77 @@
+authenticationCanceled=Authentication canceled: no password
+closeListenerFailed=Ssh session close listener failed
+configInvalidPath=Invalid path in ssh config key {0}: {1}
+configInvalidPattern=Invalid pattern in ssh config key {0}: {1}
+configInvalidPositive=Ssh config entry {0} must be a strictly positive number but is ''{1}''
+configNoKnownHostKeyAlgorithms=No implementations for any of the algorithms ''{0}'' given in HostKeyAlgorithms in the ssh config; using the default.
+configNoRemainingHostKeyAlgorithms=Ssh config removed all host key algorithms: HostKeyAlgorithms ''{0}''
+ftpCloseFailed=Closing the SFTP channel failed
+gssapiFailure=GSS-API error for mechanism OID {0}
+gssapiInitFailure=GSS-API initialization failure for mechanism {0}
+gssapiUnexpectedMechanism=Server {0} replied with unknown mechanism name ''{1}'' in {2} authentication
+gssapiUnexpectedMessage=Received unexpected ssh message {1} in {0} authentication
+identityFileCannotDecrypt=Given passphrase cannot decrypt identity {0}
+identityFileNoKey=No keys found in identity {0}
+identityFileMultipleKeys=Multiple key pairs found in identity {0}
+identityFileUnsupportedFormat=Unsupported format in identity {0}
+kexServerKeyInvalid=Server key did not validate
+keyEncryptedMsg=Key ''{0}'' is encrypted. Enter the passphrase to decrypt it.
+keyEncryptedPrompt=Passphrase
+keyEncryptedRetry=Encrypted key ''{0}'' could not be decrypted. Enter the passphrase again.
+keyLoadFailed=Could not load key ''{0}''
+knownHostsCouldNotUpdate=Could not update known hosts file {0}
+knownHostsFileLockedRead=Could not read known hosts file (locked) {0}
+knownHostsFileLockedUpdate=Could not update known hosts file (locked) {0}
+knownHostsFileReadFailed=Failed to read known hosts file {0}
+knownHostsInvalidLine=Known hosts file {0} contains invalid line {1}
+knownHostsInvalidPath=Invalid path for known hosts file {0}
+knownHostsKeyFingerprints=The {0} key''s fingerprints are:
+knownHostsModifiedKeyAcceptPrompt=Accept this key and continue connecting all the same?
+knownHostsModifiedKeyDenyMsg=To resolve this add the correct host key to your known hosts file {0}
+knownHostsModifiedKeyStorePrompt=If so, also store the new key?
+knownHostsModifiedKeyWarning=WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!\n\
+The connection might be compromised (man-in-the-middle attack).\n\
+It is also possible that the {0} key of the host has just been changed.\n\
+The expected {1} key for host ''{2}'' has the fingerprints:\n\
+{3}\n\
+{4}\n\
+The {0} key actually received has the fingerprints:\n\
+{5}\n\
+{6}
+knownHostsRevokedKeyMsg=Host ''{0}'' sent a key that is marked as revoked in the known hosts file {1}.
+knownHostsUnknownKeyMsg=The authenticity of host ''{0}'' cannot be established.
+knownHostsUnknownKeyPrompt=Accept and store this key, and continue connecting?
+knownHostsUnknownKeyType=Cannot read server key from known hosts file {0}; line {1}
+knownHostsUserAskCreationMsg=File {0} does not exist.
+knownHostsUserAskCreationPrompt=Create file {0} ?
+passwordPrompt=Password
+proxyCannotAuthenticate=Cannot authenticate to proxy {0}
+proxyHttpFailure=HTTP Proxy connection to {0} failed with code {1}: {2}
+proxyHttpInvalidUserName=HTTP proxy connection {0} with invalid user name; must not contain colons: {1}
+proxyHttpUnexpectedReply=Unexpected HTTP proxy response from {0}: {1}
+proxyHttpUnspecifiedFailureReason=unspecified reason
+proxyPasswordPrompt=Proxy password
+proxySocksAuthenticationFailed=Authentication to SOCKS5 proxy {0} failed
+proxySocksFailureForbidden=SOCKS5 proxy {0}: connection to {1} not allowed by ruleset
+proxySocksFailureGeneral=SOCKS5 proxy {0}: general failure
+proxySocksFailureHostUnreachable=SOCKS5 proxy {0}: host unreachable {1}
+proxySocksFailureNetworkUnreachable=SOCKS5 proxy {0}: network unreachable {1}
+proxySocksFailureRefused=SOCKS5 proxy {0}: connection refused {1}
+proxySocksFailureTTL=TTL expired in SOCKS5 proxy connection {0}
+proxySocksFailureUnspecified=Unspecified failure in SOCKS5 proxy connection {0}
+proxySocksFailureUnsupportedAddress=SOCKS5 proxy {0} does not support address type
+proxySocksFailureUnsupportedCommand=SOCKS5 proxy {0} does not support CONNECT command
+proxySocksGssApiFailure=Cannot authenticate with GSS-API to SOCKS5 proxy {0}
+proxySocksGssApiMessageTooShort=SOCKS5 proxy {0} sent too short message
+proxySocksGssApiUnknownMessage=SOCKS5 proxy {0} sent unexpected GSS-API message type, expected 1, got {1}
+proxySocksGssApiVersionMismatch=SOCKS5 proxy {0} sent wrong GSS-API version number, expected 1, got {1}
+proxySocksNoRemoteHostName=Could not send remote address {0}
+proxySocksPasswordTooLong=Password for proxy {0} must be at most 255 bytes long, is {1} bytes
+proxySocksUnexpectedMessage=Unexpected message received from SOCKS5 proxy {0}; client state {1}: {2}
+proxySocksUnexpectedVersion=Expected SOCKS version 5, got {0}
+proxySocksUsernameTooLong=User name for proxy {0} must be at most 255 bytes long, is {1} bytes: {2}
+sessionCloseFailed=Closing the session failed
+sshClosingDown=Apache MINA sshd session factory is closing down; cannot create new ssh sessions on this factory
+sshCommandTimeout={0} timed out after {1} seconds while opening the channel
+sshProcessStillRunning={0} is not yet completed, cannot get exit code
+unknownProxyProtocol=Ignoring unknown proxy protocol {0} \ No newline at end of file
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/CachingKeyPairProvider.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/CachingKeyPairProvider.java
new file mode 100644
index 0000000000..ad2ff5256c
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/CachingKeyPairProvider.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.internal.transport.sshd;
+
+import static java.text.MessageFormat.format;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.concurrent.CancellationException;
+
+import org.eclipse.jgit.transport.sshd.KeyCache;
+
+/**
+ * A {@link EncryptedFileKeyPairProvider} that uses an external
+ * {@link KeyCache}.
+ */
+public class CachingKeyPairProvider extends EncryptedFileKeyPairProvider {
+
+ private final KeyCache cache;
+
+ /**
+ * Creates a new {@link CachingKeyPairProvider} using the given
+ * {@link KeyCache}. If the cache is {@code null}, this is a simple
+ * {@link EncryptedFileKeyPairProvider}.
+ *
+ * @param paths
+ * to load keys from
+ * @param cache
+ * to use, may be {@code null} if no external caching is desired
+ */
+ public CachingKeyPairProvider(List<Path> paths, KeyCache cache) {
+ super(paths);
+ this.cache = cache;
+ }
+
+ @Override
+ protected Iterable<KeyPair> loadKeys(Collection<? extends Path> resources) {
+ if (resources.isEmpty()) {
+ return Collections.emptyList();
+ }
+ return () -> new CancellingKeyPairIterator(resources);
+ }
+
+ @Override
+ protected KeyPair doLoadKey(Path resource)
+ throws IOException, GeneralSecurityException {
+ // By calling doLoadKey(String, Path, FilePasswordProvider) instead of
+ // super.doLoadKey(Path) we can bypass the key caching in
+ // AbstractResourceKeyPairProvider, over which we have no real control.
+ String resourceId = resource.toString();
+ if (cache == null) {
+ return doLoadKey(resourceId, resource, getPasswordFinder());
+ }
+ Throwable t[] = { null };
+ KeyPair key = cache.get(resource, p -> {
+ try {
+ return doLoadKey(resourceId, p, getPasswordFinder());
+ } catch (IOException | GeneralSecurityException e) {
+ t[0] = e;
+ return null;
+ }
+ });
+ if (t[0] != null) {
+ if (t[0] instanceof CancellationException) {
+ throw (CancellationException) t[0];
+ }
+ throw new IOException(
+ format(SshdText.get().keyLoadFailed, resource), t[0]);
+ }
+ return key;
+ }
+
+ private class CancellingKeyPairIterator implements Iterator<KeyPair> {
+
+ private final Iterator<Path> paths;
+
+ private KeyPair nextItem;
+
+ private boolean nextSet;
+
+ public CancellingKeyPairIterator(Collection<? extends Path> resources) {
+ List<Path> copy = new ArrayList<>(resources.size());
+ copy.addAll(resources);
+ paths = copy.iterator();
+ }
+
+ @Override
+ public boolean hasNext() {
+ if (nextSet) {
+ return nextItem != null;
+ }
+ nextSet = true;
+ while (nextItem == null && paths.hasNext()) {
+ try {
+ nextItem = doLoadKey(paths.next());
+ } catch (CancellationException cancelled) {
+ throw cancelled;
+ } catch (Exception other) {
+ log.warn(other.toString());
+ }
+ }
+ return nextItem != null;
+ }
+
+ @Override
+ public KeyPair next() {
+ if (!nextSet && !hasNext()) {
+ throw new NoSuchElementException();
+ }
+ KeyPair result = nextItem;
+ nextItem = null;
+ nextSet = false;
+ if (result == null) {
+ throw new NoSuchElementException();
+ }
+ return result;
+ }
+
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/EncryptedFileKeyPairProvider.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/EncryptedFileKeyPairProvider.java
new file mode 100644
index 0000000000..ff81989991
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/EncryptedFileKeyPairProvider.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.internal.transport.sshd;
+
+import static java.text.MessageFormat.format;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Path;
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.security.KeyPair;
+import java.security.NoSuchProviderException;
+import java.security.PrivateKey;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.security.auth.DestroyFailedException;
+
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.config.keys.loader.KeyPairResourceParser;
+import org.apache.sshd.common.keyprovider.FileKeyPairProvider;
+import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+import org.eclipse.jgit.internal.transport.sshd.RepeatingFilePasswordProvider.ResourceDecodeResult;
+
+/**
+ * A {@link FileKeyPairProvider} that asks repeatedly for a passphrase for an
+ * encrypted private key if the {@link FilePasswordProvider} is a
+ * {@link RepeatingFilePasswordProvider}.
+ */
+public class EncryptedFileKeyPairProvider extends FileKeyPairProvider {
+
+ // TODO: remove this class once we're based on sshd > 2.1.0. See upstream
+ // issue SSHD-850 https://issues.apache.org/jira/browse/SSHD-850 and commit
+ // https://github.com/apache/mina-sshd/commit/f19bd2e34
+
+ /**
+ * Creates a new {@link EncryptedFileKeyPairProvider} for the given
+ * {@link Path}s.
+ *
+ * @param paths
+ * to read keys from
+ */
+ public EncryptedFileKeyPairProvider(List<Path> paths) {
+ super(paths);
+ }
+
+ @Override
+ protected KeyPair doLoadKey(String resourceKey, InputStream inputStream,
+ FilePasswordProvider provider)
+ throws IOException, GeneralSecurityException {
+ if (!(provider instanceof RepeatingFilePasswordProvider)) {
+ return super.doLoadKey(resourceKey, inputStream, provider);
+ }
+ KeyPairResourceParser parser = SecurityUtils.getKeyPairResourceParser();
+ if (parser == null) {
+ // This is an internal configuration error, thus no translation.
+ throw new NoSuchProviderException(
+ "No registered key-pair resource parser"); //$NON-NLS-1$
+ }
+ RepeatingFilePasswordProvider realProvider = (RepeatingFilePasswordProvider) provider;
+ // Read the stream now so that we can process the content several
+ // times.
+ List<String> lines = IoUtils.readAllLines(inputStream);
+ Collection<KeyPair> ids = null;
+ while (ids == null) {
+ try {
+ ids = parser.loadKeyPairs(resourceKey, realProvider, lines);
+ realProvider.handleDecodeAttemptResult(resourceKey, "", null); //$NON-NLS-1$
+ // No exception; success. Exit the loop even if ids is still
+ // null!
+ break;
+ } catch (IOException | GeneralSecurityException
+ | RuntimeException e) {
+ ResourceDecodeResult loadResult = realProvider
+ .handleDecodeAttemptResult(resourceKey, "", e); //$NON-NLS-1$
+ if (loadResult == null
+ || loadResult == ResourceDecodeResult.TERMINATE) {
+ throw e;
+ } else if (loadResult == ResourceDecodeResult.RETRY) {
+ continue;
+ }
+ // IGNORE doesn't make any sense here, but OK, let's ignore it.
+ // ids == null, so we'll throw an exception below.
+ break;
+ }
+ }
+ if (ids == null) {
+ // The javadoc on loadKeyPairs says it might return null if no
+ // key pair found. Bad API.
+ throw new InvalidKeyException(
+ format(SshdText.get().identityFileNoKey, resourceKey));
+ }
+ Iterator<KeyPair> keys = ids.iterator();
+ if (!keys.hasNext()) {
+ throw new InvalidKeyException(format(
+ SshdText.get().identityFileUnsupportedFormat, resourceKey));
+ }
+ KeyPair result = keys.next();
+ if (keys.hasNext()) {
+ log.warn(format(SshdText.get().identityFileMultipleKeys,
+ resourceKey));
+ keys.forEachRemaining(k -> {
+ PrivateKey pk = k.getPrivate();
+ if (pk != null) {
+ try {
+ pk.destroy();
+ } catch (DestroyFailedException e) {
+ // Ignore
+ }
+ }
+ });
+ }
+ return result;
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/GssApiMechanisms.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/GssApiMechanisms.java
new file mode 100644
index 0000000000..cf68eac5a7
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/GssApiMechanisms.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.internal.transport.sshd;
+
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.UnknownHostException;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.eclipse.jgit.annotations.NonNull;
+import org.ietf.jgss.GSSContext;
+import org.ietf.jgss.GSSException;
+import org.ietf.jgss.GSSManager;
+import org.ietf.jgss.GSSName;
+import org.ietf.jgss.Oid;
+
+/**
+ * Global repository of GSS-API mechanisms that we can use.
+ */
+public class GssApiMechanisms {
+
+ private GssApiMechanisms() {
+ // No instantiation
+ }
+
+ /** Prefix to use with {@link GSSName#NT_HOSTBASED_SERVICE}. */
+ public static final String GSSAPI_HOST_PREFIX = "host@"; //$NON-NLS-1$
+
+ /** The {@link Oid} of Kerberos 5. */
+ public static final Oid KERBEROS_5 = createOid("1.2.840.113554.1.2.2"); //$NON-NLS-1$
+
+ /** SGNEGO is not to be used with ssh. */
+ public static final Oid SPNEGO = createOid("1.3.6.1.5.5.2"); //$NON-NLS-1$
+
+ /** Protects {@link #supportedMechanisms}. */
+ private static final Object LOCK = new Object();
+
+ /**
+ * The {@link AtomicBoolean} is set to {@code true} when the mechanism could
+ * be initialized successfully at least once.
+ */
+ private static Map<Oid, Boolean> supportedMechanisms;
+
+ /**
+ * Retrieves an immutable collection of the supported mechanisms.
+ *
+ * @return the supported mechanisms
+ */
+ @NonNull
+ public static Collection<Oid> getSupportedMechanisms() {
+ synchronized (LOCK) {
+ if (supportedMechanisms == null) {
+ GSSManager manager = GSSManager.getInstance();
+ Oid[] mechs = manager.getMechs();
+ Map<Oid, Boolean> mechanisms = new LinkedHashMap<>();
+ if (mechs != null) {
+ for (Oid oid : mechs) {
+ mechanisms.put(oid, Boolean.FALSE);
+ }
+ }
+ supportedMechanisms = mechanisms;
+ }
+ return Collections.unmodifiableSet(supportedMechanisms.keySet());
+ }
+ }
+
+ /**
+ * Report that this mechanism was used successfully.
+ *
+ * @param mechanism
+ * that worked
+ */
+ public static void worked(@NonNull Oid mechanism) {
+ synchronized (LOCK) {
+ supportedMechanisms.put(mechanism, Boolean.TRUE);
+ }
+ }
+
+ /**
+ * Mark the mechanisms as failed.
+ *
+ * @param mechanism
+ * to mark
+ */
+ public static void failed(@NonNull Oid mechanism) {
+ synchronized (LOCK) {
+ Boolean worked = supportedMechanisms.get(mechanism);
+ if (worked != null && !worked.booleanValue()) {
+ // If it never worked, remove it
+ supportedMechanisms.remove(mechanism);
+ }
+ }
+ }
+
+ /**
+ * Resolves an {@link InetSocketAddress}.
+ *
+ * @param remote
+ * to resolve
+ * @return the resolved {@link InetAddress}, or {@code null} if unresolved.
+ */
+ public static InetAddress resolve(@NonNull InetSocketAddress remote) {
+ InetAddress address = remote.getAddress();
+ if (address == null) {
+ try {
+ address = InetAddress.getByName(remote.getHostString());
+ } catch (UnknownHostException e) {
+ return null;
+ }
+ }
+ return address;
+ }
+
+ /**
+ * Determines a canonical host name for use use with GSS-API.
+ *
+ * @param remote
+ * to get the host name from
+ * @return the canonical host name, if it can be determined, otherwise the
+ * {@link InetSocketAddress#getHostString() unprocessed host name}.
+ */
+ @NonNull
+ public static String getCanonicalName(@NonNull InetSocketAddress remote) {
+ InetAddress address = resolve(remote);
+ if (address == null) {
+ return remote.getHostString();
+ }
+ return address.getCanonicalHostName();
+ }
+
+ /**
+ * Creates a {@link GSSContext} for the given mechanism to authenticate with
+ * the host given by {@code fqdn}.
+ *
+ * @param mechanism
+ * {@link Oid} of the mechanism to use
+ * @param fqdn
+ * fully qualified domain name of the host to authenticate with
+ * @return the context, if the mechanism is available and the context could
+ * be created, or {@code null} otherwise
+ */
+ public static GSSContext createContext(@NonNull Oid mechanism,
+ @NonNull String fqdn) {
+ GSSContext context = null;
+ try {
+ GSSManager manager = GSSManager.getInstance();
+ context = manager.createContext(
+ manager.createName(
+ GssApiMechanisms.GSSAPI_HOST_PREFIX + fqdn,
+ GSSName.NT_HOSTBASED_SERVICE),
+ mechanism, null, GSSContext.DEFAULT_LIFETIME);
+ } catch (GSSException e) {
+ closeContextSilently(context);
+ failed(mechanism);
+ return null;
+ }
+ worked(mechanism);
+ return context;
+ }
+
+ /**
+ * Closes (disposes of) a {@link GSSContext} ignoring any
+ * {@link GSSException}s.
+ *
+ * @param context
+ * to dispose
+ */
+ public static void closeContextSilently(GSSContext context) {
+ if (context != null) {
+ try {
+ context.dispose();
+ } catch (GSSException e) {
+ // Ignore
+ }
+ }
+ }
+
+ private static Oid createOid(String rep) {
+ try {
+ return new Oid(rep);
+ } catch (GSSException e) {
+ // Does not occur
+ return null;
+ }
+ }
+
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/GssApiWithMicAuthFactory.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/GssApiWithMicAuthFactory.java
new file mode 100644
index 0000000000..ba5630516f
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/GssApiWithMicAuthFactory.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.internal.transport.sshd;
+
+import org.apache.sshd.client.auth.AbstractUserAuthFactory;
+import org.apache.sshd.client.auth.UserAuth;
+
+/**
+ * Factory to create {@link GssApiWithMicAuthentication} handlers.
+ */
+public class GssApiWithMicAuthFactory extends AbstractUserAuthFactory {
+
+ /** The authentication identifier for GSSApi-with-MIC. */
+ public static final String NAME = "gssapi-with-mic"; //$NON-NLS-1$
+
+ /** The singleton {@link GssApiWithMicAuthFactory}. */
+ public static final GssApiWithMicAuthFactory INSTANCE = new GssApiWithMicAuthFactory();
+
+ private GssApiWithMicAuthFactory() {
+ super(NAME);
+ }
+
+ @Override
+ public UserAuth create() {
+ return new GssApiWithMicAuthentication();
+ }
+
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/GssApiWithMicAuthentication.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/GssApiWithMicAuthentication.java
new file mode 100644
index 0000000000..aef263d7f4
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/GssApiWithMicAuthentication.java
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.internal.transport.sshd;
+
+import static java.text.MessageFormat.format;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.net.UnknownHostException;
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.apache.sshd.client.auth.AbstractUserAuth;
+import org.apache.sshd.client.session.ClientSession;
+import org.apache.sshd.common.SshConstants;
+import org.apache.sshd.common.util.buffer.Buffer;
+import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
+import org.ietf.jgss.GSSContext;
+import org.ietf.jgss.GSSException;
+import org.ietf.jgss.MessageProp;
+import org.ietf.jgss.Oid;
+
+/**
+ * GSSAPI-with-MIC authentication handler (Kerberos 5).
+ *
+ * @see <a href="https://tools.ietf.org/html/rfc4462">RFC 4462</a>
+ */
+public class GssApiWithMicAuthentication extends AbstractUserAuth {
+
+ /** Synonym used in RFC 4462. */
+ private static final byte SSH_MSG_USERAUTH_GSSAPI_RESPONSE = SshConstants.SSH_MSG_USERAUTH_INFO_REQUEST;
+
+ /** Synonym used in RFC 4462. */
+ private static final byte SSH_MSG_USERAUTH_GSSAPI_TOKEN = SshConstants.SSH_MSG_USERAUTH_INFO_RESPONSE;
+
+ private enum ProtocolState {
+ STARTED, TOKENS, MIC_SENT, FAILED
+ }
+
+ private Collection<Oid> mechanisms;
+
+ private Iterator<Oid> nextMechanism;
+
+ private Oid currentMechanism;
+
+ private ProtocolState state;
+
+ private GSSContext context;
+
+ /** Creates a new {@link GssApiWithMicAuthentication}. */
+ public GssApiWithMicAuthentication() {
+ super(GssApiWithMicAuthFactory.NAME);
+ }
+
+ @Override
+ protected boolean sendAuthDataRequest(ClientSession session, String service)
+ throws Exception {
+ if (mechanisms == null) {
+ mechanisms = GssApiMechanisms.getSupportedMechanisms();
+ nextMechanism = mechanisms.iterator();
+ }
+ if (context != null) {
+ close(false);
+ }
+ if (!nextMechanism.hasNext()) {
+ return false;
+ }
+ state = ProtocolState.STARTED;
+ currentMechanism = nextMechanism.next();
+ // RFC 4462 states that SPNEGO must not be used with ssh
+ while (GssApiMechanisms.SPNEGO.equals(currentMechanism)) {
+ if (!nextMechanism.hasNext()) {
+ return false;
+ }
+ currentMechanism = nextMechanism.next();
+ }
+ try {
+ String hostName = getHostName(session);
+ context = GssApiMechanisms.createContext(currentMechanism,
+ hostName);
+ context.requestMutualAuth(true);
+ context.requestConf(true);
+ context.requestInteg(true);
+ context.requestCredDeleg(true);
+ context.requestAnonymity(false);
+ } catch (GSSException | NullPointerException e) {
+ close(true);
+ if (log.isDebugEnabled()) {
+ log.debug(format(SshdText.get().gssapiInitFailure,
+ currentMechanism.toString()));
+ }
+ currentMechanism = null;
+ state = ProtocolState.FAILED;
+ return false;
+ }
+ Buffer buffer = session
+ .createBuffer(SshConstants.SSH_MSG_USERAUTH_REQUEST);
+ buffer.putString(session.getUsername());
+ buffer.putString(service);
+ buffer.putString(getName());
+ buffer.putInt(1);
+ buffer.putBytes(currentMechanism.getDER());
+ session.writePacket(buffer);
+ return true;
+ }
+
+ @Override
+ protected boolean processAuthDataRequest(ClientSession session,
+ String service, Buffer in) throws Exception {
+ // SSH_MSG_USERAUTH_FAILURE and SSH_MSG_USERAUTH_SUCCESS, as well as
+ // SSH_MSG_USERAUTH_BANNER are handled by the framework.
+ int command = in.getUByte();
+ if (context == null) {
+ return false;
+ }
+ try {
+ switch (command) {
+ case SSH_MSG_USERAUTH_GSSAPI_RESPONSE: {
+ if (state != ProtocolState.STARTED) {
+ return unexpectedMessage(command);
+ }
+ // Initial reply from the server with the mechanism to use.
+ Oid mechanism = new Oid(in.getBytes());
+ if (!currentMechanism.equals(mechanism)) {
+ return false;
+ }
+ replyToken(session, service, new byte[0]);
+ return true;
+ }
+ case SSH_MSG_USERAUTH_GSSAPI_TOKEN: {
+ if (context.isEstablished() || state != ProtocolState.TOKENS) {
+ return unexpectedMessage(command);
+ }
+ // Server sent us a token
+ replyToken(session, service, in.getBytes());
+ return true;
+ }
+ default:
+ return unexpectedMessage(command);
+ }
+ } catch (GSSException e) {
+ log.warn(format(SshdText.get().gssapiFailure,
+ currentMechanism.toString()), e);
+ state = ProtocolState.FAILED;
+ return false;
+ }
+ }
+
+ @Override
+ public void destroy() {
+ try {
+ close(false);
+ } finally {
+ super.destroy();
+ }
+ }
+
+ private void close(boolean silent) {
+ try {
+ if (context != null) {
+ context.dispose();
+ context = null;
+ }
+ } catch (GSSException e) {
+ if (!silent) {
+ log.warn(SshdText.get().gssapiFailure, e);
+ }
+ }
+ }
+
+ private void sendToken(ClientSession session, byte[] receivedToken)
+ throws IOException, GSSException {
+ state = ProtocolState.TOKENS;
+ byte[] token = context.initSecContext(receivedToken, 0,
+ receivedToken.length);
+ if (token != null) {
+ Buffer buffer = session.createBuffer(SSH_MSG_USERAUTH_GSSAPI_TOKEN);
+ buffer.putBytes(token);
+ session.writePacket(buffer);
+ }
+ }
+
+ private void sendMic(ClientSession session, String service)
+ throws IOException, GSSException {
+ state = ProtocolState.MIC_SENT;
+ // Produce MIC
+ Buffer micBuffer = new ByteArrayBuffer();
+ micBuffer.putBytes(session.getSessionId());
+ micBuffer.putByte(SshConstants.SSH_MSG_USERAUTH_REQUEST);
+ micBuffer.putString(session.getUsername());
+ micBuffer.putString(service);
+ micBuffer.putString(getName());
+ byte[] micBytes = micBuffer.getCompactData();
+ byte[] mic = context.getMIC(micBytes, 0, micBytes.length,
+ new MessageProp(0, true));
+ Buffer buffer = session
+ .createBuffer(SshConstants.SSH_MSG_USERAUTH_GSSAPI_MIC);
+ buffer.putBytes(mic);
+ session.writePacket(buffer);
+ }
+
+ private void replyToken(ClientSession session, String service, byte[] bytes)
+ throws IOException, GSSException {
+ sendToken(session, bytes);
+ if (context.isEstablished()) {
+ sendMic(session, service);
+ }
+ }
+
+ private String getHostName(ClientSession session) {
+ SocketAddress remote = session.getConnectAddress();
+ if (remote instanceof InetSocketAddress) {
+ InetAddress address = GssApiMechanisms
+ .resolve((InetSocketAddress) remote);
+ if (address != null) {
+ return address.getCanonicalHostName();
+ }
+ }
+ if (session instanceof JGitClientSession) {
+ String hostName = ((JGitClientSession) session).getHostConfigEntry()
+ .getHostName();
+ try {
+ hostName = InetAddress.getByName(hostName)
+ .getCanonicalHostName();
+ } catch (UnknownHostException e) {
+ // Ignore here; try with the non-canonical name
+ }
+ return hostName;
+ }
+ throw new IllegalStateException(
+ "Wrong session class :" + session.getClass().getName()); //$NON-NLS-1$
+ }
+
+ private boolean unexpectedMessage(int command) {
+ log.warn(format(SshdText.get().gssapiUnexpectedMessage, getName(),
+ Integer.toString(command)));
+ return false;
+ }
+
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitClientSession.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitClientSession.java
new file mode 100644
index 0000000000..dcf330a2a4
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitClientSession.java
@@ -0,0 +1,333 @@
+/*
+ * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.internal.transport.sshd;
+
+import static java.text.MessageFormat.format;
+
+import java.io.IOException;
+import java.net.SocketAddress;
+import java.security.PublicKey;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.sshd.client.ClientFactoryManager;
+import org.apache.sshd.client.config.hosts.HostConfigEntry;
+import org.apache.sshd.client.keyverifier.KnownHostsServerKeyVerifier.HostEntryPair;
+import org.apache.sshd.client.keyverifier.ServerKeyVerifier;
+import org.apache.sshd.client.session.ClientSessionImpl;
+import org.apache.sshd.common.FactoryManager;
+import org.apache.sshd.common.SshException;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.io.IoSession;
+import org.apache.sshd.common.io.IoWriteFuture;
+import org.apache.sshd.common.util.Readable;
+import org.eclipse.jgit.errors.InvalidPatternException;
+import org.eclipse.jgit.fnmatch.FileNameMatcher;
+import org.eclipse.jgit.internal.transport.sshd.proxy.StatefulProxyConnector;
+import org.eclipse.jgit.transport.CredentialsProvider;
+import org.eclipse.jgit.transport.SshConstants;
+
+/**
+ * A {@link org.apache.sshd.client.session.ClientSession ClientSession} that can
+ * be associated with the {@link HostConfigEntry} the session was created for.
+ * The {@link JGitSshClient} creates such sessions and sets this association.
+ * <p>
+ * Also provides for associating a JGit {@link CredentialsProvider} with a
+ * session.
+ * </p>
+ */
+public class JGitClientSession extends ClientSessionImpl {
+
+ private HostConfigEntry hostConfig;
+
+ private CredentialsProvider credentialsProvider;
+
+ private StatefulProxyConnector proxyHandler;
+
+ /**
+ * @param manager
+ * @param session
+ * @throws Exception
+ */
+ public JGitClientSession(ClientFactoryManager manager, IoSession session)
+ throws Exception {
+ super(manager, session);
+ }
+
+ /**
+ * Retrieves the {@link HostConfigEntry} this session was created for.
+ *
+ * @return the {@link HostConfigEntry}, or {@code null} if none set
+ */
+ public HostConfigEntry getHostConfigEntry() {
+ return hostConfig;
+ }
+
+ /**
+ * Sets the {@link HostConfigEntry} this session was created for.
+ *
+ * @param hostConfig
+ * the {@link HostConfigEntry}
+ */
+ public void setHostConfigEntry(HostConfigEntry hostConfig) {
+ this.hostConfig = hostConfig;
+ }
+
+ /**
+ * Sets the {@link CredentialsProvider} for this session.
+ *
+ * @param provider
+ * to set
+ */
+ public void setCredentialsProvider(CredentialsProvider provider) {
+ credentialsProvider = provider;
+ }
+
+ /**
+ * Retrieves the {@link CredentialsProvider} set for this session.
+ *
+ * @return the provider, or {@code null} if none is set.
+ */
+ public CredentialsProvider getCredentialsProvider() {
+ return credentialsProvider;
+ }
+
+ /**
+ * Sets a {@link StatefulProxyConnector} to handle proxy connection
+ * protocols.
+ *
+ * @param handler
+ * to set
+ */
+ public void setProxyHandler(StatefulProxyConnector handler) {
+ proxyHandler = handler;
+ }
+
+ @Override
+ protected IoWriteFuture sendIdentification(String ident)
+ throws IOException {
+ StatefulProxyConnector proxy = proxyHandler;
+ if (proxy != null) {
+ try {
+ // We must not block here; the framework starts reading messages
+ // from the peer only once the initial sendKexInit() following
+ // this call to sendIdentification() has returned!
+ proxy.runWhenDone(() -> {
+ JGitClientSession.super.sendIdentification(ident);
+ return null;
+ });
+ // Called only from the ClientSessionImpl constructor, where the
+ // return value is ignored.
+ return null;
+ } catch (IOException e) {
+ throw e;
+ } catch (Exception other) {
+ throw new IOException(other.getLocalizedMessage(), other);
+ }
+ } else {
+ return super.sendIdentification(ident);
+ }
+ }
+
+ @Override
+ protected byte[] sendKexInit() throws IOException {
+ StatefulProxyConnector proxy = proxyHandler;
+ if (proxy != null) {
+ try {
+ // We must not block here; the framework starts reading messages
+ // from the peer only once the initial sendKexInit() has
+ // returned!
+ proxy.runWhenDone(() -> {
+ JGitClientSession.super.sendKexInit();
+ return null;
+ });
+ // This is called only from the ClientSessionImpl
+ // constructor, where the return value is ignored.
+ return null;
+ } catch (IOException e) {
+ throw e;
+ } catch (Exception other) {
+ throw new IOException(other.getLocalizedMessage(), other);
+ }
+ } else {
+ return super.sendKexInit();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * As long as we're still setting up the proxy connection, diverts messages
+ * to the {@link StatefulProxyConnector}.
+ */
+ @Override
+ public void messageReceived(Readable buffer) throws Exception {
+ StatefulProxyConnector proxy = proxyHandler;
+ if (proxy != null) {
+ proxy.messageReceived(getIoSession(), buffer);
+ } else {
+ super.messageReceived(buffer);
+ }
+ }
+
+ @Override
+ protected void checkKeys() throws SshException {
+ ServerKeyVerifier serverKeyVerifier = getServerKeyVerifier();
+ // The super implementation always uses
+ // getIoSession().getRemoteAddress(). In case of a proxy connection,
+ // that would be the address of the proxy!
+ SocketAddress remoteAddress = getConnectAddress();
+ PublicKey serverKey = getKex().getServerKey();
+ if (!serverKeyVerifier.verifyServerKey(this, remoteAddress,
+ serverKey)) {
+ throw new SshException(
+ org.apache.sshd.common.SshConstants.SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE,
+ SshdText.get().kexServerKeyInvalid);
+ }
+ }
+
+ @Override
+ protected String resolveAvailableSignaturesProposal(
+ FactoryManager manager) {
+ Set<String> defaultSignatures = new LinkedHashSet<>();
+ defaultSignatures.addAll(getSignatureFactoriesNames());
+ HostConfigEntry config = resolveAttribute(
+ JGitSshClient.HOST_CONFIG_ENTRY);
+ String hostKeyAlgorithms = config
+ .getProperty(SshConstants.HOST_KEY_ALGORITHMS);
+ if (hostKeyAlgorithms != null && !hostKeyAlgorithms.isEmpty()) {
+ char first = hostKeyAlgorithms.charAt(0);
+ if (first == '+') {
+ // Additions make not much sense -- it's either in
+ // defaultSignatures already, or we have no implementation for
+ // it. No point in proposing it.
+ return String.join(",", defaultSignatures); //$NON-NLS-1$
+ } else if (first == '-') {
+ // This takes wildcard patterns!
+ removeFromList(defaultSignatures,
+ SshConstants.HOST_KEY_ALGORITHMS,
+ hostKeyAlgorithms.substring(1));
+ if (defaultSignatures.isEmpty()) {
+ // Too bad: user config error. Warn here, and then fail
+ // later.
+ log.warn(format(
+ SshdText.get().configNoRemainingHostKeyAlgorithms,
+ hostKeyAlgorithms));
+ }
+ return String.join(",", defaultSignatures); //$NON-NLS-1$
+ } else {
+ // Default is overridden -- only accept the ones for which we do
+ // have an implementation.
+ List<String> newNames = filteredList(defaultSignatures,
+ hostKeyAlgorithms);
+ if (newNames.isEmpty()) {
+ log.warn(format(
+ SshdText.get().configNoKnownHostKeyAlgorithms,
+ hostKeyAlgorithms));
+ // Use the default instead.
+ } else {
+ return String.join(",", newNames); //$NON-NLS-1$
+ }
+ }
+ }
+ // No HostKeyAlgorithms; using default -- change order to put existing
+ // keys first.
+ ServerKeyVerifier verifier = getServerKeyVerifier();
+ if (verifier instanceof ServerKeyLookup) {
+ SocketAddress remoteAddress = resolvePeerAddress(
+ resolveAttribute(JGitSshClient.ORIGINAL_REMOTE_ADDRESS));
+ List<HostEntryPair> allKnownKeys = ((ServerKeyLookup) verifier)
+ .lookup(this, remoteAddress);
+ Set<String> reordered = new LinkedHashSet<>();
+ for (HostEntryPair h : allKnownKeys) {
+ PublicKey key = h.getServerKey();
+ if (key != null) {
+ String keyType = KeyUtils.getKeyType(key);
+ if (keyType != null) {
+ reordered.add(keyType);
+ }
+ }
+ }
+ reordered.addAll(defaultSignatures);
+ return String.join(",", reordered); //$NON-NLS-1$
+ }
+ return String.join(",", defaultSignatures); //$NON-NLS-1$
+ }
+
+ private void removeFromList(Set<String> current, String key,
+ String patterns) {
+ for (String toRemove : patterns.split("\\s*,\\s*")) { //$NON-NLS-1$
+ if (toRemove.indexOf('*') < 0 && toRemove.indexOf('?') < 0) {
+ current.remove(toRemove);
+ continue;
+ }
+ try {
+ FileNameMatcher matcher = new FileNameMatcher(toRemove, null);
+ for (Iterator<String> i = current.iterator(); i.hasNext();) {
+ matcher.reset();
+ matcher.append(i.next());
+ if (matcher.isMatch()) {
+ i.remove();
+ }
+ }
+ } catch (InvalidPatternException e) {
+ log.warn(format(SshdText.get().configInvalidPattern, key,
+ toRemove));
+ }
+ }
+ }
+
+ private List<String> filteredList(Set<String> known, String values) {
+ List<String> newNames = new ArrayList<>();
+ for (String newValue : values.split("\\s*,\\s*")) { //$NON-NLS-1$
+ if (known.contains(newValue)) {
+ newNames.add(newValue);
+ }
+ }
+ return newNames;
+ }
+
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitHostConfigEntry.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitHostConfigEntry.java
new file mode 100644
index 0000000000..a0705f25f5
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitHostConfigEntry.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.internal.transport.sshd;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.sshd.client.config.hosts.HostConfigEntry;
+import org.eclipse.jgit.annotations.NonNull;
+
+/**
+ * A {@link HostConfigEntry} that provides access to the multi-valued keys as
+ * lists of strings. The super class treats them as single strings containing
+ * comma-separated lists.
+ *
+ * @since 5.2
+ */
+public class JGitHostConfigEntry extends HostConfigEntry {
+
+ private Map<String, List<String>> multiValuedOptions;
+
+ @Override
+ public String getProperty(String name, String defaultValue) {
+ // Upstream bug fix (SSHD-867): if there are _no_ properties at all, the
+ // super implementation returns always null even if a default value is
+ // given.
+ //
+ // See https://issues.apache.org/jira/projects/SSHD/issues/SSHD-867
+ //
+ // TODO: remove this override once we're based on sshd > 2.1.0
+ Map<String, String> properties = getProperties();
+ if (properties == null || properties.isEmpty()) {
+ return defaultValue;
+ }
+ return super.getProperty(name, defaultValue);
+ }
+
+ /**
+ * Sets the multi-valued options.
+ *
+ * @param options
+ * to set, may be {@code null} to set an empty map
+ */
+ public void setMultiValuedOptions(Map<String, List<String>> options) {
+ multiValuedOptions = options;
+ }
+
+ /**
+ * Retrieves all multi-valued options.
+ *
+ * @return an unmodifiable map
+ */
+ @NonNull
+ public Map<String, List<String>> getMultiValuedOptions() {
+ Map<String, List<String>> options = multiValuedOptions;
+ if (options == null) {
+ return Collections.emptyMap();
+ }
+ return Collections.unmodifiableMap(options);
+ }
+
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPasswordAuthFactory.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPasswordAuthFactory.java
new file mode 100644
index 0000000000..315d0258d6
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPasswordAuthFactory.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.internal.transport.sshd;
+
+import org.apache.sshd.client.auth.AbstractUserAuthFactory;
+import org.apache.sshd.client.auth.UserAuth;
+import org.apache.sshd.client.auth.password.UserAuthPasswordFactory;
+
+/**
+ * A customized {@link UserAuthPasswordFactory} that creates instance of
+ * {@link JGitPasswordAuthentication}.
+ */
+public class JGitPasswordAuthFactory extends AbstractUserAuthFactory {
+
+ /** The singleton {@link JGitPasswordAuthFactory}. */
+ public static final JGitPasswordAuthFactory INSTANCE = new JGitPasswordAuthFactory();
+
+ private JGitPasswordAuthFactory() {
+ super(UserAuthPasswordFactory.NAME);
+ }
+
+ @Override
+ public UserAuth create() {
+ return new JGitPasswordAuthentication();
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPasswordAuthentication.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPasswordAuthentication.java
new file mode 100644
index 0000000000..9a6ed39c68
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPasswordAuthentication.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.internal.transport.sshd;
+
+import java.util.concurrent.CancellationException;
+
+import org.apache.sshd.client.ClientAuthenticationManager;
+import org.apache.sshd.client.auth.keyboard.UserInteraction;
+import org.apache.sshd.client.auth.password.UserAuthPassword;
+import org.apache.sshd.client.session.ClientSession;
+
+/**
+ * A password authentication handler that uses the {@link JGitUserInteraction}
+ * to ask the user for the password. It also respects the
+ * {@code NumberOfPasswordPrompts} ssh config.
+ */
+public class JGitPasswordAuthentication extends UserAuthPassword {
+
+ private int maxAttempts;
+
+ private int attempts;
+
+ @Override
+ public void init(ClientSession session, String service) throws Exception {
+ super.init(session, service);
+ maxAttempts = Math.max(1,
+ session.getIntProperty(
+ ClientAuthenticationManager.PASSWORD_PROMPTS,
+ ClientAuthenticationManager.DEFAULT_PASSWORD_PROMPTS));
+ attempts = 0;
+ }
+
+ @Override
+ protected boolean sendAuthDataRequest(ClientSession session, String service)
+ throws Exception {
+ if (++attempts > maxAttempts) {
+ return false;
+ }
+ UserInteraction interaction = session.getUserInteraction();
+ if (!interaction.isInteractionAllowed(session)) {
+ return false;
+ }
+ String password = getPassword(session, interaction);
+ if (password == null) {
+ throw new CancellationException();
+ }
+ // sendPassword takes a buffer as first argument, but actually doesn't
+ // use it and creates its own buffer...
+ sendPassword(null, session, password, password);
+ return true;
+ }
+
+ private String getPassword(ClientSession session,
+ UserInteraction interaction) {
+ String[] results = interaction.interactive(session, null, null, "", //$NON-NLS-1$
+ new String[] { SshdText.get().passwordPrompt },
+ new boolean[] { false });
+ return (results == null || results.length == 0) ? null : results[0];
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthFactory.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthFactory.java
new file mode 100644
index 0000000000..0b3de4ace3
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthFactory.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.internal.transport.sshd;
+
+import java.util.List;
+
+import org.apache.sshd.client.auth.AbstractUserAuthFactory;
+import org.apache.sshd.client.auth.UserAuth;
+import org.apache.sshd.client.auth.pubkey.UserAuthPublicKeyFactory;
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.signature.Signature;
+import org.apache.sshd.common.signature.SignatureFactoriesManager;
+
+/**
+ * A customized authentication factory for public key user authentication. The
+ * default implementation {@link UserAuthPublicKeyFactory} ends up doing some
+ * crazy stream "magic" that loads too many keys too early.
+ */
+public class JGitPublicKeyAuthFactory extends AbstractUserAuthFactory
+ implements SignatureFactoriesManager {
+
+ /** The singleton {@link JGitPublicKeyAuthFactory}. */
+ public static final JGitPublicKeyAuthFactory INSTANCE = new JGitPublicKeyAuthFactory();
+
+ private JGitPublicKeyAuthFactory() {
+ super(UserAuthPublicKeyFactory.NAME);
+ }
+
+ @Override
+ public UserAuth create() {
+ return new JGitPublicKeyAuthentication(getSignatureFactories());
+ }
+
+ @Override
+ public List<NamedFactory<Signature>> getSignatureFactories() {
+ return null;
+ }
+
+ @Override
+ public void setSignatureFactories(List<NamedFactory<Signature>> factories) {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthentication.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthentication.java
new file mode 100644
index 0000000000..63b3990b13
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthentication.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.internal.transport.sshd;
+
+import java.util.List;
+
+import org.apache.sshd.client.auth.pubkey.UserAuthPublicKey;
+import org.apache.sshd.client.session.ClientSession;
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.signature.Signature;
+
+/**
+ * A specialized public key authentication handler that uses our own
+ * {@link JGitPublicKeyIterator}. The super class creates in
+ * {@link #init(ClientSession, String)} a
+ * {@link org.apache.sshd.client.auth.pubkey.UserAuthPublicKeyIterator}, which
+ * in its constructor does some strange {@link java.util.stream.Stream} "magic"
+ * that ends up loading keys prematurely.
+ */
+public class JGitPublicKeyAuthentication extends UserAuthPublicKey {
+
+ private ClientSession clientSession;
+
+ private String serviceName;
+
+ /**
+ * Creates a new {@link JGitPublicKeyAuthentication}.
+ *
+ * @param factories
+ * signature factories to use
+ */
+ public JGitPublicKeyAuthentication(
+ List<NamedFactory<Signature>> factories) {
+ super(factories);
+ }
+
+ @Override
+ public void init(ClientSession session, String service) throws Exception {
+ // Do *not* call super.init(); it'll create a UserAuthPublicKeyIterator
+ // and that's where things then go wrong. Instead, do the whole
+ // initialization directly here.
+ clientSession = session;
+ serviceName = service;
+ releaseKeys();
+ // Use our own iterator!
+ keys = new JGitPublicKeyIterator(session, this);
+ }
+
+ @Override
+ public ClientSession getClientSession() {
+ return clientSession;
+ }
+
+ @Override
+ public ClientSession getSession() {
+ return clientSession;
+ }
+
+ @Override
+ public String getService() {
+ return serviceName;
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyIterator.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyIterator.java
new file mode 100644
index 0000000000..cda12623d8
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyIterator.java
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.internal.transport.sshd;
+
+import java.io.IOException;
+import java.nio.channels.Channel;
+import java.security.KeyPair;
+import java.security.PublicKey;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.sshd.agent.SshAgent;
+import org.apache.sshd.agent.SshAgentFactory;
+import org.apache.sshd.client.auth.pubkey.AbstractKeyPairIterator;
+import org.apache.sshd.client.auth.pubkey.KeyAgentIdentity;
+import org.apache.sshd.client.auth.pubkey.KeyPairIdentity;
+import org.apache.sshd.client.auth.pubkey.PublicKeyIdentity;
+import org.apache.sshd.client.config.hosts.HostConfigEntry;
+import org.apache.sshd.client.session.ClientSession;
+import org.apache.sshd.common.FactoryManager;
+import org.apache.sshd.common.keyprovider.KeyIdentityProvider;
+import org.apache.sshd.common.signature.SignatureFactoriesManager;
+
+/**
+ * A new iterator over key pairs that we use instead of the default
+ * {@link org.apache.sshd.client.auth.pubkey.UserAuthPublicKeyIterator}, which
+ * in its constructor does some strange {@link java.util.stream.Stream} "magic"
+ * that ends up loading keys prematurely. This class uses plain
+ * {@link Iterator}s instead to avoid that problem. Used in
+ * {@link JGitPublicKeyAuthentication}.
+ *
+ * @see <a href=
+ * "https://issues.apache.org/jira/projects/SSHD/issues/SSHD-860">Upstream
+ * issue SSHD-860</a>
+ */
+public class JGitPublicKeyIterator
+ extends AbstractKeyPairIterator<PublicKeyIdentity> implements Channel {
+
+ // Re: the cause for the problem mentioned above has not been determined.
+ // It looks as if either the Apache code inadvertently calls
+ // GenericUtils.isEmpty() on all streams (which would load the first key
+ // of each stream), or the Java stream implementation does some prefetching.
+ // It's not entirely clear. Using Iterators we have more control over
+ // what happens when.
+
+ private final AtomicBoolean open = new AtomicBoolean(true);
+
+ private SshAgent agent;
+
+ private final List<Iterator<PublicKeyIdentity>> keys = new ArrayList<>(3);
+
+ private final Iterator<Iterator<PublicKeyIdentity>> keyIter;
+
+ private Iterator<PublicKeyIdentity> current;
+
+ private Boolean hasElement;
+
+ /**
+ * Creates a new {@link JGitPublicKeyIterator}.
+ *
+ * @param session
+ * we're trying to authenticate
+ * @param signatureFactories
+ * to use
+ * @throws Exception
+ * if an {@link SshAgentFactory} is configured and getting
+ * identities from the agent fails
+ */
+ public JGitPublicKeyIterator(ClientSession session,
+ SignatureFactoriesManager signatureFactories) throws Exception {
+ super(session);
+ boolean useAgent = true;
+ if (session instanceof JGitClientSession) {
+ HostConfigEntry config = ((JGitClientSession) session)
+ .getHostConfigEntry();
+ useAgent = !config.isIdentitiesOnly();
+ }
+ if (useAgent) {
+ FactoryManager manager = session.getFactoryManager();
+ SshAgentFactory factory = manager == null ? null
+ : manager.getAgentFactory();
+ if (factory != null) {
+ try {
+ agent = factory.createClient(manager);
+ keys.add(new AgentIdentityIterator(agent));
+ } catch (IOException e) {
+ try {
+ closeAgent();
+ } catch (IOException err) {
+ e.addSuppressed(err);
+ }
+ throw e;
+ }
+ }
+ }
+ keys.add(
+ new KeyPairIdentityIterator(session.getRegisteredIdentities(),
+ session, signatureFactories));
+ keys.add(new KeyPairIdentityIterator(session.getKeyPairProvider(),
+ session, signatureFactories));
+ keyIter = keys.iterator();
+ }
+
+ @Override
+ public boolean isOpen() {
+ return open.get();
+ }
+
+ @Override
+ public void close() throws IOException {
+ if (open.getAndSet(false)) {
+ closeAgent();
+ }
+ }
+
+ @Override
+ public boolean hasNext() {
+ if (!isOpen()) {
+ return false;
+ }
+ if (hasElement != null) {
+ return hasElement.booleanValue();
+ }
+ while (current == null || !current.hasNext()) {
+ if (keyIter.hasNext()) {
+ current = keyIter.next();
+ } else {
+ current = null;
+ hasElement = Boolean.FALSE;
+ return false;
+ }
+ }
+ hasElement = Boolean.TRUE;
+ return true;
+ }
+
+ @Override
+ public PublicKeyIdentity next() {
+ if (!isOpen() || hasElement == null && !hasNext()
+ || !hasElement.booleanValue()) {
+ throw new NoSuchElementException();
+ }
+ hasElement = null;
+ PublicKeyIdentity result;
+ try {
+ result = current.next();
+ } catch (NoSuchElementException e) {
+ result = null;
+ }
+ return result;
+ }
+
+ private void closeAgent() throws IOException {
+ if (agent == null) {
+ return;
+ }
+ try {
+ agent.close();
+ } finally {
+ agent = null;
+ }
+ }
+
+ /**
+ * An {@link Iterator} that maps the data obtained from an agent to
+ * {@link PublicKeyIdentity}.
+ */
+ private static class AgentIdentityIterator
+ implements Iterator<PublicKeyIdentity> {
+
+ private final SshAgent agent;
+
+ private final Iterator<? extends Map.Entry<PublicKey, String>> iter;
+
+ public AgentIdentityIterator(SshAgent agent) throws IOException {
+ this.agent = agent;
+ iter = agent == null ? null : agent.getIdentities().iterator();
+ }
+
+ @Override
+ public boolean hasNext() {
+ return iter != null && iter.hasNext();
+ }
+
+ @Override
+ public PublicKeyIdentity next() {
+ if (iter == null) {
+ throw new NoSuchElementException();
+ }
+ Map.Entry<PublicKey, String> entry = iter.next();
+ return new KeyAgentIdentity(agent, entry.getKey(),
+ entry.getValue());
+ }
+ }
+
+ /**
+ * An {@link Iterator} that maps {@link KeyPair} to
+ * {@link PublicKeyIdentity}.
+ */
+ private static class KeyPairIdentityIterator
+ implements Iterator<PublicKeyIdentity> {
+
+ private final Iterator<KeyPair> keyPairs;
+
+ private final ClientSession session;
+
+ private final SignatureFactoriesManager signatureFactories;
+
+ public KeyPairIdentityIterator(KeyIdentityProvider provider,
+ ClientSession session,
+ SignatureFactoriesManager signatureFactories) {
+ this.session = session;
+ this.signatureFactories = signatureFactories;
+ keyPairs = provider == null ? null : provider.loadKeys().iterator();
+ }
+
+ @Override
+ public boolean hasNext() {
+ return keyPairs != null && keyPairs.hasNext();
+ }
+
+ @Override
+ public PublicKeyIdentity next() {
+ if (keyPairs == null) {
+ throw new NoSuchElementException();
+ }
+ KeyPair key = keyPairs.next();
+ return new KeyPairIdentity(signatureFactories, session, key);
+ }
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitSshClient.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitSshClient.java
new file mode 100644
index 0000000000..212b67fe33
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitSshClient.java
@@ -0,0 +1,442 @@
+/*
+ * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.internal.transport.sshd;
+
+import static java.text.MessageFormat.format;
+import static org.eclipse.jgit.internal.transport.ssh.OpenSshConfigFile.positive;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.Proxy;
+import java.nio.file.Files;
+import java.nio.file.InvalidPathException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.security.KeyPair;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import org.apache.sshd.client.SshClient;
+import org.apache.sshd.client.config.hosts.HostConfigEntry;
+import org.apache.sshd.client.future.ConnectFuture;
+import org.apache.sshd.client.future.DefaultConnectFuture;
+import org.apache.sshd.client.session.ClientSessionImpl;
+import org.apache.sshd.client.session.SessionFactory;
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.future.SshFutureListener;
+import org.apache.sshd.common.io.IoConnectFuture;
+import org.apache.sshd.common.io.IoSession;
+import org.apache.sshd.common.keyprovider.FileKeyPairProvider;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.session.helpers.AbstractSession;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.eclipse.jgit.internal.transport.sshd.proxy.HttpClientConnector;
+import org.eclipse.jgit.internal.transport.sshd.proxy.Socks5ClientConnector;
+import org.eclipse.jgit.transport.CredentialsProvider;
+import org.eclipse.jgit.transport.SshConstants;
+import org.eclipse.jgit.transport.sshd.KeyCache;
+import org.eclipse.jgit.transport.sshd.ProxyData;
+import org.eclipse.jgit.transport.sshd.ProxyDataFactory;
+
+/**
+ * Customized {@link SshClient} for JGit. It creates specialized
+ * {@link JGitClientSession}s that know about the {@link HostConfigEntry} they
+ * were created for, and it loads all KeyPair identities lazily.
+ */
+public class JGitSshClient extends SshClient {
+
+ /**
+ * We need access to this during the constructor of the ClientSession,
+ * before setConnectAddress() can have been called. So we have to remember
+ * it in an attribute on the SshClient, from where we can then retrieve it.
+ */
+ static final AttributeKey<HostConfigEntry> HOST_CONFIG_ENTRY = new AttributeKey<>();
+
+ static final AttributeKey<InetSocketAddress> ORIGINAL_REMOTE_ADDRESS = new AttributeKey<>();
+
+ /**
+ * An attribute key for the comma-separated list of default preferred
+ * authentication mechanisms.
+ */
+ public static final AttributeKey<String> PREFERRED_AUTHENTICATIONS = new AttributeKey<>();
+
+ private KeyCache keyCache;
+
+ private CredentialsProvider credentialsProvider;
+
+ private ProxyDataFactory proxyDatabase;
+
+ @Override
+ protected SessionFactory createSessionFactory() {
+ // Override the parent's default
+ return new JGitSessionFactory(this);
+ }
+
+ @Override
+ public ConnectFuture connect(HostConfigEntry hostConfig)
+ throws IOException {
+ if (connector == null) {
+ throw new IllegalStateException("SshClient not started."); //$NON-NLS-1$
+ }
+ Objects.requireNonNull(hostConfig, "No host configuration"); //$NON-NLS-1$
+ String host = ValidateUtils.checkNotNullAndNotEmpty(
+ hostConfig.getHostName(), "No target host"); //$NON-NLS-1$
+ int port = hostConfig.getPort();
+ ValidateUtils.checkTrue(port > 0, "Invalid port: %d", port); //$NON-NLS-1$
+ String userName = hostConfig.getUsername();
+ InetSocketAddress address = new InetSocketAddress(host, port);
+ ConnectFuture connectFuture = new DefaultConnectFuture(
+ userName + '@' + address, null);
+ SshFutureListener<IoConnectFuture> listener = createConnectCompletionListener(
+ connectFuture, userName, address, hostConfig);
+ // sshd needs some entries from the host config already in the
+ // constructor of the session. Put those as properties on this client,
+ // where it will find them. We can set the host config only once the
+ // session object has been created.
+ copyProperty(
+ hostConfig.getProperty(SshConstants.PREFERRED_AUTHENTICATIONS,
+ getAttribute(PREFERRED_AUTHENTICATIONS)),
+ PREFERRED_AUTHS);
+ setAttribute(HOST_CONFIG_ENTRY, hostConfig);
+ setAttribute(ORIGINAL_REMOTE_ADDRESS, address);
+ // Proxy support
+ ProxyData proxy = getProxyData(address);
+ if (proxy != null) {
+ address = configureProxy(proxy, address);
+ proxy.clearPassword();
+ }
+ connector.connect(address).addListener(listener);
+ return connectFuture;
+ }
+
+ private void copyProperty(String value, String key) {
+ if (value != null && !value.isEmpty()) {
+ getProperties().put(key, value);
+ }
+ }
+
+ private ProxyData getProxyData(InetSocketAddress remoteAddress) {
+ ProxyDataFactory factory = getProxyDatabase();
+ return factory == null ? null : factory.get(remoteAddress);
+ }
+
+ private InetSocketAddress configureProxy(ProxyData proxyData,
+ InetSocketAddress remoteAddress) {
+ Proxy proxy = proxyData.getProxy();
+ if (proxy.type() == Proxy.Type.DIRECT
+ || !(proxy.address() instanceof InetSocketAddress)) {
+ return remoteAddress;
+ }
+ InetSocketAddress address = (InetSocketAddress) proxy.address();
+ switch (proxy.type()) {
+ case HTTP:
+ setClientProxyConnector(
+ new HttpClientConnector(address, remoteAddress,
+ proxyData.getUser(), proxyData.getPassword()));
+ return address;
+ case SOCKS:
+ setClientProxyConnector(
+ new Socks5ClientConnector(address, remoteAddress,
+ proxyData.getUser(), proxyData.getPassword()));
+ return address;
+ default:
+ log.warn(format(SshdText.get().unknownProxyProtocol,
+ proxy.type().name()));
+ return remoteAddress;
+ }
+ }
+
+ private SshFutureListener<IoConnectFuture> createConnectCompletionListener(
+ ConnectFuture connectFuture, String username,
+ InetSocketAddress address, HostConfigEntry hostConfig) {
+ return new SshFutureListener<IoConnectFuture>() {
+
+ @Override
+ public void operationComplete(IoConnectFuture future) {
+ if (future.isCanceled()) {
+ connectFuture.cancel();
+ return;
+ }
+ Throwable t = future.getException();
+ if (t != null) {
+ connectFuture.setException(t);
+ return;
+ }
+ IoSession ioSession = future.getSession();
+ try {
+ JGitClientSession session = createSession(ioSession,
+ username, address, hostConfig);
+ connectFuture.setSession(session);
+ } catch (RuntimeException e) {
+ connectFuture.setException(e);
+ ioSession.close(true);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "JGitSshClient$ConnectCompletionListener[" + username //$NON-NLS-1$
+ + '@' + address + ']';
+ }
+ };
+ }
+
+ private JGitClientSession createSession(IoSession ioSession,
+ String username, InetSocketAddress address,
+ HostConfigEntry hostConfig) {
+ AbstractSession rawSession = AbstractSession.getSession(ioSession);
+ if (!(rawSession instanceof JGitClientSession)) {
+ throw new IllegalStateException("Wrong session type: " //$NON-NLS-1$
+ + rawSession.getClass().getCanonicalName());
+ }
+ JGitClientSession session = (JGitClientSession) rawSession;
+ session.setUsername(username);
+ session.setConnectAddress(address);
+ session.setHostConfigEntry(hostConfig);
+ if (session.getCredentialsProvider() == null) {
+ session.setCredentialsProvider(getCredentialsProvider());
+ }
+ int numberOfPasswordPrompts = getNumberOfPasswordPrompts(hostConfig);
+ session.getProperties().put(PASSWORD_PROMPTS,
+ Integer.valueOf(numberOfPasswordPrompts));
+ FilePasswordProvider provider = getFilePasswordProvider();
+ if (provider instanceof RepeatingFilePasswordProvider) {
+ ((RepeatingFilePasswordProvider) provider)
+ .setAttempts(numberOfPasswordPrompts);
+ }
+ FileKeyPairProvider ourConfiguredKeysProvider = null;
+ List<Path> identities = hostConfig.getIdentities().stream()
+ .map(s -> {
+ try {
+ return Paths.get(s);
+ } catch (InvalidPathException e) {
+ log.warn(format(SshdText.get().configInvalidPath,
+ SshConstants.IDENTITY_FILE, s), e);
+ return null;
+ }
+ }).filter(p -> p != null && Files.exists(p))
+ .collect(Collectors.toList());
+ ourConfiguredKeysProvider = new CachingKeyPairProvider(identities,
+ keyCache);
+ ourConfiguredKeysProvider.setPasswordFinder(getFilePasswordProvider());
+ if (hostConfig.isIdentitiesOnly()) {
+ session.setKeyPairProvider(ourConfiguredKeysProvider);
+ } else {
+ KeyPairProvider defaultKeysProvider = getKeyPairProvider();
+ if (defaultKeysProvider instanceof FileKeyPairProvider) {
+ ((FileKeyPairProvider) defaultKeysProvider)
+ .setPasswordFinder(getFilePasswordProvider());
+ }
+ KeyPairProvider combinedProvider = new CombinedKeyPairProvider(
+ ourConfiguredKeysProvider, defaultKeysProvider);
+ session.setKeyPairProvider(combinedProvider);
+ }
+ return session;
+ }
+
+ private int getNumberOfPasswordPrompts(HostConfigEntry hostConfig) {
+ String prompts = hostConfig
+ .getProperty(SshConstants.NUMBER_OF_PASSWORD_PROMPTS);
+ if (prompts != null) {
+ prompts = prompts.trim();
+ int value = positive(prompts);
+ if (value > 0) {
+ return value;
+ }
+ log.warn(format(SshdText.get().configInvalidPositive,
+ SshConstants.NUMBER_OF_PASSWORD_PROMPTS, prompts));
+ }
+ // Default for NumberOfPasswordPrompts according to
+ // https://man.openbsd.org/ssh_config
+ return 3;
+ }
+
+ /**
+ * Set a cache for loaded keys. Newly discovered keys will be added when
+ * IdentityFile host entries from the ssh config file are used during
+ * session authentication.
+ *
+ * @param cache
+ * to use
+ */
+ public void setKeyCache(KeyCache cache) {
+ keyCache = cache;
+ }
+
+ /**
+ * Sets a {@link ProxyDataFactory} for connecting through proxies.
+ *
+ * @param factory
+ * to use, or {@code null} if proxying is not desired or
+ * supported
+ */
+ public void setProxyDatabase(ProxyDataFactory factory) {
+ proxyDatabase = factory;
+ }
+
+ /**
+ * Retrieves the {@link ProxyDataFactory}.
+ *
+ * @return the factory, or {@code null} if none is set
+ */
+ protected ProxyDataFactory getProxyDatabase() {
+ return proxyDatabase;
+ }
+
+ /**
+ * Sets the {@link CredentialsProvider} for this client.
+ *
+ * @param provider
+ * to set
+ */
+ public void setCredentialsProvider(CredentialsProvider provider) {
+ credentialsProvider = provider;
+ }
+
+ /**
+ * Retrieves the {@link CredentialsProvider} set for this client.
+ *
+ * @return the provider, or {@code null} if none is set.
+ */
+ public CredentialsProvider getCredentialsProvider() {
+ return credentialsProvider;
+ }
+
+ /**
+ * A {@link SessionFactory} to create our own specialized
+ * {@link JGitClientSession}s.
+ */
+ private static class JGitSessionFactory extends SessionFactory {
+
+ public JGitSessionFactory(JGitSshClient client) {
+ super(client);
+ }
+
+ @Override
+ protected ClientSessionImpl doCreateSession(IoSession ioSession)
+ throws Exception {
+ return new JGitClientSession(getClient(), ioSession);
+ }
+ }
+
+ /**
+ * A {@link KeyPairProvider} that iterates over the {@link Iterable}s
+ * returned by other {@link KeyPairProvider}s.
+ */
+ private static class CombinedKeyPairProvider implements KeyPairProvider {
+
+ private final List<KeyPairProvider> providers;
+
+ public CombinedKeyPairProvider(KeyPairProvider... providers) {
+ this(Arrays.stream(providers).filter(Objects::nonNull)
+ .collect(Collectors.toList()));
+ }
+
+ public CombinedKeyPairProvider(List<KeyPairProvider> providers) {
+ this.providers = providers;
+ }
+
+ @Override
+ public Iterable<String> getKeyTypes() {
+ throw new UnsupportedOperationException(
+ "Should not have been called in a ssh client"); //$NON-NLS-1$
+ }
+
+ @Override
+ public KeyPair loadKey(String type) {
+ throw new UnsupportedOperationException(
+ "Should not have been called in a ssh client"); //$NON-NLS-1$
+ }
+
+ @Override
+ public Iterable<KeyPair> loadKeys() {
+ return () -> new Iterator<KeyPair>() {
+
+ private Iterator<KeyPairProvider> factories = providers.iterator();
+ private Iterator<KeyPair> current;
+
+ private Boolean hasElement;
+
+ @Override
+ public boolean hasNext() {
+ if (hasElement != null) {
+ return hasElement.booleanValue();
+ }
+ while (current == null || !current.hasNext()) {
+ if (factories.hasNext()) {
+ current = factories.next().loadKeys().iterator();
+ } else {
+ current = null;
+ hasElement = Boolean.FALSE;
+ return false;
+ }
+ }
+ hasElement = Boolean.TRUE;
+ return true;
+ }
+
+ @Override
+ public KeyPair next() {
+ if (hasElement == null && !hasNext()
+ || !hasElement.booleanValue()) {
+ throw new NoSuchElementException();
+ }
+ hasElement = null;
+ KeyPair result;
+ try {
+ result = current.next();
+ } catch (NoSuchElementException e) {
+ result = null;
+ }
+ return result;
+ }
+
+ };
+ }
+
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitSshConfig.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitSshConfig.java
new file mode 100644
index 0000000000..9eced0fa7f
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitSshConfig.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.internal.transport.sshd;
+
+import static org.eclipse.jgit.internal.transport.ssh.OpenSshConfigFile.flag;
+import static org.eclipse.jgit.internal.transport.ssh.OpenSshConfigFile.positive;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.apache.sshd.client.config.hosts.HostConfigEntry;
+import org.apache.sshd.client.config.hosts.HostConfigEntryResolver;
+import org.apache.sshd.common.util.net.SshdSocketAddress;
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.internal.transport.ssh.OpenSshConfigFile;
+import org.eclipse.jgit.internal.transport.ssh.OpenSshConfigFile.HostEntry;
+import org.eclipse.jgit.transport.SshConstants;
+
+/**
+ * A {@link HostConfigEntryResolver} adapted specifically for JGit.
+ * <p>
+ * We use our own config file parser and entry resolution since the default
+ * {@link org.apache.sshd.client.config.hosts.ConfigFileHostEntryResolver
+ * ConfigFileHostEntryResolver} has a number of problems:
+ * </p>
+ * <ul>
+ * <li>It does case-insensitive pattern matching. Matching in OpenSsh is
+ * case-sensitive! Compare also bug 531118.</li>
+ * <li>It only merges values from the global items (before the first "Host"
+ * line) into the host entries. Otherwise it selects the most specific match.
+ * OpenSsh processes <em>all</em> entries in the order they appear in the file
+ * and whenever one matches, it updates values as appropriate.</li>
+ * <li>We have to ensure that ~ replacement uses the same HOME directory as
+ * JGit. Compare bug bug 526175.</li>
+ * </ul>
+ * Therefore, this re-uses the parsing and caching from
+ * {@link OpenSshConfigFile}.
+ *
+ * @since 5.2
+ */
+public class JGitSshConfig implements HostConfigEntryResolver {
+
+ private OpenSshConfigFile configFile;
+
+ /**
+ * Creates a new {@link OpenSshConfigFile} that will read the config from
+ * file {@code config} use the given file {@code home} as "home" directory.
+ *
+ * @param home
+ * user's home directory for the purpose of ~ replacement
+ * @param config
+ * file to load.
+ * @param localUserName
+ * user name of the current user on the local host OS
+ */
+ public JGitSshConfig(@NonNull File home, @NonNull File config,
+ @NonNull String localUserName) {
+ configFile = new OpenSshConfigFile(home, config, localUserName);
+ }
+
+ @Override
+ public HostConfigEntry resolveEffectiveHost(String host, int port,
+ String username) throws IOException {
+ HostEntry entry = configFile.lookup(host, port, username);
+ JGitHostConfigEntry config = new JGitHostConfigEntry();
+ // Apache MINA conflates all keys, even multi-valued ones, in one map
+ // and puts multiple values separated by commas in one string. See
+ // the javadoc on HostConfigEntry.
+ Map<String, String> allOptions = new TreeMap<>(
+ String.CASE_INSENSITIVE_ORDER);
+ allOptions.putAll(entry.getOptions());
+ // And what if a value contains a comma??
+ entry.getMultiValuedOptions().entrySet().stream()
+ .forEach(e -> allOptions.put(e.getKey(),
+ String.join(",", e.getValue()))); //$NON-NLS-1$
+ config.setProperties(allOptions);
+ // The following is an extension from JGitHostConfigEntry
+ config.setMultiValuedOptions(entry.getMultiValuedOptions());
+ // Also make sure the underlying properties are set
+ String hostName = entry.getValue(SshConstants.HOST_NAME);
+ if (hostName == null || hostName.isEmpty()) {
+ hostName = host;
+ }
+ config.setHostName(hostName);
+ config.setProperty(SshConstants.HOST_NAME, hostName);
+ config.setHost(SshdSocketAddress.isIPv6Address(hostName) ? "" : hostName); //$NON-NLS-1$
+ String user = username != null && !username.isEmpty() ? username
+ : entry.getValue(SshConstants.USER);
+ if (user == null || user.isEmpty()) {
+ user = configFile.getLocalUserName();
+ }
+ config.setUsername(user);
+ config.setProperty(SshConstants.USER, user);
+ int p = port >= 0 ? port : positive(entry.getValue(SshConstants.PORT));
+ config.setPort(p >= 0 ? p : SshConstants.SSH_DEFAULT_PORT);
+ config.setProperty(SshConstants.PORT,
+ Integer.toString(config.getPort()));
+ config.setIdentities(entry.getValues(SshConstants.IDENTITY_FILE));
+ config.setIdentitiesOnly(
+ flag(entry.getValue(SshConstants.IDENTITIES_ONLY)));
+ return config;
+ }
+
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitUserInteraction.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitUserInteraction.java
new file mode 100644
index 0000000000..99288b7af1
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitUserInteraction.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.internal.transport.sshd;
+
+import java.net.InetSocketAddress;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.sshd.client.auth.keyboard.UserInteraction;
+import org.apache.sshd.client.session.ClientSession;
+import org.apache.sshd.common.session.Session;
+import org.apache.sshd.common.session.SessionListener;
+import org.eclipse.jgit.transport.CredentialItem;
+import org.eclipse.jgit.transport.CredentialsProvider;
+import org.eclipse.jgit.transport.SshConstants;
+import org.eclipse.jgit.transport.URIish;
+
+/**
+ * A {@link UserInteraction} callback implementation based on a
+ * {@link CredentialsProvider}.
+ */
+public class JGitUserInteraction implements UserInteraction {
+
+ private final CredentialsProvider provider;
+
+ /**
+ * We need to reset the JGit credentials provider if we have repeated
+ * attempts.
+ */
+ private final Map<Session, SessionListener> ongoing = new ConcurrentHashMap<>();
+
+ /**
+ * Creates a new {@link JGitUserInteraction} for interactive password input
+ * based on the given {@link CredentialsProvider}.
+ *
+ * @param provider
+ * to use
+ */
+ public JGitUserInteraction(CredentialsProvider provider) {
+ this.provider = provider;
+ }
+
+ @Override
+ public boolean isInteractionAllowed(ClientSession session) {
+ return provider != null && provider.isInteractive();
+ }
+
+ @Override
+ public String[] interactive(ClientSession session, String name,
+ String instruction, String lang, String[] prompt, boolean[] echo) {
+ // This is keyboard-interactive or password authentication
+ List<CredentialItem> items = new ArrayList<>();
+ int numberOfHiddenInputs = 0;
+ for (int i = 0; i < prompt.length; i++) {
+ boolean hidden = i < echo.length && !echo[i];
+ if (hidden) {
+ numberOfHiddenInputs++;
+ }
+ }
+ // RFC 4256 (SSH_MSG_USERAUTH_INFO_REQUEST) says: "The language tag is
+ // deprecated and SHOULD be the empty string." and "[If there are no
+ // prompts] the client SHOULD still display the name and instruction
+ // fields" and "[The] client SHOULD print the name and instruction (if
+ // non-empty)"
+ if (name != null && !name.isEmpty()) {
+ items.add(new CredentialItem.InformationalMessage(name));
+ }
+ if (instruction != null && !instruction.isEmpty()) {
+ items.add(new CredentialItem.InformationalMessage(instruction));
+ }
+ for (int i = 0; i < prompt.length; i++) {
+ boolean hidden = i < echo.length && !echo[i];
+ if (hidden && numberOfHiddenInputs == 1) {
+ // We need to somehow trigger storing the password in the
+ // Eclipse secure storage in EGit. Currently, this is done only
+ // for password fields.
+ items.add(new CredentialItem.Password());
+ // TODO Possibly change EGit to store all hidden strings
+ // (keyed by the URI and the prompt?) so that we don't have to
+ // use this kludge here.
+ } else {
+ items.add(new CredentialItem.StringType(prompt[i], hidden));
+ }
+ }
+ if (items.isEmpty()) {
+ // Huh? No info, no prompts?
+ return prompt; // Is known to have length zero here
+ }
+ URIish uri = toURI(session.getUsername(),
+ (InetSocketAddress) session.getConnectAddress());
+ // Reset the provider for this URI if it's not the first attempt and we
+ // have hidden inputs. Otherwise add a session listener that will remove
+ // itself once authenticated.
+ if (numberOfHiddenInputs > 0) {
+ SessionListener listener = ongoing.get(session);
+ if (listener != null) {
+ provider.reset(uri);
+ } else {
+ listener = new SessionAuthMarker(ongoing);
+ ongoing.put(session, listener);
+ session.addSessionListener(listener);
+ }
+ }
+ if (provider.get(uri, items)) {
+ return items.stream().map(i -> {
+ if (i instanceof CredentialItem.Password) {
+ return new String(((CredentialItem.Password) i).getValue());
+ } else if (i instanceof CredentialItem.StringType) {
+ return ((CredentialItem.StringType) i).getValue();
+ }
+ return null;
+ }).filter(s -> s != null).toArray(String[]::new);
+ }
+ // TODO What to throw to abort the connection/authentication process?
+ // In UserAuthKeyboardInteractive.getUserResponses() it's clear that
+ // returning null is valid and signifies "an error"; we'll try the
+ // next authentication method. But if the user explicitly canceled,
+ // then we don't want to try the next methods...
+ //
+ // Probably not a serious issue with the typical order of public-key,
+ // keyboard-interactive, password.
+ return null;
+ }
+
+ @Override
+ public String getUpdatedPassword(ClientSession session, String prompt,
+ String lang) {
+ // TODO Implement password update in password authentication?
+ return null;
+ }
+
+ /**
+ * Creates a {@link URIish} from the given remote address and user name.
+ *
+ * @param userName
+ * for the uri
+ * @param remote
+ * address of the remote host
+ * @return the uri, with {@link SshConstants#SSH_SCHEME} as scheme
+ */
+ public static URIish toURI(String userName, InetSocketAddress remote) {
+ String host = remote.getHostString();
+ int port = remote.getPort();
+ return new URIish() //
+ .setScheme(SshConstants.SSH_SCHEME) //
+ .setHost(host) //
+ .setPort(port) //
+ .setUser(userName);
+ }
+
+ /**
+ * A {@link SessionListener} that removes itself from the session when
+ * authentication is done or the session is closed.
+ */
+ private static class SessionAuthMarker implements SessionListener {
+
+ private final Map<Session, SessionListener> registered;
+
+ public SessionAuthMarker(Map<Session, SessionListener> registered) {
+ this.registered = registered;
+ }
+
+ @Override
+ public void sessionEvent(Session session, SessionListener.Event event) {
+ if (event == SessionListener.Event.Authenticated) {
+ session.removeSessionListener(this);
+ registered.remove(session, this);
+ }
+ }
+
+ @Override
+ public void sessionClosed(Session session) {
+ session.removeSessionListener(this);
+ registered.remove(session, this);
+ }
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/KnownHostEntryReader.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/KnownHostEntryReader.java
new file mode 100644
index 0000000000..4db24a16b7
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/KnownHostEntryReader.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.internal.transport.sshd;
+
+import static java.text.MessageFormat.format;
+import static org.apache.sshd.client.config.hosts.HostPatternsHolder.NON_STANDARD_PORT_PATTERN_ENCLOSURE_END_DELIM;
+import static org.apache.sshd.client.config.hosts.HostPatternsHolder.NON_STANDARD_PORT_PATTERN_ENCLOSURE_START_DELIM;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.apache.sshd.client.config.hosts.HostPatternValue;
+import org.apache.sshd.client.config.hosts.HostPatternsHolder;
+import org.apache.sshd.client.config.hosts.KnownHostEntry;
+import org.apache.sshd.client.config.hosts.KnownHostHashValue;
+import org.apache.sshd.common.config.keys.AuthorizedKeyEntry;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Apache MINA sshd 2.0.0 KnownHostEntry cannot read a host entry line like
+ * "host:port ssh-rsa <key>"; it complains about an illegal character in the
+ * host name (correct would be "[host]:port"). The default known_hosts reader
+ * also aborts reading on the first error.
+ * <p>
+ * This reader is a bit more robust and tries to handle this case if there is
+ * only one colon (otherwise it might be an IPv6 address (without port)), and it
+ * skips and logs invalid entries, but still returns all other valid entries
+ * from the file.
+ * </p>
+ */
+public class KnownHostEntryReader {
+
+ private static final Logger LOG = LoggerFactory
+ .getLogger(KnownHostEntryReader.class);
+
+ private KnownHostEntryReader() {
+ // No instantiation
+ }
+
+ /**
+ * Reads a known_hosts file and returns all valid entries. Invalid entries
+ * are skipped (and a message is logged).
+ *
+ * @param path
+ * of the file to read
+ * @return a {@link List} of all valid entries read from the file
+ * @throws IOException
+ * if the file cannot be read.
+ */
+ public static List<KnownHostEntry> readFromFile(Path path)
+ throws IOException {
+ List<KnownHostEntry> result = new LinkedList<>();
+ try (BufferedReader r = Files.newBufferedReader(path,
+ StandardCharsets.UTF_8)) {
+ r.lines().forEachOrdered(l -> {
+ if (l == null) {
+ return;
+ }
+ String line = clean(l);
+ if (line.isEmpty()) {
+ return;
+ }
+ try {
+ KnownHostEntry entry = parseHostEntry(line);
+ if (entry != null) {
+ result.add(entry);
+ } else {
+ LOG.warn(format(SshdText.get().knownHostsInvalidLine,
+ path, line));
+ }
+ } catch (RuntimeException e) {
+ LOG.warn(format(SshdText.get().knownHostsInvalidLine, path,
+ line), e);
+ }
+ });
+ }
+ return result;
+ }
+
+ private static String clean(String line) {
+ int i = line.indexOf('#');
+ return i < 0 ? line.trim() : line.substring(0, i).trim();
+ }
+
+ private static KnownHostEntry parseHostEntry(String line) {
+ KnownHostEntry entry = new KnownHostEntry();
+ entry.setConfigLine(line);
+ String tmp = line;
+ int i = 0;
+ if (tmp.charAt(0) == KnownHostEntry.MARKER_INDICATOR) {
+ // A marker
+ i = tmp.indexOf(' ', 1);
+ if (i < 0) {
+ return null;
+ }
+ entry.setMarker(tmp.substring(1, i));
+ tmp = tmp.substring(i + 1).trim();
+ }
+ i = tmp.indexOf(' ');
+ if (i < 0) {
+ return null;
+ }
+ // Hash, or host patterns
+ if (tmp.charAt(0) == KnownHostHashValue.HASHED_HOST_DELIMITER) {
+ // Hashed host entry
+ KnownHostHashValue hash = KnownHostHashValue
+ .parse(tmp.substring(0, i));
+ if (hash == null) {
+ return null;
+ }
+ entry.setHashedEntry(hash);
+ entry.setPatterns(null);
+ } else {
+ Collection<HostPatternValue> patterns = parsePatterns(
+ tmp.substring(0, i));
+ if (patterns == null || patterns.isEmpty()) {
+ return null;
+ }
+ entry.setHashedEntry(null);
+ entry.setPatterns(patterns);
+ }
+ tmp = tmp.substring(i + 1).trim();
+ AuthorizedKeyEntry key = AuthorizedKeyEntry
+ .parseAuthorizedKeyEntry(tmp);
+ if (key == null) {
+ return null;
+ }
+ entry.setKeyEntry(key);
+ return entry;
+ }
+
+ private static Collection<HostPatternValue> parsePatterns(String text) {
+ if (text.isEmpty()) {
+ return null;
+ }
+ List<String> items = Arrays.stream(text.split(",")) //$NON-NLS-1$
+ .filter(item -> item != null && !item.isEmpty()).map(item -> {
+ if (NON_STANDARD_PORT_PATTERN_ENCLOSURE_START_DELIM == item
+ .charAt(0)) {
+ return item;
+ }
+ int firstColon = item.indexOf(':');
+ if (firstColon < 0) {
+ return item;
+ }
+ int secondColon = item.indexOf(':', firstColon + 1);
+ if (secondColon > 0) {
+ // Assume an IPv6 address (without port).
+ return item;
+ }
+ // We have "host:port", should be "[host]:port"
+ return NON_STANDARD_PORT_PATTERN_ENCLOSURE_START_DELIM
+ + item.substring(0, firstColon)
+ + NON_STANDARD_PORT_PATTERN_ENCLOSURE_END_DELIM
+ + item.substring(firstColon);
+ }).collect(Collectors.toList());
+ return items.isEmpty() ? null : HostPatternsHolder.parsePatterns(items);
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/OpenSshServerKeyVerifier.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/OpenSshServerKeyVerifier.java
new file mode 100644
index 0000000000..540b586dda
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/OpenSshServerKeyVerifier.java
@@ -0,0 +1,745 @@
+/*
+ * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.internal.transport.sshd;
+
+import static java.text.MessageFormat.format;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.InvalidPathException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.security.GeneralSecurityException;
+import java.security.PublicKey;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Supplier;
+
+import org.apache.sshd.client.config.hosts.HostConfigEntry;
+import org.apache.sshd.client.config.hosts.KnownHostEntry;
+import org.apache.sshd.client.keyverifier.KnownHostsServerKeyVerifier;
+import org.apache.sshd.client.keyverifier.KnownHostsServerKeyVerifier.HostEntryPair;
+import org.apache.sshd.client.keyverifier.ServerKeyVerifier;
+import org.apache.sshd.client.session.ClientSession;
+import org.apache.sshd.common.config.keys.AuthorizedKeyEntry;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.config.keys.PublicKeyEntryResolver;
+import org.apache.sshd.common.digest.BuiltinDigests;
+import org.apache.sshd.common.util.io.ModifiableFileWatcher;
+import org.apache.sshd.common.util.net.SshdSocketAddress;
+import org.eclipse.jgit.internal.storage.file.LockFile;
+import org.eclipse.jgit.transport.CredentialItem;
+import org.eclipse.jgit.transport.CredentialsProvider;
+import org.eclipse.jgit.transport.SshConstants;
+import org.eclipse.jgit.transport.URIish;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A sever host key verifier that honors the {@code StrictHostKeyChecking} and
+ * {@code UserKnownHostsFile} values from the ssh configuration.
+ * <p>
+ * The verifier can be given default known_hosts files in the constructor, which
+ * will be used if the ssh config does not specify a {@code UserKnownHostsFile}.
+ * If the ssh config <em>does</em> set {@code UserKnownHostsFile}, the verifier
+ * uses the given files in the order given. Non-existing or unreadable files are
+ * ignored.
+ * <p>
+ * {@code StrictHostKeyChecking} accepts the following values:
+ * </p>
+ * <dl>
+ * <dt>ask</dt>
+ * <dd>Ask the user whether new or changed keys shall be accepted and be added
+ * to the known_hosts file.</dd>
+ * <dt>yes/true</dt>
+ * <dd>Accept only keys listed in the known_hosts file.</dd>
+ * <dt>no/false</dt>
+ * <dd>Silently accept all new or changed keys, add new keys to the known_hosts
+ * file.</dd>
+ * <dt>accept-new</dt>
+ * <dd>Silently accept keys for new hosts and add them to the known_hosts
+ * file.</dd>
+ * </dl>
+ * <p>
+ * If {@code StrictHostKeyChecking} is not set, or set to any other value, the
+ * default value <b>ask</b> is active.
+ * </p>
+ * <p>
+ * This implementation relies on the {@link ClientSession} being a
+ * {@link JGitClientSession}. By default Apache MINA sshd does not forward the
+ * config file host entry to the session, so it would be unknown here which
+ * entry it was and what setting of {@code StrictHostKeyChecking} should be
+ * used. If used with some other session type, the implementation assumes
+ * "<b>ask</b>".
+ * <p>
+ * <p>
+ * Asking the user is done via a {@link CredentialsProvider} obtained from the
+ * session. If none is set, the implementation falls back to strict host key
+ * checking ("<b>yes</b>").
+ * </p>
+ * <p>
+ * Note that adding a key to the known hosts file may create the file. You can
+ * specify in the constructor whether the user shall be asked about that, too.
+ * If the the user declines updating the file, but the key was otherwise
+ * accepted (user confirmed for "<b>ask</b>", or "no" or "accept-new" are
+ * active), the key is accepted for this session only.
+ * </p>
+ * <p>
+ * If several known hosts files are specified, a new key is always added to the
+ * first file (even if it doesn't exist yet; see the note about file creation
+ * above).
+ * </p>
+ *
+ * @see <a href="http://man.openbsd.org/OpenBSD-current/man5/ssh_config.5">man
+ * ssh-config</a>
+ */
+public class OpenSshServerKeyVerifier
+ implements ServerKeyVerifier, ServerKeyLookup {
+
+ // TODO: GlobalKnownHostsFile? May need some kind of LRU caching; these
+ // files may be large!
+
+ private static final Logger LOG = LoggerFactory
+ .getLogger(OpenSshServerKeyVerifier.class);
+
+ /** Can be used to mark revoked known host lines. */
+ private static final String MARKER_REVOKED = "revoked"; //$NON-NLS-1$
+
+ private final boolean askAboutNewFile;
+
+ private final Map<Path, HostKeyFile> knownHostsFiles = new ConcurrentHashMap<>();
+
+ private final List<HostKeyFile> defaultFiles = new ArrayList<>();
+
+ private enum ModifiedKeyHandling {
+ DENY, ALLOW, ALLOW_AND_STORE
+ }
+
+ /**
+ * Creates a new {@link OpenSshServerKeyVerifier}.
+ *
+ * @param askAboutNewFile
+ * whether to ask the user, if possible, about creating a new
+ * non-existing known_hosts file
+ * @param defaultFiles
+ * typically ~/.ssh/known_hosts and ~/.ssh/known_hosts2. May be
+ * empty or {@code null}, in which case no default files are
+ * installed. The files need not exist.
+ */
+ public OpenSshServerKeyVerifier(boolean askAboutNewFile,
+ List<Path> defaultFiles) {
+ if (defaultFiles != null) {
+ for (Path file : defaultFiles) {
+ HostKeyFile newFile = new HostKeyFile(file);
+ knownHostsFiles.put(file, newFile);
+ this.defaultFiles.add(newFile);
+ }
+ }
+ this.askAboutNewFile = askAboutNewFile;
+ }
+
+ private List<HostKeyFile> getFilesToUse(ClientSession session) {
+ List<HostKeyFile> filesToUse = defaultFiles;
+ if (session instanceof JGitClientSession) {
+ HostConfigEntry entry = ((JGitClientSession) session)
+ .getHostConfigEntry();
+ if (entry instanceof JGitHostConfigEntry) {
+ // Always true!
+ List<HostKeyFile> userFiles = addUserHostKeyFiles(
+ ((JGitHostConfigEntry) entry).getMultiValuedOptions()
+ .get(SshConstants.USER_KNOWN_HOSTS_FILE));
+ if (!userFiles.isEmpty()) {
+ filesToUse = userFiles;
+ }
+ }
+ }
+ return filesToUse;
+ }
+
+ @Override
+ public List<HostEntryPair> lookup(ClientSession session,
+ SocketAddress remote) {
+ List<HostKeyFile> filesToUse = getFilesToUse(session);
+ HostKeyHelper helper = new HostKeyHelper();
+ List<HostEntryPair> result = new ArrayList<>();
+ Collection<SshdSocketAddress> candidates = helper
+ .resolveHostNetworkIdentities(session, remote);
+ for (HostKeyFile file : filesToUse) {
+ for (HostEntryPair current : file.get()) {
+ KnownHostEntry entry = current.getHostEntry();
+ for (SshdSocketAddress host : candidates) {
+ if (entry.isHostMatch(host.getHostName(), host.getPort())) {
+ result.add(current);
+ break;
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public boolean verifyServerKey(ClientSession clientSession,
+ SocketAddress remoteAddress, PublicKey serverKey) {
+ List<HostKeyFile> filesToUse = getFilesToUse(clientSession);
+ AskUser ask = new AskUser();
+ HostEntryPair[] modified = { null };
+ Path path = null;
+ HostKeyHelper helper = new HostKeyHelper();
+ for (HostKeyFile file : filesToUse) {
+ try {
+ if (find(clientSession, remoteAddress, serverKey, file.get(),
+ modified, helper)) {
+ return true;
+ }
+ } catch (RevokedKeyException e) {
+ ask.revokedKey(clientSession, remoteAddress, serverKey,
+ file.getPath());
+ return false;
+ }
+ if (path == null && modified[0] != null) {
+ // Remember the file in which we might need to update the
+ // entry
+ path = file.getPath();
+ }
+ }
+ if (modified[0] != null) {
+ // We found an entry, but with a different key
+ ModifiedKeyHandling toDo = ask.acceptModifiedServerKey(
+ clientSession, remoteAddress, modified[0].getServerKey(),
+ serverKey, path);
+ if (toDo == ModifiedKeyHandling.ALLOW_AND_STORE) {
+ try {
+ updateModifiedServerKey(clientSession, remoteAddress,
+ serverKey, modified[0], path, helper);
+ knownHostsFiles.get(path).resetReloadAttributes();
+ } catch (IOException e) {
+ LOG.warn(format(SshdText.get().knownHostsCouldNotUpdate,
+ path));
+ }
+ }
+ if (toDo == ModifiedKeyHandling.DENY) {
+ return false;
+ }
+ // TODO: OpenSsh disables password and keyboard-interactive
+ // authentication in this case. Also agent and local port forwarding
+ // are switched off. (Plus a few other things such as X11 forwarding
+ // that are of no interest to a git client.)
+ return true;
+ } else if (ask.acceptUnknownKey(clientSession, remoteAddress,
+ serverKey)) {
+ if (!filesToUse.isEmpty()) {
+ HostKeyFile toUpdate = filesToUse.get(0);
+ path = toUpdate.getPath();
+ try {
+ updateKnownHostsFile(clientSession, remoteAddress,
+ serverKey, path, helper);
+ toUpdate.resetReloadAttributes();
+ } catch (IOException e) {
+ LOG.warn(format(SshdText.get().knownHostsCouldNotUpdate,
+ path));
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ private static class RevokedKeyException extends Exception {
+ private static final long serialVersionUID = 1L;
+ }
+
+ private boolean find(ClientSession clientSession,
+ SocketAddress remoteAddress, PublicKey serverKey,
+ List<HostEntryPair> entries, HostEntryPair[] modified,
+ HostKeyHelper helper) throws RevokedKeyException {
+ Collection<SshdSocketAddress> candidates = helper
+ .resolveHostNetworkIdentities(clientSession, remoteAddress);
+ for (HostEntryPair current : entries) {
+ KnownHostEntry entry = current.getHostEntry();
+ for (SshdSocketAddress host : candidates) {
+ if (entry.isHostMatch(host.getHostName(), host.getPort())) {
+ boolean isRevoked = MARKER_REVOKED
+ .equals(entry.getMarker());
+ if (KeyUtils.compareKeys(serverKey,
+ current.getServerKey())) {
+ // Exact match
+ if (isRevoked) {
+ throw new RevokedKeyException();
+ }
+ modified[0] = null;
+ return true;
+ } else if (!isRevoked) {
+ // Server sent a different key
+ modified[0] = current;
+ // Keep going -- maybe there's another entry for this
+ // host
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ private List<HostKeyFile> addUserHostKeyFiles(List<String> fileNames) {
+ if (fileNames == null || fileNames.isEmpty()) {
+ return Collections.emptyList();
+ }
+ List<HostKeyFile> userFiles = new ArrayList<>();
+ for (String name : fileNames) {
+ try {
+ Path path = Paths.get(name);
+ HostKeyFile file = knownHostsFiles.computeIfAbsent(path,
+ p -> new HostKeyFile(path));
+ userFiles.add(file);
+ } catch (InvalidPathException e) {
+ LOG.warn(format(SshdText.get().knownHostsInvalidPath,
+ name));
+ }
+ }
+ return userFiles;
+ }
+
+ private void updateKnownHostsFile(ClientSession clientSession,
+ SocketAddress remoteAddress, PublicKey serverKey, Path path,
+ HostKeyHelper updater)
+ throws IOException {
+ KnownHostEntry entry = updater.prepareKnownHostEntry(clientSession,
+ remoteAddress, serverKey);
+ if (entry == null) {
+ return;
+ }
+ if (!Files.exists(path)) {
+ if (askAboutNewFile) {
+ CredentialsProvider provider = getCredentialsProvider(
+ clientSession);
+ if (provider == null) {
+ // We can't ask, so don't create the file
+ return;
+ }
+ URIish uri = new URIish().setPath(path.toString());
+ if (!askUser(provider, uri, //
+ format(SshdText.get().knownHostsUserAskCreationPrompt,
+ path), //
+ format(SshdText.get().knownHostsUserAskCreationMsg,
+ path))) {
+ return;
+ }
+ }
+ }
+ LockFile lock = new LockFile(path.toFile());
+ if (lock.lockForAppend()) {
+ try {
+ try (BufferedWriter writer = new BufferedWriter(
+ new OutputStreamWriter(lock.getOutputStream(),
+ StandardCharsets.UTF_8))) {
+ writer.newLine();
+ writer.write(entry.getConfigLine());
+ writer.newLine();
+ }
+ lock.commit();
+ } catch (IOException e) {
+ lock.unlock();
+ throw e;
+ }
+ } else {
+ LOG.warn(format(SshdText.get().knownHostsFileLockedUpdate,
+ path));
+ }
+ }
+
+ private void updateModifiedServerKey(ClientSession clientSession,
+ SocketAddress remoteAddress, PublicKey serverKey,
+ HostEntryPair entry, Path path, HostKeyHelper helper)
+ throws IOException {
+ KnownHostEntry hostEntry = entry.getHostEntry();
+ String oldLine = hostEntry.getConfigLine();
+ String newLine = helper.prepareModifiedServerKeyLine(clientSession,
+ remoteAddress, hostEntry, oldLine, entry.getServerKey(),
+ serverKey);
+ if (newLine == null || newLine.isEmpty()) {
+ return;
+ }
+ if (oldLine == null || oldLine.isEmpty() || newLine.equals(oldLine)) {
+ // Shouldn't happen.
+ return;
+ }
+ LockFile lock = new LockFile(path.toFile());
+ if (lock.lock()) {
+ try {
+ try (BufferedWriter writer = new BufferedWriter(
+ new OutputStreamWriter(lock.getOutputStream(),
+ StandardCharsets.UTF_8));
+ BufferedReader reader = Files.newBufferedReader(path,
+ StandardCharsets.UTF_8)) {
+ boolean done = false;
+ String line;
+ while ((line = reader.readLine()) != null) {
+ String toWrite = line;
+ if (!done) {
+ int pos = line.indexOf('#');
+ String toTest = pos < 0 ? line
+ : line.substring(0, pos);
+ if (toTest.trim().equals(oldLine)) {
+ toWrite = newLine;
+ done = true;
+ }
+ }
+ writer.write(toWrite);
+ writer.newLine();
+ }
+ }
+ lock.commit();
+ } catch (IOException e) {
+ lock.unlock();
+ throw e;
+ }
+ } else {
+ LOG.warn(format(SshdText.get().knownHostsFileLockedUpdate,
+ path));
+ }
+ }
+
+ private static CredentialsProvider getCredentialsProvider(
+ ClientSession session) {
+ if (session instanceof JGitClientSession) {
+ return ((JGitClientSession) session).getCredentialsProvider();
+ }
+ return null;
+ }
+
+ private static boolean askUser(CredentialsProvider provider, URIish uri,
+ String prompt, String... messages) {
+ List<CredentialItem> items = new ArrayList<>(messages.length + 1);
+ for (String message : messages) {
+ items.add(new CredentialItem.InformationalMessage(message));
+ }
+ if (prompt != null) {
+ CredentialItem.YesNoType answer = new CredentialItem.YesNoType(
+ prompt);
+ items.add(answer);
+ return provider.get(uri, items) && answer.getValue();
+ } else {
+ return provider.get(uri, items);
+ }
+ }
+
+ private static class AskUser {
+
+ private enum Check {
+ ASK, DENY, ALLOW;
+ }
+
+ @SuppressWarnings("nls")
+ private Check checkMode(ClientSession session,
+ SocketAddress remoteAddress, boolean changed) {
+ if (!(remoteAddress instanceof InetSocketAddress)) {
+ return Check.DENY;
+ }
+ if (session instanceof JGitClientSession) {
+ HostConfigEntry entry = ((JGitClientSession) session)
+ .getHostConfigEntry();
+ String value = entry.getProperty(
+ SshConstants.STRICT_HOST_KEY_CHECKING, "ask");
+ switch (value.toLowerCase(Locale.ROOT)) {
+ case SshConstants.YES:
+ case SshConstants.ON:
+ return Check.DENY;
+ case SshConstants.NO:
+ case SshConstants.OFF:
+ return Check.ALLOW;
+ case "accept-new":
+ return changed ? Check.DENY : Check.ALLOW;
+ default:
+ break;
+ }
+ }
+ if (getCredentialsProvider(session) == null) {
+ // This is called only for new, unknown hosts. If we have no way
+ // to interact with the user, the fallback mode is to deny the
+ // key.
+ return Check.DENY;
+ }
+ return Check.ASK;
+ }
+
+ public void revokedKey(ClientSession clientSession,
+ SocketAddress remoteAddress, PublicKey serverKey, Path path) {
+ CredentialsProvider provider = getCredentialsProvider(
+ clientSession);
+ if (provider == null) {
+ return;
+ }
+ InetSocketAddress remote = (InetSocketAddress) remoteAddress;
+ URIish uri = JGitUserInteraction.toURI(clientSession.getUsername(),
+ remote);
+ String sha256 = KeyUtils.getFingerPrint(BuiltinDigests.sha256,
+ serverKey);
+ String md5 = KeyUtils.getFingerPrint(BuiltinDigests.md5, serverKey);
+ String keyAlgorithm = serverKey.getAlgorithm();
+ askUser(provider, uri, null, //
+ format(SshdText.get().knownHostsRevokedKeyMsg,
+ remote.getHostString(), path),
+ format(SshdText.get().knownHostsKeyFingerprints,
+ keyAlgorithm),
+ md5, sha256);
+ }
+
+ public boolean acceptUnknownKey(ClientSession clientSession,
+ SocketAddress remoteAddress, PublicKey serverKey) {
+ Check check = checkMode(clientSession, remoteAddress, false);
+ if (check != Check.ASK) {
+ return check == Check.ALLOW;
+ }
+ CredentialsProvider provider = getCredentialsProvider(
+ clientSession);
+ InetSocketAddress remote = (InetSocketAddress) remoteAddress;
+ // Ask the user
+ String sha256 = KeyUtils.getFingerPrint(BuiltinDigests.sha256,
+ serverKey);
+ String md5 = KeyUtils.getFingerPrint(BuiltinDigests.md5, serverKey);
+ String keyAlgorithm = serverKey.getAlgorithm();
+ String remoteHost = remote.getHostString();
+ URIish uri = JGitUserInteraction.toURI(clientSession.getUsername(),
+ remote);
+ String prompt = SshdText.get().knownHostsUnknownKeyPrompt;
+ return askUser(provider, uri, prompt, //
+ format(SshdText.get().knownHostsUnknownKeyMsg,
+ remoteHost),
+ format(SshdText.get().knownHostsKeyFingerprints,
+ keyAlgorithm),
+ md5, sha256);
+ }
+
+ public ModifiedKeyHandling acceptModifiedServerKey(
+ ClientSession clientSession,
+ SocketAddress remoteAddress, PublicKey expected,
+ PublicKey actual, Path path) {
+ Check check = checkMode(clientSession, remoteAddress, true);
+ if (check == Check.ALLOW) {
+ // Never auto-store on CHECK.ALLOW
+ return ModifiedKeyHandling.ALLOW;
+ }
+ InetSocketAddress remote = (InetSocketAddress) remoteAddress;
+ String keyAlgorithm = actual.getAlgorithm();
+ String remoteHost = remote.getHostString();
+ URIish uri = JGitUserInteraction.toURI(clientSession.getUsername(),
+ remote);
+ List<String> messages = new ArrayList<>();
+ String warning = format(
+ SshdText.get().knownHostsModifiedKeyWarning,
+ keyAlgorithm, expected.getAlgorithm(), remoteHost,
+ KeyUtils.getFingerPrint(BuiltinDigests.md5, expected),
+ KeyUtils.getFingerPrint(BuiltinDigests.sha256, expected),
+ KeyUtils.getFingerPrint(BuiltinDigests.md5, actual),
+ KeyUtils.getFingerPrint(BuiltinDigests.sha256, actual));
+ for (String line : warning.split("\n")) { //$NON-NLS-1$
+ messages.add(line);
+ }
+
+ CredentialsProvider provider = getCredentialsProvider(
+ clientSession);
+ if (check == Check.DENY) {
+ if (provider != null) {
+ messages.add(format(
+ SshdText.get().knownHostsModifiedKeyDenyMsg, path));
+ askUser(provider, uri, null,
+ messages.toArray(new String[0]));
+ }
+ return ModifiedKeyHandling.DENY;
+ }
+ // ASK -- two questions: procceed? and store?
+ List<CredentialItem> items = new ArrayList<>(messages.size() + 2);
+ for (String message : messages) {
+ items.add(new CredentialItem.InformationalMessage(message));
+ }
+ CredentialItem.YesNoType proceed = new CredentialItem.YesNoType(
+ SshdText.get().knownHostsModifiedKeyAcceptPrompt);
+ CredentialItem.YesNoType store = new CredentialItem.YesNoType(
+ SshdText.get().knownHostsModifiedKeyStorePrompt);
+ items.add(proceed);
+ items.add(store);
+ if (provider.get(uri, items) && proceed.getValue()) {
+ return store.getValue() ? ModifiedKeyHandling.ALLOW_AND_STORE
+ : ModifiedKeyHandling.ALLOW;
+ }
+ return ModifiedKeyHandling.DENY;
+ }
+
+ }
+
+ private static class HostKeyFile extends ModifiableFileWatcher
+ implements Supplier<List<HostEntryPair>> {
+
+ private List<HostEntryPair> entries = Collections.emptyList();
+
+ public HostKeyFile(Path path) {
+ super(path);
+ }
+
+ @Override
+ public List<HostEntryPair> get() {
+ Path path = getPath();
+ try {
+ if (checkReloadRequired()) {
+ if (!Files.exists(path)) {
+ // Has disappeared.
+ resetReloadAttributes();
+ return Collections.emptyList();
+ }
+ LockFile lock = new LockFile(path.toFile());
+ if (lock.lock()) {
+ try {
+ entries = reload(getPath());
+ } finally {
+ lock.unlock();
+ }
+ } else {
+ LOG.warn(format(SshdText.get().knownHostsFileLockedRead,
+ path));
+ }
+ }
+ } catch (IOException e) {
+ LOG.warn(format(SshdText.get().knownHostsFileReadFailed, path));
+ }
+ return Collections.unmodifiableList(entries);
+ }
+
+ private List<HostEntryPair> reload(Path path) throws IOException {
+ try {
+ List<KnownHostEntry> rawEntries = KnownHostEntryReader
+ .readFromFile(path);
+ updateReloadAttributes();
+ if (rawEntries == null || rawEntries.isEmpty()) {
+ return Collections.emptyList();
+ }
+ List<HostEntryPair> newEntries = new LinkedList<>();
+ for (KnownHostEntry entry : rawEntries) {
+ AuthorizedKeyEntry keyPart = entry.getKeyEntry();
+ if (keyPart == null) {
+ continue;
+ }
+ try {
+ PublicKey serverKey = keyPart.resolvePublicKey(
+ PublicKeyEntryResolver.IGNORING);
+ if (serverKey == null) {
+ LOG.warn(format(
+ SshdText.get().knownHostsUnknownKeyType,
+ path, entry.getConfigLine()));
+ } else {
+ newEntries.add(new HostEntryPair(entry, serverKey));
+ }
+ } catch (GeneralSecurityException e) {
+ LOG.warn(format(SshdText.get().knownHostsInvalidLine,
+ path, entry.getConfigLine()));
+ }
+ }
+ return newEntries;
+ } catch (FileNotFoundException e) {
+ resetReloadAttributes();
+ return Collections.emptyList();
+ }
+ }
+ }
+
+ // The stuff below is just a hack to avoid having to copy a lot of code from
+ // KnownHostsServerKeyVerifier
+
+ private static class HostKeyHelper extends KnownHostsServerKeyVerifier {
+
+ public HostKeyHelper() {
+ // These two arguments will never be used in any way.
+ super((c, r, s) -> false, new File(".").toPath()); //$NON-NLS-1$
+ }
+
+ @Override
+ protected KnownHostEntry prepareKnownHostEntry(
+ ClientSession clientSession, SocketAddress remoteAddress,
+ PublicKey serverKey) throws IOException {
+ // Make this method accessible
+ try {
+ return super.prepareKnownHostEntry(clientSession, remoteAddress,
+ serverKey);
+ } catch (Exception e) {
+ throw new IOException(e.getMessage(), e);
+ }
+ }
+
+ @Override
+ protected String prepareModifiedServerKeyLine(
+ ClientSession clientSession, SocketAddress remoteAddress,
+ KnownHostEntry entry, String curLine, PublicKey expected,
+ PublicKey actual) throws IOException {
+ // Make this method accessible
+ try {
+ return super.prepareModifiedServerKeyLine(clientSession,
+ remoteAddress, entry, curLine, expected, actual);
+ } catch (Exception e) {
+ throw new IOException(e.getMessage(), e);
+ }
+ }
+
+ @Override
+ protected Collection<SshdSocketAddress> resolveHostNetworkIdentities(
+ ClientSession clientSession, SocketAddress remoteAddress) {
+ // Make this method accessible
+ return super.resolveHostNetworkIdentities(clientSession,
+ remoteAddress);
+ }
+ }
+
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/PasswordProviderWrapper.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/PasswordProviderWrapper.java
new file mode 100644
index 0000000000..93bd10285a
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/PasswordProviderWrapper.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.internal.transport.sshd;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.security.GeneralSecurityException;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.transport.CredentialsProvider;
+import org.eclipse.jgit.transport.URIish;
+import org.eclipse.jgit.transport.sshd.KeyPasswordProvider;
+
+/**
+ * A bridge from sshd's {@link RepeatingFilePasswordProvider} to our
+ * {@link KeyPasswordProvider} API.
+ */
+public class PasswordProviderWrapper implements RepeatingFilePasswordProvider {
+
+ private final KeyPasswordProvider delegate;
+
+ private Map<String, AtomicInteger> counts = new ConcurrentHashMap<>();
+
+ /**
+ * @param delegate
+ */
+ public PasswordProviderWrapper(@NonNull KeyPasswordProvider delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public void setAttempts(int numberOfPasswordPrompts) {
+ delegate.setAttempts(numberOfPasswordPrompts);
+ }
+
+ @Override
+ public int getAttempts() {
+ return delegate.getAttempts();
+ }
+
+ @Override
+ public String getPassword(String resourceKey) throws IOException {
+ int attempt = counts
+ .computeIfAbsent(resourceKey, k -> new AtomicInteger()).get();
+ char[] passphrase = delegate.getPassphrase(toUri(resourceKey), attempt);
+ if (passphrase == null) {
+ return null;
+ }
+ try {
+ return new String(passphrase);
+ } finally {
+ Arrays.fill(passphrase, '\000');
+ }
+ }
+
+ @Override
+ public ResourceDecodeResult handleDecodeAttemptResult(String resourceKey,
+ String password, Exception err)
+ throws IOException, GeneralSecurityException {
+ AtomicInteger count = counts.get(resourceKey);
+ int numberOfAttempts = count == null ? 0 : count.incrementAndGet();
+ ResourceDecodeResult result = null;
+ try {
+ if (delegate.keyLoaded(toUri(resourceKey), numberOfAttempts, err)) {
+ result = ResourceDecodeResult.RETRY;
+ } else {
+ result = ResourceDecodeResult.TERMINATE;
+ }
+ } finally {
+ if (result != ResourceDecodeResult.RETRY) {
+ counts.remove(resourceKey);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Creates a {@link URIish} from a given string. The
+ * {@link CredentialsProvider} uses uris as resource identifications.
+ *
+ * @param resourceKey
+ * to convert
+ * @return the uri
+ */
+ private URIish toUri(String resourceKey) {
+ try {
+ return new URIish(resourceKey);
+ } catch (URISyntaxException e) {
+ return new URIish().setPath(resourceKey); // Doesn't check!!
+ }
+ }
+
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/RepeatingFilePasswordProvider.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/RepeatingFilePasswordProvider.java
new file mode 100644
index 0000000000..5d58bd6d70
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/RepeatingFilePasswordProvider.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.internal.transport.sshd;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+
+/**
+ * A {@link FilePasswordProvider} augmented to support repeatedly asking for
+ * passwords.
+ *
+ * @since 5.2
+ */
+public interface RepeatingFilePasswordProvider extends FilePasswordProvider {
+
+ /**
+ * Define the maximum number of attempts to get a password that should be
+ * attempted for one identity resource through this provider.
+ *
+ * @param numberOfPasswordPrompts
+ * number of times to ask for a password;
+ * {@link IllegalArgumentException} may be thrown if <= 0
+ */
+ void setAttempts(int numberOfPasswordPrompts);
+
+ /**
+ * Gets the maximum number of attempts to get a password that should be
+ * attempted for one identity resource through this provider.
+ *
+ * @return the maximum number of attempts to try, always >= 1.
+ */
+ default int getAttempts() {
+ return 1;
+ }
+
+ // The following part of this interface is from the upstream resolution of
+ // SSHD-850. See https://github.com/apache/mina-sshd/commit/f19bd2e34 .
+ // TODO: remove this once we move to sshd > 2.1.0
+
+ /**
+ * Result value of
+ * {@link RepeatingFilePasswordProvider#handleDecodeAttemptResult(String, String, Exception)}.
+ */
+ public enum ResourceDecodeResult {
+ /** Re-throw the decoding exception. */
+ TERMINATE,
+ /** Retry the decoding process - including password prompt. */
+ RETRY,
+ /** Skip attempt and see if we can proceed without the key. */
+ IGNORE;
+ }
+
+ /**
+ * Invoked to inform the password provider about the decoding result.
+ * <b>Note:</b> any exception thrown from this method (including if called
+ * to inform about success) will be propagated instead of the original (if
+ * any was reported)
+ *
+ * @param resourceKey
+ * The resource key representing the <U>private</U> file
+ * @param password
+ * The password that was attempted
+ * @param err
+ * The attempt result - {@code null} for success
+ * @return How to proceed in case of error - <u>ignored</u> if invoked in
+ * order to report success. <b>Note:</b> {@code null} is same as
+ * {@link ResourceDecodeResult#TERMINATE}.
+ * @throws IOException
+ * @throws GeneralSecurityException
+ */
+ ResourceDecodeResult handleDecodeAttemptResult(String resourceKey,
+ String password, Exception err)
+ throws IOException, GeneralSecurityException;
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/ServerKeyLookup.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/ServerKeyLookup.java
new file mode 100644
index 0000000000..4f5f497f7f
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/ServerKeyLookup.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.internal.transport.sshd;
+
+import java.net.SocketAddress;
+import java.util.List;
+
+import org.apache.sshd.client.keyverifier.KnownHostsServerKeyVerifier.HostEntryPair;
+import org.apache.sshd.client.session.ClientSession;
+import org.eclipse.jgit.annotations.NonNull;
+
+/**
+ * Offers operations to retrieve server keys from known_hosts files.
+ */
+public interface ServerKeyLookup {
+
+ /**
+ * Retrieves all entries for a given remote address.
+ *
+ * @param session
+ * needed to determine the config files if specified in the ssh
+ * config
+ * @param remote
+ * to find entries for
+ * @return a possibly empty list of entries found, including revoked ones
+ */
+ @NonNull
+ List<HostEntryPair> lookup(ClientSession session, SocketAddress remote);
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/SshdText.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/SshdText.java
new file mode 100644
index 0000000000..5c79f2d40e
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/SshdText.java
@@ -0,0 +1,91 @@
+package org.eclipse.jgit.internal.transport.sshd;
+
+import org.eclipse.jgit.nls.NLS;
+import org.eclipse.jgit.nls.TranslationBundle;
+
+/**
+ * Externalized text messages for localization.
+ */
+public final class SshdText extends TranslationBundle {
+
+ /**
+ * Get an instance of this translation bundle.
+ *
+ * @return an instance of this translation bundle
+ */
+ public static SshdText get() {
+ return NLS.getBundleFor(SshdText.class);
+ }
+
+ // @formatter:off
+ /***/ public String authenticationCanceled;
+ /***/ public String closeListenerFailed;
+ /***/ public String configInvalidPath;
+ /***/ public String configInvalidPattern;
+ /***/ public String configInvalidPositive;
+ /***/ public String configNoKnownHostKeyAlgorithms;
+ /***/ public String configNoRemainingHostKeyAlgorithms;
+ /***/ public String ftpCloseFailed;
+ /***/ public String gssapiFailure;
+ /***/ public String gssapiInitFailure;
+ /***/ public String gssapiUnexpectedMechanism;
+ /***/ public String gssapiUnexpectedMessage;
+ /***/ public String identityFileCannotDecrypt;
+ /***/ public String identityFileNoKey;
+ /***/ public String identityFileMultipleKeys;
+ /***/ public String identityFileUnsupportedFormat;
+ /***/ public String kexServerKeyInvalid;
+ /***/ public String keyEncryptedMsg;
+ /***/ public String keyEncryptedPrompt;
+ /***/ public String keyEncryptedRetry;
+ /***/ public String keyLoadFailed;
+ /***/ public String knownHostsCouldNotUpdate;
+ /***/ public String knownHostsFileLockedRead;
+ /***/ public String knownHostsFileLockedUpdate;
+ /***/ public String knownHostsFileReadFailed;
+ /***/ public String knownHostsInvalidLine;
+ /***/ public String knownHostsInvalidPath;
+ /***/ public String knownHostsKeyFingerprints;
+ /***/ public String knownHostsModifiedKeyAcceptPrompt;
+ /***/ public String knownHostsModifiedKeyDenyMsg;
+ /***/ public String knownHostsModifiedKeyStorePrompt;
+ /***/ public String knownHostsModifiedKeyWarning;
+ /***/ public String knownHostsRevokedKeyMsg;
+ /***/ public String knownHostsUnknownKeyMsg;
+ /***/ public String knownHostsUnknownKeyPrompt;
+ /***/ public String knownHostsUnknownKeyType;
+ /***/ public String knownHostsUserAskCreationMsg;
+ /***/ public String knownHostsUserAskCreationPrompt;
+ /***/ public String passwordPrompt;
+ /***/ public String proxyCannotAuthenticate;
+ /***/ public String proxyHttpFailure;
+ /***/ public String proxyHttpInvalidUserName;
+ /***/ public String proxyHttpUnexpectedReply;
+ /***/ public String proxyHttpUnspecifiedFailureReason;
+ /***/ public String proxyPasswordPrompt;
+ /***/ public String proxySocksAuthenticationFailed;
+ /***/ public String proxySocksFailureForbidden;
+ /***/ public String proxySocksFailureGeneral;
+ /***/ public String proxySocksFailureHostUnreachable;
+ /***/ public String proxySocksFailureNetworkUnreachable;
+ /***/ public String proxySocksFailureRefused;
+ /***/ public String proxySocksFailureTTL;
+ /***/ public String proxySocksFailureUnspecified;
+ /***/ public String proxySocksFailureUnsupportedAddress;
+ /***/ public String proxySocksFailureUnsupportedCommand;
+ /***/ public String proxySocksGssApiFailure;
+ /***/ public String proxySocksGssApiMessageTooShort;
+ /***/ public String proxySocksGssApiUnknownMessage;
+ /***/ public String proxySocksGssApiVersionMismatch;
+ /***/ public String proxySocksNoRemoteHostName;
+ /***/ public String proxySocksPasswordTooLong;
+ /***/ public String proxySocksUnexpectedMessage;
+ /***/ public String proxySocksUnexpectedVersion;
+ /***/ public String proxySocksUsernameTooLong;
+ /***/ public String sessionCloseFailed;
+ /***/ public String sshClosingDown;
+ /***/ public String sshCommandTimeout;
+ /***/ public String sshProcessStillRunning;
+ /***/ public String unknownProxyProtocol;
+
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/auth/AbstractAuthenticationHandler.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/auth/AbstractAuthenticationHandler.java
new file mode 100644
index 0000000000..6caa1b6aa1
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/auth/AbstractAuthenticationHandler.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.internal.transport.sshd.auth;
+
+import java.net.InetSocketAddress;
+
+/**
+ * Abstract base class for {@link AuthenticationHandler}s encapsulating basic
+ * common things.
+ *
+ * @param <ParameterType>
+ * defining the parameter type for the authentication
+ * @param <TokenType>
+ * defining the token type for the authentication
+ */
+public abstract class AbstractAuthenticationHandler<ParameterType, TokenType>
+ implements AuthenticationHandler<ParameterType, TokenType> {
+
+ /** The {@link InetSocketAddress} or the proxy to connect to. */
+ protected InetSocketAddress proxy;
+
+ /** The last set parameters. */
+ protected ParameterType params;
+
+ /** A flag telling whether this authentication is done. */
+ protected boolean done;
+
+ /**
+ * Creates a new {@link AbstractAuthenticationHandler} to authenticate with
+ * the given {@code proxy}.
+ *
+ * @param proxy
+ * the {@link InetSocketAddress} of the proxy to connect to
+ */
+ public AbstractAuthenticationHandler(InetSocketAddress proxy) {
+ this.proxy = proxy;
+ }
+
+ @Override
+ public final void setParams(ParameterType input) {
+ params = input;
+ }
+
+ @Override
+ public final boolean isDone() {
+ return done;
+ }
+
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/auth/AuthenticationHandler.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/auth/AuthenticationHandler.java
new file mode 100644
index 0000000000..0e98a2e17a
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/auth/AuthenticationHandler.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.internal.transport.sshd.auth;
+
+import java.io.Closeable;
+
+/**
+ * An {@code AuthenticationHandler} encapsulates a possibly multi-step
+ * authentication protocol. Intended usage:
+ *
+ * <pre>
+ * setParams(something);
+ * start();
+ * sendToken(getToken());
+ * while (!isDone()) {
+ * setParams(receiveMessageAndExtractParams());
+ * process();
+ * Object t = getToken();
+ * if (t != null) {
+ * sendToken(t);
+ * }
+ * }
+ * </pre>
+ *
+ * An {@code AuthenticationHandler} may be stateful and therefore is a
+ * {@link Closeable}.
+ *
+ * @param <ParameterType>
+ * defining the parameter type for {@link #setParams(Object)}
+ * @param <TokenType>
+ * defining the token type for {@link #getToken()}
+ */
+public interface AuthenticationHandler<ParameterType, TokenType>
+ extends Closeable {
+
+ /**
+ * Produces the initial authentication token that can be then retrieved via
+ * {@link #getToken()}.
+ *
+ * @throws Exception
+ * if an error occurs
+ */
+ void start() throws Exception;
+
+ /**
+ * Produces the next authentication token, if any.
+ *
+ * @throws Exception
+ * if an error occurs
+ */
+ void process() throws Exception;
+
+ /**
+ * Sets the parameters for the next token generation via {@link #start()} or
+ * {@link #process()}.
+ *
+ * @param input
+ * to set, may be {@code null}
+ */
+ void setParams(ParameterType input);
+
+ /**
+ * Retrieves the last token generated.
+ *
+ * @return the token, or {@code null} if there is none
+ * @throws Exception
+ * if an error occurs
+ */
+ TokenType getToken() throws Exception;
+
+ /**
+ * Tells whether is authentication mechanism is done (successfully or
+ * unsuccessfully).
+ *
+ * @return whether this authentication is done
+ */
+ boolean isDone();
+
+ @Override
+ void close();
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/auth/BasicAuthentication.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/auth/BasicAuthentication.java
new file mode 100644
index 0000000000..efb1f55867
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/auth/BasicAuthentication.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.internal.transport.sshd.auth;
+
+import java.net.Authenticator;
+import java.net.Authenticator.RequestorType;
+import java.net.InetSocketAddress;
+import java.net.PasswordAuthentication;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.StandardCharsets;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Arrays;
+import java.util.concurrent.CancellationException;
+
+import org.eclipse.jgit.internal.transport.sshd.SshdText;
+import org.eclipse.jgit.transport.SshConstants;
+
+/**
+ * An abstract implementation of a username-password authentication. It can be
+ * given an initial known username-password pair; if so, this will be tried
+ * first. Subsequent rounds will then try to obtain a user name and password via
+ * the global {@link Authenticator}.
+ *
+ * @param <ParameterType>
+ * defining the parameter type for the authentication
+ * @param <TokenType>
+ * defining the token type for the authentication
+ */
+public abstract class BasicAuthentication<ParameterType, TokenType>
+ extends AbstractAuthenticationHandler<ParameterType, TokenType> {
+
+ /** The current user name. */
+ protected String user;
+
+ /** The current password. */
+ protected byte[] password;
+
+ /**
+ * Creates a new {@link BasicAuthentication} to authenticate with the given
+ * {@code proxy}.
+ *
+ * @param proxy
+ * {@link InetSocketAddress} of the proxy to connect to
+ * @param initialUser
+ * initial user name to try; may be {@code null}
+ * @param initialPassword
+ * initial password to try, may be {@code null}
+ */
+ public BasicAuthentication(InetSocketAddress proxy, String initialUser,
+ char[] initialPassword) {
+ super(proxy);
+ this.user = initialUser;
+ this.password = convert(initialPassword);
+ }
+
+ private byte[] convert(char[] pass) {
+ if (pass == null) {
+ return new byte[0];
+ }
+ ByteBuffer bytes = StandardCharsets.UTF_8.encode(CharBuffer.wrap(pass));
+ byte[] pwd = new byte[bytes.remaining()];
+ bytes.get(pwd);
+ if (bytes.hasArray()) {
+ Arrays.fill(bytes.array(), (byte) 0);
+ }
+ Arrays.fill(pass, '\000');
+ return pwd;
+ }
+
+ /**
+ * Clears the {@link #password}.
+ */
+ protected void clearPassword() {
+ if (password != null) {
+ Arrays.fill(password, (byte) 0);
+ }
+ password = new byte[0];
+ }
+
+ @Override
+ public final void close() {
+ clearPassword();
+ done = true;
+ }
+
+ @Override
+ public final void start() throws Exception {
+ if (user != null && !user.isEmpty()
+ || password != null && password.length > 0) {
+ return;
+ }
+ askCredentials();
+ }
+
+ @Override
+ public void process() throws Exception {
+ askCredentials();
+ }
+
+ /**
+ * Asks for credentials via the global {@link Authenticator}.
+ */
+ protected void askCredentials() {
+ clearPassword();
+ PasswordAuthentication auth = AccessController
+ .doPrivileged(new PrivilegedAction<PasswordAuthentication>() {
+
+ @Override
+ public PasswordAuthentication run() {
+ return Authenticator.requestPasswordAuthentication(
+ proxy.getHostString(), proxy.getAddress(),
+ proxy.getPort(), SshConstants.SSH_SCHEME,
+ SshdText.get().proxyPasswordPrompt, "Basic", //$NON-NLS-1$
+ null, RequestorType.PROXY);
+ }
+ });
+ if (auth == null) {
+ user = ""; //$NON-NLS-1$
+ throw new CancellationException(
+ SshdText.get().authenticationCanceled);
+ }
+ user = auth.getUserName();
+ password = convert(auth.getPassword());
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/auth/GssApiAuthentication.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/auth/GssApiAuthentication.java
new file mode 100644
index 0000000000..63cc954479
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/auth/GssApiAuthentication.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.internal.transport.sshd.auth;
+
+import static java.text.MessageFormat.format;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+
+import org.eclipse.jgit.internal.transport.sshd.GssApiMechanisms;
+import org.eclipse.jgit.internal.transport.sshd.SshdText;
+import org.ietf.jgss.GSSContext;
+
+/**
+ * An abstract implementation of a GSS-API multi-round authentication.
+ *
+ * @param <ParameterType>
+ * defining the parameter type for the authentication
+ * @param <TokenType>
+ * defining the token type for the authentication
+ */
+public abstract class GssApiAuthentication<ParameterType, TokenType>
+ extends AbstractAuthenticationHandler<ParameterType, TokenType> {
+
+ private GSSContext context;
+
+ /** The last token generated. */
+ protected byte[] token;
+
+ /**
+ * Creates a new {@link GssApiAuthentication} to authenticate with the given
+ * {@code proxy}.
+ *
+ * @param proxy
+ * the {@link InetSocketAddress} of the proxy to connect to
+ */
+ public GssApiAuthentication(InetSocketAddress proxy) {
+ super(proxy);
+ }
+
+ @Override
+ public void close() {
+ GssApiMechanisms.closeContextSilently(context);
+ context = null;
+ done = true;
+ }
+
+ @Override
+ public final void start() throws Exception {
+ try {
+ context = createContext();
+ context.requestMutualAuth(true);
+ context.requestConf(false);
+ context.requestInteg(false);
+ byte[] empty = new byte[0];
+ token = context.initSecContext(empty, 0, 0);
+ } catch (Exception e) {
+ close();
+ throw e;
+ }
+ }
+
+ @Override
+ public final void process() throws Exception {
+ if (context == null) {
+ throw new IOException(
+ format(SshdText.get().proxyCannotAuthenticate, proxy));
+ }
+ try {
+ byte[] received = extractToken(params);
+ token = context.initSecContext(received, 0, received.length);
+ checkDone();
+ } catch (Exception e) {
+ close();
+ throw e;
+ }
+ }
+
+ private void checkDone() throws Exception {
+ done = context.isEstablished();
+ if (done) {
+ context.dispose();
+ context = null;
+ }
+ }
+
+ /**
+ * Creates the {@link GSSContext} to use.
+ *
+ * @return a fresh {@link GSSContext} to use
+ * @throws Exception
+ * if the context cannot be created
+ */
+ protected abstract GSSContext createContext() throws Exception;
+
+ /**
+ * Extracts the token from the last set parameters.
+ *
+ * @param input
+ * to extract the token from
+ * @return the extracted token, or {@code null} if none
+ * @throws Exception
+ * if an error occurs
+ */
+ protected abstract byte[] extractToken(ParameterType input)
+ throws Exception;
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/AbstractClientProxyConnector.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/AbstractClientProxyConnector.java
new file mode 100644
index 0000000000..2e87c57332
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/AbstractClientProxyConnector.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.internal.transport.sshd.proxy;
+
+import java.net.InetSocketAddress;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.sshd.client.session.ClientSession;
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.internal.transport.sshd.JGitClientSession;
+
+/**
+ * Basic common functionality for a {@link StatefulProxyConnector}.
+ */
+public abstract class AbstractClientProxyConnector
+ implements StatefulProxyConnector {
+
+ private static final long DEFAULT_PROXY_TIMEOUT_MILLIS = TimeUnit.SECONDS
+ .toMillis(30L);
+
+ /** Guards {@link #done} and {@link #bufferedCommands}. */
+ private Object lock = new Object();
+
+ private boolean done;
+
+ private List<Callable<Void>> bufferedCommands = new ArrayList<>();
+
+ private AtomicReference<Runnable> unregister = new AtomicReference<>();
+
+ private long remainingProxyProtocolTime = DEFAULT_PROXY_TIMEOUT_MILLIS;
+
+ private long lastProxyOperationTime = 0L;
+
+ /** The ultimate remote address to connect to. */
+ protected final InetSocketAddress remoteAddress;
+
+ /** The proxy address. */
+ protected final InetSocketAddress proxyAddress;
+
+ /** The user to authenticate at the proxy with. */
+ protected String proxyUser;
+
+ /** The password to use for authentication at the proxy. */
+ protected char[] proxyPassword;
+
+ /**
+ * Creates a new {@link AbstractClientProxyConnector}.
+ *
+ * @param proxyAddress
+ * of the proxy server we're connecting to
+ * @param remoteAddress
+ * of the target server to connect to
+ * @param proxyUser
+ * to authenticate at the proxy with; may be {@code null}
+ * @param proxyPassword
+ * to authenticate at the proxy with; may be {@code null}
+ */
+ public AbstractClientProxyConnector(@NonNull InetSocketAddress proxyAddress,
+ @NonNull InetSocketAddress remoteAddress, String proxyUser,
+ char[] proxyPassword) {
+ this.proxyAddress = proxyAddress;
+ this.remoteAddress = remoteAddress;
+ this.proxyUser = proxyUser;
+ this.proxyPassword = proxyPassword == null ? new char[0]
+ : proxyPassword;
+ }
+
+ /**
+ * Initializes this instance. Installs itself as proxy handler on the
+ * session.
+ *
+ * @param session
+ * to initialize for
+ */
+ protected void init(ClientSession session) {
+ remainingProxyProtocolTime = session.getLongProperty(
+ StatefulProxyConnector.TIMEOUT_PROPERTY,
+ DEFAULT_PROXY_TIMEOUT_MILLIS);
+ if (remainingProxyProtocolTime <= 0L) {
+ remainingProxyProtocolTime = DEFAULT_PROXY_TIMEOUT_MILLIS;
+ }
+ if (session instanceof JGitClientSession) {
+ JGitClientSession s = (JGitClientSession) session;
+ unregister.set(() -> s.setProxyHandler(null));
+ s.setProxyHandler(this);
+ } else {
+ // Internal error, no translation
+ throw new IllegalStateException(
+ "Not a JGit session: " + session.getClass().getName()); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * Obtains the timeout for the whole rest of the proxy connection protocol.
+ *
+ * @return the timeout in milliseconds, always > 0L
+ */
+ protected long getTimeout() {
+ long last = lastProxyOperationTime;
+ long now = System.nanoTime();
+ lastProxyOperationTime = now;
+ long remaining = remainingProxyProtocolTime;
+ if (last != 0L) {
+ long elapsed = now - last;
+ remaining -= elapsed;
+ if (remaining < 0L) {
+ remaining = 10L; // Give it grace period.
+ }
+ }
+ remainingProxyProtocolTime = remaining;
+ return remaining;
+ }
+
+ /**
+ * Adjusts the timeout calculation to not account of elapsed time since the
+ * last time the timeout was gotten. Can be used for instance to ignore time
+ * spent in user dialogs be counted against the overall proxy connection
+ * protocol timeout.
+ */
+ protected void adjustTimeout() {
+ lastProxyOperationTime = System.nanoTime();
+ }
+
+ /**
+ * Sets the "done" flag.
+ *
+ * @param success
+ * whether the connector terminated successfully.
+ * @throws Exception
+ * if starting ssh fails
+ */
+ protected void setDone(boolean success) throws Exception {
+ List<Callable<Void>> buffered;
+ Runnable unset = unregister.getAndSet(null);
+ if (unset != null) {
+ unset.run();
+ }
+ synchronized (lock) {
+ done = true;
+ buffered = bufferedCommands;
+ bufferedCommands = null;
+ }
+ if (success && buffered != null) {
+ for (Callable<Void> starter : buffered) {
+ starter.call();
+ }
+ }
+ }
+
+ @Override
+ public void runWhenDone(Callable<Void> starter) throws Exception {
+ synchronized (lock) {
+ if (!done) {
+ bufferedCommands.add(starter);
+ return;
+ }
+ }
+ starter.call();
+ }
+
+ /**
+ * Clears the proxy password.
+ */
+ protected void clearPassword() {
+ Arrays.fill(proxyPassword, '\000');
+ proxyPassword = new char[0];
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/AuthenticationChallenge.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/AuthenticationChallenge.java
new file mode 100644
index 0000000000..4a6572d45b
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/AuthenticationChallenge.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.internal.transport.sshd.proxy;
+
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.eclipse.jgit.annotations.NonNull;
+
+/**
+ * A simple representation of an authentication challenge as sent in a
+ * "WWW-Authenticate" or "Proxy-Authenticate" header. Such challenges start with
+ * a mechanism name, followed either by one single token, or by a list of
+ * key=value pairs.
+ *
+ * @see <a href="https://tools.ietf.org/html/rfc7235#section-2.1">RFC 7235, sec.
+ * 2.1</a>
+ */
+public class AuthenticationChallenge {
+
+ private final String mechanism;
+
+ private String token;
+
+ private Map<String, String> arguments;
+
+ /**
+ * Create a new {@link AuthenticationChallenge} with the given mechanism.
+ *
+ * @param mechanism
+ * for the challenge
+ */
+ public AuthenticationChallenge(String mechanism) {
+ this.mechanism = mechanism;
+ }
+
+ /**
+ * Retrieves the authentication mechanism specified by this challenge, for
+ * instance "Basic".
+ *
+ * @return the mechanism name
+ */
+ public String getMechanism() {
+ return mechanism;
+ }
+
+ /**
+ * Retrieves the token of the challenge, if any.
+ *
+ * @return the token, or {@code null} if there is none.
+ */
+ public String getToken() {
+ return token;
+ }
+
+ /**
+ * Retrieves the arguments of the challenge.
+ *
+ * @return a possibly empty map of the key=value arguments of the challenge
+ */
+ @NonNull
+ public Map<String, String> getArguments() {
+ return arguments == null ? Collections.emptyMap() : arguments;
+ }
+
+ void addArgument(String key, String value) {
+ if (arguments == null) {
+ arguments = new LinkedHashMap<>();
+ }
+ arguments.put(key, value);
+ }
+
+ void setToken(String token) {
+ this.token = token;
+ }
+
+ @Override
+ public String toString() {
+ return "AuthenticationChallenge[" + mechanism + ',' + token + ',' //$NON-NLS-1$
+ + (arguments == null ? "<none>" : arguments.toString()) + ']'; //$NON-NLS-1$
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/HttpClientConnector.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/HttpClientConnector.java
new file mode 100644
index 0000000000..46cdd52f5f
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/HttpClientConnector.java
@@ -0,0 +1,403 @@
+/*
+ * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.internal.transport.sshd.proxy;
+
+import static java.text.MessageFormat.format;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.InetSocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.sshd.client.session.ClientSession;
+import org.apache.sshd.common.io.IoSession;
+import org.apache.sshd.common.util.Readable;
+import org.apache.sshd.common.util.buffer.Buffer;
+import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.internal.transport.sshd.GssApiMechanisms;
+import org.eclipse.jgit.internal.transport.sshd.SshdText;
+import org.eclipse.jgit.internal.transport.sshd.auth.AuthenticationHandler;
+import org.eclipse.jgit.internal.transport.sshd.auth.BasicAuthentication;
+import org.eclipse.jgit.internal.transport.sshd.auth.GssApiAuthentication;
+import org.eclipse.jgit.util.Base64;
+import org.ietf.jgss.GSSContext;
+
+/**
+ * Simple HTTP proxy connector using Basic Authentication.
+ */
+public class HttpClientConnector extends AbstractClientProxyConnector {
+
+ private static final String HTTP_HEADER_PROXY_AUTHENTICATION = "Proxy-Authentication:"; //$NON-NLS-1$
+
+ private static final String HTTP_HEADER_PROXY_AUTHORIZATION = "Proxy-Authorization:"; //$NON-NLS-1$
+
+ private HttpAuthenticationHandler basic;
+
+ private HttpAuthenticationHandler negotiate;
+
+ private List<HttpAuthenticationHandler> availableAuthentications;
+
+ private Iterator<HttpAuthenticationHandler> clientAuthentications;
+
+ private HttpAuthenticationHandler authenticator;
+
+ private boolean ongoing;
+
+ /**
+ * Creates a new {@link HttpClientConnector}. The connector supports
+ * anonymous proxy connections as well as Basic and Negotiate
+ * authentication.
+ *
+ * @param proxyAddress
+ * of the proxy server we're connecting to
+ * @param remoteAddress
+ * of the target server to connect to
+ */
+ public HttpClientConnector(@NonNull InetSocketAddress proxyAddress,
+ @NonNull InetSocketAddress remoteAddress) {
+ this(proxyAddress, remoteAddress, null, null);
+ }
+
+ /**
+ * Creates a new {@link HttpClientConnector}. The connector supports
+ * anonymous proxy connections as well as Basic and Negotiate
+ * authentication. If a user name and password are given, the connector
+ * tries pre-emptive Basic authentication.
+ *
+ * @param proxyAddress
+ * of the proxy server we're connecting to
+ * @param remoteAddress
+ * of the target server to connect to
+ * @param proxyUser
+ * to authenticate at the proxy with
+ * @param proxyPassword
+ * to authenticate at the proxy with
+ */
+ public HttpClientConnector(@NonNull InetSocketAddress proxyAddress,
+ @NonNull InetSocketAddress remoteAddress, String proxyUser,
+ char[] proxyPassword) {
+ super(proxyAddress, remoteAddress, proxyUser, proxyPassword);
+ basic = new HttpBasicAuthentication();
+ negotiate = new NegotiateAuthentication();
+ availableAuthentications = new ArrayList<>(2);
+ availableAuthentications.add(negotiate);
+ availableAuthentications.add(basic);
+ clientAuthentications = availableAuthentications.iterator();
+ }
+
+ private void close() {
+ HttpAuthenticationHandler current = authenticator;
+ authenticator = null;
+ if (current != null) {
+ current.close();
+ }
+ }
+
+ @Override
+ public void sendClientProxyMetadata(ClientSession sshSession)
+ throws Exception {
+ init(sshSession);
+ IoSession session = sshSession.getIoSession();
+ session.addCloseFutureListener(f -> close());
+ StringBuilder msg = connect();
+ if (proxyUser != null && !proxyUser.isEmpty()
+ || proxyPassword != null && proxyPassword.length > 0) {
+ authenticator = basic;
+ basic.setParams(null);
+ basic.start();
+ msg = authenticate(msg, basic.getToken());
+ clearPassword();
+ proxyUser = null;
+ }
+ ongoing = true;
+ try {
+ send(msg, session);
+ } catch (Exception e) {
+ ongoing = false;
+ throw e;
+ }
+ }
+
+ private void send(StringBuilder msg, IoSession session) throws Exception {
+ byte[] data = eol(msg).toString().getBytes(StandardCharsets.US_ASCII);
+ Buffer buffer = new ByteArrayBuffer(data.length, false);
+ buffer.putRawBytes(data);
+ session.writePacket(buffer).verify(getTimeout());
+ }
+
+ private StringBuilder connect() {
+ StringBuilder msg = new StringBuilder();
+ // Persistent connections are the default in HTTP 1.1 (see RFC 2616),
+ // but let's be explicit.
+ return msg.append(format(
+ "CONNECT {0}:{1} HTTP/1.1\r\nProxy-Connection: keep-alive\r\nConnection: keep-alive\r\nHost: {0}:{1}\r\n", //$NON-NLS-1$
+ remoteAddress.getHostString(),
+ Integer.toString(remoteAddress.getPort())));
+ }
+
+ private StringBuilder authenticate(StringBuilder msg, String token) {
+ msg.append(HTTP_HEADER_PROXY_AUTHORIZATION).append(' ').append(token);
+ return eol(msg);
+ }
+
+ private StringBuilder eol(StringBuilder msg) {
+ return msg.append('\r').append('\n');
+ }
+
+ @Override
+ public void messageReceived(IoSession session, Readable buffer)
+ throws Exception {
+ try {
+ int length = buffer.available();
+ byte[] data = new byte[length];
+ buffer.getRawBytes(data, 0, length);
+ String[] reply = new String(data, StandardCharsets.US_ASCII)
+ .split("\r\n"); //$NON-NLS-1$
+ handleMessage(session, Arrays.asList(reply));
+ } catch (Exception e) {
+ if (authenticator != null) {
+ authenticator.close();
+ authenticator = null;
+ }
+ ongoing = false;
+ try {
+ setDone(false);
+ } catch (Exception inner) {
+ e.addSuppressed(inner);
+ }
+ throw e;
+ }
+ }
+
+ private void handleMessage(IoSession session, List<String> reply)
+ throws Exception {
+ if (reply.isEmpty() || reply.get(0).isEmpty()) {
+ throw new IOException(
+ format(SshdText.get().proxyHttpUnexpectedReply,
+ proxyAddress, "<empty>")); //$NON-NLS-1$
+ }
+ try {
+ StatusLine status = HttpParser.parseStatusLine(reply.get(0));
+ if (!ongoing) {
+ throw new IOException(format(
+ SshdText.get().proxyHttpUnexpectedReply, proxyAddress,
+ Integer.toString(status.getResultCode()),
+ status.getReason()));
+ }
+ switch (status.getResultCode()) {
+ case HttpURLConnection.HTTP_OK:
+ if (authenticator != null) {
+ authenticator.close();
+ }
+ authenticator = null;
+ ongoing = false;
+ setDone(true);
+ break;
+ case HttpURLConnection.HTTP_PROXY_AUTH:
+ List<AuthenticationChallenge> challenges = HttpParser
+ .getAuthenticationHeaders(reply,
+ HTTP_HEADER_PROXY_AUTHENTICATION);
+ authenticator = selectProtocol(challenges, authenticator);
+ if (authenticator == null) {
+ throw new IOException(
+ format(SshdText.get().proxyCannotAuthenticate,
+ proxyAddress));
+ }
+ String token = authenticator.getToken();
+ if (token == null) {
+ throw new IOException(
+ format(SshdText.get().proxyCannotAuthenticate,
+ proxyAddress));
+ }
+ send(authenticate(connect(), token), session);
+ break;
+ default:
+ throw new IOException(format(SshdText.get().proxyHttpFailure,
+ proxyAddress, Integer.toString(status.getResultCode()),
+ status.getReason()));
+ }
+ } catch (HttpParser.ParseException e) {
+ throw new IOException(
+ format(SshdText.get().proxyHttpUnexpectedReply,
+ proxyAddress, reply.get(0)));
+ }
+ }
+
+ private HttpAuthenticationHandler selectProtocol(
+ List<AuthenticationChallenge> challenges,
+ HttpAuthenticationHandler current) throws Exception {
+ if (current != null && !current.isDone()) {
+ AuthenticationChallenge challenge = getByName(challenges,
+ current.getName());
+ if (challenge != null) {
+ current.setParams(challenge);
+ current.process();
+ return current;
+ }
+ }
+ if (current != null) {
+ current.close();
+ }
+ while (clientAuthentications.hasNext()) {
+ HttpAuthenticationHandler next = clientAuthentications.next();
+ if (!next.isDone()) {
+ AuthenticationChallenge challenge = getByName(challenges,
+ next.getName());
+ if (challenge != null) {
+ next.setParams(challenge);
+ next.start();
+ return next;
+ }
+ }
+ }
+ return null;
+ }
+
+ private AuthenticationChallenge getByName(
+ List<AuthenticationChallenge> challenges,
+ String name) {
+ return challenges.stream()
+ .filter(c -> c.getMechanism().equalsIgnoreCase(name))
+ .findFirst().orElse(null);
+ }
+
+ private interface HttpAuthenticationHandler
+ extends AuthenticationHandler<AuthenticationChallenge, String> {
+
+ public String getName();
+ }
+
+ /**
+ * @see <a href="https://tools.ietf.org/html/rfc7617">RFC 7617</a>
+ */
+ private class HttpBasicAuthentication
+ extends BasicAuthentication<AuthenticationChallenge, String>
+ implements HttpAuthenticationHandler {
+
+ private boolean asked;
+
+ public HttpBasicAuthentication() {
+ super(proxyAddress, proxyUser, proxyPassword);
+ }
+
+ @Override
+ public String getName() {
+ return "Basic"; //$NON-NLS-1$
+ }
+
+ @Override
+ protected void askCredentials() {
+ // We ask only once.
+ if (asked) {
+ throw new IllegalStateException(
+ "Basic auth: already asked user for password"); //$NON-NLS-1$
+ }
+ asked = true;
+ super.askCredentials();
+ done = true;
+ }
+
+ @Override
+ public String getToken() throws Exception {
+ if (user.indexOf(':') >= 0) {
+ throw new IOException(format(
+ SshdText.get().proxyHttpInvalidUserName, proxy, user));
+ }
+ byte[] rawUser = user.getBytes(StandardCharsets.UTF_8);
+ byte[] toEncode = new byte[rawUser.length + 1 + password.length];
+ System.arraycopy(rawUser, 0, toEncode, 0, rawUser.length);
+ toEncode[rawUser.length] = ':';
+ System.arraycopy(password, 0, toEncode, rawUser.length + 1,
+ password.length);
+ Arrays.fill(password, (byte) 0);
+ String result = Base64.encodeBytes(toEncode);
+ Arrays.fill(toEncode, (byte) 0);
+ return getName() + ' ' + result;
+ }
+
+ }
+
+ /**
+ * @see <a href="https://tools.ietf.org/html/rfc4559">RFC 4559</a>
+ */
+ private class NegotiateAuthentication
+ extends GssApiAuthentication<AuthenticationChallenge, String>
+ implements HttpAuthenticationHandler {
+
+ public NegotiateAuthentication() {
+ super(proxyAddress);
+ }
+
+ @Override
+ public String getName() {
+ return "Negotiate"; //$NON-NLS-1$
+ }
+
+ @Override
+ public String getToken() throws Exception {
+ return getName() + ' ' + Base64.encodeBytes(token);
+ }
+
+ @Override
+ protected GSSContext createContext() throws Exception {
+ return GssApiMechanisms.createContext(GssApiMechanisms.SPNEGO,
+ GssApiMechanisms.getCanonicalName(proxyAddress));
+ }
+
+ @Override
+ protected byte[] extractToken(AuthenticationChallenge input)
+ throws Exception {
+ String received = input.getToken();
+ if (received == null) {
+ return new byte[0];
+ }
+ return Base64.decode(received);
+ }
+
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/HttpParser.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/HttpParser.java
new file mode 100644
index 0000000000..b9b32b1300
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/HttpParser.java
@@ -0,0 +1,346 @@
+/*
+ * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.internal.transport.sshd.proxy;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * A basic parser for HTTP response headers. Handles status lines and
+ * authentication headers (WWW-Authenticate, Proxy-Authenticate).
+ *
+ * @see <a href="https://tools.ietf.org/html/rfc7230">RFC 7230</a>
+ * @see <a href="https://tools.ietf.org/html/rfc7235">RFC 7235</a>
+ */
+public final class HttpParser {
+
+ /**
+ * An exception indicating some problem parsing HTPP headers.
+ */
+ public static class ParseException extends Exception {
+
+ private static final long serialVersionUID = -1634090143702048640L;
+
+ }
+
+ private HttpParser() {
+ // No instantiation
+ }
+
+ /**
+ * Parse a HTTP response status line.
+ *
+ * @param line
+ * to parse
+ * @return the {@link StatusLine}
+ * @throws ParseException
+ * if the line cannot be parsed or has the wrong HTTP version
+ */
+ public static StatusLine parseStatusLine(String line)
+ throws ParseException {
+ // Format is HTTP/<version> Code Reason
+ int firstBlank = line.indexOf(' ');
+ if (firstBlank < 0) {
+ throw new ParseException();
+ }
+ int secondBlank = line.indexOf(' ', firstBlank + 1);
+ if (secondBlank < 0) {
+ // Accept the line even if the (according to RFC 2616 mandatory)
+ // reason is missing.
+ secondBlank = line.length();
+ }
+ int resultCode;
+ try {
+ resultCode = Integer.parseUnsignedInt(
+ line.substring(firstBlank + 1, secondBlank));
+ } catch (NumberFormatException e) {
+ throw new ParseException();
+ }
+ // Again, accept even if the reason is missing
+ String reason = ""; //$NON-NLS-1$
+ if (secondBlank < line.length()) {
+ reason = line.substring(secondBlank + 1);
+ }
+ return new StatusLine(line.substring(0, firstBlank), resultCode,
+ reason);
+ }
+
+ /**
+ * Extract the authentication headers from the header lines. It is assumed
+ * that the first element in {@code reply} is the raw status line as
+ * received from the server. It is skipped. Line processing stops on the
+ * first empty line thereafter.
+ *
+ * @param reply
+ * The complete (header) lines of the HTTP response
+ * @param authenticationHeader
+ * to look for (including the terminating ':'!)
+ * @return a list of {@link AuthenticationChallenge}s found.
+ */
+ public static List<AuthenticationChallenge> getAuthenticationHeaders(
+ List<String> reply, String authenticationHeader) {
+ List<AuthenticationChallenge> challenges = new ArrayList<>();
+ Iterator<String> lines = reply.iterator();
+ // We know we have at least one line. Skip the response line.
+ lines.next();
+ StringBuilder value = null;
+ while (lines.hasNext()) {
+ String line = lines.next();
+ if (line.isEmpty()) {
+ break;
+ }
+ if (Character.isWhitespace(line.charAt(0))) {
+ // Continuation line.
+ if (value == null) {
+ // Skip if we have no current value
+ continue;
+ }
+ // Skip leading whitespace
+ int i = skipWhiteSpace(line, 1);
+ value.append(' ').append(line, i, line.length());
+ continue;
+ }
+ if (value != null) {
+ parseChallenges(challenges, value.toString());
+ value = null;
+ }
+ int firstColon = line.indexOf(':');
+ if (firstColon > 0 && authenticationHeader
+ .equalsIgnoreCase(line.substring(0, firstColon + 1))) {
+ value = new StringBuilder(line.substring(firstColon + 1));
+ }
+ }
+ if (value != null) {
+ parseChallenges(challenges, value.toString());
+ }
+ return challenges;
+ }
+
+ private static void parseChallenges(
+ List<AuthenticationChallenge> challenges,
+ String header) {
+ // Comma-separated list of challenges, each itself a scheme name
+ // followed optionally by either: a comma-separated list of key=value
+ // pairs, where the value may be a quoted string with backslash escapes,
+ // or a single token value, which itself may end in zero or more '='
+ // characters. Ugh.
+ int length = header.length();
+ for (int i = 0; i < length;) {
+ int start = skipWhiteSpace(header, i);
+ int end = scanToken(header, start);
+ if (end <= start) {
+ break;
+ }
+ AuthenticationChallenge challenge = new AuthenticationChallenge(
+ header.substring(start, end));
+ challenges.add(challenge);
+ i = parseChallenge(challenge, header, end);
+ }
+ }
+
+ private static int parseChallenge(AuthenticationChallenge challenge,
+ String header, int from) {
+ int length = header.length();
+ boolean first = true;
+ for (int start = from; start <= length; first = false) {
+ // Now we have either a single token, which may end in zero or more
+ // equal signs, or a comma-separated list of key=value pairs (with
+ // optional legacy whitespace around the equals sign), where the
+ // value can be either a token or a quoted string.
+ start = skipWhiteSpace(header, start);
+ int end = scanToken(header, start);
+ if (end == start) {
+ // Nothing found. Either at end or on a comma.
+ if (start < header.length() && header.charAt(start) == ',') {
+ return start + 1;
+ }
+ return start;
+ }
+ int next = skipWhiteSpace(header, end);
+ // Comma, or equals sign, or end of string
+ if (next >= length || header.charAt(next) != '=') {
+ if (first) {
+ // It must be a token
+ challenge.setToken(header.substring(start, end));
+ if (next < length && header.charAt(next) == ',') {
+ next++;
+ }
+ return next;
+ } else {
+ // This token must be the name of the next authentication
+ // scheme.
+ return start;
+ }
+ }
+ int nextStart = skipWhiteSpace(header, next + 1);
+ if (nextStart >= length) {
+ if (next == end) {
+ // '=' immediately after the key, no value: key must be the
+ // token, and the equals sign is part of the token
+ challenge.setToken(header.substring(start, end + 1));
+ } else {
+ // Key without value...
+ challenge.addArgument(header.substring(start, end), null);
+ }
+ return nextStart;
+ }
+ if (nextStart == end + 1 && header.charAt(nextStart) == '=') {
+ // More than one equals sign: must be the single token.
+ end = nextStart + 1;
+ while (end < length && header.charAt(end) == '=') {
+ end++;
+ }
+ challenge.setToken(header.substring(start, end));
+ end = skipWhiteSpace(header, end);
+ if (end < length && header.charAt(end) == ',') {
+ end++;
+ }
+ return end;
+ }
+ if (header.charAt(nextStart) == ',') {
+ if (next == end) {
+ // '=' immediately after the key, no value: key must be the
+ // token, and the equals sign is part of the token
+ challenge.setToken(header.substring(start, end + 1));
+ return nextStart + 1;
+ } else {
+ // Key without value...
+ challenge.addArgument(header.substring(start, end), null);
+ start = nextStart + 1;
+ }
+ } else {
+ if (header.charAt(nextStart) == '"') {
+ int nextEnd[] = { nextStart + 1 };
+ String value = scanQuotedString(header, nextStart + 1,
+ nextEnd);
+ challenge.addArgument(header.substring(start, end), value);
+ start = nextEnd[0];
+ } else {
+ int nextEnd = scanToken(header, nextStart);
+ challenge.addArgument(header.substring(start, end),
+ header.substring(nextStart, nextEnd));
+ start = nextEnd;
+ }
+ start = skipWhiteSpace(header, start);
+ if (start < length && header.charAt(start) == ',') {
+ start++;
+ }
+ }
+ }
+ return length;
+ }
+
+ private static int skipWhiteSpace(String header, int i) {
+ int length = header.length();
+ while (i < length && Character.isWhitespace(header.charAt(i))) {
+ i++;
+ }
+ return i;
+ }
+
+ private static int scanToken(String header, int from) {
+ int length = header.length();
+ int i = from;
+ while (i < length) {
+ char c = header.charAt(i);
+ switch (c) {
+ case '!':
+ case '#':
+ case '$':
+ case '%':
+ case '&':
+ case '\'':
+ case '*':
+ case '+':
+ case '-':
+ case '.':
+ case '^':
+ case '_':
+ case '`':
+ case '|':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ i++;
+ break;
+ default:
+ if (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z') {
+ i++;
+ break;
+ }
+ return i;
+ }
+ }
+ return i;
+ }
+
+ private static String scanQuotedString(String header, int from, int[] to) {
+ StringBuilder result = new StringBuilder();
+ int length = header.length();
+ boolean quoted = false;
+ int i = from;
+ while (i < length) {
+ char c = header.charAt(i++);
+ if (quoted) {
+ result.append(c);
+ quoted = false;
+ } else if (c == '\\') {
+ quoted = true;
+ } else if (c == '"') {
+ break;
+ } else {
+ result.append(c);
+ }
+ }
+ to[0] = i;
+ return result.toString();
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/Socks5ClientConnector.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/Socks5ClientConnector.java
new file mode 100644
index 0000000000..1844fdc794
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/Socks5ClientConnector.java
@@ -0,0 +1,642 @@
+/*
+ * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.internal.transport.sshd.proxy;
+
+import static java.text.MessageFormat.format;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.nio.charset.StandardCharsets;
+
+import org.apache.sshd.client.session.ClientSession;
+import org.apache.sshd.common.io.IoSession;
+import org.apache.sshd.common.util.Readable;
+import org.apache.sshd.common.util.buffer.Buffer;
+import org.apache.sshd.common.util.buffer.BufferUtils;
+import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.internal.transport.sshd.GssApiMechanisms;
+import org.eclipse.jgit.internal.transport.sshd.SshdText;
+import org.eclipse.jgit.internal.transport.sshd.auth.AuthenticationHandler;
+import org.eclipse.jgit.internal.transport.sshd.auth.BasicAuthentication;
+import org.eclipse.jgit.internal.transport.sshd.auth.GssApiAuthentication;
+import org.eclipse.jgit.transport.SshConstants;
+import org.ietf.jgss.GSSContext;
+
+/**
+ * A {@link AbstractClientProxyConnector} to connect through a SOCKS5 proxy.
+ *
+ * @see <a href="https://tools.ietf.org/html/rfc1928">RFC 1928</a>
+ */
+public class Socks5ClientConnector extends AbstractClientProxyConnector {
+
+ // private static final byte SOCKS_VERSION_4 = 4;
+ private static final byte SOCKS_VERSION_5 = 5;
+
+ private static final byte SOCKS_CMD_CONNECT = 1;
+ // private static final byte SOCKS5_CMD_BIND = 2;
+ // private static final byte SOCKS5_CMD_UDP_ASSOCIATE = 3;
+
+ // Address types
+
+ private static final byte SOCKS_ADDRESS_IPv4 = 1;
+
+ private static final byte SOCKS_ADDRESS_FQDN = 3;
+
+ private static final byte SOCKS_ADDRESS_IPv6 = 4;
+
+ // Reply codes
+
+ private static final byte SOCKS_REPLY_SUCCESS = 0;
+
+ private static final byte SOCKS_REPLY_FAILURE = 1;
+
+ private static final byte SOCKS_REPLY_FORBIDDEN = 2;
+
+ private static final byte SOCKS_REPLY_NETWORK_UNREACHABLE = 3;
+
+ private static final byte SOCKS_REPLY_HOST_UNREACHABLE = 4;
+
+ private static final byte SOCKS_REPLY_CONNECTION_REFUSED = 5;
+
+ private static final byte SOCKS_REPLY_TTL_EXPIRED = 6;
+
+ private static final byte SOCKS_REPLY_COMMAND_UNSUPPORTED = 7;
+
+ private static final byte SOCKS_REPLY_ADDRESS_UNSUPPORTED = 8;
+
+ /**
+ * Authentication methods for SOCKS5.
+ *
+ * @see <a href=
+ * "https://www.iana.org/assignments/socks-methods/socks-methods.xhtml">SOCKS
+ * Methods, IANA.org</a>
+ */
+ private enum SocksAuthenticationMethod {
+
+ ANONYMOUS(0),
+ GSSAPI(1),
+ PASSWORD(2),
+ // CHALLENGE_HANDSHAKE(3),
+ // CHALLENGE_RESPONSE(5),
+ // SSL(6),
+ // NDS(7),
+ // MULTI_AUTH(8),
+ // JSON(9),
+ NONE_ACCEPTABLE(0xFF);
+
+ private byte value;
+
+ SocksAuthenticationMethod(int value) {
+ this.value = (byte) value;
+ }
+
+ public byte getValue() {
+ return value;
+ }
+ }
+
+ private enum ProtocolState {
+ NONE,
+
+ INIT {
+ @Override
+ public void handleMessage(Socks5ClientConnector connector,
+ IoSession session, Buffer data) throws Exception {
+ connector.versionCheck(data.getByte());
+ SocksAuthenticationMethod authMethod = connector.getAuthMethod(
+ data.getByte());
+ switch (authMethod) {
+ case ANONYMOUS:
+ connector.sendConnectInfo(session);
+ break;
+ case PASSWORD:
+ connector.doPasswordAuth(session);
+ break;
+ case GSSAPI:
+ connector.doGssApiAuth(session);
+ break;
+ default:
+ throw new IOException(
+ format(SshdText.get().proxyCannotAuthenticate,
+ connector.proxyAddress));
+ }
+ }
+ },
+
+ AUTHENTICATING {
+ @Override
+ public void handleMessage(Socks5ClientConnector connector,
+ IoSession session, Buffer data) throws Exception {
+ connector.authStep(session, data);
+ }
+ },
+
+ CONNECTING {
+ @Override
+ public void handleMessage(Socks5ClientConnector connector,
+ IoSession session, Buffer data) throws Exception {
+ // Special case: when GSS-API authentication completes, the
+ // client moves into CONNECTING as soon as the GSS context is
+ // established and sends the connect request. This is per RFC
+ // 1961. But for the server, RFC 1961 says it _should_ send an
+ // empty token even if none generated when its server side
+ // context is established. That means we may actually get an
+ // empty token here. That message is 4 bytes long (and has
+ // content 0x01, 0x01, 0x00, 0x00). We simply skip this message
+ // if we get it here. If the server for whatever reason sends
+ // back a "GSS failed" message (it shouldn't, at this point)
+ // it will be two bytes 0x01 0xFF, which will fail the version
+ // check.
+ if (data.available() != 4) {
+ connector.versionCheck(data.getByte());
+ connector.establishConnection(data);
+ }
+ }
+ },
+
+ CONNECTED,
+
+ FAILED;
+
+ public void handleMessage(Socks5ClientConnector connector,
+ @SuppressWarnings("unused") IoSession session, Buffer data)
+ throws Exception {
+ throw new IOException(
+ format(SshdText.get().proxySocksUnexpectedMessage,
+ connector.proxyAddress, this,
+ BufferUtils.toHex(data.array())));
+ }
+ }
+
+ private ProtocolState state;
+
+ private AuthenticationHandler<Buffer, Buffer> authenticator;
+
+ private GSSContext context;
+
+ private byte[] authenticationProposals;
+
+ /**
+ * Creates a new {@link Socks5ClientConnector}. The connector supports
+ * anonymous connections as well as username-password or Kerberos5 (GSS-API)
+ * authentication.
+ *
+ * @param proxyAddress
+ * of the proxy server we're connecting to
+ * @param remoteAddress
+ * of the target server to connect to
+ */
+ public Socks5ClientConnector(@NonNull InetSocketAddress proxyAddress,
+ @NonNull InetSocketAddress remoteAddress) {
+ this(proxyAddress, remoteAddress, null, null);
+ }
+
+ /**
+ * Creates a new {@link Socks5ClientConnector}. The connector supports
+ * anonymous connections as well as username-password or Kerberos5 (GSS-API)
+ * authentication.
+ *
+ * @param proxyAddress
+ * of the proxy server we're connecting to
+ * @param remoteAddress
+ * of the target server to connect to
+ * @param proxyUser
+ * to authenticate at the proxy with
+ * @param proxyPassword
+ * to authenticate at the proxy with
+ */
+ public Socks5ClientConnector(@NonNull InetSocketAddress proxyAddress,
+ @NonNull InetSocketAddress remoteAddress,
+ String proxyUser, char[] proxyPassword) {
+ super(proxyAddress, remoteAddress, proxyUser, proxyPassword);
+ this.state = ProtocolState.NONE;
+ }
+
+ @Override
+ public void sendClientProxyMetadata(ClientSession sshSession)
+ throws Exception {
+ init(sshSession);
+ IoSession session = sshSession.getIoSession();
+ // Send the initial request
+ Buffer buffer = new ByteArrayBuffer(5, false);
+ buffer.putByte(SOCKS_VERSION_5);
+ context = getGSSContext(remoteAddress);
+ authenticationProposals = getAuthenticationProposals();
+ buffer.putByte((byte) authenticationProposals.length);
+ buffer.putRawBytes(authenticationProposals);
+ state = ProtocolState.INIT;
+ session.writePacket(buffer).verify(getTimeout());
+ }
+
+ private byte[] getAuthenticationProposals() {
+ byte[] proposals = new byte[3];
+ int i = 0;
+ proposals[i++] = SocksAuthenticationMethod.ANONYMOUS.getValue();
+ proposals[i++] = SocksAuthenticationMethod.PASSWORD.getValue();
+ if (context != null) {
+ proposals[i++] = SocksAuthenticationMethod.GSSAPI.getValue();
+ }
+ if (i == proposals.length) {
+ return proposals;
+ } else {
+ byte[] result = new byte[i];
+ System.arraycopy(proposals, 0, result, 0, i);
+ return result;
+ }
+ }
+
+ private void sendConnectInfo(IoSession session) throws Exception {
+ GssApiMechanisms.closeContextSilently(context);
+
+ byte[] rawAddress = getRawAddress(remoteAddress);
+ byte[] remoteName = null;
+ byte type;
+ int length = 0;
+ if (rawAddress == null) {
+ remoteName = remoteAddress.getHostString()
+ .getBytes(StandardCharsets.US_ASCII);
+ if (remoteName == null || remoteName.length == 0) {
+ throw new IOException(
+ format(SshdText.get().proxySocksNoRemoteHostName,
+ remoteAddress));
+ } else if (remoteName.length > 255) {
+ // Should not occur; host names must not be longer than 255
+ // US_ASCII characters. Internal error, no translation.
+ throw new IOException(format(
+ "Proxy host name too long for SOCKS (at most 255 characters): {0}", //$NON-NLS-1$
+ remoteAddress.getHostString()));
+ }
+ type = SOCKS_ADDRESS_FQDN;
+ length = remoteName.length + 1;
+ } else {
+ length = rawAddress.length;
+ type = length == 4 ? SOCKS_ADDRESS_IPv4 : SOCKS_ADDRESS_IPv6;
+ }
+ Buffer buffer = new ByteArrayBuffer(4 + length + 2, false);
+ buffer.putByte(SOCKS_VERSION_5);
+ buffer.putByte(SOCKS_CMD_CONNECT);
+ buffer.putByte((byte) 0); // Reserved
+ buffer.putByte(type);
+ if (remoteName != null) {
+ buffer.putByte((byte) remoteName.length);
+ buffer.putRawBytes(remoteName);
+ } else {
+ buffer.putRawBytes(rawAddress);
+ }
+ int port = remoteAddress.getPort();
+ if (port <= 0) {
+ port = SshConstants.SSH_DEFAULT_PORT;
+ }
+ buffer.putByte((byte) ((port >> 8) & 0xFF));
+ buffer.putByte((byte) (port & 0xFF));
+ state = ProtocolState.CONNECTING;
+ session.writePacket(buffer).verify(getTimeout());
+ }
+
+ private void doPasswordAuth(IoSession session) throws Exception {
+ GssApiMechanisms.closeContextSilently(context);
+ authenticator = new SocksBasicAuthentication();
+ session.addCloseFutureListener(f -> close());
+ startAuth(session);
+ }
+
+ private void doGssApiAuth(IoSession session) throws Exception {
+ authenticator = new SocksGssApiAuthentication();
+ session.addCloseFutureListener(f -> close());
+ startAuth(session);
+ }
+
+ private void close() {
+ AuthenticationHandler<?, ?> handler = authenticator;
+ authenticator = null;
+ if (handler != null) {
+ handler.close();
+ }
+ }
+
+ private void startAuth(IoSession session) throws Exception {
+ Buffer buffer = null;
+ try {
+ authenticator.setParams(null);
+ authenticator.start();
+ buffer = authenticator.getToken();
+ state = ProtocolState.AUTHENTICATING;
+ if (buffer == null) {
+ // Internal error; no translation
+ throw new IOException(
+ "No data for proxy authentication with " //$NON-NLS-1$
+ + proxyAddress);
+ }
+ session.writePacket(buffer).verify(getTimeout());
+ } finally {
+ if (buffer != null) {
+ buffer.clear(true);
+ }
+ }
+ }
+
+ private void authStep(IoSession session, Buffer input) throws Exception {
+ Buffer buffer = null;
+ try {
+ authenticator.setParams(input);
+ authenticator.process();
+ buffer = authenticator.getToken();
+ if (buffer != null) {
+ session.writePacket(buffer).verify(getTimeout());
+ }
+ } finally {
+ if (buffer != null) {
+ buffer.clear(true);
+ }
+ }
+ if (authenticator.isDone()) {
+ sendConnectInfo(session);
+ }
+ }
+
+ private void establishConnection(Buffer data) throws Exception {
+ byte reply = data.getByte();
+ switch (reply) {
+ case SOCKS_REPLY_SUCCESS:
+ state = ProtocolState.CONNECTED;
+ setDone(true);
+ return;
+ case SOCKS_REPLY_FAILURE:
+ throw new IOException(format(
+ SshdText.get().proxySocksFailureGeneral, proxyAddress));
+ case SOCKS_REPLY_FORBIDDEN:
+ throw new IOException(
+ format(SshdText.get().proxySocksFailureForbidden,
+ proxyAddress, remoteAddress));
+ case SOCKS_REPLY_NETWORK_UNREACHABLE:
+ throw new IOException(
+ format(SshdText.get().proxySocksFailureNetworkUnreachable,
+ proxyAddress, remoteAddress));
+ case SOCKS_REPLY_HOST_UNREACHABLE:
+ throw new IOException(
+ format(SshdText.get().proxySocksFailureHostUnreachable,
+ proxyAddress, remoteAddress));
+ case SOCKS_REPLY_CONNECTION_REFUSED:
+ throw new IOException(
+ format(SshdText.get().proxySocksFailureRefused,
+ proxyAddress, remoteAddress));
+ case SOCKS_REPLY_TTL_EXPIRED:
+ throw new IOException(
+ format(SshdText.get().proxySocksFailureTTL, proxyAddress));
+ case SOCKS_REPLY_COMMAND_UNSUPPORTED:
+ throw new IOException(
+ format(SshdText.get().proxySocksFailureUnsupportedCommand,
+ proxyAddress));
+ case SOCKS_REPLY_ADDRESS_UNSUPPORTED:
+ throw new IOException(
+ format(SshdText.get().proxySocksFailureUnsupportedAddress,
+ proxyAddress));
+ default:
+ throw new IOException(format(
+ SshdText.get().proxySocksFailureUnspecified, proxyAddress));
+ }
+ }
+
+ @Override
+ public void messageReceived(IoSession session, Readable buffer)
+ throws Exception {
+ try {
+ // Dispatch according to protocol state
+ ByteArrayBuffer data = new ByteArrayBuffer(buffer.available(),
+ false);
+ data.putBuffer(buffer);
+ data.compact();
+ state.handleMessage(this, session, data);
+ } catch (Exception e) {
+ state = ProtocolState.FAILED;
+ if (authenticator != null) {
+ authenticator.close();
+ authenticator = null;
+ }
+ try {
+ setDone(false);
+ } catch (Exception inner) {
+ e.addSuppressed(inner);
+ }
+ throw e;
+ }
+ }
+
+ private void versionCheck(byte version) throws Exception {
+ if (version != SOCKS_VERSION_5) {
+ throw new IOException(
+ format(SshdText.get().proxySocksUnexpectedVersion,
+ Integer.toString(version & 0xFF)));
+ }
+ }
+
+ private SocksAuthenticationMethod getAuthMethod(byte value) {
+ if (value != SocksAuthenticationMethod.NONE_ACCEPTABLE.getValue()) {
+ for (byte proposed : authenticationProposals) {
+ if (proposed == value) {
+ for (SocksAuthenticationMethod method : SocksAuthenticationMethod
+ .values()) {
+ if (method.getValue() == value) {
+ return method;
+ }
+ }
+ break;
+ }
+ }
+ }
+ return SocksAuthenticationMethod.NONE_ACCEPTABLE;
+ }
+
+ private static byte[] getRawAddress(@NonNull InetSocketAddress address) {
+ InetAddress ipAddress = GssApiMechanisms.resolve(address);
+ return ipAddress == null ? null : ipAddress.getAddress();
+ }
+
+ private static GSSContext getGSSContext(
+ @NonNull InetSocketAddress address) {
+ if (!GssApiMechanisms.getSupportedMechanisms()
+ .contains(GssApiMechanisms.KERBEROS_5)) {
+ return null;
+ }
+ return GssApiMechanisms.createContext(GssApiMechanisms.KERBEROS_5,
+ GssApiMechanisms.getCanonicalName(address));
+ }
+
+ /**
+ * @see <a href="https://tools.ietf.org/html/rfc1929">RFC 1929</a>
+ */
+ private class SocksBasicAuthentication
+ extends BasicAuthentication<Buffer, Buffer> {
+
+ private static final byte SOCKS_BASIC_PROTOCOL_VERSION = 1;
+
+ private static final byte SOCKS_BASIC_AUTH_SUCCESS = 0;
+
+ public SocksBasicAuthentication() {
+ super(proxyAddress, proxyUser, proxyPassword);
+ }
+
+ @Override
+ public void process() throws Exception {
+ // Retries impossible. RFC 1929 specifies that the server MUST
+ // close the connection if authentication is unsuccessful.
+ done = true;
+ if (params.getByte() != SOCKS_BASIC_PROTOCOL_VERSION
+ || params.getByte() != SOCKS_BASIC_AUTH_SUCCESS) {
+ throw new IOException(format(
+ SshdText.get().proxySocksAuthenticationFailed, proxy));
+ }
+ }
+
+ @Override
+ protected void askCredentials() {
+ super.askCredentials();
+ adjustTimeout();
+ }
+
+ @Override
+ public Buffer getToken() throws IOException {
+ if (done) {
+ return null;
+ }
+ try {
+ byte[] rawUser = user.getBytes(StandardCharsets.UTF_8);
+ if (rawUser.length > 255) {
+ throw new IOException(format(
+ SshdText.get().proxySocksUsernameTooLong, proxy,
+ Integer.toString(rawUser.length), user));
+ }
+
+ if (password.length > 255) {
+ throw new IOException(
+ format(SshdText.get().proxySocksPasswordTooLong,
+ proxy, Integer.toString(password.length)));
+ }
+ ByteArrayBuffer buffer = new ByteArrayBuffer(
+ 3 + rawUser.length + password.length, false);
+ buffer.putByte(SOCKS_BASIC_PROTOCOL_VERSION);
+ buffer.putByte((byte) rawUser.length);
+ buffer.putRawBytes(rawUser);
+ buffer.putByte((byte) password.length);
+ buffer.putRawBytes(password);
+ return buffer;
+ } finally {
+ clearPassword();
+ done = true;
+ }
+ }
+ }
+
+ /**
+ * @see <a href="https://tools.ietf.org/html/rfc1961">RFC 1961</a>
+ */
+ private class SocksGssApiAuthentication
+ extends GssApiAuthentication<Buffer, Buffer> {
+
+ private static final byte SOCKS5_GSSAPI_VERSION = 1;
+
+ private static final byte SOCKS5_GSSAPI_TOKEN = 1;
+
+ private static final int SOCKS5_GSSAPI_FAILURE = 0xFF;
+
+ public SocksGssApiAuthentication() {
+ super(proxyAddress);
+ }
+
+ @Override
+ protected GSSContext createContext() throws Exception {
+ return context;
+ }
+
+ @Override
+ public Buffer getToken() throws Exception {
+ if (token == null) {
+ return null;
+ }
+ Buffer buffer = new ByteArrayBuffer(4 + token.length, false);
+ buffer.putByte(SOCKS5_GSSAPI_VERSION);
+ buffer.putByte(SOCKS5_GSSAPI_TOKEN);
+ buffer.putByte((byte) ((token.length >> 8) & 0xFF));
+ buffer.putByte((byte) (token.length & 0xFF));
+ buffer.putRawBytes(token);
+ return buffer;
+ }
+
+ @Override
+ protected byte[] extractToken(Buffer input) throws Exception {
+ if (context == null) {
+ return null;
+ }
+ int version = input.getUByte();
+ if (version != SOCKS5_GSSAPI_VERSION) {
+ throw new IOException(
+ format(SshdText.get().proxySocksGssApiVersionMismatch,
+ remoteAddress, Integer.toString(version)));
+ }
+ int msgType = input.getUByte();
+ if (msgType == SOCKS5_GSSAPI_FAILURE) {
+ throw new IOException(format(
+ SshdText.get().proxySocksGssApiFailure, remoteAddress));
+ } else if (msgType != SOCKS5_GSSAPI_TOKEN) {
+ throw new IOException(format(
+ SshdText.get().proxySocksGssApiUnknownMessage,
+ remoteAddress, Integer.toHexString(msgType & 0xFF)));
+ }
+ if (input.available() >= 2) {
+ int length = (input.getUByte() << 8) + input.getUByte();
+ if (input.available() >= length) {
+ byte[] value = new byte[length];
+ if (length > 0) {
+ input.getRawBytes(value);
+ }
+ return value;
+ }
+ }
+ throw new IOException(
+ format(SshdText.get().proxySocksGssApiMessageTooShort,
+ remoteAddress));
+ }
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/StatefulProxyConnector.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/StatefulProxyConnector.java
new file mode 100644
index 0000000000..6bd836a587
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/StatefulProxyConnector.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.internal.transport.sshd.proxy;
+
+import java.util.concurrent.Callable;
+
+import org.apache.sshd.client.session.ClientProxyConnector;
+import org.apache.sshd.common.io.IoSession;
+import org.apache.sshd.common.util.Readable;
+
+/**
+ * Some proxy connections are stateful and require the exchange of multiple
+ * request-reply messages. The default {@link ClientProxyConnector} has only
+ * support for sending a message; replies get routed through the Ssh session,
+ * and don't get back to this proxy connector. Augment the interface so that the
+ * session can know when to route messages received to the proxy connector, and
+ * when to start handling them itself.
+ */
+public interface StatefulProxyConnector extends ClientProxyConnector {
+
+ /**
+ * A property key for a session property defining the timeout for setting up
+ * the proxy connection.
+ */
+ static final String TIMEOUT_PROPERTY = StatefulProxyConnector.class
+ .getName() + "-timeout"; //$NON-NLS-1$
+
+ /**
+ * Handle a received message.
+ *
+ * @param session
+ * to use for writing data
+ * @param buffer
+ * received data
+ * @throws Exception
+ * if data cannot be read, or the connection attempt fails
+ */
+ void messageReceived(IoSession session, Readable buffer) throws Exception;
+
+ /**
+ * Runs {@code command} once the proxy connection is established. May be
+ * called multiple times; commands are run sequentially. If the proxy
+ * connection is already established, {@code command} is executed directly
+ * synchronously.
+ *
+ * @param command
+ * operation to run
+ * @throws Exception
+ * if the operation is run synchronously and throws an exception
+ */
+ void runWhenDone(Callable<Void> command) throws Exception;
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/StatusLine.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/StatusLine.java
new file mode 100644
index 0000000000..7ff0183b22
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/StatusLine.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.internal.transport.sshd.proxy;
+
+/**
+ * A very simple representation of a HTTP status line.
+ */
+public class StatusLine {
+
+ private final String version;
+
+ private final int resultCode;
+
+ private final String reason;
+
+ /**
+ * Create a new {@link StatusLine} with the given response code and reason
+ * string.
+ *
+ * @param version
+ * the version string (normally "HTTP/1.1" or "HTTP/1.0")
+ * @param resultCode
+ * the HTTP response code (200, 401, etc.)
+ * @param reason
+ * the reason phrase for the code
+ */
+ public StatusLine(String version, int resultCode, String reason) {
+ this.version = version;
+ this.resultCode = resultCode;
+ this.reason = reason;
+ }
+
+ /**
+ * Retrieves the version string.
+ *
+ * @return the version string
+ */
+ public String getVersion() {
+ return version;
+ }
+
+ /**
+ * Retrieves the HTTP response code.
+ *
+ * @return the code
+ */
+ public int getResultCode() {
+ return resultCode;
+ }
+
+ /**
+ * Retrieves the HTTP reason phrase.
+ *
+ * @return the reason
+ */
+ public String getReason() {
+ return reason;
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/DefaultProxyDataFactory.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/DefaultProxyDataFactory.java
new file mode 100644
index 0000000000..97e0da0428
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/DefaultProxyDataFactory.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.transport.sshd;
+
+import java.net.InetSocketAddress;
+import java.net.Proxy;
+import java.net.ProxySelector;
+import java.net.SocketAddress;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.List;
+
+/**
+ * A default implementation of a {@link ProxyDataFactory} based on the standard
+ * {@link java.net.ProxySelector}.
+ *
+ * @since 5.2
+ */
+public class DefaultProxyDataFactory implements ProxyDataFactory {
+
+ @Override
+ public ProxyData get(InetSocketAddress remoteAddress) {
+ try {
+ List<Proxy> proxies = ProxySelector.getDefault()
+ .select(new URI(Proxy.Type.SOCKS.name(),
+ "//" + remoteAddress.getHostString(), null)); //$NON-NLS-1$
+ ProxyData data = getData(proxies, Proxy.Type.SOCKS);
+ if (data == null) {
+ proxies = ProxySelector.getDefault()
+ .select(new URI(Proxy.Type.HTTP.name(),
+ "//" + remoteAddress.getHostString(), //$NON-NLS-1$
+ null));
+ data = getData(proxies, Proxy.Type.HTTP);
+ }
+ return data;
+ } catch (URISyntaxException e) {
+ return null;
+ }
+ }
+
+ private ProxyData getData(List<Proxy> proxies, Proxy.Type type) {
+ Proxy proxy = proxies.stream().filter(p -> type == p.type()).findFirst()
+ .orElse(null);
+ if (proxy == null) {
+ return null;
+ }
+ SocketAddress address = proxy.address();
+ if (!(address instanceof InetSocketAddress)) {
+ return null;
+ }
+ switch (type) {
+ case HTTP:
+ return new ProxyData(proxy);
+ case SOCKS:
+ return new ProxyData(proxy);
+ default:
+ return null;
+ }
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/IdentityPasswordProvider.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/IdentityPasswordProvider.java
new file mode 100644
index 0000000000..2a5f2ff249
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/IdentityPasswordProvider.java
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.transport.sshd;
+
+import static java.text.MessageFormat.format;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CancellationException;
+
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.internal.transport.sshd.SshdText;
+import org.eclipse.jgit.transport.CredentialItem;
+import org.eclipse.jgit.transport.CredentialsProvider;
+import org.eclipse.jgit.transport.URIish;
+
+/**
+ * A {@link KeyPasswordProvider} based on a {@link CredentialsProvider}.
+ *
+ * @since 5.2
+ */
+public class IdentityPasswordProvider implements KeyPasswordProvider {
+
+ private CredentialsProvider provider;
+
+ /**
+ * The number of times to ask successively for a password for a given
+ * identity resource.
+ */
+ private int attempts = 1;
+
+ /**
+ * A simple state object for repeated attempts to get a password for a
+ * resource.
+ */
+ protected static class State {
+
+ private int count = 0;
+
+ private char[] password;
+
+ /**
+ * Obtains the current count. The initial count is zero.
+ *
+ * @return the count
+ */
+ public int getCount() {
+ return count;
+ }
+
+ /**
+ * Increments the current count. Should be called for each new attempt
+ * to get a password.
+ *
+ * @return the incremented count.
+ */
+ public int incCount() {
+ return ++count;
+ }
+
+ /**
+ * Remembers the password.
+ *
+ * @param password
+ * the password
+ */
+ public void setPassword(char[] password) {
+ if (this.password != null) {
+ Arrays.fill(this.password, '\000');
+ }
+ if (password != null) {
+ this.password = password.clone();
+ } else {
+ this.password = null;
+ }
+ }
+
+ /**
+ * Retrieves the password from the current attempt.
+ *
+ * @return the password, or {@code null} if none was obtained
+ */
+ public char[] getPassword() {
+ return password;
+ }
+ }
+
+ /**
+ * Counts per resource key.
+ */
+ private final Map<URIish, State> current = new HashMap<>();
+
+ /**
+ * Creates a new {@link IdentityPasswordProvider} to get the passphrase for
+ * an encrypted identity.
+ *
+ * @param provider
+ * to use
+ */
+ public IdentityPasswordProvider(CredentialsProvider provider) {
+ this.provider = provider;
+ }
+
+ @Override
+ public void setAttempts(int numberOfPasswordPrompts) {
+ if (numberOfPasswordPrompts <= 0) {
+ throw new IllegalArgumentException(
+ "Number of password prompts must be >= 1"); //$NON-NLS-1$
+ }
+ attempts = numberOfPasswordPrompts;
+ }
+
+ @Override
+ public int getAttempts() {
+ return Math.max(1, attempts);
+ }
+
+ @Override
+ public char[] getPassphrase(URIish uri, int attempt) throws IOException {
+ return getPassword(uri, attempt,
+ current.computeIfAbsent(uri, r -> new State()));
+ }
+
+ /**
+ * Retrieves a password to decrypt a private key.
+ *
+ * @param uri
+ * identifying the resource to obtain a password for
+ * @param attempt
+ * number of previous attempts to get a passphrase
+ * @param state
+ * encapsulating state information about attempts to get the
+ * password
+ * @return the password, or {@code null} or the empty string if none
+ * available.
+ * @throws IOException
+ * if an error occurs
+ */
+ protected char[] getPassword(URIish uri, int attempt, @NonNull State state)
+ throws IOException {
+ state.setPassword(null);
+ state.incCount();
+ String message = state.count == 1 ? SshdText.get().keyEncryptedMsg
+ : SshdText.get().keyEncryptedRetry;
+ char[] pass = getPassword(uri, message);
+ state.setPassword(pass);
+ return pass;
+ }
+
+ private char[] getPassword(URIish uri, String message) {
+ if (provider == null) {
+ return null;
+ }
+ List<CredentialItem> items = new ArrayList<>(2);
+ items.add(new CredentialItem.InformationalMessage(
+ format(message, uri)));
+ CredentialItem.Password password = new CredentialItem.Password(
+ SshdText.get().keyEncryptedPrompt);
+ items.add(password);
+ try {
+ provider.get(uri, items);
+ char[] pass = password.getValue();
+ if (pass == null) {
+ throw new CancellationException(
+ SshdText.get().authenticationCanceled);
+ }
+ return pass.clone();
+ } finally {
+ password.clear();
+ }
+ }
+
+ /**
+ * Invoked to inform the password provider about the decoding result.
+ *
+ * @param uri
+ * identifying the key resource the key was attempted to be
+ * loaded from
+ * @param state
+ * associated with this key
+ * @param password
+ * the password that was attempted
+ * @param err
+ * the attempt result - {@code null} for success
+ * @return how to proceed in case of error
+ * @throws IOException
+ * @throws GeneralSecurityException
+ */
+ protected boolean keyLoaded(URIish uri,
+ State state, char[] password, Exception err)
+ throws IOException, GeneralSecurityException {
+ if (err == null) {
+ return false; // Success, don't retry
+ } else if (err instanceof GeneralSecurityException) {
+ throw new InvalidKeyException(
+ format(SshdText.get().identityFileCannotDecrypt, uri), err);
+ } else {
+ // Unencrypted key (state == null && password == null), or exception
+ // before having asked for the password (state != null && password
+ // == null; might also be a user cancellation), or number of
+ // attempts exhausted.
+ if (state == null || password == null
+ || state.getCount() >= attempts) {
+ return false;
+ }
+ return true;
+ }
+ }
+
+ @Override
+ public boolean keyLoaded(URIish uri, int attempt, Exception error)
+ throws IOException, GeneralSecurityException {
+ State state = null;
+ boolean retry = false;
+ try {
+ state = current.get(uri);
+ retry = keyLoaded(uri, state,
+ state == null ? null : state.getPassword(), error);
+ } finally {
+ if (state != null) {
+ state.setPassword(null);
+ }
+ if (!retry) {
+ current.remove(uri);
+ }
+ }
+ return retry;
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/JGitKeyCache.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/JGitKeyCache.java
new file mode 100644
index 0000000000..52325c6780
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/JGitKeyCache.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.transport.sshd;
+
+import java.nio.file.Path;
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Function;
+
+import javax.security.auth.DestroyFailedException;
+
+/**
+ * A simple {@link KeyCache}. JGit uses one such cache in its
+ * {@link SshdSessionFactory} to avoid loading keys multiple times.
+ *
+ * @since 5.2
+ */
+public class JGitKeyCache implements KeyCache {
+
+ private AtomicReference<Map<Path, KeyPair>> cache = new AtomicReference<>(
+ new ConcurrentHashMap<>());
+
+ @Override
+ public KeyPair get(Path path,
+ Function<? super Path, ? extends KeyPair> loader) {
+ return cache.get().computeIfAbsent(path, loader);
+ }
+
+ @Override
+ public void close() {
+ Map<Path, KeyPair> map = cache.getAndSet(null);
+ if (map == null) {
+ return;
+ }
+ for (KeyPair k : map.values()) {
+ PrivateKey p = k.getPrivate();
+ try {
+ p.destroy();
+ } catch (DestroyFailedException e) {
+ // Ignore here. We did our best.
+ }
+ }
+ map.clear();
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/KeyCache.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/KeyCache.java
new file mode 100644
index 0000000000..d8c1d30426
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/KeyCache.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.transport.sshd;
+
+import java.nio.file.Path;
+import java.security.KeyPair;
+import java.util.function.Function;
+
+/**
+ * A cache for {@link KeyPair}s.
+ *
+ * @since 5.2
+ */
+public interface KeyCache {
+
+ /**
+ * Obtains a {@link KeyPair} from the cache. Implementations must be
+ * thread-safe.
+ *
+ * @param path
+ * of the key
+ * @param loader
+ * to load the key if it isn't present in the cache yet
+ * @return the {@link KeyPair}, or {@code null} if not present and could not
+ * be loaded
+ */
+ KeyPair get(Path path, Function<? super Path, ? extends KeyPair> loader);
+
+ /**
+ * Removes all {@link KeyPair} from this cache and destroys their private
+ * keys. This cache instance must not be used anymore thereafter.
+ */
+ void close();
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/KeyPasswordProvider.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/KeyPasswordProvider.java
new file mode 100644
index 0000000000..0f315a4545
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/KeyPasswordProvider.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.transport.sshd;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+
+import org.eclipse.jgit.transport.URIish;
+
+/**
+ * A {@code KeyPasswordProvider} provides passwords for encrypted private keys.
+ *
+ * @since 5.2
+ */
+public interface KeyPasswordProvider {
+
+ /**
+ * Obtains a passphrase to use to decrypt an ecrypted private key. Returning
+ * {@code null} or an empty array will skip this key. To cancel completely,
+ * the operation should raise
+ * {@link java.util.concurrent.CancellationException}.
+ *
+ * @param uri
+ * identifying the key resource that is being attempted to be
+ * loaded
+ * @param attempt
+ * the number of previous attempts to get a passphrase; >= 0
+ * @return the passphrase
+ * @throws IOException
+ * if no password can be obtained
+ */
+ char[] getPassphrase(URIish uri, int attempt) throws IOException;
+
+ /**
+ * Define the maximum number of attempts to get a passphrase that should be
+ * attempted for one identity resource through this provider.
+ *
+ * @param maxNumberOfAttempts
+ * number of times to ask for a passphrase;
+ * {@link IllegalArgumentException} may be thrown if <= 0
+ */
+ void setAttempts(int maxNumberOfAttempts);
+
+ /**
+ * Gets the maximum number of attempts to get a passphrase that should be
+ * attempted for one identity resource through this provider. The default
+ * return 1.
+ *
+ * @return the number of times to ask for a passphrase; should be >= 1.
+ */
+ default int getAttempts() {
+ return 1;
+ }
+
+ /**
+ * Invoked after a key has been loaded. If this raises an exception, the
+ * original {@code error} is lost unless it is attached to that exception.
+ *
+ * @param uri
+ * identifying the key resource the key was attempted to be
+ * loaded from
+ * @param attempt
+ * the number of times {@link #getPassphrase(URIish, int)} had
+ * been called; zero indicates that {@code uri} refers to a
+ * non-encrypted key
+ * @param error
+ * {@code null} if the key was loaded successfully; otherwise an
+ * exception indicating why the key could not be loaded
+ * @return {@code true} to re-try again; {@code false} to re-raise the
+ * {@code error} exception; Ignored if the key was loaded
+ * successfully, i.e., if {@code error == null}.
+ * @throws IOException
+ * @throws GeneralSecurityException
+ */
+ boolean keyLoaded(URIish uri, int attempt, Exception error)
+ throws IOException, GeneralSecurityException;
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/ProxyData.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/ProxyData.java
new file mode 100644
index 0000000000..39b1e02aec
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/ProxyData.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.transport.sshd;
+
+import java.net.InetSocketAddress;
+import java.net.Proxy;
+import java.util.Arrays;
+
+import org.eclipse.jgit.annotations.NonNull;
+
+/**
+ * A DTO encapsulating the data needed to connect through a proxy server.
+ *
+ * @since 5.2
+ */
+public class ProxyData {
+
+ private final @NonNull Proxy proxy;
+
+ private final String proxyUser;
+
+ private final char[] proxyPassword;
+
+ /**
+ * Creates a new {@link ProxyData} instance without user name or password.
+ *
+ * @param proxy
+ * to connect to; must not be {@link java.net.Proxy.Type#DIRECT}
+ * and must have an {@link InetSocketAddress}.
+ */
+ public ProxyData(@NonNull Proxy proxy) {
+ this(proxy, null, null);
+ }
+
+ /**
+ * Creates a new {@link ProxyData} instance.
+ *
+ * @param proxy
+ * to connect to; must not be {@link java.net.Proxy.Type#DIRECT}
+ * and must have an {@link InetSocketAddress}.
+ * @param proxyUser
+ * to use for log-in to the proxy, may be {@code null}
+ * @param proxyPassword
+ * to use for log-in to the proxy, may be {@code null}
+ */
+ public ProxyData(@NonNull Proxy proxy, String proxyUser,
+ char[] proxyPassword) {
+ this.proxy = proxy;
+ if (!(proxy.address() instanceof InetSocketAddress)) {
+ // Internal error not translated
+ throw new IllegalArgumentException(
+ "Proxy does not have an InetSocketAddress"); //$NON-NLS-1$
+ }
+ this.proxyUser = proxyUser;
+ this.proxyPassword = proxyPassword == null ? null
+ : proxyPassword.clone();
+ }
+
+ /**
+ * Obtains the remote {@link InetSocketAddress} of the proxy to connect to.
+ *
+ * @return the remote address of the proxy
+ */
+ @NonNull
+ public Proxy getProxy() {
+ return proxy;
+ }
+
+ /**
+ * Obtains the user to log in at the proxy with.
+ *
+ * @return the user name, or {@code null} if none
+ */
+ public String getUser() {
+ return proxyUser;
+ }
+
+ /**
+ * Obtains a copy of the internally stored password.
+ *
+ * @return the password or {@code null} if none
+ */
+ public char[] getPassword() {
+ return proxyPassword == null ? null : proxyPassword.clone();
+ }
+
+ /**
+ * Clears the stored password, if any.
+ */
+ public void clearPassword() {
+ if (proxyPassword != null) {
+ Arrays.fill(proxyPassword, '\000');
+ }
+ }
+
+} \ No newline at end of file
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/ProxyDataFactory.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/ProxyDataFactory.java
new file mode 100644
index 0000000000..334fff4f64
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/ProxyDataFactory.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.transport.sshd;
+
+import java.net.InetSocketAddress;
+
+/**
+ * Interface for obtaining {@link ProxyData} to connect through some proxy.
+ *
+ * @since 5.2
+ */
+public interface ProxyDataFactory {
+
+ /**
+ * Get the {@link ProxyData} to connect to a proxy. It should return a
+ * <em>new</em> {@link ProxyData} instance every time; if the returned
+ * {@link ProxyData} contains a password, the {@link SshdSession} will clear
+ * it once it is no longer needed.
+ *
+ * @param remoteAddress
+ * to connect to
+ * @return the {@link ProxyData} or {@code null} if a direct connection is
+ * to be made
+ */
+ ProxyData get(InetSocketAddress remoteAddress);
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SessionCloseListener.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SessionCloseListener.java
new file mode 100644
index 0000000000..31fc61f82c
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SessionCloseListener.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.transport.sshd;
+
+/**
+ * A {@code SessionCloseListener} is invoked when a {@link SshdSession} is
+ * closed.
+ *
+ * @since 5.2
+ */
+@FunctionalInterface
+public interface SessionCloseListener {
+
+ /**
+ * Invoked when a {@link SshdSession} has been closed.
+ *
+ * @param session
+ * that was closed.
+ */
+ void sessionClosed(SshdSession session);
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSession.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSession.java
new file mode 100644
index 0000000000..7d0e686a28
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSession.java
@@ -0,0 +1,485 @@
+/*
+ * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.transport.sshd;
+
+import static java.text.MessageFormat.format;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InterruptedIOException;
+import java.io.OutputStream;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Supplier;
+
+import org.apache.sshd.client.SshClient;
+import org.apache.sshd.client.channel.ChannelExec;
+import org.apache.sshd.client.channel.ClientChannelEvent;
+import org.apache.sshd.client.session.ClientSession;
+import org.apache.sshd.client.subsystem.sftp.SftpClient;
+import org.apache.sshd.client.subsystem.sftp.SftpClient.CloseableHandle;
+import org.apache.sshd.client.subsystem.sftp.SftpClient.CopyMode;
+import org.apache.sshd.client.subsystem.sftp.SftpClientFactory;
+import org.apache.sshd.common.session.Session;
+import org.apache.sshd.common.session.SessionListener;
+import org.apache.sshd.common.subsystem.sftp.SftpException;
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.internal.transport.sshd.SshdText;
+import org.eclipse.jgit.transport.FtpChannel;
+import org.eclipse.jgit.transport.RemoteSession;
+import org.eclipse.jgit.transport.URIish;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * An implementation of {@link RemoteSession} based on Apache MINA sshd.
+ *
+ * @since 5.2
+ */
+public class SshdSession implements RemoteSession {
+
+ private static final Logger LOG = LoggerFactory
+ .getLogger(SshdSession.class);
+
+ private final CopyOnWriteArrayList<SessionCloseListener> listeners = new CopyOnWriteArrayList<>();
+
+ private final URIish uri;
+
+ private SshClient client;
+
+ private ClientSession session;
+
+ SshdSession(URIish uri, Supplier<SshClient> clientFactory) {
+ this.uri = uri;
+ this.client = clientFactory.get();
+ }
+
+ void connect(Duration timeout) throws IOException {
+ if (!client.isStarted()) {
+ client.start();
+ }
+ try {
+ String username = uri.getUser();
+ String host = uri.getHost();
+ int port = uri.getPort();
+ long t = timeout.toMillis();
+ if (t <= 0) {
+ session = client.connect(username, host, port).verify()
+ .getSession();
+ } else {
+ session = client.connect(username, host, port)
+ .verify(timeout.toMillis()).getSession();
+ }
+ session.addSessionListener(new SessionListener() {
+
+ @Override
+ public void sessionClosed(Session s) {
+ notifyCloseListeners();
+ }
+ });
+ // Authentication timeout is by default 2 minutes.
+ session.auth().verify(session.getAuthTimeout());
+ } catch (IOException e) {
+ disconnect(e);
+ throw e;
+ }
+ }
+
+ /**
+ * Adds a {@link SessionCloseListener} to this session. Has no effect if the
+ * given {@code listener} is already registered with this session.
+ *
+ * @param listener
+ * to add
+ */
+ public void addCloseListener(@NonNull SessionCloseListener listener) {
+ listeners.addIfAbsent(listener);
+ }
+
+ /**
+ * Removes the given {@code listener}; has no effect if the listener is not
+ * currently registered with this session.
+ *
+ * @param listener
+ * to remove
+ */
+ public void removeCloseListener(@NonNull SessionCloseListener listener) {
+ listeners.remove(listener);
+ }
+
+ private void notifyCloseListeners() {
+ for (SessionCloseListener l : listeners) {
+ try {
+ l.sessionClosed(this);
+ } catch (RuntimeException e) {
+ LOG.warn(SshdText.get().closeListenerFailed, e);
+ }
+ }
+ }
+
+ @Override
+ public Process exec(String commandName, int timeout) throws IOException {
+ @SuppressWarnings("resource")
+ ChannelExec exec = session.createExecChannel(commandName);
+ long timeoutMillis = TimeUnit.SECONDS.toMillis(timeout);
+ try {
+ if (timeout <= 0) {
+ exec.open().verify();
+ } else {
+ long start = System.nanoTime();
+ exec.open().verify(timeoutMillis);
+ timeoutMillis -= TimeUnit.NANOSECONDS
+ .toMillis(System.nanoTime() - start);
+ }
+ } catch (IOException e) {
+ exec.close(true);
+ throw e;
+ } catch (RuntimeException e) {
+ exec.close(true);
+ throw e;
+ }
+ if (timeout > 0 && timeoutMillis <= 0) {
+ // We have used up the whole timeout for opening the channel
+ exec.close(true);
+ throw new InterruptedIOException(
+ format(SshdText.get().sshCommandTimeout, commandName,
+ Integer.valueOf(timeout)));
+ }
+ return new SshdExecProcess(exec, commandName, timeoutMillis);
+ }
+
+ /**
+ * Obtain an {@link FtpChannel} to perform SFTP operations in this
+ * {@link SshdSession}.
+ */
+ @Override
+ @NonNull
+ public FtpChannel getFtpChannel() {
+ return new SshdFtpChannel();
+ }
+
+ @Override
+ public void disconnect() {
+ disconnect(null);
+ }
+
+ private void disconnect(Throwable reason) {
+ try {
+ if (session != null) {
+ session.close();
+ session = null;
+ }
+ } catch (IOException e) {
+ if (reason != null) {
+ reason.addSuppressed(e);
+ } else {
+ LOG.error(SshdText.get().sessionCloseFailed, e);
+ }
+ } finally {
+ client.stop();
+ client = null;
+ }
+ }
+
+ private static class SshdExecProcess extends Process {
+
+ private final ChannelExec channel;
+
+ private final long timeoutMillis;
+
+ private final String commandName;
+
+ public SshdExecProcess(ChannelExec channel, String commandName,
+ long timeoutMillis) {
+ this.channel = channel;
+ this.timeoutMillis = timeoutMillis > 0 ? timeoutMillis : -1L;
+ this.commandName = commandName;
+ }
+
+ @Override
+ public OutputStream getOutputStream() {
+ return channel.getInvertedIn();
+ }
+
+ @Override
+ public InputStream getInputStream() {
+ return channel.getInvertedOut();
+ }
+
+ @Override
+ public InputStream getErrorStream() {
+ return channel.getInvertedErr();
+ }
+
+ @Override
+ public int waitFor() throws InterruptedException {
+ if (waitFor(timeoutMillis, TimeUnit.MILLISECONDS)) {
+ return exitValue();
+ }
+ return -1;
+ }
+
+ @Override
+ public boolean waitFor(long timeout, TimeUnit unit)
+ throws InterruptedException {
+ long millis = timeout >= 0 ? unit.toMillis(timeout) : -1L;
+ return channel
+ .waitFor(EnumSet.of(ClientChannelEvent.CLOSED), millis)
+ .contains(ClientChannelEvent.CLOSED);
+ }
+
+ @Override
+ public int exitValue() {
+ Integer exitCode = channel.getExitStatus();
+ if (exitCode == null) {
+ throw new IllegalThreadStateException(
+ format(SshdText.get().sshProcessStillRunning,
+ commandName));
+ }
+ return exitCode.intValue();
+ }
+
+ @Override
+ public void destroy() {
+ if (channel.isOpen()) {
+ channel.close(true);
+ }
+ }
+ }
+
+ /**
+ * Helper interface like {@link Supplier}, but possibly raising an
+ * {@link IOException}.
+ *
+ * @param <T>
+ * return type
+ */
+ @FunctionalInterface
+ private interface FtpOperation<T> {
+
+ T call() throws IOException;
+
+ }
+
+ private class SshdFtpChannel implements FtpChannel {
+
+ private SftpClient ftp;
+
+ /** Current working directory. */
+ private String cwd = ""; //$NON-NLS-1$
+
+ @Override
+ public void connect(int timeout, TimeUnit unit) throws IOException {
+ if (timeout <= 0) {
+ session.getProperties().put(
+ SftpClient.SFTP_CHANNEL_OPEN_TIMEOUT,
+ Long.valueOf(Long.MAX_VALUE));
+ } else {
+ session.getProperties().put(
+ SftpClient.SFTP_CHANNEL_OPEN_TIMEOUT,
+ Long.valueOf(unit.toMillis(timeout)));
+ }
+ ftp = SftpClientFactory.instance().createSftpClient(session);
+ try {
+ cd(cwd);
+ } catch (IOException e) {
+ ftp.close();
+ }
+ }
+
+ @Override
+ public void disconnect() {
+ try {
+ ftp.close();
+ } catch (IOException e) {
+ LOG.error(SshdText.get().ftpCloseFailed, e);
+ }
+ }
+
+ @Override
+ public boolean isConnected() {
+ return session.isAuthenticated() && ftp.isOpen();
+ }
+
+ private String absolute(String path) {
+ if (path.isEmpty()) {
+ return cwd;
+ }
+ // Note: there is no path injection vulnerability here. If
+ // path has too many ".." components, we rely on the server
+ // catching it and returning an error.
+ if (path.charAt(0) != '/') {
+ if (cwd.charAt(cwd.length() - 1) == '/') {
+ return cwd + path;
+ } else {
+ return cwd + '/' + path;
+ }
+ }
+ return path;
+ }
+
+ private <T> T map(FtpOperation<T> op) throws IOException {
+ try {
+ return op.call();
+ } catch (IOException e) {
+ if (e instanceof SftpException) {
+ throw new FtpChannel.FtpException(e.getLocalizedMessage(),
+ ((SftpException) e).getStatus(), e);
+ }
+ throw e;
+ }
+ }
+
+ @Override
+ public void cd(String path) throws IOException {
+ cwd = map(() -> ftp.canonicalPath(absolute(path)));
+ if (cwd.isEmpty()) {
+ cwd += '/';
+ }
+ }
+
+ @Override
+ public String pwd() throws IOException {
+ return cwd;
+ }
+
+ @Override
+ public Collection<DirEntry> ls(String path) throws IOException {
+ return map(() -> {
+ List<DirEntry> result = new ArrayList<>();
+ try (CloseableHandle handle = ftp.openDir(absolute(path))) {
+ AtomicReference<Boolean> atEnd = new AtomicReference<>(
+ Boolean.FALSE);
+ while (!atEnd.get().booleanValue()) {
+ List<SftpClient.DirEntry> chunk = ftp.readDir(handle,
+ atEnd);
+ if (chunk == null) {
+ break;
+ }
+ for (SftpClient.DirEntry remote : chunk) {
+ result.add(new DirEntry() {
+
+ @Override
+ public String getFilename() {
+ return remote.getFilename();
+ }
+
+ @Override
+ public long getModifiedTime() {
+ return remote.getAttributes()
+ .getModifyTime().toMillis();
+ }
+
+ @Override
+ public boolean isDirectory() {
+ return remote.getAttributes().isDirectory();
+ }
+
+ });
+ }
+ }
+ }
+ return result;
+ });
+ }
+
+ @Override
+ public void rmdir(String path) throws IOException {
+ map(() -> {
+ ftp.rmdir(absolute(path));
+ return null;
+ });
+
+ }
+
+ @Override
+ public void mkdir(String path) throws IOException {
+ map(() -> {
+ ftp.mkdir(absolute(path));
+ return null;
+ });
+ }
+
+ @Override
+ public InputStream get(String path) throws IOException {
+ return map(() -> ftp.read(absolute(path)));
+ }
+
+ @Override
+ public OutputStream put(String path) throws IOException {
+ return map(() -> ftp.write(absolute(path)));
+ }
+
+ @Override
+ public void rm(String path) throws IOException {
+ map(() -> {
+ ftp.remove(absolute(path));
+ return null;
+ });
+ }
+
+ @Override
+ public void rename(String from, String to) throws IOException {
+ map(() -> {
+ String src = absolute(from);
+ String dest = absolute(to);
+ try {
+ ftp.rename(src, dest, CopyMode.Atomic, CopyMode.Overwrite);
+ } catch (UnsupportedOperationException e) {
+ // Older server cannot do POSIX rename...
+ if (!src.equals(dest)) {
+ delete(dest);
+ ftp.rename(src, dest);
+ }
+ }
+ return null;
+ });
+ }
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSessionFactory.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSessionFactory.java
new file mode 100644
index 0000000000..4ec6f22094
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSessionFactory.java
@@ -0,0 +1,499 @@
+/*
+ * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.transport.sshd;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.stream.Collectors;
+
+import org.apache.sshd.client.ClientBuilder;
+import org.apache.sshd.client.SshClient;
+import org.apache.sshd.client.auth.UserAuth;
+import org.apache.sshd.client.auth.keyboard.UserAuthKeyboardInteractiveFactory;
+import org.apache.sshd.client.config.hosts.HostConfigEntryResolver;
+import org.apache.sshd.client.keyverifier.ServerKeyVerifier;
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.compression.BuiltinCompressions;
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.keyprovider.FileKeyPairProvider;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.errors.TransportException;
+import org.eclipse.jgit.internal.transport.sshd.CachingKeyPairProvider;
+import org.eclipse.jgit.internal.transport.sshd.GssApiWithMicAuthFactory;
+import org.eclipse.jgit.internal.transport.sshd.JGitPasswordAuthFactory;
+import org.eclipse.jgit.internal.transport.sshd.JGitPublicKeyAuthFactory;
+import org.eclipse.jgit.internal.transport.sshd.JGitSshClient;
+import org.eclipse.jgit.internal.transport.sshd.JGitSshConfig;
+import org.eclipse.jgit.internal.transport.sshd.JGitUserInteraction;
+import org.eclipse.jgit.internal.transport.sshd.OpenSshServerKeyVerifier;
+import org.eclipse.jgit.internal.transport.sshd.PasswordProviderWrapper;
+import org.eclipse.jgit.internal.transport.sshd.SshdText;
+import org.eclipse.jgit.transport.CredentialsProvider;
+import org.eclipse.jgit.transport.SshConstants;
+import org.eclipse.jgit.transport.SshSessionFactory;
+import org.eclipse.jgit.transport.URIish;
+import org.eclipse.jgit.util.FS;
+
+/**
+ * A {@link SshSessionFactory} that uses Apache MINA sshd.
+ *
+ * @since 5.2
+ */
+public class SshdSessionFactory extends SshSessionFactory implements Closeable {
+
+ private final AtomicBoolean closing = new AtomicBoolean();
+
+ private final Set<SshdSession> sessions = new HashSet<>();
+
+ private final Map<Tuple, HostConfigEntryResolver> defaultHostConfigEntryResolver = new ConcurrentHashMap<>();
+
+ private final Map<Tuple, ServerKeyVerifier> defaultServerKeyVerifier = new ConcurrentHashMap<>();
+
+ private final Map<Tuple, FileKeyPairProvider> defaultKeys = new ConcurrentHashMap<>();
+
+ private final KeyCache keyCache;
+
+ private final ProxyDataFactory proxies;
+
+ private File sshDirectory;
+
+ private File homeDirectory;
+
+ /**
+ * Creates a new {@link SshdSessionFactory} without key cache and a
+ * {@link DefaultProxyDataFactory}.
+ */
+ public SshdSessionFactory() {
+ this(null, new DefaultProxyDataFactory());
+ }
+
+ /**
+ * Creates a new {@link SshdSessionFactory} using the given {@link KeyCache}
+ * and {@link ProxyDataFactory}. The {@code keyCache} is used for all sessions
+ * created through this session factory; cached keys are destroyed when the
+ * session factory is {@link #close() closed}.
+ * <p>
+ * Caching ssh keys in memory for an extended period of time is generally
+ * considered bad practice, but there may be circumstances where using a
+ * {@link KeyCache} is still the right choice, for instance to avoid that a
+ * user gets prompted several times for the same password for the same key.
+ * In general, however, it is preferable <em>not</em> to use a key cache but
+ * to use a {@link #createKeyPasswordProvider(CredentialsProvider)
+ * KeyPasswordProvider} that has access to some secure storage and can save
+ * and retrieve passwords from there without user interaction. Another
+ * approach is to use an ssh agent.
+ * </p>
+ * <p>
+ * Note that the underlying ssh library (Apache MINA sshd) may or may not
+ * keep ssh keys in memory for unspecified periods of time irrespective of
+ * the use of a {@link KeyCache}.
+ * </p>
+ *
+ * @param keyCache
+ * {@link KeyCache} to use for caching ssh keys, or {@code null}
+ * to not use a key cache
+ * @param proxies
+ * {@link ProxyDataFactory} to use, or {@code null} to not use a
+ * proxy database (in which case connections through proxies will
+ * not be possible)
+ */
+ public SshdSessionFactory(KeyCache keyCache, ProxyDataFactory proxies) {
+ super();
+ this.keyCache = keyCache;
+ this.proxies = proxies;
+ }
+
+ /** A simple general map key. */
+ private static final class Tuple {
+ private Object[] objects;
+
+ public Tuple(Object... objects) {
+ this.objects = objects;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (obj != null && obj.getClass() == Tuple.class) {
+ Tuple other = (Tuple) obj;
+ return Arrays.equals(objects, other.objects);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Arrays.hashCode(objects);
+ }
+ }
+
+ // We can't really use a single client. Clients need to be stopped
+ // properly, and we don't really know when to do that. Instead we use
+ // a dedicated SshClient instance per session. We need a bit of caching to
+ // avoid re-loading the ssh config and keys repeatedly.
+
+ @Override
+ public SshdSession getSession(URIish uri,
+ CredentialsProvider credentialsProvider, FS fs, int tms)
+ throws TransportException {
+ SshdSession session = null;
+ try {
+ session = new SshdSession(uri, () -> {
+ File home = getHomeDirectory();
+ if (home == null) {
+ // Always use the detected filesystem for the user home!
+ // It makes no sense to have different "user home"
+ // directories depending on what file system a repository
+ // is.
+ home = FS.DETECTED.userHome();
+ }
+ File sshDir = getSshDirectory();
+ if (sshDir == null) {
+ sshDir = new File(home, SshConstants.SSH_DIR);
+ }
+ HostConfigEntryResolver configFile = getHostConfigEntryResolver(
+ home, sshDir);
+ KeyPairProvider defaultKeysProvider = getDefaultKeysProvider(
+ sshDir);
+ KeyPasswordProvider passphrases = createKeyPasswordProvider(
+ credentialsProvider);
+ SshClient client = ClientBuilder.builder()
+ .factory(JGitSshClient::new)
+ .filePasswordProvider(
+ createFilePasswordProvider(passphrases))
+ .hostConfigEntryResolver(configFile)
+ .serverKeyVerifier(getServerKeyVerifier(home, sshDir))
+ .compressionFactories(
+ new ArrayList<>(BuiltinCompressions.VALUES))
+ .build();
+ client.setUserInteraction(
+ new JGitUserInteraction(credentialsProvider));
+ client.setUserAuthFactories(getUserAuthFactories());
+ client.setKeyPairProvider(defaultKeysProvider);
+ // JGit-specific things:
+ JGitSshClient jgitClient = (JGitSshClient) client;
+ jgitClient.setKeyCache(getKeyCache());
+ jgitClient.setCredentialsProvider(credentialsProvider);
+ jgitClient.setProxyDatabase(proxies);
+ String defaultAuths = getDefaultPreferredAuthentications();
+ if (defaultAuths != null) {
+ jgitClient.setAttribute(
+ JGitSshClient.PREFERRED_AUTHENTICATIONS,
+ defaultAuths);
+ }
+ // Other things?
+ return client;
+ });
+ session.addCloseListener(s -> unregister(s));
+ register(session);
+ session.connect(Duration.ofMillis(tms));
+ return session;
+ } catch (Exception e) {
+ unregister(session);
+ throw new TransportException(uri, e.getMessage(), e);
+ }
+ }
+
+ @Override
+ public void close() {
+ closing.set(true);
+ boolean cleanKeys = false;
+ synchronized (this) {
+ cleanKeys = sessions.isEmpty();
+ }
+ if (cleanKeys) {
+ KeyCache cache = getKeyCache();
+ if (cache != null) {
+ cache.close();
+ }
+ }
+ }
+
+ private void register(SshdSession newSession) throws IOException {
+ if (newSession == null) {
+ return;
+ }
+ if (closing.get()) {
+ throw new IOException(SshdText.get().sshClosingDown);
+ }
+ synchronized (this) {
+ sessions.add(newSession);
+ }
+ }
+
+ private void unregister(SshdSession oldSession) {
+ boolean cleanKeys = false;
+ synchronized (this) {
+ sessions.remove(oldSession);
+ cleanKeys = closing.get() && sessions.isEmpty();
+ }
+ if (cleanKeys) {
+ KeyCache cache = getKeyCache();
+ if (cache != null) {
+ cache.close();
+ }
+ }
+ }
+
+ /**
+ * Set a global directory to use as the user's home directory
+ *
+ * @param homeDir
+ * to use
+ */
+ public void setHomeDirectory(@NonNull File homeDir) {
+ if (homeDir.isAbsolute()) {
+ homeDirectory = homeDir;
+ } else {
+ homeDirectory = homeDir.getAbsoluteFile();
+ }
+ }
+
+ /**
+ * Retrieves the global user home directory
+ *
+ * @return the directory, or {@code null} if not set
+ */
+ public File getHomeDirectory() {
+ return homeDirectory;
+ }
+
+ /**
+ * Set a global directory to use as the .ssh directory
+ *
+ * @param sshDir
+ * to use
+ */
+ public void setSshDirectory(@NonNull File sshDir) {
+ if (sshDir.isAbsolute()) {
+ sshDirectory = sshDir;
+ } else {
+ sshDirectory = sshDir.getAbsoluteFile();
+ }
+ }
+
+ /**
+ * Retrieves the global .ssh directory
+ *
+ * @return the directory, or {@code null} if not set
+ */
+ public File getSshDirectory() {
+ return sshDirectory;
+ }
+
+ /**
+ * Obtain a {@link HostConfigEntryResolver} to read the ssh config file and
+ * to determine host entries for connections.
+ *
+ * @param homeDir
+ * home directory to use for ~ replacement
+ * @param sshDir
+ * to use for looking for the config file
+ * @return the resolver
+ */
+ @NonNull
+ private HostConfigEntryResolver getHostConfigEntryResolver(
+ @NonNull File homeDir, @NonNull File sshDir) {
+ return defaultHostConfigEntryResolver.computeIfAbsent(
+ new Tuple(homeDir, sshDir),
+ t -> new JGitSshConfig(homeDir,
+ new File(sshDir, SshConstants.CONFIG),
+ getLocalUserName()));
+ }
+
+ /**
+ * Obtain a {@link ServerKeyVerifier} to read known_hosts files and to
+ * verify server host keys. The default implementation returns a
+ * {@link ServerKeyVerifier} that recognizes the two openssh standard files
+ * {@code ~/.ssh/known_hosts} and {@code ~/.ssh/known_hosts2} as well as any
+ * files configured via the {@code UserKnownHostsFile} option in the ssh
+ * config file.
+ *
+ * @param homeDir
+ * home directory to use for ~ replacement
+ * @param sshDir
+ * representing ~/.ssh/
+ * @return the resolver
+ */
+ @NonNull
+ private ServerKeyVerifier getServerKeyVerifier(@NonNull File homeDir,
+ @NonNull File sshDir) {
+ return defaultServerKeyVerifier.computeIfAbsent(
+ new Tuple(homeDir, sshDir),
+ t -> new OpenSshServerKeyVerifier(true,
+ getDefaultKnownHostsFiles(sshDir)));
+ }
+
+ /**
+ * Gets the list of default user known hosts files. The default returns
+ * ~/.ssh/known_hosts and ~/.ssh/known_hosts2. The ssh config
+ * {@code UserKnownHostsFile} overrides this default.
+ *
+ * @param sshDir
+ * @return the possibly empty list of default known host file paths.
+ */
+ @NonNull
+ protected List<Path> getDefaultKnownHostsFiles(@NonNull File sshDir) {
+ return Arrays.asList(sshDir.toPath().resolve(SshConstants.KNOWN_HOSTS),
+ sshDir.toPath().resolve(SshConstants.KNOWN_HOSTS + '2'));
+ }
+
+ /**
+ * Determines a {@link KeyPairProvider} to use to load the default keys.
+ *
+ * @param sshDir
+ * to look in for keys
+ * @return the {@link KeyPairProvider}
+ */
+ @NonNull
+ private KeyPairProvider getDefaultKeysProvider(@NonNull File sshDir) {
+ return defaultKeys.computeIfAbsent(new Tuple(sshDir),
+ t -> new CachingKeyPairProvider(getDefaultIdentities(sshDir),
+ getKeyCache()));
+ }
+
+ /**
+ * Gets a list of default identities, i.e., private key files that shall
+ * always be tried for public key authentication. Typically those are
+ * ~/.ssh/id_dsa, ~/.ssh/id_rsa, and so on. The default implementation
+ * returns the files defined in {@link SshConstants#DEFAULT_IDENTITIES}.
+ *
+ * @param sshDir
+ * the directory that represents ~/.ssh/
+ * @return a possibly empty list of paths containing default identities
+ * (private keys)
+ */
+ @NonNull
+ protected List<Path> getDefaultIdentities(@NonNull File sshDir) {
+ return Arrays
+ .asList(SshConstants.DEFAULT_IDENTITIES).stream()
+ .map(s -> new File(sshDir, s).toPath()).filter(Files::exists)
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * Obtains the {@link KeyCache} to use to cache loaded keys.
+ *
+ * @return the {@link KeyCache}, or {@code null} if none.
+ */
+ protected final KeyCache getKeyCache() {
+ return keyCache;
+ }
+
+ /**
+ * Creates a {@link KeyPasswordProvider} for a new session.
+ *
+ * @param provider
+ * the {@link CredentialsProvider} to delegate to for user
+ * interactions
+ * @return a new {@link KeyPasswordProvider}
+ */
+ @NonNull
+ protected KeyPasswordProvider createKeyPasswordProvider(
+ CredentialsProvider provider) {
+ return new IdentityPasswordProvider(provider);
+ }
+
+ /**
+ * Creates a {@link FilePasswordProvider} for a new session.
+ *
+ * @param provider
+ * the {@link KeyPasswordProvider} to delegate to
+ * @return a new {@link FilePasswordProvider}
+ */
+ @NonNull
+ private FilePasswordProvider createFilePasswordProvider(
+ KeyPasswordProvider provider) {
+ return new PasswordProviderWrapper(provider);
+ }
+
+ /**
+ * Gets the user authentication mechanisms (or rather, factories for them).
+ * By default this returns gssapi-with-mic, public-key, password, and
+ * keyboard-interactive, in that order. The order is only significant if the
+ * ssh config does <em>not</em> set {@code PreferredAuthentications}; if it
+ * is set, the order defined there will be taken.
+ *
+ * @return the non-empty list of factories.
+ */
+ @NonNull
+ private List<NamedFactory<UserAuth>> getUserAuthFactories() {
+ // About the order of password and keyboard-interactive, see upstream
+ // bug https://issues.apache.org/jira/projects/SSHD/issues/SSHD-866 .
+ // Password auth doesn't have this problem.
+ return Collections.unmodifiableList(
+ Arrays.asList(GssApiWithMicAuthFactory.INSTANCE,
+ JGitPublicKeyAuthFactory.INSTANCE,
+ JGitPasswordAuthFactory.INSTANCE,
+ UserAuthKeyboardInteractiveFactory.INSTANCE));
+ }
+
+ /**
+ * Gets the list of default preferred authentication mechanisms. If
+ * {@code null} is returned the openssh default list will be in effect. If
+ * the ssh config defines {@code PreferredAuthentications} the value from
+ * the ssh config takes precedence.
+ *
+ * @return a comma-separated list of algorithm names, or {@code null} if
+ * none
+ */
+ protected String getDefaultPreferredAuthentications() {
+ return null;
+ }
+}
diff --git a/org.eclipse.jgit.test/.classpath b/org.eclipse.jgit.test/.classpath
index 84b5052a6c..7cc18cca34 100644
--- a/org.eclipse.jgit.test/.classpath
+++ b/org.eclipse.jgit.test/.classpath
@@ -1,9 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
- <classpathentry excluding="**/*.idx|**/*.pack" kind="src" path="tst"/>
+ <classpathentry excluding="**/*.idx|**/*.pack" kind="src" path="tst" output="bin-tst">
+ <attributes>
+ <attribute name="test" value="true"/>
+ </attributes>
+ </classpathentry>
<classpathentry kind="src" path="src"/>
- <classpathentry kind="src" path="tst-rsrc"/>
- <classpathentry kind="src" path="exttst"/>
+ <classpathentry kind="src" path="tst-rsrc" output="bin-tst">
+ <attributes>
+ <attribute name="test" value="true"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry kind="src" path="exttst" output="bin-tst">
+ <attributes>
+ <attribute name="test" value="true"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry kind="src" path="resources"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="output" path="bin"/>
diff --git a/org.eclipse.jgit.test/.gitignore b/org.eclipse.jgit.test/.gitignore
index 934e0e06ff..561cf84a1b 100644
--- a/org.eclipse.jgit.test/.gitignore
+++ b/org.eclipse.jgit.test/.gitignore
@@ -1,2 +1,3 @@
/bin
/target
+/bin-tst/
diff --git a/org.eclipse.jgit.test/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.test/.settings/org.eclipse.jdt.core.prefs
index 794592dee1..2ca78ff2d0 100644
--- a/org.eclipse.jgit.test/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.test/.settings/org.eclipse.jdt.core.prefs
@@ -91,7 +91,7 @@ org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled
org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning
org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning
-org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=ignore
org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error
org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
diff --git a/org.eclipse.jgit.test/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.test/.settings/org.eclipse.mylyn.team.ui.prefs
index 0cba949fb7..2fca432276 100644
--- a/org.eclipse.jgit.test/.settings/org.eclipse.mylyn.team.ui.prefs
+++ b/org.eclipse.jgit.test/.settings/org.eclipse.mylyn.team.ui.prefs
@@ -1,3 +1,3 @@
#Tue Jul 19 20:11:28 CEST 2011
-commit.comment.template=${task.description} \n\nBug\: ${task.key}
+commit.comment.template=${task.description}\n\nBug\: ${task.key}
eclipse.preferences.version=1
diff --git a/org.eclipse.jgit.test/BUILD b/org.eclipse.jgit.test/BUILD
index bbda838f00..0b18e5ef7d 100644
--- a/org.eclipse.jgit.test/BUILD
+++ b/org.eclipse.jgit.test/BUILD
@@ -6,7 +6,10 @@ load(
PKG = "tst/org/eclipse/jgit/"
-HELPERS = glob(["src/**/*.java"]) + [PKG + c for c in [
+HELPERS = glob(
+ ["src/**/*.java"],
+ exclude = ["src/org/eclipse/jgit/transport/ssh/*.java"],
+) + [PKG + c for c in [
"api/AbstractRemoteCommandTest.java",
"diff/AbstractDiffTestCase.java",
"internal/storage/file/GcTestCase.java",
@@ -18,6 +21,7 @@ HELPERS = glob(["src/**/*.java"]) + [PKG + c for c in [
"nls/NonTranslatedBundle.java",
"revwalk/RevQueueTestCase.java",
"revwalk/RevWalkTestCase.java",
+ "transport/ObjectIdMatcher.java",
"transport/SpiTransport.java",
"treewalk/FileTreeIteratorWithTimeControl.java",
"treewalk/filter/AlwaysCloneTreeFilter.java",
@@ -31,6 +35,8 @@ DATA = [
PKG + "lib/sorttest.gitindex.dat",
]
+RESOURCES = glob(["resources/**"])
+
tests(glob(
["tst/**/*.java"],
exclude = HELPERS + DATA,
@@ -42,9 +48,31 @@ java_library(
srcs = HELPERS,
resources = DATA,
deps = [
+ "//lib:jsch",
+ "//lib:junit",
+ "//lib:mockito",
+ "//org.eclipse.jgit:jgit",
+ "//org.eclipse.jgit.junit:junit",
+ ],
+)
+
+java_library(
+ name = "sshd-helpers",
+ testonly = 1,
+ srcs = glob(["src/org/eclipse/jgit/transport/ssh/*.java"]),
+ resource_strip_prefix = "org.eclipse.jgit.test/resources",
+ resources = RESOURCES,
+ visibility = [
+ "//org.eclipse.jgit.ssh.apache.test:__pkg__",
+ ],
+ deps = [
+ "//lib:jsch",
"//lib:junit",
+ "//lib:sshd-core",
+ "//lib:sshd-sftp",
"//org.eclipse.jgit:jgit",
"//org.eclipse.jgit.junit:junit",
+ "//org.eclipse.jgit.junit.ssh:junit-ssh",
],
)
diff --git a/org.eclipse.jgit.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.test/META-INF/MANIFEST.MF
index 69ea99b4f1..55ccecf469 100644
--- a/org.eclipse.jgit.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.test/META-INF/MANIFEST.MF
@@ -3,63 +3,73 @@ Bundle-ManifestVersion: 2
Bundle-Name: %plugin_name
Automatic-Module-Name: org.eclipse.jgit.test
Bundle-SymbolicName: org.eclipse.jgit.test
-Bundle-Version: 5.1.9.qualifier
+Bundle-Version: 5.2.3.qualifier
Bundle-Localization: plugin
Bundle-Vendor: %provider_name
Bundle-ActivationPolicy: lazy
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Import-Package: com.googlecode.javaewah;version="[1.1.6,2.0.0)",
com.jcraft.jsch;version="[0.1.54,0.2.0)",
- org.eclipse.jgit.api;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.api.errors;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.attributes;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.awtui;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.blame;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.diff;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.dircache;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.errors;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.events;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.fnmatch;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.gitrepo;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.hooks;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.ignore;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.ignore.internal;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.internal;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.internal.fsck;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.internal.storage.dfs;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.internal.storage.file;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.internal.storage.io;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.internal.storage.pack;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.internal.storage.reftable;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.internal.storage.reftree;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.junit;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.lfs;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.lib;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.merge;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.nls;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.notes;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.patch;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.pgm;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.pgm.internal;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.revplot;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.revwalk;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.revwalk.filter;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.storage.file;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.storage.pack;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.submodule;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.transport;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.transport.http;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.transport.resolver;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.treewalk;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.treewalk.filter;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.util;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.util.io;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.util.sha1;version="[5.1.9,5.2.0)",
+ net.bytebuddy.dynamic.loading;version="[1.7.0,2.0.0)",
+ org.eclipse.jgit.annotations;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.api;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.api.errors;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.attributes;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.awtui;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.blame;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.diff;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.dircache;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.errors;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.events;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.fnmatch;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.gitrepo;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.hooks;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.ignore;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.ignore.internal;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.internal;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.internal.fsck;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.internal.storage.dfs;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.internal.storage.file;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.internal.storage.io;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.internal.storage.pack;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.internal.storage.reftable;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.internal.storage.reftree;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.internal.transport.parser;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.junit;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.junit.ssh;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.lfs;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.lib;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.merge;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.nls;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.notes;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.patch;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.pgm;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.pgm.internal;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.revplot;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.revwalk;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.revwalk.filter;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.storage.file;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.storage.pack;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.submodule;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.transport;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.transport.http;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.transport.resolver;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.treewalk;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.treewalk.filter;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.util;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.util.io;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.util.sha1;version="[5.2.3,5.3.0)",
org.junit;version="[4.12,5.0.0)",
org.junit.experimental.theories;version="[4.12,5.0.0)",
org.junit.rules;version="[4.12,5.0.0)",
org.junit.runner;version="[4.12,5.0.0)",
org.junit.runners;version="[4.12,5.0.0)",
+ org.mockito;version="[2.13.0,3.0.0)",
+ org.mockito.invocation;version="[2.13.0,3.0.0)",
+ org.mockito.junit;version="[2.13.0,3.0.0)",
+ org.mockito.stubbing;version="[2.13.0,3.0.0)",
+ org.objenesis;version="[2.6.0,3.0.0)",
org.slf4j;version="[1.7.0,2.0.0)"
Require-Bundle: org.hamcrest.core;bundle-version="[1.1.0,2.0.0)",
org.hamcrest.library;bundle-version="[1.1.0,2.0.0)"
+Export-Package: org.eclipse.jgit.transport.ssh;version="5.2.3";x-friends:="org.eclipse.jgit.ssh.apache.test"
diff --git a/org.eclipse.jgit.test/build.properties b/org.eclipse.jgit.test/build.properties
index e7b3b9978e..78c8f55f3a 100644
--- a/org.eclipse.jgit.test/build.properties
+++ b/org.eclipse.jgit.test/build.properties
@@ -1,9 +1,12 @@
source.. = tst/,\
tst-rsrc/,\
exttst/,\
- src/
+ src/,\
+ resources/
bin.includes = META-INF/,\
.,\
- plugin.properties
+ plugin.properties,\
+ bin-tst/,\
+ bin/
additional.bundles = org.apache.log4j,\
org.slf4j.impl.log4j12
diff --git a/org.eclipse.jgit.test/pom.xml b/org.eclipse.jgit.test/pom.xml
index 7a4d58017d..6b19eb368c 100644
--- a/org.eclipse.jgit.test/pom.xml
+++ b/org.eclipse.jgit.test/pom.xml
@@ -52,7 +52,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>5.1.9-SNAPSHOT</version>
+ <version>5.2.3-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.test</artifactId>
@@ -73,12 +73,10 @@
</dependency>
<!-- Optional security provider for encryption tests. -->
- <!-- See https://dev.eclipse.org/ipzilla/show_bug.cgi?id=9554 -->
- <!-- See https://bugs.eclipse.org/bugs/show_bug.cgi?id=467064 -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
- <version>1.52</version>
+ <version>1.59</version>
<scope>test</scope>
</dependency>
@@ -90,6 +88,12 @@
</dependency>
<dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <version>2.13.0</version>
+ </dependency>
+
+ <dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit</artifactId>
<version>${project.version}</version>
@@ -103,6 +107,12 @@
<dependency>
<groupId>org.eclipse.jgit</groupId>
+ <artifactId>org.eclipse.jgit.junit.ssh</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit.ui</artifactId>
<version>${project.version}</version>
</dependency>
@@ -142,6 +152,18 @@
</testResource>
</testResources>
+ <resources>
+ <resource>
+ <directory>.</directory>
+ <includes>
+ <include>plugin.properties</include>
+ </includes>
+ </resource>
+ <resource>
+ <directory>resources/</directory>
+ </resource>
+ </resources>
+
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
@@ -157,7 +179,7 @@
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
- <argLine>@{argLine} -Xmx768m -Dfile.encoding=UTF-8 -Djava.io.tmpdir=${project.build.directory}</argLine>
+ <argLine>-Xmx1024m -Dfile.encoding=UTF-8 -Djava.io.tmpdir=${project.build.directory}</argLine>
<includes>
<include>**/*Test.java</include>
<include>**/*Tests.java</include>
diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_dsa b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_dsa
new file mode 100644
index 0000000000..f097516df2
--- /dev/null
+++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_dsa
@@ -0,0 +1,12 @@
+-----BEGIN DSA PRIVATE KEY-----
+MIIBuwIBAAKBgQC+mJEX/XBloWhNM+BEuoh5z+EAuZfVyJ8cHNKlQmC1sWrENKGh
+P8ZhzWeHW0A7JnvTQgMqW6yD4mDzCpbR1wEz5KeXAphEjCGPnRik7Q4RjpZTd6Nq
+nNF/CYYGYuwR7ZGUPITTpKJWgX6NkEk+a4tvTWP7xfxOq5iKIspFEhEOlQIVAIBi
+TdAR8M2twrXZdspBjdJprjDXAoGAOrRYdXRHhpsOewIi9GQah0lde7AVrmZawK9Z
+BwhDUagL58gS8PvcsNNVhS2dKEX45pqZmgayt2UEE/5bke3+CdZtStDsezBYMu8P
+I/0qjOULhl7xLJT5ayCIN2ZuvcH8vtqH89fXgZkIz0c68AzY1ZFjJPc+TdE0puI9
+3mMVRaoCgYEAslyMZiOwYA3oiFMQTJEphKdgejWsjqQ9LoKppfZ3d4Jj1V3tgI1s
+/wHfoneUUrUwM+sMHZKXbBDLWWQUOSIxDYcXKDkbZ1FlmhvJR+45D2LyLKjEnjVD
+lQCwYly4P26zXqciZS7k3H/DjiHtAPUeoHm9IYb1A03K8Bd/xW0guMcCFFeUfQeX
+3mFPCfKJ5uXMjkPUqIo/
+-----END DSA PRIVATE KEY-----
diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_dsa.pub b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_dsa.pub
new file mode 100644
index 0000000000..676685332e
--- /dev/null
+++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_dsa.pub
@@ -0,0 +1 @@
+ssh-dss AAAAB3NzaC1kc3MAAACBAL6YkRf9cGWhaE0z4ES6iHnP4QC5l9XInxwc0qVCYLWxasQ0oaE/xmHNZ4dbQDsme9NCAypbrIPiYPMKltHXATPkp5cCmESMIY+dGKTtDhGOllN3o2qc0X8JhgZi7BHtkZQ8hNOkolaBfo2QST5ri29NY/vF/E6rmIoiykUSEQ6VAAAAFQCAYk3QEfDNrcK12XbKQY3Saa4w1wAAAIA6tFh1dEeGmw57AiL0ZBqHSV17sBWuZlrAr1kHCENRqAvnyBLw+9yw01WFLZ0oRfjmmpmaBrK3ZQQT/luR7f4J1m1K0Ox7MFgy7w8j/SqM5QuGXvEslPlrIIg3Zm69wfy+2ofz19eBmQjPRzrwDNjVkWMk9z5N0TSm4j3eYxVFqgAAAIEAslyMZiOwYA3oiFMQTJEphKdgejWsjqQ9LoKppfZ3d4Jj1V3tgI1s/wHfoneUUrUwM+sMHZKXbBDLWWQUOSIxDYcXKDkbZ1FlmhvJR+45D2LyLKjEnjVDlQCwYly4P26zXqciZS7k3H/DjiHtAPUeoHm9IYb1A03K8Bd/xW0guMc= testuser
diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_dsa_testpass b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_dsa_testpass
new file mode 100644
index 0000000000..375d38fe43
--- /dev/null
+++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_dsa_testpass
@@ -0,0 +1,15 @@
+-----BEGIN DSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: AES-128-CBC,EBB6ACAA4F1FC558865344E3C2B91A5F
+
+CWMAq20YBO8ueHnmQ7IaKa7ISEvNbwbzqoBIxor6TZYSU3JvlIf5AL2UvGpMJDk1
+fyROdCjdVAeWKQC0peU54D3YnD3am4gZlrclPMjMRnjBmqO+vnU7bTudIt/8y6vg
+gmHZki0/aceQ6QvGwGrxBezBPaK4Bc926lePujHHE/PbtuQgkBw7rhIBGKVuy0qN
+sFbC4AGnYl5tudy5RLvCcpQvpDCjnYAfGQVimRYSOsaOwTEBvsnQFUH1pqQAYLC4
+Capo1yj6Q0smzwsGoyFSvmPkyzLbMTT42m+M48gc5nuaOkbU5absqOb8cQgRVmWB
+W1HnpufqGtyF6vBK+qlzg157bhQDYMwZuubX+IrTRL67djBiSIpiRDZduJavT3zq
+iSrRGSnjnkhp4NxtJJjprDQe4VAZEccN5GWPjClbogjpsG+fmTJiNDMI88L11DrV
+Vjeaxsql31iur/xGwvmBYd+/V+Nu4v7kA4XViO/3ZIpqi8qvQ3si5hbALSX0OPnm
+9q0eMp9qfmzPvbmysq2BEenBaZDwEWYTYpcF23pjwc1EvmfP8EAYT+xH95ZhxVmc
+Sujq0VyGeIhy7+gRHZo2Fg==
+-----END DSA PRIVATE KEY-----
diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_dsa_testpass.pub b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_dsa_testpass.pub
new file mode 100644
index 0000000000..676685332e
--- /dev/null
+++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_dsa_testpass.pub
@@ -0,0 +1 @@
+ssh-dss AAAAB3NzaC1kc3MAAACBAL6YkRf9cGWhaE0z4ES6iHnP4QC5l9XInxwc0qVCYLWxasQ0oaE/xmHNZ4dbQDsme9NCAypbrIPiYPMKltHXATPkp5cCmESMIY+dGKTtDhGOllN3o2qc0X8JhgZi7BHtkZQ8hNOkolaBfo2QST5ri29NY/vF/E6rmIoiykUSEQ6VAAAAFQCAYk3QEfDNrcK12XbKQY3Saa4w1wAAAIA6tFh1dEeGmw57AiL0ZBqHSV17sBWuZlrAr1kHCENRqAvnyBLw+9yw01WFLZ0oRfjmmpmaBrK3ZQQT/luR7f4J1m1K0Ox7MFgy7w8j/SqM5QuGXvEslPlrIIg3Zm69wfy+2ofz19eBmQjPRzrwDNjVkWMk9z5N0TSm4j3eYxVFqgAAAIEAslyMZiOwYA3oiFMQTJEphKdgejWsjqQ9LoKppfZ3d4Jj1V3tgI1s/wHfoneUUrUwM+sMHZKXbBDLWWQUOSIxDYcXKDkbZ1FlmhvJR+45D2LyLKjEnjVDlQCwYly4P26zXqciZS7k3H/DjiHtAPUeoHm9IYb1A03K8Bd/xW0guMc= testuser
diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_256 b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_256
new file mode 100644
index 0000000000..8a4c864afa
--- /dev/null
+++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_256
@@ -0,0 +1,5 @@
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIAqXVKoLNr7/wNluxmGZnZmJCD/5h06ptAICRk+8FIjfoAoGCCqGSM49
+AwEHoUQDQgAEoQHTUWwu3nJnCHeSv3YE59UxfuGNjAXLzK0MjDwoXt6/qePjjKAQ
+ehHdAIYQHr9zYJu5SA5b86HL5glqjcy+Pg==
+-----END EC PRIVATE KEY-----
diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_256.pub b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_256.pub
new file mode 100644
index 0000000000..43540ec0ec
--- /dev/null
+++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_256.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBKEB01FsLt5yZwh3kr92BOfVMX7hjYwFy8ytDIw8KF7ev6nj44ygEHoR3QCGEB6/c2CbuUgOW/Ohy+YJao3Mvj4= testuser
diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_256_testpass b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_256_testpass
new file mode 100644
index 0000000000..b767c8e99a
--- /dev/null
+++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_256_testpass
@@ -0,0 +1,8 @@
+-----BEGIN EC PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: AES-128-CBC,86940587F5C93441B585F469FF31AC06
+
+LaIyzOCeBPJA6OkFOFnFfVorYO+Rm1g5QpvqEcFZ+FCuEvhMZN00NMZ5hHKvwQLt
+XSK5Se8MUD+e6qFH/ZcoYTixUqYjYJlOkxJzKaXg5nM82wQHa1LqQqcL4IDrJmzv
+qJbCLtl6XOfkQQUA6gezqhtiNYWLDZIPfZ0dsaIB/fU=
+-----END EC PRIVATE KEY-----
diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_256_testpass.pub b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_256_testpass.pub
new file mode 100644
index 0000000000..43540ec0ec
--- /dev/null
+++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_256_testpass.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBKEB01FsLt5yZwh3kr92BOfVMX7hjYwFy8ytDIw8KF7ev6nj44ygEHoR3QCGEB6/c2CbuUgOW/Ohy+YJao3Mvj4= testuser
diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_384 b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_384
new file mode 100644
index 0000000000..dc2ac86508
--- /dev/null
+++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_384
@@ -0,0 +1,6 @@
+-----BEGIN EC PRIVATE KEY-----
+MIGkAgEBBDAgAgPcgkPaitxOrphrrLe+am0eUhYi346UUTnb5WZL3164MEjFByd9
+Egv6KwB4hCqgBwYFK4EEACKhZANiAAQhJrJ+vJLbkbd9C1he+4XuxaOyZ1IqYJqz
+PZCXcKkIlgy+0I07RAxRUd75GHKc4ViyUnLq5odV25H6FNzHJHO7ifE4H6jrEpA/
+UL6LkfZReYZ4sNmeQI7MBXm2IXQsIZ4=
+-----END EC PRIVATE KEY-----
diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_384.pub b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_384.pub
new file mode 100644
index 0000000000..3e813a5e48
--- /dev/null
+++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_384.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBCEmsn68ktuRt30LWF77he7Fo7JnUipgmrM9kJdwqQiWDL7QjTtEDFFR3vkYcpzhWLJScurmh1XbkfoU3Mckc7uJ8TgfqOsSkD9QvouR9lF5hniw2Z5AjswFebYhdCwhng== testuser
diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_384_testpass b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_384_testpass
new file mode 100644
index 0000000000..06032d049f
--- /dev/null
+++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_384_testpass
@@ -0,0 +1,9 @@
+-----BEGIN EC PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: AES-128-CBC,80B1C4D6D9B45690A07B9886050C63A7
+
+WxS7EGs77p1aPZuxXW0G/yTFKAh4M30AaeGQBPjDR/HTAmPJe3irDH56fdmGhY4+
+zBT+6X1VppB+UqB0nJ/qHq7FeA37eJPXJnuskPh2BzLlBaVhmEnzZylEW33gzAuH
+XzC/Z2OjdWRjn+rBXM5fwo9IIC0WzTNpBokdeMo8tpnPzGTlsTFeyVgMZJ3wOlCO
+4ItX9ddY5P+MrLzWP672IyZZqAQGfLec4YoJ286wpHY=
+-----END EC PRIVATE KEY-----
diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_384_testpass.pub b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_384_testpass.pub
new file mode 100644
index 0000000000..3e813a5e48
--- /dev/null
+++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_384_testpass.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBCEmsn68ktuRt30LWF77he7Fo7JnUipgmrM9kJdwqQiWDL7QjTtEDFFR3vkYcpzhWLJScurmh1XbkfoU3Mckc7uJ8TgfqOsSkD9QvouR9lF5hniw2Z5AjswFebYhdCwhng== testuser
diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_521 b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_521
new file mode 100644
index 0000000000..c28151e5ab
--- /dev/null
+++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_521
@@ -0,0 +1,7 @@
+-----BEGIN EC PRIVATE KEY-----
+MIHcAgEBBEIB4zI/MCFSfJ0wfyLwZPxG1vP2o3fF7fEuOTpK+fxbDHKYz6r4bNv3
+HkPQEVTIAqDl7r5Ebcx0BMeYr9oe69tPZIigBwYFK4EEACOhgYkDgYYABAChltEM
+zT8dXwIhQD2iuy7QbaBkhWMhpFaxztvzSQqoTZvBgBsOmSr9frFA93lSQoHD1Bge
+wuwBkNGm9lRcw0tEgABqifONkj07Qj2847MKS1iiVu1sHh7Ys3YimyfJc+nZRNi+
+W03nkcdvWd6PP8y/VENoV7+BtIO9txj8Dt5LYOtFgw==
+-----END EC PRIVATE KEY-----
diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_521.pub b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_521.pub
new file mode 100644
index 0000000000..9bac1e80f0
--- /dev/null
+++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_521.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAChltEMzT8dXwIhQD2iuy7QbaBkhWMhpFaxztvzSQqoTZvBgBsOmSr9frFA93lSQoHD1BgewuwBkNGm9lRcw0tEgABqifONkj07Qj2847MKS1iiVu1sHh7Ys3YimyfJc+nZRNi+W03nkcdvWd6PP8y/VENoV7+BtIO9txj8Dt5LYOtFgw== testuser
diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_521_testpass b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_521_testpass
new file mode 100644
index 0000000000..c1c1bbaced
--- /dev/null
+++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_521_testpass
@@ -0,0 +1,10 @@
+-----BEGIN EC PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: AES-128-CBC,7070032284B3C310353B8C352AB2D8CE
+
+UBgXTwobcLX1VFtQaLNiwwVzdN1+TlmhSRCnU+kv2EpunXxfvyOVS1mZTam9NyhE
+O0Mc7REi5hDHp8UYM7MP+wrwK+QM3D2Vm2/Rh0+acd4Gu2XGACJHWXGIyKwNsU0R
+ZddusHIi+979sHw3vSUFCvuDwc9YZBoujpzls7NYEWXiAVv6wd1RCtAynkBk/uvc
+1F7iHLuRttejBPvrb/a2AxY0pFpCuCVmGjuiS5bfVWBj7xLEplqdU6/95rd9pRwx
+e2uRlU0AFiQGNPStfhjgfCWnmf+aX3vAgVqkLMYKYQE=
+-----END EC PRIVATE KEY-----
diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_521_testpass.pub b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_521_testpass.pub
new file mode 100644
index 0000000000..9bac1e80f0
--- /dev/null
+++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_521_testpass.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAChltEMzT8dXwIhQD2iuy7QbaBkhWMhpFaxztvzSQqoTZvBgBsOmSr9frFA93lSQoHD1BgewuwBkNGm9lRcw0tEgABqifONkj07Qj2847MKS1iiVu1sHh7Ys3YimyfJc+nZRNi+W03nkcdvWd6PP8y/VENoV7+BtIO9txj8Dt5LYOtFgw== testuser
diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ed25519 b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ed25519
new file mode 100644
index 0000000000..02afa54788
--- /dev/null
+++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ed25519
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACBIJlrW8XB46iAVY0XqbjYKG8wJ95iILxOb5ONQhFBvPQAAAJC8jORLvIzk
+SwAAAAtzc2gtZWQyNTUxOQAAACBIJlrW8XB46iAVY0XqbjYKG8wJ95iILxOb5ONQhFBvPQ
+AAAECjklggj+glO2K60Ptg+aXYGBdvXtk9TQnKINhrEIxW9UgmWtbxcHjqIBVjRepuNgob
+zAn3mIgvE5vk41CEUG89AAAACHRlc3R1c2VyAQIDBAU=
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ed25519.pub b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ed25519.pub
new file mode 100644
index 0000000000..7857db5602
--- /dev/null
+++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ed25519.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEgmWtbxcHjqIBVjRepuNgobzAn3mIgvE5vk41CEUG89 testuser
diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ed25519_testpass b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ed25519_testpass
new file mode 100644
index 0000000000..7ad4a77056
--- /dev/null
+++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ed25519_testpass
@@ -0,0 +1,8 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jYmMAAAAGYmNyeXB0AAAAGAAAABA4hLhtuV
+MNBBC+j45F4KFcAAAAEAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIEgmWtbxcHjqIBVj
+RepuNgobzAn3mIgvE5vk41CEUG89AAAAkPH343T+NbHb05J/6CHnF9h7C11LJDHe2x9+HC
+dNB50fP9M+KJ/cC5cqIeHm8y0fg+wX2WLlJPjNVoSd5MciWCfUWO0k32ciVpoyrGCz5Gh6
+axKVVY42QjdgO0S2QxWClnAuMdkVdl2ke/PcGp4yqTTIruAAB0m3d0jZdKNT1Vziww0rQB
++DOo7xQ9Tx99U+rA==
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ed25519_testpass.pub b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ed25519_testpass.pub
new file mode 100644
index 0000000000..7857db5602
--- /dev/null
+++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ed25519_testpass.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEgmWtbxcHjqIBVjRepuNgobzAn3mIgvE5vk41CEUG89 testuser
diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_1024 b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_1024
new file mode 100644
index 0000000000..0b403674fa
--- /dev/null
+++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_1024
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQDGfj0Jmqj+CUb+WdFrlkRV49TJtNzvvMb/nX20zqgGm50cOIYr
+MzfFpSQN630pXeAidIgiV/PWAsipntQfSWPRG+RpB/wMKHVUNPJCJkjjRFEa56Yx
+gAhgNwF511K13x4p2tEN0r6wsfw1nos9VoO8XDBAu3lellAgBdufyCt8vwIDAQAB
+AoGBAKU+bNP1BGDQGmEfJv+5DlSuofP19MREVSpx0zfVnv45SFc5G0EVl4Wb0GMi
+O4VXmIM2nipxLBZrJOBI0HDnaQcx1zQR6tpvBO7BbAU0sflOvUDldUStTnz3TTQW
+2ECm2y8bsArNqkeLndqis3ICmYL1budhDdUYYcqv10IlbjPJAkEA6yE0zduCE2wM
+Ob7lcqiQCOiXeZ0KijHTmSZV4Fn4HRbp+XuxUpjSWFaoDTO0bncGNE+JYjywe64V
+XvEORb1hTQJBANgcjEoCrUFY7VYWx3f1tpN0Q6jwwcj67Sd+ysaZNgghTPU32GTa
+auGQFv+tifUQMyyVrhAfZ6s8myKOH5SWUDsCQGVvqOkaRq58UXXkDfZ+E81UEm0L
+u81Mm52ZdTjZd3mNNhlELIaWmUA0+kDfynpRbOLKYVl5FyX0PxH7ao3Zmo0CQFpL
++1YFLk0KkggRdoCp+wI7ZvXUurN2HNcOxD0c0RWujFA9aD4jgNsEcIeeA/GQNkGf
+vN3hsVg793oFti5Ia/cCQAubCMvRqFTyXUBervPVC0kibO3OwYt2xN/7lQXAVSfm
+nRwV/46trioV3rMF84hpOk/46Qe5hqbWyQnL+dZljpY=
+-----END RSA PRIVATE KEY-----
diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_1024.pub b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_1024.pub
new file mode 100644
index 0000000000..4aded97ab6
--- /dev/null
+++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_1024.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDGfj0Jmqj+CUb+WdFrlkRV49TJtNzvvMb/nX20zqgGm50cOIYrMzfFpSQN630pXeAidIgiV/PWAsipntQfSWPRG+RpB/wMKHVUNPJCJkjjRFEa56YxgAhgNwF511K13x4p2tEN0r6wsfw1nos9VoO8XDBAu3lellAgBdufyCt8vw== testuser
diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_1024_testpass b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_1024_testpass
new file mode 100644
index 0000000000..0b66dc0aa9
--- /dev/null
+++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_1024_testpass
@@ -0,0 +1,18 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: AES-128-CBC,4B8025AB7456E0A2B48408407C6E3FF4
+
+B9gztX+5QQPqMR/79eJHxjNdo9baoKjfWY+Ye7t1h7ucOPMCEXRSP8FwPwBfbzQh
+6W1AHOfiDCHTzArDRG9SXrFfRlU+8o5ffs/TStTNqde/AXJeNuM3pwbmqKV1m9oY
+oWelabmGtNUvGMAHMFm/2uk4BgS9Kjv71KnJg0cQQfIiPKTPBncJe/R5mf6O12rB
+ByOrrlDmjtgveZZsgggEZbU9Y9DYiHZp6yT0JepxIWNImQ/A9EeUPTQheVB2ECT6
+DLUOwRfyFhdvsfD2eXLK+u7T47keFny3rIfm1e8HC1y3X+T/nFxKGoShecx1NmEL
+HMgOKyFSwGSZh5jxE66dSQoc+rRZhCWSyPJEb9cjwp8JLON8oH3Yg+PIXYJhMFK+
+nghAIVXp3/H+cYXMN27j21cRGC7ePuF3YX242Gr+LSj42Wf4qCMTyvWur8WrSe6U
+iyrWJ8+w2J7O7rRHGM8v+GYGaiX1qIXFheM/774vsDmjuueOhkjiqs254gaap8xk
+LcJUuqJU2AL21+eW+R+EG3Rl/AbMIaQ4GFDpHfgEmmvVVoOvJunNQkDIP9JzKczO
+g7cN/EYLUC2TcdmNaiunB8RhXMiaTqw4kYJEzy4lsxk/xjubC7vlQKTvtnWCpob+
+WpHX/2FBdPPULt38AIk4HQq7vKvKw9TmvGeOvQmCUun7eCFFhxKrwNKO5YCXAHvs
+fv7JNGfrST4jwbqCvamuk+XTf0GkgJN83G7DT04EIzee6wwai/NRDybgYptJsj9G
+6wBpKH15BtkktuUzM1MCt5+T6Ccsg+d6xE6eStimwDxkXCjvgz/KlS+sPKe7uS4h
+-----END RSA PRIVATE KEY-----
diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_1024_testpass.pub b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_1024_testpass.pub
new file mode 100644
index 0000000000..4aded97ab6
--- /dev/null
+++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_1024_testpass.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDGfj0Jmqj+CUb+WdFrlkRV49TJtNzvvMb/nX20zqgGm50cOIYrMzfFpSQN630pXeAidIgiV/PWAsipntQfSWPRG+RpB/wMKHVUNPJCJkjjRFEa56YxgAhgNwF511K13x4p2tEN0r6wsfw1nos9VoO8XDBAu3lellAgBdufyCt8vw== testuser
diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_2048 b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_2048
new file mode 100644
index 0000000000..a2d7d62310
--- /dev/null
+++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_2048
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEA1sFEWiIp8SVO8/sDhKJ67O7tQdtDwsqWi9Fm238tAuy26OH+
+ylireX/qVcndU8Yr0qYShcwloEUaeNe77VgffZa2ZIUee75u8u+WGCYjea4RQ1bZ
+tDcioWkxl+xYfVuuKaA8CQn47XUdyoA/5P3DpDhaJl8KevaYupJNHOo9Lt2E5dVT
+93OksZBOQ6E3nNlsefP/hnFByiczlde2GIXP2sWLoxsiVsbI+CLeGtxQxPubX9yu
+vWrl/nv/yERR5ZBOEVY5N2+1BdT7DvOIMg2q60FpJv6zZpQi7Ov1iONMVafytIRW
+Ma2rPkpS83Ebxh5c92T3rgLUf5DcZjKvBgxtpwIDAQABAoIBAHeDZv6iNKU3FhFB
+iFuv8Kka7n7P/43QIKf/CTbuN6aBBenkm18QqZ0cStUjWkDc8FZyhaxgSDBBRNIr
+fTJA8IV78lVOoABNooEgRG98ChIVhRXsp4tbg7JAUJEzvqtE8k/IFKETI61CmCmx
+5d0SPGaP1du02KhFxAlQkgmdch85st+tRFv5GZXqiKbR6QlNaJgIXIoOlykVvnz6
+rnl6Q1SDutBOKGC8xFrDzFI8KxLFe3RFQxtHtsLRPcrrpukNSHICTMO4jtGXSZ12
+9Zh29ZtkouCDk+b176dGrJKfIBbxXtBGVXtkuo7rj8EWVWrJiiYbL2hcWD+Pw1VL
+0GWkaEECgYEA+RrE4nVkfdJ0Zgx+sACQqs4uKi/JuFHU69JnO7RB2lDwzQbIPKl7
+nn0ExJ4V9m035/3mqKReBIyMIjIhwXgLFiakNO/+GAWa4ycRMB3pV8WaVFCnWZEF
+oLRg1ukoLs01TfOszcux831n8zmPlz/NLTTkC26O3WXsVmnCSlPXd1MCgYEA3LMW
+B8ONEDFYACB8xKA5zn3jrKq/yVFfiQzEO87zSkgG1mQbsb5T8jggWiIHVyZKQUSk
+8ZkrwBKW+LwyRik1lVwawALmcvvN3VyCW5BukniErAUu8jb3+R2aFdrjzpiNqzMF
+M18BPDElirTXMjJusC7z/0I7+gyAu9ttYJY1id0CgYBqjIiqVIwnRV2ESNPndFZs
+uMQGR2qA7H+mXtjJMND6EKTvDXeYeuXlZJQlhXjfbtf64x9GAwgz6eoGtmq51h7n
+2p9iBUUqATu+7Xbsnd6xLFRWvCjYpq9BjeXeBtypKB0kupWvcPEstPdBkd1ZVHDu
+ZTElsqRpDq+IRrRUFoiTAQKBgQCxTGmRWSa08H8asv6o03M9EONbrlyedXHDXu8y
+gQHwFcbwasHY2+cCetZ6skWlXIxgvK2prXx5NDX2ovHcbXSvhauzv2C01NdAUvYi
+avh5ULp8mzlouoIhrgdAMXW7XdDJzRYLe/I5Ed5v/PG4UM2dWksIMISQT4UH5bKL
+2oAuPQKBgCQaJ2oc5qE/f6MiL0XfGSdY26gOZcVrm9L1XKXtyHkfj4xWYQ58DSYa
+vNZH3fGyfR+q7g1WgUmLib5etOjUjbVYRjIEov8xLA41UZZLNGRLc4VzgeCT73CW
+YvbxeN93fL0tgvKeyNVzIsWRazHMo+aQodlXvpPckHXYxYHS93W+
+-----END RSA PRIVATE KEY-----
diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_2048.pub b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_2048.pub
new file mode 100644
index 0000000000..b787e36483
--- /dev/null
+++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_2048.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDWwURaIinxJU7z+wOEonrs7u1B20PCypaL0Wbbfy0C7Lbo4f7KWKt5f+pVyd1TxivSphKFzCWgRRp417vtWB99lrZkhR57vm7y75YYJiN5rhFDVtm0NyKhaTGX7Fh9W64poDwJCfjtdR3KgD/k/cOkOFomXwp69pi6kk0c6j0u3YTl1VP3c6SxkE5DoTec2Wx58/+GcUHKJzOV17YYhc/axYujGyJWxsj4It4a3FDE+5tf3K69auX+e//IRFHlkE4RVjk3b7UF1PsO84gyDarrQWkm/rNmlCLs6/WI40xVp/K0hFYxras+SlLzcRvGHlz3ZPeuAtR/kNxmMq8GDG2n testuser
diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_2048_testpass b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_2048_testpass
new file mode 100644
index 0000000000..7b3a3f4e58
--- /dev/null
+++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_2048_testpass
@@ -0,0 +1,30 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: AES-128-CBC,DED10D02EF74A02F24F46AB44A84F4B3
+
+DL0M2dNu7cXNLE6KGPqEt3pSKiQw6ajlxYaaXyyAwvpZB0Pv44HUjXfhMONs5FH5
+rDjz1RSYDRdMF/h6FtltdEEareXwMtRTvP2wb0gsQKiYS5M9WeebM3TY55JmwS1U
+hhrPrEaP6hs6WEy9xp9DVxJN2y1MA5iss7M+fQ4/C6QeSp9On6bgCEvNPwdMTS4A
+3sLp+yzRvrefQmSi2SWbJoYlChitOMdc84iDJXDo951QQLX75GqMm41fFLHHrcTO
+7v/k/D7p/KLlNf43Ru+2yPNE7qyEK0pDSjvPnjPykIa6SWq3Qx2DnVdtZ7bWF8LA
+B349QmuE1r/YYHNvWnp0/5SztivJk3NMeTT29PIiZoHioo53Vtru6RcXYMOvHbh1
+maioVkgRl5gkhLC86o4V+3hiJQNrVCWMuT+lxLY2Tt6bFXulbf3WH69AEAFW4S4a
+e7zH4fwvkSwz+bFxg9B+Yynv42ke1a+tvDI+aDvsMmv9JUCy6G4Te+isXYxLdtT0
+nyqJ+wwP53AWS8gOvoUXzxxsEchTDtQnBQMWuSHEdFrk3OLGykNN6vZaxUROxpJf
+vcPl7JniWGhzDzUdHh0AQbLxXoZlv4YU1uO/+1OnrvIkuO5DCDg8v2sTFRW6sgiU
+JXm3QPJiU/bu3/FJ4XCU75cTcunZMXsL7TY9mURq7Y5FxcByuvSL2nlA7KfROTVq
+I6w+Ej+r99C1u0G63sk5b99Pm4cb2+V/sr7pslqlU9Yw1Z5hw55ibih04CiWZAhJ
+Az7s8ho4dY9E1n/XJSe26p14RPYU+w7WZuN6Xb04t3+BhF4Ubbsdn6F3lAVOrrWH
+6xNoncmIEYdfdcI089UPpV4/bIpdakXRIbaLmpshyU6aIRUXqYkzwduXcHUrxgq3
+1QCZHNvq1+9i5Wqj8JP8cZrq9YVldOeXdIIsm1SSepbDQ7820d5T4Dk6cj85BXYC
+6t12UNZ5mhzTvIAqbR3Who53jQ8cY0MSVXR6Jd6vPih2OhAnccnuJmRCNNJkL4mg
+pVcsSgYjoUx+w6Ou1muCIkkGpdEhLLwEnKFc0HUmPBToRqgiB1Aec+7oMv62XhXe
+yA26/dpT6N6SWYKN7MyDWUe2ilkmjXI+JrPCH+/w4FXh+GKafOn8XlcBnRWHVBEX
+ZQfYLckd1j9B6p7By7ed2H+8FxZLz3gthcSxRG89IP/EQImY/e9A3aoLrFX6C/W0
+Gd6JrIvzC2bZCvrq+VTYs3101j1xe6ZDJnq68HokjpG8P9DlFYDOpRetCjR7TuqN
+I5s606KAsGkt/jfbSNUMIEtuM0AC75m3TTJeWdfYh/PVYevUC+pUoreJ0ZsttQ2i
+D550sAAzU7PCzZQsDF1i2jv/YZ0wXz7+C7YFiGNmb3HmXH0Lb2HISJR5UL+x+hHY
+RArXtVubqjFz179pawzI0n03Z1OXiHolwer7C+Twmarv7SPe8rMU3HcHP25JeTAW
+mo0PxNGG3yQPlRZWpPz8LEWGo+fDqfA4kbqy4+Pvo7B8YFIQyE9QG+oBv+/7uqMU
+UOs1ZqsmvEUmvWMeQnWsjETmHKucbmTBm8ktsesb3sCKfY/pf8hAHbO6+9J3ebYf
+-----END RSA PRIVATE KEY-----
diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_2048_testpass.pub b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_2048_testpass.pub
new file mode 100644
index 0000000000..b787e36483
--- /dev/null
+++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_2048_testpass.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDWwURaIinxJU7z+wOEonrs7u1B20PCypaL0Wbbfy0C7Lbo4f7KWKt5f+pVyd1TxivSphKFzCWgRRp417vtWB99lrZkhR57vm7y75YYJiN5rhFDVtm0NyKhaTGX7Fh9W64poDwJCfjtdR3KgD/k/cOkOFomXwp69pi6kk0c6j0u3YTl1VP3c6SxkE5DoTec2Wx58/+GcUHKJzOV17YYhc/axYujGyJWxsj4It4a3FDE+5tf3K69auX+e//IRFHlkE4RVjk3b7UF1PsO84gyDarrQWkm/rNmlCLs6/WI40xVp/K0hFYxras+SlLzcRvGHlz3ZPeuAtR/kNxmMq8GDG2n testuser
diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_3072 b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_3072
new file mode 100644
index 0000000000..10d622c905
--- /dev/null
+++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_3072
@@ -0,0 +1,39 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIG5AIBAAKCAYEA1M84bePnK6cR9Ei/H0S36QhdfUl0qUIZXrHNvS9i/npTZdN1
+mCzWxeNHm0YJQWpn9AqPZGG/dPGt3CQEL52TKXawY/0ks+4p0JJ9260oqVBFJrXE
+5latVQCdIZ1GR2iJL3kZLXHXSkURygEL9aBfOEUSmC4SkNY0LOGuwMZ2TyXiFWHL
+Y9le1DU2UMbfk65+6LgzU+FKzO4sg/zZD3oB9A+n+ozSZv/YEMuPvUAboMUJru/u
+c6D5UxhwJ6GSKNkSt3xJUKnsohkCbRAq/ansvVJqEsgZc+oKVFidLPPz9rLjoEl3
+w+cUlM0TbbXaqtFXCoE8S6CAJG/G5Rkfrw7bUUkjGbYrVqjR1W32dg3txzZMVojI
+zolB5LWtsbZY620b+hHk7Vh+F3Vw2yinGNrPDVnVMwB+pRCsPkWSvlLvpR4C7xqq
+iEucB1eFqwWSVhfDgzkvtTiaMJ7M7YunJ5pFWjNd0yLZNgIa5SESzrn564wwjcwB
+bMVifimMp3pvFBbrAgMBAAECggGBAIGcT8cGFiaNE69Pmy/FH6nLUX1b/rSTsHXv
+HtpJgSZyhFaxKp7rOEe//D3CsyJnVzbYM6s0qXHlPDmmqfICK74GLrpHVFJODKOe
+hQ8FcI1meSdxb6HGSr1JqWnuqv4U2fDS9ZWrDy+Jz6LTbmBEM5pG32NWNDKIc7Ce
+J1v7w9TCwua48DI5Ert2SUV7SnJcxaihf4ln3rHfobcliWIWshfebTV5DTB0RDk+
+caYW5HzPZO1p7jX4ZcHJUY2hpy4/vjwHLNLhXBV75bkZwXZJGaITD+uDAbQIAb8g
+T401x/+YZlNWP1kK1zu5Mo9cCl5o4o4oK3FyLKUoCXyYrahTfmbHgVc/toiJ0F91
+BMUWkunpz+B9GcKPUkBmu0xGTominFmz1ZW/etpufJcDt8B42kcoDoOsQMl2B2CT
+zW7Bo+R3hFD80I4tIAtlFiKuKwKlRBF+E405yTxjlUwefczshWSSeppPZfxFwSQc
+3Q07RB0MepIZ0w2RqVsG1rkq/GPaqQKBwQD/wAapoc02U034Id2ny3HALiV1u/io
+Ve9r7oq02oltrRa9bUydLF9jknInl2p64R3x19JwIgtBK/AwDb6KKnrVFfJhw80W
+TlyVvls0b0jmohhYcn/5EY/ROg3ex4eySJIvJYZDLDWNAToMWLr6STBFXQdaYHIf
+BleOsyO/ARoiKZdtJB5Foes/GFwdIo5tgJgfZXw3mBrcF+UVIhyd9VRotg2ltIHX
+UvbF0vanm+nN77g0dPAYz+p7IYQbguZANqUCgcEA1QRz/6KErIfjly00Dx3hMf+T
+YCTe2Z0IyDex9b15tsF1C/sFJ7P3HUel198Fon86Wmc6OCxhfAHyhoTYdpaCwgGp
+2rVRd6flkABW+4koi8Kr0qXOnjAe54LcXlrZgo7/iDUjEMjCXKOkirXW8L1Uqk2X
+nuXJq3Vp7iexBaZCRe7Q2kPcV03QXA3r5sph67SsjJWrEVSll7/XaL1RBiTDRXHZ
+1aQpnf0DQnvdHnnqrwewbrUxcEBPVq7faoWPizJPAoHBALdNbnkOWwLg2jVKMJAf
+JLxVVsv3mdUtIpj9M7VEHNPbBz1lpU/RidzYDbGKuOqxhsDbqxxrih1/3HrUnwhw
+QfGP9VVU/R1LtNguwzflux5yd3iNOGPPzoBrV52g7QU/NmdMQdrLSOZzRqOqxPi2
+lD5i2u5Pyfuqk/7XLnur0otBvCKhjIDj+LQURZEsP2EElgOKvWkrP7UX+z0WYeRk
+/ca/FTD7G0S1VeGbvuWKvhy4ABK47Y0bGDiAYStGurizcQKBwBTj0ehg7Lfqv6QE
+t9U/reT0VmSYWQ5oOwM/iwE5aqVEhZD+Nfw1xuclLptj8K6F4ZgaBXiayZiarEkK
+4BuJGRujhB/BplKgsX+UuPMD+WjzV1xaDFAxEebMS4YpTKlkEqUt6NlthroFBk7g
+FEsZliL5ZwQbLtLUueW0GMUgD+HB0NOG0iXxqJxOdTL15/Jwjnde+h7B+VdPZfWM
+k1SR6GB4EM/FwJsQw/ASK5YgiKZPj7rbpBSJCf7LOXe9z1zsOwKBwFZ3GdC9arW+
+AFvXk0TuF5xjq0WuTDmEJn8PI5HPAajyeNoAnp9xwUpMnklfT6uk5ZWKQUJszbtm
+IFaNUDXwOlE/S7Zf8FXQsoUz7koCs/IGKBBdRwK+Hh4e89Qme3nOU8I66DWxeohF
+t0zuJJaVCUdJdEW6HbOdS9/J/zzIPeL2kQU+lvD7FfmN0ynFcGi9M8O6dEl/2L/0
+FmI9bz1F+bExm39yFXnY4lsK/gTVdkjyeEK7T6Fg9PFCqxhqh0lyww==
+-----END RSA PRIVATE KEY-----
diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_3072.pub b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_3072.pub
new file mode 100644
index 0000000000..686d3b06b1
--- /dev/null
+++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_3072.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDUzzht4+crpxH0SL8fRLfpCF19SXSpQhlesc29L2L+elNl03WYLNbF40ebRglBamf0Co9kYb908a3cJAQvnZMpdrBj/SSz7inQkn3brSipUEUmtcTmVq1VAJ0hnUZHaIkveRktcddKRRHKAQv1oF84RRKYLhKQ1jQs4a7AxnZPJeIVYctj2V7UNTZQxt+Trn7ouDNT4UrM7iyD/NkPegH0D6f6jNJm/9gQy4+9QBugxQmu7+5zoPlTGHAnoZIo2RK3fElQqeyiGQJtECr9qey9UmoSyBlz6gpUWJ0s8/P2suOgSXfD5xSUzRNttdqq0VcKgTxLoIAkb8blGR+vDttRSSMZtitWqNHVbfZ2De3HNkxWiMjOiUHkta2xtljrbRv6EeTtWH4XdXDbKKcY2s8NWdUzAH6lEKw+RZK+Uu+lHgLvGqqIS5wHV4WrBZJWF8ODOS+1OJownszti6cnmkVaM13TItk2AhrlIRLOufnrjDCNzAFsxWJ+KYynem8UFus= testuser
diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_3072_testpass b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_3072_testpass
new file mode 100644
index 0000000000..353a24c206
--- /dev/null
+++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_3072_testpass
@@ -0,0 +1,42 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: AES-128-CBC,040847DD0487D72145EB88CB09486EB0
+
+2vC15lwRqJvaSU+yYCmqerJft8dqlrx9EK3gW4WtMW6C4ebqlj5DkIthSvJLgF6O
+wufFV0hgmEeOhLEIrdJc/FTeD6VsSBYHTttoMeQ0Yb0eETnLhSbFO+9NRvPBpT4/
+EsBozu1m/fnv14qbXtgiX9d3zRR5Il9Q/TP9/MO25QO0/7SLHn8ar255piZobBQ4
+xqW26UywI9pUMjcfgroE4PYZTqTPY8xGFBeOIXBGuw3m4geKcOMbiPehB2o7gZDJ
+iC2conFycbi0xUBYytnRO4BboB1PhFnh4CXFqAkJycWj20Q2iFVliEXEey+Qyd4m
+vu9Hr1sp+35kByS5uQ7UfDgBcoo25JKz3HIcqFrSzJ3cwRuRrj27eydojR12o4FI
+Cd06GTMq6khN3lovVUaQWlE1MLUpT9zT0rLzJylZ7fgHi3cTZ9n5Nr70vX8+pvFA
+mzQ/53nvXQkiKfyUWV1aVypNsl0kYEM9+6uLyknyUPmLDOGxwAz5bS2xp6J7BKku
+PojN6NHChyqndHArpR6EUx8RYCQV7PL0EPCSVlyiscetNBfTe9+BzCbPisorukQT
+EweviRMUmW/pdr4zPuMwfZQSzRGYZ+19sIfV/VsRvgYvTUqUZ4mvWQbyiGpeLoM9
+W/bAJrqJBgfMISw4n+j3oVd0HJULWxktZGD8grLsmeh3Yjk5TCXcv6dH5OGx66nR
+ATMjEinVcwop+z5RdlaP48Lw7/FfaWTiOln9O9DMT1pjbyO01qXHCKvo+TnSYryK
+SqqaomMm7vMQMytxPPZGuiSCKpaIWwfMLIzreFw2LdvzGEF3wX/SBW+8g30hwyfq
+YKrP3ZXe1g56oRqU8S2dB69rkap4nljj4HSXvIr/7XNQpkKlJX8yOAncGUcXfBaB
+kIytyAfX7Xfibk8uPnDFxL7JEmCMR78LP3jYLX1Icl7lLdbUFUfxb2WM6Fng5qyX
+Ffggcd7gucydjFNKR/KYlJVCIfxJTt9D1tGz9MT0sk7hSEIlIPieG2VkKEYKHbUj
+UHEwbPbeFxm9INyccBAdnCvqfJ5ppQKB9TrZliPeLclx52NlX+3gtBErneycyzOk
+oQmFtV+Bqg2hgH8TDLenGmG2xJsviuNTXeAjyZFLXkE1kFAPEKmz3Bys/tSJ+NTw
+mAQxRnZ14BmO11o+/3xrrA3FkxiZq6hVUOyUZ/rejkbMTXUb81kyJe5o0kgLnQ8p
+EJGi6tQl2z9YPQC9wWXO5ssu+Q+5MJ+H8YlvV6oc0nXUcLq9mgxPDPRBu33n79zq
+mKymh4jO5qTExqnC6lLOsw7YVsss91opBLPGO8nXtcRvtqiRGwI+2D9kUVHH4J9R
+dHXQaVXgUGxmhJFUxHEEckrT6NN923uY13R9Uw5Ifmh3XHob2hFQlbBP3GeiwfTI
+DlNxIEguXxuZddJD2Fg/vLn5KNzkCOlYcrvoa+eH2jzcLN94tLNjliOgX69eERdt
+qjz3x8Xoyh/bWcrdw7LZC7PtjwfLlkoubUVtOv++ZN4iR1XEjmEuyzibOUTQ8Ydz
+ZwUXchQKupTxEGgIJ6tl7NGXSjA/TT1KYQUgVil9Uv5zZbOZecFClFF+1Vcmuzgd
+hLzWG1DhZzvEAI3whQafUZf2BuyfYdnS2aKjVYR+k9dCTKAIz0MWOl29BC7/v1L8
+d7uqonqiVhwfHOjnUH0cD+QRM1i63+Luyo4c2WyCnQ7DFOfs5l+SwnQL1Lxu67F9
+7lGr2g0l721hBTaUKMETrTjNSz/OBURebumgMtr+45K5JCj8hJ2NFQUbmqkqhyf8
+f3niFJymhtywyUPafsodRbQhKMVg4TYVzQsRnpdsQ1IOFt3vcZnRNVuv0Y4bTXH0
+TjdwxAxtxtulvE6K7esXTQdElW+yH2Fkq2edHsxquf7PoMhBLV/myMPq+4inrLU0
+rr+Er/yYLZLdolld849WTtYdDB1GwcPQ6PmuBTpt5ccoFQDvK20U4uG2EswpVkoY
+YCWf9sUnGwZh9YE0h6Ag0IY13CeQL3dsiua0+xsVEOiAZ3Y6Mawb7W0VZPHo35Kh
+ettpfjDQUF3FA/J7hW0qa4soapbymbtlkOjdQMe3tOV28ElWe2ve/TmDvUtVVB8j
+y0vjRJtwkcONM3CUuOiJPHKFvKwUBAC+7VyvRy2lRPKYVZibIr98fyd6BXsP4tD1
+R9e+Me6Cq2UsC7ywii9DmkBqpSP8XBOMNdBzbDN9gPnQzGx8oXo2w3mZZlfJe9uK
+v09UMglCxrYBDw30MEfoF913crEofxrHRSzp17tFEB74M/r7OmeegSCD8Ud7twH1
+mpnZRlGanu2DQrEmhVpfJxjn7pHPmolJsQirFfVY6wCz5UQ7iXRV3LILnruVjpIZ
+-----END RSA PRIVATE KEY-----
diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_3072_testpass.pub b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_3072_testpass.pub
new file mode 100644
index 0000000000..686d3b06b1
--- /dev/null
+++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_3072_testpass.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDUzzht4+crpxH0SL8fRLfpCF19SXSpQhlesc29L2L+elNl03WYLNbF40ebRglBamf0Co9kYb908a3cJAQvnZMpdrBj/SSz7inQkn3brSipUEUmtcTmVq1VAJ0hnUZHaIkveRktcddKRRHKAQv1oF84RRKYLhKQ1jQs4a7AxnZPJeIVYctj2V7UNTZQxt+Trn7ouDNT4UrM7iyD/NkPegH0D6f6jNJm/9gQy4+9QBugxQmu7+5zoPlTGHAnoZIo2RK3fElQqeyiGQJtECr9qey9UmoSyBlz6gpUWJ0s8/P2suOgSXfD5xSUzRNttdqq0VcKgTxLoIAkb8blGR+vDttRSSMZtitWqNHVbfZ2De3HNkxWiMjOiUHkta2xtljrbRv6EeTtWH4XdXDbKKcY2s8NWdUzAH6lEKw+RZK+Uu+lHgLvGqqIS5wHV4WrBZJWF8ODOS+1OJownszti6cnmkVaM13TItk2AhrlIRLOufnrjDCNzAFsxWJ+KYynem8UFus= testuser
diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_4096 b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_4096
new file mode 100644
index 0000000000..1a10b388e6
--- /dev/null
+++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_4096
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKQIBAAKCAgEAwzbSXgR8dM/EU36T2lAKUoRlojKspPhKVfDt7N3prGAc2L6A
+P0y3G1HLLgKPK29S0Cydcqyl694ST+uu9qYzDLQlFQHbxIG76POmHXj92bF47lJU
+RNxi78hoEDnZWtDG0rsUCBD1I4z+tXjWV81pv3BqVg5ilR6uqNgv3RzXj2jL6Q+3
+zwXxeMw7jJ3Tuukhf50hlxblH4bBIOLuZyb4t8R4EyXmrAPupHaUZSiwbxaDrV+s
+gdu/7G8dnyB0dVL3AUNUEp7Wrh2PewnjgUcNQQmyJuB98wEP3+GrTsktixjIEmCd
+e/gfDsl5JxBzzbUFtlQ8JVOnn9JCQ2U37cRggsW3yojFxGCU+bJaXz0zSgPmfR/r
+T7oXgDKR76JZ2VSTGuAFrPcdRyErPg4PC6FtW0mNxn2S6RK28s6xpTDywEDu8ETh
+lKIXGnN9XDX26gYw56ZlmAaJQ6KQP/F0Akf8nRARzkPJtIa21iBrUHRXLF7YKnBw
+LyCUgTA3WSDgNdP9Ga7+6JC5gGPW5KGIKoK7SZY9LxNoV66iglp0nGEM27ZU1raw
+llwcJAzkbSaViD/vvrIiuz04s4+5K+rAhe8CU4UTBWUJgUvtTSV7d/SBfFLsQJI/
+W11n9+SCIbBCx9nON+xkMkMQVyrMPWoD+oYRx/wXGIO2qkkPeegGyb8oKYsCAwEA
+AQKCAgA7qPx/yUUz+e9ZSRzsonuVHmtlN7F1tYAaZciBFIz+pl0KjKXrMonfao76
+38NbleksQAZabpNC05qrHC9bqA1/+2o90lSU6MVB+3ywEzMZndiElVq1tNjzyT6s
+ftGDpLyu2IfVs0EH/WY2ldiD+v4viK6m4DyWsErWxUNTgyYJ6RAwiSI2ve0/asNk
+RTPZMriPJLmIUHHzwZ4ya8hKdCmdGAlOaM3nkkgTsT3G8LmDKdFSYiP5h+xO2OKn
+qCaPWKyukSIXkr2vds9L3gjOkKVnVAxDP2aepptwY6qUKH2nvgofO7HFml37ie1h
+1/BcVM+LGpFLIxbejEa+DCgcnWCU7VbWRSvU3TeV0uTdrGBhKSHLBMigyqtt4OTw
+QcWLd9zygDO02Jm9vlMO2D6WmI0medbgXPT+vwFBXvt6/Z2sNf2zW55qXn7yeFlu
+7/GiZFIlpH4jOw6U8uG6YV7YueXSaKmbeI9hSB4d8hrRqud0Ny7fu6m5+/GB8Q6q
+2cZ7mETvrNmISe4waD9xk4CP7NchM0LSU2RWP5VtZAHEM2iIYin27aI0GjdhEm8Q
+oc5fU+kGJdLiMZ7IaCp2tZZ16PLjtWXqdbCgqjmdp8jwtwLuMil9XAFHm22jbrnP
+/bFCnlNLcknH/csS0jVxZI+nunS9UgMZVCudvJ8lzY9LDlFUcQKCAQEA+b5tSOfC
+EVdVY5+9zvx2glvQRxqN/5fonMTZXK1qqVNcbxb5tQ9I5uBQCykg7HJ30ukgK00+
+qbGCc64l1XNu2dFFXKJbSOV/8Ts5vzfmgdwZoC+W4IwojRQmfyKCwfIsP8IwrBSp
+IlcO7LMkHCnlmRPPMSPeQ1NB/N3mnilz0I5KfihahziKccCTGBvpESD945qWqCrL
+ynHmuEyb1zvwU8Z4psrfiP/RosFjItVJpsQzeVS3CGrTFe0b4PzrIQo12wPXhUX+
+um2WMQYoBVZzcrRSIH31RY9PJ3avbPJC8RqGBAZov0Zv5KvpZcL1EeDfBn8leld/
+eCpqIheDiOdewwKCAQEAyBq0DF6Qhz5Rl7CJ7BxmaN/CbW4aHw9m9dfpNVqqi36Z
+ODfpb0sl40QnRLeWByfDj6BdhTBc3XXcIDVBjsstnnX1IAc3PZgzaONrmDaoIUfi
+GIROql5l86tMSjuW53eGze713z86GhvUv19r748asaKTepXgssaY7ppXCZ42dKt7
+0euXYyJSirMmO+A98wOtqamKf0X2FK/ZB7CyfhLFskHEVO2noojvZiJwAyz8zvm/
+GpOArbRTjEfg2Sqxk27CATVIVjVc4LBzsZc9mzLKVb+Cs/sZa72gy+gLmIM4ItID
++FPW8NbeZmVngiARJcIL4alxXXy+p/uXBILxhuLtmQKCAQEAzzlF3seGzPLFRGuo
+iBYNk27xa/5JsrnuZh4kKXUvWp5zxS2wNp8fI4sef5Q54Fe+uv97FNL8WruSfcAT
+XoBwi0XMoueIjPz440X8TYDpv/jMPpEeROWnRCBjLPyKuLjkJGdSEYb3LCpGlPqz
+zLaq7xBzy9dyNjTgPRw2nifRFEzs3K9JJogwv+BFbSzDf9X7NJ7xwUn5XNqT0Xqn
+mLkAWdMGC4esYTW7UavbQWzutvR3rYYwdUiGK9xZVJ8nznt1YmxWqRwCF9iUVctA
+6+Tm2FdtCc7Z9ETMLfeZ6fE+wGX8q1xSD9w3PeuzNx/ET3hiNjbL9y6g8ylmdTFD
+kBZDFwKCAQA2by0zgDYI1GcVwKyEUmV5egVGB4GLmYEEt6t1HCjwsYu0w2D5KZQw
+8sVL6DUj1SlZ1OIb7UAV7o3nJRWkZpkOVkBMaioY02KI0fTe/19VTlyvFq7fobZS
+RvMF7pfqd5VwR+USyfxgRdnmBWszS9aTJArCeisZ9vR7U/kBYMyniE6ymEgia5/Q
+o1NvTl0L0qBXWwuV+84pany7ntGvgiPNjh5+i/fiOyYEvrGB66cKFt5puF504m0n
+6BW+feK4nJSiB4CaEwIlDVsroFzd7z8jfGlt1IzhxkALuCAPaQLIViFGWGhMM+dk
+K4mw2FBR2SuqQ5HXQKwMvmAilgxmCS1hAoIBAQCykRU4k5qTxoNWfkYz9oYxsLUt
+FnyBoLxAzGrzM7F3fImVjetXoCow2xRxHnsD4dns7OdE3VbuJrbUDFdvzkEHBT/i
+MFJpaF/zrdnKA4hlQ3omccq+y0n1wLcG5LoHMoKoQQNHPO6G+Wf4uA4M9+p0ImH7
+ajEf/Rs+PC3cqKuvJdoFpSOseFNwAo5Vbc6N9nVgFfuaZ95puKgq9BzdCJnpK0Ss
+J1K4VmpE98jBMYiEAAVPBdLA01nBiAY+Nwdkh4VjAJ46E++5pofTm4xvYljxIoMl
+v7FbW0X6S4azOtIrGJ6EC2mziz07PA2Ad1zf7yPWilMfxC8mNIbS1pAmcVoy
+-----END RSA PRIVATE KEY-----
diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_4096.pub b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_4096.pub
new file mode 100644
index 0000000000..3c3c16feeb
--- /dev/null
+++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_4096.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDDNtJeBHx0z8RTfpPaUApShGWiMqyk+EpV8O3s3emsYBzYvoA/TLcbUcsuAo8rb1LQLJ1yrKXr3hJP6672pjMMtCUVAdvEgbvo86YdeP3ZsXjuUlRE3GLvyGgQOdla0MbSuxQIEPUjjP61eNZXzWm/cGpWDmKVHq6o2C/dHNePaMvpD7fPBfF4zDuMndO66SF/nSGXFuUfhsEg4u5nJvi3xHgTJeasA+6kdpRlKLBvFoOtX6yB27/sbx2fIHR1UvcBQ1QSntauHY97CeOBRw1BCbIm4H3zAQ/f4atOyS2LGMgSYJ17+B8OyXknEHPNtQW2VDwlU6ef0kJDZTftxGCCxbfKiMXEYJT5slpfPTNKA+Z9H+tPuheAMpHvolnZVJMa4AWs9x1HISs+Dg8LoW1bSY3GfZLpErbyzrGlMPLAQO7wROGUohcac31cNfbqBjDnpmWYBolDopA/8XQCR/ydEBHOQ8m0hrbWIGtQdFcsXtgqcHAvIJSBMDdZIOA10/0Zrv7okLmAY9bkoYgqgrtJlj0vE2hXrqKCWnScYQzbtlTWtrCWXBwkDORtJpWIP+++siK7PTizj7kr6sCF7wJThRMFZQmBS+1NJXt39IF8UuxAkj9bXWf35IIhsELH2c437GQyQxBXKsw9agP6hhHH/BcYg7aqSQ956AbJvygpiw== testuser
diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_4096_testpass b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_4096_testpass
new file mode 100644
index 0000000000..96e29fc15c
--- /dev/null
+++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_4096_testpass
@@ -0,0 +1,54 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: AES-128-CBC,1EFAFB79DD5E78C98C5A2204D6747AF8
+
+p+WHiqnR+5M7mTVZH2xYA6TcpD5824tU0qCgcU0VdUx9Ikb4Mq7X9Y2by2jTXpDP
+9TN/XcUoaiEm/lAG+RESwFIFjMDe3kbWqv6IFw2GAsvwzeQ3HTjqke1MSpmcoRwA
+vUgHXMl1wK/SQaJIrr0P7aiSt02Zu/hWCZg19rZLLYREC27oLFhgpVsB1HsNzmvt
+au3RaPAkiZ78RpTz5ynSWawTUEqXuL0ctaivvmCnIoThy72gw5RQqw0GmkGEv/lT
+uWZHxqXj+dZggeOvq8G3xNS+eoub/OFrH5t4+5zJB9P8f28vwlsGCYe25dH0oH+K
+2Mhhnp4RNjsJ+YaqkTVjpJrMddz0WUgFWFzmD3b59DIDxWigmKIH6sCjlkMkCvVC
+djS6B+D5HE7dtWm12u38hZ6I1dgz6W+dtlpqZvt7j/opHNYeyAlaY1yEL2HiEoF9
+hI4FdxxXC332FdOP/FS/q+nuTj4wqvO6QsVG6V2nEhIKe7tLEiKmlBf9rAVqTEZp
+rWURoDDfUZPwGe38AloFpMr3k+NR1k0CmG9j9L6aw5bugS1Yqb/6oX3e/d5AQkJK
+XmhfsGUTShNEF5WthotgPGoBKF2astUAF0p50GB9lfuzlBZVvt6hVIecQDUO6/G7
+MT68JbRk2kHw2U0K9+3T2y8PpvHurE8jcH1kkSy0bKW+h0CTK17869keLSLH4+2D
+3gk6xrEEUFb+qLGTTfIbWCLxbCUJP5FGZHsQiTmecGECP4qYNlaedAiI76wxJGG3
+UrMi8kkae5PeujFDVo1CsRXAQoeBAzVkuVU93acCPm62hm8Z3wBJafEIWwEQmXRQ
+Zuk443OkjT4eB3U1RJSoglDaFBvj3eq9CthXZBDZPWFD21gXa4r3MW84aRBX3FPc
+FVrLqAbEcoULomvQz/lKJ4Q2i6jHloHioz/X4OgyrkkYXqst5UuXB8hE7jI48i4e
+mlOxQ0ORyXEwhXS6CnT0zGYlyrevipqI0ch0QSW4391dDVG+ud6PTaft9kc3zDpK
+CDONQYlN2GNQ91KxUDYKcPtH5wDjsSUPYYfsPBL10+yhhJLQ3S9lKsnNOnRvtTa5
+EORCFcDkDi18pR4rXz2qQhdrv5slWiWrB76d/1bhUo3hFnbSHDbl1jOO/e/OJ+wP
+cb/bfIH6iua4X3EVrVK0hm22SaoarhXi4XLdPiIUTVrEiSqDKF3XOE5uq+kGzfWc
+YaToLAOTFuwBYjIfgnhu/CrrrPganMFQrKOxjnR5q12xYmkneRc8xc5XYab9jVG2
+vdYh3yNl9/bwbguPmYZkwh3POrSiUfMnhTr/s6umNMjvnacab1c35hJUGssYZ7kV
+20a1jjTvYzH+RFhzPZpRUwiCcYKTQneta54h4eVCOOE1wdhWxeBv8MwtXijvf8Mq
+0+wpbCuW46/jO0F+oHEunTppXGgFKiwiKlElcMqrCpgVaFGgmyHDrE0Kgi+up9hv
+a5UG//0uRAvBgZAsffX9KbbkJLrZsv/YXqvlN5xhFolUNjtUndxLgRrEe6Z4r5EL
+FAjkH0ex1/Yvb3WromGbfAQRRzLqDKGqdAO6OgYeIW5q13QO1UwrPPPFHdXTDx64
+/8t5YC2ctJ/PAS6QMPFpHl3CrybkO7mvugQYaEG0vxV1whXb1uFe1OGILDUsGR/E
+XCz0D9xTNojphOK1zRof0Qg4FPIZGI90SZLGJTNZnwN52b/ig839B4MIlT6nwUCr
+42yBCbI/k1QYm4Gb3zxDxBZwlOkQjU9LSv5lsmW/ObRsPmnK2pAjmT8n5O7wyXnR
+I3LuIWB2ssxySbvqzRAx2WC6fo4PBXpAKRgM5ZTH9NwFACyR84AC0ijw5UAGztXe
+WUAxx4l2aUYRasKQsQ9IS1wDmUE+q9zhCiv/toyDMwTENW3iFMoWFnaZVGWNAnlA
+YTjrix/SPwA9ybYIxRbh+FpP/aEWyp7OGDk9hQQvDLUkzwNJnfAycV8jq5OETid3
+3l+xzpGe414S5xAMMr3KDZnwVNbIkoYDAmtjIrfemnB0NuT1lDZ0eRZZXpFQPUAv
+U9y3p/5VRU7Ihe7TWjOrs9WGF2yBt5pcC8WbNDu8WMs3wtA8e+DBZHIJnHa/UsSu
+HTIKAXrrB4fmchumVwQT3Fdd8ZgJVvlgAcGNmko6fPVbM+CgwJ1iVwzsNKinF9xT
+J53twma7cpAYpwqSLMENZle9Wc2RPzv/mb4brud38csgrwQ28xfkcntjcT+Jykgw
+2ae5zlaP/R1a2sYbbT/ta0PncdfBuYuRbGZSNBQKKbe2+0BDqSvFSJGNB0beQ/xE
+daxg7Q6nZdeWksmIUZB5BHC1WDmfmk9N6M+pl0+7YbH1pUMqg61JE2QTCMzfQAoC
+v4jQ4o703KdvMRcnjQQCqab/Ihoeq5HUmXRCy3za6Vpxxp6mJpIK/6OWGn2UU/6w
+saujG7F2ewBBBGReg8pgUZODayAX+TBu8+5JCKeAD+u707KABaeBEyGa8bp3AZZu
+onzQ2tMmylusmmC/GiJO5UnousOovogl8HtsANdP16A/U6222kuQ5aahAnGTHLpF
+2EwMWDo6SWN5bBDlUQq0IA9WIMGvGFaID1rDwNKw4ZOLdVhGlXhZxq8FoVZVozrX
+khyiiK1UAp9/BpeXTzqJm+aUQNJu3J28LcRMNgmrdWTjzA9X7s/7mFQfYauLehz+
+Jf/RwSca4EXTFkvmhauhzwnPhdBqCSncCJmNi2I0OeJRFsOerajicxvHW6AUDkiD
+7SCDSTvOBEl20cZwdk/WJ7n+ID5QwWDxV+KzB6dXoMPFZXggat5qA+e0JMEbWxJD
+be0HwuiHyK3lLpBMc7Vv7KzZOtH1JscVT9n1Yd184CphTyi0gexcdwa5T0WmVpyi
+ze1zT8pbTOOHWCvJkdqmxKfHp9GutAtviEloNoK4YbRUJUM4uCF81p7vOYNK6vot
+bGvqXtQ7QvTkyKA/Ue4uSQCG1dLaedZQPSIVGFrqMrAFoUxDWFN8NcPiMkETrGFE
+l9psgia1ktvdFdUOgMjpy7xNBodRedSHMcsyVHjfhGxdxPGW9sG8N1DWxTeArGpX
+nkXs+RaZmMWijknT1dZdNt3XZ7+cGm04NG6JfjxY/kvWcuDKAnhCWmNUnQzLEbZF
+-----END RSA PRIVATE KEY-----
diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_4096_testpass.pub b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_4096_testpass.pub
new file mode 100644
index 0000000000..3c3c16feeb
--- /dev/null
+++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_4096_testpass.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDDNtJeBHx0z8RTfpPaUApShGWiMqyk+EpV8O3s3emsYBzYvoA/TLcbUcsuAo8rb1LQLJ1yrKXr3hJP6672pjMMtCUVAdvEgbvo86YdeP3ZsXjuUlRE3GLvyGgQOdla0MbSuxQIEPUjjP61eNZXzWm/cGpWDmKVHq6o2C/dHNePaMvpD7fPBfF4zDuMndO66SF/nSGXFuUfhsEg4u5nJvi3xHgTJeasA+6kdpRlKLBvFoOtX6yB27/sbx2fIHR1UvcBQ1QSntauHY97CeOBRw1BCbIm4H3zAQ/f4atOyS2LGMgSYJ17+B8OyXknEHPNtQW2VDwlU6ef0kJDZTftxGCCxbfKiMXEYJT5slpfPTNKA+Z9H+tPuheAMpHvolnZVJMa4AWs9x1HISs+Dg8LoW1bSY3GfZLpErbyzrGlMPLAQO7wROGUohcac31cNfbqBjDnpmWYBolDopA/8XQCR/ydEBHOQ8m0hrbWIGtQdFcsXtgqcHAvIJSBMDdZIOA10/0Zrv7okLmAY9bkoYgqgrtJlj0vE2hXrqKCWnScYQzbtlTWtrCWXBwkDORtJpWIP+++siK7PTizj7kr6sCF7wJThRMFZQmBS+1NJXt39IF8UuxAkj9bXWf35IIhsELH2c437GQyQxBXKsw9agP6hhHH/BcYg7aqSQ956AbJvygpiw== testuser
diff --git a/org.eclipse.jgit.test/src/org/eclipse/jgit/transport/ssh/SshTestBase.java b/org.eclipse.jgit.test/src/org/eclipse/jgit/transport/ssh/SshTestBase.java
new file mode 100644
index 0000000000..dde55b6d79
--- /dev/null
+++ b/org.eclipse.jgit.test/src/org/eclipse/jgit/transport/ssh/SshTestBase.java
@@ -0,0 +1,844 @@
+/*
+ * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.transport.ssh;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.util.List;
+import java.util.Locale;
+
+import org.eclipse.jgit.api.errors.TransportException;
+import org.eclipse.jgit.transport.CredentialItem;
+import org.eclipse.jgit.transport.JschConfigSessionFactory;
+import org.junit.Test;
+import org.junit.experimental.theories.DataPoints;
+import org.junit.experimental.theories.Theory;
+
+/**
+ * The ssh tests. Concrete subclasses can re-use these tests by implementing the
+ * abstract operations from {@link SshTestHarness}. This gives a way to test
+ * different ssh clients against a unified test suite.
+ */
+public abstract class SshTestBase extends SshTestHarness {
+
+ @DataPoints
+ public static String[] KEY_RESOURCES = { //
+ "id_dsa", //
+ "id_rsa_1024", //
+ "id_rsa_2048", //
+ "id_rsa_3072", //
+ "id_rsa_4096", //
+ "id_ecdsa_256", //
+ "id_ecdsa_384", //
+ "id_ecdsa_521", //
+ "id_ed25519", //
+ // And now encrypted. Passphrase is "testpass".
+ "id_dsa_testpass", //
+ "id_rsa_1024_testpass", //
+ "id_rsa_2048_testpass", //
+ "id_rsa_3072_testpass", //
+ "id_rsa_4096_testpass", //
+ "id_ecdsa_256_testpass", //
+ "id_ecdsa_384_testpass", //
+ "id_ecdsa_521_testpass" };
+
+ protected File defaultCloneDir;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ defaultCloneDir = new File(getTemporaryDirectory(), "cloned");
+ }
+
+ @Test(expected = TransportException.class)
+ public void testSshWithoutConfig() throws Exception {
+ cloneWith("ssh://" + TEST_USER + "@localhost:" + testPort
+ + "/doesntmatter", defaultCloneDir, null);
+ }
+
+ @Test
+ public void testSshWithGlobalIdentity() throws Exception {
+ cloneWith(
+ "ssh://" + TEST_USER + "@localhost:" + testPort
+ + "/doesntmatter",
+ defaultCloneDir, null,
+ "IdentityFile " + privateKey1.getAbsolutePath());
+ }
+
+ @Test
+ public void testSshWithDefaultIdentity() throws Exception {
+ File idRsa = new File(privateKey1.getParentFile(), "id_rsa");
+ Files.copy(privateKey1.toPath(), idRsa.toPath());
+ // We expect the session factory to pick up these keys...
+ cloneWith("ssh://" + TEST_USER + "@localhost:" + testPort
+ + "/doesntmatter", defaultCloneDir, null);
+ }
+
+ @Test
+ public void testSshWithConfig() throws Exception {
+ cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, null, //
+ "Host localhost", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey1.getAbsolutePath());
+ }
+
+ @Test
+ public void testSshWithConfigEncryptedUnusedKey() throws Exception {
+ // Copy the encrypted test key from the bundle.
+ File encryptedKey = new File(sshDir, "id_dsa");
+ copyTestResource("id_dsa_testpass", encryptedKey);
+ TestCredentialsProvider provider = new TestCredentialsProvider(
+ "testpass");
+ cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, provider, //
+ "Host localhost", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey1.getAbsolutePath());
+ assertEquals("CredentialsProvider should not have been called", 0,
+ provider.getLog().size());
+ }
+
+ @Test
+ public void testSshWithConfigEncryptedUnusedKeyInConfigLast()
+ throws Exception {
+ // Copy the encrypted test key from the bundle.
+ File encryptedKey = new File(sshDir, "id_dsa_test_key");
+ copyTestResource("id_dsa_testpass", encryptedKey);
+ TestCredentialsProvider provider = new TestCredentialsProvider(
+ "testpass");
+ cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, provider, //
+ "Host localhost", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey1.getAbsolutePath(),
+ "IdentityFile " + encryptedKey.getAbsolutePath());
+ // This test passes with JSch per chance because JSch completely ignores
+ // the second IdentityFile
+ assertEquals("CredentialsProvider should not have been called", 0,
+ provider.getLog().size());
+ }
+
+ @Test
+ public void testSshWithConfigEncryptedUnusedKeyInConfigFirst()
+ throws Exception {
+ // Test cannot pass with JSch; it handles only one IdentityFile.
+ // assumeTrue(!(getSessionFactory() instanceof
+ // JschConfigSessionFactory)); gives in bazel a failure with "Never
+ // found parameters that satisfied method assumptions."
+ // In maven it's fine!?
+ if (getSessionFactory() instanceof JschConfigSessionFactory) {
+ return;
+ }
+ // Copy the encrypted test key from the bundle.
+ File encryptedKey = new File(sshDir, "id_dsa_test_key");
+ copyTestResource("id_dsa_testpass", encryptedKey);
+ TestCredentialsProvider provider = new TestCredentialsProvider(
+ "testpass");
+ cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, provider, //
+ "Host localhost", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + encryptedKey.getAbsolutePath(),
+ "IdentityFile " + privateKey1.getAbsolutePath());
+ assertEquals("CredentialsProvider should have been called once", 1,
+ provider.getLog().size());
+ }
+
+ @Test
+ public void testSshEncryptedUsedKeyCached() throws Exception {
+ // Make sure we are asked for the password only once if we do several
+ // operations with an encrypted key.
+ File encryptedKey = new File(sshDir, "id_dsa_test_key");
+ copyTestResource("id_dsa_testpass", encryptedKey);
+ File encryptedPublicKey = new File(sshDir, "id_dsa_test_key.pub");
+ copyTestResource("id_dsa_testpass.pub", encryptedPublicKey);
+ server.setTestUserPublicKey(encryptedPublicKey.toPath());
+ TestCredentialsProvider provider = new TestCredentialsProvider(
+ "testpass");
+ pushTo(provider,
+ cloneWith("ssh://localhost/doesntmatter", //
+ defaultCloneDir, provider, //
+ "Host localhost", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + encryptedKey.getAbsolutePath()));
+ assertEquals("CredentialsProvider should have been called once", 1,
+ provider.getLog().size());
+ }
+
+ @Test(expected = TransportException.class)
+ public void testSshEncryptedUsedKeyWrongPassword() throws Exception {
+ File encryptedKey = new File(sshDir, "id_dsa_test_key");
+ copyTestResource("id_dsa_testpass", encryptedKey);
+ File encryptedPublicKey = new File(sshDir, "id_dsa_test_key.pub");
+ copyTestResource("id_dsa_testpass.pub", encryptedPublicKey);
+ server.setTestUserPublicKey(encryptedPublicKey.toPath());
+ TestCredentialsProvider provider = new TestCredentialsProvider(
+ "wrongpass");
+ cloneWith("ssh://localhost/doesntmatter", //
+ defaultCloneDir, provider, //
+ "Host localhost", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "NumberOfPasswordPrompts 1", //
+ "IdentityFile " + encryptedKey.getAbsolutePath());
+ }
+
+ @Test
+ public void testSshEncryptedUsedKeySeveralPassword() throws Exception {
+ File encryptedKey = new File(sshDir, "id_dsa_test_key");
+ copyTestResource("id_dsa_testpass", encryptedKey);
+ File encryptedPublicKey = new File(sshDir, "id_dsa_test_key.pub");
+ copyTestResource("id_dsa_testpass.pub", encryptedPublicKey);
+ server.setTestUserPublicKey(encryptedPublicKey.toPath());
+ TestCredentialsProvider provider = new TestCredentialsProvider(
+ "wrongpass", "wrongpass2", "testpass");
+ cloneWith("ssh://localhost/doesntmatter", //
+ defaultCloneDir, provider, //
+ "Host localhost", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + encryptedKey.getAbsolutePath());
+ assertEquals("CredentialsProvider should have been called 3 times", 3,
+ provider.getLog().size());
+ }
+
+ @Test(expected = TransportException.class)
+ public void testSshWithoutKnownHosts() throws Exception {
+ assertTrue("Could not delete known_hosts", knownHosts.delete());
+ cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, null, //
+ "Host localhost", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey1.getAbsolutePath());
+ }
+
+ @Test
+ public void testSshWithoutKnownHostsWithProviderAsk()
+ throws Exception {
+ File copiedHosts = new File(knownHosts.getParentFile(),
+ "copiedKnownHosts");
+ assertTrue("Failed to rename known_hosts",
+ knownHosts.renameTo(copiedHosts));
+ // The provider will answer "yes" to all questions, so we should be able
+ // to connect and end up with a new known_hosts file with the host key.
+ TestCredentialsProvider provider = new TestCredentialsProvider();
+ cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, provider, //
+ "Host localhost", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey1.getAbsolutePath());
+ List<LogEntry> messages = provider.getLog();
+ assertFalse("Expected user interaction", messages.isEmpty());
+ if (getSessionFactory() instanceof JschConfigSessionFactory) {
+ // JSch doesn't create a non-existing file.
+ assertEquals("Expected to be asked about the key", 1,
+ messages.size());
+ return;
+ }
+ assertEquals(
+ "Expected to be asked about the key, and the file creation",
+ 2, messages.size());
+ assertTrue("~/.ssh/known_hosts should exist now", knownHosts.exists());
+ // Instead of checking the file contents, let's just clone again
+ // without provider. If it works, the server host key was written
+ // correctly.
+ File clonedAgain = new File(getTemporaryDirectory(), "cloned2");
+ cloneWith("ssh://localhost/doesntmatter", clonedAgain, provider, //
+ "Host localhost", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey1.getAbsolutePath());
+ }
+
+ @Test
+ public void testSshWithoutKnownHostsWithProviderAcceptNew()
+ throws Exception {
+ File copiedHosts = new File(knownHosts.getParentFile(),
+ "copiedKnownHosts");
+ assertTrue("Failed to rename known_hosts",
+ knownHosts.renameTo(copiedHosts));
+ TestCredentialsProvider provider = new TestCredentialsProvider();
+ cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, provider, //
+ "Host localhost", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "StrictHostKeyChecking accept-new", //
+ "IdentityFile " + privateKey1.getAbsolutePath());
+ if (getSessionFactory() instanceof JschConfigSessionFactory) {
+ // JSch doesn't create new files.
+ assertTrue("CredentialsProvider not called",
+ provider.getLog().isEmpty());
+ return;
+ }
+ assertEquals("Expected to be asked about the file creation", 1,
+ provider.getLog().size());
+ assertTrue("~/.ssh/known_hosts should exist now", knownHosts.exists());
+ // Instead of checking the file contents, let's just clone again
+ // without provider. If it works, the server host key was written
+ // correctly.
+ File clonedAgain = new File(getTemporaryDirectory(), "cloned2");
+ cloneWith("ssh://localhost/doesntmatter", clonedAgain, null, //
+ "Host localhost", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey1.getAbsolutePath());
+ }
+
+ @Test(expected = TransportException.class)
+ public void testSshWithoutKnownHostsDeny() throws Exception {
+ File copiedHosts = new File(knownHosts.getParentFile(),
+ "copiedKnownHosts");
+ assertTrue("Failed to rename known_hosts",
+ knownHosts.renameTo(copiedHosts));
+ cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, null, //
+ "Host localhost", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "StrictHostKeyChecking yes", //
+ "IdentityFile " + privateKey1.getAbsolutePath());
+ }
+
+ @Test(expected = TransportException.class)
+ public void testSshModifiedHostKeyDeny()
+ throws Exception {
+ File copiedHosts = new File(knownHosts.getParentFile(),
+ "copiedKnownHosts");
+ assertTrue("Failed to rename known_hosts",
+ knownHosts.renameTo(copiedHosts));
+ // Now produce a new known_hosts file containing some other key.
+ createKnownHostsFile(knownHosts, "localhost", testPort, publicKey1);
+ cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, null, //
+ "Host localhost", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "StrictHostKeyChecking yes", //
+ "IdentityFile " + privateKey1.getAbsolutePath());
+ }
+
+ @Test(expected = TransportException.class)
+ public void testSshModifiedHostKeyWithProviderDeny() throws Exception {
+ File copiedHosts = new File(knownHosts.getParentFile(),
+ "copiedKnownHosts");
+ assertTrue("Failed to rename known_hosts",
+ knownHosts.renameTo(copiedHosts));
+ // Now produce a new known_hosts file containing some other key.
+ createKnownHostsFile(knownHosts, "localhost", testPort, publicKey1);
+ TestCredentialsProvider provider = new TestCredentialsProvider();
+ try {
+ cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, provider, //
+ "Host localhost", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "StrictHostKeyChecking yes", //
+ "IdentityFile " + privateKey1.getAbsolutePath());
+ } catch (Exception e) {
+ assertEquals("Expected to be told about the modified key", 1,
+ provider.getLog().size());
+ assertTrue("Only messages expected", provider.getLog().stream()
+ .flatMap(l -> l.getItems().stream()).allMatch(
+ c -> c instanceof CredentialItem.InformationalMessage));
+ throw e;
+ }
+ }
+
+ private void checkKnownHostsModifiedHostKey(File backup, File newFile,
+ String wrongKey) throws IOException {
+ List<String> oldLines = Files.readAllLines(backup.toPath(),
+ StandardCharsets.UTF_8);
+ // Find the original entry. We should have that again in known_hosts.
+ String oldKeyPart = null;
+ for (String oldLine : oldLines) {
+ if (oldLine.contains("[localhost]:")) {
+ String[] parts = oldLine.split("\\s+");
+ if (parts.length > 2) {
+ oldKeyPart = parts[parts.length - 2] + ' '
+ + parts[parts.length - 1];
+ break;
+ }
+ }
+ }
+ assertNotNull("Old key not found", oldKeyPart);
+ List<String> newLines = Files.readAllLines(newFile.toPath(),
+ StandardCharsets.UTF_8);
+ assertFalse("Old host key still found in known_hosts file" + newFile,
+ hasHostKey("localhost", testPort, wrongKey, newLines));
+ assertTrue("New host key not found in known_hosts file" + newFile,
+ hasHostKey("localhost", testPort, oldKeyPart, newLines));
+
+ }
+
+ @Test
+ public void testSshModifiedHostKeyAllow() throws Exception {
+ assertTrue("Failed to delete known_hosts", knownHosts.delete());
+ createKnownHostsFile(knownHosts, "localhost", testPort, publicKey1);
+ File backup = new File(getTemporaryDirectory(), "backupKnownHosts");
+ Files.copy(knownHosts.toPath(), backup.toPath());
+ cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, null, //
+ "Host localhost", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "StrictHostKeyChecking no", //
+ "IdentityFile " + privateKey1.getAbsolutePath());
+ // File should not have been updated!
+ String[] oldLines = Files
+ .readAllLines(backup.toPath(), StandardCharsets.UTF_8)
+ .toArray(new String[0]);
+ String[] newLines = Files
+ .readAllLines(knownHosts.toPath(), StandardCharsets.UTF_8)
+ .toArray(new String[0]);
+ assertArrayEquals("Known hosts file should not be modified", oldLines,
+ newLines);
+ }
+
+ @Test
+ public void testSshModifiedHostKeyAsk() throws Exception {
+ File copiedHosts = new File(knownHosts.getParentFile(),
+ "copiedKnownHosts");
+ assertTrue("Failed to rename known_hosts",
+ knownHosts.renameTo(copiedHosts));
+ String wrongKeyPart = createKnownHostsFile(knownHosts, "localhost",
+ testPort, publicKey1);
+ TestCredentialsProvider provider = new TestCredentialsProvider();
+ cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, provider, //
+ "Host localhost", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey1.getAbsolutePath());
+ checkKnownHostsModifiedHostKey(copiedHosts, knownHosts, wrongKeyPart);
+ assertEquals("Expected to be asked about the modified key", 1,
+ provider.getLog().size());
+ }
+
+ @Test
+ public void testSshCloneWithConfigAndPush() throws Exception {
+ pushTo(cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, null, //
+ "Host localhost", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey1.getAbsolutePath()));
+ }
+
+ @Test
+ public void testSftpWithConfig() throws Exception {
+ cloneWith("sftp://localhost/.git", defaultCloneDir, null, //
+ "Host localhost", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey1.getAbsolutePath());
+ }
+
+ @Test
+ public void testSftpCloneWithConfigAndPush() throws Exception {
+ pushTo(cloneWith("sftp://localhost/.git", defaultCloneDir, null, //
+ "Host localhost", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey1.getAbsolutePath()));
+ }
+
+ @Test(expected = TransportException.class)
+ public void testSshWithConfigWrongKey() throws Exception {
+ cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, null, //
+ "Host localhost", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey2.getAbsolutePath());
+ }
+
+ @Test
+ public void testSshWithWrongUserNameInConfig() throws Exception {
+ // Bug 526778
+ cloneWith(
+ "ssh://" + TEST_USER + "@localhost:" + testPort
+ + "/doesntmatter",
+ defaultCloneDir, null, //
+ "Host localhost", //
+ "HostName localhost", //
+ "User sombody_else", //
+ "IdentityFile " + privateKey1.getAbsolutePath());
+ }
+
+ @Test
+ public void testSshWithWrongPortInConfig() throws Exception {
+ // Bug 526778
+ cloneWith(
+ "ssh://" + TEST_USER + "@localhost:" + testPort
+ + "/doesntmatter",
+ defaultCloneDir, null, //
+ "Host localhost", //
+ "HostName localhost", //
+ "Port 22", //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey1.getAbsolutePath());
+ }
+
+ @Test
+ public void testSshWithAliasInConfig() throws Exception {
+ // Bug 531118
+ cloneWith("ssh://git/doesntmatter", defaultCloneDir, null, //
+ "Host git", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey1.getAbsolutePath(), "", //
+ "Host localhost", //
+ "HostName localhost", //
+ "Port 22", //
+ "User someone_else", //
+ "IdentityFile " + privateKey2.getAbsolutePath());
+ }
+
+ @Test
+ public void testSshWithUnknownCiphersInConfig() throws Exception {
+ // Bug 535672
+ cloneWith("ssh://git/doesntmatter", defaultCloneDir, null, //
+ "Host git", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey1.getAbsolutePath(), //
+ "Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr");
+ }
+
+ @Test
+ public void testSshWithUnknownHostKeyAlgorithmsInConfig()
+ throws Exception {
+ // Bug 535672
+ cloneWith("ssh://git/doesntmatter", defaultCloneDir, null, //
+ "Host git", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey1.getAbsolutePath(), //
+ "HostKeyAlgorithms foobar,ssh-rsa,ssh-dss");
+ }
+
+ @Test
+ public void testSshWithUnknownKexAlgorithmsInConfig()
+ throws Exception {
+ // Bug 535672
+ cloneWith("ssh://git/doesntmatter", defaultCloneDir, null, //
+ "Host git", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey1.getAbsolutePath(), //
+ "KexAlgorithms foobar,diffie-hellman-group14-sha1,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521");
+ }
+
+ @Test
+ public void testSshWithMinimalHostKeyAlgorithmsInConfig()
+ throws Exception {
+ // Bug 537790
+ cloneWith("ssh://git/doesntmatter", defaultCloneDir, null, //
+ "Host git", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey1.getAbsolutePath(), //
+ "HostKeyAlgorithms ssh-rsa,ssh-dss");
+ }
+
+ @Test
+ public void testSshWithUnknownAuthInConfig() throws Exception {
+ cloneWith("ssh://git/doesntmatter", defaultCloneDir, null, //
+ "Host git", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey1.getAbsolutePath(), //
+ "PreferredAuthentications gssapi-with-mic,hostbased,publickey,keyboard-interactive,password");
+ }
+
+ @Test(expected = TransportException.class)
+ public void testSshWithNoMatchingAuthInConfig() throws Exception {
+ // Server doesn't do password, and anyway we set no password.
+ cloneWith("ssh://git/doesntmatter", defaultCloneDir, null, //
+ "Host git", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey1.getAbsolutePath(), //
+ "PreferredAuthentications password");
+ }
+
+ @Test
+ public void testRsaHostKeySecond() throws Exception {
+ // See https://git.eclipse.org/r/#/c/130402/ : server has EcDSA
+ // (preferred), RSA, we have RSA in known_hosts: client and server
+ // should agree on RSA.
+ File newHostKey = new File(getTemporaryDirectory(), "newhostkey");
+ copyTestResource("id_ecdsa_256", newHostKey);
+ server.addHostKey(newHostKey.toPath(), true);
+ cloneWith("ssh://git/doesntmatter", defaultCloneDir, null, //
+ "Host git", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey1.getAbsolutePath());
+ }
+
+ @Test
+ public void testEcDsaHostKey() throws Exception {
+ // See https://git.eclipse.org/r/#/c/130402/ : server has RSA
+ // (preferred), EcDSA, we have EcDSA in known_hosts: client and server
+ // should agree on EcDSA.
+ File newHostKey = new File(getTemporaryDirectory(), "newhostkey");
+ copyTestResource("id_ecdsa_256", newHostKey);
+ server.addHostKey(newHostKey.toPath(), false);
+ File newHostKeyPub = new File(getTemporaryDirectory(),
+ "newhostkey.pub");
+ copyTestResource("id_ecdsa_256.pub", newHostKeyPub);
+ createKnownHostsFile(knownHosts, "localhost", testPort, newHostKeyPub);
+ cloneWith("ssh://git/doesntmatter", defaultCloneDir, null, //
+ "Host git", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey1.getAbsolutePath());
+ }
+
+ @Test
+ public void testPasswordAuth() throws Exception {
+ server.enablePasswordAuthentication();
+ TestCredentialsProvider provider = new TestCredentialsProvider(
+ TEST_USER.toUpperCase(Locale.ROOT));
+ cloneWith("ssh://git/doesntmatter", defaultCloneDir, provider, //
+ "Host git", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "PreferredAuthentications password");
+ }
+
+ @Test
+ public void testPasswordAuthSeveralTimes() throws Exception {
+ server.enablePasswordAuthentication();
+ TestCredentialsProvider provider = new TestCredentialsProvider(
+ "wrongpass", "wrongpass", TEST_USER.toUpperCase(Locale.ROOT));
+ cloneWith("ssh://git/doesntmatter", defaultCloneDir, provider, //
+ "Host git", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "PreferredAuthentications password");
+ }
+
+ @Test(expected = TransportException.class)
+ public void testPasswordAuthWrongPassword() throws Exception {
+ server.enablePasswordAuthentication();
+ TestCredentialsProvider provider = new TestCredentialsProvider(
+ "wrongpass");
+ cloneWith("ssh://git/doesntmatter", defaultCloneDir, provider, //
+ "Host git", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "PreferredAuthentications password");
+ }
+
+ @Test(expected = TransportException.class)
+ public void testPasswordAuthNoPassword() throws Exception {
+ server.enablePasswordAuthentication();
+ TestCredentialsProvider provider = new TestCredentialsProvider();
+ cloneWith("ssh://git/doesntmatter", defaultCloneDir, provider, //
+ "Host git", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "PreferredAuthentications password");
+ }
+
+ @Test(expected = TransportException.class)
+ public void testPasswordAuthCorrectPasswordTooLate() throws Exception {
+ server.enablePasswordAuthentication();
+ TestCredentialsProvider provider = new TestCredentialsProvider(
+ "wrongpass", "wrongpass", "wrongpass",
+ TEST_USER.toUpperCase(Locale.ROOT));
+ cloneWith("ssh://git/doesntmatter", defaultCloneDir, provider, //
+ "Host git", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "PreferredAuthentications password");
+ }
+
+ @Test
+ public void testKeyboardInteractiveAuth() throws Exception {
+ server.enableKeyboardInteractiveAuthentication();
+ TestCredentialsProvider provider = new TestCredentialsProvider(
+ TEST_USER.toUpperCase(Locale.ROOT));
+ cloneWith("ssh://git/doesntmatter", defaultCloneDir, provider, //
+ "Host git", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "PreferredAuthentications keyboard-interactive");
+ }
+
+ @Test
+ public void testKeyboardInteractiveAuthSeveralTimes() throws Exception {
+ server.enableKeyboardInteractiveAuthentication();
+ TestCredentialsProvider provider = new TestCredentialsProvider(
+ "wrongpass", "wrongpass", TEST_USER.toUpperCase(Locale.ROOT));
+ cloneWith("ssh://git/doesntmatter", defaultCloneDir, provider, //
+ "Host git", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "PreferredAuthentications keyboard-interactive");
+ }
+
+ @Test(expected = TransportException.class)
+ public void testKeyboardInteractiveAuthWrongPassword() throws Exception {
+ server.enableKeyboardInteractiveAuthentication();
+ TestCredentialsProvider provider = new TestCredentialsProvider(
+ "wrongpass");
+ cloneWith("ssh://git/doesntmatter", defaultCloneDir, provider, //
+ "Host git", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "PreferredAuthentications keyboard-interactive");
+ }
+
+ @Test(expected = TransportException.class)
+ public void testKeyboardInteractiveAuthNoPassword() throws Exception {
+ server.enableKeyboardInteractiveAuthentication();
+ TestCredentialsProvider provider = new TestCredentialsProvider();
+ cloneWith("ssh://git/doesntmatter", defaultCloneDir, provider, //
+ "Host git", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "PreferredAuthentications keyboard-interactive");
+ }
+
+ @Test(expected = TransportException.class)
+ public void testKeyboardInteractiveAuthCorrectPasswordTooLate()
+ throws Exception {
+ server.enableKeyboardInteractiveAuthentication();
+ TestCredentialsProvider provider = new TestCredentialsProvider(
+ "wrongpass", "wrongpass", "wrongpass",
+ TEST_USER.toUpperCase(Locale.ROOT));
+ cloneWith("ssh://git/doesntmatter", defaultCloneDir, provider, //
+ "Host git", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "PreferredAuthentications keyboard-interactive");
+ }
+
+ @Theory
+ public void testSshKeys(String keyName) throws Exception {
+ // JSch fails on ECDSA 384/521 keys. Compare
+ // https://sourceforge.net/p/jsch/patches/10/
+ assumeTrue(!(getSessionFactory() instanceof JschConfigSessionFactory
+ && (keyName.contains("ed25519")
+ || keyName.startsWith("id_ecdsa_384")
+ || keyName.startsWith("id_ecdsa_521"))));
+ File cloned = new File(getTemporaryDirectory(), "cloned");
+ String keyFileName = keyName + "_key";
+ File privateKey = new File(sshDir, keyFileName);
+ copyTestResource(keyName, privateKey);
+ File publicKey = new File(sshDir, keyFileName + ".pub");
+ copyTestResource(keyName + ".pub", publicKey);
+ server.setTestUserPublicKey(publicKey.toPath());
+ TestCredentialsProvider provider = new TestCredentialsProvider(
+ "testpass");
+ pushTo(provider,
+ cloneWith("ssh://localhost/doesntmatter", //
+ cloned, provider, //
+ "Host localhost", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey.getAbsolutePath()));
+ int expectedCalls = keyName.endsWith("testpass") ? 1 : 0;
+ assertEquals("Unexpected calls to CredentialsProvider", expectedCalls,
+ provider.getLog().size());
+ // Should now also work without credentials provider, even if the key
+ // was encrypted.
+ cloned = new File(getTemporaryDirectory(), "cloned2");
+ pushTo(null,
+ cloneWith("ssh://localhost/doesntmatter", //
+ cloned, null, //
+ "Host localhost", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey.getAbsolutePath()));
+ }
+}
diff --git a/org.eclipse.jgit.test/src/org/eclipse/jgit/transport/ssh/SshTestHarness.java b/org.eclipse.jgit.test/src/org/eclipse/jgit/transport/ssh/SshTestHarness.java
new file mode 100644
index 0000000000..59925a5a16
--- /dev/null
+++ b/org.eclipse.jgit.test/src/org/eclipse/jgit/transport/ssh/SshTestHarness.java
@@ -0,0 +1,452 @@
+/*
+ * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.transport.ssh;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.jgit.api.CloneCommand;
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.PushCommand;
+import org.eclipse.jgit.api.ResetCommand.ResetType;
+import org.eclipse.jgit.errors.UnsupportedCredentialItem;
+import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.junit.ssh.SshTestGitServer;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.transport.CredentialItem;
+import org.eclipse.jgit.transport.CredentialsProvider;
+import org.eclipse.jgit.transport.PushResult;
+import org.eclipse.jgit.transport.RemoteRefUpdate;
+import org.eclipse.jgit.transport.SshSessionFactory;
+import org.eclipse.jgit.transport.URIish;
+import org.eclipse.jgit.util.FS;
+import org.junit.After;
+
+import com.jcraft.jsch.JSch;
+import com.jcraft.jsch.KeyPair;
+
+/**
+ * Root class for ssh tests. Sets up the ssh test server. A set of pre-computed
+ * keys for testing is provided in the bundle and can be used in test cases via
+ * {@link #copyTestResource(String, File)}. These test key files names have four
+ * components, separated by a single underscore: "id", the algorithm, the bits
+ * (if variable), and the password if the private key is encrypted. For instance
+ * "{@code id_ecdsa_384_testpass}" is an encrypted ECDSA-384 key. The passphrase
+ * to decrypt is "testpass". The key "{@code id_ecdsa_384}" is the same but
+ * unencrypted. All keys were generated and encrypted via ssh-keygen. Note that
+ * DSA and ec25519 have no "bits" component. Available keys are listed in
+ * {@link SshTestBase#KEY_RESOURCES}.
+ */
+public abstract class SshTestHarness extends RepositoryTestCase {
+
+ protected static final String TEST_USER = "testuser";
+
+ protected File sshDir;
+
+ protected File privateKey1;
+
+ protected File privateKey2;
+
+ protected File publicKey1;
+
+ protected SshTestGitServer server;
+
+ private SshSessionFactory factory;
+
+ protected int testPort;
+
+ protected File knownHosts;
+
+ private File homeDir;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ writeTrashFile("file.txt", "something");
+ try (Git git = new Git(db)) {
+ git.add().addFilepattern("file.txt").call();
+ git.commit().setMessage("Initial commit").call();
+ }
+ mockSystemReader.setProperty("user.home",
+ getTemporaryDirectory().getAbsolutePath());
+ mockSystemReader.setProperty("HOME",
+ getTemporaryDirectory().getAbsolutePath());
+ homeDir = FS.DETECTED.userHome();
+ FS.DETECTED.setUserHome(getTemporaryDirectory().getAbsoluteFile());
+ sshDir = new File(getTemporaryDirectory(), ".ssh");
+ assertTrue(sshDir.mkdir());
+ File serverDir = new File(getTemporaryDirectory(), "srv");
+ assertTrue(serverDir.mkdir());
+ // Create two key pairs. Let's not call them "id_rsa".
+ privateKey1 = new File(sshDir, "first_key");
+ privateKey2 = new File(sshDir, "second_key");
+ publicKey1 = createKeyPair(privateKey1);
+ createKeyPair(privateKey2);
+ ByteArrayOutputStream publicHostKey = new ByteArrayOutputStream();
+ // Start a server with our test user and the first key.
+ server = new SshTestGitServer(TEST_USER, publicKey1.toPath(), db,
+ createHostKey(publicHostKey));
+ testPort = server.start();
+ assertTrue(testPort > 0);
+ knownHosts = new File(sshDir, "known_hosts");
+ Files.write(knownHosts.toPath(), Collections.singleton("[localhost]:"
+ + testPort + ' '
+ + publicHostKey.toString(StandardCharsets.US_ASCII.name())));
+ factory = createSessionFactory();
+ SshSessionFactory.setInstance(factory);
+ }
+
+ private static File createKeyPair(File privateKeyFile) throws Exception {
+ // Found no way to do this with MINA sshd except rolling it all
+ // ourselves...
+ JSch jsch = new JSch();
+ KeyPair pair = KeyPair.genKeyPair(jsch, KeyPair.RSA, 2048);
+ try (OutputStream out = new FileOutputStream(privateKeyFile)) {
+ pair.writePrivateKey(out);
+ }
+ File publicKeyFile = new File(privateKeyFile.getParentFile(),
+ privateKeyFile.getName() + ".pub");
+ try (OutputStream out = new FileOutputStream(publicKeyFile)) {
+ pair.writePublicKey(out, TEST_USER);
+ }
+ return publicKeyFile;
+ }
+
+ private static byte[] createHostKey(OutputStream publicKey)
+ throws Exception {
+ JSch jsch = new JSch();
+ KeyPair pair = KeyPair.genKeyPair(jsch, KeyPair.RSA, 2048);
+ pair.writePublicKey(publicKey, "");
+ try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
+ pair.writePrivateKey(out);
+ out.flush();
+ return out.toByteArray();
+ }
+ }
+
+ /**
+ * Creates a new known_hosts file with one entry for the given host and port
+ * taken from the given public key file.
+ *
+ * @param file
+ * to write the known_hosts file to
+ * @param host
+ * for the entry
+ * @param port
+ * for the entry
+ * @param publicKey
+ * to use
+ * @return the public-key part of the line
+ * @throws IOException
+ */
+ protected static String createKnownHostsFile(File file, String host,
+ int port, File publicKey) throws IOException {
+ List<String> lines = Files.readAllLines(publicKey.toPath(),
+ StandardCharsets.UTF_8);
+ assertEquals("Public key has too many lines", 1, lines.size());
+ String pubKey = lines.get(0);
+ // Strip off the comment.
+ String[] parts = pubKey.split("\\s+");
+ assertTrue("Unexpected key content",
+ parts.length == 2 || parts.length == 3);
+ String keyPart = parts[0] + ' ' + parts[1];
+ String line = '[' + host + "]:" + port + ' ' + keyPart;
+ Files.write(file.toPath(), Collections.singletonList(line));
+ return keyPart;
+ }
+
+ /**
+ * Checks whether there is a line for the given host and port that also
+ * matches the given key part in the list of lines.
+ *
+ * @param host
+ * to look for
+ * @param port
+ * to look for
+ * @param keyPart
+ * to look for
+ * @param lines
+ * to look in
+ * @return {@code true} if found, {@code false} otherwise
+ */
+ protected boolean hasHostKey(String host, int port, String keyPart,
+ List<String> lines) {
+ String h = '[' + host + "]:" + port;
+ return lines.stream()
+ .anyMatch(l -> l.contains(h) && l.contains(keyPart));
+ }
+
+ @After
+ public void shutdownServer() throws Exception {
+ if (server != null) {
+ server.stop();
+ server = null;
+ }
+ FS.DETECTED.setUserHome(homeDir);
+ SshSessionFactory.setInstance(null);
+ factory = null;
+ }
+
+ protected abstract SshSessionFactory createSessionFactory();
+
+ protected SshSessionFactory getSessionFactory() {
+ return factory;
+ }
+
+ protected abstract void installConfig(String... config);
+
+ /**
+ * Copies a test data file contained in the test bundle to the given file.
+ * Equivalent to {@link #copyTestResource(Class, String, File)} with
+ * {@code SshTestHarness.class} as first parameter.
+ *
+ * @param resourceName
+ * of the test resource to copy
+ * @param to
+ * file to copy the resource to
+ * @throws IOException
+ * if the resource cannot be copied
+ */
+ protected void copyTestResource(String resourceName, File to)
+ throws IOException {
+ copyTestResource(SshTestHarness.class, resourceName, to);
+ }
+
+ /**
+ * Copies a test data file contained in the test bundle to the given file,
+ * using {@link Class#getResourceAsStream(String)} to get the test resource.
+ *
+ * @param loader
+ * {@link Class} to use to load the resource
+ * @param resourceName
+ * of the test resource to copy
+ * @param to
+ * file to copy the resource to
+ * @throws IOException
+ * if the resource cannot be copied
+ */
+ protected void copyTestResource(Class<?> loader, String resourceName,
+ File to) throws IOException {
+ try (InputStream in = loader.getResourceAsStream(resourceName)) {
+ Files.copy(in, to.toPath());
+ }
+ }
+
+ protected File cloneWith(String uri, File to, CredentialsProvider provider,
+ String... config) throws Exception {
+ installConfig(config);
+ CloneCommand clone = Git.cloneRepository().setCloneAllBranches(true)
+ .setDirectory(to).setURI(uri);
+ if (provider != null) {
+ clone.setCredentialsProvider(provider);
+ }
+ try (Git git = clone.call()) {
+ Repository repo = git.getRepository();
+ assertNotNull(repo.resolve("master"));
+ assertNotEquals(db.getWorkTree(),
+ git.getRepository().getWorkTree());
+ assertTrue(new File(git.getRepository().getWorkTree(), "file.txt")
+ .exists());
+ return repo.getWorkTree();
+ }
+ }
+
+ protected void pushTo(File localClone) throws Exception {
+ pushTo(null, localClone);
+ }
+
+ protected void pushTo(CredentialsProvider provider, File localClone)
+ throws Exception {
+ RevCommit commit;
+ File newFile = null;
+ try (Git git = Git.open(localClone)) {
+ // Write a new file and modify a file.
+ Repository local = git.getRepository();
+ newFile = File.createTempFile("new", "sshtest",
+ local.getWorkTree());
+ write(newFile, "something new");
+ File existingFile = new File(local.getWorkTree(), "file.txt");
+ write(existingFile, "something else");
+ git.add().addFilepattern("file.txt")
+ .addFilepattern(newFile.getName())
+ .call();
+ commit = git.commit().setMessage("Local commit").call();
+ // Push
+ PushCommand push = git.push().setPushAll();
+ if (provider != null) {
+ push.setCredentialsProvider(provider);
+ }
+ Iterable<PushResult> results = push.call();
+ for (PushResult result : results) {
+ for (RemoteRefUpdate u : result.getRemoteUpdates()) {
+ assertEquals(
+ "Could not update " + u.getRemoteName() + ' '
+ + u.getMessage(),
+ RemoteRefUpdate.Status.OK, u.getStatus());
+ }
+ }
+ }
+ // Now check "master" in the remote repo directly:
+ assertEquals("Unexpected remote commit", commit, db.resolve("master"));
+ assertEquals("Unexpected remote commit", commit,
+ db.resolve(Constants.HEAD));
+ File remoteFile = new File(db.getWorkTree(), newFile.getName());
+ assertFalse("File should not exist on remote", remoteFile.exists());
+ try (Git git = new Git(db)) {
+ git.reset().setMode(ResetType.HARD).setRef(Constants.HEAD).call();
+ }
+ assertTrue("File does not exist on remote", remoteFile.exists());
+ checkFile(remoteFile, "something new");
+ }
+
+ protected static class TestCredentialsProvider extends CredentialsProvider {
+
+ private final List<String> stringStore;
+
+ private final Iterator<String> strings;
+
+ public TestCredentialsProvider(String... strings) {
+ if (strings == null || strings.length == 0) {
+ stringStore = Collections.emptyList();
+ } else {
+ stringStore = Arrays.asList(strings);
+ }
+ this.strings = stringStore.iterator();
+ }
+
+ @Override
+ public boolean isInteractive() {
+ return true;
+ }
+
+ @Override
+ public boolean supports(CredentialItem... items) {
+ return true;
+ }
+
+ @Override
+ public boolean get(URIish uri, CredentialItem... items)
+ throws UnsupportedCredentialItem {
+ System.out.println("URI: " + uri);
+ for (CredentialItem item : items) {
+ System.out.println(item.getClass().getSimpleName() + ' '
+ + item.getPromptText());
+ }
+ logItems(uri, items);
+ for (CredentialItem item : items) {
+ if (item instanceof CredentialItem.InformationalMessage) {
+ continue;
+ }
+ if (item instanceof CredentialItem.YesNoType) {
+ ((CredentialItem.YesNoType) item).setValue(true);
+ } else if (item instanceof CredentialItem.CharArrayType) {
+ if (strings.hasNext()) {
+ ((CredentialItem.CharArrayType) item)
+ .setValue(strings.next().toCharArray());
+ } else {
+ return false;
+ }
+ } else if (item instanceof CredentialItem.StringType) {
+ if (strings.hasNext()) {
+ ((CredentialItem.StringType) item)
+ .setValue(strings.next());
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private List<LogEntry> log = new ArrayList<>();
+
+ private void logItems(URIish uri, CredentialItem... items) {
+ log.add(new LogEntry(uri, Arrays.asList(items)));
+ }
+
+ public List<LogEntry> getLog() {
+ return log;
+ }
+ }
+
+ protected static class LogEntry {
+
+ private URIish uri;
+
+ private List<CredentialItem> items;
+
+ public LogEntry(URIish uri, List<CredentialItem> items) {
+ this.uri = uri;
+ this.items = items;
+ }
+
+ public URIish getURIish() {
+ return uri;
+ }
+
+ public List<CredentialItem> getItems() {
+ return items;
+ }
+ }
+}
diff --git a/org.eclipse.jgit.test/tests.bzl b/org.eclipse.jgit.test/tests.bzl
index bc06e3ef40..dc2102964f 100644
--- a/org.eclipse.jgit.test/tests.bzl
+++ b/org.eclipse.jgit.test/tests.bzl
@@ -41,7 +41,18 @@ def tests(tests):
additional_deps = [
"//lib:jsch",
]
-
+ if src.endswith("JSchSshTest.java"):
+ additional_deps = [
+ "//lib:jsch",
+ "//lib:jzlib",
+ "//lib:sshd-core",
+ "//lib:sshd-sftp",
+ ":sshd-helpers",
+ ]
+ if src.endswith("JDKHttpConnectionTest.java"):
+ additional_deps = [
+ "//lib:mockito",
+ ]
heap_size = "-Xmx256m"
if src.endswith("HugeCommitMessageTest.java"):
heap_size = "-Xmx512m"
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java
index 911f659149..1a5793ce31 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java
@@ -43,6 +43,7 @@
*/
package org.eclipse.jgit.api;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static org.eclipse.jgit.util.FileUtils.RECURSIVE;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -118,7 +119,7 @@ public class AddCommandTest extends RepositoryTestCase {
public void testAddExistingSingleFile() throws IOException, GitAPIException {
File file = new File(db.getWorkTree(), "a.txt");
FileUtils.createNewFile(file);
- try (PrintWriter writer = new PrintWriter(file)) {
+ try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) {
writer.print("content");
}
@@ -489,7 +490,7 @@ public class AddCommandTest extends RepositoryTestCase {
GitAPIException {
File file = new File(db.getWorkTree(), "a.txt");
FileUtils.createNewFile(file);
- try (PrintWriter writer = new PrintWriter(file)) {
+ try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) {
writer.print("row1\r\nrow2");
}
@@ -519,7 +520,7 @@ public class AddCommandTest extends RepositoryTestCase {
data.append("row1\r\nrow2");
}
String crData = data.toString();
- try (PrintWriter writer = new PrintWriter(file)) {
+ try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) {
writer.print(crData);
}
String lfData = data.toString().replaceAll("\r", "");
@@ -544,7 +545,7 @@ public class AddCommandTest extends RepositoryTestCase {
GitAPIException {
File file = new File(db.getWorkTree(), "a.txt");
FileUtils.createNewFile(file);
- try (PrintWriter writer = new PrintWriter(file)) {
+ try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) {
writer.print("row1\r\nrow2\u0000");
}
@@ -570,7 +571,7 @@ public class AddCommandTest extends RepositoryTestCase {
FileUtils.mkdir(new File(db.getWorkTree(), "sub"));
File file = new File(db.getWorkTree(), "sub/a.txt");
FileUtils.createNewFile(file);
- try (PrintWriter writer = new PrintWriter(file)) {
+ try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) {
writer.print("content");
}
@@ -588,7 +589,7 @@ public class AddCommandTest extends RepositoryTestCase {
GitAPIException {
File file = new File(db.getWorkTree(), "a.txt");
FileUtils.createNewFile(file);
- try (PrintWriter writer = new PrintWriter(file)) {
+ try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) {
writer.print("content");
}
@@ -597,7 +598,7 @@ public class AddCommandTest extends RepositoryTestCase {
dc.getEntry(0).getObjectId();
- try (PrintWriter writer = new PrintWriter(file)) {
+ try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) {
writer.print("other content");
}
@@ -613,7 +614,7 @@ public class AddCommandTest extends RepositoryTestCase {
public void testAddExistingSingleFileTwiceWithCommit() throws Exception {
File file = new File(db.getWorkTree(), "a.txt");
FileUtils.createNewFile(file);
- try (PrintWriter writer = new PrintWriter(file)) {
+ try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) {
writer.print("content");
}
@@ -624,7 +625,7 @@ public class AddCommandTest extends RepositoryTestCase {
git.commit().setMessage("commit a.txt").call();
- try (PrintWriter writer = new PrintWriter(file)) {
+ try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) {
writer.print("other content");
}
@@ -640,7 +641,7 @@ public class AddCommandTest extends RepositoryTestCase {
public void testAddRemovedFile() throws Exception {
File file = new File(db.getWorkTree(), "a.txt");
FileUtils.createNewFile(file);
- try (PrintWriter writer = new PrintWriter(file)) {
+ try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) {
writer.print("content");
}
@@ -663,7 +664,7 @@ public class AddCommandTest extends RepositoryTestCase {
public void testAddRemovedCommittedFile() throws Exception {
File file = new File(db.getWorkTree(), "a.txt");
FileUtils.createNewFile(file);
- try (PrintWriter writer = new PrintWriter(file)) {
+ try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) {
writer.print("content");
}
@@ -690,13 +691,13 @@ public class AddCommandTest extends RepositoryTestCase {
File file = new File(db.getWorkTree(), "a.txt");
FileUtils.createNewFile(file);
- try (PrintWriter writer = new PrintWriter(file)) {
+ try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) {
writer.print("content");
}
File file2 = new File(db.getWorkTree(), "b.txt");
FileUtils.createNewFile(file2);
- try (PrintWriter writer = new PrintWriter(file2)) {
+ try (PrintWriter writer = new PrintWriter(file2, UTF_8.name())) {
writer.print("content b");
}
@@ -707,12 +708,12 @@ public class AddCommandTest extends RepositoryTestCase {
addEntryToBuilder("b.txt", file2, newObjectInserter, builder, 0);
addEntryToBuilder("a.txt", file, newObjectInserter, builder, 1);
- try (PrintWriter writer = new PrintWriter(file)) {
+ try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) {
writer.print("other content");
}
addEntryToBuilder("a.txt", file, newObjectInserter, builder, 3);
- try (PrintWriter writer = new PrintWriter(file)) {
+ try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) {
writer.print("our content");
}
addEntryToBuilder("a.txt", file, newObjectInserter, builder, 2)
@@ -743,13 +744,13 @@ public class AddCommandTest extends RepositoryTestCase {
public void testAddTwoFiles() throws Exception {
File file = new File(db.getWorkTree(), "a.txt");
FileUtils.createNewFile(file);
- try (PrintWriter writer = new PrintWriter(file)) {
+ try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) {
writer.print("content");
}
File file2 = new File(db.getWorkTree(), "b.txt");
FileUtils.createNewFile(file2);
- try (PrintWriter writer = new PrintWriter(file2)) {
+ try (PrintWriter writer = new PrintWriter(file2, UTF_8.name())) {
writer.print("content b");
}
@@ -767,13 +768,13 @@ public class AddCommandTest extends RepositoryTestCase {
FileUtils.mkdir(new File(db.getWorkTree(), "sub"));
File file = new File(db.getWorkTree(), "sub/a.txt");
FileUtils.createNewFile(file);
- try (PrintWriter writer = new PrintWriter(file)) {
+ try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) {
writer.print("content");
}
File file2 = new File(db.getWorkTree(), "sub/b.txt");
FileUtils.createNewFile(file2);
- try (PrintWriter writer = new PrintWriter(file2)) {
+ try (PrintWriter writer = new PrintWriter(file2, UTF_8.name())) {
writer.print("content b");
}
@@ -791,19 +792,19 @@ public class AddCommandTest extends RepositoryTestCase {
FileUtils.mkdir(new File(db.getWorkTree(), "sub"));
File file = new File(db.getWorkTree(), "sub/a.txt");
FileUtils.createNewFile(file);
- try (PrintWriter writer = new PrintWriter(file)) {
+ try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) {
writer.print("content");
}
File ignoreFile = new File(db.getWorkTree(), ".gitignore");
FileUtils.createNewFile(ignoreFile);
- try (PrintWriter writer = new PrintWriter(ignoreFile)) {
+ try (PrintWriter writer = new PrintWriter(ignoreFile, UTF_8.name())) {
writer.print("sub/b.txt");
}
File file2 = new File(db.getWorkTree(), "sub/b.txt");
FileUtils.createNewFile(file2);
- try (PrintWriter writer = new PrintWriter(file2)) {
+ try (PrintWriter writer = new PrintWriter(file2, UTF_8.name())) {
writer.print("content b");
}
@@ -821,13 +822,13 @@ public class AddCommandTest extends RepositoryTestCase {
FileUtils.mkdir(new File(db.getWorkTree(), "sub"));
File file = new File(db.getWorkTree(), "sub/a.txt");
FileUtils.createNewFile(file);
- try (PrintWriter writer = new PrintWriter(file)) {
+ try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) {
writer.print("content");
}
File file2 = new File(db.getWorkTree(), "sub/b.txt");
FileUtils.createNewFile(file2);
- try (PrintWriter writer = new PrintWriter(file2)) {
+ try (PrintWriter writer = new PrintWriter(file2, UTF_8.name())) {
writer.print("content b");
}
@@ -849,13 +850,13 @@ public class AddCommandTest extends RepositoryTestCase {
FileUtils.mkdir(new File(db.getWorkTree(), "sub"));
File file = new File(db.getWorkTree(), "sub/a.txt");
FileUtils.createNewFile(file);
- try (PrintWriter writer = new PrintWriter(file)) {
+ try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) {
writer.print("content");
}
File file2 = new File(db.getWorkTree(), "sub/b.txt");
FileUtils.createNewFile(file2);
- try (PrintWriter writer = new PrintWriter(file2)) {
+ try (PrintWriter writer = new PrintWriter(file2, UTF_8.name())) {
writer.print("content b");
}
@@ -872,12 +873,12 @@ public class AddCommandTest extends RepositoryTestCase {
// new unstaged file sub/c.txt
File file3 = new File(db.getWorkTree(), "sub/c.txt");
FileUtils.createNewFile(file3);
- try (PrintWriter writer = new PrintWriter(file3)) {
+ try (PrintWriter writer = new PrintWriter(file3, UTF_8.name())) {
writer.print("content c");
}
// file sub/a.txt is modified
- try (PrintWriter writer = new PrintWriter(file)) {
+ try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) {
writer.print("modified content");
}
@@ -904,13 +905,13 @@ public class AddCommandTest extends RepositoryTestCase {
FileUtils.mkdir(new File(db.getWorkTree(), "sub"));
File file = new File(db.getWorkTree(), "sub/a.txt");
FileUtils.createNewFile(file);
- try (PrintWriter writer = new PrintWriter(file)) {
+ try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) {
writer.print("content");
}
File file2 = new File(db.getWorkTree(), "sub/b.txt");
FileUtils.createNewFile(file2);
- try (PrintWriter writer = new PrintWriter(file2)) {
+ try (PrintWriter writer = new PrintWriter(file2, UTF_8.name())) {
writer.print("content b");
}
@@ -927,12 +928,12 @@ public class AddCommandTest extends RepositoryTestCase {
// new unstaged file sub/c.txt
File file3 = new File(db.getWorkTree(), "sub/c.txt");
FileUtils.createNewFile(file3);
- try (PrintWriter writer = new PrintWriter(file3)) {
+ try (PrintWriter writer = new PrintWriter(file3, UTF_8.name())) {
writer.print("content c");
}
// file sub/a.txt is modified
- try (PrintWriter writer = new PrintWriter(file)) {
+ try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) {
writer.print("modified content");
}
@@ -1244,7 +1245,8 @@ public class AddCommandTest extends RepositoryTestCase {
ConfigConstants.CONFIG_KEY_DIRNOGITLINKS, true);
config.save();
- assert (db.getConfig().get(WorkingTreeOptions.KEY).isDirNoGitLinks());
+ assertTrue(
+ db.getConfig().get(WorkingTreeOptions.KEY).isDirNoGitLinks());
try (Git git = new Git(db)) {
git.add().addFilepattern("nested-repo").call();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ArchiveCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ArchiveCommandTest.java
index 1300f98d8a..1c41018161 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ArchiveCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ArchiveCommandTest.java
@@ -42,6 +42,7 @@
*/
package org.eclipse.jgit.api;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
@@ -190,7 +191,8 @@ public class ArchiveCommandTest extends RepositoryTestCase {
}
}
- private class MockFormat implements ArchiveCommand.Format<MockOutputStream> {
+ private static class MockFormat
+ implements ArchiveCommand.Format<MockOutputStream> {
private Map<String, String> entries = new HashMap<>();
@@ -230,7 +232,9 @@ public class ArchiveCommandTest extends RepositoryTestCase {
@Override
public void putEntry(MockOutputStream out, ObjectId tree, String path, FileMode mode, ObjectLoader loader) {
- String content = mode != FileMode.TREE ? new String(loader.getBytes()) : null;
+ String content = mode != FileMode.TREE
+ ? new String(loader.getBytes(), UTF_8)
+ : null;
entries.put(path, content);
}
@@ -240,7 +244,7 @@ public class ArchiveCommandTest extends RepositoryTestCase {
}
}
- public class MockOutputStream extends OutputStream {
+ public static class MockOutputStream extends OutputStream {
private int foo;
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/BlameCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/BlameCommandTest.java
index 7a1d222ca0..7e73084e8e 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/BlameCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/BlameCommandTest.java
@@ -44,6 +44,7 @@ package org.eclipse.jgit.api;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
import java.io.File;
@@ -489,4 +490,73 @@ public class BlameCommandTest extends RepositoryTestCase {
assertEquals(side, lines.getSourceCommit(2));
}
}
+
+ @Test
+ public void testBlameWithNulByteInHistory() throws Exception {
+ try (Git git = new Git(db)) {
+ String[] content1 = { "First line", "Another line" };
+ writeTrashFile("file.txt", join(content1));
+ git.add().addFilepattern("file.txt").call();
+ RevCommit c1 = git.commit().setMessage("create file").call();
+
+ String[] content2 = { "First line", "Second line with NUL >\000<",
+ "Another line" };
+ assertTrue("Content should contain a NUL byte",
+ content2[1].indexOf(0) > 0);
+ writeTrashFile("file.txt", join(content2));
+ git.add().addFilepattern("file.txt").call();
+ git.commit().setMessage("add line with NUL").call();
+
+ String[] content3 = { "First line", "Second line with NUL >\000<",
+ "Third line" };
+ writeTrashFile("file.txt", join(content3));
+ git.add().addFilepattern("file.txt").call();
+ RevCommit c3 = git.commit().setMessage("change third line").call();
+
+ String[] content4 = { "First line", "Second line with NUL >\\000<",
+ "Third line" };
+ assertTrue("Content should not contain a NUL byte",
+ content4[1].indexOf(0) < 0);
+ writeTrashFile("file.txt", join(content4));
+ git.add().addFilepattern("file.txt").call();
+ RevCommit c4 = git.commit().setMessage("fix NUL line").call();
+
+ BlameResult lines = git.blame().setFilePath("file.txt").call();
+ assertEquals(3, lines.getResultContents().size());
+ assertEquals(c1, lines.getSourceCommit(0));
+ assertEquals(c4, lines.getSourceCommit(1));
+ assertEquals(c3, lines.getSourceCommit(2));
+ }
+ }
+
+ @Test
+ public void testBlameWithNulByteInTopRevision() throws Exception {
+ try (Git git = new Git(db)) {
+ String[] content1 = { "First line", "Another line" };
+ writeTrashFile("file.txt", join(content1));
+ git.add().addFilepattern("file.txt").call();
+ RevCommit c1 = git.commit().setMessage("create file").call();
+
+ String[] content2 = { "First line", "Second line with NUL >\000<",
+ "Another line" };
+ assertTrue("Content should contain a NUL byte",
+ content2[1].indexOf(0) > 0);
+ writeTrashFile("file.txt", join(content2));
+ git.add().addFilepattern("file.txt").call();
+ RevCommit c2 = git.commit().setMessage("add line with NUL").call();
+
+ String[] content3 = { "First line", "Second line with NUL >\000<",
+ "Third line" };
+ writeTrashFile("file.txt", join(content3));
+ git.add().addFilepattern("file.txt").call();
+ RevCommit c3 = git.commit().setMessage("change third line").call();
+
+ BlameResult lines = git.blame().setFilePath("file.txt").call();
+ assertEquals(3, lines.getResultContents().size());
+ assertEquals(c1, lines.getSourceCommit(0));
+ assertEquals(c2, lines.getSourceCommit(1));
+ assertEquals(c3, lines.getSourceCommit(2));
+ }
+ }
+
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java
index 71df59e1ff..498005deda 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java
@@ -154,7 +154,7 @@ public class CheckoutCommandTest extends RepositoryTestCase {
}
@Test
- public void testCheckoutWithConflict() {
+ public void testCheckoutWithConflict() throws Exception {
CheckoutCommand co = git.checkout();
try {
writeTrashFile("Test.txt", "Another change");
@@ -165,6 +165,8 @@ public class CheckoutCommandTest extends RepositoryTestCase {
assertEquals(Status.CONFLICTS, co.getResult().getStatus());
assertTrue(co.getResult().getConflictList().contains("Test.txt"));
}
+ git.checkout().setName("master").setForce(true).call();
+ assertThat(read("Test.txt"), is("Hello world"));
}
@Test
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CleanCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CleanCommandTest.java
index 065b5b4c3e..139f199f7a 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CleanCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CleanCommandTest.java
@@ -234,6 +234,27 @@ public class CleanCommandTest extends RepositoryTestCase {
}
@Test
+ public void testCleanDirsWithPrefixFolder() throws Exception {
+ String path = "sub/foo.txt";
+ writeTrashFile(path, "sub is a prefix of sub-noclean");
+ git.add().addFilepattern(path).call();
+ Status beforeCleanStatus = git.status().call();
+ assertTrue(beforeCleanStatus.getAdded().contains(path));
+
+ Set<String> cleanedFiles = git.clean().setCleanDirectories(true).call();
+
+ // The "sub" directory should not be cleaned.
+ assertTrue(!cleanedFiles.contains(path + "/"));
+
+ assertTrue(cleanedFiles.contains("File2.txt"));
+ assertTrue(cleanedFiles.contains("File3.txt"));
+ assertTrue(!cleanedFiles.contains("sub-noclean/File1.txt"));
+ assertTrue(cleanedFiles.contains("sub-noclean/File2.txt"));
+ assertTrue(cleanedFiles.contains("sub-clean/"));
+ assertTrue(cleanedFiles.size() == 4);
+ }
+
+ @Test
public void testCleanDirsWithSubmodule() throws Exception {
SubmoduleAddCommand command = new SubmoduleAddCommand(db);
String path = "sub";
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CloneCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CloneCommandTest.java
index 0d7009dca4..6b5fe502b7 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CloneCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CloneCommandTest.java
@@ -370,8 +370,7 @@ public class CloneCommandTest extends RepositoryTestCase {
}
@Test
- public void testCloneRepositoryOnlyOneBranch() throws IOException,
- JGitInternalException, GitAPIException {
+ public void testCloneRepositoryOnlyOneBranch() throws Exception {
File directory = createTempDirectory("testCloneRepositoryWithBranch");
CloneCommand command = Git.cloneRepository();
command.setBranch("refs/heads/master");
@@ -382,25 +381,47 @@ public class CloneCommandTest extends RepositoryTestCase {
Git git2 = command.call();
addRepoToClose(git2.getRepository());
assertNotNull(git2);
+ assertNull(git2.getRepository().resolve("tag-for-blob"));
+ assertNotNull(git2.getRepository().resolve("tag-initial"));
assertEquals(git2.getRepository().getFullBranch(), "refs/heads/master");
assertEquals("refs/remotes/origin/master", allRefNames(git2
.branchList().setListMode(ListMode.REMOTE).call()));
+ RemoteConfig cfg = new RemoteConfig(git2.getRepository().getConfig(),
+ Constants.DEFAULT_REMOTE_NAME);
+ List<RefSpec> specs = cfg.getFetchRefSpecs();
+ assertEquals(1, specs.size());
+ assertEquals(
+ new RefSpec("+refs/heads/master:refs/remotes/origin/master"),
+ specs.get(0));
+ }
+ @Test
+ public void testBareCloneRepositoryOnlyOneBranch() throws Exception {
// Same thing, but now test with bare repo
- directory = createTempDirectory("testCloneRepositoryWithBranch_bare");
- command = Git.cloneRepository();
+ File directory = createTempDirectory(
+ "testCloneRepositoryWithBranch_bare");
+ CloneCommand command = Git.cloneRepository();
command.setBranch("refs/heads/master");
command.setBranchesToClone(Collections
.singletonList("refs/heads/master"));
command.setDirectory(directory);
command.setURI(fileUri());
command.setBare(true);
- git2 = command.call();
+ Git git2 = command.call();
addRepoToClose(git2.getRepository());
assertNotNull(git2);
+ assertNull(git2.getRepository().resolve("tag-for-blob"));
+ assertNotNull(git2.getRepository().resolve("tag-initial"));
assertEquals(git2.getRepository().getFullBranch(), "refs/heads/master");
assertEquals("refs/heads/master", allRefNames(git2.branchList()
.setListMode(ListMode.ALL).call()));
+ RemoteConfig cfg = new RemoteConfig(git2.getRepository().getConfig(),
+ Constants.DEFAULT_REMOTE_NAME);
+ List<RefSpec> specs = cfg.getFetchRefSpecs();
+ assertEquals(1, specs.size());
+ assertEquals(
+ new RefSpec("+refs/heads/master:refs/heads/master"),
+ specs.get(0));
}
public static String allRefNames(List<Ref> refs) {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitAndLogCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitAndLogCommandTest.java
index ca0630ea35..c028ca300c 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitAndLogCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitAndLogCommandTest.java
@@ -42,6 +42,7 @@
*/
package org.eclipse.jgit.api;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -120,7 +121,7 @@ public class CommitAndLogCommandTest extends RepositoryTestCase {
// create first file
File file = new File(db.getWorkTree(), "a.txt");
FileUtils.createNewFile(file);
- try (PrintWriter writer = new PrintWriter(file)) {
+ try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) {
writer.print("content1");
}
@@ -131,7 +132,7 @@ public class CommitAndLogCommandTest extends RepositoryTestCase {
// create second file
file = new File(db.getWorkTree(), "b.txt");
FileUtils.createNewFile(file);
- try (PrintWriter writer = new PrintWriter(file)) {
+ try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) {
writer.print("content2");
}
@@ -231,7 +232,7 @@ public class CommitAndLogCommandTest extends RepositoryTestCase {
JGitInternalException, GitAPIException {
File file = new File(db.getWorkTree(), "a.txt");
FileUtils.createNewFile(file);
- try (PrintWriter writer = new PrintWriter(file)) {
+ try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) {
writer.print("content");
}
@@ -242,7 +243,7 @@ public class CommitAndLogCommandTest extends RepositoryTestCase {
assertEquals("6b584e8ece562ebffc15d38808cd6b98fc3d97ea",
tw.getObjectId(0).getName());
- try (PrintWriter writer = new PrintWriter(file)) {
+ try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) {
writer.print("content2");
}
commit = git.commit().setMessage("second commit").call();
@@ -265,7 +266,7 @@ public class CommitAndLogCommandTest extends RepositoryTestCase {
// create file
File file = new File(db.getWorkTree(), "a.txt");
FileUtils.createNewFile(file);
- try (PrintWriter writer = new PrintWriter(file)) {
+ try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) {
writer.print("content1");
}
@@ -358,7 +359,7 @@ public class CommitAndLogCommandTest extends RepositoryTestCase {
messageHeader + messageFooter)
.setInsertChangeId(true).call();
// we should find a real change id (at the end of the file)
- byte[] chars = commit.getFullMessage().getBytes();
+ byte[] chars = commit.getFullMessage().getBytes(UTF_8);
int lastLineBegin = RawParseUtils.prevLF(chars, chars.length - 2);
String lastLine = RawParseUtils.decode(chars, lastLineBegin + 1,
chars.length);
@@ -371,7 +372,7 @@ public class CommitAndLogCommandTest extends RepositoryTestCase {
.setInsertChangeId(true).call();
// we should find a real change id (in the line as dictated by the
// template)
- chars = commit.getFullMessage().getBytes();
+ chars = commit.getFullMessage().getBytes(UTF_8);
int lineStart = 0;
int lineEnd = 0;
for (int i = 0; i < 4; i++) {
@@ -389,7 +390,7 @@ public class CommitAndLogCommandTest extends RepositoryTestCase {
messageHeader + changeIdTemplate + messageFooter)
.setInsertChangeId(false).call();
// we should find the untouched template
- chars = commit.getFullMessage().getBytes();
+ chars = commit.getFullMessage().getBytes(UTF_8);
lineStart = 0;
lineEnd = 0;
for (int i = 0; i < 4; i++) {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitOnlyTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitOnlyTest.java
index bbd6ec0bde..83181eea23 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitOnlyTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitOnlyTest.java
@@ -43,6 +43,7 @@
*/
package org.eclipse.jgit.api;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@@ -1298,7 +1299,7 @@ public class CommitOnlyTest extends RepositoryTestCase {
final TreeWalk tw = TreeWalk.forPath(repo, path,
rw.parseTree(headId));
return new String(tw.getObjectReader().open(tw.getObjectId(0))
- .getBytes());
+ .getBytes(), UTF_8);
}
} catch (Exception e) {
return "";
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CrLfNativeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CrLfNativeTest.java
new file mode 100644
index 0000000000..c72612850a
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CrLfNativeTest.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.api;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.jgit.api.ResetCommand.ResetType;
+import org.eclipse.jgit.junit.MockSystemReader;
+import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.lib.CoreConfig.EolStreamType;
+import org.eclipse.jgit.storage.file.FileBasedConfig;
+import org.eclipse.jgit.treewalk.FileTreeIterator;
+import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.treewalk.TreeWalk.OperationType;
+import org.eclipse.jgit.util.SystemReader;
+import org.junit.Test;
+
+public class CrLfNativeTest extends RepositoryTestCase {
+
+ @Test
+ public void checkoutWithCrLfNativeUnix() throws Exception {
+ verifyNativeCheckout(new MockSystemReader() {
+ {
+ setUnix();
+ }
+ });
+ }
+
+ @Test
+ public void checkoutWithCrLfNativeWindows() throws Exception {
+ verifyNativeCheckout(new MockSystemReader() {
+ {
+ setWindows();
+ }
+ });
+ }
+
+ private void verifyNativeCheckout(SystemReader systemReader)
+ throws Exception {
+ SystemReader.setInstance(systemReader);
+ Git git = Git.wrap(db);
+ FileBasedConfig config = db.getConfig();
+ config.setString("core", null, "autocrlf", "false");
+ config.setString("core", null, "eol", "native");
+ config.save();
+ // core.eol is active only if text is set, or if text=auto
+ writeTrashFile(".gitattributes", "*.txt text\n");
+ File file = writeTrashFile("file.txt", "line 1\nline 2\n");
+ git.add().addFilepattern("file.txt").addFilepattern(".gitattributes")
+ .call();
+ git.commit().setMessage("Initial").call();
+ // Check-in with core.eol=native normalization
+ assertEquals(
+ "[.gitattributes, mode:100644, content:*.txt text\n]"
+ + "[file.txt, mode:100644, content:line 1\nline 2\n]",
+ indexState(CONTENT));
+ writeTrashFile("file.txt", "something else");
+ git.add().addFilepattern("file.txt").call();
+ git.commit().setMessage("New commit").call();
+ git.reset().setMode(ResetType.HARD).setRef("HEAD~").call();
+ // Check-out should convert to the native line separator
+ checkFile(file, systemReader.isWindows() ? "line 1\r\nline 2\r\n"
+ : "line 1\nline 2\n");
+ Status status = git.status().call();
+ assertTrue("git status should be clean", status.isClean());
+ }
+
+ /**
+ * Verifies the handling of the crlf attribute: crlf == text, -crlf ==
+ * -text, crlf=input == eol=lf
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testCrLfAttribute() throws Exception {
+ FileBasedConfig config = db.getConfig();
+ config.setString("core", null, "autocrlf", "false");
+ config.setString("core", null, "eol", "crlf");
+ config.save();
+ writeTrashFile(".gitattributes",
+ "*.txt text\n*.crlf crlf\n*.bin -text\n*.nocrlf -crlf\n*.input crlf=input\n*.eol eol=lf");
+ writeTrashFile("foo.txt", "");
+ writeTrashFile("foo.crlf", "");
+ writeTrashFile("foo.bin", "");
+ writeTrashFile("foo.nocrlf", "");
+ writeTrashFile("foo.input", "");
+ writeTrashFile("foo.eol", "");
+ Map<String, EolStreamType> inTypes = new HashMap<>();
+ Map<String, EolStreamType> outTypes = new HashMap<>();
+ try (TreeWalk walk = new TreeWalk(db)) {
+ walk.addTree(new FileTreeIterator(db));
+ while (walk.next()) {
+ String path = walk.getPathString();
+ if (".gitattributes".equals(path)) {
+ continue;
+ }
+ EolStreamType in = walk
+ .getEolStreamType(OperationType.CHECKIN_OP);
+ EolStreamType out = walk
+ .getEolStreamType(OperationType.CHECKOUT_OP);
+ inTypes.put(path, in);
+ outTypes.put(path, out);
+ }
+ }
+ assertEquals("", checkTypes("check-in", inTypes));
+ assertEquals("", checkTypes("check-out", outTypes));
+ }
+
+ private String checkTypes(String prefix, Map<String, EolStreamType> types) {
+ StringBuilder result = new StringBuilder();
+ EolStreamType a = types.get("foo.crlf");
+ EolStreamType b = types.get("foo.txt");
+ report(result, prefix, "crlf != text", a, b);
+ a = types.get("foo.nocrlf");
+ b = types.get("foo.bin");
+ report(result, prefix, "-crlf != -text", a, b);
+ a = types.get("foo.input");
+ b = types.get("foo.eol");
+ report(result, prefix, "crlf=input != eol=lf", a, b);
+ return result.toString();
+ }
+
+ private void report(StringBuilder result, String prefix, String label,
+ EolStreamType a,
+ EolStreamType b) {
+ if (a == null || b == null || !a.equals(b)) {
+ result.append(prefix).append(' ').append(label).append(": ")
+ .append(toString(a)).append(" != ").append(toString(b))
+ .append('\n');
+ }
+ }
+
+ private String toString(EolStreamType type) {
+ return type == null ? "null" : type.name();
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/DescribeCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/DescribeCommandTest.java
index f2093e3940..807079eb23 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/DescribeCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/DescribeCommandTest.java
@@ -42,14 +42,16 @@
*/
package org.eclipse.jgit.api;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import java.io.BufferedWriter;
import java.io.File;
-import java.io.FileWriter;
import java.io.IOException;
+import java.nio.file.Files;
import java.util.Arrays;
import java.util.Collection;
@@ -427,7 +429,7 @@ public class DescribeCommandTest extends RepositoryTestCase {
}
private static void touch(File f, String contents) throws Exception {
- try (FileWriter w = new FileWriter(f)) {
+ try (BufferedWriter w = Files.newBufferedWriter(f.toPath(), UTF_8)) {
w.write(contents);
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/EolRepositoryTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/EolRepositoryTest.java
index 48d373344e..47806cb99d 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/EolRepositoryTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/EolRepositoryTest.java
@@ -38,6 +38,7 @@
*/
package org.eclipse.jgit.api;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -722,10 +723,10 @@ public class EolRepositoryTest extends RepositoryTestCase {
}
e.attrs = e.attrs.trim();
e.file = new String(
- IO.readFully(new File(db.getWorkTree(), pathName)));
+ IO.readFully(new File(db.getWorkTree(), pathName)), UTF_8);
DirCacheEntry dce = dirCache.getEntry(pathName);
ObjectLoader open = walk.getObjectReader().open(dce.getObjectId());
- e.index = new String(open.getBytes());
+ e.index = new String(open.getBytes(), UTF_8);
e.indexContentLength = dce.getLength();
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PushCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PushCommandTest.java
index ca86d81301..98dfcc083e 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PushCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PushCommandTest.java
@@ -148,7 +148,7 @@ public class PushCommandTest extends RepositoryTestCase {
git1.push().setRemote("test").setRefSpecs(spec).call();
assertEquals("1:test, 2:" + uri + ", 3:\n" + "refs/heads/master "
+ commit.getName() + " refs/heads/x "
- + ObjectId.zeroId().name(), read(hookOutput));
+ + ObjectId.zeroId().name() + "\n", read(hookOutput));
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ResetCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ResetCommandTest.java
index 2b97b307bf..9b12011aab 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ResetCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ResetCommandTest.java
@@ -82,6 +82,8 @@ public class ResetCommandTest extends RepositoryTestCase {
private File indexFile;
+ private File indexNestedFile;
+
private File untrackedFile;
private DirCacheEntry prestage;
@@ -97,7 +99,7 @@ public class ResetCommandTest extends RepositoryTestCase {
indexFile = writeTrashFile("a.txt", "content");
// create nested file
- writeTrashFile("dir/b.txt", "content");
+ indexNestedFile = writeTrashFile("dir/b.txt", "content");
// add files and commit them
git.add().addFilepattern("a.txt").addFilepattern("dir/b.txt").call();
@@ -119,13 +121,16 @@ public class ResetCommandTest extends RepositoryTestCase {
AmbiguousObjectException, IOException, GitAPIException {
setupRepository();
ObjectId prevHead = db.resolve(Constants.HEAD);
- assertSameAsHead(git.reset().setMode(ResetType.HARD)
+ ResetCommand reset = git.reset();
+ assertSameAsHead(reset.setMode(ResetType.HARD)
.setRef(initialCommit.getName()).call());
+ assertFalse("reflog should be enabled", reset.isReflogDisabled());
// check if HEAD points to initial commit now
ObjectId head = db.resolve(Constants.HEAD);
assertEquals(initialCommit, head);
// check if files were removed
assertFalse(indexFile.exists());
+ assertFalse(indexNestedFile.exists());
assertTrue(untrackedFile.exists());
// fileInIndex must no longer be in HEAD and in the index
String fileInIndexPath = indexFile.getAbsolutePath();
@@ -148,6 +153,7 @@ public class ResetCommandTest extends RepositoryTestCase {
assertEquals(initialCommit, head);
// check if files were removed
assertFalse(indexFile.exists());
+ assertFalse(indexNestedFile.exists());
assertTrue(untrackedFile.exists());
// fileInIndex must no longer be in HEAD and in the index
String fileInIndexPath = indexFile.getAbsolutePath();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesMatcherTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesMatcherTest.java
index 196c4f7d9c..08553e1ae4 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesMatcherTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesMatcherTest.java
@@ -415,6 +415,14 @@ public class AttributesMatcherTest {
}
}
+ @Test
+ public void testFileNameWithLineTerminator() {
+ assertMatched("a?", "a\r");
+ assertMatched("a?", "dir/a\r");
+ assertMatched("*a", "\ra");
+ assertMatched("dir/*a*", "dir/\ra\r");
+ }
+
/**
* Check for a match. If target ends with "/", match will assume that the
* target is meant to be a directory.
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeTest.java
index f0d3c3690f..f4ccf0506b 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeTest.java
@@ -42,6 +42,7 @@
*/
package org.eclipse.jgit.attributes;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static org.eclipse.jgit.attributes.Attribute.State.SET;
import static org.eclipse.jgit.attributes.Attribute.State.UNSET;
import static org.junit.Assert.assertEquals;
@@ -88,7 +89,7 @@ public class AttributesNodeTest {
String attributeFileContent = "*.type1 A -B C=value\n"
+ "*.type2 -A B C=value2";
- is = new ByteArrayInputStream(attributeFileContent.getBytes());
+ is = new ByteArrayInputStream(attributeFileContent.getBytes(UTF_8));
AttributesNode node = new AttributesNode();
node.parse(is);
assertAttribute("file.type1", node,
@@ -102,7 +103,7 @@ public class AttributesNodeTest {
String attributeFileContent = "!*.type1 A -B C=value\n"
+ "!*.type2 -A B C=value2";
- is = new ByteArrayInputStream(attributeFileContent.getBytes());
+ is = new ByteArrayInputStream(attributeFileContent.getBytes(UTF_8));
AttributesNode node = new AttributesNode();
node.parse(is);
assertAttribute("file.type1", node, new Attributes());
@@ -113,7 +114,7 @@ public class AttributesNodeTest {
public void testEmptyNegativeAttributeKey() throws IOException {
String attributeFileContent = "*.type1 - \n" //
+ "*.type2 - -A";
- is = new ByteArrayInputStream(attributeFileContent.getBytes());
+ is = new ByteArrayInputStream(attributeFileContent.getBytes(UTF_8));
AttributesNode node = new AttributesNode();
node.parse(is);
assertAttribute("file.type1", node, new Attributes());
@@ -125,7 +126,7 @@ public class AttributesNodeTest {
String attributeFileContent = "*.type1 = \n" //
+ "*.type2 =value\n"//
+ "*.type3 attr=\n";
- is = new ByteArrayInputStream(attributeFileContent.getBytes());
+ is = new ByteArrayInputStream(attributeFileContent.getBytes(UTF_8));
AttributesNode node = new AttributesNode();
node.parse(is);
assertAttribute("file.type1", node, new Attributes());
@@ -140,7 +141,7 @@ public class AttributesNodeTest {
+ " \n" //
+ "*.type2 -A B C=value2";
- is = new ByteArrayInputStream(attributeFileContent.getBytes());
+ is = new ByteArrayInputStream(attributeFileContent.getBytes(UTF_8));
AttributesNode node = new AttributesNode();
node.parse(is);
assertAttribute("file.type1", node,
@@ -156,7 +157,7 @@ public class AttributesNodeTest {
+ "*.type3 \t\t B\n" //
+ "*.type3\t-A";//
- is = new ByteArrayInputStream(attributeFileContent.getBytes());
+ is = new ByteArrayInputStream(attributeFileContent.getBytes(UTF_8));
AttributesNode node = new AttributesNode();
node.parse(is);
assertAttribute("file.type1", node,
@@ -170,7 +171,7 @@ public class AttributesNodeTest {
public void testDoubleAsteriskAtEnd() throws IOException {
String attributeFileContent = "dir/** \tA -B\tC=value";
- is = new ByteArrayInputStream(attributeFileContent.getBytes());
+ is = new ByteArrayInputStream(attributeFileContent.getBytes(UTF_8));
AttributesNode node = new AttributesNode();
node.parse(is);
assertAttribute("dir", node,
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffEntryTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffEntryTest.java
index 73c230ac68..de768118bf 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffEntryTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffEntryTest.java
@@ -59,9 +59,12 @@ import org.eclipse.jgit.diff.DiffEntry.ChangeType;
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.dircache.DirCacheEditor;
import org.eclipse.jgit.dircache.DirCacheEditor.PathEdit;
+import org.eclipse.jgit.internal.storage.file.FileRepository;
import org.eclipse.jgit.dircache.DirCacheEntry;
+import org.eclipse.jgit.junit.JGitTestUtil;
import org.eclipse.jgit.junit.RepositoryTestCase;
import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.treewalk.EmptyTreeIterator;
import org.eclipse.jgit.treewalk.FileTreeIterator;
@@ -417,4 +420,64 @@ public class DiffEntryTest extends RepositoryTestCase {
assertEquals(FileMode.REGULAR_FILE, diff.getOldMode());
}
}
+
+ @Test
+ public void shouldReportSubmoduleReplacedByFileMove() throws Exception {
+ // Create a submodule
+ FileRepository submoduleStandalone = createWorkRepository();
+ JGitTestUtil.writeTrashFile(submoduleStandalone, "fileInSubmodule",
+ "submodule");
+ Git submoduleStandaloneGit = Git.wrap(submoduleStandalone);
+ submoduleStandaloneGit.add().addFilepattern("fileInSubmodule").call();
+ submoduleStandaloneGit.commit().setMessage("add file to submodule")
+ .call();
+
+ Repository submodule_db = Git.wrap(db).submoduleAdd()
+ .setPath("modules/submodule")
+ .setURI(submoduleStandalone.getDirectory().toURI().toString())
+ .call();
+ File submodule_trash = submodule_db.getWorkTree();
+ addRepoToClose(submodule_db);
+ writeTrashFile("fileInRoot", "root");
+ Git rootGit = Git.wrap(db);
+ rootGit.add().addFilepattern("fileInRoot").call();
+ rootGit.commit().setMessage("add submodule and root file").call();
+ // Dummy change on fileInRoot
+ writeTrashFile("fileInRoot", "changed");
+ rootGit.add().addFilepattern("fileInRoot").call();
+ RevCommit firstCommit = rootGit.commit().setMessage("change root file")
+ .call();
+ // Remove the submodule again and move fileInRoot into that subfolder
+ rootGit.rm().setCached(true).addFilepattern("modules/submodule").call();
+ recursiveDelete(submodule_trash);
+ JGitTestUtil.deleteTrashFile(db, "fileInRoot");
+ // Move the fileInRoot file
+ writeTrashFile("modules/submodule/fileInRoot", "changed");
+ rootGit.rm().addFilepattern("fileInRoot").addFilepattern("modules/")
+ .call();
+ rootGit.add().addFilepattern("modules/").call();
+ RevCommit secondCommit = rootGit.commit()
+ .setMessage("remove submodule and move root file")
+ .call();
+ // Diff should report submodule having been deleted and file moved
+ // (deleted and added)
+ try (TreeWalk walk = new TreeWalk(db)) {
+ walk.addTree(firstCommit.getTree());
+ walk.addTree(secondCommit.getTree());
+ walk.setRecursive(true);
+ List<DiffEntry> diffs = DiffEntry.scan(walk);
+ assertEquals(3, diffs.size());
+ DiffEntry e = diffs.get(0);
+ assertEquals(DiffEntry.ChangeType.DELETE, e.getChangeType());
+ assertEquals("fileInRoot", e.getOldPath());
+ e = diffs.get(1);
+ assertEquals(DiffEntry.ChangeType.DELETE, e.getChangeType());
+ assertEquals("modules/submodule", e.getOldPath());
+ assertEquals(FileMode.GITLINK, e.getOldMode());
+ e = diffs.get(2);
+ assertEquals(DiffEntry.ChangeType.ADD, e.getChangeType());
+ assertEquals("modules/submodule/fileInRoot", e.getNewPath());
+ }
+
+ }
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/RawTextTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/RawTextTest.java
index 5885d9b7e6..178d62072d 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/RawTextTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/RawTextTest.java
@@ -66,13 +66,16 @@ public class RawTextTest {
}
@Test
- public void testBinary() {
+ public void testNul() {
String input = "foo-a\nf\0o-b\n";
byte[] data = Constants.encodeASCII(input);
final RawText a = new RawText(data);
assertArrayEquals(a.content, data);
- assertEquals(a.size(), 1);
- assertEquals(a.getString(0, 1, false), input);
+ assertEquals(2, a.size());
+ assertEquals("foo-a\n", a.getString(0, 1, false));
+ assertEquals("f\0o-b\n", a.getString(1, 2, false));
+ assertEquals("foo-a", a.getString(0, 1, true));
+ assertEquals("f\0o-b", a.getString(1, 2, true));
}
@Test
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBuilderTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBuilderTest.java
index d12f302dae..b6291bfca4 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBuilderTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBuilderTest.java
@@ -89,6 +89,7 @@ public class DirCacheBuilderTest extends RepositoryTestCase {
assertEquals(0, e.getRawMode());
try {
b.add(e);
+ fail("did not reject unset file mode");
} catch (IllegalArgumentException err) {
assertEquals("FileMode not set for path a", err.getMessage());
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandTest.java
index 92f0cf6196..1f6861b356 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandTest.java
@@ -52,16 +52,20 @@ import static org.junit.Assert.fail;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
-import java.io.FileReader;
import java.io.IOException;
import java.net.URI;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.api.errors.InvalidRefNameException;
import org.eclipse.jgit.api.errors.InvalidRemoteException;
-import org.eclipse.jgit.api.errors.RefNotFoundException;
+import org.eclipse.jgit.gitrepo.RepoCommand.RemoteFile;
+import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.junit.JGitTestUtil;
import org.eclipse.jgit.junit.RepositoryTestCase;
import org.eclipse.jgit.lib.BlobBasedConfig;
@@ -73,6 +77,7 @@ import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.storage.file.FileBasedConfig;
+import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.util.FS;
import org.junit.Test;
@@ -139,8 +144,9 @@ public class RepoCommandTest extends RepositoryTestCase {
resolveRelativeUris();
}
- class IndexedRepos implements RepoCommand.RemoteReader {
+ static class IndexedRepos implements RepoCommand.RemoteReader {
Map<String, Repository> uriRepoMap;
+
IndexedRepos() {
uriRepoMap = new HashMap<>();
}
@@ -169,19 +175,21 @@ public class RepoCommandTest extends RepositoryTestCase {
}
@Override
- public byte[] readFile(String uri, String refName, String path)
- throws GitAPIException, IOException {
+ public RemoteFile readFileWithMode(String uri, String ref, String path)
+ throws GitAPIException, IOException {
Repository repo = uriRepoMap.get(uri);
-
- String idStr = refName + ":" + path;
- ObjectId id = repo.resolve(idStr);
- if (id == null) {
- throw new RefNotFoundException(
- String.format("repo %s does not have %s", repo.toString(), idStr));
- }
- try (ObjectReader reader = repo.newObjectReader()) {
- return reader.open(id).getCachedBytes(Integer.MAX_VALUE);
+ ObjectId refCommitId = sha1(uri, ref);
+ if (refCommitId == null) {
+ throw new InvalidRefNameException(MessageFormat
+ .format(JGitText.get().refNotResolved, ref));
}
+ RevCommit commit = repo.parseCommit(refCommitId);
+ TreeWalk tw = TreeWalk.forPath(repo, path, commit.getTree());
+
+ // TODO(ifrade): Cope better with big files (e.g. using InputStream
+ // instead of byte[])
+ return new RemoteFile(tw.getObjectReader().open(tw.getObjectId(0))
+ .getCachedBytes(Integer.MAX_VALUE), tw.getFileMode(0));
}
}
@@ -199,6 +207,15 @@ public class RepoCommandTest extends RepositoryTestCase {
return r;
}
+ private static void assertContents(Path path, String expected)
+ throws IOException {
+ try (BufferedReader reader = Files.newBufferedReader(path, UTF_8)) {
+ String content = reader.readLine();
+ assertEquals("Unexpected content in " + path.getFileName(),
+ expected, content);
+ }
+ }
+
@Test
public void runTwiceIsNOP() throws Exception {
try (Repository child = cloneRepository(groupADb, true);
@@ -474,12 +491,7 @@ public class RepoCommandTest extends RepositoryTestCase {
.call();
File hello = new File(db.getWorkTree(), "foo/hello.txt");
assertTrue("submodule should be checked out", hello.exists());
- try (BufferedReader reader = new BufferedReader(
- new FileReader(hello))) {
- String content = reader.readLine();
- assertEquals("submodule content should be as expected",
- "master world", content);
- }
+ assertContents(hello.toPath(), "master world");
}
@Test
@@ -565,20 +577,66 @@ public class RepoCommandTest extends RepositoryTestCase {
// The original file should exist
File hello = new File(localDb.getWorkTree(), "foo/hello.txt");
assertTrue("The original file should exist", hello.exists());
- try (BufferedReader reader = new BufferedReader(
- new FileReader(hello))) {
+ assertFalse("The original file should not be executable",
+ hello.canExecute());
+ assertContents(hello.toPath(), "master world");
+ // The dest file should also exist
+ hello = new File(localDb.getWorkTree(), "Hello");
+ assertTrue("The destination file should exist", hello.exists());
+ assertFalse("The destination file should not be executable",
+ hello.canExecute());
+ assertContents(hello.toPath(), "master world");
+ }
+
+ @Test
+ public void testRepoManifestCopyFile_executable() throws Exception {
+ try (Git git = new Git(defaultDb)) {
+ git.checkout().setName("master").call();
+ File f = JGitTestUtil.writeTrashFile(defaultDb, "hello.sh",
+ "content of the executable file");
+ f.setExecutable(true);
+ git.add().addFilepattern("hello.sh").call();
+ git.commit().setMessage("Add binary file").call();
+ }
+
+ Repository localDb = createWorkRepository();
+ StringBuilder xmlContent = new StringBuilder();
+ xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
+ .append("<manifest>")
+ .append("<remote name=\"remote1\" fetch=\".\" />")
+ .append("<default revision=\"master\" remote=\"remote1\" />")
+ .append("<project path=\"foo\" name=\"").append(defaultUri)
+ .append("\">")
+ .append("<copyfile src=\"hello.sh\" dest=\"copy-hello.sh\" />")
+ .append("</project>").append("</manifest>");
+ JGitTestUtil.writeTrashFile(localDb, "manifest.xml",
+ xmlContent.toString());
+ RepoCommand command = new RepoCommand(localDb);
+ command.setPath(
+ localDb.getWorkTree().getAbsolutePath() + "/manifest.xml")
+ .setURI(rootUri).call();
+
+ // The original file should exist and be an executable
+ File hello = new File(localDb.getWorkTree(), "foo/hello.sh");
+ assertTrue("The original file should exist", hello.exists());
+ assertTrue("The original file must be executable", hello.canExecute());
+ try (BufferedReader reader = Files.newBufferedReader(hello.toPath(),
+ UTF_8)) {
String content = reader.readLine();
assertEquals("The original file should have expected content",
- "master world", content);
+ "content of the executable file", content);
}
- // The dest file should also exist
- hello = new File(localDb.getWorkTree(), "Hello");
+
+ // The destination file should also exist and be an executable
+ hello = new File(localDb.getWorkTree(), "copy-hello.sh");
assertTrue("The destination file should exist", hello.exists());
- try (BufferedReader reader = new BufferedReader(
- new FileReader(hello))) {
+ assertTrue("The destination file must be executable",
+ hello.canExecute());
+ try (BufferedReader reader = Files.newBufferedReader(hello.toPath(),
+ UTF_8)) {
String content = reader.readLine();
assertEquals("The destination file should have expected content",
- "master world", content);
+ "content of the executable file", content);
}
}
@@ -610,8 +668,8 @@ public class RepoCommandTest extends RepositoryTestCase {
assertTrue("The .gitmodules file should exist",
gitmodules.exists());
// The first line of .gitmodules file should be expected
- try (BufferedReader reader = new BufferedReader(
- new FileReader(gitmodules))) {
+ try (BufferedReader reader = Files
+ .newBufferedReader(gitmodules.toPath(), UTF_8)) {
String content = reader.readLine();
assertEquals(
"The first line of .gitmodules file should be as expected",
@@ -644,8 +702,8 @@ public class RepoCommandTest extends RepositoryTestCase {
.setURI(rootUri)
.call();
File hello = new File(db.getWorkTree(), "foo/hello.txt");
- try (BufferedReader reader = new BufferedReader(
- new FileReader(hello))) {
+ try (BufferedReader reader = Files.newBufferedReader(hello.toPath(),
+ UTF_8)) {
String content = reader.readLine();
assertEquals("submodule content should be as expected",
"branch world", content);
@@ -671,12 +729,7 @@ public class RepoCommandTest extends RepositoryTestCase {
.setURI(rootUri)
.call();
File hello = new File(db.getWorkTree(), "foo/hello.txt");
- try (BufferedReader reader = new BufferedReader(
- new FileReader(hello))) {
- String content = reader.readLine();
- assertEquals("submodule content should be as expected",
- "branch world", content);
- }
+ assertContents(hello.toPath(), "branch world");
}
@Test
@@ -698,12 +751,7 @@ public class RepoCommandTest extends RepositoryTestCase {
.setURI(rootUri)
.call();
File hello = new File(db.getWorkTree(), "foo/hello.txt");
- try (BufferedReader reader = new BufferedReader(
- new FileReader(hello))) {
- String content = reader.readLine();
- assertEquals("submodule content should be as expected",
- "branch world", content);
- }
+ assertContents(hello.toPath(), "branch world");
}
@Test
@@ -771,12 +819,69 @@ public class RepoCommandTest extends RepositoryTestCase {
assertFalse("The foo/Hello file should be skipped",
foohello.exists());
// The content of Hello file should be expected
- try (BufferedReader reader = new BufferedReader(
- new FileReader(hello))) {
+ assertContents(hello.toPath(), "branch world");
+ }
+ }
+
+ @Test
+ public void testCopyFileBare_executable() throws Exception {
+ try (Git git = new Git(defaultDb)) {
+ git.checkout().setName(BRANCH).call();
+ File f = JGitTestUtil.writeTrashFile(defaultDb, "hello.sh",
+ "content of the executable file");
+ f.setExecutable(true);
+ git.add().addFilepattern("hello.sh").call();
+ git.commit().setMessage("Add binary file").call();
+ }
+
+ Repository remoteDb = createBareRepository();
+ Repository tempDb = createWorkRepository();
+
+ StringBuilder xmlContent = new StringBuilder();
+ xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
+ .append("<manifest>")
+ .append("<remote name=\"remote1\" fetch=\".\" />")
+ .append("<default revision=\"master\" remote=\"remote1\" />")
+ .append("<project path=\"foo\" name=\"").append(defaultUri)
+ .append("\" revision=\"").append(BRANCH)
+ .append("\" >")
+ .append("<copyfile src=\"hello.txt\" dest=\"Hello\" />")
+ .append("<copyfile src=\"hello.txt\" dest=\"foo/Hello\" />")
+ .append("<copyfile src=\"hello.sh\" dest=\"copy-hello.sh\" />")
+ .append("</project>").append("</manifest>");
+ JGitTestUtil.writeTrashFile(tempDb, "manifest.xml",
+ xmlContent.toString());
+ RepoCommand command = new RepoCommand(remoteDb);
+ command.setPath(
+ tempDb.getWorkTree().getAbsolutePath() + "/manifest.xml")
+ .setURI(rootUri).call();
+ // Clone it
+ File directory = createTempDirectory("testCopyFileBare");
+ try (Repository localDb = Git.cloneRepository().setDirectory(directory)
+ .setURI(remoteDb.getDirectory().toURI().toString()).call()
+ .getRepository()) {
+ // The Hello file should exist
+ File hello = new File(localDb.getWorkTree(), "Hello");
+ assertTrue("The Hello file should exist", hello.exists());
+ // The foo/Hello file should be skipped.
+ File foohello = new File(localDb.getWorkTree(), "foo/Hello");
+ assertFalse("The foo/Hello file should be skipped",
+ foohello.exists());
+ // The content of Hello file should be expected
+ try (BufferedReader reader = Files.newBufferedReader(hello.toPath(),
+ UTF_8)) {
String content = reader.readLine();
assertEquals("The Hello file should have expected content",
"branch world", content);
}
+
+ // The executable file must be there and preserve the executable bit
+ File helloSh = new File(localDb.getWorkTree(), "copy-hello.sh");
+ assertTrue("Destination file should exist", helloSh.exists());
+ assertContents(helloSh.toPath(), "content of the executable file");
+ assertTrue("Destination file should be executable",
+ helloSh.canExecute());
+
}
}
@@ -829,8 +934,8 @@ public class RepoCommandTest extends RepositoryTestCase {
// The .gitmodules file should have 'submodule "bar"' and shouldn't
// have
// 'submodule "foo"' lines.
- try (BufferedReader reader = new BufferedReader(
- new FileReader(dotmodules))) {
+ try (BufferedReader reader = Files
+ .newBufferedReader(dotmodules.toPath(), UTF_8)) {
boolean foo = false;
boolean bar = false;
while (true) {
@@ -879,8 +984,8 @@ public class RepoCommandTest extends RepositoryTestCase {
}
// Check .gitmodules file
- try (BufferedReader reader = new BufferedReader(
- new FileReader(dotmodules))) {
+ try (BufferedReader reader = Files
+ .newBufferedReader(dotmodules.toPath(), UTF_8)) {
boolean foo = false;
boolean foobar = false;
boolean a = false;
@@ -935,8 +1040,8 @@ public class RepoCommandTest extends RepositoryTestCase {
.call();
File hello = new File(localDb.getWorkTree(), "foo/hello.txt");
assertTrue("submodule should be checked out", hello.exists());
- try (BufferedReader reader = new BufferedReader(
- new FileReader(hello))) {
+ try (BufferedReader reader = Files.newBufferedReader(hello.toPath(),
+ UTF_8)) {
String content = reader.readLine();
assertEquals("submodule content should be as expected",
"master world", content);
@@ -1074,8 +1179,9 @@ public class RepoCommandTest extends RepositoryTestCase {
".gitattributes");
assertTrue("The .gitattributes file should exist",
gitattributes.exists());
- try (BufferedReader reader = new BufferedReader(
- new FileReader(gitattributes));) {
+ try (BufferedReader reader = Files
+ .newBufferedReader(gitattributes.toPath(),
+ UTF_8)) {
String content = reader.readLine();
assertEquals(".gitattributes content should be as expected",
"/test a1 a2", content);
@@ -1142,12 +1248,7 @@ public class RepoCommandTest extends RepositoryTestCase {
.setURI(rootUri)
.call();
File hello = new File(db.getWorkTree(), "foo/hello.txt");
- try (BufferedReader reader = new BufferedReader(
- new FileReader(hello))) {
- String content = reader.readLine();
- assertEquals("submodule content should be as expected",
- "branch world", content);
- }
+ assertContents(hello.toPath(), "branch world");
}
@Test
@@ -1169,8 +1270,8 @@ public class RepoCommandTest extends RepositoryTestCase {
.setURI(rootUri)
.call();
File hello = new File(db.getWorkTree(), "foo/hello.txt");
- try (BufferedReader reader = new BufferedReader(
- new FileReader(hello))) {
+ try (BufferedReader reader = Files.newBufferedReader(hello.toPath(),
+ UTF_8)) {
String content = reader.readLine();
assertEquals("submodule content should be as expected",
"branch world", content);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/FastIgnoreRuleTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/FastIgnoreRuleTest.java
index 2a1721e66c..4bd1dab3e8 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/FastIgnoreRuleTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/FastIgnoreRuleTest.java
@@ -512,6 +512,15 @@ public class FastIgnoreRuleTest {
assertMatched("x/**/", "x/y/a/");
}
+ @Test
+ public void testFileNameWithLineTerminator() {
+ assertMatched("a?", "a\r");
+ assertMatched("a?", "dir/a\r");
+ assertMatched("a?", "a\r/file");
+ assertMatched("*a", "\ra");
+ assertMatched("dir/*a*", "dir/\ra\r");
+ }
+
private void assertMatched(String pattern, String path) {
boolean match = match(pattern, path);
String result = path + " is " + (match ? "ignored" : "not ignored")
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsFsckTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsFsckTest.java
index 804d744ae2..c1811251c6 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsFsckTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsFsckTest.java
@@ -266,4 +266,59 @@ public class DfsFsckTest {
"refs/heads/master");
}
+ private ObjectId insertGitModules(String contents) throws IOException {
+ ObjectId blobId = ins.insert(Constants.OBJ_BLOB,
+ Constants.encode(contents));
+
+ byte[] blobIdBytes = new byte[OBJECT_ID_LENGTH];
+ blobId.copyRawTo(blobIdBytes, 0);
+ byte[] data = concat(encodeASCII("100644 .gitmodules\0"), blobIdBytes);
+ ins.insert(Constants.OBJ_TREE, data);
+ ins.flush();
+
+ return blobId;
+ }
+
+ @Test
+ public void testInvalidGitModules() throws Exception {
+ String fakeGitmodules = new StringBuilder()
+ .append("[submodule \"test\"]\n")
+ .append(" path = xlib\n")
+ .append(" url = https://example.com/repo/xlib.git\n\n")
+ .append("[submodule \"test2\"]\n")
+ .append(" path = zlib\n")
+ .append(" url = -upayload.sh\n")
+ .toString();
+
+ ObjectId blobId = insertGitModules(fakeGitmodules);
+
+ DfsFsck fsck = new DfsFsck(repo);
+ FsckError errors = fsck.check(null);
+ assertEquals(errors.getCorruptObjects().size(), 1);
+
+ CorruptObject error = errors.getCorruptObjects().iterator().next();
+ assertEquals(error.getId(), blobId);
+ assertEquals(error.getType(), Constants.OBJ_BLOB);
+ assertEquals(error.getErrorType(), ErrorType.GITMODULES_URL);
+ }
+
+
+ @Test
+ public void testValidGitModules() throws Exception {
+ String fakeGitmodules = new StringBuilder()
+ .append("[submodule \"test\"]\n")
+ .append(" path = xlib\n")
+ .append(" url = https://example.com/repo/xlib.git\n\n")
+ .append("[submodule \"test2\"]\n")
+ .append(" path = zlib\n")
+ .append(" url = ok/path\n")
+ .toString();
+
+ insertGitModules(fakeGitmodules);
+
+ DfsFsck fsck = new DfsFsck(repo);
+ FsckError errors = fsck.check(null);
+ assertEquals(errors.getCorruptObjects().size(), 0);
+ }
+
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileRepositoryBuilderTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileRepositoryBuilderTest.java
index deffa04b54..f6cb55870b 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileRepositoryBuilderTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileRepositoryBuilderTest.java
@@ -43,14 +43,16 @@
package org.eclipse.jgit.internal.storage.file;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import java.io.BufferedWriter;
import java.io.File;
-import java.io.FileWriter;
import java.io.IOException;
+import java.nio.file.Files;
import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
import org.eclipse.jgit.lib.ConfigConstants;
@@ -120,18 +122,19 @@ public class FileRepositoryBuilderTest extends LocalDiskRepositoryTestCase {
Repository repo1 = createWorkRepository();
File dir = createTempDirectory("dir");
File dotGit = new File(dir, Constants.DOT_GIT);
- try (FileWriter writer = new FileWriter(dotGit)) {
- writer.append("gitdir: " + repo1.getDirectory().getAbsolutePath()).close();
- FileRepositoryBuilder builder = new FileRepositoryBuilder();
+ try (BufferedWriter writer = Files.newBufferedWriter(dotGit.toPath(),
+ UTF_8)) {
+ writer.append("gitdir: " + repo1.getDirectory().getAbsolutePath());
+ }
+ FileRepositoryBuilder builder = new FileRepositoryBuilder();
- builder.setWorkTree(dir);
- builder.setMustExist(true);
- Repository repo2 = builder.build();
+ builder.setWorkTree(dir);
+ builder.setMustExist(true);
+ Repository repo2 = builder.build();
- assertEquals(repo1.getDirectory().getAbsolutePath(), repo2
- .getDirectory().getAbsolutePath());
- assertEquals(dir, repo2.getWorkTree());
- }
+ assertEquals(repo1.getDirectory().getAbsolutePath(),
+ repo2.getDirectory().getAbsolutePath());
+ assertEquals(dir, repo2.getWorkTree());
}
@Test
@@ -140,20 +143,20 @@ public class FileRepositoryBuilderTest extends LocalDiskRepositoryTestCase {
File dir = new File(repo1.getWorkTree(), "dir");
assertTrue(dir.mkdir());
File dotGit = new File(dir, Constants.DOT_GIT);
- try (FileWriter writer = new FileWriter(dotGit)) {
- writer.append("gitdir: ../" + Constants.DOT_GIT).close();
-
- FileRepositoryBuilder builder = new FileRepositoryBuilder();
- builder.setWorkTree(dir);
- builder.setMustExist(true);
- Repository repo2 = builder.build();
-
- // The tmp directory may be a symlink so the actual path
- // may not
- assertEquals(repo1.getDirectory().getCanonicalPath(), repo2
- .getDirectory().getCanonicalPath());
- assertEquals(dir, repo2.getWorkTree());
+ try (BufferedWriter writer = Files.newBufferedWriter(dotGit.toPath(),
+ UTF_8)) {
+ writer.append("gitdir: ../" + Constants.DOT_GIT);
}
+ FileRepositoryBuilder builder = new FileRepositoryBuilder();
+ builder.setWorkTree(dir);
+ builder.setMustExist(true);
+ Repository repo2 = builder.build();
+
+ // The tmp directory may be a symlink so the actual path
+ // may not
+ assertEquals(repo1.getDirectory().getCanonicalPath(),
+ repo2.getDirectory().getCanonicalPath());
+ assertEquals(dir, repo2.getWorkTree());
}
@Test
@@ -161,22 +164,23 @@ public class FileRepositoryBuilderTest extends LocalDiskRepositoryTestCase {
Repository repo1 = createWorkRepository();
File dir = createTempDirectory("dir");
File dotGit = new File(dir, Constants.DOT_GIT);
- try (FileWriter writer = new FileWriter(dotGit)) {
+ try (BufferedWriter writer = Files.newBufferedWriter(dotGit.toPath(),
+ UTF_8)) {
writer.append(
- "gitdir: " + repo1.getDirectory().getAbsolutePath()).close();
- FileRepositoryBuilder builder = new FileRepositoryBuilder();
-
- builder.setWorkTree(dir);
- builder.findGitDir(dir);
- assertEquals(repo1.getDirectory().getAbsolutePath(), builder
- .getGitDir().getAbsolutePath());
- builder.setMustExist(true);
- Repository repo2 = builder.build();
-
- // The tmp directory may be a symlink
- assertEquals(repo1.getDirectory().getCanonicalPath(), repo2
- .getDirectory().getCanonicalPath());
- assertEquals(dir, repo2.getWorkTree());
+ "gitdir: " + repo1.getDirectory().getAbsolutePath());
}
+ FileRepositoryBuilder builder = new FileRepositoryBuilder();
+
+ builder.setWorkTree(dir);
+ builder.findGitDir(dir);
+ assertEquals(repo1.getDirectory().getAbsolutePath(),
+ builder.getGitDir().getAbsolutePath());
+ builder.setMustExist(true);
+ Repository repo2 = builder.build();
+
+ // The tmp directory may be a symlink
+ assertEquals(repo1.getDirectory().getCanonicalPath(),
+ repo2.getDirectory().getCanonicalPath());
+ assertEquals(dir, repo2.getWorkTree());
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ObjectDirectoryTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ObjectDirectoryTest.java
index cbb73bb08e..3ca689ac01 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ObjectDirectoryTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ObjectDirectoryTest.java
@@ -42,6 +42,7 @@
package org.eclipse.jgit.internal.storage.file;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -189,7 +190,8 @@ public class ObjectDirectoryTest extends RepositoryTestCase {
String commit = "d3148f9410b071edd4a4c85d2a43d1fa2574b0d2";
try (PrintWriter writer = new PrintWriter(
- new File(repository.getDirectory(), Constants.SHALLOW))) {
+ new File(repository.getDirectory(), Constants.SHALLOW),
+ UTF_8.name())) {
writer.println(commit);
}
Set<ObjectId> shallowCommits = dir.getShallowCommits();
@@ -205,7 +207,8 @@ public class ObjectDirectoryTest extends RepositoryTestCase {
String commit = "X3148f9410b071edd4a4c85d2a43d1fa2574b0d2";
try (PrintWriter writer = new PrintWriter(
- new File(repository.getDirectory(), Constants.SHALLOW))) {
+ new File(repository.getDirectory(), Constants.SHALLOW),
+ UTF_8.name())) {
writer.println(commit);
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogReaderTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogReaderTest.java
index dc05eeabe1..acdaf3aa3c 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogReaderTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogReaderTest.java
@@ -44,6 +44,7 @@
package org.eclipse.jgit.internal.storage.file;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
@@ -67,31 +68,31 @@ import org.junit.Test;
public class ReflogReaderTest extends SampleDataRepositoryTestCase {
static byte[] oneLine = "da85355dfc525c9f6f3927b876f379f46ccf826e 3e7549db262d1e836d9bf0af7e22355468f1717c A O Thor Too <authortoo@wri.tr> 1243028200 +0200\tcommit: Add a toString for debugging to RemoteRefUpdate\n"
- .getBytes();
+ .getBytes(UTF_8);
static byte[] twoLine = ("0000000000000000000000000000000000000000 c6734895958052a9dbc396cff4459dc1a25029ab A U Thor <thor@committer.au> 1243028201 -0100\tbranch: Created from rr/renamebranchv4\n"
+ "c6734895958052a9dbc396cff4459dc1a25029ab 54794942a18a237c57a80719afed44bb78172b10 Same A U Thor <same.author@example.com> 1243028202 +0100\trebase finished: refs/heads/rr/renamebranch5 onto c6e3b9fe2da0293f11eae202ec35fb343191a82d\n")
- .getBytes();
+ .getBytes(UTF_8);
static byte[] twoLineWithAppendInProgress = ("0000000000000000000000000000000000000000 c6734895958052a9dbc396cff4459dc1a25029ab A U Thor <thor@committer.au> 1243028201 -0100\tbranch: Created from rr/renamebranchv4\n"
+ "c6734895958052a9dbc396cff4459dc1a25029ab 54794942a18a237c57a80719afed44bb78172b10 Same A U Thor <same.author@example.com> 1243028202 +0100\trebase finished: refs/heads/rr/renamebranch5 onto c6e3b9fe2da0293f11eae202ec35fb343191a82d\n"
+ "54794942a18a237c57a80719afed44bb78172b10 ")
- .getBytes();
+ .getBytes(UTF_8);
static byte[] aLine = "1111111111111111111111111111111111111111 3e7549db262d1e836d9bf0af7e22355468f1717c A U Thor <thor@committer.au> 1243028201 -0100\tbranch: change to a\n"
- .getBytes();
+ .getBytes(UTF_8);
static byte[] masterLine = "2222222222222222222222222222222222222222 3e7549db262d1e836d9bf0af7e22355468f1717c A U Thor <thor@committer.au> 1243028201 -0100\tbranch: change to master\n"
- .getBytes();
+ .getBytes(UTF_8);
static byte[] headLine = "3333333333333333333333333333333333333333 3e7549db262d1e836d9bf0af7e22355468f1717c A U Thor <thor@committer.au> 1243028201 -0100\tbranch: change to HEAD\n"
- .getBytes();
+ .getBytes(UTF_8);
static byte[] oneLineWithoutComment = "da85355dfc525c9f6f3927b876f379f46ccf826e 3e7549db262d1e836d9bf0af7e22355468f1717c A O Thor Too <authortoo@wri.tr> 1243028200 +0200\n"
- .getBytes();
+ .getBytes(UTF_8);
static byte[] switchBranch = "0d43a6890a19fd657faad1c4cfbe3cb1b47851c3 4809df9c0d8bce5b00955563f77c5a9f25aa0d12 A O Thor Too <authortoo@wri.tr> 1315088009 +0200\tcheckout: moving from new/work to master\n"
- .getBytes();
+ .getBytes(UTF_8);
@Test
public void testReadOneLine() throws Exception {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogWriterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogWriterTest.java
index 1d188c3148..a84be7e9f0 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogWriterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogWriterTest.java
@@ -42,6 +42,7 @@
*******************************************************************************/
package org.eclipse.jgit.internal.storage.file;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertEquals;
import java.io.File;
@@ -73,9 +74,9 @@ public class ReflogWriterTest extends SampleDataRepositoryTestCase {
writer.log("refs/heads/master", oldId, newId, ident,
"stash: Add\nmessage\r\nwith line feeds");
- byte[] buffer = new byte[oneLine.getBytes().length];
+ byte[] buffer = new byte[oneLine.getBytes(UTF_8).length];
readReflog(buffer);
- assertEquals(oneLine, new String(buffer));
+ assertEquals(oneLine, new String(buffer, UTF_8));
}
private void readReflog(byte[] buffer)
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/T0003_BasicTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/T0003_BasicTest.java
index 02073226d0..96caa01a9e 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/T0003_BasicTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/T0003_BasicTest.java
@@ -333,9 +333,9 @@ public class T0003_BasicTest extends SampleDataRepositoryTestCase {
public void test002_CreateBadTree() throws Exception {
// We won't create a tree entry with an empty filename
//
+ final TreeFormatter formatter = new TreeFormatter();
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage(JGitText.get().invalidTreeZeroLengthName);
- final TreeFormatter formatter = new TreeFormatter();
formatter.append("", FileMode.TREE,
ObjectId.fromString("4b825dc642cb6eb9a060e54bf8d69288fbee4904"));
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/WindowCacheGetTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/WindowCacheGetTest.java
index 01426eeb8c..9063b65189 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/WindowCacheGetTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/WindowCacheGetTest.java
@@ -141,7 +141,7 @@ public class WindowCacheGetTest extends SampleDataRepositoryTestCase {
}
}
- private class TestObject {
+ private static class TestObject {
ObjectId id;
int type;
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/transport/parser/FirstWantTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/transport/parser/FirstWantTest.java
new file mode 100644
index 0000000000..b877c598ef
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/transport/parser/FirstWantTest.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2018, Google LLC.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.internal.transport.parser;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.jgit.errors.PackProtocolException;
+import org.junit.Test;
+
+public class FirstWantTest {
+
+ @Test
+ public void testFirstWantWithOptions() throws PackProtocolException {
+ String line = "want b9d4d1eb2f93058814480eae9e1b67550f46ee38 "
+ + "no-progress include-tag ofs-delta agent=JGit/unknown";
+
+ FirstWant r = FirstWant.fromLine(line);
+ assertEquals("want b9d4d1eb2f93058814480eae9e1b67550f46ee38",
+ r.getLine());
+ Set<String> capabilities = r.getCapabilities();
+ Set<String> expectedCapabilities = new HashSet<>(
+ Arrays.asList("no-progress", "include-tag", "ofs-delta"));
+ assertEquals(expectedCapabilities, capabilities);
+ assertEquals("JGit/unknown", r.getAgent());
+ }
+
+ @Test
+ public void testFirstWantWithoutOptions() throws PackProtocolException {
+ String line = "want b9d4d1eb2f93058814480eae9e1b67550f46ee38";
+
+ FirstWant r = FirstWant.fromLine(line);
+ assertEquals("want b9d4d1eb2f93058814480eae9e1b67550f46ee38",
+ r.getLine());
+ assertTrue(r.getCapabilities().isEmpty());
+ assertNull(r.getAgent());
+ }
+
+ private String makeFirstWantLine(String capability) {
+ return String.format("want b9d4d1eb2f93058814480eae9e1b67550f46ee38 %s", capability);
+ }
+
+ @Test
+ public void testFirstWantNoWhitespace() {
+ try {
+ FirstWant.fromLine(
+ "want b9d4d1eb2f93058814480eae9e1b67550f400000capability");
+ fail("Accepting first want line without SP between oid and first capability");
+ } catch (PackProtocolException e) {
+ // pass
+ }
+ }
+
+ @Test
+ public void testFirstWantOnlyWhitespace() throws PackProtocolException {
+ FirstWant r = FirstWant
+ .fromLine("want b9d4d1eb2f93058814480eae9e1b67550f46ee38 ");
+ assertEquals("want b9d4d1eb2f93058814480eae9e1b67550f46ee38",
+ r.getLine());
+ }
+
+ @Test
+ public void testFirstWantValidCapabilityNames()
+ throws PackProtocolException {
+ List<String> validNames = Arrays.asList(
+ "c", "cap", "C", "CAP", "1", "1cap", "cap-64k_test",
+ "-", "-cap",
+ "_", "_cap");
+
+ for (String capability: validNames) {
+ FirstWant r = FirstWant.fromLine(makeFirstWantLine(capability));
+ assertEquals(r.getCapabilities().size(), 1);
+ assertTrue(r.getCapabilities().contains(capability));
+ }
+ }
+
+ @Test
+ public void testFirstWantValidAgentName() throws PackProtocolException {
+ FirstWant r = FirstWant.fromLine(makeFirstWantLine("agent=pack.age/Version"));
+ assertEquals(r.getCapabilities().size(), 0);
+ assertEquals("pack.age/Version", r.getAgent());
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java
index c4c4da8165..21d8d66adf 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java
@@ -48,6 +48,7 @@
package org.eclipse.jgit.lib;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.concurrent.TimeUnit.DAYS;
import static java.util.concurrent.TimeUnit.HOURS;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
@@ -66,11 +67,15 @@ import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.text.MessageFormat;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
+import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
import org.eclipse.jgit.api.MergeCommand.FastForwardMode;
import org.eclipse.jgit.errors.ConfigInvalidException;
@@ -78,6 +83,7 @@ import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.junit.MockSystemReader;
import org.eclipse.jgit.merge.MergeConfig;
import org.eclipse.jgit.storage.file.FileBasedConfig;
+import org.eclipse.jgit.transport.RefSpec;
import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.SystemReader;
import org.junit.After;
@@ -94,6 +100,12 @@ public class ConfigTest {
// A non-ASCII whitespace character: U+2002 EN QUAD.
private static final char WS = '\u2002';
+ private static final String REFS_ORIGIN = "+refs/heads/*:refs/remotes/origin/*";
+
+ private static final String REFS_UPSTREAM = "+refs/heads/*:refs/remotes/upstream/*";
+
+ private static final String REFS_BACKUP = "+refs/heads/*:refs/remotes/backup/*";
+
@Rule
public ExpectedException expectedEx = ExpectedException.none();
@@ -690,11 +702,7 @@ public class ConfigTest {
assertEquals("", c.getString("a", null, "y"));
assertArrayEquals(new String[]{""}, c.getStringList("a", null, "y"));
- try {
- c.getInt("a", null, "y", 1);
- } catch (IllegalArgumentException e) {
- assertEquals("Invalid integer value: a.y=", e.getMessage());
- }
+ assertEquals(1, c.getInt("a", null, "y", 1));
assertNull(c.getString("a", null, "z"));
assertArrayEquals(new String[]{}, c.getStringList("a", null, "z"));
@@ -711,11 +719,7 @@ public class ConfigTest {
assertNull(c.getString("a", null, "y"));
assertArrayEquals(new String[]{null}, c.getStringList("a", null, "y"));
- try {
- c.getInt("a", null, "y", 1);
- } catch (IllegalArgumentException e) {
- assertEquals("Invalid integer value: a.y=", e.getMessage());
- }
+ assertEquals(1, c.getInt("a", null, "y", 1));
assertNull(c.getString("a", null, "z"));
assertArrayEquals(new String[]{}, c.getStringList("a", null, "z"));
@@ -801,11 +805,9 @@ public class ConfigTest {
public void testIncludeTooManyRecursions() throws IOException {
File config = tmp.newFile("config");
String include = "[include]\npath=" + pathToString(config) + "\n";
- Files.write(config.toPath(), include.getBytes());
- FileBasedConfig fbConfig = new FileBasedConfig(null, config,
- FS.DETECTED);
+ Files.write(config.toPath(), include.getBytes(UTF_8));
try {
- fbConfig.load();
+ loadConfig(config);
fail();
} catch (ConfigInvalidException cie) {
for (Throwable t = cie; t != null; t = t.getCause()) {
@@ -824,7 +826,7 @@ public class ConfigTest {
File config = tmp.newFile("config");
String fooBar = "[foo]\nbar=true\n";
- Files.write(config.toPath(), fooBar.getBytes());
+ Files.write(config.toPath(), fooBar.getBytes(UTF_8));
Config parsed = parse("[include]\npath=" + pathToString(config) + "\n");
assertFalse(parsed.getBoolean("foo", "bar", false));
@@ -835,15 +837,13 @@ public class ConfigTest {
throws IOException, ConfigInvalidException {
File included = tmp.newFile("included");
String content = "[foo]\nbar=true\n";
- Files.write(included.toPath(), content.getBytes());
+ Files.write(included.toPath(), content.getBytes(UTF_8));
File config = tmp.newFile("config");
content = "[Include]\npath=" + pathToString(included) + "\n";
- Files.write(config.toPath(), content.getBytes());
+ Files.write(config.toPath(), content.getBytes(UTF_8));
- FileBasedConfig fbConfig = new FileBasedConfig(null, config,
- FS.DETECTED);
- fbConfig.load();
+ FileBasedConfig fbConfig = loadConfig(config);
assertTrue(fbConfig.getBoolean("foo", "bar", false));
}
@@ -852,15 +852,13 @@ public class ConfigTest {
throws IOException, ConfigInvalidException {
File included = tmp.newFile("included");
String content = "[foo]\nbar=true\n";
- Files.write(included.toPath(), content.getBytes());
+ Files.write(included.toPath(), content.getBytes(UTF_8));
File config = tmp.newFile("config");
content = "[include]\nPath=" + pathToString(included) + "\n";
- Files.write(config.toPath(), content.getBytes());
+ Files.write(config.toPath(), content.getBytes(UTF_8));
- FileBasedConfig fbConfig = new FileBasedConfig(null, config,
- FS.DETECTED);
- fbConfig.load();
+ FileBasedConfig fbConfig = loadConfig(config);
assertTrue(fbConfig.getBoolean("foo", "bar", false));
}
@@ -881,15 +879,13 @@ public class ConfigTest {
File included = tmp.newFile("included");
String includedPath = pathToString(included);
String content = "[include]\npath=\n";
- Files.write(included.toPath(), content.getBytes());
+ Files.write(included.toPath(), content.getBytes(UTF_8));
File config = tmp.newFile("config");
String include = "[include]\npath=" + includedPath + "\n";
- Files.write(config.toPath(), include.getBytes());
- FileBasedConfig fbConfig = new FileBasedConfig(null, config,
- FS.DETECTED);
+ Files.write(config.toPath(), include.getBytes(UTF_8));
try {
- fbConfig.load();
+ loadConfig(config);
fail("Expected ConfigInvalidException");
} catch (ConfigInvalidException e) {
// Check that there is some exception in the chain that contains
@@ -904,6 +900,306 @@ public class ConfigTest {
}
}
+ @Test
+ public void testIncludeSetValueMustNotTouchIncludedLines1()
+ throws IOException, ConfigInvalidException {
+ File includedFile = createAllTypesIncludedContent();
+
+ File configFile = tmp.newFile("config");
+ String content = createAllTypesSampleContent("Alice Parker", false, 11,
+ 21, 31, CoreConfig.AutoCRLF.FALSE,
+ "+refs/heads/*:refs/remotes/origin/*") + "\n[include]\npath="
+ + pathToString(includedFile);
+ Files.write(configFile.toPath(), content.getBytes(UTF_8));
+
+ FileBasedConfig fbConfig = loadConfig(configFile);
+ assertValuesAsIncluded(fbConfig, REFS_ORIGIN, REFS_UPSTREAM);
+ assertSections(fbConfig, "user", "core", "remote", "include");
+
+ setAllValuesNew(fbConfig);
+ assertValuesAsIsSaveLoad(fbConfig, config -> {
+ assertValuesAsIncluded(config, REFS_BACKUP, REFS_UPSTREAM);
+ assertSections(fbConfig, "user", "core", "remote", "include");
+ });
+ }
+
+ @Test
+ public void testIncludeSetValueMustNotTouchIncludedLines2()
+ throws IOException, ConfigInvalidException {
+ File includedFile = createAllTypesIncludedContent();
+
+ File configFile = tmp.newFile("config");
+ String content = "[include]\npath=" + pathToString(includedFile) + "\n"
+ + createAllTypesSampleContent("Alice Parker", false, 11, 21, 31,
+ CoreConfig.AutoCRLF.FALSE,
+ "+refs/heads/*:refs/remotes/origin/*");
+ Files.write(configFile.toPath(), content.getBytes(UTF_8));
+
+ FileBasedConfig fbConfig = loadConfig(configFile);
+ assertValuesAsConfig(fbConfig, REFS_UPSTREAM, REFS_ORIGIN);
+ assertSections(fbConfig, "include", "user", "core", "remote");
+
+ setAllValuesNew(fbConfig);
+ assertValuesAsIsSaveLoad(fbConfig, config -> {
+ assertValuesAsNew(config, REFS_UPSTREAM, REFS_BACKUP);
+ assertSections(fbConfig, "include", "user", "core", "remote");
+ });
+ }
+
+ @Test
+ public void testIncludeSetValueOnFileWithJustContainsInclude()
+ throws IOException, ConfigInvalidException {
+ File includedFile = createAllTypesIncludedContent();
+
+ File configFile = tmp.newFile("config");
+ String content = "[include]\npath=" + pathToString(includedFile);
+ Files.write(configFile.toPath(), content.getBytes(UTF_8));
+
+ FileBasedConfig fbConfig = loadConfig(configFile);
+ assertValuesAsIncluded(fbConfig, REFS_UPSTREAM);
+ assertSections(fbConfig, "include", "user", "core", "remote");
+
+ setAllValuesNew(fbConfig);
+ assertValuesAsIsSaveLoad(fbConfig, config -> {
+ assertValuesAsNew(config, REFS_UPSTREAM, REFS_BACKUP);
+ assertSections(fbConfig, "include", "user", "core", "remote");
+ });
+ }
+
+ @Test
+ public void testIncludeSetValueOnFileWithJustEmptySection1()
+ throws IOException, ConfigInvalidException {
+ File includedFile = createAllTypesIncludedContent();
+
+ File configFile = tmp.newFile("config");
+ String content = "[user]\n[include]\npath="
+ + pathToString(includedFile);
+ Files.write(configFile.toPath(), content.getBytes(UTF_8));
+
+ FileBasedConfig fbConfig = loadConfig(configFile);
+ assertValuesAsIncluded(fbConfig, REFS_UPSTREAM);
+ assertSections(fbConfig, "user", "include", "core", "remote");
+
+ setAllValuesNew(fbConfig);
+ assertValuesAsIsSaveLoad(fbConfig, config -> {
+ assertValuesAsNewWithName(config, "Alice Muller", REFS_UPSTREAM,
+ REFS_BACKUP);
+ assertSections(fbConfig, "user", "include", "core", "remote");
+ });
+ }
+
+ @Test
+ public void testIncludeSetValueOnFileWithJustEmptySection2()
+ throws IOException, ConfigInvalidException {
+ File includedFile = createAllTypesIncludedContent();
+
+ File configFile = tmp.newFile("config");
+ String content = "[include]\npath=" + pathToString(includedFile)
+ + "\n[user]";
+ Files.write(configFile.toPath(), content.getBytes(UTF_8));
+
+ FileBasedConfig fbConfig = loadConfig(configFile);
+ assertValuesAsIncluded(fbConfig, REFS_UPSTREAM);
+ assertSections(fbConfig, "include", "user", "core", "remote");
+
+ setAllValuesNew(fbConfig);
+ assertValuesAsIsSaveLoad(fbConfig, config -> {
+ assertValuesAsNew(config, REFS_UPSTREAM, REFS_BACKUP);
+ assertSections(fbConfig, "include", "user", "core", "remote");
+ });
+ }
+
+ @Test
+ public void testIncludeSetValueOnFileWithJustExistingSection1()
+ throws IOException, ConfigInvalidException {
+ File includedFile = createAllTypesIncludedContent();
+
+ File configFile = tmp.newFile("config");
+ String content = "[user]\nemail=alice@home\n[include]\npath="
+ + pathToString(includedFile);
+ Files.write(configFile.toPath(), content.getBytes(UTF_8));
+
+ FileBasedConfig fbConfig = loadConfig(configFile);
+ assertValuesAsIncluded(fbConfig, REFS_UPSTREAM);
+ assertSections(fbConfig, "user", "include", "core", "remote");
+
+ setAllValuesNew(fbConfig);
+ assertValuesAsIsSaveLoad(fbConfig, config -> {
+ assertValuesAsNewWithName(config, "Alice Muller", REFS_UPSTREAM,
+ REFS_BACKUP);
+ assertSections(fbConfig, "user", "include", "core", "remote");
+ });
+ }
+
+ @Test
+ public void testIncludeSetValueOnFileWithJustExistingSection2()
+ throws IOException, ConfigInvalidException {
+ File includedFile = createAllTypesIncludedContent();
+
+ File configFile = tmp.newFile("config");
+ String content = "[include]\npath=" + pathToString(includedFile)
+ + "\n[user]\nemail=alice@home\n";
+ Files.write(configFile.toPath(), content.getBytes(UTF_8));
+
+ FileBasedConfig fbConfig = loadConfig(configFile);
+ assertValuesAsIncluded(fbConfig, REFS_UPSTREAM);
+ assertSections(fbConfig, "include", "user", "core", "remote");
+
+ setAllValuesNew(fbConfig);
+ assertValuesAsIsSaveLoad(fbConfig, config -> {
+ assertValuesAsNew(config, REFS_UPSTREAM, REFS_BACKUP);
+ assertSections(fbConfig, "include", "user", "core", "remote");
+ });
+ }
+
+ @Test
+ public void testIncludeUnsetSectionMustNotTouchIncludedLines()
+ throws IOException, ConfigInvalidException {
+ File includedFile = tmp.newFile("included");
+ RefSpec includedRefSpec = new RefSpec(REFS_UPSTREAM);
+ String includedContent = "[remote \"origin\"]\n" + "fetch="
+ + includedRefSpec;
+ Files.write(includedFile.toPath(), includedContent.getBytes(UTF_8));
+
+ File configFile = tmp.newFile("config");
+ RefSpec refSpec = new RefSpec(REFS_ORIGIN);
+ String content = "[include]\npath=" + pathToString(includedFile) + "\n"
+ + "[remote \"origin\"]\n" + "fetch=" + refSpec;
+ Files.write(configFile.toPath(), content.getBytes(UTF_8));
+
+ FileBasedConfig fbConfig = loadConfig(configFile);
+
+ Consumer<FileBasedConfig> assertion = config -> {
+ assertEquals(Arrays.asList(includedRefSpec, refSpec),
+ config.getRefSpecs("remote", "origin", "fetch"));
+ };
+ assertion.accept(fbConfig);
+
+ fbConfig.unsetSection("remote", "origin");
+ assertValuesAsIsSaveLoad(fbConfig, config -> {
+ assertEquals(Collections.singletonList(includedRefSpec),
+ config.getRefSpecs("remote", "origin", "fetch"));
+ });
+ }
+
+ private File createAllTypesIncludedContent() throws IOException {
+ File includedFile = tmp.newFile("included");
+ String includedContent = createAllTypesSampleContent("Alice Muller",
+ true, 10, 20, 30, CoreConfig.AutoCRLF.TRUE,
+ "+refs/heads/*:refs/remotes/upstream/*");
+ Files.write(includedFile.toPath(), includedContent.getBytes(UTF_8));
+ return includedFile;
+ }
+
+ private static void assertValuesAsIsSaveLoad(FileBasedConfig fbConfig,
+ Consumer<FileBasedConfig> assertion)
+ throws IOException, ConfigInvalidException {
+ assertion.accept(fbConfig);
+
+ fbConfig.save();
+ assertion.accept(fbConfig);
+
+ fbConfig = loadConfig(fbConfig.getFile());
+ assertion.accept(fbConfig);
+ }
+
+ private static void setAllValuesNew(Config config) {
+ config.setString("user", null, "name", "Alice Bauer");
+ config.setBoolean("core", null, "fileMode", false);
+ config.setInt("core", null, "deltaBaseCacheLimit", 12);
+ config.setLong("core", null, "packedGitLimit", 22);
+ config.setLong("core", null, "repositoryCacheExpireAfter", 32);
+ config.setEnum("core", null, "autocrlf", CoreConfig.AutoCRLF.FALSE);
+ config.setString("remote", "origin", "fetch",
+ "+refs/heads/*:refs/remotes/backup/*");
+ }
+
+ private static void assertValuesAsIncluded(Config config, String... refs) {
+ assertAllTypesSampleContent("Alice Muller", true, 10, 20, 30,
+ CoreConfig.AutoCRLF.TRUE, config, refs);
+ }
+
+ private static void assertValuesAsConfig(Config config, String... refs) {
+ assertAllTypesSampleContent("Alice Parker", false, 11, 21, 31,
+ CoreConfig.AutoCRLF.FALSE, config, refs);
+ }
+
+ private static void assertValuesAsNew(Config config, String... refs) {
+ assertValuesAsNewWithName(config, "Alice Bauer", refs);
+ }
+
+ private static void assertValuesAsNewWithName(Config config, String name,
+ String... refs) {
+ assertAllTypesSampleContent(name, false, 12, 22, 32,
+ CoreConfig.AutoCRLF.FALSE, config, refs);
+ }
+
+ private static void assertSections(Config config, String... sections) {
+ assertEquals(Arrays.asList(sections),
+ new ArrayList<>(config.getSections()));
+ }
+
+ private static String createAllTypesSampleContent(String name,
+ boolean fileMode, int deltaBaseCacheLimit, long packedGitLimit,
+ long repositoryCacheExpireAfter, CoreConfig.AutoCRLF autoCRLF,
+ String fetchRefSpec) {
+ final StringBuilder builder = new StringBuilder();
+ builder.append("[user]\n");
+ builder.append("name=");
+ builder.append(name);
+ builder.append("\n");
+
+ builder.append("[core]\n");
+ builder.append("fileMode=");
+ builder.append(fileMode);
+ builder.append("\n");
+
+ builder.append("deltaBaseCacheLimit=");
+ builder.append(deltaBaseCacheLimit);
+ builder.append("\n");
+
+ builder.append("packedGitLimit=");
+ builder.append(packedGitLimit);
+ builder.append("\n");
+
+ builder.append("repositoryCacheExpireAfter=");
+ builder.append(repositoryCacheExpireAfter);
+ builder.append("\n");
+
+ builder.append("autocrlf=");
+ builder.append(autoCRLF.name());
+ builder.append("\n");
+
+ builder.append("[remote \"origin\"]\n");
+ builder.append("fetch=");
+ builder.append(fetchRefSpec);
+ builder.append("\n");
+ return builder.toString();
+ }
+
+ private static void assertAllTypesSampleContent(String name,
+ boolean fileMode, int deltaBaseCacheLimit, long packedGitLimit,
+ long repositoryCacheExpireAfter, CoreConfig.AutoCRLF autoCRLF,
+ Config config, String... fetchRefSpecs) {
+ assertEquals(name, config.getString("user", null, "name"));
+ assertEquals(fileMode,
+ config.getBoolean("core", "fileMode", !fileMode));
+ assertEquals(deltaBaseCacheLimit,
+ config.getInt("core", "deltaBaseCacheLimit", -1));
+ assertEquals(packedGitLimit,
+ config.getLong("core", "packedGitLimit", -1));
+ assertEquals(repositoryCacheExpireAfter, config.getTimeUnit("core",
+ null, "repositoryCacheExpireAfter", -1, MILLISECONDS));
+ assertEquals(autoCRLF, config.getEnum("core", null, "autocrlf",
+ CoreConfig.AutoCRLF.INPUT));
+ final List<RefSpec> refspecs = new ArrayList<>();
+ for (String fetchRefSpec : fetchRefSpecs) {
+ refspecs.add(new RefSpec(fetchRefSpec));
+ }
+
+ assertEquals(refspecs, config.getRefSpecs("remote", "origin", "fetch"));
+ }
+
private static void assertReadLong(long exp) throws ConfigInvalidException {
assertReadLong(exp, String.valueOf(exp));
}
@@ -1217,4 +1513,12 @@ public class ConfigTest {
assertEquals(expectedMessage, e.getMessage());
}
}
+
+ private static FileBasedConfig loadConfig(File file)
+ throws IOException, ConfigInvalidException {
+ final FileBasedConfig config = new FileBasedConfig(null, file,
+ FS.DETECTED);
+ config.load();
+ return config;
+ }
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutMaliciousPathTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutMaliciousPathTest.java
index 32a1ec96a5..057e0c881b 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutMaliciousPathTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutMaliciousPathTest.java
@@ -37,6 +37,7 @@
*/
package org.eclipse.jgit.lib;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -343,7 +344,7 @@ public class DirCacheCheckoutMaliciousPathTest extends RepositoryTestCase {
ObjectInserter newObjectInserter;
newObjectInserter = git.getRepository().newObjectInserter();
ObjectId blobId = newObjectInserter.insert(Constants.OBJ_BLOB,
- "data".getBytes());
+ "data".getBytes(UTF_8));
newObjectInserter = git.getRepository().newObjectInserter();
FileMode mode = FileMode.REGULAR_FILE;
ObjectId insertId = blobId;
@@ -366,8 +367,8 @@ public class DirCacheCheckoutMaliciousPathTest extends RepositoryTestCase {
insertId = blobId;
for (int i = path.length - 1; i >= 0; --i) {
TreeFormatter treeFormatter = new TreeFormatter();
- treeFormatter.append(path[i].getBytes(), 0,
- path[i].getBytes().length,
+ treeFormatter.append(path[i].getBytes(UTF_8), 0,
+ path[i].getBytes(UTF_8).length,
mode, insertId, true);
insertId = newObjectInserter.insert(treeFormatter);
mode = FileMode.TREE;
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java
index eb87827805..534b323fe6 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java
@@ -40,6 +40,7 @@
*/
package org.eclipse.jgit.lib;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -310,7 +311,7 @@ public class DirCacheCheckoutTest extends RepositoryTestCase {
assertTrue("unexpected content for path " + path
+ " in index. Expected: <" + expectedValue + ">",
Arrays.equals(db.open(read.getEntry(j).getObjectId())
- .getCachedBytes(), i.get(path).getBytes()));
+ .getCachedBytes(), i.get(path).getBytes(UTF_8)));
}
}
@@ -405,7 +406,7 @@ public class DirCacheCheckoutTest extends RepositoryTestCase {
ObjectId genSha1(String data) {
try (ObjectInserter w = db.newObjectInserter()) {
- ObjectId id = w.insert(Constants.OBJ_BLOB, data.getBytes());
+ ObjectId id = w.insert(Constants.OBJ_BLOB, data.getBytes(UTF_8));
w.flush();
return id;
} catch (IOException e) {
@@ -928,6 +929,7 @@ public class DirCacheCheckoutTest extends RepositoryTestCase {
"e/g3"));
try {
checkout();
+ fail("did not throw CheckoutConflictException");
} catch (CheckoutConflictException e) {
assertWorkDir(mkmap("a", "a", "b/c", "b/c", "d", "d", "e/f",
"e/f", "e/g", "e/g3"));
@@ -2048,7 +2050,7 @@ public class DirCacheCheckoutTest extends RepositoryTestCase {
assertArrayEquals(
"unexpected content for path " + path
+ " in workDir. ",
- buffer, i.get(path).getBytes());
+ buffer, i.get(path).getBytes(UTF_8));
}
nrFiles++;
} else if (file.isDirectory()) {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/GpgConfigTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/GpgConfigTest.java
new file mode 100644
index 0000000000..2098b17f41
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/GpgConfigTest.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2018, Salesforce.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.lib;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.junit.Test;
+
+public class GpgConfigTest {
+
+ private static Config parse(String content) throws ConfigInvalidException {
+ final Config c = new Config(null);
+ c.fromText(content);
+ return c;
+ }
+
+ @Test
+ public void isSignCommits_defaultIsFalse() throws Exception {
+ Config c = parse("");
+
+ assertFalse(new GpgConfig(c).isSignCommits());
+ }
+
+ @Test
+ public void isSignCommits_false() throws Exception {
+ Config c = parse("" //
+ + "[gpg]\n" //
+ + " format = x509\n" //
+ + "[commit]\n" //
+ + " gpgSign = false\n" //
+ );
+
+ assertFalse(new GpgConfig(c).isSignCommits());
+ }
+
+ @Test
+ public void isSignCommits_true() throws Exception {
+ Config c = parse("" //
+ + "[commit]\n" //
+ + " gpgSign = true\n" //
+ );
+
+ assertTrue(new GpgConfig(c).isSignCommits());
+ }
+
+ @Test
+ public void testGetKeyFormat_defaultsToOpenpgp() throws Exception {
+ Config c = parse("");
+
+ assertEquals(GpgConfig.GpgFormat.OPENPGP,
+ new GpgConfig(c).getKeyFormat());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testGetKeyFormat_failsForInvalidValue() throws Exception {
+ Config c = parse("" //
+ + "[gpg]\n" //
+ + " format = invalid\n" //
+ );
+
+ new GpgConfig(c).getKeyFormat();
+ fail("Call should not have succeeded!");
+ }
+
+ @Test
+ public void testGetKeyFormat_openpgp() throws Exception {
+ Config c = parse("" //
+ + "[gpg]\n" //
+ + " format = openpgp\n" //
+ );
+
+ assertEquals(GpgConfig.GpgFormat.OPENPGP,
+ new GpgConfig(c).getKeyFormat());
+ }
+
+ @Test
+ public void testGetKeyFormat_x509() throws Exception {
+ Config c = parse("" //
+ + "[gpg]\n" //
+ + " format = x509\n" //
+ );
+
+ assertEquals(GpgConfig.GpgFormat.X509, new GpgConfig(c).getKeyFormat());
+ }
+
+ @Test
+ public void testGetSigningKey() throws Exception {
+ Config c = parse("" //
+ + "[user]\n" //
+ + " signingKey = 0x2345\n" //
+ );
+
+ assertEquals("0x2345", new GpgConfig(c).getSigningKey());
+ }
+
+ @Test
+ public void testGetSigningKey_defaultToNull() throws Exception {
+ Config c = parse("");
+
+ assertNull(new GpgConfig(c).getSigningKey());
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffSubmoduleTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffSubmoduleTest.java
index d89aabe75f..fa7f5ab522 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffSubmoduleTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffSubmoduleTest.java
@@ -43,14 +43,17 @@
package org.eclipse.jgit.lib;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.io.File;
import java.io.IOException;
+import java.util.Arrays;
import java.util.Set;
+import org.eclipse.jgit.api.CloneCommand;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.errors.NoWorkTreeException;
@@ -109,6 +112,59 @@ public class IndexDiffSubmoduleTest extends RepositoryTestCase {
assertFalse(indexDiff.diff());
}
+ private Repository cloneWithoutCloningSubmodule() throws Exception {
+ File directory = createTempDirectory(
+ "testCloneWithoutCloningSubmodules");
+ CloneCommand clone = Git.cloneRepository();
+ clone.setDirectory(directory);
+ clone.setCloneSubmodules(false);
+ clone.setURI(db.getDirectory().toURI().toString());
+ Git git2 = clone.call();
+ addRepoToClose(git2.getRepository());
+ return git2.getRepository();
+ }
+
+ @Theory
+ public void testCleanAfterClone(IgnoreSubmoduleMode mode) throws Exception {
+ Repository db2 = cloneWithoutCloningSubmodule();
+ IndexDiff indexDiff = new IndexDiff(db2, Constants.HEAD,
+ new FileTreeIterator(db2));
+ indexDiff.setIgnoreSubmoduleMode(mode);
+ boolean changed = indexDiff.diff();
+ assertFalse(changed);
+ }
+
+ @Theory
+ public void testMissingIfDirectoryGone(IgnoreSubmoduleMode mode)
+ throws Exception {
+ recursiveDelete(submodule_trash);
+ IndexDiff indexDiff = new IndexDiff(db, Constants.HEAD,
+ new FileTreeIterator(db));
+ indexDiff.setIgnoreSubmoduleMode(mode);
+ boolean hasChanges = indexDiff.diff();
+ if (mode != IgnoreSubmoduleMode.ALL) {
+ assertTrue(hasChanges);
+ assertEquals("[modules/submodule]",
+ indexDiff.getMissing().toString());
+ } else {
+ assertFalse(hasChanges);
+ }
+ }
+
+ @Theory
+ public void testSubmoduleReplacedByFile(IgnoreSubmoduleMode mode)
+ throws Exception {
+ recursiveDelete(submodule_trash);
+ writeTrashFile("modules/submodule", "nonsense");
+ IndexDiff indexDiff = new IndexDiff(db, Constants.HEAD,
+ new FileTreeIterator(db));
+ indexDiff.setIgnoreSubmoduleMode(mode);
+ assertTrue(indexDiff.diff());
+ assertEquals("[]", indexDiff.getMissing().toString());
+ assertEquals("[]", indexDiff.getUntracked().toString());
+ assertEquals("[modules/submodule]", indexDiff.getModified().toString());
+ }
+
@Theory
public void testDirtyRootWorktree(IgnoreSubmoduleMode mode)
throws IOException {
@@ -210,4 +266,33 @@ public class IndexDiffSubmoduleTest extends RepositoryTestCase {
assertDiff(indexDiff, mode, IgnoreSubmoduleMode.ALL,
IgnoreSubmoduleMode.DIRTY, IgnoreSubmoduleMode.UNTRACKED);
}
+
+ @Theory
+ public void testSubmoduleReplacedByMovedFile(IgnoreSubmoduleMode mode)
+ throws Exception {
+ Git git = Git.wrap(db);
+ git.rm().setCached(true).addFilepattern("modules/submodule").call();
+ recursiveDelete(submodule_trash);
+ JGitTestUtil.deleteTrashFile(db, "fileInRoot");
+ // Move the fileInRoot file
+ writeTrashFile("modules/submodule/fileInRoot", "root");
+ git.rm().addFilepattern("fileInRoot").addFilepattern("modules/").call();
+ git.add().addFilepattern("modules/").call();
+ IndexDiff indexDiff = new IndexDiff(db, Constants.HEAD,
+ new FileTreeIterator(db));
+ indexDiff.setIgnoreSubmoduleMode(mode);
+ assertTrue(indexDiff.diff());
+ String[] removed = indexDiff.getRemoved().toArray(new String[0]);
+ Arrays.sort(removed);
+ if (IgnoreSubmoduleMode.ALL.equals(mode)) {
+ assertArrayEquals(new String[] { "fileInRoot" }, removed);
+ } else {
+ assertArrayEquals(
+ new String[] { "fileInRoot", "modules/submodule" },
+ removed);
+ }
+ assertEquals("[modules/submodule/fileInRoot]",
+ indexDiff.getAdded().toString());
+ }
+
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffTest.java
index 580b08b42f..ba5aaf1b18 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffTest.java
@@ -119,6 +119,28 @@ public class IndexDiffTest extends RepositoryTestCase {
}
@Test
+ public void testMissing() throws Exception {
+ File file2 = writeTrashFile("file2", "file2");
+ File file3 = writeTrashFile("dir/file3", "dir/file3");
+ Git git = Git.wrap(db);
+ git.add().addFilepattern("file2").addFilepattern("dir/file3").call();
+ git.commit().setMessage("commit").call();
+ assertTrue(file2.delete());
+ assertTrue(file3.delete());
+ IndexDiff diff = new IndexDiff(db, Constants.HEAD,
+ new FileTreeIterator(db));
+ diff.diff();
+ assertEquals(2, diff.getMissing().size());
+ assertTrue(diff.getMissing().contains("file2"));
+ assertTrue(diff.getMissing().contains("dir/file3"));
+ assertEquals(0, diff.getChanged().size());
+ assertEquals(0, diff.getModified().size());
+ assertEquals(0, diff.getAdded().size());
+ assertEquals(0, diff.getRemoved().size());
+ assertEquals(Collections.EMPTY_SET, diff.getUntrackedFolders());
+ }
+
+ @Test
public void testRemoved() throws IOException {
writeTrashFile("file2", "file2");
writeTrashFile("dir/file3", "dir/file3");
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/MergeHeadMsgTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/MergeHeadMsgTest.java
index 347883f842..abf7d56819 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/MergeHeadMsgTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/MergeHeadMsgTest.java
@@ -42,6 +42,7 @@
*/
package org.eclipse.jgit.lib;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -72,7 +73,9 @@ public class MergeHeadMsgTest extends RepositoryTestCase {
// same test again, this time with lower-level io
try (FileOutputStream fos = new FileOutputStream(
new File(db.getDirectory(), "MERGE_HEAD"));) {
- fos.write("0000000000000000000000000000000000000000\n1c6db447abdbb291b25f07be38ea0b1bf94947c5\n".getBytes(Constants.CHARACTER_ENCODING));
+ fos.write(
+ "0000000000000000000000000000000000000000\n1c6db447abdbb291b25f07be38ea0b1bf94947c5\n"
+ .getBytes(UTF_8));
}
assertEquals(db.readMergeHeads().size(), 2);
assertEquals(db.readMergeHeads().get(0), ObjectId.zeroId());
@@ -82,7 +85,7 @@ public class MergeHeadMsgTest extends RepositoryTestCase {
assertEquals(db.readMergeHeads(), null);
try (FileOutputStream fos = new FileOutputStream(
new File(db.getDirectory(), "MERGE_HEAD"))) {
- fos.write(sampleId.getBytes(Constants.CHARACTER_ENCODING));
+ fos.write(sampleId.getBytes(UTF_8));
}
assertEquals(db.readMergeHeads().size(), 1);
assertEquals(db.readMergeHeads().get(0), ObjectId.fromString(sampleId));
@@ -100,7 +103,7 @@ public class MergeHeadMsgTest extends RepositoryTestCase {
assertFalse(new File(db.getDirectory(), "MERGE_MSG").exists());
try (FileOutputStream fos = new FileOutputStream(
new File(db.getDirectory(), Constants.MERGE_MSG))) {
- fos.write(mergeMsg.getBytes(Constants.CHARACTER_ENCODING));
+ fos.write(mergeMsg.getBytes(UTF_8));
}
assertEquals(db.readMergeCommitMsg(), mergeMsg);
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectLoaderTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectLoaderTest.java
index 83e61d9ab4..055e66ed81 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectLoaderTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectLoaderTest.java
@@ -260,6 +260,12 @@ public class ObjectLoaderTest {
fail("never should have reached read");
return -1;
}
+
+ @Override
+ public int read(byte b[], int off, int len) {
+ fail("never should have reached read");
+ return -1;
+ }
};
}
};
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RacyGitTests.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RacyGitTests.java
index 3542dfad2d..bb24994ee8 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RacyGitTests.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RacyGitTests.java
@@ -43,6 +43,7 @@
package org.eclipse.jgit.lib;
import static java.lang.Long.valueOf;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
@@ -181,7 +182,7 @@ public class RacyGitTests extends RepositoryTestCase {
private File addToWorkDir(String path, String content) throws IOException {
File f = new File(db.getWorkTree(), path);
try (FileOutputStream fos = new FileOutputStream(f)) {
- fos.write(content.getBytes(Constants.CHARACTER_ENCODING));
+ fos.write(content.getBytes(UTF_8));
return f;
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefTest.java
index a42027b584..7d2c4a2784 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefTest.java
@@ -45,6 +45,7 @@
package org.eclipse.jgit.lib;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static org.eclipse.jgit.junit.Assert.assertEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -261,7 +262,7 @@ public class RefTest extends SampleDataRepositoryTestCase {
assertEquals(Storage.PACKED, ref.getStorage());
try (FileOutputStream os = new FileOutputStream(
new File(db.getDirectory(), "refs/heads/master"))) {
- os.write(ref.getObjectId().name().getBytes());
+ os.write(ref.getObjectId().name().getBytes(UTF_8));
os.write('\n');
}
@@ -333,4 +334,17 @@ public class RefTest extends SampleDataRepositoryTestCase {
assertEquals(1, refs.size());
checkContainsRef(refs, db.exactRef("refs/heads/prefix/a"));
}
+
+ @Test
+ public void testGetRefsByPrefixes() throws IOException {
+ List<Ref> refs = db.getRefDatabase().getRefsByPrefix();
+ assertEquals(0, refs.size());
+
+ refs = db.getRefDatabase().getRefsByPrefix("refs/heads/p",
+ "refs/tags/A");
+ assertEquals(3, refs.size());
+ checkContainsRef(refs, db.exactRef("refs/heads/pa"));
+ checkContainsRef(refs, db.exactRef("refs/heads/prefix/a"));
+ checkContainsRef(refs, db.exactRef("refs/tags/A"));
+ }
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/SquashCommitMsgTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/SquashCommitMsgTest.java
index 203c00e28a..f58ab06074 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/SquashCommitMsgTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/SquashCommitMsgTest.java
@@ -42,6 +42,7 @@
*/
package org.eclipse.jgit.lib;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -68,7 +69,7 @@ public class SquashCommitMsgTest extends RepositoryTestCase {
assertFalse(new File(db.getDirectory(), Constants.SQUASH_MSG).exists());
try (FileOutputStream fos = new FileOutputStream(
new File(db.getDirectory(), Constants.SQUASH_MSG))) {
- fos.write(squashMsg.getBytes(Constants.CHARACTER_ENCODING));
+ fos.write(squashMsg.getBytes(UTF_8));
}
assertEquals(db.readSquashCommitMsg(), squashMsg);
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ValidRefNameTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ValidRefNameTest.java
index 87e901fcfe..df5079ae16 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ValidRefNameTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ValidRefNameTest.java
@@ -270,8 +270,8 @@ public class ValidRefNameTest {
@Test
public void testNormalizeBranchName() {
- assertEquals(true, Repository.normalizeBranchName(null) == "");
- assertEquals(true, Repository.normalizeBranchName("").equals(""));
+ assertEquals("", Repository.normalizeBranchName(null));
+ assertEquals("", Repository.normalizeBranchName(""));
assertNormalized("Bug 12345::::Hello World", "Bug_12345-Hello_World");
assertNormalized("Bug 12345 :::: Hello World", "Bug_12345_Hello_World");
assertNormalized("Bug 12345 :::: Hello::: World",
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergeAlgorithmTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergeAlgorithmTest.java
index 5af62b6704..3da779b4ec 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergeAlgorithmTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergeAlgorithmTest.java
@@ -43,6 +43,7 @@
package org.eclipse.jgit.merge;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertEquals;
import java.io.ByteArrayOutputStream;
@@ -301,8 +302,8 @@ public class MergeAlgorithmTest {
MergeResult r = new MergeAlgorithm().merge(RawTextComparator.DEFAULT,
T(commonBase), T(ours), T(theirs));
ByteArrayOutputStream bo=new ByteArrayOutputStream(50);
- fmt.formatMerge(bo, r, "B", "O", "T", Constants.CHARACTER_ENCODING);
- return new String(bo.toByteArray(), Constants.CHARACTER_ENCODING);
+ fmt.formatMerge(bo, r, "B", "O", "T", UTF_8);
+ return new String(bo.toByteArray(), UTF_8);
}
public String t(String text) {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergerTest.java
index 8f12dd7b27..8ca5d453fb 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergerTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergerTest.java
@@ -839,7 +839,7 @@ public class MergerTest extends RepositoryTestCase {
/**
* Throws an exception if reading beyond limit.
*/
- class BigReadForbiddenStream extends ObjectStream.Filter {
+ static class BigReadForbiddenStream extends ObjectStream.Filter {
int limit;
BigReadForbiddenStream(ObjectStream orig, int limit) {
@@ -878,7 +878,7 @@ public class MergerTest extends RepositoryTestCase {
}
}
- class BigReadForbiddenReader extends ObjectReader.Filter {
+ static class BigReadForbiddenReader extends ObjectReader.Filter {
ObjectReader delegate;
int limit;
@@ -972,7 +972,7 @@ public class MergerTest extends RepositoryTestCase {
merger.getMergeResults().get("file");
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
fmt.formatMerge(out, merger.getMergeResults().get("file"),
- "BASE", "OURS", "THEIRS", UTF_8.name());
+ "BASE", "OURS", "THEIRS", UTF_8);
String expected = "<<<<<<< OURS\n"
+ "1master\n"
+ "=======\n"
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revplot/PlotCommitListTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revplot/PlotCommitListTest.java
index 9a6043f321..7297de3646 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revplot/PlotCommitListTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revplot/PlotCommitListTest.java
@@ -56,7 +56,7 @@ import org.junit.Test;
public class PlotCommitListTest extends RevWalkTestCase {
- class CommitListAssert {
+ static class CommitListAssert {
private PlotCommitList<PlotLane> pcl;
private PlotCommit<PlotLane> current;
private int nextIndex = 0;
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/FileBasedConfigTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/FileBasedConfigTest.java
index 7f0d60295c..b401d2bea2 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/FileBasedConfigTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/FileBasedConfigTest.java
@@ -52,6 +52,7 @@ import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.util.StringTokenizer;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.util.FS;
@@ -67,16 +68,23 @@ public class FileBasedConfigTest {
private static final String NAME = "name";
+ private static final String EMAIL = "email";
+
private static final String ALICE = "Alice";
private static final String BOB = "Bob";
+ private static final String ALICE_EMAIL = "alice@home";
+
private static final String CONTENT1 = "[" + USER + "]\n\t" + NAME + " = "
+ ALICE + "\n";
private static final String CONTENT2 = "[" + USER + "]\n\t" + NAME + " = "
+ BOB + "\n";
+ private static final String CONTENT3 = "[" + USER + "]\n\t" + NAME + " = "
+ + ALICE + "\n" + "[" + USER + "]\n\t" + EMAIL + " = " + ALICE_EMAIL;
+
private File trash;
@Before
@@ -93,14 +101,14 @@ public class FileBasedConfigTest {
@Test
public void testSystemEncoding() throws IOException, ConfigInvalidException {
- final File file = createFile(CONTENT1.getBytes());
+ final File file = createFile(CONTENT1.getBytes(UTF_8));
final FileBasedConfig config = new FileBasedConfig(file, FS.DETECTED);
config.load();
assertEquals(ALICE, config.getString(USER, null, NAME));
config.setString(USER, null, NAME, BOB);
config.save();
- assertArrayEquals(CONTENT2.getBytes(), IO.readFully(file));
+ assertArrayEquals(CONTENT2.getBytes(UTF_8), IO.readFully(file));
}
@Test
@@ -112,7 +120,7 @@ public class FileBasedConfigTest {
config.setString(USER, null, NAME, BOB);
config.save();
- assertArrayEquals(CONTENT2.getBytes(), IO.readFully(file));
+ assertArrayEquals(CONTENT2.getBytes(UTF_8), IO.readFully(file));
}
@Test
@@ -142,8 +150,8 @@ public class FileBasedConfigTest {
@Test
public void testLeadingWhitespaces() throws IOException, ConfigInvalidException {
final ByteArrayOutputStream bos1 = new ByteArrayOutputStream();
- bos1.write(" \n\t".getBytes());
- bos1.write(CONTENT1.getBytes());
+ bos1.write(" \n\t".getBytes(UTF_8));
+ bos1.write(CONTENT1.getBytes(UTF_8));
final File file = createFile(bos1.toByteArray());
final FileBasedConfig config = new FileBasedConfig(file, FS.DETECTED);
@@ -154,18 +162,18 @@ public class FileBasedConfigTest {
config.save();
final ByteArrayOutputStream bos2 = new ByteArrayOutputStream();
- bos2.write(" \n\t".getBytes());
- bos2.write(CONTENT2.getBytes());
+ bos2.write(" \n\t".getBytes(UTF_8));
+ bos2.write(CONTENT2.getBytes(UTF_8));
assertArrayEquals(bos2.toByteArray(), IO.readFully(file));
}
@Test
public void testIncludeAbsolute()
throws IOException, ConfigInvalidException {
- final File includedFile = createFile(CONTENT1.getBytes());
+ final File includedFile = createFile(CONTENT1.getBytes(UTF_8));
final ByteArrayOutputStream bos = new ByteArrayOutputStream();
- bos.write("[include]\npath=".getBytes());
- bos.write(pathToString(includedFile).getBytes());
+ bos.write("[include]\npath=".getBytes(UTF_8));
+ bos.write(pathToString(includedFile).getBytes(UTF_8));
final File file = createFile(bos.toByteArray());
final FileBasedConfig config = new FileBasedConfig(file, FS.DETECTED);
@@ -176,10 +184,10 @@ public class FileBasedConfigTest {
@Test
public void testIncludeRelativeDot()
throws IOException, ConfigInvalidException {
- final File includedFile = createFile(CONTENT1.getBytes(), "dir1");
+ final File includedFile = createFile(CONTENT1.getBytes(UTF_8), "dir1");
final ByteArrayOutputStream bos = new ByteArrayOutputStream();
- bos.write("[include]\npath=".getBytes());
- bos.write(("./" + includedFile.getName()).getBytes());
+ bos.write("[include]\npath=".getBytes(UTF_8));
+ bos.write(("./" + includedFile.getName()).getBytes(UTF_8));
final File file = createFile(bos.toByteArray(), "dir1");
final FileBasedConfig config = new FileBasedConfig(file, FS.DETECTED);
@@ -190,11 +198,11 @@ public class FileBasedConfigTest {
@Test
public void testIncludeRelativeDotDot()
throws IOException, ConfigInvalidException {
- final File includedFile = createFile(CONTENT1.getBytes(), "dir1");
+ final File includedFile = createFile(CONTENT1.getBytes(UTF_8), "dir1");
final ByteArrayOutputStream bos = new ByteArrayOutputStream();
- bos.write("[include]\npath=".getBytes());
+ bos.write("[include]\npath=".getBytes(UTF_8));
bos.write(("../" + includedFile.getParentFile().getName() + "/"
- + includedFile.getName()).getBytes());
+ + includedFile.getName()).getBytes(UTF_8));
final File file = createFile(bos.toByteArray(), "dir2");
final FileBasedConfig config = new FileBasedConfig(file, FS.DETECTED);
@@ -205,10 +213,10 @@ public class FileBasedConfigTest {
@Test
public void testIncludeRelativeDotDotNotFound()
throws IOException, ConfigInvalidException {
- final File includedFile = createFile(CONTENT1.getBytes());
+ final File includedFile = createFile(CONTENT1.getBytes(UTF_8));
final ByteArrayOutputStream bos = new ByteArrayOutputStream();
- bos.write("[include]\npath=".getBytes());
- bos.write(("../" + includedFile.getName()).getBytes());
+ bos.write("[include]\npath=".getBytes(UTF_8));
+ bos.write(("../" + includedFile.getName()).getBytes(UTF_8));
final File file = createFile(bos.toByteArray());
final FileBasedConfig config = new FileBasedConfig(file, FS.DETECTED);
@@ -219,10 +227,10 @@ public class FileBasedConfigTest {
@Test
public void testIncludeWithTilde()
throws IOException, ConfigInvalidException {
- final File includedFile = createFile(CONTENT1.getBytes(), "home");
+ final File includedFile = createFile(CONTENT1.getBytes(UTF_8), "home");
final ByteArrayOutputStream bos = new ByteArrayOutputStream();
- bos.write("[include]\npath=".getBytes());
- bos.write(("~/" + includedFile.getName()).getBytes());
+ bos.write("[include]\npath=".getBytes(UTF_8));
+ bos.write(("~/" + includedFile.getName()).getBytes(UTF_8));
final File file = createFile(bos.toByteArray(), "repo");
final FS fs = FS.DETECTED.newInstance();
@@ -233,6 +241,50 @@ public class FileBasedConfigTest {
assertEquals(ALICE, config.getString(USER, null, NAME));
}
+ @Test
+ public void testIncludeDontInlineIncludedLinesOnSave()
+ throws IOException, ConfigInvalidException {
+ // use a content with multiple sections and multiple key/value pairs
+ // because code for first line works different than for subsequent lines
+ final File includedFile = createFile(CONTENT3.getBytes(UTF_8), "dir1");
+
+ final File file = createFile(new byte[0], "dir2");
+ FileBasedConfig config = new FileBasedConfig(file, FS.DETECTED);
+ config.setString("include", null, "path",
+ ("../" + includedFile.getParentFile().getName() + "/"
+ + includedFile.getName()));
+
+ // just by setting the include.path, it won't be included
+ assertEquals(null, config.getString(USER, null, NAME));
+ assertEquals(null, config.getString(USER, null, EMAIL));
+ config.save();
+
+ // and it won't be included after saving
+ assertEquals(null, config.getString(USER, null, NAME));
+ assertEquals(null, config.getString(USER, null, EMAIL));
+
+ final String expectedText = config.toText();
+ assertEquals(2,
+ new StringTokenizer(expectedText, "\n", false).countTokens());
+
+ config = new FileBasedConfig(file, FS.DETECTED);
+ config.load();
+
+ String actualText = config.toText();
+ assertEquals(expectedText, actualText);
+ // but it will be included after (re)loading
+ assertEquals(ALICE, config.getString(USER, null, NAME));
+ assertEquals(ALICE_EMAIL, config.getString(USER, null, EMAIL));
+
+ config.save();
+
+ actualText = config.toText();
+ assertEquals(expectedText, actualText);
+ // and of course preserved after saving
+ assertEquals(ALICE, config.getString(USER, null, NAME));
+ assertEquals(ALICE_EMAIL, config.getString(USER, null, EMAIL));
+ }
+
private File createFile(byte[] content) throws IOException {
return createFile(content, null);
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleWalkTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleWalkTest.java
index fed22c0262..a0cd37ee5f 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleWalkTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleWalkTest.java
@@ -42,6 +42,7 @@
*/
package org.eclipse.jgit.submodule;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_PATH;
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_URL;
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_SUBMODULE_SECTION;
@@ -52,9 +53,10 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import java.io.BufferedWriter;
import java.io.File;
-import java.io.FileWriter;
import java.io.IOException;
+import java.nio.file.Files;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.Status;
@@ -155,10 +157,12 @@ public class SubmoduleWalkTest extends RepositoryTestCase {
if (!dotGit.getParentFile().exists())
dotGit.getParentFile().mkdirs();
- File modulesGitDir = new File(db.getDirectory(), "modules"
- + File.separatorChar + path);
- new FileWriter(dotGit).append(
- "gitdir: " + modulesGitDir.getAbsolutePath()).close();
+ File modulesGitDir = new File(db.getDirectory(),
+ "modules" + File.separatorChar + path);
+ try (BufferedWriter fw = Files.newBufferedWriter(dotGit.toPath(),
+ UTF_8)) {
+ fw.append("gitdir: " + modulesGitDir.getAbsolutePath());
+ }
FileRepositoryBuilder builder = new FileRepositoryBuilder();
builder.setWorkTree(new File(db.getWorkTree(), path));
builder.build().create();
@@ -209,9 +213,11 @@ public class SubmoduleWalkTest extends RepositoryTestCase {
File modulesGitDir = new File(db.getDirectory(), "modules"
+ File.separatorChar + path);
- new FileWriter(dotGit).append(
- "gitdir: " + "../" + Constants.DOT_GIT + "/modules/" + path)
- .close();
+ try (BufferedWriter fw = Files.newBufferedWriter(dotGit.toPath(),
+ UTF_8)) {
+ fw.append("gitdir: " + "../" + Constants.DOT_GIT + "/modules/"
+ + path);
+ }
FileRepositoryBuilder builder = new FileRepositoryBuilder();
builder.setWorkTree(new File(db.getWorkTree(), path));
builder.build().create();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/JSchSshTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/JSchSshTest.java
new file mode 100644
index 0000000000..8ff70c4e97
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/JSchSshTest.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.transport;
+
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.nio.file.Files;
+import java.util.Arrays;
+
+import org.eclipse.jgit.errors.TransportException;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.transport.OpenSshConfig.Host;
+import org.eclipse.jgit.transport.ssh.SshTestBase;
+import org.eclipse.jgit.util.FS;
+import org.junit.experimental.theories.Theories;
+import org.junit.runner.RunWith;
+
+import com.jcraft.jsch.JSch;
+import com.jcraft.jsch.JSchException;
+import com.jcraft.jsch.Session;
+
+@RunWith(Theories.class)
+public class JSchSshTest extends SshTestBase {
+
+ private class TestSshSessionFactory extends JschConfigSessionFactory {
+
+ @Override
+ protected void configure(Host hc, Session session) {
+ // Nothing
+ }
+
+ @Override
+ public synchronized RemoteSession getSession(URIish uri,
+ CredentialsProvider credentialsProvider, FS fs, int tms)
+ throws TransportException {
+ return super.getSession(uri, credentialsProvider, fs, tms);
+ }
+
+ @Override
+ protected JSch createDefaultJSch(FS fs) throws JSchException {
+ JSch defaultJSch = super.createDefaultJSch(fs);
+ if (knownHosts.exists()) {
+ defaultJSch.setKnownHosts(knownHosts.getAbsolutePath());
+ }
+ return defaultJSch;
+ }
+ }
+
+ @Override
+ protected SshSessionFactory createSessionFactory() {
+ return new TestSshSessionFactory();
+ }
+
+ @Override
+ protected void installConfig(String... config) {
+ SshSessionFactory factory = getSessionFactory();
+ assertTrue(factory instanceof JschConfigSessionFactory);
+ JschConfigSessionFactory j = (JschConfigSessionFactory) factory;
+ try {
+ j.setConfig(createConfig(config));
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
+ private OpenSshConfig createConfig(String... content) throws IOException {
+ File configFile = new File(sshDir, Constants.CONFIG);
+ if (content != null) {
+ Files.write(configFile.toPath(), Arrays.asList(content));
+ }
+ return new OpenSshConfig(getTemporaryDirectory(), configFile);
+ }
+
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ObjectIdMatcher.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ObjectIdMatcher.java
new file mode 100644
index 0000000000..4c6e0f0add
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ObjectIdMatcher.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2018, Google LLC.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.transport;
+
+import java.util.Collection;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Sets;
+import org.hamcrest.Description;
+import org.hamcrest.Factory;
+import org.hamcrest.Matcher;
+import org.hamcrest.TypeSafeMatcher;
+
+/**
+ * Multiple tests check that a collection of ObjectIds contain certain SHA1
+ * (written as strings). This matcher hides the ObjectId to string conversion to
+ * make the assertion more readable:
+ *
+ * assertThat(req.getWantsIds(), hasOnlyObjectIds("123123", "234234"));
+ */
+class ObjectIdMatcher extends TypeSafeMatcher<Collection<ObjectId>> {
+
+ private final Set<ObjectId> expectedOids;
+
+ private ObjectIdMatcher(Set<String> oids) {
+ this.expectedOids = oids.stream().map(ObjectId::fromString)
+ .collect(Collectors.toSet());
+ }
+
+ @Override
+ public void describeTo(Description desc) {
+ desc.appendText("Object ids:");
+ desc.appendValueList("<", ",", ">", expectedOids);
+ }
+
+ @Override
+ protected boolean matchesSafely(Collection<ObjectId> resultOids) {
+ return resultOids.containsAll(expectedOids)
+ && expectedOids.containsAll(resultOids);
+ }
+
+ /**
+ * Assert that all and only the received {@link ObjectId object ids} are in
+ * the expected set.
+ * <p>
+ * ObjectIds are compared by SHA1.
+ *
+ * @param oids
+ * Object ids to examine.
+ * @return true if examined and specified sets contains exactly the same
+ * elements.
+ */
+ @Factory
+ static Matcher<Collection<ObjectId>> hasOnlyObjectIds(
+ String... oids) {
+ return new ObjectIdMatcher(Sets.of(oids));
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/OpenSshConfigTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/OpenSshConfigTest.java
index 19fcbfd7a2..1a22e10f4c 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/OpenSshConfigTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/OpenSshConfigTest.java
@@ -50,7 +50,6 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import java.io.File;
@@ -67,6 +66,7 @@ import org.junit.Before;
import org.junit.Test;
import com.jcraft.jsch.ConfigRepository;
+import com.jcraft.jsch.ConfigRepository.Config;
public class OpenSshConfigTest extends RepositoryTestCase {
private File home;
@@ -164,6 +164,20 @@ public class OpenSshConfigTest extends RepositoryTestCase {
}
@Test
+ public void testCaseInsensitiveKeyLookup() throws Exception {
+ config("Host orcz\n" + "Port 29418\n"
+ + "\tHostName repo.or.cz\nStrictHostKeyChecking yes\n");
+ final Host h = osc.lookup("orcz");
+ Config c = h.getConfig();
+ String exactCase = c.getValue("StrictHostKeyChecking");
+ assertEquals("yes", exactCase);
+ assertEquals(exactCase, c.getValue("stricthostkeychecking"));
+ assertEquals(exactCase, c.getValue("STRICTHOSTKEYCHECKING"));
+ assertEquals(exactCase, c.getValue("sTrIcThostKEYcheckING"));
+ assertNull(c.getValue("sTrIcThostKEYcheckIN"));
+ }
+
+ @Test
public void testAlias_DoesNotMatch() throws Exception {
config("Host orcz\n" + "Port 29418\n" + "\tHostName repo.or.cz\n");
final Host h = osc.lookup("repo.or.cz");
@@ -334,21 +348,6 @@ public class OpenSshConfigTest extends RepositoryTestCase {
}
@Test
- public void testRepeatedLookups() throws Exception {
- config("Host orcz\n" + "\tConnectionAttempts 5\n");
- final Host h1 = osc.lookup("orcz");
- final Host h2 = osc.lookup("orcz");
- assertNotNull(h1);
- assertSame(h1, h2);
- assertEquals(5, h1.getConnectionAttempts());
- assertEquals(h1.getConnectionAttempts(), h2.getConnectionAttempts());
- final ConfigRepository.Config c = osc.getConfig("orcz");
- assertNotNull(c);
- assertSame(c, h1.getConfig());
- assertSame(c, h2.getConfig());
- }
-
- @Test
public void testRepeatedLookupsWithModification() throws Exception {
config("Host orcz\n" + "\tConnectionAttempts -1\n");
final Host h1 = osc.lookup("orcz");
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PacketLineOutTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PacketLineOutTest.java
index 391a701b8e..ad8ae42536 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PacketLineOutTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PacketLineOutTest.java
@@ -43,6 +43,7 @@
package org.eclipse.jgit.transport;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
@@ -50,7 +51,6 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
-import org.eclipse.jgit.lib.Constants;
import org.junit.Before;
import org.junit.Test;
@@ -173,8 +173,8 @@ public class PacketLineOutTest {
assertEquals(1, flushCnt[0]);
}
- private void assertBuffer(String exp) throws IOException {
+ private void assertBuffer(String exp) {
assertEquals(exp, new String(rawOut.toByteArray(),
- Constants.CHARACTER_ENCODING));
+ UTF_8));
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ProtocolV0ParserTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ProtocolV0ParserTest.java
new file mode 100644
index 0000000000..2c98c84ae5
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ProtocolV0ParserTest.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2018, Google LLC.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.transport;
+
+import static org.eclipse.jgit.transport.ObjectIdMatcher.hasOnlyObjectIds;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import org.eclipse.jgit.errors.PackProtocolException;
+import org.eclipse.jgit.lib.Config;
+import org.junit.Test;
+
+public class ProtocolV0ParserTest {
+ /*
+ * Convert the input lines to the PacketLine that the parser reads.
+ */
+ private static PacketLineIn formatAsPacketLine(String... inputLines)
+ throws IOException {
+ ByteArrayOutputStream send = new ByteArrayOutputStream();
+ PacketLineOut pckOut = new PacketLineOut(send);
+ for (String line : inputLines) {
+ if (line == PacketLineIn.END) {
+ pckOut.end();
+ } else if (line == PacketLineIn.DELIM) {
+ pckOut.writeDelim();
+ } else {
+ pckOut.writeString(line);
+ }
+ }
+
+ return new PacketLineIn(new ByteArrayInputStream(send.toByteArray()));
+ }
+
+ private static TransferConfig defaultConfig() {
+ Config rc = new Config();
+ rc.setBoolean("uploadpack", null, "allowfilter", true);
+ return new TransferConfig(rc);
+ }
+
+ @Test
+ public void testRecvWantsWithCapabilities()
+ throws PackProtocolException, IOException {
+ PacketLineIn pckIn = formatAsPacketLine(
+ String.join(" ", "want",
+ "4624442d68ee402a94364191085b77137618633e", "thin-pack",
+ "no-progress", "include-tag", "ofs-delta", "\n"),
+ "want f900c8326a43303685c46b279b9f70411bff1a4b\n",
+ PacketLineIn.END);
+ ProtocolV0Parser parser = new ProtocolV0Parser(defaultConfig());
+ FetchV0Request request = parser.recvWants(pckIn);
+ assertTrue(request.getClientCapabilities()
+ .contains(GitProtocolConstants.OPTION_THIN_PACK));
+ assertTrue(request.getClientCapabilities()
+ .contains(GitProtocolConstants.OPTION_NO_PROGRESS));
+ assertTrue(request.getClientCapabilities()
+ .contains(GitProtocolConstants.OPTION_INCLUDE_TAG));
+ assertTrue(request.getClientCapabilities()
+ .contains(GitProtocolConstants.CAPABILITY_OFS_DELTA));
+ assertThat(request.getWantIds(),
+ hasOnlyObjectIds("4624442d68ee402a94364191085b77137618633e",
+ "f900c8326a43303685c46b279b9f70411bff1a4b"));
+ }
+
+ @Test
+ public void testRecvWantsWithAgent()
+ throws PackProtocolException, IOException {
+ PacketLineIn pckIn = formatAsPacketLine(
+ String.join(" ", "want",
+ "4624442d68ee402a94364191085b77137618633e", "thin-pack",
+ "agent=JGit.test/0.0.1", "\n"),
+ "want f900c8326a43303685c46b279b9f70411bff1a4b\n",
+ PacketLineIn.END);
+ ProtocolV0Parser parser = new ProtocolV0Parser(defaultConfig());
+ FetchV0Request request = parser.recvWants(pckIn);
+ assertTrue(request.getClientCapabilities()
+ .contains(GitProtocolConstants.OPTION_THIN_PACK));
+ assertEquals(1, request.getClientCapabilities().size());
+ assertEquals("JGit.test/0.0.1", request.getAgent());
+ assertThat(request.getWantIds(),
+ hasOnlyObjectIds("4624442d68ee402a94364191085b77137618633e",
+ "f900c8326a43303685c46b279b9f70411bff1a4b"));
+ }
+
+ /*
+ * First round of protocol v0 negotiation. Client send wants, no
+ * capabilities.
+ */
+ @Test
+ public void testRecvWantsWithoutCapabilities()
+ throws PackProtocolException, IOException {
+ PacketLineIn pckIn = formatAsPacketLine(
+ "want 4624442d68ee402a94364191085b77137618633e\n",
+ "want f900c8326a43303685c46b279b9f70411bff1a4b\n",
+ PacketLineIn.END);
+ ProtocolV0Parser parser = new ProtocolV0Parser(defaultConfig());
+ FetchV0Request request = parser.recvWants(pckIn);
+ assertTrue(request.getClientCapabilities().isEmpty());
+ assertThat(request.getWantIds(),
+ hasOnlyObjectIds("4624442d68ee402a94364191085b77137618633e",
+ "f900c8326a43303685c46b279b9f70411bff1a4b"));
+ }
+
+ @Test
+ public void testRecvWantsDeepen()
+ throws PackProtocolException, IOException {
+ PacketLineIn pckIn = formatAsPacketLine(
+ "want 4624442d68ee402a94364191085b77137618633e\n",
+ "want f900c8326a43303685c46b279b9f70411bff1a4b\n", "deepen 3\n",
+ PacketLineIn.END);
+ ProtocolV0Parser parser = new ProtocolV0Parser(defaultConfig());
+ FetchV0Request request = parser.recvWants(pckIn);
+ assertTrue(request.getClientCapabilities().isEmpty());
+ assertEquals(3, request.getDepth());
+ assertThat(request.getWantIds(),
+ hasOnlyObjectIds("4624442d68ee402a94364191085b77137618633e",
+ "f900c8326a43303685c46b279b9f70411bff1a4b"));
+ }
+
+ @Test
+ public void testRecvWantsShallow()
+ throws PackProtocolException, IOException {
+ PacketLineIn pckIn = formatAsPacketLine(
+ "want 4624442d68ee402a94364191085b77137618633e\n",
+ "want f900c8326a43303685c46b279b9f70411bff1a4b\n",
+ "shallow 4b643d0ef739a1b494e7d6926d8d8ed80d35edf4\n",
+ PacketLineIn.END);
+ ProtocolV0Parser parser = new ProtocolV0Parser(defaultConfig());
+ FetchV0Request request = parser.recvWants(pckIn);
+ assertTrue(request.getClientCapabilities().isEmpty());
+ assertThat(request.getWantIds(),
+ hasOnlyObjectIds("4624442d68ee402a94364191085b77137618633e",
+ "f900c8326a43303685c46b279b9f70411bff1a4b"));
+ assertThat(request.getClientShallowCommits(),
+ hasOnlyObjectIds("4b643d0ef739a1b494e7d6926d8d8ed80d35edf4"));
+ }
+
+ @Test
+ public void testRecvWantsFilter()
+ throws PackProtocolException, IOException {
+ PacketLineIn pckIn = formatAsPacketLine(
+ "want 4624442d68ee402a94364191085b77137618633e\n",
+ "want f900c8326a43303685c46b279b9f70411bff1a4b\n",
+ "filter blob:limit=13000\n",
+ PacketLineIn.END);
+ ProtocolV0Parser parser = new ProtocolV0Parser(defaultConfig());
+ FetchV0Request request = parser.recvWants(pckIn);
+ assertTrue(request.getClientCapabilities().isEmpty());
+ assertThat(request.getWantIds(),
+ hasOnlyObjectIds("4624442d68ee402a94364191085b77137618633e",
+ "f900c8326a43303685c46b279b9f70411bff1a4b"));
+ assertEquals(13000, request.getFilterBlobLimit());
+ }
+
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ProtocolV2ParserTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ProtocolV2ParserTest.java
index bf67d46d51..dafa81ecd0 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ProtocolV2ParserTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ProtocolV2ParserTest.java
@@ -44,22 +44,20 @@ package org.eclipse.jgit.transport;
import static org.hamcrest.Matchers.hasItems;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
+import static org.eclipse.jgit.transport.ObjectIdMatcher.hasOnlyObjectIds;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
-import java.util.Collection;
-import java.util.List;
-import java.util.stream.Collectors;
import org.eclipse.jgit.errors.PackProtocolException;
import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.revwalk.RevCommit;
import org.junit.Before;
import org.junit.Rule;
@@ -137,12 +135,6 @@ public class ProtocolV2ParserTest {
return new PacketLineIn(new ByteArrayInputStream(send.toByteArray()));
}
- private static List<String> objIdsAsStrings(Collection<ObjectId> objIds) {
- // TODO(ifrade) Translate this to a matcher, so it would read as
- // assertThat(req.wantsIds(), hasObjectIds("...", "..."))
- return objIds.stream().map(ObjectId::name).collect(Collectors.toList());
- }
-
/*
* Succesful fetch with the basic core commands of the protocol.
*/
@@ -160,19 +152,19 @@ public class ProtocolV2ParserTest {
ProtocolV2Parser parser = new ProtocolV2Parser(
ConfigBuilder.getDefault());
FetchV2Request request = parser.parseFetchRequest(pckIn);
- assertTrue(request.getOptions()
+ assertTrue(request.getClientCapabilities()
.contains(GitProtocolConstants.OPTION_THIN_PACK));
- assertTrue(request.getOptions()
+ assertTrue(request.getClientCapabilities()
.contains(GitProtocolConstants.OPTION_NO_PROGRESS));
- assertTrue(request.getOptions()
+ assertTrue(request.getClientCapabilities()
.contains(GitProtocolConstants.OPTION_INCLUDE_TAG));
- assertTrue(request.getOptions()
+ assertTrue(request.getClientCapabilities()
.contains(GitProtocolConstants.CAPABILITY_OFS_DELTA));
- assertThat(objIdsAsStrings(request.getWantsIds()),
- hasItems("4624442d68ee402a94364191085b77137618633e",
+ assertThat(request.getWantIds(),
+ hasOnlyObjectIds("4624442d68ee402a94364191085b77137618633e",
"f900c8326a43303685c46b279b9f70411bff1a4b"));
- assertThat(objIdsAsStrings(request.getPeerHas()),
- hasItems("554f6e41067b9e3e565b6988a8294fac1cb78f4b",
+ assertThat(request.getPeerHas(),
+ hasOnlyObjectIds("554f6e41067b9e3e565b6988a8294fac1cb78f4b",
"abc760ab9ad72f08209943251b36cb886a578f87"));
assertTrue(request.getWantedRefs().isEmpty());
assertTrue(request.wasDoneReceived());
@@ -190,12 +182,12 @@ public class ProtocolV2ParserTest {
ProtocolV2Parser parser = new ProtocolV2Parser(
ConfigBuilder.getDefault());
FetchV2Request request = parser.parseFetchRequest(pckIn);
- assertThat(objIdsAsStrings(request.getClientShallowCommits()),
- hasItems("28274d02c489f4c7e68153056e9061a46f62d7a0",
+ assertThat(request.getClientShallowCommits(),
+ hasOnlyObjectIds("28274d02c489f4c7e68153056e9061a46f62d7a0",
"145e683b229dcab9d0e2ccb01b386f9ecc17d29d"));
assertTrue(request.getDeepenNotRefs().isEmpty());
assertEquals(15, request.getDepth());
- assertTrue(request.getOptions()
+ assertTrue(request.getClientCapabilities()
.contains(GitProtocolConstants.OPTION_DEEPEN_RELATIVE));
}
@@ -209,8 +201,8 @@ public class ProtocolV2ParserTest {
ProtocolV2Parser parser = new ProtocolV2Parser(
ConfigBuilder.getDefault());
FetchV2Request request = parser.parseFetchRequest(pckIn);
- assertThat(objIdsAsStrings(request.getClientShallowCommits()),
- hasItems("28274d02c489f4c7e68153056e9061a46f62d7a0",
+ assertThat(request.getClientShallowCommits(),
+ hasOnlyObjectIds("28274d02c489f4c7e68153056e9061a46f62d7a0",
"145e683b229dcab9d0e2ccb01b386f9ecc17d29d"));
assertThat(request.getDeepenNotRefs(),
hasItems("a08595f76159b09d57553e37a5123f1091bb13e7"));
@@ -226,8 +218,8 @@ public class ProtocolV2ParserTest {
ProtocolV2Parser parser = new ProtocolV2Parser(
ConfigBuilder.getDefault());
FetchV2Request request = parser.parseFetchRequest(pckIn);
- assertThat(objIdsAsStrings(request.getClientShallowCommits()),
- hasItems("28274d02c489f4c7e68153056e9061a46f62d7a0",
+ assertThat(request.getClientShallowCommits(),
+ hasOnlyObjectIds("28274d02c489f4c7e68153056e9061a46f62d7a0",
"145e683b229dcab9d0e2ccb01b386f9ecc17d29d"));
assertEquals(123123123, request.getDeepenSince());
}
@@ -256,24 +248,25 @@ public class ProtocolV2ParserTest {
@Test
public void testFetchMustNotHaveMultipleFilters() throws IOException {
- thrown.expect(PackProtocolException.class);
PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.DELIM,
"filter blob:none",
"filter blob:limit=12",
PacketLineIn.END);
ProtocolV2Parser parser = new ProtocolV2Parser(
ConfigBuilder.start().allowFilter().done());
- FetchV2Request request = parser.parseFetchRequest(pckIn);
- assertEquals(0, request.getFilterBlobLimit());
+
+ thrown.expect(PackProtocolException.class);
+ parser.parseFetchRequest(pckIn);
}
@Test
public void testFetchFilterWithoutAllowFilter() throws IOException {
- thrown.expect(PackProtocolException.class);
PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.DELIM,
"filter blob:limit=12", PacketLineIn.END);
ProtocolV2Parser parser = new ProtocolV2Parser(
ConfigBuilder.getDefault());
+
+ thrown.expect(PackProtocolException.class);
parser.parseFetchRequest(pckIn);
}
@@ -293,10 +286,11 @@ public class ProtocolV2ParserTest {
FetchV2Request request = parser.parseFetchRequest(pckIn);
assertEquals(1, request.getWantedRefs().size());
- assertThat(request.getWantedRefs(), hasItems("refs/heads/branchA"));
- assertEquals(1, request.getWantsIds().size());
- assertThat(objIdsAsStrings(request.getWantsIds()),
- hasItems("e4980cdc48cfa1301493ca94eb70523f6788b819"));
+ assertThat(request.getWantedRefs(),
+ hasItems("refs/heads/branchA"));
+ assertEquals(1, request.getWantIds().size());
+ assertThat(request.getWantIds(), hasOnlyObjectIds(
+ "e4980cdc48cfa1301493ca94eb70523f6788b819"));
}
@Test
@@ -318,4 +312,60 @@ public class ProtocolV2ParserTest {
assertThat(request.getWantedRefs(), hasItems("refs/heads/branchC"));
}
+ @Test
+ public void testLsRefsMinimalReq() throws IOException {
+ PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.DELIM,
+ PacketLineIn.END);
+
+ ProtocolV2Parser parser = new ProtocolV2Parser(
+ ConfigBuilder.getDefault());
+ LsRefsV2Request req = parser.parseLsRefsRequest(pckIn);
+ assertFalse(req.getPeel());
+ assertFalse(req.getSymrefs());
+ assertEquals(0, req.getRefPrefixes().size());
+ }
+
+ @Test
+ public void testLsRefsSymrefs() throws IOException {
+ PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.DELIM, "symrefs",
+ PacketLineIn.END);
+
+ ProtocolV2Parser parser = new ProtocolV2Parser(
+ ConfigBuilder.getDefault());
+ LsRefsV2Request req = parser.parseLsRefsRequest(pckIn);
+ assertFalse(req.getPeel());
+ assertTrue(req.getSymrefs());
+ assertEquals(0, req.getRefPrefixes().size());
+
+ }
+
+ @Test
+ public void testLsRefsPeel() throws IOException {
+ PacketLineIn pckIn = formatAsPacketLine(
+ PacketLineIn.DELIM,
+ "peel",
+ PacketLineIn.END);
+
+ ProtocolV2Parser parser = new ProtocolV2Parser(
+ ConfigBuilder.getDefault());
+ LsRefsV2Request req = parser.parseLsRefsRequest(pckIn);
+ assertTrue(req.getPeel());
+ assertFalse(req.getSymrefs());
+ assertEquals(0, req.getRefPrefixes().size());
+ }
+
+ @Test
+ public void testLsRefsRefPrefixes() throws IOException {
+ PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.DELIM,
+ "ref-prefix refs/for", "ref-prefix refs/heads",
+ PacketLineIn.END);
+
+ ProtocolV2Parser parser = new ProtocolV2Parser(
+ ConfigBuilder.getDefault());
+ LsRefsV2Request req = parser.parseLsRefsRequest(pckIn);
+ assertFalse(req.getPeel());
+ assertFalse(req.getSymrefs());
+ assertEquals(2, req.getRefPrefixes().size());
+ assertThat(req.getRefPrefixes(), hasItems("refs/for", "refs/heads"));
+ }
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateParserTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateParserTest.java
index 0647167eab..4bf26b6288 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateParserTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateParserTest.java
@@ -42,6 +42,7 @@
package org.eclipse.jgit.transport;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
@@ -334,7 +335,7 @@ public class PushCertificateParserTest {
assertFalse(input.contains(PushCertificateParser.END_CERT));
input += input;
Reader reader = new InputStreamReader(
- new ByteArrayInputStream(Constants.encode(input)));
+ new ByteArrayInputStream(Constants.encode(input)), UTF_8);
assertNotNull(PushCertificateParser.fromReader(reader));
assertNotNull(PushCertificateParser.fromReader(reader));
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateStoreTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateStoreTest.java
index 68e0129525..fa4fd65069 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateStoreTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateStoreTest.java
@@ -43,6 +43,7 @@
package org.eclipse.jgit.transport;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static org.eclipse.jgit.lib.ObjectId.zeroId;
import static org.eclipse.jgit.lib.RefUpdate.Result.FAST_FORWARD;
import static org.eclipse.jgit.lib.RefUpdate.Result.LOCK_FAILURE;
@@ -96,7 +97,9 @@ public class PushCertificateStoreTest {
+ "-----END PGP SIGNATURE-----\n");
try {
return PushCertificateParser.fromReader(new InputStreamReader(
- new ByteArrayInputStream(Constants.encode(cert.toString()))));
+ new ByteArrayInputStream(
+ Constants.encode(cert.toString())),
+ UTF_8));
} catch (IOException e) {
throw new IllegalArgumentException(e);
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ReceivePackAdvertiseRefsHookTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ReceivePackAdvertiseRefsHookTest.java
index c959f6c497..dfa50b6bb6 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ReceivePackAdvertiseRefsHookTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ReceivePackAdvertiseRefsHookTest.java
@@ -492,9 +492,8 @@ public class ReceivePackAdvertiseRefsHookTest extends LocalDiskRepositoryTestCas
assertSame(PacketLineIn.END, r.readString());
String errorLine = r.readString();
- System.out.println(errorLine);
- assertTrue(errorLine.startsWith(
- "unpack error Invalid submodule URL '-"));
+ assertTrue(errorLine.startsWith("unpack error"));
+ assertTrue(errorLine.contains("Invalid submodule URL '-"));
assertEquals("ng refs/heads/s n/a (unpacker error)", r.readString());
assertSame(PacketLineIn.END, r.readString());
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/SideBandOutputStreamTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/SideBandOutputStreamTest.java
index 4d3e162240..b6cf3564c1 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/SideBandOutputStreamTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/SideBandOutputStreamTest.java
@@ -44,6 +44,7 @@
package org.eclipse.jgit.transport;
import static java.lang.Integer.valueOf;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static org.eclipse.jgit.transport.SideBandOutputStream.CH_DATA;
import static org.eclipse.jgit.transport.SideBandOutputStream.CH_ERROR;
import static org.eclipse.jgit.transport.SideBandOutputStream.CH_PROGRESS;
@@ -59,7 +60,6 @@ import java.io.OutputStream;
import java.text.MessageFormat;
import org.eclipse.jgit.internal.JGitText;
-import org.eclipse.jgit.lib.Constants;
import org.junit.Before;
import org.junit.Test;
@@ -259,8 +259,7 @@ public class SideBandOutputStreamTest {
}
}
- private void assertBuffer(String exp) throws IOException {
- assertEquals(exp, new String(rawOut.toByteArray(),
- Constants.CHARACTER_ENCODING));
+ private void assertBuffer(String exp) {
+ assertEquals(exp, new String(rawOut.toByteArray(), UTF_8));
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TestProtocolTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TestProtocolTest.java
index 953c9fc30a..1c4d0cfe24 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TestProtocolTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TestProtocolTest.java
@@ -46,6 +46,7 @@ package org.eclipse.jgit.transport;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import java.util.ArrayList;
import java.util.List;
@@ -238,6 +239,7 @@ public class TestProtocolTest {
.setRemote(user1Uri.toString())
.setRefSpecs(MASTER)
.call();
+ fail("accepted not permitted fetch");
} catch (InvalidRemoteException expected) {
// Expected.
}
@@ -282,6 +284,7 @@ public class TestProtocolTest {
.setRemote(user1Uri.toString())
.setRefSpecs(HEADS)
.call();
+ fail("accepted not permitted push");
} catch (TransportException expected) {
assertTrue(expected.getMessage().contains(
JGitText.get().pushNotPermitted));
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java
index 317ac32e6d..8acbcce36d 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java
@@ -7,6 +7,7 @@ import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.theInstance;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -27,6 +28,7 @@ import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
@@ -427,17 +429,7 @@ public class UploadPackTest {
RefFilter refFilter, ProtocolV2Hook hook, String... inputLines)
throws Exception {
- ByteArrayOutputStream send = new ByteArrayOutputStream();
- PacketLineOut pckOut = new PacketLineOut(send);
- for (String line : inputLines) {
- if (line == PacketLineIn.END) {
- pckOut.end();
- } else if (line == PacketLineIn.DELIM) {
- pckOut.writeDelim();
- } else {
- pckOut.writeString(line);
- }
- }
+ ByteArrayInputStream send = linesAsInputStream(inputLines);
server.getConfig().setString("protocol", null, "version", "2");
UploadPack up = new UploadPack(server);
@@ -451,11 +443,28 @@ public class UploadPackTest {
}
ByteArrayOutputStream recv = new ByteArrayOutputStream();
- up.upload(new ByteArrayInputStream(send.toByteArray()), recv, null);
+ up.upload(send, recv, null);
return new ByteArrayInputStream(recv.toByteArray());
}
+ private static ByteArrayInputStream linesAsInputStream(String... inputLines)
+ throws IOException {
+ try (ByteArrayOutputStream send = new ByteArrayOutputStream()) {
+ PacketLineOut pckOut = new PacketLineOut(send);
+ for (String line : inputLines) {
+ if (line == PacketLineIn.END) {
+ pckOut.end();
+ } else if (line == PacketLineIn.DELIM) {
+ pckOut.writeDelim();
+ } else {
+ pckOut.writeString(line);
+ }
+ }
+ return new ByteArrayInputStream(send.toByteArray());
+ }
+ }
+
/*
* Invokes UploadPack with protocol v2 and sends it the given lines.
* Returns UploadPack's output stream, not including the capability
@@ -484,6 +493,8 @@ public class UploadPackTest {
private LsRefsV2Request lsRefsRequest;
+ private FetchV2Request fetchRequest;
+
@Override
public void onCapabilities(CapabilitiesV2Request req) {
capabilitiesRequest = req;
@@ -493,6 +504,11 @@ public class UploadPackTest {
public void onLsRefs(LsRefsV2Request req) {
lsRefsRequest = req;
}
+
+ @Override
+ public void onFetch(FetchV2Request req) {
+ fetchRequest = req;
+ }
}
@Test
@@ -501,18 +517,18 @@ public class UploadPackTest {
ByteArrayInputStream recvStream =
uploadPackV2Setup(null, null, hook, PacketLineIn.END);
PacketLineIn pckIn = new PacketLineIn(recvStream);
-
assertThat(hook.capabilitiesRequest, notNullValue());
assertThat(pckIn.readString(), is("version 2"));
assertThat(
- Arrays.asList(pckIn.readString(), pckIn.readString()),
- // TODO(jonathantanmy) This check is written this way
- // to make it simple to see that we expect this list of
- // capabilities, but probably should be loosened to
- // allow additional commands to be added to the list,
- // and additional capabilities to be added to existing
- // commands without requiring test changes.
- hasItems("ls-refs", "fetch=shallow"));
+ Arrays.asList(pckIn.readString(), pckIn.readString(),
+ pckIn.readString()),
+ // TODO(jonathantanmy) This check is written this way
+ // to make it simple to see that we expect this list of
+ // capabilities, but probably should be loosened to
+ // allow additional commands to be added to the list,
+ // and additional capabilities to be added to existing
+ // commands without requiring test changes.
+ hasItems("ls-refs", "fetch=shallow", "server-option"));
assertTrue(pckIn.readString() == PacketLineIn.END);
}
@@ -525,10 +541,11 @@ public class UploadPackTest {
assertThat(pckIn.readString(), is("version 2"));
assertThat(
- Arrays.asList(pckIn.readString(), pckIn.readString()),
- // TODO(jonathantanmy) This check overspecifies the
- // order of the capabilities of "fetch".
- hasItems("ls-refs", "fetch=filter shallow"));
+ Arrays.asList(pckIn.readString(), pckIn.readString(),
+ pckIn.readString()),
+ // TODO(jonathantanmy) This check overspecifies the
+ // order of the capabilities of "fetch".
+ hasItems("ls-refs", "fetch=filter shallow", "server-option"));
assertTrue(pckIn.readString() == PacketLineIn.END);
}
@@ -541,10 +558,12 @@ public class UploadPackTest {
assertThat(pckIn.readString(), is("version 2"));
assertThat(
- Arrays.asList(pckIn.readString(), pckIn.readString()),
- // TODO(jonathantanmy) This check overspecifies the
- // order of the capabilities of "fetch".
- hasItems("ls-refs", "fetch=ref-in-want shallow"));
+ Arrays.asList(pckIn.readString(), pckIn.readString(),
+ pckIn.readString()),
+ // TODO(jonathantanmy) This check overspecifies the
+ // order of the capabilities of "fetch".
+ hasItems("ls-refs", "fetch=ref-in-want shallow",
+ "server-option"));
assertTrue(pckIn.readString() == PacketLineIn.END);
}
@@ -557,8 +576,9 @@ public class UploadPackTest {
assertThat(pckIn.readString(), is("version 2"));
assertThat(
- Arrays.asList(pckIn.readString(), pckIn.readString()),
- hasItems("ls-refs", "fetch=shallow"));
+ Arrays.asList(pckIn.readString(), pckIn.readString(),
+ pckIn.readString()),
+ hasItems("ls-refs", "fetch=shallow", "server-option"));
assertTrue(pckIn.readString() == PacketLineIn.END);
}
@@ -572,8 +592,9 @@ public class UploadPackTest {
assertThat(pckIn.readString(), is("version 2"));
assertThat(
- Arrays.asList(pckIn.readString(), pckIn.readString()),
- hasItems("ls-refs", "fetch=shallow"));
+ Arrays.asList(pckIn.readString(), pckIn.readString(),
+ pckIn.readString()),
+ hasItems("ls-refs", "fetch=shallow", "server-option"));
assertTrue(pckIn.readString() == PacketLineIn.END);
}
@@ -718,6 +739,21 @@ public class UploadPackTest {
PacketLineIn.END);
}
+ @Test
+ public void testV2LsRefsServerOptions() throws Exception {
+ String[] lines = { "command=ls-refs\n",
+ "server-option=one\n", "server-option=two\n",
+ PacketLineIn.DELIM,
+ PacketLineIn.END };
+
+ TestV2Hook testHook = new TestV2Hook();
+ uploadPackV2Setup(null, null, testHook, lines);
+
+ LsRefsV2Request req = testHook.lsRefsRequest;
+ assertEquals(2, req.getServerOptions().size());
+ assertThat(req.getServerOptions(), hasItems("one", "two"));
+ }
+
/*
* Parse multiplexed packfile output from upload-pack using protocol V2
* into the client repository.
@@ -1191,6 +1227,270 @@ public class UploadPackTest {
}
@Test
+ public void testV2FetchShallowSince() throws Exception {
+ PersonIdent person = new PersonIdent(remote.getRepository());
+
+ RevCommit beyondBoundary = remote.commit()
+ .committer(new PersonIdent(person, 1510000000, 0)).create();
+ RevCommit boundary = remote.commit().parent(beyondBoundary)
+ .committer(new PersonIdent(person, 1520000000, 0)).create();
+ RevCommit tooOld = remote.commit()
+ .committer(new PersonIdent(person, 1500000000, 0)).create();
+ RevCommit merge = remote.commit().parent(boundary).parent(tooOld)
+ .committer(new PersonIdent(person, 1530000000, 0)).create();
+
+ remote.update("branch1", merge);
+
+ // Report that we only have "boundary" as a shallow boundary.
+ ByteArrayInputStream recvStream = uploadPackV2(
+ "command=fetch\n",
+ PacketLineIn.DELIM,
+ "shallow " + boundary.toObjectId().getName() + "\n",
+ "deepen-since 1510000\n",
+ "want " + merge.toObjectId().getName() + "\n",
+ "have " + boundary.toObjectId().getName() + "\n",
+ "done\n",
+ PacketLineIn.END);
+ PacketLineIn pckIn = new PacketLineIn(recvStream);
+ assertThat(pckIn.readString(), is("shallow-info"));
+
+ // "merge" is shallow because one of its parents is committed
+ // earlier than the given deepen-since time.
+ assertThat(pckIn.readString(), is("shallow " + merge.toObjectId().getName()));
+
+ // "boundary" is unshallow because its parent committed at or
+ // later than the given deepen-since time.
+ assertThat(pckIn.readString(), is("unshallow " + boundary.toObjectId().getName()));
+
+ assertThat(pckIn.readString(), theInstance(PacketLineIn.DELIM));
+ assertThat(pckIn.readString(), is("packfile"));
+ parsePack(recvStream);
+
+ // The server does not send this because it is committed
+ // earlier than the given deepen-since time.
+ assertFalse(client.hasObject(tooOld.toObjectId()));
+
+ // The server does not send this because the client claims to
+ // have it.
+ assertFalse(client.hasObject(boundary.toObjectId()));
+
+ // The server sends both these commits.
+ assertTrue(client.hasObject(beyondBoundary.toObjectId()));
+ assertTrue(client.hasObject(merge.toObjectId()));
+ }
+
+ @Test
+ public void testV2FetchShallowSince_excludedParentWithMultipleChildren() throws Exception {
+ PersonIdent person = new PersonIdent(remote.getRepository());
+
+ RevCommit base = remote.commit()
+ .committer(new PersonIdent(person, 1500000000, 0)).create();
+ RevCommit child1 = remote.commit().parent(base)
+ .committer(new PersonIdent(person, 1510000000, 0)).create();
+ RevCommit child2 = remote.commit().parent(base)
+ .committer(new PersonIdent(person, 1520000000, 0)).create();
+
+ remote.update("branch1", child1);
+ remote.update("branch2", child2);
+
+ ByteArrayInputStream recvStream = uploadPackV2(
+ "command=fetch\n",
+ PacketLineIn.DELIM,
+ "deepen-since 1510000\n",
+ "want " + child1.toObjectId().getName() + "\n",
+ "want " + child2.toObjectId().getName() + "\n",
+ "done\n",
+ PacketLineIn.END);
+ PacketLineIn pckIn = new PacketLineIn(recvStream);
+ assertThat(pckIn.readString(), is("shallow-info"));
+
+ // "base" is excluded, so its children are shallow.
+ assertThat(
+ Arrays.asList(pckIn.readString(), pckIn.readString()),
+ hasItems(
+ "shallow " + child1.toObjectId().getName(),
+ "shallow " + child2.toObjectId().getName()));
+
+ assertThat(pckIn.readString(), theInstance(PacketLineIn.DELIM));
+ assertThat(pckIn.readString(), is("packfile"));
+ parsePack(recvStream);
+
+ // Only the children are sent.
+ assertFalse(client.hasObject(base.toObjectId()));
+ assertTrue(client.hasObject(child1.toObjectId()));
+ assertTrue(client.hasObject(child2.toObjectId()));
+ }
+
+ @Test
+ public void testV2FetchShallowSince_noCommitsSelected() throws Exception {
+ PersonIdent person = new PersonIdent(remote.getRepository());
+
+ RevCommit tooOld = remote.commit()
+ .committer(new PersonIdent(person, 1500000000, 0)).create();
+
+ remote.update("branch1", tooOld);
+
+ thrown.expect(PackProtocolException.class);
+ thrown.expectMessage("No commits selected for shallow request");
+ uploadPackV2(
+ "command=fetch\n",
+ PacketLineIn.DELIM,
+ "deepen-since 1510000\n",
+ "want " + tooOld.toObjectId().getName() + "\n",
+ "done\n",
+ PacketLineIn.END);
+ }
+
+ @Test
+ public void testV2FetchDeepenNot() throws Exception {
+ RevCommit one = remote.commit().message("one").create();
+ RevCommit two = remote.commit().message("two").parent(one).create();
+ RevCommit three = remote.commit().message("three").parent(two).create();
+ RevCommit side = remote.commit().message("side").parent(one).create();
+ RevCommit merge = remote.commit().message("merge")
+ .parent(three).parent(side).create();
+
+ remote.update("branch1", merge);
+ remote.update("side", side);
+
+ // The client is a shallow clone that only has "three", and
+ // wants "merge" while excluding "side".
+ ByteArrayInputStream recvStream = uploadPackV2(
+ "command=fetch\n",
+ PacketLineIn.DELIM,
+ "shallow " + three.toObjectId().getName() + "\n",
+ "deepen-not side\n",
+ "want " + merge.toObjectId().getName() + "\n",
+ "have " + three.toObjectId().getName() + "\n",
+ "done\n",
+ PacketLineIn.END);
+ PacketLineIn pckIn = new PacketLineIn(recvStream);
+ assertThat(pckIn.readString(), is("shallow-info"));
+
+ // "merge" is shallow because "side" is excluded by deepen-not.
+ // "two" is shallow because "one" (as parent of "side") is excluded by deepen-not.
+ assertThat(
+ Arrays.asList(pckIn.readString(), pckIn.readString()),
+ hasItems(
+ "shallow " + merge.toObjectId().getName(),
+ "shallow " + two.toObjectId().getName()));
+
+ // "three" is unshallow because its parent "two" is now available.
+ assertThat(pckIn.readString(), is("unshallow " + three.toObjectId().getName()));
+
+ assertThat(pckIn.readString(), theInstance(PacketLineIn.DELIM));
+ assertThat(pckIn.readString(), is("packfile"));
+ parsePack(recvStream);
+
+ // The server does not send these because they are excluded by
+ // deepen-not.
+ assertFalse(client.hasObject(side.toObjectId()));
+ assertFalse(client.hasObject(one.toObjectId()));
+
+ // The server does not send this because the client claims to
+ // have it.
+ assertFalse(client.hasObject(three.toObjectId()));
+
+ // The server sends both these commits.
+ assertTrue(client.hasObject(merge.toObjectId()));
+ assertTrue(client.hasObject(two.toObjectId()));
+ }
+
+ @Test
+ public void testV2FetchDeepenNot_excludeDescendantOfWant() throws Exception {
+ RevCommit one = remote.commit().message("one").create();
+ RevCommit two = remote.commit().message("two").parent(one).create();
+ RevCommit three = remote.commit().message("three").parent(two).create();
+ RevCommit four = remote.commit().message("four").parent(three).create();
+
+ remote.update("two", two);
+ remote.update("four", four);
+
+ thrown.expect(PackProtocolException.class);
+ thrown.expectMessage("No commits selected for shallow request");
+ uploadPackV2(
+ "command=fetch\n",
+ PacketLineIn.DELIM,
+ "deepen-not four\n",
+ "want " + two.toObjectId().getName() + "\n",
+ "done\n",
+ PacketLineIn.END);
+ }
+
+ @Test
+ public void testV2FetchDeepenNot_supportAnnotatedTags() throws Exception {
+ RevCommit one = remote.commit().message("one").create();
+ RevCommit two = remote.commit().message("two").parent(one).create();
+ RevCommit three = remote.commit().message("three").parent(two).create();
+ RevCommit four = remote.commit().message("four").parent(three).create();
+ RevTag twoTag = remote.tag("twotag", two);
+
+ remote.update("refs/tags/twotag", twoTag);
+ remote.update("four", four);
+
+ ByteArrayInputStream recvStream = uploadPackV2(
+ "command=fetch\n",
+ PacketLineIn.DELIM,
+ "deepen-not twotag\n",
+ "want " + four.toObjectId().getName() + "\n",
+ "done\n",
+ PacketLineIn.END);
+ PacketLineIn pckIn = new PacketLineIn(recvStream);
+ assertThat(pckIn.readString(), is("shallow-info"));
+ assertThat(pckIn.readString(), is("shallow " + three.toObjectId().getName()));
+ assertThat(pckIn.readString(), theInstance(PacketLineIn.DELIM));
+ assertThat(pckIn.readString(), is("packfile"));
+ parsePack(recvStream);
+ assertFalse(client.hasObject(one.toObjectId()));
+ assertFalse(client.hasObject(two.toObjectId()));
+ assertTrue(client.hasObject(three.toObjectId()));
+ assertTrue(client.hasObject(four.toObjectId()));
+ }
+
+ @Test
+ public void testV2FetchDeepenNot_excludedParentWithMultipleChildren() throws Exception {
+ PersonIdent person = new PersonIdent(remote.getRepository());
+
+ RevCommit base = remote.commit()
+ .committer(new PersonIdent(person, 1500000000, 0)).create();
+ RevCommit child1 = remote.commit().parent(base)
+ .committer(new PersonIdent(person, 1510000000, 0)).create();
+ RevCommit child2 = remote.commit().parent(base)
+ .committer(new PersonIdent(person, 1520000000, 0)).create();
+
+ remote.update("base", base);
+ remote.update("branch1", child1);
+ remote.update("branch2", child2);
+
+ ByteArrayInputStream recvStream = uploadPackV2(
+ "command=fetch\n",
+ PacketLineIn.DELIM,
+ "deepen-not base\n",
+ "want " + child1.toObjectId().getName() + "\n",
+ "want " + child2.toObjectId().getName() + "\n",
+ "done\n",
+ PacketLineIn.END);
+ PacketLineIn pckIn = new PacketLineIn(recvStream);
+ assertThat(pckIn.readString(), is("shallow-info"));
+
+ // "base" is excluded, so its children are shallow.
+ assertThat(
+ Arrays.asList(pckIn.readString(), pckIn.readString()),
+ hasItems(
+ "shallow " + child1.toObjectId().getName(),
+ "shallow " + child2.toObjectId().getName()));
+
+ assertThat(pckIn.readString(), theInstance(PacketLineIn.DELIM));
+ assertThat(pckIn.readString(), is("packfile"));
+ parsePack(recvStream);
+
+ // Only the children are sent.
+ assertFalse(client.hasObject(base.toObjectId()));
+ assertTrue(client.hasObject(child1.toObjectId()));
+ assertTrue(client.hasObject(child2.toObjectId()));
+ }
+
+ @Test
public void testV2FetchUnrecognizedArgument() throws Exception {
thrown.expect(PackProtocolException.class);
thrown.expectMessage("unexpected invalid-argument");
@@ -1202,6 +1502,21 @@ public class UploadPackTest {
}
@Test
+ public void testV2FetchServerOptions() throws Exception {
+ String[] lines = { "command=fetch\n", "server-option=one\n",
+ "server-option=two\n", PacketLineIn.DELIM,
+ PacketLineIn.END };
+
+ TestV2Hook testHook = new TestV2Hook();
+ uploadPackV2Setup(null, null, testHook, lines);
+
+ FetchV2Request req = testHook.fetchRequest;
+ assertNotNull(req);
+ assertEquals(2, req.getServerOptions().size());
+ assertThat(req.getServerOptions(), hasItems("one", "two"));
+ }
+
+ @Test
public void testV2FetchFilter() throws Exception {
RevBlob big = remote.blob("foobar");
RevBlob small = remote.blob("fooba");
@@ -1455,6 +1770,45 @@ public class UploadPackTest {
assertTrue(client.hasObject(three.toObjectId()));
}
+ @Test
+ public void testGetPeerAgentProtocolV0() throws Exception {
+ RevCommit one = remote.commit().message("1").create();
+ remote.update("one", one);
+
+ UploadPack up = new UploadPack(server);
+ ByteArrayInputStream send = linesAsInputStream(
+ "want " + one.getName() + " agent=JGit-test/1.2.3\n",
+ PacketLineIn.END,
+ "have 11cedf1b796d44207da702f7d420684022fc0f09\n", "done\n");
+
+ ByteArrayOutputStream recv = new ByteArrayOutputStream();
+ up.upload(send, recv, null);
+
+ assertEquals(up.getPeerUserAgent(), "JGit-test/1.2.3");
+ }
+
+ @Test
+ public void testGetPeerAgentProtocolV2() throws Exception {
+ server.getConfig().setString("protocol", null, "version", "2");
+
+ RevCommit one = remote.commit().message("1").create();
+ remote.update("one", one);
+
+ UploadPack up = new UploadPack(server);
+ up.setExtraParameters(Sets.of("version=2"));
+
+ ByteArrayInputStream send = linesAsInputStream(
+ "command=fetch\n", "agent=JGit-test/1.2.4\n",
+ PacketLineIn.DELIM, "want " + one.getName() + "\n",
+ "have 11cedf1b796d44207da702f7d420684022fc0f09\n", "done\n",
+ PacketLineIn.END);
+
+ ByteArrayOutputStream recv = new ByteArrayOutputStream();
+ up.upload(send, recv, null);
+
+ assertEquals(up.getPeerUserAgent(), "JGit-test/1.2.4");
+ }
+
private static class RejectAllRefFilter implements RefFilter {
@Override
public Map<String, Ref> filter(Map<String, Ref> refs) {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/WalkEncryptionTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/WalkEncryptionTest.java
index f2fb0224ef..4750d15b3d 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/WalkEncryptionTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/WalkEncryptionTest.java
@@ -651,7 +651,8 @@ public class WalkEncryptionTest {
Properties props = Props.discover();
props.put(AmazonS3.Keys.PASSWORD, JGIT_PASS);
props.put(AmazonS3.Keys.CRYPTO_ALG, algorithm);
- try (PrintWriter writer = new PrintWriter(JGIT_CONF_FILE)) {
+ try (PrintWriter writer = new PrintWriter(JGIT_CONF_FILE,
+ UTF_8.name())) {
props.store(writer, "JGIT S3 connection configuration file.");
}
}
@@ -665,7 +666,8 @@ public class WalkEncryptionTest {
static void configCreate(Properties source) throws Exception {
Properties target = Props.discover();
target.putAll(source);
- try (PrintWriter writer = new PrintWriter(JGIT_CONF_FILE)) {
+ try (PrintWriter writer = new PrintWriter(JGIT_CONF_FILE,
+ UTF_8.name())) {
target.store(writer, "JGIT S3 connection configuration file.");
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/http/JDKHttpConnectionTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/http/JDKHttpConnectionTest.java
new file mode 100644
index 0000000000..10ee829199
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/http/JDKHttpConnectionTest.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2018 Matthias Sohn <matthias.sohn@sap.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.transport.http;
+
+import static java.util.Arrays.asList;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.net.HttpURLConnection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class JDKHttpConnectionTest {
+
+ private Map<String, List<String>> headers = new HashMap<>();
+
+ private HttpURLConnection u;
+
+ private JDKHttpConnection c;
+
+ @Before
+ public void setup() {
+ u = mock(HttpURLConnection.class);
+ c = new JDKHttpConnection(u);
+ headers.put("ABC", asList("x"));
+ }
+
+ @Test
+ public void testSingle() {
+ when(u.getHeaderFields()).thenReturn(headers);
+ assertValues("AbC", "x");
+ }
+
+ @Test
+ public void testMultiple1() {
+ headers.put("abc", asList("a"));
+ headers.put("aBC", asList("d", "e"));
+ headers.put("ABc", Collections.emptyList());
+ headers.put("AbC", (List<String>) null);
+ when(u.getHeaderFields()).thenReturn(headers);
+ assertValues("AbC", "a", "d", "e", "x");
+ }
+
+ @Test
+ public void testMultiple2() {
+ headers.put("ab", asList("y", "z", "z"));
+ when(u.getHeaderFields()).thenReturn(headers);
+ assertValues("ab", "z", "y", "z");
+ assertValues("abc", "x");
+ assertValues("aBc", "x");
+ assertValues("AbCd");
+ }
+
+ @Test
+ public void testCommaSeparatedList() {
+ headers.put("abc", asList("a,b,c", "d"));
+ when(u.getHeaderFields()).thenReturn(headers);
+ assertValues("Abc", "a,b,c", "x", "d");
+ }
+
+ private void assertValues(String key, String... values) {
+ List<String> l = new LinkedList<>();
+ List<String> hf = c.getHeaderFields(key);
+ if (hf != null) {
+ l.addAll(hf);
+ }
+ for (String v : values) {
+ if (!l.remove(v)) {
+ fail("value " + v + " not found");
+ }
+ }
+ assertTrue("found unexpected entries " + l, l.isEmpty());
+ }
+
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/AbstractTreeIteratorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/AbstractTreeIteratorTest.java
index 934984f7e5..8afd49ab45 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/AbstractTreeIteratorTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/AbstractTreeIteratorTest.java
@@ -66,7 +66,7 @@ public class AbstractTreeIteratorTest {
return s > 0 ? path.substring(0, s) : "";
}
- public class FakeTreeIterator extends WorkingTreeIterator {
+ public static class FakeTreeIterator extends WorkingTreeIterator {
public FakeTreeIterator(String pathName, FileMode fileMode) {
super(prefix(pathName), new Config().get(WorkingTreeOptions.KEY));
mode = fileMode.getBits();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/InterIndexDiffFilterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/InterIndexDiffFilterTest.java
index cba35d8042..ea5db09349 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/InterIndexDiffFilterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/InterIndexDiffFilterTest.java
@@ -42,6 +42,7 @@
*/
package org.eclipse.jgit.treewalk.filter;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -114,7 +115,7 @@ public class InterIndexDiffFilterTest extends LocalDiskRepositoryTestCase {
}
private ObjectId id(String data) {
- byte[] bytes = data.getBytes();
+ byte[] bytes = data.getBytes(UTF_8);
return db.newObjectInserter().idFor(Constants.OBJ_BLOB, bytes);
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/BlockListTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/BlockListTest.java
index 0a3de85f7c..dca9c57a64 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/BlockListTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/BlockListTest.java
@@ -47,6 +47,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import java.util.Iterator;
@@ -84,18 +85,21 @@ public class BlockListTest {
try {
list.get(-1);
+ fail("accepted out-of-bounds index");
} catch (IndexOutOfBoundsException badIndex) {
assertEquals(String.valueOf(-1), badIndex.getMessage());
}
try {
list.get(0);
+ fail("accepted out-of-bounds index");
} catch (IndexOutOfBoundsException badIndex) {
assertEquals(String.valueOf(0), badIndex.getMessage());
}
try {
list.get(4);
+ fail("accepted out-of-bounds index");
} catch (IndexOutOfBoundsException badIndex) {
assertEquals(String.valueOf(4), badIndex.getMessage());
}
@@ -114,6 +118,7 @@ public class BlockListTest {
try {
list.get(3);
+ fail("accepted out-of-bounds index");
} catch (IndexOutOfBoundsException badIndex) {
assertEquals(String.valueOf(3), badIndex.getMessage());
}
@@ -125,18 +130,21 @@ public class BlockListTest {
try {
list.set(-1, "foo");
+ fail("accepted out-of-bounds index");
} catch (IndexOutOfBoundsException badIndex) {
assertEquals(String.valueOf(-1), badIndex.getMessage());
}
try {
list.set(0, "foo");
+ fail("accepted out-of-bounds index");
} catch (IndexOutOfBoundsException badIndex) {
assertEquals(String.valueOf(0), badIndex.getMessage());
}
try {
list.set(4, "foo");
+ fail("accepted out-of-bounds index");
} catch (IndexOutOfBoundsException badIndex) {
assertEquals(String.valueOf(4), badIndex.getMessage());
}
@@ -161,6 +169,7 @@ public class BlockListTest {
try {
list.set(3, "bar");
+ fail("accepted out-of-bounds index");
} catch (IndexOutOfBoundsException badIndex) {
assertEquals(String.valueOf(3), badIndex.getMessage());
}
@@ -323,12 +332,14 @@ public class BlockListTest {
try {
list.add(-1, Integer.valueOf(42));
+ fail("accepted out-of-bounds index");
} catch (IndexOutOfBoundsException badIndex) {
assertEquals(String.valueOf(-1), badIndex.getMessage());
}
try {
list.add(4, Integer.valueOf(42));
+ fail("accepted out-of-bounds index");
} catch (IndexOutOfBoundsException badIndex) {
assertEquals(String.valueOf(4), badIndex.getMessage());
}
@@ -341,12 +352,14 @@ public class BlockListTest {
try {
list.remove(-1);
+ fail("accepted out-of-bounds index");
} catch (IndexOutOfBoundsException badIndex) {
assertEquals(String.valueOf(-1), badIndex.getMessage());
}
try {
list.remove(4);
+ fail("accepted out-of-bounds index");
} catch (IndexOutOfBoundsException badIndex) {
assertEquals(String.valueOf(4), badIndex.getMessage());
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/HookTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/HookTest.java
index e34c3cebd6..e5fcbf9d7c 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/HookTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/HookTest.java
@@ -199,7 +199,8 @@ public class HookTest extends RepositoryTestCase {
assumeSupportedPlatform();
writeHookFile(PreCommitHook.NAME,
- "#!/bin/sh\necho \"test $1 $2\"\nread INPUT\necho $INPUT\necho 1>&2 \"stderr\"");
+ "#!/bin/sh\necho \"test $1 $2\"\nread INPUT\necho $INPUT\n"
+ + "echo $GIT_DIR\necho $GIT_WORK_TREE\necho 1>&2 \"stderr\"");
ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayOutputStream err = new ByteArrayOutputStream();
ProcessResult res = FS.DETECTED.runHookIfPresent(db,
@@ -208,7 +209,9 @@ public class HookTest extends RepositoryTestCase {
"arg1", "arg2" },
new PrintStream(out), new PrintStream(err), "stdin");
- assertEquals("unexpected hook output", "test arg1 arg2\nstdin\n",
+ assertEquals("unexpected hook output",
+ "test arg1 arg2\nstdin\n" + db.getDirectory().getAbsolutePath()
+ + '\n' + db.getWorkTree().getAbsolutePath() + '\n',
out.toString("UTF-8"));
assertEquals("unexpected output on stderr stream", "stderr\n",
err.toString("UTF-8"));
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/IOReadLineTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/IOReadLineTest.java
index 928fb2ed9a..fa303ec286 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/IOReadLineTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/IOReadLineTest.java
@@ -43,6 +43,7 @@
package org.eclipse.jgit.util;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertEquals;
import java.io.BufferedReader;
@@ -105,7 +106,7 @@ public class IOReadLineTest {
private Reader newReader(String in) {
Reader r = new InputStreamReader(
- new ByteArrayInputStream(Constants.encode(in)));
+ new ByteArrayInputStream(Constants.encode(in)), UTF_8);
if (buffered) {
r = new BufferedReader(r);
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RawParseUtils_LineMapTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RawParseUtils_LineMapTest.java
index 7630c11185..e7bfa000ac 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RawParseUtils_LineMapTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RawParseUtils_LineMapTest.java
@@ -85,10 +85,11 @@ public class RawParseUtils_LineMapTest {
}
@Test
- public void testBinary() {
+ public void testNulByte() {
final byte[] buf = "xxxfoo\nb\0ar".getBytes(ISO_8859_1);
final IntList map = RawParseUtils.lineMap(buf, 3, buf.length);
- assertArrayEquals(new int[]{Integer.MIN_VALUE, 3, buf.length}, asInts(map));
+ assertArrayEquals(new int[] { Integer.MIN_VALUE, 3, 7, buf.length },
+ asInts(map));
}
@Test
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RunExternalScriptTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RunExternalScriptTest.java
index 7c0985ef42..19af83611b 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RunExternalScriptTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RunExternalScriptTest.java
@@ -43,6 +43,7 @@
package org.eclipse.jgit.util;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertEquals;
import java.io.ByteArrayInputStream;
@@ -75,10 +76,10 @@ public class RunExternalScriptTest {
File script = writeTempFile("cat -");
int rc = FS.DETECTED.runProcess(
new ProcessBuilder("sh", script.getPath()), out, err,
- new ByteArrayInputStream(inputStr.getBytes()));
+ new ByteArrayInputStream(inputStr.getBytes(UTF_8)));
assertEquals(0, rc);
- assertEquals(inputStr, new String(out.toByteArray()));
- assertEquals("", new String(err.toByteArray()));
+ assertEquals(inputStr, new String(out.toByteArray(), UTF_8));
+ assertEquals("", new String(err.toByteArray(), UTF_8));
}
@Test
@@ -88,8 +89,8 @@ public class RunExternalScriptTest {
new ProcessBuilder("sh", script.getPath()), out, err,
(InputStream) null);
assertEquals(0, rc);
- assertEquals("", new String(out.toByteArray()));
- assertEquals("", new String(err.toByteArray()));
+ assertEquals("", new String(out.toByteArray(), UTF_8));
+ assertEquals("", new String(err.toByteArray(), UTF_8));
}
@Test
@@ -99,8 +100,8 @@ public class RunExternalScriptTest {
new ProcessBuilder("sh",
script.getPath(), "a", "b", "c"), out, err, (InputStream) null);
assertEquals(0, rc);
- assertEquals("3,a,b,c,,,\n", new String(out.toByteArray()));
- assertEquals("", new String(err.toByteArray()));
+ assertEquals("3,a,b,c,,,\n", new String(out.toByteArray(), UTF_8));
+ assertEquals("", new String(err.toByteArray(), UTF_8));
}
@Test
@@ -110,8 +111,8 @@ public class RunExternalScriptTest {
new ProcessBuilder("sh", script.getPath(), "a", "b", "c"),
out, err, (InputStream) null);
assertEquals(3, rc);
- assertEquals("", new String(out.toByteArray()));
- assertEquals("", new String(err.toByteArray()));
+ assertEquals("", new String(out.toByteArray(), UTF_8));
+ assertEquals("", new String(err.toByteArray(), UTF_8));
}
@Test
@@ -121,8 +122,8 @@ public class RunExternalScriptTest {
new ProcessBuilder("sh", script.getPath()), null, err,
(InputStream) null);
assertEquals(0, rc);
- assertEquals("", new String(out.toByteArray()));
- assertEquals("", new String(err.toByteArray()));
+ assertEquals("", new String(out.toByteArray(), UTF_8));
+ assertEquals("", new String(err.toByteArray(), UTF_8));
}
@Test
@@ -132,8 +133,8 @@ public class RunExternalScriptTest {
new ProcessBuilder("sh", script.getPath()), null, err,
(InputStream) null);
assertEquals(0, rc);
- assertEquals("", new String(out.toByteArray()));
- assertEquals("hi" + LF, new String(err.toByteArray()));
+ assertEquals("", new String(out.toByteArray(), UTF_8));
+ assertEquals("hi" + LF, new String(err.toByteArray(), UTF_8));
}
@Test
@@ -142,10 +143,10 @@ public class RunExternalScriptTest {
File script = writeTempFile("echo $#,$1,$2,$3,$4,$5,$6 >&2 ; cat -; exit 5");
int rc = FS.DETECTED.runProcess(
new ProcessBuilder("sh", script.getPath(), "a", "b", "c"),
- out, err, new ByteArrayInputStream(inputStr.getBytes()));
+ out, err, new ByteArrayInputStream(inputStr.getBytes(UTF_8)));
assertEquals(5, rc);
- assertEquals(inputStr, new String(out.toByteArray()));
- assertEquals("3,a,b,c,,," + LF, new String(err.toByteArray()));
+ assertEquals(inputStr, new String(out.toByteArray(), UTF_8));
+ assertEquals("3,a,b,c,,," + LF, new String(err.toByteArray(), UTF_8));
}
@Test(expected = IOException.class)
@@ -172,10 +173,11 @@ public class RunExternalScriptTest {
File script = writeTempFile("cat -");
ProcessBuilder pb = new ProcessBuilder("sh", script.getPath());
ExecutionResult res = FS.DETECTED.execute(pb,
- new ByteArrayInputStream(inputStr.getBytes()));
+ new ByteArrayInputStream(inputStr.getBytes(UTF_8)));
assertEquals(0, res.getRc());
- assertEquals(inputStr, new String(res.getStdout().toByteArray()));
- assertEquals("", new String(res.getStderr().toByteArray()));
+ assertEquals(inputStr,
+ new String(res.getStdout().toByteArray(), UTF_8));
+ assertEquals("", new String(res.getStderr().toByteArray(), UTF_8));
}
@Test
@@ -184,8 +186,9 @@ public class RunExternalScriptTest {
ProcessBuilder pb = new ProcessBuilder("sh", script.getPath());
ExecutionResult res = FS.DETECTED.execute(pb, null);
assertEquals(0, res.getRc());
- assertEquals("", new String(res.getStdout().toByteArray()));
- assertEquals("hi" + LF, new String(res.getStderr().toByteArray()));
+ assertEquals("", new String(res.getStdout().toByteArray(), UTF_8));
+ assertEquals("hi" + LF,
+ new String(res.getStderr().toByteArray(), UTF_8));
}
@Test
@@ -197,11 +200,12 @@ public class RunExternalScriptTest {
ProcessBuilder pb = new ProcessBuilder("sh", script.getPath(), "a",
"b", "c");
ExecutionResult res = FS.DETECTED.execute(pb,
- new ByteArrayInputStream(inputStr.getBytes()));
+ new ByteArrayInputStream(inputStr.getBytes(UTF_8)));
assertEquals(5, res.getRc());
- assertEquals(inputStr, new String(res.getStdout().toByteArray()));
+ assertEquals(inputStr,
+ new String(res.getStdout().toByteArray(), UTF_8));
assertEquals("3,a,b,c,,," + LF,
- new String(res.getStderr().toByteArray()));
+ new String(res.getStderr().toByteArray(), UTF_8));
}
private File writeTempFile(String body) throws IOException {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/AutoCRLFInputStreamTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/AutoCRLFInputStreamTest.java
index 1272e16173..8f77c55af2 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/AutoCRLFInputStreamTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/AutoCRLFInputStreamTest.java
@@ -43,6 +43,8 @@
package org.eclipse.jgit.util.io;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -89,8 +91,8 @@ public class AutoCRLFInputStreamTest {
private void assertNoCrLfHelper(String expect, String input)
throws IOException {
- byte[] inbytes = input.getBytes();
- byte[] expectBytes = expect.getBytes();
+ byte[] inbytes = input.getBytes(UTF_8);
+ byte[] expectBytes = expect.getBytes(UTF_8);
for (int i = 0; i < 5; ++i) {
byte[] buf = new byte[i];
try (ByteArrayInputStream bis = new ByteArrayInputStream(inbytes);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/AutoCRLFOutputStreamTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/AutoCRLFOutputStreamTest.java
index 0655827310..3a3dc8117f 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/AutoCRLFOutputStreamTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/AutoCRLFOutputStreamTest.java
@@ -44,6 +44,8 @@
package org.eclipse.jgit.util.io;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -91,8 +93,8 @@ public class AutoCRLFOutputStreamTest {
private void assertNoCrLfHelper(String expect, String input)
throws IOException {
- byte[] inbytes = input.getBytes();
- byte[] expectBytes = expect.getBytes();
+ byte[] inbytes = input.getBytes(UTF_8);
+ byte[] expectBytes = expect.getBytes(UTF_8);
for (int i = -4; i < 5; ++i) {
int size = Math.abs(i);
byte[] buf = new byte[size];
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/TimeoutOutputStreamTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/TimeoutOutputStreamTest.java
index a63b1cb02a..c35f90ca7d 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/TimeoutOutputStreamTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/TimeoutOutputStreamTest.java
@@ -282,7 +282,7 @@ public class TimeoutOutputStreamTest {
return System.currentTimeMillis();
}
- private final class FullPipeInputStream extends PipedInputStream {
+ private static final class FullPipeInputStream extends PipedInputStream {
FullPipeInputStream(PipedOutputStream src) throws IOException {
super(src);
src.write(new byte[PIPE_SIZE]);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/UnionInputStreamTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/UnionInputStreamTest.java
index b824fae9fd..a6e0eedfbc 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/UnionInputStreamTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/UnionInputStreamTest.java
@@ -167,6 +167,8 @@ public class UnionInputStreamTest {
u.add(new ByteArrayInputStream(new byte[] { 20, 30 }) {
@Override
+ @SuppressWarnings("UnsynchronizedOverridesSynchronized")
+ // This is only used in tests and is thread-safe
public long skip(long n) {
return 0;
}
@@ -259,6 +261,11 @@ public class UnionInputStreamTest {
public int read() throws IOException {
throw new IOException("Expected");
}
+
+ @Override
+ public int read(byte b[], int off, int len) throws IOException {
+ throw new IOException("Expected");
+ }
};
@SuppressWarnings("resource" /* java 7 */)
final UnionInputStream u = new UnionInputStream(
diff --git a/org.eclipse.jgit.ui/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.ui/.settings/org.eclipse.jdt.core.prefs
index 89394eca41..525ac67142 100644
--- a/org.eclipse.jgit.ui/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.ui/.settings/org.eclipse.jdt.core.prefs
@@ -91,7 +91,7 @@ org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled
org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning
org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning
-org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=ignore
org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error
org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
diff --git a/org.eclipse.jgit.ui/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.ui/.settings/org.eclipse.mylyn.team.ui.prefs
index 0cba949fb7..2fca432276 100644
--- a/org.eclipse.jgit.ui/.settings/org.eclipse.mylyn.team.ui.prefs
+++ b/org.eclipse.jgit.ui/.settings/org.eclipse.mylyn.team.ui.prefs
@@ -1,3 +1,3 @@
#Tue Jul 19 20:11:28 CEST 2011
-commit.comment.template=${task.description} \n\nBug\: ${task.key}
+commit.comment.template=${task.description}\n\nBug\: ${task.key}
eclipse.preferences.version=1
diff --git a/org.eclipse.jgit.ui/META-INF/MANIFEST.MF b/org.eclipse.jgit.ui/META-INF/MANIFEST.MF
index 18d0bd55ac..380de32d25 100644
--- a/org.eclipse.jgit.ui/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ui/META-INF/MANIFEST.MF
@@ -4,14 +4,14 @@ Bundle-ManifestVersion: 2
Bundle-Name: %plugin_name
Automatic-Module-Name: org.eclipse.jgit.ui
Bundle-SymbolicName: org.eclipse.jgit.ui
-Bundle-Version: 5.1.9.qualifier
+Bundle-Version: 5.2.3.qualifier
Bundle-Vendor: %provider_name
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
-Export-Package: org.eclipse.jgit.awtui;version="5.1.9"
-Import-Package: org.eclipse.jgit.errors;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.lib;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.nls;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.revplot;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.revwalk;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.transport;version="[5.1.9,5.2.0)",
- org.eclipse.jgit.util;version="[5.1.9,5.2.0)"
+Export-Package: org.eclipse.jgit.awtui;version="5.2.3"
+Import-Package: org.eclipse.jgit.errors;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.lib;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.nls;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.revplot;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.revwalk;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.transport;version="[5.2.3,5.3.0)",
+ org.eclipse.jgit.util;version="[5.2.3,5.3.0)"
diff --git a/org.eclipse.jgit.ui/pom.xml b/org.eclipse.jgit.ui/pom.xml
index 0260d77ca2..0cb3d65c9d 100644
--- a/org.eclipse.jgit.ui/pom.xml
+++ b/org.eclipse.jgit.ui/pom.xml
@@ -52,7 +52,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>5.1.9-SNAPSHOT</version>
+ <version>5.2.3-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.ui</artifactId>
diff --git a/org.eclipse.jgit/.settings/.api_filters b/org.eclipse.jgit/.settings/.api_filters
index 524c591912..4131901d78 100644
--- a/org.eclipse.jgit/.settings/.api_filters
+++ b/org.eclipse.jgit/.settings/.api_filters
@@ -3,8 +3,8 @@
<resource path="META-INF/MANIFEST.MF">
<filter id="924844039">
<message_arguments>
- <message_argument value="5.1.8"/>
- <message_argument value="5.1.0"/>
+ <message_argument value="5.2.3"/>
+ <message_argument value="5.2.0"/>
</message_arguments>
</filter>
</resource>
@@ -22,22 +22,6 @@
</message_arguments>
</filter>
</resource>
- <resource path="src/org/eclipse/jgit/lib/GitmoduleEntry.java" type="org.eclipse.jgit.lib.GitmoduleEntry">
- <filter id="1109393411">
- <message_arguments>
- <message_argument value="4.7.5"/>
- <message_argument value="org.eclipse.jgit.lib.GitmoduleEntry"/>
- </message_arguments>
- </filter>
- </resource>
- <resource path="src/org/eclipse/jgit/lib/ObjectChecker.java" type="org.eclipse.jgit.lib.ObjectChecker">
- <filter id="1142947843">
- <message_arguments>
- <message_argument value="4.7.5"/>
- <message_argument value="getGitsubmodules()"/>
- </message_arguments>
- </filter>
- </resource>
<resource path="src/org/eclipse/jgit/storage/pack/PackConfig.java" type="org.eclipse.jgit.storage.pack.PackConfig">
<filter id="336658481">
<message_arguments>
diff --git a/org.eclipse.jgit/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit/.settings/org.eclipse.jdt.core.prefs
index 13c32a6d94..ef6f5e732f 100644
--- a/org.eclipse.jgit/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit/.settings/org.eclipse.jdt.core.prefs
@@ -91,7 +91,7 @@ org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled
org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning
org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning
-org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=ignore
org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error
org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
diff --git a/org.eclipse.jgit/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit/.settings/org.eclipse.mylyn.team.ui.prefs
index 0cba949fb7..2fca432276 100644
--- a/org.eclipse.jgit/.settings/org.eclipse.mylyn.team.ui.prefs
+++ b/org.eclipse.jgit/.settings/org.eclipse.mylyn.team.ui.prefs
@@ -1,3 +1,3 @@
#Tue Jul 19 20:11:28 CEST 2011
-commit.comment.template=${task.description} \n\nBug\: ${task.key}
+commit.comment.template=${task.description}\n\nBug\: ${task.key}
eclipse.preferences.version=1
diff --git a/org.eclipse.jgit/META-INF/MANIFEST.MF b/org.eclipse.jgit/META-INF/MANIFEST.MF
index 470871c503..b03ff4ac94 100644
--- a/org.eclipse.jgit/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit/META-INF/MANIFEST.MF
@@ -3,12 +3,12 @@ Bundle-ManifestVersion: 2
Bundle-Name: %plugin_name
Automatic-Module-Name: org.eclipse.jgit
Bundle-SymbolicName: org.eclipse.jgit
-Bundle-Version: 5.1.9.qualifier
+Bundle-Version: 5.2.3.qualifier
Bundle-Localization: plugin
Bundle-Vendor: %provider_name
Bundle-ActivationPolicy: lazy
-Export-Package: org.eclipse.jgit.annotations;version="5.1.9",
- org.eclipse.jgit.api;version="5.1.9";
+Export-Package: org.eclipse.jgit.annotations;version="5.2.3",
+ org.eclipse.jgit.api;version="5.2.3";
uses:="org.eclipse.jgit.revwalk,
org.eclipse.jgit.treewalk.filter,
org.eclipse.jgit.diff,
@@ -22,65 +22,73 @@ Export-Package: org.eclipse.jgit.annotations;version="5.1.9",
org.eclipse.jgit.submodule,
org.eclipse.jgit.transport,
org.eclipse.jgit.merge",
- org.eclipse.jgit.api.errors;version="5.1.9";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.errors",
- org.eclipse.jgit.attributes;version="5.1.9",
- org.eclipse.jgit.blame;version="5.1.9";
+ org.eclipse.jgit.api.errors;version="5.2.3";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.errors",
+ org.eclipse.jgit.attributes;version="5.2.3",
+ org.eclipse.jgit.blame;version="5.2.3";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.revwalk,
org.eclipse.jgit.treewalk.filter,
org.eclipse.jgit.diff",
- org.eclipse.jgit.diff;version="5.1.9";
+ org.eclipse.jgit.diff;version="5.2.3";
uses:="org.eclipse.jgit.patch,
org.eclipse.jgit.lib,
org.eclipse.jgit.treewalk,
org.eclipse.jgit.revwalk,
org.eclipse.jgit.treewalk.filter,
org.eclipse.jgit.util",
- org.eclipse.jgit.dircache;version="5.1.9";
+ org.eclipse.jgit.dircache;version="5.2.3";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.treewalk,
org.eclipse.jgit.util,
org.eclipse.jgit.events,
org.eclipse.jgit.attributes",
- org.eclipse.jgit.errors;version="5.1.9";
+ org.eclipse.jgit.errors;version="5.2.3";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.internal.storage.pack,
org.eclipse.jgit.transport,
org.eclipse.jgit.dircache",
- org.eclipse.jgit.events;version="5.1.9";uses:="org.eclipse.jgit.lib",
- org.eclipse.jgit.fnmatch;version="5.1.9",
- org.eclipse.jgit.gitrepo;version="5.1.9";
+ org.eclipse.jgit.events;version="5.2.3";uses:="org.eclipse.jgit.lib",
+ org.eclipse.jgit.fnmatch;version="5.2.3",
+ org.eclipse.jgit.gitrepo;version="5.2.3";
uses:="org.eclipse.jgit.api,
org.eclipse.jgit.lib,
org.eclipse.jgit.revwalk,
org.xml.sax.helpers,
org.xml.sax",
- org.eclipse.jgit.gitrepo.internal;version="5.1.9";x-internal:=true,
- org.eclipse.jgit.hooks;version="5.1.9";uses:="org.eclipse.jgit.lib",
- org.eclipse.jgit.ignore;version="5.1.9",
- org.eclipse.jgit.ignore.internal;version="5.1.9";x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal;version="5.1.9";x-friends:="org.eclipse.jgit.test,org.eclipse.jgit.http.test",
- org.eclipse.jgit.internal.fsck;version="5.1.9";x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal.ketch;version="5.1.9";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
- org.eclipse.jgit.internal.storage.dfs;version="5.1.9";
+ org.eclipse.jgit.gitrepo.internal;version="5.2.3";x-internal:=true,
+ org.eclipse.jgit.hooks;version="5.2.3";uses:="org.eclipse.jgit.lib",
+ org.eclipse.jgit.ignore;version="5.2.3",
+ org.eclipse.jgit.ignore.internal;version="5.2.3";x-friends:="org.eclipse.jgit.test",
+ org.eclipse.jgit.internal;version="5.2.3";x-friends:="org.eclipse.jgit.test,org.eclipse.jgit.http.test",
+ org.eclipse.jgit.internal.fsck;version="5.2.3";x-friends:="org.eclipse.jgit.test",
+ org.eclipse.jgit.internal.ketch;version="5.2.3";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
+ org.eclipse.jgit.internal.revwalk;version="5.2.3";x-internal:=true,
+ org.eclipse.jgit.internal.storage.dfs;version="5.2.3";
x-friends:="org.eclipse.jgit.test,
org.eclipse.jgit.http.server,
org.eclipse.jgit.http.test,
org.eclipse.jgit.lfs.test",
- org.eclipse.jgit.internal.storage.file;version="5.1.9";
+ org.eclipse.jgit.internal.storage.file;version="5.2.3";
x-friends:="org.eclipse.jgit.test,
org.eclipse.jgit.junit,
org.eclipse.jgit.junit.http,
org.eclipse.jgit.http.server,
org.eclipse.jgit.lfs,
org.eclipse.jgit.pgm,
- org.eclipse.jgit.pgm.test",
- org.eclipse.jgit.internal.storage.io;version="5.1.9";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
- org.eclipse.jgit.internal.storage.pack;version="5.1.9";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
- org.eclipse.jgit.internal.storage.reftable;version="5.1.9";
- x-friends:="org.eclipse.jgit.http.test,org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
- org.eclipse.jgit.internal.storage.reftree;version="5.1.9";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
- org.eclipse.jgit.lib;version="5.1.9";
+ org.eclipse.jgit.pgm.test,
+ org.eclipse.jgit.ssh.apache",
+ org.eclipse.jgit.internal.storage.io;version="5.2.3";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
+ org.eclipse.jgit.internal.storage.pack;version="5.2.3";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
+ org.eclipse.jgit.internal.storage.reftable;version="5.2.3";
+ x-friends:="org.eclipse.jgit.http.test,
+ org.eclipse.jgit.junit,
+ org.eclipse.jgit.test,
+ org.eclipse.jgit.pgm",
+ org.eclipse.jgit.internal.storage.reftree;version="5.2.3";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
+ org.eclipse.jgit.internal.submodule;version="5.2.3";x-internal:=true,
+ org.eclipse.jgit.internal.transport.parser;version="5.2.3";x-friends:="org.eclipse.jgit.test",
+ org.eclipse.jgit.internal.transport.ssh;version="5.2.3";x-friends:="org.eclipse.jgit.ssh.apache",
+ org.eclipse.jgit.lib;version="5.2.3";
uses:="org.eclipse.jgit.revwalk,
org.eclipse.jgit.treewalk.filter,
org.eclipse.jgit.util,
@@ -90,33 +98,33 @@ Export-Package: org.eclipse.jgit.annotations;version="5.1.9",
org.eclipse.jgit.treewalk,
org.eclipse.jgit.transport,
org.eclipse.jgit.submodule",
- org.eclipse.jgit.lib.internal;version="5.1.9";x-internal:=true,
- org.eclipse.jgit.merge;version="5.1.9";
+ org.eclipse.jgit.lib.internal;version="5.2.3";x-internal:=true,
+ org.eclipse.jgit.merge;version="5.2.3";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.treewalk,
org.eclipse.jgit.revwalk,
org.eclipse.jgit.diff,
org.eclipse.jgit.dircache,
org.eclipse.jgit.api",
- org.eclipse.jgit.nls;version="5.1.9",
- org.eclipse.jgit.notes;version="5.1.9";
+ org.eclipse.jgit.nls;version="5.2.3",
+ org.eclipse.jgit.notes;version="5.2.3";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.treewalk,
org.eclipse.jgit.revwalk,
org.eclipse.jgit.merge",
- org.eclipse.jgit.patch;version="5.1.9";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.diff",
- org.eclipse.jgit.revplot;version="5.1.9";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.revwalk",
- org.eclipse.jgit.revwalk;version="5.1.9";
+ org.eclipse.jgit.patch;version="5.2.3";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.diff",
+ org.eclipse.jgit.revplot;version="5.2.3";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.revwalk",
+ org.eclipse.jgit.revwalk;version="5.2.3";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.treewalk,
org.eclipse.jgit.treewalk.filter,
org.eclipse.jgit.diff,
org.eclipse.jgit.revwalk.filter",
- org.eclipse.jgit.revwalk.filter;version="5.1.9";uses:="org.eclipse.jgit.revwalk,org.eclipse.jgit.lib,org.eclipse.jgit.util",
- org.eclipse.jgit.storage.file;version="5.1.9";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.util",
- org.eclipse.jgit.storage.pack;version="5.1.9";uses:="org.eclipse.jgit.lib",
- org.eclipse.jgit.submodule;version="5.1.9";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.treewalk.filter,org.eclipse.jgit.treewalk",
- org.eclipse.jgit.transport;version="5.1.9";
+ org.eclipse.jgit.revwalk.filter;version="5.2.3";uses:="org.eclipse.jgit.revwalk,org.eclipse.jgit.lib,org.eclipse.jgit.util",
+ org.eclipse.jgit.storage.file;version="5.2.3";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.util",
+ org.eclipse.jgit.storage.pack;version="5.2.3";uses:="org.eclipse.jgit.lib",
+ org.eclipse.jgit.submodule;version="5.2.3";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.treewalk.filter,org.eclipse.jgit.treewalk",
+ org.eclipse.jgit.transport;version="5.2.3";
uses:="org.eclipse.jgit.transport.resolver,
org.eclipse.jgit.revwalk,
org.eclipse.jgit.internal.storage.pack,
@@ -128,24 +136,24 @@ Export-Package: org.eclipse.jgit.annotations;version="5.1.9",
org.eclipse.jgit.transport.http,
org.eclipse.jgit.errors,
org.eclipse.jgit.storage.pack",
- org.eclipse.jgit.transport.http;version="5.1.9";uses:="javax.net.ssl",
- org.eclipse.jgit.transport.resolver;version="5.1.9";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.transport",
- org.eclipse.jgit.treewalk;version="5.1.9";
+ org.eclipse.jgit.transport.http;version="5.2.3";uses:="javax.net.ssl",
+ org.eclipse.jgit.transport.resolver;version="5.2.3";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.transport",
+ org.eclipse.jgit.treewalk;version="5.2.3";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.revwalk,
org.eclipse.jgit.attributes,
org.eclipse.jgit.treewalk.filter,
org.eclipse.jgit.util,
org.eclipse.jgit.dircache",
- org.eclipse.jgit.treewalk.filter;version="5.1.9";uses:="org.eclipse.jgit.treewalk",
- org.eclipse.jgit.util;version="5.1.9";
+ org.eclipse.jgit.treewalk.filter;version="5.2.3";uses:="org.eclipse.jgit.treewalk",
+ org.eclipse.jgit.util;version="5.2.3";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.transport.http,
org.eclipse.jgit.storage.file,
org.ietf.jgss",
- org.eclipse.jgit.util.io;version="5.1.9",
- org.eclipse.jgit.util.sha1;version="5.1.9",
- org.eclipse.jgit.util.time;version="5.1.9"
+ org.eclipse.jgit.util.io;version="5.2.3",
+ org.eclipse.jgit.util.sha1;version="5.2.3",
+ org.eclipse.jgit.util.time;version="5.2.3"
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Import-Package: com.googlecode.javaewah;version="[1.1.6,2.0.0)",
com.jcraft.jsch;version="[0.1.37,0.2.0)",
diff --git a/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF
index 8885e64cb9..8e22086cce 100644
--- a/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@ Bundle-ManifestVersion: 2
Bundle-Name: org.eclipse.jgit - Sources
Bundle-SymbolicName: org.eclipse.jgit.source
Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 5.1.9.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit;version="5.1.9.qualifier";roots="."
+Bundle-Version: 5.2.3.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit;version="5.2.3.qualifier";roots="."
diff --git a/org.eclipse.jgit/pom.xml b/org.eclipse.jgit/pom.xml
index eedb5e80d7..c74e26f6d4 100644
--- a/org.eclipse.jgit/pom.xml
+++ b/org.eclipse.jgit/pom.xml
@@ -53,7 +53,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>5.1.9-SNAPSHOT</version>
+ <version>5.2.3-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit</artifactId>
diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
index b7805b19e6..12fded8361 100644
--- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
+++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
@@ -302,7 +302,7 @@ expectedPktLineWithService=expected pkt-line with ''# service=-'', got ''{0}''
expectedReceivedContentType=expected Content-Type {0}; received Content-Type {1}
expectedReportForRefNotReceived={0}: expected report for ref {1} not received
failedAtomicFileCreation=Atomic file creation failed, number of hard links to file {0} was not 2 but {1}"
-failedToDetermineFilterDefinition=An exception occured while determining filter definitions
+failedToDetermineFilterDefinition=An exception occurred while determining filter definitions
failedUpdatingRefs=failed updating refs
failureDueToOneOfTheFollowing=Failure due to one of the following:
failureUpdatingFETCH_HEAD=Failure updating FETCH_HEAD: {0}
@@ -468,6 +468,7 @@ newIdMustNotBeNull=New ID must not be null
newlineInQuotesNotAllowed=Newline in quotes not allowed
noApplyInDelete=No apply in delete
noClosingBracket=No closing {0} found for {1} at index {2}.
+noCommitsSelectedForShallow=No commits selected for shallow request
noCredentialsProvider=Authentication is required but no CredentialsProvider has been registered
noHEADExistsAndNoExplicitStartingRevisionWasSpecified=No HEAD exists and no explicit starting revision was specified
noHMACsupport=No {0} support: {1}
@@ -638,8 +639,8 @@ sourceRefNotSpecifiedForRefspec=Source ref not specified for refspec: {0}
squashCommitNotUpdatingHEAD=Squash commit -- not updating HEAD
sshCommandFailed=Execution of ssh command ''{0}'' failed with error ''{1}''
sshUserNameError=Jsch error: failed to set SSH user name correctly to ''{0}''; using ''{1}'' picked up from SSH config file.
-sslFailureExceptionMessage=Secure connection to {0} could not be stablished because of SSL problems
-sslFailureInfo=A secure connection to {0}\ncould not be established because the server''s certificate could not be validated.
+sslFailureExceptionMessage=Secure connection to {0} could not be established because of SSL problems
+sslFailureInfo=A secure connection to {0} could not be established because the server''s certificate could not be validated.
sslFailureCause=SSL reported: {0}
sslFailureTrustExplanation=Do you want to skip SSL verification for this server?
sslTrustAlways=Always skip SSL verification for this server from now on
@@ -671,7 +672,6 @@ submodulePathInvalid=Invalid submodule path ''{0}''
submoduleUrlInvalid=Invalid submodule URL ''{0}''
submodulesNotSupported=Submodules are not supported
supportOnlyPackIndexVersion2=Only support index version 2
-symlinkCannotBeWrittenAsTheLinkTarget=Symlink "{0}" cannot be written as the link target cannot be read from within Java.
systemConfigFileInvalid=System wide config file {0} is invalid {1}
tagAlreadyExists=tag ''{0}'' already exists
tagNameInvalid=tag name {0} is invalid
@@ -771,7 +771,9 @@ uriNotFound={0} not found
uriNotFoundWithMessage={0} not found: {1}
URINotSupported=URI not supported: {0}
userConfigFileInvalid=User config file {0} invalid {1}
+validatingGitModules=Validating .gitmodules files
walkFailure=Walk failure.
+wantNoSpaceWithCapabilities=No space between oid and first capability in first want line
wantNotValid=want {0} not valid
weeksAgo={0} weeks ago
windowSizeMustBeLesserThanLimit=Window size must be < limit
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/ApplyCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/ApplyCommand.java
index 5b84032b15..c6f3c671a9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/ApplyCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/ApplyCommand.java
@@ -42,11 +42,14 @@
*/
package org.eclipse.jgit.api;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
import java.io.File;
import java.io.FileOutputStream;
-import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
import java.nio.file.StandardCopyOption;
import java.text.MessageFormat;
import java.util.ArrayList;
@@ -258,7 +261,8 @@ public class ApplyCommand extends GitCommand<ApplyResult> {
if (sb.length() > 0) {
sb.deleteCharAt(sb.length() - 1);
}
- try (FileWriter fw = new FileWriter(f)) {
+ try (Writer fw = new OutputStreamWriter(new FileOutputStream(f),
+ UTF_8)) {
fw.write(sb.toString());
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java
index 11edb10894..455a2e665f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java
@@ -269,7 +269,7 @@ public class CheckoutCommand extends GitCommand<Ref> {
try {
dco = new DirCacheCheckout(repo, headTree, dc,
newCommit.getTree());
- dco.setFailOnConflict(true);
+ dco.setFailOnConflict(!force);
dco.setProgressMonitor(monitor);
try {
dco.checkout();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java
index 5c06bac1f2..73af8ba16d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java
@@ -283,12 +283,11 @@ public class CloneCommand extends TransportCommand<CloneCommand, Git> {
config.addURI(u);
final String dst = (bare ? Constants.R_HEADS : Constants.R_REMOTES
- + config.getName() + "/") + "*"; //$NON-NLS-1$//$NON-NLS-2$
- RefSpec refSpec = new RefSpec();
- refSpec = refSpec.setForceUpdate(true);
- refSpec = refSpec.setSourceDestination(Constants.R_HEADS + "*", dst); //$NON-NLS-1$
+ + config.getName() + '/') + '*';
+ boolean fetchAll = cloneAllBranches || branchesToClone == null
+ || branchesToClone.isEmpty();
- config.addFetchRefSpec(refSpec);
+ config.setFetchRefSpecs(calculateRefSpecs(fetchAll, dst));
config.update(clonedRepo.getConfig());
clonedRepo.getConfig().save();
@@ -297,27 +296,25 @@ public class CloneCommand extends TransportCommand<CloneCommand, Git> {
FetchCommand command = new FetchCommand(clonedRepo);
command.setRemote(remote);
command.setProgressMonitor(monitor);
- command.setTagOpt(TagOpt.FETCH_TAGS);
+ command.setTagOpt(fetchAll ? TagOpt.FETCH_TAGS : TagOpt.AUTO_FOLLOW);
configure(command);
- List<RefSpec> specs = calculateRefSpecs(dst);
- command.setRefSpecs(specs);
-
return command.call();
}
- private List<RefSpec> calculateRefSpecs(String dst) {
+ private List<RefSpec> calculateRefSpecs(boolean fetchAll, String dst) {
RefSpec wcrs = new RefSpec();
wcrs = wcrs.setForceUpdate(true);
- wcrs = wcrs.setSourceDestination(Constants.R_HEADS + "*", dst); //$NON-NLS-1$
+ wcrs = wcrs.setSourceDestination(Constants.R_HEADS + '*', dst);
List<RefSpec> specs = new ArrayList<>();
- if (cloneAllBranches)
- specs.add(wcrs);
- else if (branchesToClone != null
- && branchesToClone.size() > 0) {
- for (String selectedRef : branchesToClone)
- if (wcrs.matchSource(selectedRef))
+ if (!fetchAll) {
+ for (String selectedRef : branchesToClone) {
+ if (wcrs.matchSource(selectedRef)) {
specs.add(wcrs.expandFromSource(selectedRef));
+ }
+ }
+ } else {
+ specs.add(wcrs);
}
return specs;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/ListBranchCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/ListBranchCommand.java
index 28a27a90e0..29a51a0f02 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/ListBranchCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/ListBranchCommand.java
@@ -44,6 +44,10 @@
*/
package org.eclipse.jgit.api;
+import static org.eclipse.jgit.lib.Constants.HEAD;
+import static org.eclipse.jgit.lib.Constants.R_HEADS;
+import static org.eclipse.jgit.lib.Constants.R_REMOTES;
+
import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
@@ -56,7 +60,6 @@ import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.api.errors.RefNotFoundException;
import org.eclipse.jgit.internal.JGitText;
-import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
@@ -113,17 +116,18 @@ public class ListBranchCommand extends GitCommand<List<Ref>> {
Collection<Ref> refs = new ArrayList<>();
// Also return HEAD if it's detached
- Ref head = repo.exactRef(Constants.HEAD);
- if (head != null && head.getLeaf().getName().equals(Constants.HEAD))
+ Ref head = repo.exactRef(HEAD);
+ if (head != null && head.getLeaf().getName().equals(HEAD)) {
refs.add(head);
+ }
if (listMode == null) {
- refs.addAll(getRefs(Constants.R_HEADS));
+ refs.addAll(repo.getRefDatabase().getRefsByPrefix(R_HEADS));
} else if (listMode == ListMode.REMOTE) {
- refs.addAll(getRefs(Constants.R_REMOTES));
+ refs.addAll(repo.getRefDatabase().getRefsByPrefix(R_REMOTES));
} else {
- refs.addAll(getRefs(Constants.R_HEADS));
- refs.addAll(getRefs(Constants.R_REMOTES));
+ refs.addAll(repo.getRefDatabase().getRefsByPrefix(R_HEADS,
+ R_REMOTES));
}
resultRefs = new ArrayList<>(filterRefs(refs));
} catch (IOException e) {
@@ -185,8 +189,4 @@ public class ListBranchCommand extends GitCommand<List<Ref>> {
this.containsCommitish = containsCommitish;
return this;
}
-
- private Collection<Ref> getRefs(String prefix) throws IOException {
- return repo.getRefDatabase().getRefsByPrefix(prefix);
- }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java
index 1783c4193e..9653c365b2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java
@@ -43,6 +43,8 @@
*/
package org.eclipse.jgit.api;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
@@ -1015,8 +1017,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
df.setRepository(repo);
df.format(commitToPick.getParent(0), commitToPick);
}
- rebaseState.createFile(PATCH, new String(bos.toByteArray(),
- Constants.CHARACTER_ENCODING));
+ rebaseState.createFile(PATCH, new String(bos.toByteArray(), UTF_8));
rebaseState.createFile(STOPPED_SHA,
repo.newObjectReader()
.abbreviate(
@@ -1733,7 +1734,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
throws IOException {
File file = new File(parentDir, name);
try (FileOutputStream fos = new FileOutputStream(file)) {
- fos.write(content.getBytes(Constants.CHARACTER_ENCODING));
+ fos.write(content.getBytes(UTF_8));
fos.write('\n');
}
}
@@ -1741,7 +1742,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
private static void appendToFile(File file, String content)
throws IOException {
try (FileOutputStream fos = new FileOutputStream(file, true)) {
- fos.write(content.getBytes(Constants.CHARACTER_ENCODING));
+ fos.write(content.getBytes(UTF_8));
fos.write('\n');
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleAddCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleAddCommand.java
index 244a15686f..f92455a96a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleAddCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleAddCommand.java
@@ -179,21 +179,6 @@ public class SubmoduleAddCommand extends
// Use the path as the default.
name = path;
}
- if (name.contains("/../") || name.contains("\\..\\") //$NON-NLS-1$ //$NON-NLS-2$
- || name.startsWith("../") || name.startsWith("..\\") //$NON-NLS-1$ //$NON-NLS-2$
- || name.endsWith("/..") || name.endsWith("\\..")) { //$NON-NLS-1$ //$NON-NLS-2$
- // Submodule names are used to store the submodule repositories
- // under $GIT_DIR/modules. Having ".." in submodule names makes a
- // vulnerability (CVE-2018-11235
- // https://bugs.eclipse.org/bugs/show_bug.cgi?id=535027#c0)
- // Reject the names with them. The callers need to make sure the
- // names free from these. We don't automatically replace these
- // characters or canonicalize by regarding the name as a file path.
- // Since Path class is platform dependent, we manually check '/' and
- // '\\' patterns here.
- throw new IllegalArgumentException(MessageFormat
- .format(JGitText.get().invalidNameContainsDotDot, name));
- }
try {
SubmoduleValidator.assertValidSubmoduleName(name);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/TransportConfigCallback.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/TransportConfigCallback.java
index f60926c562..d73453c5af 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/TransportConfigCallback.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/TransportConfigCallback.java
@@ -68,5 +68,5 @@ public interface TransportConfigCallback {
* @param transport
* a {@link org.eclipse.jgit.transport.Transport} object.
*/
- public void configure(Transport transport);
+ void configure(Transport transport);
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNodeProvider.java b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNodeProvider.java
index f1d7d7be0e..2d1cde12e0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNodeProvider.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNodeProvider.java
@@ -63,7 +63,7 @@ public interface AttributesNodeProvider {
* @throws java.io.IOException
* if an error is raised while parsing the attributes file
*/
- public AttributesNode getInfoAttributesNode() throws IOException;
+ AttributesNode getInfoAttributesNode() throws IOException;
/**
* Retrieve the {@link org.eclipse.jgit.attributes.AttributesNode} that
@@ -76,6 +76,6 @@ public interface AttributesNodeProvider {
* attributes file
* @see CoreConfig#getAttributesFile()
*/
- public AttributesNode getGlobalAttributesNode() throws IOException;
+ AttributesNode getGlobalAttributesNode() throws IOException;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesProvider.java b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesProvider.java
index 1545e3523d..7b51f6dd32 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesProvider.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesProvider.java
@@ -53,5 +53,5 @@ public interface AttributesProvider {
*
* @return the currently active attributes
*/
- public Attributes getAttributes();
+ Attributes getAttributes();
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/FilterCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/FilterCommand.java
index c4357d1297..0bb4516297 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/FilterCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/FilterCommand.java
@@ -95,7 +95,7 @@ public abstract class FilterCommand {
* -1. -1 means that the {@link java.io.InputStream} is completely
* processed.
* @throws java.io.IOException
- * when {@link java.io.IOException} occured while reading from
+ * when {@link java.io.IOException} occurred while reading from
* {@link #in} or writing to {@link #out}
*/
public abstract int run() throws IOException;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/FilterCommandFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/FilterCommandFactory.java
index 11b76b0d90..78573d2a63 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/FilterCommandFactory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/FilterCommandFactory.java
@@ -69,7 +69,7 @@ public interface FilterCommandFactory {
* thrown when the command constructor throws an
* java.io.IOException
*/
- public FilterCommand create(Repository db, InputStream in,
- OutputStream out) throws IOException;
+ FilterCommand create(Repository db, InputStream in, OutputStream out)
+ throws IOException;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
index ca1e3ab275..db6073f89a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
@@ -522,7 +522,7 @@ public class DirCacheCheckout {
builder.finish();
// init progress reporting
- int numTotal = removed.size() + updated.size();
+ int numTotal = removed.size() + updated.size() + conflicts.size();
monitor.beginTask(JGitText.get().checkingOutFiles, numTotal);
performingCheckout = true;
@@ -597,6 +597,33 @@ public class DirCacheCheckout {
}
throw ex;
}
+ for (String conflict : conflicts) {
+ // the conflicts are likely to have multiple entries in the
+ // dircache, we only want to check out the one for the "theirs"
+ // tree
+ int entryIdx = dc.findEntry(conflict);
+ if (entryIdx >= 0) {
+ while (entryIdx < dc.getEntryCount()) {
+ DirCacheEntry entry = dc.getEntry(entryIdx);
+ if (!entry.getPathString().equals(conflict)) {
+ break;
+ }
+ if (entry.getStage() == DirCacheEntry.STAGE_3) {
+ checkoutEntry(repo, entry, objectReader, false,
+ null);
+ break;
+ }
+ ++entryIdx;
+ }
+ }
+
+ monitor.update(1);
+ if (monitor.isCancelled()) {
+ throw new CanceledException(MessageFormat.format(
+ JGitText.get().operationCanceled,
+ JGitText.get().checkingOutFiles));
+ }
+ }
monitor.endTask();
// commit the index builder - a new index is persisted
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheIterator.java
index 19c916f810..6196e758a9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheIterator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheIterator.java
@@ -44,6 +44,8 @@
package org.eclipse.jgit.dircache;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
@@ -75,7 +77,7 @@ import org.eclipse.jgit.util.RawParseUtils;
public class DirCacheIterator extends AbstractTreeIterator {
/** Byte array holding ".gitattributes" string */
private static final byte[] DOT_GIT_ATTRIBUTES_BYTES = Constants.DOT_GIT_ATTRIBUTES
- .getBytes();
+ .getBytes(UTF_8);
/** The cache this iterator was created to walk. */
protected final DirCache cache;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/events/WorkingTreeModifiedEvent.java b/org.eclipse.jgit/src/org/eclipse/jgit/events/WorkingTreeModifiedEvent.java
index 9fbcc4dd50..afd7889f8a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/events/WorkingTreeModifiedEvent.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/events/WorkingTreeModifiedEvent.java
@@ -94,7 +94,8 @@ public class WorkingTreeModifiedEvent
*
* @return the set
*/
- public @NonNull Collection<String> getModified() {
+ @NonNull
+ public Collection<String> getModified() {
Collection<String> result = modified;
if (result == null) {
result = Collections.emptyList();
@@ -109,7 +110,8 @@ public class WorkingTreeModifiedEvent
*
* @return the set
*/
- public @NonNull Collection<String> getDeleted() {
+ @NonNull
+ public Collection<String> getDeleted() {
Collection<String> result = deleted;
if (result == null) {
result = Collections.emptyList();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/Head.java b/org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/Head.java
index 49839f8e6e..e8f6844dda 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/Head.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/Head.java
@@ -54,5 +54,5 @@ interface Head {
* the character which decides which heads are returned.
* @return a list of heads based on the input.
*/
- public abstract List<Head> getNextHeads(char c);
+ List<Head> getNextHeads(char c);
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/ManifestParser.java b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/ManifestParser.java
index 26e783ddd7..8e463415b8 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/ManifestParser.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/ManifestParser.java
@@ -358,7 +358,8 @@ public class ManifestParser extends DefaultHandler {
*
* @return filtered projects list reference, never null
*/
- public @NonNull List<RepoProject> getFilteredProjects() {
+ @NonNull
+ public List<RepoProject> getFilteredProjects() {
return filteredProjects;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java
index 45a239da0e..e9d86dfa83 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java
@@ -42,6 +42,7 @@
*/
package org.eclipse.jgit.gitrepo;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static org.eclipse.jgit.lib.Constants.DEFAULT_REMOTE_NAME;
import static org.eclipse.jgit.lib.Constants.R_REMOTES;
@@ -58,12 +59,14 @@ import java.util.Objects;
import java.util.StringJoiner;
import java.util.TreeMap;
+import org.eclipse.jgit.annotations.NonNull;
import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.GitCommand;
import org.eclipse.jgit.api.SubmoduleAddCommand;
import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException;
import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.api.errors.InvalidRefNameException;
import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.dircache.DirCacheBuilder;
@@ -79,7 +82,6 @@ import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
-import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.Ref;
@@ -89,6 +91,7 @@ import org.eclipse.jgit.lib.RefUpdate.Result;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.util.FileUtils;
/**
@@ -143,7 +146,9 @@ public class RepoCommand extends GitCommand<RevCommit> {
* @param uri
* The URI of the remote repository
* @param ref
- * The ref (branch/tag/etc.) to read
+ * Name of the ref to lookup. May be a short-hand form, e.g.
+ * "master" which is is automatically expanded to
+ * "refs/heads/master" if "refs/heads/master" already exists.
* @return the sha1 of the remote repository, or null if the ref does
* not exist.
* @throws GitAPIException
@@ -164,13 +169,93 @@ public class RepoCommand extends GitCommand<RevCommit> {
* @throws GitAPIException
* @throws IOException
* @since 3.5
+ *
+ * @deprecated Use {@link #readFileWithMode(String, String, String)}
+ * instead
+ */
+ @Deprecated
+ public default byte[] readFile(String uri, String ref, String path)
+ throws GitAPIException, IOException {
+ return readFileWithMode(uri, ref, path).getContents();
+ }
+
+ /**
+ * Read contents and mode (i.e. permissions) of the file from a remote
+ * repository.
+ *
+ * @param uri
+ * The URI of the remote repository
+ * @param ref
+ * Name of the ref to lookup. May be a short-hand form, e.g.
+ * "master" which is is automatically expanded to
+ * "refs/heads/master" if "refs/heads/master" already exists.
+ * @param path
+ * The relative path (inside the repo) to the file to read
+ * @return The contents and file mode of the file in the given
+ * repository and branch. Never null.
+ * @throws GitAPIException
+ * If the ref have an invalid or ambiguous name, or it does
+ * not exist in the repository,
+ * @throws IOException
+ * If the object does not exist or is too large
+ * @since 5.2
*/
- public byte[] readFile(String uri, String ref, String path)
+ @NonNull
+ public RemoteFile readFileWithMode(String uri, String ref, String path)
throws GitAPIException, IOException;
}
+ /**
+ * Read-only view of contents and file mode (i.e. permissions) for a file in
+ * a remote repository.
+ *
+ * @since 5.2
+ */
+ public static final class RemoteFile {
+ @NonNull
+ private final byte[] contents;
+
+ @NonNull
+ private final FileMode fileMode;
+
+ /**
+ * @param contents
+ * Raw contents of the file.
+ * @param fileMode
+ * Git file mode for this file (e.g. executable or regular)
+ */
+ public RemoteFile(@NonNull byte[] contents,
+ @NonNull FileMode fileMode) {
+ this.contents = Objects.requireNonNull(contents);
+ this.fileMode = Objects.requireNonNull(fileMode);
+ }
+
+ /**
+ * Contents of the file.
+ * <p>
+ * Callers who receive this reference must not modify its contents (as
+ * it can point to internal cached data).
+ *
+ * @return Raw contents of the file. Do not modify it.
+ */
+ @NonNull
+ public byte[] getContents() {
+ return contents;
+ }
+
+ /**
+ * @return Git file mode for this file (e.g. executable or regular)
+ */
+ @NonNull
+ public FileMode getFileMode() {
+ return fileMode;
+ }
+
+ }
+
/** A default implementation of {@link RemoteReader} callback. */
public static class DefaultRemoteReader implements RemoteReader {
+
@Override
public ObjectId sha1(String uri, String ref) throws GitAPIException {
Map<String, Ref> map = Git
@@ -182,38 +267,30 @@ public class RepoCommand extends GitCommand<RevCommit> {
}
@Override
- public byte[] readFile(String uri, String ref, String path)
+ public RemoteFile readFileWithMode(String uri, String ref, String path)
throws GitAPIException, IOException {
File dir = FileUtils.createTempDir("jgit_", ".git", null); //$NON-NLS-1$ //$NON-NLS-2$
try (Git git = Git.cloneRepository().setBare(true).setDirectory(dir)
.setURI(uri).call()) {
- return readFileFromRepo(git.getRepository(), ref, path);
+ Repository repo = git.getRepository();
+ ObjectId refCommitId = sha1(uri, ref);
+ if (refCommitId == null) {
+ throw new InvalidRefNameException(MessageFormat
+ .format(JGitText.get().refNotResolved, ref));
+ }
+ RevCommit commit = repo.parseCommit(refCommitId);
+ TreeWalk tw = TreeWalk.forPath(repo, path, commit.getTree());
+
+ // TODO(ifrade): Cope better with big files (e.g. using
+ // InputStream instead of byte[])
+ return new RemoteFile(
+ tw.getObjectReader().open(tw.getObjectId(0))
+ .getCachedBytes(Integer.MAX_VALUE),
+ tw.getFileMode(0));
} finally {
FileUtils.delete(dir, FileUtils.RECURSIVE);
}
}
-
- /**
- * Read a file from the repository
- *
- * @param repo
- * The repository containing the file
- * @param ref
- * The ref (branch/tag/etc.) to read
- * @param path
- * The relative path (inside the repo) to the file to read
- * @return the file's content
- * @throws GitAPIException
- * @throws IOException
- * @since 3.5
- */
- protected byte[] readFileFromRepo(Repository repo,
- String ref, String path) throws GitAPIException, IOException {
- try (ObjectReader reader = repo.newObjectReader()) {
- ObjectId oid = repo.resolve(ref + ":" + path); //$NON-NLS-1$
- return reader.open(oid).getBytes(Integer.MAX_VALUE);
- }
- }
}
@SuppressWarnings("serial")
@@ -586,12 +663,13 @@ public class RepoCommand extends GitCommand<RevCommit> {
builder.add(dcEntry);
for (CopyFile copyfile : proj.getCopyFiles()) {
- byte[] src = callback.readFile(
+ RemoteFile rf = callback.readFileWithMode(
url, proj.getRevision(), copyfile.src);
- objectId = inserter.insert(Constants.OBJ_BLOB, src);
+ objectId = inserter.insert(Constants.OBJ_BLOB,
+ rf.getContents());
dcEntry = new DirCacheEntry(copyfile.dest);
dcEntry.setObjectId(objectId);
- dcEntry.setFileMode(FileMode.REGULAR_FILE);
+ dcEntry.setFileMode(rf.getFileMode());
builder.add(dcEntry);
}
for (LinkFile linkfile : proj.getLinkFiles()) {
@@ -606,8 +684,7 @@ public class RepoCommand extends GitCommand<RevCommit> {
}
objectId = inserter.insert(Constants.OBJ_BLOB,
- link.getBytes(
- Constants.CHARACTER_ENCODING));
+ link.getBytes(UTF_8));
dcEntry = new DirCacheEntry(linkfile.dest);
dcEntry.setObjectId(objectId);
dcEntry.setFileMode(FileMode.SYMLINK);
@@ -620,7 +697,7 @@ public class RepoCommand extends GitCommand<RevCommit> {
// create a new DirCacheEntry for .gitmodules file.
final DirCacheEntry dcEntry = new DirCacheEntry(Constants.DOT_GIT_MODULES);
ObjectId objectId = inserter.insert(Constants.OBJ_BLOB,
- content.getBytes(Constants.CHARACTER_ENCODING));
+ content.getBytes(UTF_8));
dcEntry.setObjectId(objectId);
dcEntry.setFileMode(FileMode.REGULAR_FILE);
builder.add(dcEntry);
@@ -629,7 +706,7 @@ public class RepoCommand extends GitCommand<RevCommit> {
// create a new DirCacheEntry for .gitattributes file.
final DirCacheEntry dcEntryAttr = new DirCacheEntry(Constants.DOT_GIT_ATTRIBUTES);
ObjectId attrId = inserter.insert(Constants.OBJ_BLOB,
- attributes.toString().getBytes(Constants.CHARACTER_ENCODING));
+ attributes.toString().getBytes(UTF_8));
dcEntryAttr.setObjectId(attrId);
dcEntryAttr.setFileMode(FileMode.REGULAR_FILE);
builder.add(dcEntryAttr);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoProject.java b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoProject.java
index 7ba83c7cbf..d79dfa8b2f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoProject.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoProject.java
@@ -136,6 +136,7 @@ public class RepoProject implements Comparable<RepoProject> {
FileChannel channel = input.getChannel();
output.getChannel().transferFrom(channel, 0, channel.size());
}
+ destFile.setExecutable(srcFile.canExecute());
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/hooks/PrePushHook.java b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/PrePushHook.java
index 051a1d1c81..431944f9d4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/hooks/PrePushHook.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/PrePushHook.java
@@ -164,12 +164,7 @@ public class PrePushHook extends GitHook<String> {
*/
public void setRefs(Collection<RemoteRefUpdate> toRefs) {
StringBuilder b = new StringBuilder();
- boolean first = true;
for (RemoteRefUpdate u : toRefs) {
- if (!first)
- b.append("\n"); //$NON-NLS-1$
- else
- first = false;
b.append(u.getSrcRef());
b.append(" "); //$NON-NLS-1$
b.append(u.getNewObjectId().getName());
@@ -179,6 +174,7 @@ public class PrePushHook extends GitHook<String> {
ObjectId ooid = u.getExpectedOldObjectId();
b.append((ooid == null) ? ObjectId.zeroId().getName() : ooid
.getName());
+ b.append("\n"); //$NON-NLS-1$
}
refs = b.toString();
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/Strings.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/Strings.java
index 9b255b41c3..41923eed18 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/Strings.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/Strings.java
@@ -443,7 +443,7 @@ public class Strings {
if (in_brackets > 0)
throw new InvalidPatternException("Not closed bracket?", pattern); //$NON-NLS-1$
try {
- return Pattern.compile(sb.toString());
+ return Pattern.compile(sb.toString(), Pattern.DOTALL);
} catch (PatternSyntaxException e) {
throw new InvalidPatternException(
MessageFormat.format(JGitText.get().invalidIgnoreRule,
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
index da0ba4a775..8f884b44f5 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
@@ -529,6 +529,7 @@ public class JGitText extends TranslationBundle {
/***/ public String newlineInQuotesNotAllowed;
/***/ public String noApplyInDelete;
/***/ public String noClosingBracket;
+ /***/ public String noCommitsSelectedForShallow;
/***/ public String noCredentialsProvider;
/***/ public String noHEADExistsAndNoExplicitStartingRevisionWasSpecified;
/***/ public String noHMACsupport;
@@ -729,10 +730,8 @@ public class JGitText extends TranslationBundle {
/***/ public String submoduleNameInvalid;
/***/ public String submoduleParentRemoteUrlInvalid;
/***/ public String submodulePathInvalid;
- /***/ public String submodulesNotSupported;
/***/ public String submoduleUrlInvalid;
/***/ public String supportOnlyPackIndexVersion2;
- /***/ public String symlinkCannotBeWrittenAsTheLinkTarget;
/***/ public String systemConfigFileInvalid;
/***/ public String tagAlreadyExists;
/***/ public String tagNameInvalid;
@@ -832,7 +831,9 @@ public class JGitText extends TranslationBundle {
/***/ public String uriNotFoundWithMessage;
/***/ public String URINotSupported;
/***/ public String userConfigFileInvalid;
+ /***/ public String validatingGitModules;
/***/ public String walkFailure;
+ /***/ public String wantNoSpaceWithCapabilities;
/***/ public String wantNotValid;
/***/ public String weeksAgo;
/***/ public String windowSizeMustBeLesserThanLimit;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/fsck/FsckError.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/fsck/FsckError.java
index 131b0048ae..335ac667cc 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/fsck/FsckError.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/fsck/FsckError.java
@@ -61,20 +61,21 @@ public class FsckError {
final int type;
- ObjectChecker.ErrorType errorType;
+ @Nullable
+ final ObjectChecker.ErrorType errorType;
/**
* @param id
* the object identifier.
* @param type
* type of the object.
+ * @param errorType
+ * kind of error
*/
- public CorruptObject(ObjectId id, int type) {
+ public CorruptObject(ObjectId id, int type,
+ @Nullable ObjectChecker.ErrorType errorType) {
this.id = id;
this.type = type;
- }
-
- void setErrorType(ObjectChecker.ErrorType errorType) {
this.errorType = errorType;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/fsck/FsckPackParser.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/fsck/FsckPackParser.java
index 5397ba4798..50594df1dd 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/fsck/FsckPackParser.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/fsck/FsckPackParser.java
@@ -174,12 +174,8 @@ public class FsckPackParser extends PackParser {
try {
super.verifySafeObject(id, type, data);
} catch (CorruptObjectException e) {
- // catch the exception and continue parse the pack file
- CorruptObject o = new CorruptObject(id.toObjectId(), type);
- if (e.getErrorType() != null) {
- o.setErrorType(e.getErrorType());
- }
- corruptObjects.add(o);
+ corruptObjects.add(
+ new CorruptObject(id.toObjectId(), type, e.getErrorType()));
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsFsck.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsFsck.java
index 3f96d0919b..c0e24c02cf 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsFsck.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsFsck.java
@@ -43,6 +43,7 @@
package org.eclipse.jgit.internal.storage.dfs;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static org.eclipse.jgit.internal.storage.pack.PackExt.INDEX;
import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK;
@@ -54,12 +55,18 @@ import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.internal.fsck.FsckError;
import org.eclipse.jgit.internal.fsck.FsckError.CorruptIndex;
+import org.eclipse.jgit.internal.fsck.FsckError.CorruptObject;
import org.eclipse.jgit.internal.fsck.FsckPackParser;
import org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource;
+import org.eclipse.jgit.internal.submodule.SubmoduleValidator;
+import org.eclipse.jgit.internal.submodule.SubmoduleValidator.SubmoduleValidationException;
+import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.GitmoduleEntry;
import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectChecker;
import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectLoader;
import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.revwalk.ObjectWalk;
@@ -102,6 +109,7 @@ public class DfsFsck {
FsckError errors = new FsckError();
if (!connectivityOnly) {
+ objChecker.reset();
checkPacks(pm, errors);
}
checkConnectivity(pm, errors);
@@ -128,6 +136,8 @@ public class DfsFsck {
}
}
}
+
+ checkGitModules(pm, errors);
}
private void verifyPack(ProgressMonitor pm, FsckError errors, DfsReader ctx,
@@ -142,6 +152,28 @@ public class DfsFsck {
fpp.verifyIndex(pack.getPackIndex(ctx));
}
+ private void checkGitModules(ProgressMonitor pm, FsckError errors)
+ throws IOException {
+ pm.beginTask(JGitText.get().validatingGitModules,
+ objChecker.getGitsubmodules().size());
+ for (GitmoduleEntry entry : objChecker.getGitsubmodules()) {
+ AnyObjectId blobId = entry.getBlobId();
+ ObjectLoader blob = objdb.open(blobId, Constants.OBJ_BLOB);
+
+ try {
+ SubmoduleValidator.assertValidGitModulesFile(
+ new String(blob.getBytes(), UTF_8));
+ } catch (SubmoduleValidationException e) {
+ CorruptObject co = new FsckError.CorruptObject(
+ blobId.toObjectId(), Constants.OBJ_BLOB,
+ e.getFsckMessageId());
+ errors.getCorruptObjects().add(co);
+ }
+ pm.update(1);
+ }
+ pm.endTask();
+ }
+
private void checkConnectivity(ProgressMonitor pm, FsckError errors)
throws IOException {
pm.beginTask(JGitText.get().countingObjects, ProgressMonitor.UNKNOWN);
@@ -179,6 +211,9 @@ public class DfsFsck {
* Use a customized object checker instead of the default one. Caller can
* specify a skip list to ignore some errors.
*
+ * It will be reset at the start of each {{@link #check(ProgressMonitor)}
+ * call.
+ *
* @param objChecker
* A customized object checker.
*/
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/ReadableChannel.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/ReadableChannel.java
index 9b98250884..d5e17224cf 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/ReadableChannel.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/ReadableChannel.java
@@ -57,7 +57,7 @@ public interface ReadableChannel extends ReadableByteChannel {
* @throws java.io.IOException
* the channel's current position cannot be obtained.
*/
- public long position() throws IOException;
+ long position() throws IOException;
/**
* Seek the current position of the channel to a new offset.
@@ -70,7 +70,7 @@ public interface ReadableChannel extends ReadableByteChannel {
* channel only supports block aligned IO and the current
* position is not block aligned.
*/
- public void position(long newPosition) throws IOException;
+ void position(long newPosition) throws IOException;
/**
* Get the total size of the channel.
@@ -83,7 +83,7 @@ public interface ReadableChannel extends ReadableByteChannel {
* @throws java.io.IOException
* the size cannot be determined.
*/
- public long size() throws IOException;
+ long size() throws IOException;
/**
* Get the recommended alignment for reads.
@@ -102,7 +102,7 @@ public interface ReadableChannel extends ReadableByteChannel {
* @return recommended alignment size for randomly positioned reads. Does
* not need to be a power of 2.
*/
- public int blockSize();
+ int blockSize();
/**
* Recommend the channel maintain a read-ahead buffer.
@@ -131,5 +131,5 @@ public interface ReadableChannel extends ReadableByteChannel {
* @throws java.io.IOException
* if the read ahead cannot be adjusted.
*/
- public void setReadAheadBytes(int bufferSize) throws IOException;
+ void setReadAheadBytes(int bufferSize) throws IOException;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java
index e35b9c9e4a..35522667e0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java
@@ -43,6 +43,7 @@
package org.eclipse.jgit.internal.storage.file;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static org.eclipse.jgit.internal.storage.pack.PackExt.INDEX;
import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK;
@@ -50,7 +51,6 @@ import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
-import java.io.FileReader;
import java.io.IOException;
import java.nio.file.AtomicMoveNotSupportedException;
import java.nio.file.Files;
@@ -1036,8 +1036,8 @@ public class ObjectDirectory extends FileObjectDatabase {
}
private static BufferedReader open(File f)
- throws FileNotFoundException {
- return new BufferedReader(new FileReader(f));
+ throws IOException, FileNotFoundException {
+ return Files.newBufferedReader(f.toPath(), UTF_8);
}
private AlternateHandle openAlternate(String location)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexRemapper.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexRemapper.java
index 70695880d3..964cc4d120 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexRemapper.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexRemapper.java
@@ -207,7 +207,7 @@ public class PackBitmapIndexRemapper extends PackBitmapIndex
}
/** An entry in the old PackBitmapIndex. */
- public final class Entry extends ObjectId {
+ public static final class Entry extends ObjectId {
private final int flags;
Entry(AnyObjectId src, int flags) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/ObjectReuseAsIs.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/ObjectReuseAsIs.java
index f759e23ef4..2c806235a6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/ObjectReuseAsIs.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/ObjectReuseAsIs.java
@@ -79,7 +79,7 @@ public interface ObjectReuseAsIs {
* the Git type of the object that will be packed.
* @return a new instance for this object.
*/
- public ObjectToPack newObjectToPack(AnyObjectId objectId, int type);
+ ObjectToPack newObjectToPack(AnyObjectId objectId, int type);
/**
* Select the best object representation for a packer.
@@ -114,7 +114,7 @@ public interface ObjectReuseAsIs {
* @throws java.io.IOException
* the repository cannot be accessed. Packing will abort.
*/
- public void selectObjectRepresentation(PackWriter packer,
+ void selectObjectRepresentation(PackWriter packer,
ProgressMonitor monitor, Iterable<ObjectToPack> objects)
throws IOException, MissingObjectException;
@@ -155,7 +155,7 @@ public interface ObjectReuseAsIs {
* the stream cannot be written to, or one or more required
* objects cannot be accessed from the object database.
*/
- public void writeObjects(PackOutputStream out, List<ObjectToPack> list)
+ void writeObjects(PackOutputStream out, List<ObjectToPack> list)
throws IOException;
/**
@@ -200,7 +200,7 @@ public interface ObjectReuseAsIs {
* the stream's write method threw an exception. Packing will
* abort.
*/
- public void copyObjectAsIs(PackOutputStream out, ObjectToPack otp,
+ void copyObjectAsIs(PackOutputStream out, ObjectToPack otp,
boolean validate) throws IOException,
StoredObjectRepresentationNotAvailableException;
@@ -216,7 +216,7 @@ public interface ObjectReuseAsIs {
* @throws java.io.IOException
* the pack cannot be read, or stream did not accept a write.
*/
- public abstract void copyPackAsIs(PackOutputStream out, CachedPack pack)
+ void copyPackAsIs(PackOutputStream out, CachedPack pack)
throws IOException;
/**
@@ -234,6 +234,6 @@ public interface ObjectReuseAsIs {
* Callers may choose to ignore this and continue as-if there
* were no cached packs.
*/
- public Collection<CachedPack> getCachedPacksAndUpdate(
+ Collection<CachedPack> getCachedPacksAndUpdate(
BitmapBuilder needBitmap) throws IOException;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackOutputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackOutputStream.java
index 7f38a7b51a..eb777be809 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackOutputStream.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackOutputStream.java
@@ -187,6 +187,7 @@ public final class PackOutputStream extends OutputStream {
* @throws java.io.IOException
* the underlying stream refused to accept the header.
*/
+ @SuppressWarnings("ShortCircuitBoolean")
public final void writeHeader(ObjectToPack otp, long rawLength)
throws IOException {
ObjectToPack b = otp.getDeltaBase();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/submodule/SubmoduleValidator.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/submodule/SubmoduleValidator.java
index 3651631573..7b872b1860 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/submodule/SubmoduleValidator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/submodule/SubmoduleValidator.java
@@ -45,13 +45,17 @@ package org.eclipse.jgit.internal.submodule;
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_PATH;
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_URL;
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_SUBMODULE_SECTION;
+import static org.eclipse.jgit.lib.ObjectChecker.ErrorType.GITMODULES_NAME;
+import static org.eclipse.jgit.lib.ObjectChecker.ErrorType.GITMODULES_PARSE;
+import static org.eclipse.jgit.lib.ObjectChecker.ErrorType.GITMODULES_PATH;
+import static org.eclipse.jgit.lib.ObjectChecker.ErrorType.GITMODULES_URL;
-import java.io.IOException;
import java.text.MessageFormat;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.ObjectChecker;
/**
* Validations for the git submodule fields (name, path, uri).
@@ -66,15 +70,30 @@ public class SubmoduleValidator {
*/
public static class SubmoduleValidationException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ private final ObjectChecker.ErrorType fsckMessageId;
+
/**
* @param message
* Description of the problem
+ * @param fsckMessageId
+ * Error identifier, following the git fsck fsck.<msg-id>
+ * format
*/
- public SubmoduleValidationException(String message) {
+ SubmoduleValidationException(String message,
+ ObjectChecker.ErrorType fsckMessageId) {
super(message);
+ this.fsckMessageId = fsckMessageId;
}
- private static final long serialVersionUID = 1L;
+
+ /**
+ * @return the error identifier
+ */
+ public ObjectChecker.ErrorType getFsckMessageId() {
+ return fsckMessageId;
+ }
}
/**
@@ -100,13 +119,15 @@ public class SubmoduleValidator {
// Since Path class is platform dependent, we manually check '/' and
// '\\' patterns here.
throw new SubmoduleValidationException(MessageFormat
- .format(JGitText.get().invalidNameContainsDotDot, name));
+ .format(JGitText.get().invalidNameContainsDotDot, name),
+ GITMODULES_NAME);
}
if (name.startsWith("-")) { //$NON-NLS-1$
throw new SubmoduleValidationException(
MessageFormat.format(
- JGitText.get().submoduleNameInvalid, name));
+ JGitText.get().submoduleNameInvalid, name),
+ GITMODULES_NAME);
}
}
@@ -123,7 +144,8 @@ public class SubmoduleValidator {
if (uri.startsWith("-")) { //$NON-NLS-1$
throw new SubmoduleValidationException(
MessageFormat.format(
- JGitText.get().submoduleUrlInvalid, uri));
+ JGitText.get().submoduleUrlInvalid, uri),
+ GITMODULES_URL);
}
}
@@ -140,19 +162,22 @@ public class SubmoduleValidator {
if (path.startsWith("-")) { //$NON-NLS-1$
throw new SubmoduleValidationException(
MessageFormat.format(
- JGitText.get().submodulePathInvalid, path));
+ JGitText.get().submodulePathInvalid, path),
+ GITMODULES_PATH);
}
}
/**
+ * Validate a .gitmodules file
+ *
* @param gitModulesContents
* Contents of a .gitmodule file. They will be parsed internally.
- * @throws IOException
- * If the contents
+ * @throws SubmoduleValidationException
+ * if the contents don't look like a configuration file or field
+ * values are not valid
*/
public static void assertValidGitModulesFile(String gitModulesContents)
- throws IOException {
- // Validate .gitmodules file
+ throws SubmoduleValidationException {
Config c = new Config();
try {
c.fromText(gitModulesContents);
@@ -173,12 +198,9 @@ public class SubmoduleValidator {
}
}
} catch (ConfigInvalidException e) {
- throw new IOException(
- MessageFormat.format(
- JGitText.get().invalidGitModules,
- e));
- } catch (SubmoduleValidationException e) {
- throw new IOException(e.getMessage(), e);
+ throw new SubmoduleValidationException(
+ JGitText.get().invalidGitModules,
+ GITMODULES_PARSE);
}
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/parser/FirstWant.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/parser/FirstWant.java
new file mode 100644
index 0000000000..2dae021702
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/parser/FirstWant.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2018, Google LLC.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.internal.transport.parser;
+
+import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_AGENT;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.errors.PackProtocolException;
+import org.eclipse.jgit.internal.JGitText;
+
+/**
+ * In the pack negotiation phase (protocol v0/v1), the client sends a list of
+ * wants. The first "want" line is special, as it (can) have a list of
+ * capabilities appended.
+ *
+ * E.g. "want oid cap1 cap2 cap3"
+ *
+ * Do not confuse this line with the first one in the reference advertisement,
+ * which is sent by the server, looks like
+ * "b8f7c471373b8583ced0025cfad8c9916c484b76 HEAD\0 cap1 cap2 cap3" and is
+ * parsed by the BasePackConnection.readAdvertisedRefs method.
+ *
+ * This class parses the input want line and holds the results: the actual want
+ * line and the capabilities.
+ *
+ * @since 5.2
+ */
+public class FirstWant {
+ private final String line;
+
+ private final Set<String> capabilities;
+
+ @Nullable
+ private final String agent;
+
+ private static final String AGENT_PREFIX = OPTION_AGENT + '=';
+
+ /**
+ * Parse the first want line in the protocol v0/v1 pack negotiation.
+ *
+ * @param line
+ * line from the client.
+ * @return an instance of FirstWant
+ * @throws PackProtocolException
+ * if the line doesn't follow the protocol format.
+ */
+ public static FirstWant fromLine(String line) throws PackProtocolException {
+ String wantLine;
+ Set<String> capabilities;
+ String agent = null;
+
+ if (line.length() > 45) {
+ String opt = line.substring(45);
+ if (!opt.startsWith(" ")) { //$NON-NLS-1$
+ throw new PackProtocolException(JGitText.get().wantNoSpaceWithCapabilities);
+ }
+ opt = opt.substring(1);
+
+ HashSet<String> opts = new HashSet<>();
+ for (String clientCapability : opt.split(" ")) { //$NON-NLS-1$
+ if (clientCapability.startsWith(AGENT_PREFIX)) {
+ agent = clientCapability.substring(AGENT_PREFIX.length());
+ } else {
+ opts.add(clientCapability);
+ }
+ }
+ wantLine = line.substring(0, 45);
+ capabilities = Collections.unmodifiableSet(opts);
+ } else {
+ wantLine = line;
+ capabilities = Collections.emptySet();
+ }
+
+ return new FirstWant(wantLine, capabilities, agent);
+ }
+
+ private FirstWant(String line, Set<String> capabilities,
+ @Nullable String agent) {
+ this.line = line;
+ this.capabilities = capabilities;
+ this.agent = agent;
+ }
+
+ /** @return non-capabilities part of the line. */
+ public String getLine() {
+ return line;
+ }
+
+ /**
+ * @return capabilities parsed from the line as an immutable set (excluding
+ * agent).
+ */
+ public Set<String> getCapabilities() {
+ return capabilities;
+ }
+
+ /** @return client user agent parsed from the line. */
+ @Nullable
+ public String getAgent() {
+ return agent;
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/ssh/OpenSshConfigFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/ssh/OpenSshConfigFile.java
new file mode 100644
index 0000000000..e8a6ba7308
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/ssh/OpenSshConfigFile.java
@@ -0,0 +1,922 @@
+/*
+ * Copyright (C) 2008, 2017, Google Inc.
+ * Copyright (C) 2017, 2018, Thomas Wolf <thomas.wolf@paranor.ch>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.internal.transport.ssh;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.errors.InvalidPatternException;
+import org.eclipse.jgit.fnmatch.FileNameMatcher;
+import org.eclipse.jgit.transport.SshConstants;
+import org.eclipse.jgit.util.StringUtils;
+import org.eclipse.jgit.util.SystemReader;
+
+/**
+ * Fairly complete configuration parser for the openssh ~/.ssh/config file.
+ * <p>
+ * Both JSch 0.1.54 and Apache MINA sshd 2.1.0 have parsers for this, but both
+ * are buggy. Therefore we implement our own parser to read an openssh
+ * configuration file.
+ * </p>
+ * <p>
+ * Limitations compared to the full openssh 7.5 parser:
+ * </p>
+ * <ul>
+ * <li>This parser does not handle Match or Include keywords.
+ * <li>This parser does not do host name canonicalization.
+ * </ul>
+ * <p>
+ * Note that openssh's readconf.c is a validating parser; this parser does not
+ * validate entries.
+ * </p>
+ * <p>
+ * This config does %-substitutions for the following tokens:
+ * </p>
+ * <ul>
+ * <li>%% - single %
+ * <li>%C - short-hand for %l%h%p%r.
+ * <li>%d - home directory path
+ * <li>%h - remote host name
+ * <li>%L - local host name without domain
+ * <li>%l - FQDN of the local host
+ * <li>%n - host name as specified in {@link #lookup(String, int, String)}
+ * <li>%p - port number; if not given in {@link #lookup(String, int, String)}
+ * replaced only if set in the config
+ * <li>%r - remote user name; if not given in
+ * {@link #lookup(String, int, String)} replaced only if set in the config
+ * <li>%u - local user name
+ * </ul>
+ * <p>
+ * %i is not handled; Java has no concept of a "user ID". %T is always replaced
+ * by NONE.
+ * </p>
+ *
+ * @see <a href="http://man.openbsd.org/OpenBSD-current/man5/ssh_config.5">man
+ * ssh-config</a>
+ */
+public class OpenSshConfigFile {
+
+ /**
+ * "Host" name of the HostEntry for the default options before the first
+ * host block in a config file.
+ */
+ private static final String DEFAULT_NAME = ""; //$NON-NLS-1$
+
+ /** The user's home directory, as key files may be relative to here. */
+ private final File home;
+
+ /** The .ssh/config file we read and monitor for updates. */
+ private final File configFile;
+
+ /** User name of the user on the host OS. */
+ private final String localUserName;
+
+ /** Modification time of {@link #configFile} when it was last loaded. */
+ private long lastModified;
+
+ /**
+ * Encapsulates entries read out of the configuration file, and a cache of
+ * fully resolved entries created from that.
+ */
+ private static class State {
+ // Keyed by pattern; if a "Host" line has multiple patterns, we generate
+ // duplicate HostEntry objects
+ Map<String, HostEntry> entries = new LinkedHashMap<>();
+
+ // Keyed by user@hostname:port
+ Map<String, HostEntry> hosts = new HashMap<>();
+
+ @Override
+ @SuppressWarnings("nls")
+ public String toString() {
+ return "State [entries=" + entries + ", hosts=" + hosts + "]";
+ }
+ }
+
+ /** State read from the config file, plus the cache. */
+ private State state;
+
+ /**
+ * Creates a new {@link OpenSshConfigFile} that will read the config from
+ * file {@code config} use the given file {@code home} as "home" directory.
+ *
+ * @param home
+ * user's home directory for the purpose of ~ replacement
+ * @param config
+ * file to load.
+ * @param localUserName
+ * user name of the current user on the local host OS
+ */
+ public OpenSshConfigFile(@NonNull File home, @NonNull File config,
+ @NonNull String localUserName) {
+ this.home = home;
+ this.configFile = config;
+ this.localUserName = localUserName;
+ state = new State();
+ }
+
+ /**
+ * Locate the configuration for a specific host request.
+ *
+ * @param hostName
+ * the name the user has supplied to the SSH tool. This may be a
+ * real host name, or it may just be a "Host" block in the
+ * configuration file.
+ * @param port
+ * the user supplied; <= 0 if none
+ * @param userName
+ * the user supplied, may be {@code null} or empty if none given
+ * @return r configuration for the requested name.
+ */
+ @NonNull
+ public HostEntry lookup(@NonNull String hostName, int port,
+ String userName) {
+ final State cache = refresh();
+ String cacheKey = toCacheKey(hostName, port, userName);
+ HostEntry h = cache.hosts.get(cacheKey);
+ if (h != null) {
+ return h;
+ }
+ HostEntry fullConfig = new HostEntry();
+ // Initialize with default entries at the top of the file, before the
+ // first Host block.
+ fullConfig.merge(cache.entries.get(DEFAULT_NAME));
+ for (Map.Entry<String, HostEntry> e : cache.entries.entrySet()) {
+ String pattern = e.getKey();
+ if (isHostMatch(pattern, hostName)) {
+ fullConfig.merge(e.getValue());
+ }
+ }
+ fullConfig.substitute(hostName, port, userName, localUserName, home);
+ cache.hosts.put(cacheKey, fullConfig);
+ return fullConfig;
+ }
+
+ @NonNull
+ private String toCacheKey(@NonNull String hostName, int port,
+ String userName) {
+ String key = hostName;
+ if (port > 0) {
+ key = key + ':' + Integer.toString(port);
+ }
+ if (userName != null && !userName.isEmpty()) {
+ key = userName + '@' + key;
+ }
+ return key;
+ }
+
+ private synchronized State refresh() {
+ final long mtime = configFile.lastModified();
+ if (mtime != lastModified) {
+ State newState = new State();
+ try (BufferedReader br = Files
+ .newBufferedReader(configFile.toPath(), UTF_8)) {
+ newState.entries = parse(br);
+ } catch (IOException | RuntimeException none) {
+ // Ignore -- we'll set and return an empty state
+ }
+ lastModified = mtime;
+ state = newState;
+ }
+ return state;
+ }
+
+ private Map<String, HostEntry> parse(BufferedReader reader)
+ throws IOException {
+ final Map<String, HostEntry> entries = new LinkedHashMap<>();
+ final List<HostEntry> current = new ArrayList<>(4);
+ String line;
+
+ // The man page doesn't say so, but the openssh parser (readconf.c)
+ // starts out in active mode and thus always applies any lines that
+ // occur before the first host block. We gather those options in a
+ // HostEntry for DEFAULT_NAME.
+ HostEntry defaults = new HostEntry();
+ current.add(defaults);
+ entries.put(DEFAULT_NAME, defaults);
+
+ while ((line = reader.readLine()) != null) {
+ line = line.trim();
+ if (line.isEmpty() || line.startsWith("#")) { //$NON-NLS-1$
+ continue;
+ }
+ String[] parts = line.split("[ \t]*[= \t]", 2); //$NON-NLS-1$
+ // Although the ssh-config man page doesn't say so, the openssh
+ // parser does allow quoted keywords.
+ String keyword = dequote(parts[0].trim());
+ // man 5 ssh-config says lines had the format "keyword arguments",
+ // with no indication that arguments were optional. However, let's
+ // not crap out on missing arguments. See bug 444319.
+ String argValue = parts.length > 1 ? parts[1].trim() : ""; //$NON-NLS-1$
+
+ if (StringUtils.equalsIgnoreCase(SshConstants.HOST, keyword)) {
+ current.clear();
+ for (String name : parseList(argValue)) {
+ if (name == null || name.isEmpty()) {
+ // null should not occur, but better be safe than sorry.
+ continue;
+ }
+ HostEntry c = entries.get(name);
+ if (c == null) {
+ c = new HostEntry();
+ entries.put(name, c);
+ }
+ current.add(c);
+ }
+ continue;
+ }
+
+ if (current.isEmpty()) {
+ // We received an option outside of a Host block. We
+ // don't know who this should match against, so skip.
+ continue;
+ }
+
+ if (HostEntry.isListKey(keyword)) {
+ List<String> args = validate(keyword, parseList(argValue));
+ for (HostEntry entry : current) {
+ entry.setValue(keyword, args);
+ }
+ } else if (!argValue.isEmpty()) {
+ argValue = validate(keyword, dequote(argValue));
+ for (HostEntry entry : current) {
+ entry.setValue(keyword, argValue);
+ }
+ }
+ }
+
+ return entries;
+ }
+
+ /**
+ * Splits the argument into a list of whitespace-separated elements.
+ * Elements containing whitespace must be quoted and will be de-quoted.
+ *
+ * @param argument
+ * argument part of the configuration line as read from the
+ * config file
+ * @return a {@link List} of elements, possibly empty and possibly
+ * containing empty elements, but not containing {@code null}
+ */
+ private List<String> parseList(String argument) {
+ List<String> result = new ArrayList<>(4);
+ int start = 0;
+ int length = argument.length();
+ while (start < length) {
+ // Skip whitespace
+ if (Character.isSpaceChar(argument.charAt(start))) {
+ start++;
+ continue;
+ }
+ if (argument.charAt(start) == '"') {
+ int stop = argument.indexOf('"', ++start);
+ if (stop < start) {
+ // No closing double quote: skip
+ break;
+ }
+ result.add(argument.substring(start, stop));
+ start = stop + 1;
+ } else {
+ int stop = start + 1;
+ while (stop < length
+ && !Character.isSpaceChar(argument.charAt(stop))) {
+ stop++;
+ }
+ result.add(argument.substring(start, stop));
+ start = stop + 1;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Hook to perform validation on a single value, or to sanitize it. If this
+ * throws an (unchecked) exception, parsing of the file is abandoned.
+ *
+ * @param key
+ * of the entry
+ * @param value
+ * as read from the config file
+ * @return the validated and possibly sanitized value
+ */
+ protected String validate(String key, String value) {
+ if (String.CASE_INSENSITIVE_ORDER.compare(key,
+ SshConstants.PREFERRED_AUTHENTICATIONS) == 0) {
+ return stripWhitespace(value);
+ }
+ return value;
+ }
+
+ /**
+ * Hook to perform validation on values, or to sanitize them. If this throws
+ * an (unchecked) exception, parsing of the file is abandoned.
+ *
+ * @param key
+ * of the entry
+ * @param value
+ * list of arguments as read from the config file
+ * @return a {@link List} of values, possibly empty and possibly containing
+ * empty elements, but not containing {@code null}
+ */
+ protected List<String> validate(String key, List<String> value) {
+ return value;
+ }
+
+ private static boolean isHostMatch(String pattern, String name) {
+ if (pattern.startsWith("!")) { //$NON-NLS-1$
+ return !patternMatchesHost(pattern.substring(1), name);
+ } else {
+ return patternMatchesHost(pattern, name);
+ }
+ }
+
+ private static boolean patternMatchesHost(String pattern, String name) {
+ if (pattern.indexOf('*') >= 0 || pattern.indexOf('?') >= 0) {
+ final FileNameMatcher fn;
+ try {
+ fn = new FileNameMatcher(pattern, null);
+ } catch (InvalidPatternException e) {
+ return false;
+ }
+ fn.append(name);
+ return fn.isMatch();
+ } else {
+ // Not a pattern but a full host name
+ return pattern.equals(name);
+ }
+ }
+
+ private static String dequote(String value) {
+ if (value.startsWith("\"") && value.endsWith("\"") //$NON-NLS-1$ //$NON-NLS-2$
+ && value.length() > 1)
+ return value.substring(1, value.length() - 1);
+ return value;
+ }
+
+ private static String stripWhitespace(String value) {
+ final StringBuilder b = new StringBuilder();
+ for (int i = 0; i < value.length(); i++) {
+ if (!Character.isSpaceChar(value.charAt(i)))
+ b.append(value.charAt(i));
+ }
+ return b.toString();
+ }
+
+ private static File toFile(String path, File home) {
+ if (path.startsWith("~/") || path.startsWith("~" + File.separator)) { //$NON-NLS-1$ //$NON-NLS-2$
+ return new File(home, path.substring(2));
+ }
+ File ret = new File(path);
+ if (ret.isAbsolute()) {
+ return ret;
+ }
+ return new File(home, path);
+ }
+
+ /**
+ * Converts a positive value into an {@code int}.
+ *
+ * @param value
+ * to convert
+ * @return the value, or -1 if it wasn't a positive integral value
+ */
+ public static int positive(String value) {
+ if (value != null) {
+ try {
+ return Integer.parseUnsignedInt(value);
+ } catch (NumberFormatException e) {
+ // Ignore
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Converts a ssh config flag value (yes/true/on - no/false/off) into an
+ * {@code boolean}.
+ *
+ * @param value
+ * to convert
+ * @return {@code true} if {@code value} is "yes", "on", or "true";
+ * {@code false} otherwise
+ */
+ public static boolean flag(String value) {
+ if (value == null) {
+ return false;
+ }
+ return SshConstants.YES.equals(value) || SshConstants.ON.equals(value)
+ || SshConstants.TRUE.equals(value);
+ }
+
+ /**
+ * Retrieves the local user name as given in the constructor.
+ *
+ * @return the user name
+ */
+ public String getLocalUserName() {
+ return localUserName;
+ }
+
+ /**
+ * A host entry from the ssh config file. Any merging of global values and
+ * of several matching host entries, %-substitutions, and ~ replacement have
+ * all been done.
+ */
+ public static class HostEntry {
+
+ /**
+ * Keys that can be specified multiple times, building up a list. (I.e.,
+ * those are the keys that do not follow the general rule of "first
+ * occurrence wins".)
+ */
+ private static final Set<String> MULTI_KEYS = new TreeSet<>(
+ String.CASE_INSENSITIVE_ORDER);
+
+ static {
+ MULTI_KEYS.add(SshConstants.CERTIFICATE_FILE);
+ MULTI_KEYS.add(SshConstants.IDENTITY_FILE);
+ MULTI_KEYS.add(SshConstants.LOCAL_FORWARD);
+ MULTI_KEYS.add(SshConstants.REMOTE_FORWARD);
+ MULTI_KEYS.add(SshConstants.SEND_ENV);
+ }
+
+ /**
+ * Keys that take a whitespace-separated list of elements as argument.
+ * Because the dequote-handling is different, we must handle those in
+ * the parser. There are a few other keys that take comma-separated
+ * lists as arguments, but for the parser those are single arguments
+ * that must be quoted if they contain whitespace, and taking them apart
+ * is the responsibility of the user of those keys.
+ */
+ private static final Set<String> LIST_KEYS = new TreeSet<>(
+ String.CASE_INSENSITIVE_ORDER);
+
+ static {
+ LIST_KEYS.add(SshConstants.CANONICAL_DOMAINS);
+ LIST_KEYS.add(SshConstants.GLOBAL_KNOWN_HOSTS_FILE);
+ LIST_KEYS.add(SshConstants.SEND_ENV);
+ LIST_KEYS.add(SshConstants.USER_KNOWN_HOSTS_FILE);
+ }
+
+ private Map<String, String> options;
+
+ private Map<String, List<String>> multiOptions;
+
+ private Map<String, List<String>> listOptions;
+
+ /**
+ * Retrieves the value of a single-valued key, or the first is the key
+ * has multiple values. Keys are case-insensitive, so
+ * {@code getValue("HostName") == getValue("HOSTNAME")}.
+ *
+ * @param key
+ * to get the value of
+ * @return the value, or {@code null} if none
+ */
+ public String getValue(String key) {
+ String result = options != null ? options.get(key) : null;
+ if (result == null) {
+ // Let's be lenient and return at least the first value from
+ // a list-valued or multi-valued key.
+ List<String> values = listOptions != null ? listOptions.get(key)
+ : null;
+ if (values == null) {
+ values = multiOptions != null ? multiOptions.get(key)
+ : null;
+ }
+ if (values != null && !values.isEmpty()) {
+ result = values.get(0);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Retrieves the values of a multi or list-valued key. Keys are
+ * case-insensitive, so
+ * {@code getValue("HostName") == getValue("HOSTNAME")}.
+ *
+ * @param key
+ * to get the values of
+ * @return a possibly empty list of values
+ */
+ public List<String> getValues(String key) {
+ List<String> values = listOptions != null ? listOptions.get(key)
+ : null;
+ if (values == null) {
+ values = multiOptions != null ? multiOptions.get(key) : null;
+ }
+ if (values == null || values.isEmpty()) {
+ return new ArrayList<>();
+ }
+ return new ArrayList<>(values);
+ }
+
+ /**
+ * Sets the value of a single-valued key if it not set yet, or adds a
+ * value to a multi-valued key. If the value is {@code null}, the key is
+ * removed altogether, whether it is single-, list-, or multi-valued.
+ *
+ * @param key
+ * to modify
+ * @param value
+ * to set or add
+ */
+ public void setValue(String key, String value) {
+ if (value == null) {
+ if (multiOptions != null) {
+ multiOptions.remove(key);
+ }
+ if (listOptions != null) {
+ listOptions.remove(key);
+ }
+ if (options != null) {
+ options.remove(key);
+ }
+ return;
+ }
+ if (MULTI_KEYS.contains(key)) {
+ if (multiOptions == null) {
+ multiOptions = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+ }
+ List<String> values = multiOptions.get(key);
+ if (values == null) {
+ values = new ArrayList<>(4);
+ multiOptions.put(key, values);
+ }
+ values.add(value);
+ } else {
+ if (options == null) {
+ options = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+ }
+ if (!options.containsKey(key)) {
+ options.put(key, value);
+ }
+ }
+ }
+
+ /**
+ * Sets the values of a multi- or list-valued key.
+ *
+ * @param key
+ * to set
+ * @param values
+ * a non-empty list of values
+ */
+ public void setValue(String key, List<String> values) {
+ if (values.isEmpty()) {
+ return;
+ }
+ // Check multi-valued keys first; because of the replacement
+ // strategy, they must take precedence over list-valued keys
+ // which always follow the "first occurrence wins" strategy.
+ //
+ // Note that SendEnv is a multi-valued list-valued key. (It's
+ // rather immaterial for JGit, though.)
+ if (MULTI_KEYS.contains(key)) {
+ if (multiOptions == null) {
+ multiOptions = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+ }
+ List<String> items = multiOptions.get(key);
+ if (items == null) {
+ items = new ArrayList<>(values);
+ multiOptions.put(key, items);
+ } else {
+ items.addAll(values);
+ }
+ } else {
+ if (listOptions == null) {
+ listOptions = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+ }
+ if (!listOptions.containsKey(key)) {
+ listOptions.put(key, values);
+ }
+ }
+ }
+
+ /**
+ * Does the key take a whitespace-separated list of values?
+ *
+ * @param key
+ * to check
+ * @return {@code true} if the key is a list-valued key.
+ */
+ public static boolean isListKey(String key) {
+ return LIST_KEYS.contains(key.toUpperCase(Locale.ROOT));
+ }
+
+ void merge(HostEntry entry) {
+ if (entry == null) {
+ // Can occur if we could not read the config file
+ return;
+ }
+ if (entry.options != null) {
+ if (options == null) {
+ options = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+ }
+ for (Map.Entry<String, String> item : entry.options
+ .entrySet()) {
+ if (!options.containsKey(item.getKey())) {
+ options.put(item.getKey(), item.getValue());
+ }
+ }
+ }
+ if (entry.listOptions != null) {
+ if (listOptions == null) {
+ listOptions = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+ }
+ for (Map.Entry<String, List<String>> item : entry.listOptions
+ .entrySet()) {
+ if (!listOptions.containsKey(item.getKey())) {
+ listOptions.put(item.getKey(), item.getValue());
+ }
+ }
+
+ }
+ if (entry.multiOptions != null) {
+ if (multiOptions == null) {
+ multiOptions = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+ }
+ for (Map.Entry<String, List<String>> item : entry.multiOptions
+ .entrySet()) {
+ List<String> values = multiOptions.get(item.getKey());
+ if (values == null) {
+ values = new ArrayList<>(item.getValue());
+ multiOptions.put(item.getKey(), values);
+ } else {
+ values.addAll(item.getValue());
+ }
+ }
+ }
+ }
+
+ private List<String> substitute(List<String> values, String allowed,
+ Replacer r) {
+ List<String> result = new ArrayList<>(values.size());
+ for (String value : values) {
+ result.add(r.substitute(value, allowed));
+ }
+ return result;
+ }
+
+ private List<String> replaceTilde(List<String> values, File home) {
+ List<String> result = new ArrayList<>(values.size());
+ for (String value : values) {
+ result.add(toFile(value, home).getPath());
+ }
+ return result;
+ }
+
+ void substitute(String originalHostName, int port, String userName,
+ String localUserName, File home) {
+ int p = port >= 0 ? port : positive(getValue(SshConstants.PORT));
+ if (p < 0) {
+ p = SshConstants.SSH_DEFAULT_PORT;
+ }
+ String u = userName != null && !userName.isEmpty() ? userName
+ : getValue(SshConstants.USER);
+ if (u == null || u.isEmpty()) {
+ u = localUserName;
+ }
+ Replacer r = new Replacer(originalHostName, p, u, localUserName,
+ home);
+ if (options != null) {
+ // HOSTNAME first
+ String hostName = options.get(SshConstants.HOST_NAME);
+ if (hostName == null || hostName.isEmpty()) {
+ options.put(SshConstants.HOST_NAME, originalHostName);
+ } else {
+ hostName = r.substitute(hostName, "h"); //$NON-NLS-1$
+ options.put(SshConstants.HOST_NAME, hostName);
+ r.update('h', hostName);
+ }
+ }
+ if (multiOptions != null) {
+ List<String> values = multiOptions
+ .get(SshConstants.IDENTITY_FILE);
+ if (values != null) {
+ values = substitute(values, "dhlru", r); //$NON-NLS-1$
+ values = replaceTilde(values, home);
+ multiOptions.put(SshConstants.IDENTITY_FILE, values);
+ }
+ values = multiOptions.get(SshConstants.CERTIFICATE_FILE);
+ if (values != null) {
+ values = substitute(values, "dhlru", r); //$NON-NLS-1$
+ values = replaceTilde(values, home);
+ multiOptions.put(SshConstants.CERTIFICATE_FILE, values);
+ }
+ }
+ if (listOptions != null) {
+ List<String> values = listOptions
+ .get(SshConstants.USER_KNOWN_HOSTS_FILE);
+ if (values != null) {
+ values = replaceTilde(values, home);
+ listOptions.put(SshConstants.USER_KNOWN_HOSTS_FILE, values);
+ }
+ }
+ if (options != null) {
+ // HOSTNAME already done above
+ String value = options.get(SshConstants.IDENTITY_AGENT);
+ if (value != null) {
+ value = r.substitute(value, "dhlru"); //$NON-NLS-1$
+ value = toFile(value, home).getPath();
+ options.put(SshConstants.IDENTITY_AGENT, value);
+ }
+ value = options.get(SshConstants.CONTROL_PATH);
+ if (value != null) {
+ value = r.substitute(value, "ChLlnpru"); //$NON-NLS-1$
+ value = toFile(value, home).getPath();
+ options.put(SshConstants.CONTROL_PATH, value);
+ }
+ value = options.get(SshConstants.LOCAL_COMMAND);
+ if (value != null) {
+ value = r.substitute(value, "CdhlnprTu"); //$NON-NLS-1$
+ options.put(SshConstants.LOCAL_COMMAND, value);
+ }
+ value = options.get(SshConstants.REMOTE_COMMAND);
+ if (value != null) {
+ value = r.substitute(value, "Cdhlnpru"); //$NON-NLS-1$
+ options.put(SshConstants.REMOTE_COMMAND, value);
+ }
+ value = options.get(SshConstants.PROXY_COMMAND);
+ if (value != null) {
+ value = r.substitute(value, "hpr"); //$NON-NLS-1$
+ options.put(SshConstants.PROXY_COMMAND, value);
+ }
+ }
+ // Match is not implemented and would need to be done elsewhere
+ // anyway.
+ }
+
+ /**
+ * Retrieves an unmodifiable map of all single-valued options, with
+ * case-insensitive lookup by keys.
+ *
+ * @return all single-valued options
+ */
+ @NonNull
+ public Map<String, String> getOptions() {
+ if (options == null) {
+ return Collections.emptyMap();
+ }
+ return Collections.unmodifiableMap(options);
+ }
+
+ /**
+ * Retrieves an unmodifiable map of all multi-valued options, with
+ * case-insensitive lookup by keys.
+ *
+ * @return all multi-valued options
+ */
+ @NonNull
+ public Map<String, List<String>> getMultiValuedOptions() {
+ if (listOptions == null && multiOptions == null) {
+ return Collections.emptyMap();
+ }
+ Map<String, List<String>> allValues = new TreeMap<>(
+ String.CASE_INSENSITIVE_ORDER);
+ if (multiOptions != null) {
+ allValues.putAll(multiOptions);
+ }
+ if (listOptions != null) {
+ allValues.putAll(listOptions);
+ }
+ return Collections.unmodifiableMap(allValues);
+ }
+
+ @Override
+ @SuppressWarnings("nls")
+ public String toString() {
+ return "HostEntry [options=" + options + ", multiOptions="
+ + multiOptions + ", listOptions=" + listOptions + "]";
+ }
+ }
+
+ private static class Replacer {
+ private final Map<Character, String> replacements = new HashMap<>();
+
+ public Replacer(String host, int port, String user,
+ String localUserName, File home) {
+ replacements.put(Character.valueOf('%'), "%"); //$NON-NLS-1$
+ replacements.put(Character.valueOf('d'), home.getPath());
+ replacements.put(Character.valueOf('h'), host);
+ String localhost = SystemReader.getInstance().getHostname();
+ replacements.put(Character.valueOf('l'), localhost);
+ int period = localhost.indexOf('.');
+ if (period > 0) {
+ localhost = localhost.substring(0, period);
+ }
+ replacements.put(Character.valueOf('L'), localhost);
+ replacements.put(Character.valueOf('n'), host);
+ replacements.put(Character.valueOf('p'), Integer.toString(port));
+ replacements.put(Character.valueOf('r'), user == null ? "" : user); //$NON-NLS-1$
+ replacements.put(Character.valueOf('u'), localUserName);
+ replacements.put(Character.valueOf('C'),
+ substitute("%l%h%p%r", "hlpr")); //$NON-NLS-1$ //$NON-NLS-2$
+ replacements.put(Character.valueOf('T'), "NONE"); //$NON-NLS-1$
+ }
+
+ public void update(char key, String value) {
+ replacements.put(Character.valueOf(key), value);
+ if ("lhpr".indexOf(key) >= 0) { //$NON-NLS-1$
+ replacements.put(Character.valueOf('C'),
+ substitute("%l%h%p%r", "hlpr")); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ }
+
+ public String substitute(String input, String allowed) {
+ if (input == null || input.length() <= 1
+ || input.indexOf('%') < 0) {
+ return input;
+ }
+ StringBuilder builder = new StringBuilder();
+ int start = 0;
+ int length = input.length();
+ while (start < length) {
+ int percent = input.indexOf('%', start);
+ if (percent < 0 || percent + 1 >= length) {
+ builder.append(input.substring(start));
+ break;
+ }
+ String replacement = null;
+ char ch = input.charAt(percent + 1);
+ if (ch == '%' || allowed.indexOf(ch) >= 0) {
+ replacement = replacements.get(Character.valueOf(ch));
+ }
+ if (replacement == null) {
+ builder.append(input.substring(start, percent + 2));
+ } else {
+ builder.append(input.substring(start, percent))
+ .append(replacement);
+ }
+ start = percent + 2;
+ }
+ return builder.toString();
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ @SuppressWarnings("nls")
+ public String toString() {
+ return "OpenSshConfig [home=" + home + ", configFile=" + configFile
+ + ", lastModified=" + lastModified + ", state=" + state + "]";
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/AsyncObjectLoaderQueue.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/AsyncObjectLoaderQueue.java
index b4ea0e907f..659c67c5ab 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/AsyncObjectLoaderQueue.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/AsyncObjectLoaderQueue.java
@@ -78,7 +78,7 @@ public interface AsyncObjectLoaderQueue<T extends ObjectId> extends
* @throws java.io.IOException
* the object store cannot be accessed.
*/
- public boolean next() throws MissingObjectException, IOException;
+ boolean next() throws MissingObjectException, IOException;
/**
* Get the current object, null if the implementation lost track.
@@ -87,14 +87,14 @@ public interface AsyncObjectLoaderQueue<T extends ObjectId> extends
* Implementations may for performance reasons discard the caller's
* ObjectId and provider their own through {@link #getObjectId()}.
*/
- public T getCurrent();
+ T getCurrent();
/**
* Get the ObjectId of the current object. Never null.
*
* @return the ObjectId of the current object. Never null.
*/
- public ObjectId getObjectId();
+ ObjectId getObjectId();
/**
* Obtain a loader to read the object.
@@ -115,5 +115,5 @@ public interface AsyncObjectLoaderQueue<T extends ObjectId> extends
* @throws java.io.IOException
* the object store cannot be accessed.
*/
- public ObjectLoader open() throws IOException;
+ ObjectLoader open() throws IOException;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/AsyncObjectSizeQueue.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/AsyncObjectSizeQueue.java
index 03efcd295e..6b8642f119 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/AsyncObjectSizeQueue.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/AsyncObjectSizeQueue.java
@@ -73,7 +73,7 @@ public interface AsyncObjectSizeQueue<T extends ObjectId> extends
* @throws java.io.IOException
* the object store cannot be accessed.
*/
- public boolean next() throws MissingObjectException, IOException;
+ boolean next() throws MissingObjectException, IOException;
/**
* <p>getCurrent.</p>
@@ -82,19 +82,19 @@ public interface AsyncObjectSizeQueue<T extends ObjectId> extends
* Implementations may for performance reasons discard the caller's
* ObjectId and provider their own through {@link #getObjectId()}.
*/
- public T getCurrent();
+ T getCurrent();
/**
* Get the ObjectId of the current object. Never null.
*
* @return the ObjectId of the current object. Never null.
*/
- public ObjectId getObjectId();
+ ObjectId getObjectId();
/**
* Get the size of the current object.
*
* @return the size of the current object.
*/
- public long getSize();
+ long getSize();
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/AsyncOperation.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/AsyncOperation.java
index 00555b0907..27b9c2038a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/AsyncOperation.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/AsyncOperation.java
@@ -68,10 +68,10 @@ public interface AsyncOperation {
* @return false if the task could not be cancelled, typically because it
* has already completed normally; true otherwise
*/
- public boolean cancel(boolean mayInterruptIfRunning);
+ boolean cancel(boolean mayInterruptIfRunning);
/**
* Release resources used by the operation, including cancellation.
*/
- public void release();
+ void release();
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BlobObjectChecker.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BlobObjectChecker.java
index 3fa3168327..7878351ce8 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BlobObjectChecker.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BlobObjectChecker.java
@@ -55,7 +55,7 @@ import org.eclipse.jgit.errors.CorruptObjectException;
*/
public interface BlobObjectChecker {
/** No-op implementation of {@link BlobObjectChecker}. */
- public static final BlobObjectChecker NULL_CHECKER =
+ BlobObjectChecker NULL_CHECKER =
new BlobObjectChecker() {
@Override
public void update(byte[] in, int p, int len) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CheckoutEntry.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CheckoutEntry.java
index cfc0cc86d1..84ff0a8936 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CheckoutEntry.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CheckoutEntry.java
@@ -12,13 +12,13 @@ public interface CheckoutEntry {
*
* @return the name of the branch before checkout
*/
- public abstract String getFromBranch();
+ String getFromBranch();
/**
* Get the name of the branch after checkout
*
* @return the name of the branch after checkout
*/
- public abstract String getToBranch();
+ String getToBranch();
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java
index b666f21d0b..4726975d07 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java
@@ -62,7 +62,6 @@ import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
-import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.events.ConfigChangedEvent;
import org.eclipse.jgit.events.ConfigChangedListener;
@@ -868,7 +867,7 @@ public class Config {
boolean lastWasMatch = false;
for (ConfigLine e : srcState.entryList) {
- if (e.match(section, subsection)) {
+ if (e.includedFrom == null && e.match(section, subsection)) {
// Skip this record, it's for the section we are removing.
lastWasMatch = true;
continue;
@@ -923,7 +922,7 @@ public class Config {
//
while (entryIndex < entries.size() && valueIndex < values.size()) {
final ConfigLine e = entries.get(entryIndex);
- if (e.match(section, subsection, name)) {
+ if (e.includedFrom == null && e.match(section, subsection, name)) {
entries.set(entryIndex, e.forValue(values.get(valueIndex++)));
insertPosition = entryIndex + 1;
}
@@ -935,7 +934,8 @@ public class Config {
if (valueIndex == values.size() && entryIndex < entries.size()) {
while (entryIndex < entries.size()) {
final ConfigLine e = entries.get(entryIndex++);
- if (e.match(section, subsection, name))
+ if (e.includedFrom == null
+ && e.match(section, subsection, name))
entries.remove(--entryIndex);
}
}
@@ -948,7 +948,8 @@ public class Config {
// is already a section available that matches. Insert
// after the last key of that section.
//
- insertPosition = findSectionEnd(entries, section, subsection);
+ insertPosition = findSectionEnd(entries, section, subsection,
+ true);
}
if (insertPosition < 0) {
// We didn't find any matching section header for this key,
@@ -985,9 +986,14 @@ public class Config {
}
private static int findSectionEnd(final List<ConfigLine> entries,
- final String section, final String subsection) {
+ final String section, final String subsection,
+ boolean skipIncludedLines) {
for (int i = 0; i < entries.size(); i++) {
ConfigLine e = entries.get(i);
+ if (e.includedFrom != null && skipIncludedLines) {
+ continue;
+ }
+
if (e.match(section, subsection, null)) {
i++;
while (i < entries.size()) {
@@ -1011,6 +1017,8 @@ public class Config {
public String toText() {
final StringBuilder out = new StringBuilder();
for (ConfigLine e : state.get().entryList) {
+ if (e.includedFrom != null)
+ continue;
if (e.prefix != null)
out.append(e.prefix);
if (e.section != null && e.name == null) {
@@ -1060,11 +1068,11 @@ public class Config {
* made to {@code this}.
*/
public void fromText(String text) throws ConfigInvalidException {
- state.set(newState(fromTextRecurse(text, 1)));
+ state.set(newState(fromTextRecurse(text, 1, null)));
}
- private List<ConfigLine> fromTextRecurse(String text, int depth)
- throws ConfigInvalidException {
+ private List<ConfigLine> fromTextRecurse(String text, int depth,
+ String includedFrom) throws ConfigInvalidException {
if (depth > MAX_DEPTH) {
throw new ConfigInvalidException(
JGitText.get().tooManyIncludeRecursions);
@@ -1073,6 +1081,7 @@ public class Config {
final StringReader in = new StringReader(text);
ConfigLine last = null;
ConfigLine e = new ConfigLine();
+ e.includedFrom = includedFrom;
for (;;) {
int input = in.read();
if (-1 == input) {
@@ -1088,7 +1097,7 @@ public class Config {
if (e.section != null)
last = e;
e = new ConfigLine();
-
+ e.includedFrom = includedFrom;
} else if (e.suffix != null) {
// Everything up until the end-of-line is in the suffix.
e.suffix += c;
@@ -1148,7 +1157,6 @@ public class Config {
* if something went wrong while reading the config
* @since 4.10
*/
- @Nullable
protected byte[] readIncludedConfig(String relPath)
throws ConfigInvalidException {
return null;
@@ -1173,7 +1181,7 @@ public class Config {
decoded = RawParseUtils.decode(bytes);
}
try {
- newEntries.addAll(fromTextRecurse(decoded, depth + 1));
+ newEntries.addAll(fromTextRecurse(decoded, depth + 1, line.value));
} catch (ConfigInvalidException e) {
throw new ConfigInvalidException(MessageFormat
.format(JGitText.get().cannotReadFile, line.value), e);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
index d4a0280da6..9763fb72fe 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
@@ -119,6 +119,36 @@ public final class ConfigConstants {
*/
public static final String CONFIG_FILTER_SECTION = "filter";
+ /**
+ * The "gpg" section
+ * @since 5.2
+ */
+ public static final String CONFIG_GPG_SECTION = "gpg";
+
+ /**
+ * The "format" key
+ * @since 5.2
+ */
+ public static final String CONFIG_KEY_FORMAT = "format";
+
+ /**
+ * The "signingKey" key
+ * @since 5.2
+ */
+ public static final String CONFIG_KEY_SIGNINGKEY = "signingKey";
+
+ /**
+ * The "commit" section
+ * @since 5.2
+ */
+ public static final String CONFIG_COMMIT_SECTION = "commit";
+
+ /**
+ * The "gpgSign" key
+ * @since 5.2
+ */
+ public static final String CONFIG_KEY_GPGSIGN = "gpgSign";
+
/** The "algorithm" key */
public static final String CONFIG_KEY_ALGORITHM = "algorithm";
@@ -432,4 +462,18 @@ public final class ConfigConstants {
* @since 4.11
*/
public static final String CONFIG_SECTION_LFS = "lfs";
+
+ /**
+ * The "i18n" section
+ *
+ * @since 5.2
+ */
+ public static final String CONFIG_SECTION_I18N = "i18n";
+
+ /**
+ * The "logOutputEncoding" key
+ *
+ * @since 5.2
+ */
+ public static final String CONFIG_KEY_LOG_OUTPUT_ENCODING = "logOutputEncoding";
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigLine.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigLine.java
index 937ba925c5..e623a8cebc 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigLine.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigLine.java
@@ -73,6 +73,9 @@ class ConfigLine {
/** The text content after entry. */
String suffix;
+ /** The source from which this line was included from. */
+ String includedFrom;
+
ConfigLine forValue(String newValue) {
final ConfigLine e = new ConfigLine();
e.prefix = prefix;
@@ -81,6 +84,7 @@ class ConfigLine {
e.name = name;
e.value = newValue;
e.suffix = suffix;
+ e.includedFrom = includedFrom;
return e;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java
index ed0055416b..4c55196961 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java
@@ -232,11 +232,17 @@ public final class Constants {
*
* @deprecated Use {@link java.nio.charset.StandardCharsets#UTF_8} directly
* instead.
- **/
+ */
@Deprecated
public static final Charset CHARSET;
- /** Native character encoding for commit messages, file names... */
+ /**
+ * Native character encoding for commit messages, file names...
+ *
+ * @deprecated Use {@link java.nio.charset.StandardCharsets#UTF_8} directly
+ * instead.
+ */
+ @Deprecated
public static final String CHARACTER_ENCODING;
/** Default main branch name */
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/DefaultTypedConfigGetter.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/DefaultTypedConfigGetter.java
index 891c7f23b4..6a66cf682f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/DefaultTypedConfigGetter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/DefaultTypedConfigGetter.java
@@ -293,7 +293,8 @@ public class DefaultTypedConfigGetter implements TypedConfigGetter {
/** {@inheritDoc} */
@Override
- public @NonNull List<RefSpec> getRefSpecs(Config config, String section,
+ @NonNull
+ public List<RefSpec> getRefSpecs(Config config, String section,
String subsection, String name) {
String[] values = config.getStringList(section, subsection, name);
List<RefSpec> result = new ArrayList<>(values.length);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgConfig.java
new file mode 100644
index 0000000000..a09bc00786
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgConfig.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2018, Salesforce.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.lib;
+
+/**
+ * Typed access to GPG related configuration options.
+ *
+ * @since 5.2
+ */
+public class GpgConfig {
+
+ /**
+ * Config values for gpg.format.
+ */
+ public enum GpgFormat implements Config.ConfigEnum {
+
+ /** Value for openpgp */
+ OPENPGP("openpgp"), //$NON-NLS-1$
+ /** Value for x509 */
+ X509("x509"); //$NON-NLS-1$
+
+ private final String configValue;
+
+ private GpgFormat(String configValue) {
+ this.configValue = configValue;
+ }
+
+ @Override
+ public boolean matchConfigValue(String s) {
+ return configValue.equals(s);
+ }
+
+ @Override
+ public String toConfigValue() {
+ return configValue;
+ }
+ }
+
+ private final Config config;
+
+ /**
+ * Create a new GPG config, which will read configuration from config.
+ *
+ * @param config
+ * the config to read from
+ */
+ public GpgConfig(Config config) {
+ this.config = config;
+ }
+
+ /**
+ * Retrieves the config value of gpg.format.
+ *
+ * @return the {@link org.eclipse.jgit.lib.GpgConfig.GpgFormat}
+ */
+ public GpgFormat getKeyFormat() {
+ return config.getEnum(GpgFormat.values(),
+ ConfigConstants.CONFIG_GPG_SECTION, null,
+ ConfigConstants.CONFIG_KEY_FORMAT, GpgFormat.OPENPGP);
+ }
+
+ /**
+ * Retrieves the config value of user.signingKey.
+ *
+ * @return the value of user.signingKey (may be <code>null</code>)
+ */
+ public String getSigningKey() {
+ return config.getString(ConfigConstants.CONFIG_USER_SECTION, null,
+ ConfigConstants.CONFIG_KEY_SIGNINGKEY);
+ }
+
+ /**
+ * Retrieves the config value of commit.gpgSign.
+ *
+ * @return the value of commit.gpgSign (defaults to <code>false</code>)
+ */
+ public boolean isSignCommits() {
+ return config.getBoolean(ConfigConstants.CONFIG_COMMIT_SECTION,
+ ConfigConstants.CONFIG_KEY_GPGSIGN, false);
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java
index 94b9ddc188..f37c310752 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java
@@ -47,7 +47,11 @@
package org.eclipse.jgit.lib;
+import java.io.File;
import java.io.IOException;
+import java.nio.file.DirectoryIteratorException;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Files;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
@@ -261,6 +265,8 @@ public class IndexDiff {
private Set<String> missing = new HashSet<>();
+ private Set<String> missingSubmodules = new HashSet<>();
+
private Set<String> modified = new HashSet<>();
private Set<String> untracked = new HashSet<>();
@@ -501,9 +507,15 @@ public class IndexDiff {
if (dirCacheIterator != null) {
if (workingTreeIterator == null) {
// in index, not in workdir => missing
- if (!isEntryGitLink(dirCacheIterator)
- || ignoreSubmoduleMode != IgnoreSubmoduleMode.ALL)
- missing.add(treeWalk.getPathString());
+ boolean isGitLink = isEntryGitLink(dirCacheIterator);
+ if (!isGitLink
+ || ignoreSubmoduleMode != IgnoreSubmoduleMode.ALL) {
+ String path = treeWalk.getPathString();
+ missing.add(path);
+ if (isGitLink) {
+ missingSubmodules.add(path);
+ }
+ }
} else {
if (workingTreeIterator.isModified(
dirCacheIterator.getDirCacheEntry(), true,
@@ -543,8 +555,8 @@ public class IndexDiff {
smw.getPath()), e);
}
try (Repository subRepo = smw.getRepository()) {
+ String subRepoPath = smw.getPath();
if (subRepo != null) {
- String subRepoPath = smw.getPath();
ObjectId subHead = subRepo.resolve("HEAD"); //$NON-NLS-1$
if (subHead != null
&& !subHead.equals(smw.getObjectId())) {
@@ -573,6 +585,21 @@ public class IndexDiff {
recordFileMode(subRepoPath, FileMode.GITLINK);
}
}
+ } else if (missingSubmodules.remove(subRepoPath)) {
+ // If the directory is there and empty but the submodule
+ // repository in .git/modules doesn't exist yet it isn't
+ // "missing".
+ File gitDir = new File(
+ new File(repository.getDirectory(),
+ Constants.MODULES),
+ subRepoPath);
+ if (!gitDir.isDirectory()) {
+ File dir = SubmoduleWalk.getSubmoduleDirectory(
+ repository, subRepoPath);
+ if (dir.isDirectory() && !hasFiles(dir)) {
+ missing.remove(subRepoPath);
+ }
+ }
}
}
}
@@ -592,6 +619,15 @@ public class IndexDiff {
return true;
}
+ private boolean hasFiles(File directory) {
+ try (DirectoryStream<java.nio.file.Path> dir = Files
+ .newDirectoryStream(directory.toPath())) {
+ return dir.iterator().hasNext();
+ } catch (DirectoryIteratorException | IOException e) {
+ return false;
+ }
+ }
+
private void recordFileMode(String path, FileMode mode) {
Set<String> values = fileModes.get(mode);
if (path != null) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectChecker.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectChecker.java
index d37fb21c93..127f019c46 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectChecker.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectChecker.java
@@ -109,7 +109,8 @@ import org.eclipse.jgit.util.StringUtils;
* the caller can provide both of these validations on its own.
* <p>
* Instances of this class are not thread safe, but they may be reused to
- * perform multiple object validations.
+ * perform multiple object validations, calling {@link #reset()} between them to
+ * clear the internal state (e.g. {@link #getGitsubmodules()})
*/
public class ObjectChecker {
/** Header "tree " */
@@ -173,6 +174,13 @@ public class ObjectChecker {
/***/ BAD_TIMEZONE,
/***/ MISSING_EMAIL,
/***/ MISSING_SPACE_BEFORE_DATE,
+ /** @since 5.2 */ GITMODULES_BLOB,
+ /** @since 5.2 */ GITMODULES_LARGE,
+ /** @since 5.2 */ GITMODULES_NAME,
+ /** @since 5.2 */ GITMODULES_PARSE,
+ /** @since 5.2 */ GITMODULES_PATH,
+ /** @since 5.2 */ GITMODULES_SYMLINK,
+ /** @since 5.2 */ GITMODULES_URL,
/***/ UNKNOWN_TYPE,
// These are unique to JGit.
@@ -1251,4 +1259,19 @@ public class ObjectChecker {
public List<GitmoduleEntry> getGitsubmodules() {
return gitsubmodules;
}
+
+ /**
+ * Reset the invocation-specific state from this instance. Specifically this
+ * clears the list of .gitmodules files encountered (see
+ * {@link #getGitsubmodules()})
+ *
+ * Configurations like errors to filter, skip lists or the specified O.S.
+ * (set via {@link #setSafeForMacOS(boolean)} or
+ * {@link #setSafeForWindows(boolean)}) are NOT cleared.
+ *
+ * @since 5.2
+ */
+ public void reset() {
+ gitsubmodules.clear();
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ProgressMonitor.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ProgressMonitor.java
index d81ee45c9e..9d8d71a0be 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ProgressMonitor.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ProgressMonitor.java
@@ -49,7 +49,7 @@ package org.eclipse.jgit.lib;
*/
public interface ProgressMonitor {
/** Constant indicating the total work units cannot be predicted. */
- public static final int UNKNOWN = 0;
+ int UNKNOWN = 0;
/**
* Advise the monitor of the total number of subtasks.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Ref.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Ref.java
index b000558944..faabbf892f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Ref.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Ref.java
@@ -61,7 +61,7 @@ import org.eclipse.jgit.annotations.Nullable;
*/
public interface Ref {
/** Location where a {@link Ref} is stored. */
- public static enum Storage {
+ enum Storage {
/**
* The ref does not exist yet, updating it may create it.
* <p>
@@ -131,7 +131,7 @@ public interface Ref {
* @return name of this ref.
*/
@NonNull
- public String getName();
+ String getName();
/**
* Test if this reference is a symbolic reference.
@@ -144,7 +144,7 @@ public interface Ref {
* @return true if this is a symbolic reference; false if this reference
* contains its own ObjectId.
*/
- public abstract boolean isSymbolic();
+ boolean isSymbolic();
/**
* Traverse target references until {@link #isSymbolic()} is false.
@@ -163,7 +163,7 @@ public interface Ref {
* @return the reference that actually stores the ObjectId value.
*/
@NonNull
- public abstract Ref getLeaf();
+ Ref getLeaf();
/**
* Get the reference this reference points to, or {@code this}.
@@ -178,7 +178,7 @@ public interface Ref {
* @return the target reference, or {@code this}.
*/
@NonNull
- public abstract Ref getTarget();
+ Ref getTarget();
/**
* Cached value of this ref.
@@ -188,7 +188,7 @@ public interface Ref {
* symbolic ref pointing to an unborn branch.
*/
@Nullable
- public abstract ObjectId getObjectId();
+ ObjectId getObjectId();
/**
* Cached value of <code>ref^{}</code> (the ref peeled to commit).
@@ -198,14 +198,14 @@ public interface Ref {
* does not refer to an annotated tag.
*/
@Nullable
- public abstract ObjectId getPeeledObjectId();
+ ObjectId getPeeledObjectId();
/**
* Whether the Ref represents a peeled tag.
*
* @return whether the Ref represents a peeled tag.
*/
- public abstract boolean isPeeled();
+ boolean isPeeled();
/**
* How was this ref obtained?
@@ -216,5 +216,5 @@ public interface Ref {
* @return type of ref.
*/
@NonNull
- public abstract Storage getStorage();
+ Storage getStorage();
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java
index 3170787dd9..68929b4220 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java
@@ -415,6 +415,31 @@ public abstract class RefDatabase {
}
/**
+ * Returns refs whose names start with one of the given prefixes.
+ * <p>
+ * The default implementation uses {@link #getRefsByPrefix(String)}.
+ * Implementors of {@link RefDatabase} should override this method directly
+ * if a better implementation is possible.
+ *
+ * @param prefixes
+ * strings that names of refs should start with.
+ * @return immutable list of refs whose names start with one of
+ * {@code prefixes}. Refs can be unsorted and may contain duplicates
+ * if the prefixes overlap.
+ * @throws java.io.IOException
+ * the reference space cannot be accessed.
+ * @since 5.2
+ */
+ @NonNull
+ public List<Ref> getRefsByPrefix(String... prefixes) throws IOException {
+ List<Ref> result = new ArrayList<>();
+ for (String prefix : prefixes) {
+ result.addAll(getRefsByPrefix(prefix));
+ }
+ return Collections.unmodifiableList(result);
+ }
+
+ /**
* Check if any refs exist in the ref database.
* <p>
* This uses the same definition of refs as {@link #getRefs()}. In
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefUpdate.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefUpdate.java
index 5cd593ed5e..fc3ea8467a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefUpdate.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefUpdate.java
@@ -823,7 +823,7 @@ public abstract class RefUpdate {
* Handle the abstraction of storing a ref update. This is because both
* updating and deleting of a ref have merge testing in common.
*/
- private abstract class Store {
+ private static abstract class Store {
abstract Result execute(Result status) throws IOException;
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ReflogEntry.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ReflogEntry.java
index 51f2ea0ab7..824bbc4201 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ReflogEntry.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ReflogEntry.java
@@ -57,7 +57,7 @@ public interface ReflogEntry {
*
* @since 4.9
*/
- public static final String PREFIX_CREATED = "created"; //$NON-NLS-1$
+ String PREFIX_CREATED = "created"; //$NON-NLS-1$
/**
* Prefix used in reflog messages when the ref was updated with a fast
@@ -69,7 +69,7 @@ public interface ReflogEntry {
*
* @since 4.9
*/
- public static final String PREFIX_FAST_FORWARD = "fast-forward"; //$NON-NLS-1$
+ String PREFIX_FAST_FORWARD = "fast-forward"; //$NON-NLS-1$
/**
* Prefix used in reflog messages when the ref was force updated.
@@ -80,35 +80,35 @@ public interface ReflogEntry {
*
* @since 4.9
*/
- public static final String PREFIX_FORCED_UPDATE = "forced-update"; //$NON-NLS-1$
+ String PREFIX_FORCED_UPDATE = "forced-update"; //$NON-NLS-1$
/**
* Get the commit id before the change
*
* @return the commit id before the change
*/
- public abstract ObjectId getOldId();
+ ObjectId getOldId();
/**
* Get the commit id after the change
*
* @return the commit id after the change
*/
- public abstract ObjectId getNewId();
+ ObjectId getNewId();
/**
* Get user performing the change
*
* @return user performing the change
*/
- public abstract PersonIdent getWho();
+ PersonIdent getWho();
/**
* Get textual description of the change
*
* @return textual description of the change
*/
- public abstract String getComment();
+ String getComment();
/**
* Parse checkout
@@ -117,6 +117,6 @@ public interface ReflogEntry {
* information about a branch switch, or null if the entry is not a
* checkout
*/
- public abstract CheckoutEntry parseCheckout();
+ CheckoutEntry parseCheckout();
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ReflogReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ReflogReader.java
index f97b07e08c..4f104d2d7c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ReflogReader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ReflogReader.java
@@ -59,7 +59,7 @@ public interface ReflogReader {
* @return the latest reflog entry, or null if no log
* @throws java.io.IOException
*/
- public abstract ReflogEntry getLastEntry() throws IOException;
+ ReflogEntry getLastEntry() throws IOException;
/**
* Get all reflog entries in reverse order
@@ -67,7 +67,7 @@ public interface ReflogReader {
* @return all reflog entries in reverse order
* @throws java.io.IOException
*/
- public abstract List<ReflogEntry> getReverseEntries() throws IOException;
+ List<ReflogEntry> getReverseEntries() throws IOException;
/**
* Get specific entry in the reflog relative to the last entry which is
@@ -77,7 +77,7 @@ public interface ReflogReader {
* @return reflog entry or null if not found
* @throws java.io.IOException
*/
- public abstract ReflogEntry getReverseEntry(int number) throws IOException;
+ ReflogEntry getReverseEntry(int number) throws IOException;
/**
* Get all reflog entries in reverse order
@@ -87,7 +87,5 @@ public interface ReflogReader {
* @return all reflog entries in reverse order
* @throws java.io.IOException
*/
- public abstract List<ReflogEntry> getReverseEntries(int max)
- throws IOException;
-
+ List<ReflogEntry> getReverseEntries(int max) throws IOException;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
index d73c05e243..77d268a3bd 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
@@ -49,6 +49,7 @@
package org.eclipse.jgit.lib;
import static org.eclipse.jgit.lib.Constants.LOCK_SUFFIX;
+import static java.nio.charset.StandardCharsets.UTF_8;
import java.io.BufferedOutputStream;
import java.io.File;
@@ -1965,7 +1966,7 @@ public abstract class Repository implements AutoCloseable {
private void writeCommitMsg(File msgFile, String msg) throws IOException {
if (msg != null) {
try (FileOutputStream fos = new FileOutputStream(msgFile)) {
- fos.write(msg.getBytes(Constants.CHARACTER_ENCODING));
+ fos.write(msg.getBytes(UTF_8));
}
} else {
FileUtils.delete(msgFile, FileUtils.SKIP_MISSING);
@@ -1980,7 +1981,6 @@ public abstract class Repository implements AutoCloseable {
* empty
* @throws IOException
*/
- @Nullable
private byte[] readGitDirectoryFile(String filename) throws IOException {
File file = new File(getDirectory(), filename);
try {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeFormatter.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeFormatter.java
index 036917e62a..479670873d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeFormatter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeFormatter.java
@@ -45,6 +45,7 @@ package org.eclipse.jgit.merge;
import java.io.IOException;
import java.io.OutputStream;
+import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
@@ -63,7 +64,7 @@ public class MergeFormatter {
* that are LF-separated lines.
*
* @param out
- * the outputstream where to write the textual presentation
+ * the output stream where to write the textual presentation
* @param res
* the merge result which should be presented
* @param seqName
@@ -72,13 +73,44 @@ public class MergeFormatter {
* " or "&gt;&gt;&gt;&gt;&gt;&gt;&gt; " conflict markers. The
* names for the sequences are given in this list
* @param charsetName
- * the name of the characterSet used when writing conflict
+ * the name of the character set used when writing conflict
* metadata
* @throws java.io.IOException
+ * @deprecated Use
+ * {@link #formatMerge(OutputStream, MergeResult, List, Charset)}
+ * instead.
*/
+ @Deprecated
public void formatMerge(OutputStream out, MergeResult<RawText> res,
List<String> seqName, String charsetName) throws IOException {
- new MergeFormatterPass(out, res, seqName, charsetName).formatMerge();
+ formatMerge(out, res, seqName, Charset.forName(charsetName));
+ }
+
+ /**
+ * Formats the results of a merge of {@link org.eclipse.jgit.diff.RawText}
+ * objects in a Git conformant way. This method also assumes that the
+ * {@link org.eclipse.jgit.diff.RawText} objects being merged are line
+ * oriented files which use LF as delimiter. This method will also use LF to
+ * separate chunks and conflict metadata, therefore it fits only to texts
+ * that are LF-separated lines.
+ *
+ * @param out
+ * the output stream where to write the textual presentation
+ * @param res
+ * the merge result which should be presented
+ * @param seqName
+ * When a conflict is reported each conflicting range will get a
+ * name. This name is following the "&lt;&lt;&lt;&lt;&lt;&lt;&lt;
+ * " or "&gt;&gt;&gt;&gt;&gt;&gt;&gt; " conflict markers. The
+ * names for the sequences are given in this list
+ * @param charset
+ * the character set used when writing conflict metadata
+ * @throws java.io.IOException
+ * @since 5.2
+ */
+ public void formatMerge(OutputStream out, MergeResult<RawText> res,
+ List<String> seqName, Charset charset) throws IOException {
+ new MergeFormatterPass(out, res, seqName, charset).formatMerge();
}
/**
@@ -100,17 +132,51 @@ public class MergeFormatter {
* @param theirsName
* the name ranges from theirs should get
* @param charsetName
- * the name of the characterSet used when writing conflict
+ * the name of the character set used when writing conflict
* metadata
* @throws java.io.IOException
+ * @deprecated use
+ * {@link #formatMerge(OutputStream, MergeResult, String, String, String, Charset)}
+ * instead.
*/
- @SuppressWarnings("unchecked")
+ @Deprecated
public void formatMerge(OutputStream out, MergeResult res, String baseName,
String oursName, String theirsName, String charsetName) throws IOException {
+ formatMerge(out, res, baseName, oursName, theirsName,
+ Charset.forName(charsetName));
+ }
+
+ /**
+ * Formats the results of a merge of exactly two
+ * {@link org.eclipse.jgit.diff.RawText} objects in a Git conformant way.
+ * This convenience method accepts the names for the three sequences (base
+ * and the two merged sequences) as explicit parameters and doesn't require
+ * the caller to specify a List
+ *
+ * @param out
+ * the {@link java.io.OutputStream} where to write the textual
+ * presentation
+ * @param res
+ * the merge result which should be presented
+ * @param baseName
+ * the name ranges from the base should get
+ * @param oursName
+ * the name ranges from ours should get
+ * @param theirsName
+ * the name ranges from theirs should get
+ * @param charset
+ * the character set used when writing conflict metadata
+ * @throws java.io.IOException
+ * @since 5.2
+ */
+ @SuppressWarnings("unchecked")
+ public void formatMerge(OutputStream out, MergeResult res, String baseName,
+ String oursName, String theirsName, Charset charset)
+ throws IOException {
List<String> names = new ArrayList<>(3);
names.add(baseName);
names.add(oursName);
names.add(theirsName);
- formatMerge(out, res, names, charsetName);
+ formatMerge(out, res, names, charset);
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeFormatterPass.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeFormatterPass.java
index 060f06884a..e1a8d3110e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeFormatterPass.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeFormatterPass.java
@@ -46,6 +46,7 @@ package org.eclipse.jgit.merge;
import java.io.IOException;
import java.io.OutputStream;
+import java.nio.charset.Charset;
import java.util.List;
import org.eclipse.jgit.diff.RawText;
@@ -59,19 +60,33 @@ class MergeFormatterPass {
private final List<String> seqName;
- private final String charsetName;
+ private final Charset charset;
private final boolean threeWayMerge;
private String lastConflictingName; // is set to non-null whenever we are in
// a conflict
- MergeFormatterPass(OutputStream out, MergeResult<RawText> res, List<String> seqName,
- String charsetName) {
+ /**
+ * @param out
+ * the {@link java.io.OutputStream} where to write the textual
+ * presentation
+ * @param res
+ * the merge result which should be presented
+ * @param seqName
+ * When a conflict is reported each conflicting range will get a
+ * name. This name is following the "&lt;&lt;&lt;&lt;&lt;&lt;&lt;
+ * " or "&gt;&gt;&gt;&gt;&gt;&gt;&gt; " conflict markers. The
+ * names for the sequences are given in this list
+ * @param charset
+ * the character set used when writing conflict metadata
+ */
+ MergeFormatterPass(OutputStream out, MergeResult<RawText> res,
+ List<String> seqName, Charset charset) {
this.out = new EolAwareOutputStream(out);
this.res = res;
this.seqName = seqName;
- this.charsetName = charsetName;
+ this.charset = charset;
this.threeWayMerge = (res.getSequences().size() == 3);
}
@@ -133,7 +148,7 @@ class MergeFormatterPass {
private void writeln(String s) throws IOException {
out.beginln();
- out.write((s + "\n").getBytes(charsetName)); //$NON-NLS-1$
+ out.write((s + "\n").getBytes(charset)); //$NON-NLS-1$
}
private void writeLine(RawText seq, int i) throws IOException {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
index f60c95f647..412d9bba73 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
@@ -46,10 +46,10 @@
*/
package org.eclipse.jgit.merge;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static org.eclipse.jgit.diff.DiffAlgorithm.SupportedAlgorithm.HISTOGRAM;
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_DIFF_SECTION;
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_ALGORITHM;
-import static org.eclipse.jgit.lib.Constants.CHARACTER_ENCODING;
import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
import java.io.BufferedOutputStream;
@@ -1026,7 +1026,7 @@ public class ResolveMerger extends ThreeWayMerger {
db != null ? nonNullRepo().getDirectory() : null, inCoreLimit);
try {
new MergeFormatter().formatMerge(buf, result,
- Arrays.asList(commitNames), CHARACTER_ENCODING);
+ Arrays.asList(commitNames), UTF_8);
buf.close();
} catch (IOException e) {
buf.destroy();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/AsyncRevObjectQueue.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/AsyncRevObjectQueue.java
index d263184622..98654f14c2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/AsyncRevObjectQueue.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/AsyncRevObjectQueue.java
@@ -66,5 +66,5 @@ public interface AsyncRevObjectQueue extends AsyncOperation {
* @throws java.io.IOException
* the object store cannot be accessed.
*/
- public RevObject next() throws MissingObjectException, IOException;
+ RevObject next() throws MissingObjectException, IOException;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthGenerator.java
index eaec305b47..5154920393 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthGenerator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthGenerator.java
@@ -48,6 +48,7 @@ import java.io.IOException;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.lib.ObjectId;
/**
* Only produce commits which are below a specified depth.
@@ -59,6 +60,8 @@ class DepthGenerator extends Generator {
private final int depth;
+ private final int deepenSince;
+
private final RevWalk walk;
/**
@@ -79,6 +82,11 @@ class DepthGenerator extends Generator {
private final RevFlag REINTERESTING;
/**
+ * Commits reachable from commits that the client specified using --shallow-exclude.
+ */
+ private final RevFlag DEEPEN_NOT;
+
+ /**
* @param w
* @param s Parent generator
* @throws MissingObjectException
@@ -91,8 +99,10 @@ class DepthGenerator extends Generator {
walk = (RevWalk)w;
this.depth = w.getDepth();
+ this.deepenSince = w.getDeepenSince();
this.UNSHALLOW = w.getUnshallowFlag();
this.REINTERESTING = w.getReinterestingFlag();
+ this.DEEPEN_NOT = w.getDeepenNotFlag();
s.shareFreeList(pending);
@@ -105,6 +115,37 @@ class DepthGenerator extends Generator {
if (((DepthWalk.Commit) c).getDepth() == 0)
pending.add(c);
}
+
+ // Mark DEEPEN_NOT on all deepen-not commits and their ancestors.
+ // TODO(jonathantanmy): This implementation is somewhat
+ // inefficient in that any "deepen-not <ref>" in the request
+ // results in all commits reachable from that ref being parsed
+ // and marked, even if the commit topology is such that it is
+ // not necessary.
+ for (ObjectId oid : w.getDeepenNots()) {
+ RevCommit c;
+ try {
+ c = walk.parseCommit(oid);
+ } catch (IncorrectObjectTypeException notCommit) {
+ // The C Git implementation silently tolerates
+ // non-commits, so do the same here.
+ continue;
+ }
+
+ FIFORevQueue queue = new FIFORevQueue();
+ queue.add(c);
+ while ((c = queue.next()) != null) {
+ if (c.has(DEEPEN_NOT)) {
+ continue;
+ }
+
+ walk.parseHeaders(c);
+ c.add(DEEPEN_NOT);
+ for (RevCommit p : c.getParents()) {
+ queue.add(p);
+ }
+ }
+ }
}
@Override
@@ -132,6 +173,14 @@ class DepthGenerator extends Generator {
if ((c.flags & RevWalk.PARSED) == 0)
c.parseHeaders(walk);
+ if (c.getCommitTime() < deepenSince) {
+ continue;
+ }
+
+ if (c.has(DEEPEN_NOT)) {
+ continue;
+ }
+
int newDepth = c.depth + 1;
for (RevCommit p : c.parents) {
@@ -142,12 +191,29 @@ class DepthGenerator extends Generator {
// this depth is guaranteed to be the smallest value that
// any path could produce.
if (dp.depth == -1) {
+ boolean failsDeepenSince = false;
+ if (deepenSince != 0) {
+ if ((p.flags & RevWalk.PARSED) == 0) {
+ p.parseHeaders(walk);
+ }
+ failsDeepenSince =
+ p.getCommitTime() < deepenSince;
+ }
+
dp.depth = newDepth;
- // If the parent is not too deep, add it to the queue
- // so that we can produce it later
- if (newDepth <= depth)
+ // If the parent is not too deep and was not excluded, add
+ // it to the queue so that we can produce it later
+ if (newDepth <= depth && !failsDeepenSince &&
+ !p.has(DEEPEN_NOT)) {
pending.add(p);
+ } else {
+ dp.makesChildBoundary = true;
+ }
+ }
+
+ if (dp.makesChildBoundary) {
+ c.isBoundary = true;
}
// If the current commit has become unshallowed, everything
@@ -160,8 +226,7 @@ class DepthGenerator extends Generator {
}
}
- // Produce all commits less than the depth cutoff
- boolean produce = c.depth <= depth;
+ boolean produce = true;
// Unshallow commits are uninteresting, but still need to be sent
// up to the PackWriter so that it will exclude objects correctly.
@@ -169,6 +234,10 @@ class DepthGenerator extends Generator {
if ((c.flags & RevWalk.UNINTERESTING) != 0 && !c.has(UNSHALLOW))
produce = false;
+ if (c.getCommitTime() < deepenSince) {
+ produce = false;
+ }
+
if (produce)
return c;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthWalk.java
index 06a5272b98..0201f0b602 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthWalk.java
@@ -45,10 +45,14 @@
package org.eclipse.jgit.revwalk;
import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.Repository;
@@ -61,7 +65,24 @@ public interface DepthWalk {
*
* @return Depth to filter to.
*/
- public int getDepth();
+ int getDepth();
+
+ /**
+ * @return the deepen-since value; if not 0, this walk only returns commits
+ * whose commit time is at or after this limit
+ * @since 5.2
+ */
+ default int getDeepenSince() {
+ return 0;
+ }
+
+ /**
+ * @return the objects specified by the client using --shallow-exclude
+ * @since 5.2
+ */
+ default List<ObjectId> getDeepenNots() {
+ return Collections.emptyList();
+ }
/** @return flag marking commits that should become unshallow. */
/**
@@ -69,26 +90,49 @@ public interface DepthWalk {
*
* @return flag marking commits that should become unshallow.
*/
- public RevFlag getUnshallowFlag();
+ RevFlag getUnshallowFlag();
/**
* Get flag marking commits that are interesting again.
*
* @return flag marking commits that are interesting again.
*/
- public RevFlag getReinterestingFlag();
+ RevFlag getReinterestingFlag();
+
+ /**
+ * @return flag marking commits that are to be excluded because of --shallow-exclude
+ * @since 5.2
+ */
+ RevFlag getDeepenNotFlag();
/** RevCommit with a depth (in commits) from a root. */
public static class Commit extends RevCommit {
/** Depth of this commit in the graph, via shortest path. */
int depth;
+ boolean isBoundary;
+
+ /**
+ * True if this commit was excluded due to a shallow fetch
+ * setting. All its children are thus boundary commits.
+ */
+ boolean makesChildBoundary;
+
/** @return depth of this commit, as found by the shortest path. */
public int getDepth() {
return depth;
}
/**
+ * @return true if at least one of this commit's parents was excluded
+ * due to a shallow fetch setting, false otherwise
+ * @since 5.2
+ */
+ public boolean isBoundary() {
+ return isBoundary;
+ }
+
+ /**
* Initialize a new commit.
*
* @param id
@@ -104,10 +148,16 @@ public interface DepthWalk {
public class RevWalk extends org.eclipse.jgit.revwalk.RevWalk implements DepthWalk {
private final int depth;
+ private int deepenSince;
+
+ private List<ObjectId> deepenNots;
+
private final RevFlag UNSHALLOW;
private final RevFlag REINTERESTING;
+ private final RevFlag DEEPEN_NOT;
+
/**
* @param repo Repository to walk
* @param depth Maximum depth to return
@@ -116,8 +166,10 @@ public interface DepthWalk {
super(repo);
this.depth = depth;
+ this.deepenNots = Collections.emptyList();
this.UNSHALLOW = newFlag("UNSHALLOW"); //$NON-NLS-1$
this.REINTERESTING = newFlag("REINTERESTING"); //$NON-NLS-1$
+ this.DEEPEN_NOT = newFlag("DEEPEN_NOT"); //$NON-NLS-1$
}
/**
@@ -128,8 +180,10 @@ public interface DepthWalk {
super(or);
this.depth = depth;
+ this.deepenNots = Collections.emptyList();
this.UNSHALLOW = newFlag("UNSHALLOW"); //$NON-NLS-1$
this.REINTERESTING = newFlag("REINTERESTING"); //$NON-NLS-1$
+ this.DEEPEN_NOT = newFlag("DEEPEN_NOT"); //$NON-NLS-1$
}
/**
@@ -159,6 +213,39 @@ public interface DepthWalk {
}
@Override
+ public int getDeepenSince() {
+ return deepenSince;
+ }
+
+ /**
+ * Sets the deepen-since value.
+ *
+ * @param limit
+ * new deepen-since value
+ * @since 5.2
+ */
+ public void setDeepenSince(int limit) {
+ deepenSince = limit;
+ }
+
+ @Override
+ public List<ObjectId> getDeepenNots() {
+ return deepenNots;
+ }
+
+ /**
+ * Mark objects that the client specified using
+ * --shallow-exclude. Objects that are not commits have no
+ * effect.
+ *
+ * @param deepenNots specified objects
+ * @since 5.2
+ */
+ public void setDeepenNots(List<ObjectId> deepenNots) {
+ this.deepenNots = Objects.requireNonNull(deepenNots);
+ }
+
+ @Override
public RevFlag getUnshallowFlag() {
return UNSHALLOW;
}
@@ -168,12 +255,19 @@ public interface DepthWalk {
return REINTERESTING;
}
+ @Override
+ public RevFlag getDeepenNotFlag() {
+ return DEEPEN_NOT;
+ }
+
/**
* @since 4.5
*/
@Override
public ObjectWalk toObjectWalkWithSameObjects() {
ObjectWalk ow = new ObjectWalk(reader, depth);
+ ow.deepenSince = deepenSince;
+ ow.deepenNots = deepenNots;
ow.objects = objects;
ow.freeFlags = freeFlags;
return ow;
@@ -184,10 +278,16 @@ public interface DepthWalk {
public class ObjectWalk extends org.eclipse.jgit.revwalk.ObjectWalk implements DepthWalk {
private final int depth;
+ private int deepenSince;
+
+ private List<ObjectId> deepenNots;
+
private final RevFlag UNSHALLOW;
private final RevFlag REINTERESTING;
+ private final RevFlag DEEPEN_NOT;
+
/**
* @param repo Repository to walk
* @param depth Maximum depth to return
@@ -196,8 +296,10 @@ public interface DepthWalk {
super(repo);
this.depth = depth;
+ this.deepenNots = Collections.emptyList();
this.UNSHALLOW = newFlag("UNSHALLOW"); //$NON-NLS-1$
this.REINTERESTING = newFlag("REINTERESTING"); //$NON-NLS-1$
+ this.DEEPEN_NOT = newFlag("DEEPEN_NOT"); //$NON-NLS-1$
}
/**
@@ -208,8 +310,10 @@ public interface DepthWalk {
super(or);
this.depth = depth;
+ this.deepenNots = Collections.emptyList();
this.UNSHALLOW = newFlag("UNSHALLOW"); //$NON-NLS-1$
this.REINTERESTING = newFlag("REINTERESTING"); //$NON-NLS-1$
+ this.DEEPEN_NOT = newFlag("DEEPEN_NOT"); //$NON-NLS-1$
}
/**
@@ -263,6 +367,16 @@ public interface DepthWalk {
}
@Override
+ public int getDeepenSince() {
+ return deepenSince;
+ }
+
+ @Override
+ public List<ObjectId> getDeepenNots() {
+ return deepenNots;
+ }
+
+ @Override
public RevFlag getUnshallowFlag() {
return UNSHALLOW;
}
@@ -271,5 +385,10 @@ public interface DepthWalk {
public RevFlag getReinterestingFlag() {
return REINTERESTING;
}
+
+ @Override
+ public RevFlag getDeepenNotFlag() {
+ return DEEPEN_NOT;
+ }
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevCommit.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevCommit.java
index 86ecd8eaee..af4ec1f00b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevCommit.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevCommit.java
@@ -407,7 +407,7 @@ public class RevCommit extends RevObject {
* @return contents of the gpg signature; null if the commit was not signed.
* @since 5.1
*/
- public final @Nullable byte[] getRawGpgSignature() {
+ public final byte[] getRawGpgSignature() {
final byte[] raw = buffer;
final byte[] header = {'g', 'p', 'g', 's', 'i', 'g'};
final int start = RawParseUtils.headerStart(header, raw, 0);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java
index 4d555d2178..400ea33c21 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java
@@ -54,6 +54,7 @@ import java.util.Iterator;
import java.util.List;
import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.errors.CorruptObjectException;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.LargeObjectException;
@@ -1336,6 +1337,22 @@ public class RevWalk implements Iterable<RevCommit>, AutoCloseable {
}
/**
+ * Like {@link #next()}, but if a checked exception is thrown during the
+ * walk it is rethrown as a {@link RevWalkException}.
+ *
+ * @throws RevWalkException if an {@link IOException} was thrown.
+ * @return next most recent commit; null if traversal is over.
+ */
+ @Nullable
+ private RevCommit nextForIterator() {
+ try {
+ return next();
+ } catch (IOException e) {
+ throw new RevWalkException(e);
+ }
+ }
+
+ /**
* {@inheritDoc}
* <p>
* Returns an Iterator over the commits of this walker.
@@ -1353,16 +1370,7 @@ public class RevWalk implements Iterable<RevCommit>, AutoCloseable {
*/
@Override
public Iterator<RevCommit> iterator() {
- final RevCommit first;
- try {
- first = RevWalk.this.next();
- } catch (MissingObjectException e) {
- throw new RevWalkException(e);
- } catch (IncorrectObjectTypeException e) {
- throw new RevWalkException(e);
- } catch (IOException e) {
- throw new RevWalkException(e);
- }
+ RevCommit first = nextForIterator();
return new Iterator<RevCommit>() {
RevCommit next = first;
@@ -1374,17 +1382,9 @@ public class RevWalk implements Iterable<RevCommit>, AutoCloseable {
@Override
public RevCommit next() {
- try {
- final RevCommit r = next;
- next = RevWalk.this.next();
- return r;
- } catch (MissingObjectException e) {
- throw new RevWalkException(e);
- } catch (IncorrectObjectTypeException e) {
- throw new RevWalkException(e);
- } catch (IOException e) {
- throw new RevWalkException(e);
- }
+ RevCommit r = next;
+ next = nextForIterator();
+ return r;
}
@Override
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java
index 93b3baa61f..2b31ebd8e3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java
@@ -57,7 +57,6 @@ import java.io.FileNotFoundException;
import java.io.IOException;
import java.text.MessageFormat;
-import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.errors.LockFailedException;
import org.eclipse.jgit.internal.JGitText;
@@ -281,7 +280,6 @@ public class FileBasedConfig extends StoredConfig {
* @since 4.10
*/
@Override
- @Nullable
protected byte[] readIncludedConfig(String relPath)
throws ConfigInvalidException {
final File file;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/AdvertiseRefsHook.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/AdvertiseRefsHook.java
index ed05c733f3..72b4255df9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/AdvertiseRefsHook.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/AdvertiseRefsHook.java
@@ -55,7 +55,7 @@ public interface AdvertiseRefsHook {
* {@link UploadPack#setAdvertisedRefs(java.util.Map)} and
* {@link BaseReceivePack#setAdvertisedRefs(java.util.Map,java.util.Set)}.
*/
- public static final AdvertiseRefsHook DEFAULT = new AdvertiseRefsHook() {
+ AdvertiseRefsHook DEFAULT = new AdvertiseRefsHook() {
@Override
public void advertiseRefs(UploadPack uploadPack) {
// Do nothing.
@@ -77,7 +77,7 @@ public interface AdvertiseRefsHook {
* @throws org.eclipse.jgit.transport.ServiceMayNotContinueException
* abort; the message will be sent to the user.
*/
- public void advertiseRefs(UploadPack uploadPack)
+ void advertiseRefs(UploadPack uploadPack)
throws ServiceMayNotContinueException;
/**
@@ -90,6 +90,6 @@ public interface AdvertiseRefsHook {
* @throws org.eclipse.jgit.transport.ServiceMayNotContinueException
* abort; the message will be sent to the user.
*/
- public void advertiseRefs(BaseReceivePack receivePack)
+ void advertiseRefs(BaseReceivePack receivePack)
throws ServiceMayNotContinueException;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/AmazonS3.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/AmazonS3.java
index f6ec4b90eb..c5661e5083 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/AmazonS3.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/AmazonS3.java
@@ -542,7 +542,7 @@ public class AmazonS3 {
}
buf = b.toByteArray();
if (buf.length > 0) {
- err.initCause(new IOException("\n" + new String(buf))); //$NON-NLS-1$
+ err.initCause(new IOException("\n" + new String(buf, UTF_8))); //$NON-NLS-1$
}
}
return err;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseReceivePack.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseReceivePack.java
index d3419bc201..03763368a8 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseReceivePack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseReceivePack.java
@@ -72,12 +72,14 @@ import java.util.concurrent.TimeUnit;
import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.errors.InvalidObjectIdException;
+import org.eclipse.jgit.errors.LargeObjectException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.errors.PackProtocolException;
import org.eclipse.jgit.errors.TooLargePackException;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.internal.storage.file.PackLock;
import org.eclipse.jgit.internal.submodule.SubmoduleValidator;
+import org.eclipse.jgit.internal.submodule.SubmoduleValidator.SubmoduleValidationException;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.BatchRefUpdate;
import org.eclipse.jgit.lib.Config;
@@ -1528,8 +1530,12 @@ public abstract class BaseReceivePack {
AnyObjectId blobId = entry.getBlobId();
ObjectLoader blob = odb.open(blobId, Constants.OBJ_BLOB);
- SubmoduleValidator.assertValidGitModulesFile(
- new String(blob.getBytes(), UTF_8));
+ try {
+ SubmoduleValidator.assertValidGitModulesFile(
+ new String(blob.getBytes(), UTF_8));
+ } catch (LargeObjectException | SubmoduleValidationException e) {
+ throw new IOException(e);
+ }
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Connection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Connection.java
index d4c514e636..19a1ab0b93 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Connection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Connection.java
@@ -68,7 +68,7 @@ public interface Connection extends AutoCloseable {
* modifiable. The collection can be empty if the remote side has no
* refs (it is an empty/newly created repository).
*/
- public Map<String, Ref> getRefsMap();
+ Map<String, Ref> getRefsMap();
/**
* Get the complete list of refs advertised as available for fetching or
@@ -82,7 +82,7 @@ public interface Connection extends AutoCloseable {
* collection can be empty if the remote side has no refs (it is an
* empty/newly created repository).
*/
- public Collection<Ref> getRefs();
+ Collection<Ref> getRefs();
/**
* Get a single advertised ref by name.
@@ -95,7 +95,7 @@ public interface Connection extends AutoCloseable {
* name of the ref to obtain.
* @return the requested ref; null if the remote did not advertise this ref.
*/
- public Ref getRef(String name);
+ Ref getRef(String name);
/**
* {@inheritDoc}
@@ -115,7 +115,7 @@ public interface Connection extends AutoCloseable {
* the signature to prevent them from doing so.
*/
@Override
- public void close();
+ void close();
/**
* Get the additional messages, if any, returned by the remote process.
@@ -132,7 +132,7 @@ public interface Connection extends AutoCloseable {
* newline (LF) character. The empty string is returned if the
* remote produced no additional messages.
*/
- public String getMessages();
+ String getMessages();
/**
* User agent advertised by the remote server.
@@ -141,5 +141,5 @@ public interface Connection extends AutoCloseable {
* server does not advertise this version.
* @since 4.0
*/
- public String getPeerUserAgent();
+ String getPeerUserAgent();
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchConnection.java
index f0c45d5fb6..1eb7cbd93a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchConnection.java
@@ -109,7 +109,7 @@ public interface FetchConnection extends Connection {
* protocol error, or error on remote side, or connection was
* already used for fetch.
*/
- public void fetch(final ProgressMonitor monitor,
+ void fetch(final ProgressMonitor monitor,
final Collection<Ref> want, final Set<ObjectId> have)
throws TransportException;
@@ -151,7 +151,7 @@ public interface FetchConnection extends Connection {
* already used for fetch.
* @since 3.0
*/
- public void fetch(final ProgressMonitor monitor,
+ void fetch(final ProgressMonitor monitor,
final Collection<Ref> want, final Set<ObjectId> have,
OutputStream out) throws TransportException;
@@ -173,7 +173,7 @@ public interface FetchConnection extends Connection {
* @return true if the last fetch call implicitly included tag objects;
* false if tags were not implicitly obtained.
*/
- public boolean didFetchIncludeTags();
+ boolean didFetchIncludeTags();
/**
* Did the last {@link #fetch(ProgressMonitor, Collection, Set)} validate
@@ -196,7 +196,7 @@ public interface FetchConnection extends Connection {
* client side in order to succeed; false if the last fetch assumed
* the remote peer supplied a complete graph.
*/
- public boolean didFetchTestConnectivity();
+ boolean didFetchTestConnectivity();
/**
* Set the lock message used when holding a pack out of garbage collection.
@@ -208,7 +208,7 @@ public interface FetchConnection extends Connection {
*
* @param message message to use when holding a pack in place.
*/
- public void setPackLockMessage(String message);
+ void setPackLockMessage(String message);
/**
* All locks created by the last
@@ -218,5 +218,5 @@ public interface FetchConnection extends Connection {
* fetch. The caller must release these after refs are updated in
* order to safely permit garbage collection.
*/
- public Collection<PackLock> getPackLocks();
+ Collection<PackLock> getPackLocks();
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java
index c43ab18c35..211707e9ad 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java
@@ -44,6 +44,7 @@
package org.eclipse.jgit.transport;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static org.eclipse.jgit.transport.ReceiveCommand.Result.NOT_ATTEMPTED;
import static org.eclipse.jgit.transport.ReceiveCommand.Result.OK;
import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_NONFASTFORWARD;
@@ -337,7 +338,7 @@ class FetchProcess {
try {
if (lock.lock()) {
try (Writer w = new OutputStreamWriter(
- lock.getOutputStream())) {
+ lock.getOutputStream(), UTF_8)) {
for (FetchHeadRecord h : fetchHeadUpdates) {
h.write(w);
result.add(h);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchRequest.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchRequest.java
new file mode 100644
index 0000000000..40ba3a3ad2
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchRequest.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2018, Google LLC.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.transport;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.lib.ObjectId;
+
+/**
+ * Common fields between v0/v1/v2 fetch requests.
+ */
+abstract class FetchRequest {
+
+ final Set<ObjectId> wantIds;
+
+ final int depth;
+
+ final Set<ObjectId> clientShallowCommits;
+
+ final long filterBlobLimit;
+
+ final Set<String> clientCapabilities;
+
+ final int deepenSince;
+
+ final List<String> deepenNotRefs;
+
+ @Nullable
+ final String agent;
+
+ /**
+ * Initialize the common fields of a fetch request.
+ *
+ * @param wantIds
+ * list of want ids
+ * @param depth
+ * how deep to go in the tree
+ * @param clientShallowCommits
+ * commits the client has without history
+ * @param filterBlobLimit
+ * to exclude blobs on certain conditions
+ * @param clientCapabilities
+ * capabilities sent in the request
+ * @param deepenNotRefs
+ * Requests that the shallow clone/fetch should be cut at these
+ * specific revisions instead of a depth.
+ * @param deepenSince
+ * Requests that the shallow clone/fetch should be cut at a
+ * specific time, instead of depth
+ * @param agent
+ * agent as reported by the client in the request body
+ */
+ FetchRequest(@NonNull Set<ObjectId> wantIds, int depth,
+ @NonNull Set<ObjectId> clientShallowCommits, long filterBlobLimit,
+ @NonNull Set<String> clientCapabilities, int deepenSince,
+ @NonNull List<String> deepenNotRefs, @Nullable String agent) {
+ this.wantIds = requireNonNull(wantIds);
+ this.depth = depth;
+ this.clientShallowCommits = requireNonNull(clientShallowCommits);
+ this.filterBlobLimit = filterBlobLimit;
+ this.clientCapabilities = requireNonNull(clientCapabilities);
+ this.deepenSince = deepenSince;
+ this.deepenNotRefs = requireNonNull(deepenNotRefs);
+ this.agent = agent;
+ }
+
+ /**
+ * @return object ids in the "want" (and "want-ref") lines of the request
+ */
+ @NonNull
+ Set<ObjectId> getWantIds() {
+ return wantIds;
+ }
+
+ /**
+ * @return the depth set in a "deepen" line. 0 by default.
+ */
+ int getDepth() {
+ return depth;
+ }
+
+ /**
+ * Shallow commits the client already has.
+ *
+ * These are sent by the client in "shallow" request lines.
+ *
+ * @return set of commits the client has declared as shallow.
+ */
+ @NonNull
+ Set<ObjectId> getClientShallowCommits() {
+ return clientShallowCommits;
+ }
+
+ /**
+ * @return the blob limit set in a "filter" line (-1 if not set)
+ */
+ long getFilterBlobLimit() {
+ return filterBlobLimit;
+ }
+
+ /**
+ * Capabilities that the client wants enabled from the server.
+ *
+ * Capabilities are options that tune the expected response from the server,
+ * like "thin-pack", "no-progress" or "ofs-delta". This list should be a
+ * subset of the capabilities announced by the server in its first response.
+ *
+ * These options are listed and well-defined in the git protocol
+ * specification.
+ *
+ * The agent capability is not included in this set. It can be retrieved via
+ * {@link #getAgent()}.
+ *
+ * @return capabilities sent by the client (excluding the "agent"
+ * capability)
+ */
+ @NonNull
+ Set<String> getClientCapabilities() {
+ return clientCapabilities;
+ }
+
+ /**
+ * The value in a "deepen-since" line in the request, indicating the
+ * timestamp where to stop fetching/cloning.
+ *
+ * @return timestamp in seconds since the epoch, where to stop the shallow
+ * fetch/clone. Defaults to 0 if not set in the request.
+ */
+ int getDeepenSince() {
+ return deepenSince;
+ }
+
+ /**
+ * @return refs received in "deepen-not" lines.
+ */
+ @NonNull
+ List<String> getDeepenNotRefs() {
+ return deepenNotRefs;
+ }
+
+ /**
+ * @return string identifying the agent (as sent in the request body by the
+ * client)
+ */
+ @Nullable
+ String getAgent() {
+ return agent;
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV0Request.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV0Request.java
new file mode 100644
index 0000000000..05f4a8155f
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV0Request.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2018, Google LLC.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.transport;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.lib.ObjectId;
+
+/**
+ * Fetch request in the V0/V1 protocol.
+ */
+final class FetchV0Request extends FetchRequest {
+
+ FetchV0Request(@NonNull Set<ObjectId> wantIds, int depth,
+ @NonNull Set<ObjectId> clientShallowCommits, long filterBlobLimit,
+ @NonNull Set<String> clientCapabilities, @Nullable String agent) {
+ super(wantIds, depth, clientShallowCommits, filterBlobLimit,
+ clientCapabilities, 0, Collections.emptyList(), agent);
+ }
+
+ static final class Builder {
+
+ int depth;
+
+ final Set<ObjectId> wantIds = new HashSet<>();
+
+ final Set<ObjectId> clientShallowCommits = new HashSet<>();
+
+ long filterBlobLimit = -1;
+
+ final Set<String> clientCaps = new HashSet<>();
+
+ String agent;
+
+ /**
+ * @param objectId
+ * object id received in a "want" line
+ * @return this builder
+ */
+ Builder addWantId(ObjectId objectId) {
+ wantIds.add(objectId);
+ return this;
+ }
+
+ /**
+ * @param d
+ * depth set in a "deepen" line
+ * @return this builder
+ */
+ Builder setDepth(int d) {
+ depth = d;
+ return this;
+ }
+
+ /**
+ * @param shallowOid
+ * object id received in a "shallow" line
+ * @return this builder
+ */
+ Builder addClientShallowCommit(ObjectId shallowOid) {
+ clientShallowCommits.add(shallowOid);
+ return this;
+ }
+
+ /**
+ * @param clientCapabilities
+ * client capabilities sent by the client in the first want
+ * line of the request
+ * @return this builder
+ */
+ Builder addClientCapabilities(Collection<String> clientCapabilities) {
+ clientCaps.addAll(clientCapabilities);
+ return this;
+ }
+
+ /**
+ * @param clientAgent
+ * agent line sent by the client in the request body
+ * @return this builder
+ */
+ Builder setAgent(String clientAgent) {
+ agent = clientAgent;
+ return this;
+ }
+
+ /**
+ * @param filterBlobLim
+ * blob limit set in a "filter" line
+ * @return this builder
+ */
+ Builder setFilterBlobLimit(long filterBlobLim) {
+ filterBlobLimit = filterBlobLim;
+ return this;
+ }
+
+ FetchV0Request build() {
+ return new FetchV0Request(wantIds, depth, clientShallowCommits,
+ filterBlobLimit, clientCaps, agent);
+ }
+
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV2Request.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV2Request.java
index 34f3484951..ac6361cdeb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV2Request.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV2Request.java
@@ -42,70 +42,62 @@
*/
package org.eclipse.jgit.transport;
+import static java.util.Objects.requireNonNull;
+
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.lib.ObjectId;
/**
- * fetch protocol v2 request.
+ * Fetch request from git protocol v2.
*
* <p>
* This is used as an input to {@link ProtocolV2Hook}.
*
* @since 5.1
*/
-public final class FetchV2Request {
+public final class FetchV2Request extends FetchRequest {
private final List<ObjectId> peerHas;
private final List<String> wantedRefs;
- private final Set<ObjectId> wantsIds;
-
- private final Set<ObjectId> clientShallowCommits;
-
- private final int deepenSince;
-
- private final List<String> deepenNotRefs;
-
- private final int depth;
-
- private final long filterBlobLimit;
-
- private final Set<String> options;
-
private final boolean doneReceived;
- private FetchV2Request(List<ObjectId> peerHas,
- List<String> wantedRefs, Set<ObjectId> wantsIds,
- Set<ObjectId> clientShallowCommits, int deepenSince,
- List<String> deepenNotRefs, int depth, long filterBlobLimit,
- boolean doneReceived, Set<String> options) {
- this.peerHas = peerHas;
- this.wantedRefs = wantedRefs;
- this.wantsIds = wantsIds;
- this.clientShallowCommits = clientShallowCommits;
- this.deepenSince = deepenSince;
- this.deepenNotRefs = deepenNotRefs;
- this.depth = depth;
- this.filterBlobLimit = filterBlobLimit;
+ @NonNull
+ private final List<String> serverOptions;
+
+ FetchV2Request(@NonNull List<ObjectId> peerHas,
+ @NonNull List<String> wantedRefs,
+ @NonNull Set<ObjectId> wantIds,
+ @NonNull Set<ObjectId> clientShallowCommits, int deepenSince,
+ @NonNull List<String> deepenNotRefs, int depth,
+ long filterBlobLimit,
+ boolean doneReceived, @NonNull Set<String> clientCapabilities,
+ @Nullable String agent, @NonNull List<String> serverOptions) {
+ super(wantIds, depth, clientShallowCommits, filterBlobLimit,
+ clientCapabilities, deepenSince, deepenNotRefs, agent);
+ this.peerHas = requireNonNull(peerHas);
+ this.wantedRefs = requireNonNull(wantedRefs);
this.doneReceived = doneReceived;
- this.options = options;
+ this.serverOptions = requireNonNull(serverOptions);
}
/**
- * @return object ids in the "have" lines of the request
+ * @return object ids received in the "have" lines
*/
@NonNull
List<ObjectId> getPeerHas() {
- return this.peerHas;
+ return peerHas;
}
/**
- * @return list of references in the "want-ref" lines of the request
+ * @return list of references received in "want-ref" lines
*/
@NonNull
List<String> getWantedRefs() {
@@ -113,59 +105,6 @@ public final class FetchV2Request {
}
/**
- * @return object ids in the "want" (but not "want-ref") lines of the request
- */
- @NonNull
- Set<ObjectId> getWantsIds() {
- return wantsIds;
- }
-
- /**
- * Shallow commits the client already has.
- *
- * These are sent by the client in "shallow" request lines.
- *
- * @return set of commits the client has declared as shallow.
- */
- @NonNull
- Set<ObjectId> getClientShallowCommits() {
- return clientShallowCommits;
- }
-
- /**
- * The value in a "deepen-since" line in the request, indicating the
- * timestamp where to stop fetching/cloning.
- *
- * @return timestamp in seconds since the epoch, where to stop the shallow
- * fetch/clone. Defaults to 0 if not set in the request.
- */
- int getDeepenSince() {
- return deepenSince;
- }
-
- /**
- * @return the refs in "deepen-not" lines in the request.
- */
- @NonNull
- List<String> getDeepenNotRefs() {
- return deepenNotRefs;
- }
-
- /**
- * @return the depth set in a "deepen" line. 0 by default.
- */
- int getDepth() {
- return depth;
- }
-
- /**
- * @return the blob limit set in a "filter" line (-1 if not set)
- */
- long getFilterBlobLimit() {
- return filterBlobLimit;
- }
-
- /**
* @return true if the request had a "done" line
*/
boolean wasDoneReceived() {
@@ -173,17 +112,16 @@ public final class FetchV2Request {
}
/**
- * Options that tune the expected response from the server, like
- * "thin-pack", "no-progress" or "ofs-delta"
+ * Options received in server-option lines. The caller can choose to act on
+ * these in an application-specific way
*
- * These are options listed and well-defined in the git protocol
- * specification
+ * @return Immutable list of server options received in the request
*
- * @return options found in the request lines
+ * @since 5.2
*/
@NonNull
- Set<String> getOptions() {
- return options;
+ public List<String> getServerOptions() {
+ return serverOptions;
}
/** @return A builder of {@link FetchV2Request}. */
@@ -191,20 +129,19 @@ public final class FetchV2Request {
return new Builder();
}
-
/** A builder for {@link FetchV2Request}. */
static final class Builder {
- List<ObjectId> peerHas = new ArrayList<>();
+ final List<ObjectId> peerHas = new ArrayList<>();
- List<String> wantedRefs = new ArrayList<>();
+ final List<String> wantedRefs = new ArrayList<>();
- Set<ObjectId> wantsIds = new HashSet<>();
+ final Set<ObjectId> wantIds = new HashSet<>();
- Set<ObjectId> clientShallowCommits = new HashSet<>();
+ final Set<ObjectId> clientShallowCommits = new HashSet<>();
- List<String> deepenNotRefs = new ArrayList<>();
+ final List<String> deepenNotRefs = new ArrayList<>();
- Set<String> options = new HashSet<>();
+ final Set<String> clientCapabilities = new HashSet<>();
int depth;
@@ -214,13 +151,18 @@ public final class FetchV2Request {
boolean doneReceived;
+ @Nullable
+ String agent;
+
+ final List<String> serverOptions = new ArrayList<>();
+
private Builder() {
}
/**
* @param objectId
- * from a "have" line in a fetch request
- * @return the builder
+ * object id received in a "have" line
+ * @return this builder
*/
Builder addPeerHas(ObjectId objectId) {
peerHas.add(objectId);
@@ -228,11 +170,11 @@ public final class FetchV2Request {
}
/**
- * From a "want-ref" line in a fetch request
+ * Ref received in "want-ref" line and the object-id it refers to
*
* @param refName
* reference name
- * @return the builder
+ * @return this builder
*/
Builder addWantedRef(String refName) {
wantedRefs.add(refName);
@@ -240,42 +182,42 @@ public final class FetchV2Request {
}
/**
- * @param option
- * fetch request lines acting as options
- * @return the builder
+ * @param clientCapability
+ * capability line sent by the client
+ * @return this builder
*/
- Builder addOption(String option) {
- options.add(option);
+ Builder addClientCapability(String clientCapability) {
+ clientCapabilities.add(clientCapability);
return this;
}
/**
- * @param objectId
- * from a "want" line in a fetch request
- * @return the builder
+ * @param wantId
+ * object id received in a "want" line
+ * @return this builder
*/
- Builder addWantsId(ObjectId objectId) {
- wantsIds.add(objectId);
+ Builder addWantId(ObjectId wantId) {
+ wantIds.add(wantId);
return this;
}
/**
* @param shallowOid
- * from a "shallow" line in the fetch request
- * @return the builder
+ * object id received in a "shallow" line
+ * @return this builder
*/
Builder addClientShallowCommit(ObjectId shallowOid) {
- this.clientShallowCommits.add(shallowOid);
+ clientShallowCommits.add(shallowOid);
return this;
}
/**
* @param d
- * from a "deepen" line in the fetch request
- * @return the builder
+ * Depth received in a "deepen" line
+ * @return this builder
*/
Builder setDepth(int d) {
- this.depth = d;
+ depth = d;
return this;
}
@@ -284,32 +226,34 @@ public final class FetchV2Request {
* 0 if not set.
*/
int getDepth() {
- return this.depth;
+ return depth;
}
/**
- * @return if there has been any "deepen not" line in the request
+ * @return true if there has been at least one "deepen not" line in the
+ * request so far
*/
boolean hasDeepenNotRefs() {
return !deepenNotRefs.isEmpty();
}
/**
- * @param deepenNotRef reference in a "deepen not" line
- * @return the builder
+ * @param deepenNotRef
+ * reference received in a "deepen not" line
+ * @return this builder
*/
Builder addDeepenNotRef(String deepenNotRef) {
- this.deepenNotRefs.add(deepenNotRef);
+ deepenNotRefs.add(deepenNotRef);
return this;
}
/**
* @param value
* Unix timestamp received in a "deepen since" line
- * @return the builder
+ * @return this builder
*/
Builder setDeepenSince(int value) {
- this.deepenSince = value;
+ deepenSince = value;
return this;
}
@@ -318,35 +262,66 @@ public final class FetchV2Request {
* by default.
*/
int getDeepenSince() {
- return this.deepenSince;
+ return deepenSince;
}
/**
- * @param filterBlobLimit
+ * @param filterBlobLim
* set in a "filter" line
- * @return the builder
+ * @return this builder
*/
- Builder setFilterBlobLimit(long filterBlobLimit) {
- this.filterBlobLimit = filterBlobLimit;
+ Builder setFilterBlobLimit(long filterBlobLim) {
+ filterBlobLimit = filterBlobLim;
return this;
}
/**
* Mark that the "done" line has been received.
*
- * @return the builder
+ * @return this builder
*/
Builder setDoneReceived() {
- this.doneReceived = true;
+ doneReceived = true;
return this;
}
+
+ /**
+ * Value of an agent line received after the command and before the
+ * arguments. E.g. "agent=a.b.c/1.0" should set "a.b.c/1.0".
+ *
+ * @param agentValue
+ * the client-supplied agent capability, without the leading
+ * "agent="
+ * @return this builder
+ */
+ Builder setAgent(@Nullable String agentValue) {
+ agent = agentValue;
+ return this;
+ }
+
+ /**
+ * Records an application-specific option supplied in a server-option
+ * line, for later retrieval with
+ * {@link FetchV2Request#getServerOptions}.
+ *
+ * @param value
+ * the client-supplied server-option capability, without
+ * leading "server-option=".
+ * @return this builder
+ */
+ Builder addServerOption(@NonNull String value) {
+ serverOptions.add(value);
+ return this;
+ }
+
/**
* @return Initialized fetch request
*/
FetchV2Request build() {
- return new FetchV2Request(peerHas, wantedRefs, wantsIds,
+ return new FetchV2Request(peerHas, wantedRefs, wantIds,
clientShallowCommits, deepenSince, deepenNotRefs,
- depth, filterBlobLimit, doneReceived, options);
+ depth, filterBlobLimit, doneReceived, clientCapabilities,
+ agent, Collections.unmodifiableList(serverOptions));
}
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FtpChannel.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FtpChannel.java
new file mode 100644
index 0000000000..39e87b246c
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FtpChannel.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.transport;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Collection;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * An interface providing FTP operations over a {@link RemoteSession}. All
+ * operations are supposed to throw {@link FtpException} for remote file system
+ * errors and other IOExceptions on connection errors.
+ *
+ * @since 5.2
+ */
+public interface FtpChannel {
+
+ /**
+ * An {@link Exception} for reporting SFTP errors.
+ */
+ static class FtpException extends IOException {
+
+ private static final long serialVersionUID = 7176525179280330876L;
+
+ public static final int OK = 0;
+
+ public static final int EOF = 1;
+
+ public static final int NO_SUCH_FILE = 2;
+
+ public static final int NO_PERMISSION = 3;
+
+ public static final int UNSPECIFIED_FAILURE = 4;
+
+ public static final int PROTOCOL_ERROR = 5;
+
+ public static final int UNSUPPORTED = 8;
+
+ private final int status;
+
+ public FtpException(String message, int status) {
+ super(message);
+ this.status = status;
+ }
+
+ public FtpException(String message, int status, Throwable cause) {
+ super(message, cause);
+ this.status = status;
+ }
+
+ public int getStatus() {
+ return status;
+ }
+ }
+
+ /**
+ * Connects the {@link FtpChannel} to the remote end.
+ *
+ * @param timeout
+ * for establishing the FTP connection
+ * @param unit
+ * of the {@code timeout}
+ * @throws IOException
+ */
+ void connect(int timeout, TimeUnit unit) throws IOException;
+
+ /**
+ * Disconnects and {@link FtpChannel}.
+ */
+ void disconnect();
+
+ /**
+ * @return whether the {@link FtpChannel} is connected
+ */
+ boolean isConnected();
+
+ /**
+ * Changes the current remote directory.
+ *
+ * @param path
+ * target directory
+ * @throws IOException
+ * if the operation could not be performed remotely
+ */
+ void cd(String path) throws IOException;
+
+ /**
+ * @return the current remote directory path
+ * @throws IOException
+ */
+ String pwd() throws IOException;
+
+ /**
+ * Simplified remote directory entry.
+ */
+ interface DirEntry {
+ String getFilename();
+
+ long getModifiedTime();
+
+ boolean isDirectory();
+ }
+
+ /**
+ * Lists contents of a remote directory
+ *
+ * @param path
+ * of the directory to list
+ * @return the directory entries
+ * @throws IOException
+ */
+ Collection<DirEntry> ls(String path) throws IOException;
+
+ /**
+ * Deletes a directory on the remote file system. The directory must be
+ * empty.
+ *
+ * @param path
+ * to delete
+ * @throws IOException
+ */
+ void rmdir(String path) throws IOException;
+
+ /**
+ * Creates a directory on the remote file system.
+ *
+ * @param path
+ * to create
+ * @throws IOException
+ */
+ void mkdir(String path) throws IOException;
+
+ /**
+ * Obtain an {@link InputStream} to read the contents of a remote file.
+ *
+ * @param path
+ * of the file to read
+ *
+ * @return the stream to read from
+ * @throws IOException
+ */
+ InputStream get(String path) throws IOException;
+
+ /**
+ * Obtain an {@link OutputStream} to write to a remote file. If the file
+ * exists already, it will be overwritten.
+ *
+ * @param path
+ * of the file to read
+ *
+ * @return the stream to read from
+ * @throws IOException
+ */
+ OutputStream put(String path) throws IOException;
+
+ /**
+ * Deletes a file on the remote file system.
+ *
+ * @param path
+ * to delete
+ * @throws IOException
+ * if the file does not exist or could otherwise not be deleted
+ */
+ void rm(String path) throws IOException;
+
+ /**
+ * Deletes a file on the remote file system. If the file does not exist, no
+ * exception is thrown.
+ *
+ * @param path
+ * to delete
+ * @throws IOException
+ * if the file exist but could not be deleted
+ */
+ default void delete(String path) throws IOException {
+ try {
+ rm(path);
+ } catch (FileNotFoundException e) {
+ // Ignore; it's OK if the file doesn't exist
+ } catch (FtpException f) {
+ if (f.getStatus() == FtpException.NO_SUCH_FILE) {
+ return;
+ }
+ throw f;
+ }
+ }
+
+ /**
+ * Renames a file on the remote file system. If {@code to} exists, it is
+ * replaced by {@code from}. (POSIX rename() semantics)
+ *
+ * @param from
+ * original name of the file
+ * @param to
+ * new name of the file
+ * @throws IOException
+ * @see <a href=
+ * "http://pubs.opengroup.org/onlinepubs/9699919799/functions/rename.html">stdio.h:
+ * rename()</a>
+ */
+ void rename(String from, String to) throws IOException;
+
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java
index 760ac6c1d7..1561c93b95 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java
@@ -245,6 +245,20 @@ public final class GitProtocolConstants {
public static final String CAPABILITY_REF_IN_WANT = "ref-in-want"; //$NON-NLS-1$
/**
+ * The server supports arbitrary options
+ *
+ * @since 5.2
+ */
+ public static final String CAPABILITY_SERVER_OPTION = "server-option"; //$NON-NLS-1$
+
+ /**
+ * Option for passing application-specific options to the server.
+ *
+ * @since 5.2
+ */
+ public static final String OPTION_SERVER_OPTION = "server-option"; //$NON-NLS-1$
+
+ /**
* The server supports listing refs using protocol v2.
*
* @since 5.0
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/InternalPushConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/InternalPushConnection.java
index 732be63dc1..f05e0b8c7d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/InternalPushConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/InternalPushConnection.java
@@ -46,6 +46,7 @@ package org.eclipse.jgit.transport;
import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
+import java.io.UncheckedIOException;
import org.eclipse.jgit.errors.TransportException;
import org.eclipse.jgit.internal.JGitText;
@@ -103,10 +104,13 @@ class InternalPushConnection<C> extends BasePackPushConnection {
// Ignored. Client cannot use this repository.
} catch (ServiceNotAuthorizedException e) {
// Ignored. Client cannot use this repository.
- } catch (IOException err) {
- // Client side of the pipes should report the problem.
- } catch (RuntimeException err) {
- // Clients side will notice we went away, and report.
+ } catch (IOException e) {
+ // Since the InternalPushConnection
+ // is used in tests, we want to avoid hiding exceptions
+ // because they can point to programming errors on the server
+ // side. By rethrowing, the default handler will dump it
+ // to stderr.
+ throw new UncheckedIOException(e);
} finally {
try {
out_r.close();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/JschConfigSessionFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/JschConfigSessionFactory.java
index 4e712a5567..0bdd6ba812 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/JschConfigSessionFactory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/JschConfigSessionFactory.java
@@ -52,7 +52,6 @@ package org.eclipse.jgit.transport;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toList;
-import static org.eclipse.jgit.transport.OpenSshConfig.SSH_PORT;
import java.io.File;
import java.io.FileInputStream;
@@ -275,10 +274,11 @@ public abstract class JschConfigSessionFactory extends SshSessionFactory {
}
private static String hostName(Session s) {
- if (s.getPort() == SSH_PORT) {
+ if (s.getPort() == SshConstants.SSH_DEFAULT_PORT) {
return s.getHost();
}
- return String.format("[%s]:%d", s.getHost(), s.getPort()); //$NON-NLS-1$
+ return String.format("[%s]:%d", s.getHost(), //$NON-NLS-1$
+ Integer.valueOf(s.getPort()));
}
private void copyConfigValueToSession(Session session, Config cfg,
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/JschSession.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/JschSession.java
index e3ef832343..843b90c951 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/JschSession.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/JschSession.java
@@ -52,6 +52,11 @@ import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.TimeUnit;
import org.eclipse.jgit.errors.TransportException;
import org.eclipse.jgit.internal.JGitText;
@@ -59,8 +64,10 @@ import org.eclipse.jgit.util.io.IsolatedOutputStream;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelExec;
+import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
+import com.jcraft.jsch.SftpException;
/**
* Run remote commands using Jsch.
@@ -109,12 +116,24 @@ public class JschSession implements RemoteSession {
* @return a channel suitable for Sftp operations.
* @throws com.jcraft.jsch.JSchException
* on problems getting the channel.
+ * @deprecated since 5.2; use {@link #getFtpChannel()} instead
*/
+ @Deprecated
public Channel getSftpChannel() throws JSchException {
return sock.openChannel("sftp"); //$NON-NLS-1$
}
/**
+ * {@inheritDoc}
+ *
+ * @since 5.2
+ */
+ @Override
+ public FtpChannel getFtpChannel() {
+ return new JschFtpChannel();
+ }
+
+ /**
* Implementation of Process for running a single command using Jsch.
* <p>
* Uses the Jsch session to do actual command execution and manage the
@@ -233,4 +252,154 @@ public class JschSession implements RemoteSession {
return exitValue();
}
}
+
+ private class JschFtpChannel implements FtpChannel {
+
+ private ChannelSftp ftp;
+
+ @Override
+ public void connect(int timeout, TimeUnit unit) throws IOException {
+ try {
+ ftp = (ChannelSftp) sock.openChannel("sftp"); //$NON-NLS-1$
+ ftp.connect((int) unit.toMillis(timeout));
+ } catch (JSchException e) {
+ ftp = null;
+ throw new IOException(e.getLocalizedMessage(), e);
+ }
+ }
+
+ @Override
+ public void disconnect() {
+ ftp.disconnect();
+ ftp = null;
+ }
+
+ private <T> T map(Callable<T> op) throws IOException {
+ try {
+ return op.call();
+ } catch (Exception e) {
+ if (e instanceof SftpException) {
+ throw new FtpChannel.FtpException(e.getLocalizedMessage(),
+ ((SftpException) e).id, e);
+ }
+ throw new IOException(e.getLocalizedMessage(), e);
+ }
+ }
+
+ @Override
+ public boolean isConnected() {
+ return ftp != null && sock.isConnected();
+ }
+
+ @Override
+ public void cd(String path) throws IOException {
+ map(() -> {
+ ftp.cd(path);
+ return null;
+ });
+ }
+
+ @Override
+ public String pwd() throws IOException {
+ return map(() -> ftp.pwd());
+ }
+
+ @Override
+ public Collection<DirEntry> ls(String path) throws IOException {
+ return map(() -> {
+ List<DirEntry> result = new ArrayList<>();
+ for (Object e : ftp.ls(path)) {
+ ChannelSftp.LsEntry entry = (ChannelSftp.LsEntry) e;
+ result.add(new DirEntry() {
+
+ @Override
+ public String getFilename() {
+ return entry.getFilename();
+ }
+
+ @Override
+ public long getModifiedTime() {
+ return entry.getAttrs().getMTime();
+ }
+
+ @Override
+ public boolean isDirectory() {
+ return entry.getAttrs().isDir();
+ }
+ });
+ }
+ return result;
+ });
+ }
+
+ @Override
+ public void rmdir(String path) throws IOException {
+ map(() -> {
+ ftp.rm(path);
+ return null;
+ });
+ }
+
+ @Override
+ public void mkdir(String path) throws IOException {
+ map(() -> {
+ ftp.mkdir(path);
+ return null;
+ });
+ }
+
+ @Override
+ public InputStream get(String path) throws IOException {
+ return map(() -> ftp.get(path));
+ }
+
+ @Override
+ public OutputStream put(String path) throws IOException {
+ return map(() -> ftp.put(path));
+ }
+
+ @Override
+ public void rm(String path) throws IOException {
+ map(() -> {
+ ftp.rm(path);
+ return null;
+ });
+ }
+
+ @Override
+ public void rename(String from, String to) throws IOException {
+ map(() -> {
+ // Plain FTP rename will fail if "to" exists. Jsch knows about
+ // the FTP extension "posix-rename@openssh.com", which will
+ // remove "to" first if it exists.
+ if (hasPosixRename()) {
+ ftp.rename(from, to);
+ } else if (!to.equals(from)) {
+ // Try to remove "to" first. With git, we typically get this
+ // when a lock file is moved over the file locked. Note that
+ // the check for to being equal to from may still fail in
+ // the general case, but for use with JGit's TransportSftp
+ // it should be good enough.
+ delete(to);
+ ftp.rename(from, to);
+ }
+ return null;
+ });
+ }
+
+ /**
+ * Determine whether the server has the posix-rename extension.
+ *
+ * @return {@code true} if it is supported, {@code false} otherwise
+ * @see <a href=
+ * "https://cvsweb.openbsd.org/src/usr.bin/ssh/PROTOCOL?annotate=HEAD">OpenSSH
+ * deviations and extensions to the published SSH protocol</a>
+ * @see <a href=
+ * "http://pubs.opengroup.org/onlinepubs/9699919799/functions/rename.html">stdio.h:
+ * rename()</a>
+ */
+ private boolean hasPosixRename() {
+ return "1".equals(ftp.getExtension("posix-rename@openssh.com")); //$NON-NLS-1$//$NON-NLS-2$
+ }
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/LsRefsV2Request.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/LsRefsV2Request.java
index 3aff584a00..add373147c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/LsRefsV2Request.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/LsRefsV2Request.java
@@ -42,9 +42,15 @@
*/
package org.eclipse.jgit.transport;
+import static java.util.Objects.requireNonNull;
+
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.annotations.Nullable;
+
/**
* ls-refs protocol v2 request.
*
@@ -60,11 +66,20 @@ public final class LsRefsV2Request {
private final boolean peel;
+ @Nullable
+ private final String agent;
+
+ @NonNull
+ private final List<String> serverOptions;
+
private LsRefsV2Request(List<String> refPrefixes, boolean symrefs,
- boolean peel) {
+ boolean peel, @Nullable String agent,
+ @NonNull List<String> serverOptions) {
this.refPrefixes = refPrefixes;
this.symrefs = symrefs;
this.peel = peel;
+ this.agent = agent;
+ this.serverOptions = requireNonNull(serverOptions);
}
/** @return ref prefixes that the client requested. */
@@ -82,6 +97,34 @@ public final class LsRefsV2Request {
return peel;
}
+ /**
+ * @return agent as reported by the client
+ *
+ * @since 5.2
+ */
+ @Nullable
+ public String getAgent() {
+ return agent;
+ }
+
+ /**
+ * Get application-specific options provided by the client using
+ * --server-option.
+ * <p>
+ * It returns just the content, without the "server-option=" prefix. E.g. a
+ * request with server-option=A and server-option=B lines returns the list
+ * [A, B].
+ *
+ * @return application-specific options from the client as an unmodifiable
+ * list
+ *
+ * @since 5.2
+ */
+ @NonNull
+ public List<String> getServerOptions() {
+ return serverOptions;
+ }
+
/** @return A builder of {@link LsRefsV2Request}. */
public static Builder builder() {
return new Builder();
@@ -95,6 +138,10 @@ public final class LsRefsV2Request {
private boolean peel;
+ private final List<String> serverOptions = new ArrayList<>();
+
+ private String agent;
+
private Builder() {
}
@@ -125,10 +172,43 @@ public final class LsRefsV2Request {
return this;
}
+ /**
+ * Records an application-specific option supplied in a server-option
+ * line, for later retrieval with
+ * {@link LsRefsV2Request#getServerOptions}.
+ *
+ * @param value
+ * the client-supplied server-option capability, without
+ * leading "server-option=".
+ * @return this builder
+ * @since 5.2
+ */
+ public Builder addServerOption(@NonNull String value) {
+ serverOptions.add(value);
+ return this;
+ }
+
+ /**
+ * Value of an agent line received after the command and before the
+ * arguments. E.g. "agent=a.b.c/1.0" should set "a.b.c/1.0".
+ *
+ * @param value
+ * the client-supplied agent capability, without leading
+ * "agent="
+ * @return this builder
+ *
+ * @since 5.2
+ */
+ public Builder setAgent(@Nullable String value) {
+ agent = value;
+ return this;
+ }
+
/** @return LsRefsV2Request */
public LsRefsV2Request build() {
return new LsRefsV2Request(
- Collections.unmodifiableList(refPrefixes), symrefs, peel);
+ Collections.unmodifiableList(refPrefixes), symrefs, peel,
+ agent, Collections.unmodifiableList(serverOptions));
}
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/NetRC.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/NetRC.java
index e688f6340d..8562376aa9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/NetRC.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/NetRC.java
@@ -42,10 +42,13 @@
package org.eclipse.jgit.transport;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
import java.io.BufferedReader;
import java.io.File;
-import java.io.FileReader;
+import java.io.FileInputStream;
import java.io.IOException;
+import java.io.InputStreamReader;
import java.util.Collection;
import java.util.HashMap;
import java.util.Locale;
@@ -211,7 +214,8 @@ public class NetRC {
this.hosts.clear();
this.lastModified = this.netrc.lastModified();
- try (BufferedReader r = new BufferedReader(new FileReader(netrc))) {
+ try (BufferedReader r = new BufferedReader(
+ new InputStreamReader(new FileInputStream(netrc), UTF_8))) {
String line = null;
NetRCEntry entry = new NetRCEntry();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/NonceGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/NonceGenerator.java
index 51fe9070c0..fc22034340 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/NonceGenerator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/NonceGenerator.java
@@ -66,7 +66,7 @@ public interface NonceGenerator {
* @return The nonce to be signed by the pusher
* @throws java.lang.IllegalStateException
*/
- public String createNonce(Repository db, long timestamp)
+ String createNonce(Repository db, long timestamp)
throws IllegalStateException;
/**
@@ -91,6 +91,6 @@ public interface NonceGenerator {
* @return a NonceStatus indicating the trustworthiness of the received
* nonce.
*/
- public NonceStatus verify(String received, String sent,
+ NonceStatus verify(String received, String sent,
Repository db, boolean allowSlop, int slop);
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/OpenSshConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/OpenSshConfig.java
index 480055c2e4..32e1dff234 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/OpenSshConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/OpenSshConfig.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008, 2017, Google Inc.
+ * Copyright (C) 2008, 2018, Google Inc.
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -43,29 +43,16 @@
package org.eclipse.jgit.transport;
-import java.io.BufferedReader;
+import static org.eclipse.jgit.internal.transport.ssh.OpenSshConfigFile.positive;
+
import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedHashMap;
import java.util.List;
-import java.util.Locale;
import java.util.Map;
-import java.util.Set;
+import java.util.TreeMap;
-import org.eclipse.jgit.errors.InvalidPatternException;
-import org.eclipse.jgit.fnmatch.FileNameMatcher;
-import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.internal.transport.ssh.OpenSshConfigFile;
+import org.eclipse.jgit.internal.transport.ssh.OpenSshConfigFile.HostEntry;
import org.eclipse.jgit.util.FS;
-import org.eclipse.jgit.util.StringUtils;
-import org.eclipse.jgit.util.SystemReader;
import com.jcraft.jsch.ConfigRepository;
@@ -83,8 +70,7 @@ import com.jcraft.jsch.ConfigRepository;
* <li>JSch's OpenSSHConfig doesn't monitor for config file changes.
* </ul>
* <p>
- * Therefore implement our own parser to read an OpenSSH configuration file. It
- * makes the critical options available to
+ * This parser makes the critical options available to
* {@link org.eclipse.jgit.transport.SshSessionFactory} via
* {@link org.eclipse.jgit.transport.OpenSshConfig.Host} objects returned by
* {@link #lookup(String)}, and implements a fully conforming
@@ -92,49 +78,11 @@ import com.jcraft.jsch.ConfigRepository;
* {@link com.jcraft.jsch.ConfigRepository.Config}s via
* {@link #getConfig(String)}.
* </p>
- * <p>
- * Limitations compared to the full OpenSSH 7.5 parser:
- * </p>
- * <ul>
- * <li>This parser does not handle Match or Include keywords.
- * <li>This parser does not do host name canonicalization (Jsch ignores it
- * anyway).
- * </ul>
- * <p>
- * Note that OpenSSH's readconf.c is a validating parser; Jsch's
- * ConfigRepository OTOH treats all option values as plain strings, so any
- * validation must happen in Jsch outside of the parser. Thus this parser does
- * not validate option values, except for a few options when constructing a
- * {@link org.eclipse.jgit.transport.OpenSshConfig.Host} object.
- * </p>
- * <p>
- * This config does %-substitutions for the following tokens:
- * </p>
- * <ul>
- * <li>%% - single %
- * <li>%C - short-hand for %l%h%p%r. See %p and %r below; the replacement may be
- * done partially only and may leave %p or %r or both unreplaced.
- * <li>%d - home directory path
- * <li>%h - remote host name
- * <li>%L - local host name without domain
- * <li>%l - FQDN of the local host
- * <li>%n - host name as specified in {@link #lookup(String)}
- * <li>%p - port number; replaced only if set in the config
- * <li>%r - remote user name; replaced only if set in the config
- * <li>%u - local user name
- * </ul>
- * <p>
- * If the config doesn't set the port or the remote user name, %p and %r remain
- * un-substituted. It's the caller's responsibility to replace them with values
- * obtained from the connection URI. %i is not handled; Java has no concept of a
- * "user ID".
- * </p>
+ *
+ * @see OpenSshConfigFile
*/
public class OpenSshConfig implements ConfigRepository {
- /** IANA assigned port number for SSH. */
- static final int SSH_PORT = 22;
-
/**
* Obtain the user's configuration data.
* <p>
@@ -153,43 +101,17 @@ public class OpenSshConfig implements ConfigRepository {
if (home == null)
home = new File(".").getAbsoluteFile(); //$NON-NLS-1$
- final File config = new File(new File(home, ".ssh"), Constants.CONFIG); //$NON-NLS-1$
- final OpenSshConfig osc = new OpenSshConfig(home, config);
- osc.refresh();
- return osc;
+ final File config = new File(new File(home, SshConstants.SSH_DIR),
+ SshConstants.CONFIG);
+ return new OpenSshConfig(home, config);
}
- /** The user's home directory, as key files may be relative to here. */
- private final File home;
-
- /** The .ssh/config file we read and monitor for updates. */
- private final File configFile;
-
- /** Modification time of {@link #configFile} when it was last loaded. */
- private long lastModified;
-
- /**
- * Encapsulates entries read out of the configuration file, and
- * {@link Host}s created from that.
- */
- private static class State {
- Map<String, HostEntry> entries = new LinkedHashMap<>();
- Map<String, Host> hosts = new HashMap<>();
-
- @Override
- @SuppressWarnings("nls")
- public String toString() {
- return "State [entries=" + entries + ", hosts=" + hosts + "]";
- }
- }
-
- /** State read from the config file, plus {@link Host}s created from it. */
- private State state;
+ /** The base file. */
+ private OpenSshConfigFile configFile;
OpenSshConfig(File h, File cfg) {
- home = h;
- configFile = cfg;
- state = new State();
+ configFile = new OpenSshConfigFile(h, cfg,
+ SshSessionFactory.getLocalUserName());
}
/**
@@ -202,603 +124,8 @@ public class OpenSshConfig implements ConfigRepository {
* @return r configuration for the requested name. Never null.
*/
public Host lookup(String hostName) {
- final State cache = refresh();
- Host h = cache.hosts.get(hostName);
- if (h != null) {
- return h;
- }
- HostEntry fullConfig = new HostEntry();
- // Initialize with default entries at the top of the file, before the
- // first Host block.
- fullConfig.merge(cache.entries.get(HostEntry.DEFAULT_NAME));
- for (Map.Entry<String, HostEntry> e : cache.entries.entrySet()) {
- String key = e.getKey();
- if (isHostMatch(key, hostName)) {
- fullConfig.merge(e.getValue());
- }
- }
- fullConfig.substitute(hostName, home);
- h = new Host(fullConfig, hostName, home);
- cache.hosts.put(hostName, h);
- return h;
- }
-
- private synchronized State refresh() {
- final long mtime = configFile.lastModified();
- if (mtime != lastModified) {
- State newState = new State();
- try (FileInputStream in = new FileInputStream(configFile)) {
- newState.entries = parse(in);
- } catch (IOException none) {
- // Ignore -- we'll set and return an empty state
- }
- lastModified = mtime;
- state = newState;
- }
- return state;
- }
-
- private Map<String, HostEntry> parse(InputStream in)
- throws IOException {
- final Map<String, HostEntry> m = new LinkedHashMap<>();
- final BufferedReader br = new BufferedReader(new InputStreamReader(in));
- final List<HostEntry> current = new ArrayList<>(4);
- String line;
-
- // The man page doesn't say so, but the OpenSSH parser (readconf.c)
- // starts out in active mode and thus always applies any lines that
- // occur before the first host block. We gather those options in a
- // HostEntry for DEFAULT_NAME.
- HostEntry defaults = new HostEntry();
- current.add(defaults);
- m.put(HostEntry.DEFAULT_NAME, defaults);
-
- while ((line = br.readLine()) != null) {
- line = line.trim();
- if (line.isEmpty() || line.startsWith("#")) { //$NON-NLS-1$
- continue;
- }
- String[] parts = line.split("[ \t]*[= \t]", 2); //$NON-NLS-1$
- // Although the ssh-config man page doesn't say so, the OpenSSH
- // parser does allow quoted keywords.
- String keyword = dequote(parts[0].trim());
- // man 5 ssh-config says lines had the format "keyword arguments",
- // with no indication that arguments were optional. However, let's
- // not crap out on missing arguments. See bug 444319.
- String argValue = parts.length > 1 ? parts[1].trim() : ""; //$NON-NLS-1$
-
- if (StringUtils.equalsIgnoreCase("Host", keyword)) { //$NON-NLS-1$
- current.clear();
- for (String name : HostEntry.parseList(argValue)) {
- if (name == null || name.isEmpty()) {
- // null should not occur, but better be safe than sorry.
- continue;
- }
- HostEntry c = m.get(name);
- if (c == null) {
- c = new HostEntry();
- m.put(name, c);
- }
- current.add(c);
- }
- continue;
- }
-
- if (current.isEmpty()) {
- // We received an option outside of a Host block. We
- // don't know who this should match against, so skip.
- continue;
- }
-
- if (HostEntry.isListKey(keyword)) {
- List<String> args = HostEntry.parseList(argValue);
- for (HostEntry entry : current) {
- entry.setValue(keyword, args);
- }
- } else if (!argValue.isEmpty()) {
- argValue = dequote(argValue);
- for (HostEntry entry : current) {
- entry.setValue(keyword, argValue);
- }
- }
- }
-
- return m;
- }
-
- private static boolean isHostMatch(final String pattern,
- final String name) {
- if (pattern.startsWith("!")) { //$NON-NLS-1$
- return !patternMatchesHost(pattern.substring(1), name);
- } else {
- return patternMatchesHost(pattern, name);
- }
- }
-
- private static boolean patternMatchesHost(final String pattern,
- final String name) {
- if (pattern.indexOf('*') >= 0 || pattern.indexOf('?') >= 0) {
- final FileNameMatcher fn;
- try {
- fn = new FileNameMatcher(pattern, null);
- } catch (InvalidPatternException e) {
- return false;
- }
- fn.append(name);
- return fn.isMatch();
- } else {
- // Not a pattern but a full host name
- return pattern.equals(name);
- }
- }
-
- private static String dequote(String value) {
- if (value.startsWith("\"") && value.endsWith("\"") //$NON-NLS-1$ //$NON-NLS-2$
- && value.length() > 1)
- return value.substring(1, value.length() - 1);
- return value;
- }
-
- private static String nows(String value) {
- final StringBuilder b = new StringBuilder();
- for (int i = 0; i < value.length(); i++) {
- if (!Character.isSpaceChar(value.charAt(i)))
- b.append(value.charAt(i));
- }
- return b.toString();
- }
-
- private static Boolean yesno(String value) {
- if (StringUtils.equalsIgnoreCase("yes", value)) //$NON-NLS-1$
- return Boolean.TRUE;
- return Boolean.FALSE;
- }
-
- private static File toFile(String path, File home) {
- if (path.startsWith("~/")) { //$NON-NLS-1$
- return new File(home, path.substring(2));
- }
- File ret = new File(path);
- if (ret.isAbsolute()) {
- return ret;
- }
- return new File(home, path);
- }
-
- private static int positive(String value) {
- if (value != null) {
- try {
- return Integer.parseUnsignedInt(value);
- } catch (NumberFormatException e) {
- // Ignore
- }
- }
- return -1;
- }
-
- static String userName() {
- return AccessController.doPrivileged(new PrivilegedAction<String>() {
- @Override
- public String run() {
- return SystemReader.getInstance()
- .getProperty(Constants.OS_USER_NAME_KEY);
- }
- });
- }
-
- private static class HostEntry implements ConfigRepository.Config {
-
- /**
- * "Host name" of the HostEntry for the default options before the first
- * host block in a config file.
- */
- public static final String DEFAULT_NAME = ""; //$NON-NLS-1$
-
- // See com.jcraft.jsch.OpenSSHConfig. Translates some command-line keys
- // to ssh-config keys.
- private static final Map<String, String> KEY_MAP = new HashMap<>();
-
- static {
- KEY_MAP.put("kex", "KexAlgorithms"); //$NON-NLS-1$//$NON-NLS-2$
- KEY_MAP.put("server_host_key", "HostKeyAlgorithms"); //$NON-NLS-1$ //$NON-NLS-2$
- KEY_MAP.put("cipher.c2s", "Ciphers"); //$NON-NLS-1$ //$NON-NLS-2$
- KEY_MAP.put("cipher.s2c", "Ciphers"); //$NON-NLS-1$ //$NON-NLS-2$
- KEY_MAP.put("mac.c2s", "Macs"); //$NON-NLS-1$ //$NON-NLS-2$
- KEY_MAP.put("mac.s2c", "Macs"); //$NON-NLS-1$ //$NON-NLS-2$
- KEY_MAP.put("compression.s2c", "Compression"); //$NON-NLS-1$ //$NON-NLS-2$
- KEY_MAP.put("compression.c2s", "Compression"); //$NON-NLS-1$ //$NON-NLS-2$
- KEY_MAP.put("compression_level", "CompressionLevel"); //$NON-NLS-1$ //$NON-NLS-2$
- KEY_MAP.put("MaxAuthTries", "NumberOfPasswordPrompts"); //$NON-NLS-1$ //$NON-NLS-2$
- }
-
- /**
- * Keys that can be specified multiple times, building up a list. (I.e.,
- * those are the keys that do not follow the general rule of "first
- * occurrence wins".)
- */
- private static final Set<String> MULTI_KEYS = new HashSet<>();
-
- static {
- MULTI_KEYS.add("CERTIFICATEFILE"); //$NON-NLS-1$
- MULTI_KEYS.add("IDENTITYFILE"); //$NON-NLS-1$
- MULTI_KEYS.add("LOCALFORWARD"); //$NON-NLS-1$
- MULTI_KEYS.add("REMOTEFORWARD"); //$NON-NLS-1$
- MULTI_KEYS.add("SENDENV"); //$NON-NLS-1$
- }
-
- /**
- * Keys that take a whitespace-separated list of elements as argument.
- * Because the dequote-handling is different, we must handle those in
- * the parser. There are a few other keys that take comma-separated
- * lists as arguments, but for the parser those are single arguments
- * that must be quoted if they contain whitespace, and taking them apart
- * is the responsibility of the user of those keys.
- */
- private static final Set<String> LIST_KEYS = new HashSet<>();
-
- static {
- LIST_KEYS.add("CANONICALDOMAINS"); //$NON-NLS-1$
- LIST_KEYS.add("GLOBALKNOWNHOSTSFILE"); //$NON-NLS-1$
- LIST_KEYS.add("SENDENV"); //$NON-NLS-1$
- LIST_KEYS.add("USERKNOWNHOSTSFILE"); //$NON-NLS-1$
- }
-
- private Map<String, String> options;
-
- private Map<String, List<String>> multiOptions;
-
- private Map<String, List<String>> listOptions;
-
- @Override
- public String getHostname() {
- return getValue("HOSTNAME"); //$NON-NLS-1$
- }
-
- @Override
- public String getUser() {
- return getValue("USER"); //$NON-NLS-1$
- }
-
- @Override
- public int getPort() {
- return positive(getValue("PORT")); //$NON-NLS-1$
- }
-
- private static String mapKey(String key) {
- String k = KEY_MAP.get(key);
- if (k == null) {
- k = key;
- }
- return k.toUpperCase(Locale.ROOT);
- }
-
- private String findValue(String key) {
- String k = mapKey(key);
- String result = options != null ? options.get(k) : null;
- if (result == null) {
- // Also check the list and multi options. Modern OpenSSH treats
- // UserKnownHostsFile and GlobalKnownHostsFile as list-valued,
- // and so does this parser. Jsch 0.1.54 in general doesn't know
- // about list-valued options (it _does_ know multi-valued
- // options, though), and will ask for a single value for such
- // options.
- //
- // Let's be lenient and return at least the first value from
- // a list-valued or multi-valued key for which Jsch asks for a
- // single value.
- List<String> values = listOptions != null ? listOptions.get(k)
- : null;
- if (values == null) {
- values = multiOptions != null ? multiOptions.get(k) : null;
- }
- if (values != null && !values.isEmpty()) {
- result = values.get(0);
- }
- }
- return result;
- }
-
- @Override
- public String getValue(String key) {
- // See com.jcraft.jsch.OpenSSHConfig.MyConfig.getValue() for this
- // special case.
- if (key.equals("compression.s2c") //$NON-NLS-1$
- || key.equals("compression.c2s")) { //$NON-NLS-1$
- String foo = findValue(key);
- if (foo == null || foo.equals("no")) { //$NON-NLS-1$
- return "none,zlib@openssh.com,zlib"; //$NON-NLS-1$
- }
- return "zlib@openssh.com,zlib,none"; //$NON-NLS-1$
- }
- return findValue(key);
- }
-
- @Override
- public String[] getValues(String key) {
- String k = mapKey(key);
- List<String> values = listOptions != null ? listOptions.get(k)
- : null;
- if (values == null) {
- values = multiOptions != null ? multiOptions.get(k) : null;
- }
- if (values == null || values.isEmpty()) {
- return new String[0];
- }
- return values.toArray(new String[0]);
- }
-
- public void setValue(String key, String value) {
- String k = key.toUpperCase(Locale.ROOT);
- if (MULTI_KEYS.contains(k)) {
- if (multiOptions == null) {
- multiOptions = new HashMap<>();
- }
- List<String> values = multiOptions.get(k);
- if (values == null) {
- values = new ArrayList<>(4);
- multiOptions.put(k, values);
- }
- values.add(value);
- } else {
- if (options == null) {
- options = new HashMap<>();
- }
- if (!options.containsKey(k)) {
- options.put(k, value);
- }
- }
- }
-
- public void setValue(String key, List<String> values) {
- if (values.isEmpty()) {
- // Can occur only on a missing argument: ignore.
- return;
- }
- String k = key.toUpperCase(Locale.ROOT);
- // Check multi-valued keys first; because of the replacement
- // strategy, they must take precedence over list-valued keys
- // which always follow the "first occurrence wins" strategy.
- //
- // Note that SendEnv is a multi-valued list-valued key. (It's
- // rather immaterial for JGit, though.)
- if (MULTI_KEYS.contains(k)) {
- if (multiOptions == null) {
- multiOptions = new HashMap<>(2 * MULTI_KEYS.size());
- }
- List<String> items = multiOptions.get(k);
- if (items == null) {
- items = new ArrayList<>(values);
- multiOptions.put(k, items);
- } else {
- items.addAll(values);
- }
- } else {
- if (listOptions == null) {
- listOptions = new HashMap<>(2 * LIST_KEYS.size());
- }
- if (!listOptions.containsKey(k)) {
- listOptions.put(k, values);
- }
- }
- }
-
- public static boolean isListKey(String key) {
- return LIST_KEYS.contains(key.toUpperCase(Locale.ROOT));
- }
-
- /**
- * Splits the argument into a list of whitespace-separated elements.
- * Elements containing whitespace must be quoted and will be de-quoted.
- *
- * @param argument
- * argument part of the configuration line as read from the
- * config file
- * @return a {@link List} of elements, possibly empty and possibly
- * containing empty elements
- */
- public static List<String> parseList(String argument) {
- List<String> result = new ArrayList<>(4);
- int start = 0;
- int length = argument.length();
- while (start < length) {
- // Skip whitespace
- if (Character.isSpaceChar(argument.charAt(start))) {
- start++;
- continue;
- }
- if (argument.charAt(start) == '"') {
- int stop = argument.indexOf('"', ++start);
- if (stop < start) {
- // No closing double quote: skip
- break;
- }
- result.add(argument.substring(start, stop));
- start = stop + 1;
- } else {
- int stop = start + 1;
- while (stop < length
- && !Character.isSpaceChar(argument.charAt(stop))) {
- stop++;
- }
- result.add(argument.substring(start, stop));
- start = stop + 1;
- }
- }
- return result;
- }
-
- protected void merge(HostEntry entry) {
- if (entry == null) {
- // Can occur if we could not read the config file
- return;
- }
- if (entry.options != null) {
- if (options == null) {
- options = new HashMap<>();
- }
- for (Map.Entry<String, String> item : entry.options
- .entrySet()) {
- if (!options.containsKey(item.getKey())) {
- options.put(item.getKey(), item.getValue());
- }
- }
- }
- if (entry.listOptions != null) {
- if (listOptions == null) {
- listOptions = new HashMap<>(2 * LIST_KEYS.size());
- }
- for (Map.Entry<String, List<String>> item : entry.listOptions
- .entrySet()) {
- if (!listOptions.containsKey(item.getKey())) {
- listOptions.put(item.getKey(), item.getValue());
- }
- }
-
- }
- if (entry.multiOptions != null) {
- if (multiOptions == null) {
- multiOptions = new HashMap<>(2 * MULTI_KEYS.size());
- }
- for (Map.Entry<String, List<String>> item : entry.multiOptions
- .entrySet()) {
- List<String> values = multiOptions.get(item.getKey());
- if (values == null) {
- values = new ArrayList<>(item.getValue());
- multiOptions.put(item.getKey(), values);
- } else {
- values.addAll(item.getValue());
- }
- }
- }
- }
-
- private class Replacer {
- private final Map<Character, String> replacements = new HashMap<>();
-
- public Replacer(String originalHostName, File home) {
- replacements.put(Character.valueOf('%'), "%"); //$NON-NLS-1$
- replacements.put(Character.valueOf('d'), home.getPath());
- // Needs special treatment...
- String host = getValue("HOSTNAME"); //$NON-NLS-1$
- replacements.put(Character.valueOf('h'), originalHostName);
- if (host != null && host.indexOf('%') >= 0) {
- host = substitute(host, "h"); //$NON-NLS-1$
- options.put("HOSTNAME", host); //$NON-NLS-1$
- }
- if (host != null) {
- replacements.put(Character.valueOf('h'), host);
- }
- String localhost = SystemReader.getInstance().getHostname();
- replacements.put(Character.valueOf('l'), localhost);
- int period = localhost.indexOf('.');
- if (period > 0) {
- localhost = localhost.substring(0, period);
- }
- replacements.put(Character.valueOf('L'), localhost);
- replacements.put(Character.valueOf('n'), originalHostName);
- replacements.put(Character.valueOf('p'), getValue("PORT")); //$NON-NLS-1$
- replacements.put(Character.valueOf('r'), getValue("USER")); //$NON-NLS-1$
- replacements.put(Character.valueOf('u'), userName());
- replacements.put(Character.valueOf('C'),
- substitute("%l%h%p%r", "hlpr")); //$NON-NLS-1$ //$NON-NLS-2$
- }
-
- public String substitute(String input, String allowed) {
- if (input == null || input.length() <= 1
- || input.indexOf('%') < 0) {
- return input;
- }
- StringBuilder builder = new StringBuilder();
- int start = 0;
- int length = input.length();
- while (start < length) {
- int percent = input.indexOf('%', start);
- if (percent < 0 || percent + 1 >= length) {
- builder.append(input.substring(start));
- break;
- }
- String replacement = null;
- char ch = input.charAt(percent + 1);
- if (ch == '%' || allowed.indexOf(ch) >= 0) {
- replacement = replacements.get(Character.valueOf(ch));
- }
- if (replacement == null) {
- builder.append(input.substring(start, percent + 2));
- } else {
- builder.append(input.substring(start, percent))
- .append(replacement);
- }
- start = percent + 2;
- }
- return builder.toString();
- }
- }
-
- private List<String> substitute(List<String> values, String allowed,
- Replacer r) {
- List<String> result = new ArrayList<>(values.size());
- for (String value : values) {
- result.add(r.substitute(value, allowed));
- }
- return result;
- }
-
- private List<String> replaceTilde(List<String> values, File home) {
- List<String> result = new ArrayList<>(values.size());
- for (String value : values) {
- result.add(toFile(value, home).getPath());
- }
- return result;
- }
-
- protected void substitute(String originalHostName, File home) {
- Replacer r = new Replacer(originalHostName, home);
- if (multiOptions != null) {
- List<String> values = multiOptions.get("IDENTITYFILE"); //$NON-NLS-1$
- if (values != null) {
- values = substitute(values, "dhlru", r); //$NON-NLS-1$
- values = replaceTilde(values, home);
- multiOptions.put("IDENTITYFILE", values); //$NON-NLS-1$
- }
- values = multiOptions.get("CERTIFICATEFILE"); //$NON-NLS-1$
- if (values != null) {
- values = substitute(values, "dhlru", r); //$NON-NLS-1$
- values = replaceTilde(values, home);
- multiOptions.put("CERTIFICATEFILE", values); //$NON-NLS-1$
- }
- }
- if (listOptions != null) {
- List<String> values = listOptions.get("GLOBALKNOWNHOSTSFILE"); //$NON-NLS-1$
- if (values != null) {
- values = replaceTilde(values, home);
- listOptions.put("GLOBALKNOWNHOSTSFILE", values); //$NON-NLS-1$
- }
- values = listOptions.get("USERKNOWNHOSTSFILE"); //$NON-NLS-1$
- if (values != null) {
- values = replaceTilde(values, home);
- listOptions.put("USERKNOWNHOSTSFILE", values); //$NON-NLS-1$
- }
- }
- if (options != null) {
- // HOSTNAME already done in Replacer constructor
- String value = options.get("IDENTITYAGENT"); //$NON-NLS-1$
- if (value != null) {
- value = r.substitute(value, "dhlru"); //$NON-NLS-1$
- value = toFile(value, home).getPath();
- options.put("IDENTITYAGENT", value); //$NON-NLS-1$
- }
- }
- // Match is not implemented and would need to be done elsewhere
- // anyway. ControlPath, LocalCommand, ProxyCommand, and
- // RemoteCommand are not used by Jsch.
- }
-
- @Override
- @SuppressWarnings("nls")
- public String toString() {
- return "HostEntry [options=" + options + ", multiOptions="
- + multiOptions + ", listOptions=" + listOptions + "]";
- }
+ HostEntry entry = configFile.lookup(hostName, -1, null);
+ return new Host(entry, hostName, configFile.getLocalUserName());
}
/**
@@ -829,8 +156,34 @@ public class OpenSshConfig implements ConfigRepository {
int connectionAttempts;
+ private HostEntry entry;
+
private Config config;
+ // See com.jcraft.jsch.OpenSSHConfig. Translates some command-line keys
+ // to ssh-config keys.
+ private static final Map<String, String> KEY_MAP = new TreeMap<>(
+ String.CASE_INSENSITIVE_ORDER);
+
+ static {
+ KEY_MAP.put("kex", SshConstants.KEX_ALGORITHMS); //$NON-NLS-1$
+ KEY_MAP.put("server_host_key", SshConstants.HOST_KEY_ALGORITHMS); //$NON-NLS-1$
+ KEY_MAP.put("cipher.c2s", SshConstants.CIPHERS); //$NON-NLS-1$
+ KEY_MAP.put("cipher.s2c", SshConstants.CIPHERS); //$NON-NLS-1$
+ KEY_MAP.put("mac.c2s", SshConstants.MACS); //$NON-NLS-1$
+ KEY_MAP.put("mac.s2c", SshConstants.MACS); //$NON-NLS-1$
+ KEY_MAP.put("compression.s2c", SshConstants.COMPRESSION); //$NON-NLS-1$
+ KEY_MAP.put("compression.c2s", SshConstants.COMPRESSION); //$NON-NLS-1$
+ KEY_MAP.put("compression_level", "CompressionLevel"); //$NON-NLS-1$ //$NON-NLS-2$
+ KEY_MAP.put("MaxAuthTries", //$NON-NLS-1$
+ SshConstants.NUMBER_OF_PASSWORD_PROMPTS);
+ }
+
+ private static String mapKey(String key) {
+ String k = KEY_MAP.get(key);
+ return k != null ? k : key;
+ }
+
/**
* Creates a new uninitialized {@link Host}.
*/
@@ -838,9 +191,9 @@ public class OpenSshConfig implements ConfigRepository {
// For API backwards compatibility with pre-4.9 JGit
}
- Host(Config config, String hostName, File homeDir) {
- this.config = config;
- complete(hostName, homeDir);
+ Host(HostEntry entry, String hostName, String localUserName) {
+ this.entry = entry;
+ complete(hostName, localUserName);
}
/**
@@ -910,42 +263,84 @@ public class OpenSshConfig implements ConfigRepository {
}
- private void complete(String initialHostName, File homeDir) {
+ private void complete(String initialHostName, String localUserName) {
// Try to set values from the options.
- hostName = config.getHostname();
- user = config.getUser();
- port = config.getPort();
+ hostName = entry.getValue(SshConstants.HOST_NAME);
+ user = entry.getValue(SshConstants.USER);
+ port = positive(entry.getValue(SshConstants.PORT));
connectionAttempts = positive(
- config.getValue("ConnectionAttempts")); //$NON-NLS-1$
- strictHostKeyChecking = config.getValue("StrictHostKeyChecking"); //$NON-NLS-1$
- String value = config.getValue("BatchMode"); //$NON-NLS-1$
- if (value != null) {
- batchMode = yesno(value);
- }
- value = config.getValue("PreferredAuthentications"); //$NON-NLS-1$
- if (value != null) {
- preferredAuthentications = nows(value);
- }
+ entry.getValue(SshConstants.CONNECTION_ATTEMPTS));
+ strictHostKeyChecking = entry
+ .getValue(SshConstants.STRICT_HOST_KEY_CHECKING);
+ batchMode = Boolean.valueOf(OpenSshConfigFile
+ .flag(entry.getValue(SshConstants.BATCH_MODE)));
+ preferredAuthentications = entry
+ .getValue(SshConstants.PREFERRED_AUTHENTICATIONS);
// Fill in defaults if still not set
- if (hostName == null) {
+ if (hostName == null || hostName.isEmpty()) {
hostName = initialHostName;
}
- if (user == null) {
- user = OpenSshConfig.userName();
+ if (user == null || user.isEmpty()) {
+ user = localUserName;
}
if (port <= 0) {
- port = OpenSshConfig.SSH_PORT;
+ port = SshConstants.SSH_DEFAULT_PORT;
}
if (connectionAttempts <= 0) {
connectionAttempts = 1;
}
- String[] identityFiles = config.getValues("IdentityFile"); //$NON-NLS-1$
- if (identityFiles != null && identityFiles.length > 0) {
- identityFile = toFile(identityFiles[0], homeDir);
+ List<String> identityFiles = entry
+ .getValues(SshConstants.IDENTITY_FILE);
+ if (identityFiles != null && !identityFiles.isEmpty()) {
+ identityFile = new File(identityFiles.get(0));
}
}
Config getConfig() {
+ if (config == null) {
+ config = new Config() {
+
+ @Override
+ public String getHostname() {
+ return Host.this.getHostName();
+ }
+
+ @Override
+ public String getUser() {
+ return Host.this.getUser();
+ }
+
+ @Override
+ public int getPort() {
+ return Host.this.getPort();
+ }
+
+ @Override
+ public String getValue(String key) {
+ // See com.jcraft.jsch.OpenSSHConfig.MyConfig.getValue()
+ // for this special case.
+ if (key.equals("compression.s2c") //$NON-NLS-1$
+ || key.equals("compression.c2s")) { //$NON-NLS-1$
+ if (!OpenSshConfigFile.flag(
+ Host.this.entry.getValue(mapKey(key)))) {
+ return "none,zlib@openssh.com,zlib"; //$NON-NLS-1$
+ }
+ return "zlib@openssh.com,zlib,none"; //$NON-NLS-1$
+ }
+ return Host.this.entry.getValue(mapKey(key));
+ }
+
+ @Override
+ public String[] getValues(String key) {
+ List<String> values = Host.this.entry
+ .getValues(mapKey(key));
+ if (values == null) {
+ return new String[0];
+ }
+ return values.toArray(new String[0]);
+ }
+ };
+ }
return config;
}
@@ -957,7 +352,7 @@ public class OpenSshConfig implements ConfigRepository {
+ ", preferredAuthentications=" + preferredAuthentications
+ ", batchMode=" + batchMode + ", strictHostKeyChecking="
+ strictHostKeyChecking + ", connectionAttempts="
- + connectionAttempts + ", config=" + config + "]";
+ + connectionAttempts + ", entry=" + entry + "]";
}
}
@@ -977,9 +372,7 @@ public class OpenSshConfig implements ConfigRepository {
/** {@inheritDoc} */
@Override
- @SuppressWarnings("nls")
public String toString() {
- return "OpenSshConfig [home=" + home + ", configFile=" + configFile
- + ", lastModified=" + lastModified + ", state=" + state + "]";
+ return "OpenSshConfig [configFile=" + configFile + ']'; //$NON-NLS-1$
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PostReceiveHook.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PostReceiveHook.java
index 5cbb6f5dfb..ba5d2f3c8f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PostReceiveHook.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PostReceiveHook.java
@@ -63,7 +63,7 @@ import java.util.Collection;
*/
public interface PostReceiveHook {
/** A simple no-op hook. */
- public static final PostReceiveHook NULL = new PostReceiveHook() {
+ PostReceiveHook NULL = new PostReceiveHook() {
@Override
public void onPostReceive(final ReceivePack rp,
final Collection<ReceiveCommand> commands) {
@@ -81,6 +81,6 @@ public interface PostReceiveHook {
* unmodifiable set of successfully completed commands. May be
* the empty set.
*/
- public void onPostReceive(ReceivePack rp,
+ void onPostReceive(ReceivePack rp,
Collection<ReceiveCommand> commands);
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PostUploadHook.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PostUploadHook.java
index 09667eb01a..3aa3b127e5 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PostUploadHook.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PostUploadHook.java
@@ -57,7 +57,7 @@ import org.eclipse.jgit.storage.pack.PackStatistics;
*/
public interface PostUploadHook {
/** A simple no-op hook. */
- public static final PostUploadHook NULL = new PostUploadHook() {
+ PostUploadHook NULL = new PostUploadHook() {
@Override
public void onPostUpload(PackStatistics stats) {
// Do nothing.
@@ -72,5 +72,5 @@ public interface PostUploadHook {
* {@link org.eclipse.jgit.internal.storage.pack.PackWriter} for
* the uploaded pack
*/
- public void onPostUpload(PackStatistics stats);
+ void onPostUpload(PackStatistics stats);
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PreReceiveHook.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PreReceiveHook.java
index 77c1a8af29..30845d3b68 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PreReceiveHook.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PreReceiveHook.java
@@ -79,7 +79,7 @@ import java.util.Collection;
*/
public interface PreReceiveHook {
/** A simple no-op hook. */
- public static final PreReceiveHook NULL = new PreReceiveHook() {
+ PreReceiveHook NULL = new PreReceiveHook() {
@Override
public void onPreReceive(final ReceivePack rp,
final Collection<ReceiveCommand> commands) {
@@ -99,5 +99,5 @@ public interface PreReceiveHook {
* unmodifiable set of valid commands still pending execution.
* May be the empty set.
*/
- public void onPreReceive(ReceivePack rp, Collection<ReceiveCommand> commands);
+ void onPreReceive(ReceivePack rp, Collection<ReceiveCommand> commands);
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PreUploadHook.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PreUploadHook.java
index 2e1cd5800a..65dc241584 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PreUploadHook.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PreUploadHook.java
@@ -59,7 +59,7 @@ import org.eclipse.jgit.lib.ObjectId;
*/
public interface PreUploadHook {
/** A simple no-op hook. */
- public static final PreUploadHook NULL = new PreUploadHook() {
+ PreUploadHook NULL = new PreUploadHook() {
@Override
public void onBeginNegotiateRound(UploadPack up,
Collection<? extends ObjectId> wants, int cntOffered)
@@ -96,7 +96,7 @@ public interface PreUploadHook {
* @throws org.eclipse.jgit.transport.ServiceMayNotContinueException
* abort; the message will be sent to the user.
*/
- public void onBeginNegotiateRound(UploadPack up,
+ void onBeginNegotiateRound(UploadPack up,
Collection<? extends ObjectId> wants, int cntOffered)
throws ServiceMayNotContinueException;
@@ -120,7 +120,7 @@ public interface PreUploadHook {
* @throws org.eclipse.jgit.transport.ServiceMayNotContinueException
* abort; the message will be sent to the user.
*/
- public void onEndNegotiateRound(UploadPack up,
+ void onEndNegotiateRound(UploadPack up,
Collection<? extends ObjectId> wants, int cntCommon,
int cntNotFound, boolean ready)
throws ServiceMayNotContinueException;
@@ -141,7 +141,7 @@ public interface PreUploadHook {
* @throws org.eclipse.jgit.transport.ServiceMayNotContinueException
* abort; the message will be sent to the user.
*/
- public void onSendPack(UploadPack up, Collection<? extends ObjectId> wants,
+ void onSendPack(UploadPack up, Collection<? extends ObjectId> wants,
Collection<? extends ObjectId> haves)
throws ServiceMayNotContinueException;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV0Parser.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV0Parser.java
new file mode 100644
index 0000000000..21498d6f5c
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV0Parser.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2018, Google LLC.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.transport;
+
+import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_FILTER;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.text.MessageFormat;
+
+import org.eclipse.jgit.errors.PackProtocolException;
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.transport.parser.FirstWant;
+import org.eclipse.jgit.lib.ObjectId;
+
+/**
+ * Parser for git protocol versions 0 and 1.
+ *
+ * It reads the lines coming through the {@link PacketLineIn} and builds a
+ * {@link FetchV0Request} object.
+ *
+ * It requires a transferConfig object to know if the server supports filters.
+ */
+final class ProtocolV0Parser {
+
+ private final TransferConfig transferConfig;
+
+ ProtocolV0Parser(TransferConfig transferConfig) {
+ this.transferConfig = transferConfig;
+ }
+
+ /**
+ * Parse an incoming protocol v1 upload request arguments from the wire.
+ *
+ * The incoming PacketLineIn is consumed until an END line, but the caller
+ * is responsible for closing it (if needed).
+ *
+ * @param pckIn
+ * incoming lines. This method will read until an END line.
+ * @return a FetchV0Request with the data received in the wire.
+ * @throws PackProtocolException
+ * @throws IOException
+ */
+ FetchV0Request recvWants(PacketLineIn pckIn)
+ throws PackProtocolException, IOException {
+ FetchV0Request.Builder reqBuilder = new FetchV0Request.Builder();
+
+ boolean isFirst = true;
+ boolean filterReceived = false;
+
+ for (;;) {
+ String line;
+ try {
+ line = pckIn.readString();
+ } catch (EOFException eof) {
+ if (isFirst) {
+ break;
+ }
+ throw eof;
+ }
+
+ if (line == PacketLineIn.END) {
+ break;
+ }
+
+ if (line.startsWith("deepen ")) { //$NON-NLS-1$
+ int depth = Integer.parseInt(line.substring(7));
+ if (depth <= 0) {
+ throw new PackProtocolException(
+ MessageFormat.format(JGitText.get().invalidDepth,
+ Integer.valueOf(depth)));
+ }
+ reqBuilder.setDepth(depth);
+ continue;
+ }
+
+ if (line.startsWith("shallow ")) { //$NON-NLS-1$
+ reqBuilder.addClientShallowCommit(
+ ObjectId.fromString(line.substring(8)));
+ continue;
+ }
+
+ if (transferConfig.isAllowFilter()
+ && line.startsWith(OPTION_FILTER + " ")) { //$NON-NLS-1$
+ String arg = line.substring(OPTION_FILTER.length() + 1);
+
+ if (filterReceived) {
+ throw new PackProtocolException(
+ JGitText.get().tooManyFilters);
+ }
+ filterReceived = true;
+
+ reqBuilder.setFilterBlobLimit(ProtocolV2Parser.filterLine(arg));
+ continue;
+ }
+
+ if (!line.startsWith("want ") || line.length() < 45) { //$NON-NLS-1$
+ throw new PackProtocolException(MessageFormat
+ .format(JGitText.get().expectedGot, "want", line)); //$NON-NLS-1$
+ }
+
+ if (isFirst) {
+ if (line.length() > 45) {
+ FirstWant firstLine = FirstWant.fromLine(line);
+ reqBuilder.addClientCapabilities(firstLine.getCapabilities());
+ reqBuilder.setAgent(firstLine.getAgent());
+ line = firstLine.getLine();
+ }
+ }
+
+ reqBuilder.addWantId(ObjectId.fromString(line.substring(5)));
+ isFirst = false;
+ }
+
+ return reqBuilder.build();
+ }
+
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2Parser.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2Parser.java
index 2cc50a7f38..8f4b86ee0a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2Parser.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2Parser.java
@@ -44,15 +44,20 @@ package org.eclipse.jgit.transport;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_DEEPEN_RELATIVE;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_FILTER;
+import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_AGENT;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_INCLUDE_TAG;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_NO_PROGRESS;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_OFS_DELTA;
+import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SERVER_OPTION;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDE_BAND_64K;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_THIN_PACK;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_WANT_REF;
import java.io.IOException;
import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
import org.eclipse.jgit.errors.PackProtocolException;
import org.eclipse.jgit.internal.JGitText;
@@ -73,6 +78,35 @@ final class ProtocolV2Parser {
this.transferConfig = transferConfig;
}
+ /*
+ * Read lines until DELIM or END, calling the appropiate consumer.
+ *
+ * Returns the last read line (so caller can check if there is more to read
+ * in the line).
+ */
+ private static String consumeCapabilities(PacketLineIn pckIn,
+ Consumer<String> serverOptionConsumer,
+ Consumer<String> agentConsumer) throws IOException {
+
+ String serverOptionPrefix = OPTION_SERVER_OPTION + '=';
+ String agentPrefix = OPTION_AGENT + '=';
+
+ String line = pckIn.readString();
+ while (line != PacketLineIn.DELIM && line != PacketLineIn.END) {
+ if (line.startsWith(serverOptionPrefix)) {
+ serverOptionConsumer
+ .accept(line.substring(serverOptionPrefix.length()));
+ } else if (line.startsWith(agentPrefix)) {
+ agentConsumer.accept(line.substring(agentPrefix.length()));
+ } else {
+ // Unrecognized capability. Ignore it.
+ }
+ line = pckIn.readString();
+ }
+
+ return line;
+ }
+
/**
* Parse the incoming fetch request arguments from the wire. The caller must
* be sure that what is comings is a fetch request before coming here.
@@ -93,21 +127,26 @@ final class ProtocolV2Parser {
// Packs are always sent multiplexed and using full 64K
// lengths.
- reqBuilder.addOption(OPTION_SIDE_BAND_64K);
+ reqBuilder.addClientCapability(OPTION_SIDE_BAND_64K);
- String line;
+ String line = consumeCapabilities(pckIn,
+ serverOption -> reqBuilder.addServerOption(serverOption),
+ agent -> reqBuilder.setAgent(agent));
- // Currently, we do not support any capabilities, so the next
- // line is DELIM.
- if ((line = pckIn.readString()) != PacketLineIn.DELIM) {
- throw new PackProtocolException(MessageFormat
- .format(JGitText.get().unexpectedPacketLine, line));
+ if (line == PacketLineIn.END) {
+ return reqBuilder.build();
+ }
+
+ if (line != PacketLineIn.DELIM) {
+ throw new PackProtocolException(
+ MessageFormat.format(JGitText.get().unexpectedPacketLine,
+ line));
}
boolean filterReceived = false;
while ((line = pckIn.readString()) != PacketLineIn.END) {
if (line.startsWith("want ")) { //$NON-NLS-1$
- reqBuilder.addWantsId(ObjectId.fromString(line.substring(5)));
+ reqBuilder.addWantId(ObjectId.fromString(line.substring(5)));
} else if (transferConfig.isAllowRefInWant()
&& line.startsWith(OPTION_WANT_REF + " ")) { //$NON-NLS-1$
reqBuilder.addWantedRef(line.substring(OPTION_WANT_REF.length() + 1));
@@ -116,13 +155,13 @@ final class ProtocolV2Parser {
} else if (line.equals("done")) { //$NON-NLS-1$
reqBuilder.setDoneReceived();
} else if (line.equals(OPTION_THIN_PACK)) {
- reqBuilder.addOption(OPTION_THIN_PACK);
+ reqBuilder.addClientCapability(OPTION_THIN_PACK);
} else if (line.equals(OPTION_NO_PROGRESS)) {
- reqBuilder.addOption(OPTION_NO_PROGRESS);
+ reqBuilder.addClientCapability(OPTION_NO_PROGRESS);
} else if (line.equals(OPTION_INCLUDE_TAG)) {
- reqBuilder.addOption(OPTION_INCLUDE_TAG);
+ reqBuilder.addClientCapability(OPTION_INCLUDE_TAG);
} else if (line.equals(OPTION_OFS_DELTA)) {
- reqBuilder.addOption(OPTION_OFS_DELTA);
+ reqBuilder.addClientCapability(OPTION_OFS_DELTA);
} else if (line.startsWith("shallow ")) { //$NON-NLS-1$
reqBuilder.addClientShallowCommit(
ObjectId.fromString(line.substring(8)));
@@ -149,7 +188,7 @@ final class ProtocolV2Parser {
JGitText.get().deepenNotWithDeepen);
}
} else if (line.equals(OPTION_DEEPEN_RELATIVE)) {
- reqBuilder.addOption(OPTION_DEEPEN_RELATIVE);
+ reqBuilder.addClientCapability(OPTION_DEEPEN_RELATIVE);
} else if (line.startsWith("deepen-since ")) { //$NON-NLS-1$
int ts = Integer.parseInt(line.substring(13));
if (ts <= 0) {
@@ -180,6 +219,57 @@ final class ProtocolV2Parser {
}
/**
+ * Parse the incoming ls-refs request arguments from the wire. This is meant
+ * for calling immediately after the caller has consumed a "command=ls-refs"
+ * line indicating the beginning of a ls-refs request.
+ *
+ * The incoming PacketLineIn is consumed until an END line, but the caller
+ * is responsible for closing it (if needed)
+ *
+ * @param pckIn
+ * incoming lines. This method will read until an END line.
+ * @return a LsRefsV2Request object with the data received in the wire.
+ * @throws PackProtocolException
+ * for inconsistencies in the protocol (e.g. unexpected lines)
+ * @throws IOException
+ * reporting problems reading the incoming messages from the
+ * wire
+ */
+ LsRefsV2Request parseLsRefsRequest(PacketLineIn pckIn)
+ throws PackProtocolException, IOException {
+ LsRefsV2Request.Builder builder = LsRefsV2Request.builder();
+ List<String> prefixes = new ArrayList<>();
+
+ String line = consumeCapabilities(pckIn,
+ serverOption -> builder.addServerOption(serverOption),
+ agent -> builder.setAgent(agent));
+
+ if (line == PacketLineIn.END) {
+ return builder.build();
+ }
+
+ if (line != PacketLineIn.DELIM) {
+ throw new PackProtocolException(MessageFormat
+ .format(JGitText.get().unexpectedPacketLine, line));
+ }
+
+ while ((line = pckIn.readString()) != PacketLineIn.END) {
+ if (line.equals("peel")) { //$NON-NLS-1$
+ builder.setPeel(true);
+ } else if (line.equals("symrefs")) { //$NON-NLS-1$
+ builder.setSymrefs(true);
+ } else if (line.startsWith("ref-prefix ")) { //$NON-NLS-1$
+ prefixes.add(line.substring("ref-prefix ".length())); //$NON-NLS-1$
+ } else {
+ throw new PackProtocolException(MessageFormat
+ .format(JGitText.get().unexpectedPacketLine, line));
+ }
+ }
+
+ return builder.setRefPrefixes(prefixes).build();
+ }
+
+ /*
* Process the content of "filter" line from the protocol. It has a shape
* like "blob:none" or "blob:limit=N", with limit a positive number.
*
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushConnection.java
index ff2939a3d6..7f98d4dcc9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushConnection.java
@@ -113,7 +113,7 @@ public interface PushConnection extends Connection {
* created. Non-critical errors concerning only isolated refs
* should be placed in refUpdates.
*/
- public void push(final ProgressMonitor monitor,
+ void push(final ProgressMonitor monitor,
final Map<String, RemoteRefUpdate> refUpdates)
throws TransportException;
@@ -163,7 +163,7 @@ public interface PushConnection extends Connection {
* should be placed in refUpdates.
* @since 3.0
*/
- public void push(final ProgressMonitor monitor,
+ void push(final ProgressMonitor monitor,
final Map<String, RemoteRefUpdate> refUpdates, OutputStream out)
throws TransportException;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java
index 35fb0b17a7..577aaf4e9e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java
@@ -76,8 +76,6 @@ public class ReceivePack extends BaseReceivePack {
/** If {@link BasePackPushConnection#CAPABILITY_REPORT_STATUS} is enabled. */
private boolean reportStatus;
- private boolean echoCommandFailures;
-
/** Whether the client intends to use push options. */
private boolean usePushOptions;
private List<String> pushOptions;
@@ -191,9 +189,11 @@ public class ReceivePack extends BaseReceivePack {
* messages before sending the command results. This is usually
* not necessary, but may help buggy Git clients that discard the
* errors when all branches fail.
+ * @deprecated no widely used Git versions need this any more
*/
+ @Deprecated
public void setEchoCommandFailures(boolean echo) {
- echoCommandFailures = echo;
+ // No-op.
}
/**
@@ -269,36 +269,28 @@ public class ReceivePack extends BaseReceivePack {
}
}
- if (unpackError == null) {
- boolean atomic = isCapabilityEnabled(CAPABILITY_ATOMIC);
- setAtomic(atomic);
+ try {
+ if (unpackError == null) {
+ boolean atomic = isCapabilityEnabled(CAPABILITY_ATOMIC);
+ setAtomic(atomic);
- validateCommands();
- if (atomic && anyRejects())
- failPendingCommands();
+ validateCommands();
+ if (atomic && anyRejects()) {
+ failPendingCommands();
+ }
- preReceive.onPreReceive(this, filterCommands(Result.NOT_ATTEMPTED));
- if (atomic && anyRejects())
- failPendingCommands();
- executeCommands();
+ preReceive.onPreReceive(
+ this, filterCommands(Result.NOT_ATTEMPTED));
+ if (atomic && anyRejects()) {
+ failPendingCommands();
+ }
+ executeCommands();
+ }
+ } finally {
+ unlockPack();
}
- unlockPack();
if (reportStatus) {
- if (echoCommandFailures && msgOut != null) {
- sendStatusReport(false, unpackError, new Reporter() {
- @Override
- void sendString(String s) throws IOException {
- msgOut.write(Constants.encode(s + "\n")); //$NON-NLS-1$
- }
- });
- msgOut.flush();
- try {
- Thread.sleep(500);
- } catch (InterruptedException wakeUp) {
- // Ignore an early wake up.
- }
- }
sendStatusReport(true, unpackError, new Reporter() {
@Override
void sendString(String s) throws IOException {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefAdvertiser.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefAdvertiser.java
index 4662435ea7..6595cab71d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefAdvertiser.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefAdvertiser.java
@@ -207,7 +207,8 @@ public abstract class RefAdvertiser {
* <p>
* This method must be invoked prior to any of the following:
* <ul>
- * <li>{@link #send(Map)}
+ * <li>{@link #send(Map)}</li>
+ * <li>{@link #send(Collection)}</li>
* </ul>
*
* @param deref
@@ -223,8 +224,9 @@ public abstract class RefAdvertiser {
* <p>
* This method must be invoked prior to any of the following:
* <ul>
- * <li>{@link #send(Map)}
- * <li>{@link #advertiseHave(AnyObjectId)}
+ * <li>{@link #send(Map)}</li>
+ * <li>{@link #send(Collection)}</li>
+ * <li>{@link #advertiseHave(AnyObjectId)}</li>
* </ul>
*
* @param name
@@ -257,8 +259,9 @@ public abstract class RefAdvertiser {
* <p>
* This method must be invoked prior to any of the following:
* <ul>
- * <li>{@link #send(Map)}
- * <li>{@link #advertiseHave(AnyObjectId)}
+ * <li>{@link #send(Map)}</li>
+ * <li>{@link #send(Collection)}</li>
+ * <li>{@link #advertiseHave(AnyObjectId)}</li>
* </ul>
*
* @param from
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefFilter.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefFilter.java
index 992ddc6e53..d6d6198f5b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefFilter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefFilter.java
@@ -61,7 +61,7 @@ public interface RefFilter {
/**
* The default filter, allows all refs to be shown.
*/
- public static final RefFilter DEFAULT = new RefFilter() {
+ RefFilter DEFAULT = new RefFilter() {
@Override
public Map<String, Ref> filter (Map<String, Ref> refs) {
return refs;
@@ -76,5 +76,5 @@ public interface RefFilter {
* @return
* the filtered map of refs.
*/
- public Map<String, Ref> filter(Map<String, Ref> refs);
+ Map<String, Ref> filter(Map<String, Ref> refs);
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteRefUpdate.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteRefUpdate.java
index 931653fa90..9a67f0f8fe 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteRefUpdate.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteRefUpdate.java
@@ -521,7 +521,7 @@ public class RemoteRefUpdate {
: "(null)") + "..."
+ (newObjectId != null ? newObjectId.name() : "(null)")
+ (fastForward ? ", fastForward" : "")
- + ", srcRef=" + srcRef
+ + ", srcRef=" + srcRef
+ (forceUpdate ? ", forceUpdate" : "") + ", message="
+ (message != null ? "\"" + message + "\"" : "null") + "]";
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteSession.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteSession.java
index 525c895f45..e2109c2c5b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteSession.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteSession.java
@@ -78,10 +78,21 @@ public interface RemoteSession {
* a TransportException may be thrown (a subclass of
* java.io.IOException).
*/
- public Process exec(String commandName, int timeout) throws IOException;
+ Process exec(String commandName, int timeout) throws IOException;
+
+ /**
+ * Obtain an {@link FtpChannel} for performing FTP operations over this
+ * {@link RemoteSession}. The default implementation returns {@code null}.
+ *
+ * @return the {@link FtpChannel}
+ * @since 5.2
+ */
+ default FtpChannel getFtpChannel() {
+ return null;
+ }
/**
* Disconnect the remote session
*/
- public void disconnect();
+ void disconnect();
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SideBandInputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SideBandInputStream.java
index 90600cbb98..fde4401289 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SideBandInputStream.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SideBandInputStream.java
@@ -236,7 +236,7 @@ public class SideBandInputStream extends InputStream {
messages.write(msg);
if (out != null)
- out.write(msg.getBytes());
+ out.write(msg.getBytes(UTF_8));
}
private void beginTask(int totalWorkUnits) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshConstants.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshConstants.java
new file mode 100644
index 0000000000..2b79d7105c
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshConstants.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2018 Thomas Wolf <thomas.wolf@paranor.ch>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.transport;
+
+import org.eclipse.jgit.lib.Constants;
+
+/**
+ * Constants relating to ssh.
+ *
+ * @since 5.2
+ */
+@SuppressWarnings("nls")
+public final class SshConstants {
+
+ private SshConstants() {
+ // No instances, please.
+ }
+
+ /** IANA assigned port number for ssh. */
+ public static final int SSH_DEFAULT_PORT = 22;
+
+ /** URI scheme for ssh. */
+ public static final String SSH_SCHEME = "ssh";
+
+ /** URI scheme for sftp. */
+ public static final String SFTP_SCHEME = "sftp";
+
+ /** Default name for a ssh directory. */
+ public static final String SSH_DIR = ".ssh";
+
+ /** Name of the ssh config file. */
+ public static final String CONFIG = Constants.CONFIG;
+
+ /** Default name of the user "known hosts" file. */
+ public static final String KNOWN_HOSTS = "known_hosts";
+
+ // Config file keys
+
+ /** Key in an ssh config file. */
+ public static final String BATCH_MODE = "BatchMode";
+
+ /** Key in an ssh config file. */
+ public static final String CANONICAL_DOMAINS = "CanonicalDomains";
+
+ /** Key in an ssh config file. */
+ public static final String CERTIFICATE_FILE = "CertificateFile";
+
+ /** Key in an ssh config file. */
+ public static final String CIPHERS = "Ciphers";
+
+ /** Key in an ssh config file. */
+ public static final String COMPRESSION = "Compression";
+
+ /** Key in an ssh config file. */
+ public static final String CONNECTION_ATTEMPTS = "ConnectionAttempts";
+
+ /** Key in an ssh config file. */
+ public static final String CONTROL_PATH = "ControlPath";
+
+ /** Key in an ssh config file. */
+ public static final String GLOBAL_KNOWN_HOSTS_FILE = "GlobalKnownHostsFile";
+
+ /** Key in an ssh config file. */
+ public static final String HOST = "Host";
+
+ /** Key in an ssh config file. */
+ public static final String HOST_KEY_ALGORITHMS = "HostKeyAlgorithms";
+
+ /** Key in an ssh config file. */
+ public static final String HOST_NAME = "HostName";
+
+ /** Key in an ssh config file. */
+ public static final String IDENTITIES_ONLY = "IdentitiesOnly";
+
+ /** Key in an ssh config file. */
+ public static final String IDENTITY_AGENT = "IdentityAgent";
+
+ /** Key in an ssh config file. */
+ public static final String IDENTITY_FILE = "IdentityFile";
+
+ /** Key in an ssh config file. */
+ public static final String KEX_ALGORITHMS = "KexAlgorithms";
+
+ /** Key in an ssh config file. */
+ public static final String LOCAL_COMMAND = "LocalCommand";
+
+ /** Key in an ssh config file. */
+ public static final String LOCAL_FORWARD = "LocalForward";
+
+ /** Key in an ssh config file. */
+ public static final String MACS = "MACs";
+
+ /** Key in an ssh config file. */
+ public static final String NUMBER_OF_PASSWORD_PROMPTS = "NumberOfPasswordPrompts";
+
+ /** Key in an ssh config file. */
+ public static final String PORT = "Port";
+
+ /** Key in an ssh config file. */
+ public static final String PREFERRED_AUTHENTICATIONS = "PreferredAuthentications";
+
+ /** Key in an ssh config file. */
+ public static final String PROXY_COMMAND = "ProxyCommand";
+
+ /** Key in an ssh config file. */
+ public static final String REMOTE_COMMAND = "RemoteCommand";
+
+ /** Key in an ssh config file. */
+ public static final String REMOTE_FORWARD = "RemoteForward";
+
+ /** Key in an ssh config file. */
+ public static final String SEND_ENV = "SendEnv";
+
+ /** Key in an ssh config file. */
+ public static final String STRICT_HOST_KEY_CHECKING = "StrictHostKeyChecking";
+
+ /** Key in an ssh config file. */
+ public static final String USER = "User";
+
+ /** Key in an ssh config file. */
+ public static final String USER_KNOWN_HOSTS_FILE = "UserKnownHostsFile";
+
+ // Values
+
+ /** Flag value. */
+ public static final String YES = "yes";
+
+ /** Flag value. */
+ public static final String ON = "on";
+
+ /** Flag value. */
+ public static final String TRUE = "true";
+
+ /** Flag value. */
+ public static final String NO = "no";
+
+ /** Flag value. */
+ public static final String OFF = "off";
+
+ /** Flag value. */
+ public static final String FALSE = "false";
+
+ // Default identity file names
+
+ /** Name of the default RSA private identity file. */
+ public static final String ID_RSA = "id_rsa";
+
+ /** Name of the default DSA private identity file. */
+ public static final String ID_DSA = "id_dsa";
+
+ /** Name of the default ECDSA private identity file. */
+ public static final String ID_ECDSA = "id_ecdsa";
+
+ /** Name of the default ECDSA private identity file. */
+ public static final String ID_ED25519 = "id_ed25519";
+
+ /** All known default identity file names. */
+ public static final String[] DEFAULT_IDENTITIES = { //
+ ID_RSA, ID_DSA, ID_ECDSA, ID_ED25519
+ };
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshSessionFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshSessionFactory.java
index ae357dfb75..005a0c2d0e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshSessionFactory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshSessionFactory.java
@@ -44,8 +44,13 @@
package org.eclipse.jgit.transport;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
import org.eclipse.jgit.errors.TransportException;
+import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.util.FS;
+import org.eclipse.jgit.util.SystemReader;
/**
* Creates and destroys SSH connections to a remote system.
@@ -88,21 +93,38 @@ public abstract class SshSessionFactory {
}
/**
+ * Retrieves the local user name as defined by the system property
+ * "user.name".
+ *
+ * @return the user name
+ * @since 5.2
+ */
+ public static String getLocalUserName() {
+ return AccessController.doPrivileged(new PrivilegedAction<String>() {
+ @Override
+ public String run() {
+ return SystemReader.getInstance()
+ .getProperty(Constants.OS_USER_NAME_KEY);
+ }
+ });
+ }
+
+ /**
* Open (or reuse) a session to a host.
* <p>
* A reasonable UserInfo that can interact with the end-user (if necessary)
* is installed on the returned session by this method.
* <p>
- * The caller must connect the session by invoking <code>connect()</code>
- * if it has not already been connected.
+ * The caller must connect the session by invoking <code>connect()</code> if
+ * it has not already been connected.
*
* @param uri
* URI information about the remote host
* @param credentialsProvider
* provider to support authentication, may be null.
* @param fs
- * the file system abstraction which will be necessary to
- * perform certain file system operations.
+ * the file system abstraction which will be necessary to perform
+ * certain file system operations.
* @param tms
* Timeout value, in milliseconds.
* @return a session that can contact the remote host.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransferConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransferConfig.java
index db95396047..a3e655cd92 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransferConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransferConfig.java
@@ -106,7 +106,8 @@ public class TransferConfig {
this.name = name;
}
- static @Nullable ProtocolVersion parse(@Nullable String name) {
+ @Nullable
+ static ProtocolVersion parse(@Nullable String name) {
if (name == null) {
return null;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportBundle.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportBundle.java
index 6a285e59f5..ee851cc620 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportBundle.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportBundle.java
@@ -58,5 +58,5 @@ public interface TransportBundle extends PackTransport {
/**
* Bundle signature
*/
- public static final String V2_BUNDLE_SIGNATURE = "# v2 git bundle"; //$NON-NLS-1$
+ String V2_BUNDLE_SIGNATURE = "# v2 git bundle"; //$NON-NLS-1$
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportSftp.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportSftp.java
index f129ba34da..5c68308f90 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportSftp.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportSftp.java
@@ -53,13 +53,14 @@ import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
-import java.util.Comparator;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
import org.eclipse.jgit.errors.NotSupportedException;
import org.eclipse.jgit.errors.TransportException;
@@ -73,12 +74,6 @@ import org.eclipse.jgit.lib.Ref.Storage;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.SymbolicRef;
-import com.jcraft.jsch.Channel;
-import com.jcraft.jsch.ChannelSftp;
-import com.jcraft.jsch.JSchException;
-import com.jcraft.jsch.SftpATTRS;
-import com.jcraft.jsch.SftpException;
-
/**
* Transport over the non-Git aware SFTP (SSH based FTP) protocol.
* <p>
@@ -158,24 +153,16 @@ public class TransportSftp extends SshTransport implements WalkTransport {
return r;
}
- ChannelSftp newSftp() throws TransportException {
- final int tms = getTimeout() > 0 ? getTimeout() * 1000 : 0;
- try {
- // @TODO: Fix so that this operation is generic and casting to
- // JschSession is no longer necessary.
- final Channel channel = ((JschSession) getSession())
- .getSftpChannel();
- channel.connect(tms);
- return (ChannelSftp) channel;
- } catch (JSchException je) {
- throw new TransportException(uri, je.getMessage(), je);
- }
+ FtpChannel newSftp() throws IOException {
+ FtpChannel channel = getSession().getFtpChannel();
+ channel.connect(getTimeout(), TimeUnit.SECONDS);
+ return channel;
}
class SftpObjectDB extends WalkRemoteObjectDatabase {
private final String objectsPath;
- private ChannelSftp ftp;
+ private FtpChannel ftp;
SftpObjectDB(String path) throws TransportException {
if (path.startsWith("/~")) //$NON-NLS-1$
@@ -187,13 +174,13 @@ public class TransportSftp extends SshTransport implements WalkTransport {
ftp.cd(path);
ftp.cd("objects"); //$NON-NLS-1$
objectsPath = ftp.pwd();
- } catch (TransportException err) {
- close();
- throw err;
- } catch (SftpException je) {
+ } catch (FtpChannel.FtpException f) {
throw new TransportException(MessageFormat.format(
JGitText.get().cannotEnterObjectsPath, path,
- je.getMessage()), je);
+ f.getMessage()), f);
+ } catch (IOException ioe) {
+ close();
+ throw new TransportException(uri, ioe.getMessage(), ioe);
}
}
@@ -204,13 +191,13 @@ public class TransportSftp extends SshTransport implements WalkTransport {
ftp.cd(parent.objectsPath);
ftp.cd(p);
objectsPath = ftp.pwd();
- } catch (TransportException err) {
- close();
- throw err;
- } catch (SftpException je) {
+ } catch (FtpChannel.FtpException f) {
throw new TransportException(MessageFormat.format(
JGitText.get().cannotEnterPathFromParent, p,
- parent.objectsPath, je.getMessage()), je);
+ parent.objectsPath, f.getMessage()), f);
+ } catch (IOException ioe) {
+ close();
+ throw new TransportException(uri, ioe.getMessage(), ioe);
}
}
@@ -238,41 +225,32 @@ public class TransportSftp extends SshTransport implements WalkTransport {
Collection<String> getPackNames() throws IOException {
final List<String> packs = new ArrayList<>();
try {
- @SuppressWarnings("unchecked")
- final Collection<ChannelSftp.LsEntry> list = ftp.ls("pack"); //$NON-NLS-1$
- final HashMap<String, ChannelSftp.LsEntry> files;
- final HashMap<String, Integer> mtimes;
-
- files = new HashMap<>();
- mtimes = new HashMap<>();
-
- for (ChannelSftp.LsEntry ent : list)
- files.put(ent.getFilename(), ent);
- for (ChannelSftp.LsEntry ent : list) {
- final String n = ent.getFilename();
- if (!n.startsWith("pack-") || !n.endsWith(".pack")) //$NON-NLS-1$ //$NON-NLS-2$
+ Collection<FtpChannel.DirEntry> list = ftp.ls("pack"); //$NON-NLS-1$
+ Set<String> files = list.stream()
+ .map(FtpChannel.DirEntry::getFilename)
+ .collect(Collectors.toSet());
+ HashMap<String, Long> mtimes = new HashMap<>();
+
+ for (FtpChannel.DirEntry ent : list) {
+ String n = ent.getFilename();
+ if (!n.startsWith("pack-") || !n.endsWith(".pack")) { //$NON-NLS-1$ //$NON-NLS-2$
continue;
-
- final String in = n.substring(0, n.length() - 5) + ".idx"; //$NON-NLS-1$
- if (!files.containsKey(in))
+ }
+ String in = n.substring(0, n.length() - 5) + ".idx"; //$NON-NLS-1$
+ if (!files.contains(in)) {
continue;
-
- mtimes.put(n, Integer.valueOf(ent.getAttrs().getMTime()));
+ }
+ mtimes.put(n, Long.valueOf(ent.getModifiedTime()));
packs.add(n);
}
- Collections.sort(packs, new Comparator<String>() {
- @Override
- public int compare(String o1, String o2) {
- return mtimes.get(o2).intValue()
- - mtimes.get(o1).intValue();
- }
- });
- } catch (SftpException je) {
+ Collections.sort(packs,
+ (o1, o2) -> mtimes.get(o2).compareTo(mtimes.get(o1)));
+ } catch (FtpChannel.FtpException f) {
throw new TransportException(
MessageFormat.format(JGitText.get().cannotListPackPath,
- objectsPath, je.getMessage()),
- je);
+ objectsPath, f.getMessage()),
+ f);
}
return packs;
}
@@ -280,27 +258,25 @@ public class TransportSftp extends SshTransport implements WalkTransport {
@Override
FileStream open(String path) throws IOException {
try {
- final SftpATTRS a = ftp.lstat(path);
- return new FileStream(ftp.get(path), a.getSize());
- } catch (SftpException je) {
- if (je.id == ChannelSftp.SSH_FX_NO_SUCH_FILE)
+ return new FileStream(ftp.get(path));
+ } catch (FtpChannel.FtpException f) {
+ if (f.getStatus() == FtpChannel.FtpException.NO_SUCH_FILE) {
throw new FileNotFoundException(path);
+ }
throw new TransportException(MessageFormat.format(
JGitText.get().cannotGetObjectsPath, objectsPath, path,
- je.getMessage()), je);
+ f.getMessage()), f);
}
}
@Override
void deleteFile(String path) throws IOException {
try {
- ftp.rm(path);
- } catch (SftpException je) {
- if (je.id == ChannelSftp.SSH_FX_NO_SUCH_FILE)
- return;
+ ftp.delete(path);
+ } catch (FtpChannel.FtpException f) {
throw new TransportException(MessageFormat.format(
JGitText.get().cannotDeleteObjectsPath, objectsPath,
- path, je.getMessage()), je);
+ path, f.getMessage()), f);
}
// Prune any now empty directories.
@@ -312,7 +288,7 @@ public class TransportSftp extends SshTransport implements WalkTransport {
dir = dir.substring(0, s);
ftp.rmdir(dir);
s = dir.lastIndexOf('/');
- } catch (SftpException je) {
+ } catch (IOException je) {
// If we cannot delete it, leave it alone. It may have
// entries still in it, or maybe we lack write access on
// the parent. Either way it isn't a fatal error.
@@ -323,25 +299,31 @@ public class TransportSftp extends SshTransport implements WalkTransport {
}
@Override
- OutputStream writeFile(final String path,
- final ProgressMonitor monitor, final String monitorTask)
- throws IOException {
+ OutputStream writeFile(String path, ProgressMonitor monitor,
+ String monitorTask) throws IOException {
+ Throwable err = null;
try {
return ftp.put(path);
- } catch (SftpException je) {
- if (je.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) {
+ } catch (FileNotFoundException e) {
+ mkdir_p(path);
+ } catch (FtpChannel.FtpException je) {
+ if (je.getStatus() == FtpChannel.FtpException.NO_SUCH_FILE) {
mkdir_p(path);
- try {
- return ftp.put(path);
- } catch (SftpException je2) {
- je = je2;
- }
+ } else {
+ err = je;
}
-
- throw new TransportException(MessageFormat.format(
- JGitText.get().cannotWriteObjectsPath, objectsPath,
- path, je.getMessage()), je);
}
+ if (err == null) {
+ try {
+ return ftp.put(path);
+ } catch (IOException e) {
+ err = e;
+ }
+ }
+ throw new TransportException(
+ MessageFormat.format(JGitText.get().cannotWriteObjectsPath,
+ objectsPath, path, err.getMessage()),
+ err);
}
@Override
@@ -351,15 +333,15 @@ public class TransportSftp extends SshTransport implements WalkTransport {
super.writeFile(lock, data);
try {
ftp.rename(lock, path);
- } catch (SftpException je) {
+ } catch (IOException e) {
throw new TransportException(MessageFormat.format(
JGitText.get().cannotWriteObjectsPath, objectsPath,
- path, je.getMessage()), je);
+ path, e.getMessage()), e);
}
} catch (IOException err) {
try {
ftp.rm(lock);
- } catch (SftpException e) {
+ } catch (IOException e) {
// Ignore deletion failure, we are already
// failing anyway.
}
@@ -373,23 +355,30 @@ public class TransportSftp extends SshTransport implements WalkTransport {
return;
path = path.substring(0, s);
+ Throwable err = null;
try {
ftp.mkdir(path);
- } catch (SftpException je) {
- if (je.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) {
+ return;
+ } catch (FileNotFoundException f) {
+ mkdir_p(path);
+ } catch (FtpChannel.FtpException je) {
+ if (je.getStatus() == FtpChannel.FtpException.NO_SUCH_FILE) {
mkdir_p(path);
- try {
- ftp.mkdir(path);
- return;
- } catch (SftpException je2) {
- je = je2;
- }
+ } else {
+ err = je;
}
-
- throw new TransportException(MessageFormat.format(
- JGitText.get().cannotMkdirObjectPath, objectsPath, path,
- je.getMessage()), je);
}
+ if (err == null) {
+ try {
+ ftp.mkdir(path);
+ return;
+ } catch (IOException e) {
+ err = e;
+ }
+ }
+ throw new TransportException(MessageFormat.format(
+ JGitText.get().cannotMkdirObjectPath, objectsPath, path,
+ err.getMessage()), err);
}
Map<String, Ref> readAdvertisedRefs() throws TransportException {
@@ -400,34 +389,33 @@ public class TransportSftp extends SshTransport implements WalkTransport {
return avail;
}
- @SuppressWarnings("unchecked")
- private void readLooseRefs(final TreeMap<String, Ref> avail,
- final String dir, final String prefix)
- throws TransportException {
- final Collection<ChannelSftp.LsEntry> list;
+ private void readLooseRefs(TreeMap<String, Ref> avail, String dir,
+ String prefix) throws TransportException {
+ final Collection<FtpChannel.DirEntry> list;
try {
list = ftp.ls(dir);
- } catch (SftpException je) {
+ } catch (IOException e) {
throw new TransportException(MessageFormat.format(
JGitText.get().cannotListObjectsPath, objectsPath, dir,
- je.getMessage()), je);
+ e.getMessage()), e);
}
- for (ChannelSftp.LsEntry ent : list) {
- final String n = ent.getFilename();
+ for (FtpChannel.DirEntry ent : list) {
+ String n = ent.getFilename();
if (".".equals(n) || "..".equals(n)) //$NON-NLS-1$ //$NON-NLS-2$
continue;
- final String nPath = dir + "/" + n; //$NON-NLS-1$
- if (ent.getAttrs().isDir())
+ String nPath = dir + "/" + n; //$NON-NLS-1$
+ if (ent.isDirectory()) {
readLooseRefs(avail, nPath, prefix + n + "/"); //$NON-NLS-1$
- else
+ } else {
readRef(avail, nPath, prefix + n);
+ }
}
}
- private Ref readRef(final TreeMap<String, Ref> avail,
- final String path, final String name) throws TransportException {
+ private Ref readRef(TreeMap<String, Ref> avail, String path,
+ String name) throws TransportException {
final String line;
try (BufferedReader br = openReader(path)) {
line = br.readLine();
@@ -439,10 +427,10 @@ public class TransportSftp extends SshTransport implements WalkTransport {
err.getMessage()), err);
}
- if (line == null)
+ if (line == null) {
throw new TransportException(
MessageFormat.format(JGitText.get().emptyRef, name));
-
+ }
if (line.startsWith("ref: ")) { //$NON-NLS-1$
final String target = line.substring("ref: ".length()); //$NON-NLS-1$
Ref r = avail.get(target);
@@ -467,8 +455,9 @@ public class TransportSftp extends SshTransport implements WalkTransport {
}
private Storage loose(Ref r) {
- if (r != null && r.getStorage() == Storage.PACKED)
+ if (r != null && r.getStorage() == Storage.PACKED) {
return Storage.LOOSE_PACKED;
+ }
return Storage.LOOSE;
}
@@ -476,8 +465,9 @@ public class TransportSftp extends SshTransport implements WalkTransport {
void close() {
if (ftp != null) {
try {
- if (ftp.isConnected())
+ if (ftp.isConnected()) {
ftp.disconnect();
+ }
} finally {
ftp = null;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java
index 026fd819c4..70fb1f0e56 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java
@@ -48,10 +48,11 @@
package org.eclipse.jgit.transport;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.Serializable;
-import java.io.UnsupportedEncodingException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.BitSet;
@@ -282,12 +283,7 @@ public class URIish implements Serializable {
if (s.indexOf('%') < 0)
return s;
- byte[] bytes;
- try {
- bytes = s.getBytes(Constants.CHARACTER_ENCODING);
- } catch (UnsupportedEncodingException e) {
- throw new RuntimeException(e); // can't happen
- }
+ byte[] bytes = s.getBytes(UTF_8);
byte[] os = new byte[bytes.length];
int j = 0;
@@ -335,12 +331,7 @@ public class URIish implements Serializable {
if (s == null)
return null;
ByteArrayOutputStream os = new ByteArrayOutputStream(s.length());
- byte[] bytes;
- try {
- bytes = s.getBytes(Constants.CHARACTER_ENCODING);
- } catch (UnsupportedEncodingException e) {
- throw new RuntimeException(e); // cannot happen
- }
+ byte[] bytes = s.getBytes(UTF_8);
for (int i = 0; i < bytes.length; ++i) {
int b = bytes[i] & 0xFF;
if (b <= 32 || (encodeNonAscii && b > 127) || b == '%'
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
index 48a3e0b38f..2fbcaa2928 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
@@ -49,6 +49,7 @@ import static org.eclipse.jgit.lib.Constants.R_TAGS;
import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_REF_IN_WANT;
import static org.eclipse.jgit.transport.GitProtocolConstants.COMMAND_FETCH;
import static org.eclipse.jgit.transport.GitProtocolConstants.COMMAND_LS_REFS;
+import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_SERVER_OPTION;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_AGENT;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_ALLOW_REACHABLE_SHA1_IN_WANT;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_ALLOW_TIP_SHA1_IN_WANT;
@@ -70,11 +71,11 @@ import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.io.UncheckedIOException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -88,6 +89,7 @@ import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.errors.PackProtocolException;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.internal.storage.pack.PackWriter;
+import org.eclipse.jgit.internal.transport.parser.FirstWant;
import org.eclipse.jgit.lib.BitmapIndex;
import org.eclipse.jgit.lib.BitmapIndex.BitmapBuilder;
import org.eclipse.jgit.lib.Constants;
@@ -96,6 +98,7 @@ import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefDatabase;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.AsyncRevObjectQueue;
import org.eclipse.jgit.revwalk.BitmapWalker;
@@ -179,44 +182,53 @@ public class UploadPack {
throws PackProtocolException, IOException;
}
- /** Data in the first line of a request, the line itself plus options. */
+ /**
+ * Data in the first line of a want-list, the line itself plus options.
+ *
+ * @deprecated Use {@link FirstWant} instead
+ */
+ @Deprecated
public static class FirstLine {
- private final String line;
- private final Set<String> options;
+
+ private final FirstWant firstWant;
/**
- * Parse the first line of a receive-pack request.
- *
* @param line
* line from the client.
*/
public FirstLine(String line) {
- if (line.length() > 45) {
- final HashSet<String> opts = new HashSet<>();
- String opt = line.substring(45);
- if (opt.startsWith(" ")) //$NON-NLS-1$
- opt = opt.substring(1);
- for (String c : opt.split(" ")) //$NON-NLS-1$
- opts.add(c);
- this.line = line.substring(0, 45);
- this.options = Collections.unmodifiableSet(opts);
- } else {
- this.line = line;
- this.options = Collections.emptySet();
+ try {
+ firstWant = FirstWant.fromLine(line);
+ } catch (PackProtocolException e) {
+ throw new UncheckedIOException(e);
}
}
/** @return non-capabilities part of the line. */
public String getLine() {
- return line;
+ return firstWant.getLine();
}
- /** @return options parsed from the line. */
+ /** @return capabilities parsed from the line. */
public Set<String> getOptions() {
- return options;
+ if (firstWant.getAgent() != null) {
+ Set<String> caps = new HashSet<>(firstWant.getCapabilities());
+ caps.add(OPTION_AGENT + '=' + firstWant.getAgent());
+ return caps;
+ }
+ return firstWant.getCapabilities();
}
}
+ /*
+ * {@link java.util.function.Consumer} doesn't allow throwing checked
+ * exceptions. Define our own to propagate IOExceptions.
+ */
+ @FunctionalInterface
+ private static interface IOConsumer<R> {
+ void accept(R t) throws IOException;
+ }
+
/** Database we read the objects from. */
private final Repository db;
@@ -288,12 +300,11 @@ public class UploadPack {
/** Hook for taking post upload actions. */
private PostUploadHook postUploadHook = PostUploadHook.NULL;
- /** Capabilities requested by the client. */
- private Set<String> options;
+ /** Caller user agent */
String userAgent;
/** Raw ObjectIds the client has asked for, before validating them. */
- private final Set<ObjectId> wantIds = new HashSet<>();
+ private Set<ObjectId> wantIds = new HashSet<>();
/** Objects the client wants to obtain. */
private final Set<RevObject> wantAll = new HashSet<>();
@@ -301,25 +312,6 @@ public class UploadPack {
/** Objects on both sides, these don't have to be sent. */
private final Set<RevObject> commonBase = new HashSet<>();
- /** Shallow commits the client already has. */
- private Set<ObjectId> clientShallowCommits = new HashSet<>();
-
- /** Desired depth from the client on a shallow request. */
- private int depth;
-
- /**
- * Commit time of the newest objects the client has asked us using
- * --shallow-since not to send. Cannot be nonzero if depth is nonzero.
- */
- private int shallowSince;
-
- /**
- * (Possibly short) ref names, ancestors of which the client has asked us
- * not to send using --shallow-exclude. Cannot be non-empty if depth is
- * nonzero.
- */
- private List<String> deepenNotRefs = new ArrayList<>();
-
/** Commit time of the oldest common commit, in seconds. */
private int oldestTime;
@@ -353,7 +345,14 @@ public class UploadPack {
private PackStatistics statistics;
- private long filterBlobLimit = -1;
+ /**
+ * Request this instance is handling.
+ *
+ * We need to keep a reference to it for {@link PreUploadHook pre upload
+ * hooks}. They receive a reference this instance and invoke methods like
+ * getDepth() to get information about the request.
+ */
+ private FetchRequest currentRequest;
/**
* Create a new pack upload for an open repository.
@@ -695,10 +694,12 @@ public class UploadPack {
* read.
*/
public boolean isSideBand() throws RequestNotYetReadException {
- if (options == null)
+ if (currentRequest == null) {
throw new RequestNotYetReadException();
- return (options.contains(OPTION_SIDE_BAND)
- || options.contains(OPTION_SIDE_BAND_64K));
+ }
+ Set<String> caps = currentRequest.getClientCapabilities();
+ return caps.contains(OPTION_SIDE_BAND)
+ || caps.contains(OPTION_SIDE_BAND_64K);
}
/**
@@ -829,12 +830,10 @@ public class UploadPack {
}
if (refs == null) {
// Fast path: the advertised refs hook did not set advertised refs.
- Map<String, Ref> rs = new HashMap<>();
- for (String p : refPrefixes) {
- for (Ref r : db.getRefDatabase().getRefsByPrefix(p)) {
- rs.put(r.getName(), r);
- }
- }
+ String[] prefixes = refPrefixes.toArray(new String[0]);
+ Map<String, Ref> rs =
+ db.getRefDatabase().getRefsByPrefix(prefixes).stream()
+ .collect(toMap(Ref::getName, identity(), (a, b) -> b));
if (refFilter != RefFilter.DEFAULT) {
return refFilter.filter(rs);
}
@@ -880,12 +879,45 @@ public class UploadPack {
return getAdvertisedOrDefaultRefs().get(name);
}
+ /**
+ * Find a ref in the usual search path on behalf of the client.
+ * <p>
+ * This checks that the ref is present in the ref advertisement since
+ * otherwise the client might not be supposed to be able to read it.
+ *
+ * @param name
+ * short name of the ref to find, e.g. "master" to find
+ * "refs/heads/master".
+ * @return the requested Ref, or {@code null} if it is not visible or
+ * does not exist.
+ * @throws java.io.IOException
+ * on failure to read the ref or check it for visibility.
+ */
+ @Nullable
+ private Ref findRef(String name) throws IOException {
+ if (refs != null) {
+ return RefDatabase.findRef(refs, name);
+ }
+ if (!advertiseRefsHookCalled) {
+ advertiseRefsHook.advertiseRefs(this);
+ advertiseRefsHookCalled = true;
+ }
+ if (refs == null &&
+ refFilter == RefFilter.DEFAULT &&
+ transferConfig.hasDefaultRefFilter()) {
+ // Fast path: no ref filtering is needed.
+ return db.getRefDatabase().getRef(name);
+ }
+ return RefDatabase.findRef(getAdvertisedOrDefaultRefs(), name);
+ }
+
private void service() throws IOException {
boolean sendPack = false;
// If it's a non-bidi request, we need to read the entire request before
// writing a response. Buffer the response until then.
PackStatistics.Accumulator accumulator = new PackStatistics.Accumulator();
List<ObjectId> unshallowCommits = new ArrayList<>();
+ FetchRequest req;
try {
if (biDirectionalPipe)
sendAdvertisedRefs(new PacketLineOutRefAdvertiser(pckOut));
@@ -896,29 +928,46 @@ public class UploadPack {
long negotiateStart = System.currentTimeMillis();
accumulator.advertised = advertised.size();
- recvWants();
- if (wantIds.isEmpty()) {
- preUploadHook.onBeginNegotiateRound(this, wantIds, 0);
- preUploadHook.onEndNegotiateRound(this, wantIds, 0, 0, false);
+
+ ProtocolV0Parser parser = new ProtocolV0Parser(transferConfig);
+ req = parser.recvWants(pckIn);
+ currentRequest = req;
+
+ wantIds = req.getWantIds();
+
+ if (req.getWantIds().isEmpty()) {
+ preUploadHook.onBeginNegotiateRound(this, req.getWantIds(), 0);
+ preUploadHook.onEndNegotiateRound(this, req.getWantIds(), 0, 0,
+ false);
return;
}
- accumulator.wants = wantIds.size();
+ accumulator.wants = req.getWantIds().size();
- if (options.contains(OPTION_MULTI_ACK_DETAILED)) {
+ if (req.getClientCapabilities().contains(OPTION_MULTI_ACK_DETAILED)) {
multiAck = MultiAck.DETAILED;
- noDone = options.contains(OPTION_NO_DONE);
- } else if (options.contains(OPTION_MULTI_ACK))
+ noDone = req.getClientCapabilities().contains(OPTION_NO_DONE);
+ } else if (req.getClientCapabilities().contains(OPTION_MULTI_ACK))
multiAck = MultiAck.CONTINUE;
else
multiAck = MultiAck.OFF;
- if (!clientShallowCommits.isEmpty())
- verifyClientShallow(clientShallowCommits);
- if (depth != 0)
- processShallow(null, unshallowCommits, true);
- if (!clientShallowCommits.isEmpty())
- walk.assumeShallow(clientShallowCommits);
- sendPack = negotiate(accumulator);
+ if (!req.getClientShallowCommits().isEmpty()) {
+ verifyClientShallow(req.getClientShallowCommits());
+ }
+
+ if (req.getDepth() != 0 || req.getDeepenSince() != 0) {
+ computeShallowsAndUnshallows(req, shallow -> {
+ pckOut.writeString("shallow " + shallow.name() + '\n'); //$NON-NLS-1$
+ }, unshallow -> {
+ pckOut.writeString("unshallow " + unshallow.name() + '\n'); //$NON-NLS-1$
+ unshallowCommits.add(unshallow);
+ }, Collections.emptyList());
+ pckOut.end();
+ }
+
+ if (!req.getClientShallowCommits().isEmpty())
+ walk.assumeShallow(req.getClientShallowCommits());
+ sendPack = negotiate(req, accumulator);
accumulator.timeNegotiating += System.currentTimeMillis()
- negotiateStart;
@@ -968,35 +1017,14 @@ public class UploadPack {
}
if (sendPack) {
- sendPack(accumulator, refs == null ? null : refs.values(), unshallowCommits);
+ sendPack(accumulator, req, refs == null ? null : refs.values(),
+ unshallowCommits, Collections.emptyList());
}
}
private void lsRefsV2() throws IOException {
- LsRefsV2Request.Builder builder = LsRefsV2Request.builder();
- List<String> prefixes = new ArrayList<>();
- String line = pckIn.readString();
- // Currently, we do not support any capabilities, so the next
- // line is DELIM if there are arguments or END if not.
- if (line == PacketLineIn.DELIM) {
- while ((line = pckIn.readString()) != PacketLineIn.END) {
- if (line.equals("peel")) { //$NON-NLS-1$
- builder.setPeel(true);
- } else if (line.equals("symrefs")) { //$NON-NLS-1$
- builder.setSymrefs(true);
- } else if (line.startsWith("ref-prefix ")) { //$NON-NLS-1$
- prefixes.add(line.substring("ref-prefix ".length())); //$NON-NLS-1$
- } else {
- throw new PackProtocolException(MessageFormat
- .format(JGitText.get().unexpectedPacketLine, line));
- }
- }
- } else if (line != PacketLineIn.END) {
- throw new PackProtocolException(MessageFormat
- .format(JGitText.get().unexpectedPacketLine, line));
- }
- LsRefsV2Request req = builder.setRefPrefixes(prefixes).build();
-
+ ProtocolV2Parser parser = new ProtocolV2Parser(transferConfig);
+ LsRefsV2Request req = parser.parseLsRefsRequest(pckIn);
protocolV2Hook.onLsRefs(req);
rawOut.stopBuffering();
@@ -1029,20 +1057,23 @@ public class UploadPack {
ProtocolV2Parser parser = new ProtocolV2Parser(transferConfig);
FetchV2Request req = parser.parseFetchRequest(pckIn);
+ currentRequest = req;
rawOut.stopBuffering();
protocolV2Hook.onFetch(req);
// TODO(ifrade): Refactor to pass around the Request object, instead of
// copying data back to class fields
- options = req.getOptions();
- clientShallowCommits = req.getClientShallowCommits();
- depth = req.getDepth();
- shallowSince = req.getDeepenSince();
- filterBlobLimit = req.getFilterBlobLimit();
- deepenNotRefs = req.getDeepenNotRefs();
-
- wantIds.addAll(req.getWantsIds());
+ List<ObjectId> deepenNots = new ArrayList<>();
+ for (String s : req.getDeepenNotRefs()) {
+ Ref ref = findRef(s);
+ if (ref == null) {
+ throw new PackProtocolException(MessageFormat
+ .format(JGitText.get().invalidRefName, s));
+ }
+ deepenNots.add(ref.getObjectId());
+ }
+
Map<String, ObjectId> wantedRefs = new TreeMap<>();
for (String refName : req.getWantedRefs()) {
Ref ref = getRef(refName);
@@ -1055,21 +1086,27 @@ public class UploadPack {
throw new PackProtocolException(MessageFormat
.format(JGitText.get().invalidRefName, refName));
}
- wantIds.add(oid);
+ // TODO(ifrade): Avoid mutating the parsed request.
+ req.getWantIds().add(oid);
wantedRefs.put(refName, oid);
}
+ wantIds = req.getWantIds();
boolean sectionSent = false;
- @Nullable List<ObjectId> shallowCommits = null;
+ boolean mayHaveShallow = req.getDepth() != 0
+ || req.getDeepenSince() != 0
+ || !req.getDeepenNotRefs().isEmpty();
+ List<ObjectId> shallowCommits = new ArrayList<>();
List<ObjectId> unshallowCommits = new ArrayList<>();
if (!req.getClientShallowCommits().isEmpty()) {
verifyClientShallow(req.getClientShallowCommits());
}
- if (req.getDepth() != 0 || req.getDeepenSince() != 0
- || !req.getDeepenNotRefs().isEmpty()) {
- shallowCommits = new ArrayList<>();
- processShallow(shallowCommits, unshallowCommits, false);
+ if (mayHaveShallow) {
+ computeShallowsAndUnshallows(req,
+ shallowCommit -> shallowCommits.add(shallowCommit),
+ unshallowCommit -> unshallowCommits.add(unshallowCommit),
+ deepenNots);
}
if (!req.getClientShallowCommits().isEmpty())
walk.assumeShallow(req.getClientShallowCommits());
@@ -1095,7 +1132,7 @@ public class UploadPack {
}
if (req.wasDoneReceived() || okToGiveUp()) {
- if (shallowCommits != null) {
+ if (mayHaveShallow) {
if (sectionSent)
pckOut.writeDelim();
pckOut.writeString("shallow-info\n"); //$NON-NLS-1$
@@ -1125,10 +1162,11 @@ public class UploadPack {
pckOut.writeDelim();
pckOut.writeString("packfile\n"); //$NON-NLS-1$
sendPack(new PackStatistics.Accumulator(),
- req.getOptions().contains(OPTION_INCLUDE_TAG)
+ req,
+ req.getClientCapabilities().contains(OPTION_INCLUDE_TAG)
? db.getRefDatabase().getRefsByPrefix(R_TAGS)
: null,
- unshallowCommits);
+ unshallowCommits, deepenNots);
// sendPack invokes pckOut.end() for us, so we do not
// need to invoke it here.
} else {
@@ -1179,6 +1217,7 @@ public class UploadPack {
(transferConfig.isAllowFilter() ? OPTION_FILTER + ' ' : "") + //$NON-NLS-1$
(advertiseRefInWant ? CAPABILITY_REF_IN_WANT + ' ' : "") + //$NON-NLS-1$
OPTION_SHALLOW);
+ caps.add(CAPABILITY_SERVER_OPTION);
return caps;
}
@@ -1227,28 +1266,28 @@ public class UploadPack {
}
/*
- * Determines what "shallow" and "unshallow" lines to send to the user.
- * The information is written to shallowCommits (if not null) and
- * unshallowCommits, and also written to #pckOut (if writeToPckOut is
- * true).
+ * Determines what object ids must be marked as shallow or unshallow for the
+ * client.
*/
- private void processShallow(@Nullable List<ObjectId> shallowCommits,
- List<ObjectId> unshallowCommits,
- boolean writeToPckOut) throws IOException {
- if (options.contains(OPTION_DEEPEN_RELATIVE) ||
- shallowSince != 0 ||
- !deepenNotRefs.isEmpty()) {
- // TODO(jonathantanmy): Implement deepen-relative, deepen-since,
- // and deepen-not.
+ private void computeShallowsAndUnshallows(FetchRequest req,
+ IOConsumer<ObjectId> shallowFunc,
+ IOConsumer<ObjectId> unshallowFunc,
+ List<ObjectId> deepenNots)
+ throws IOException {
+ if (req.getClientCapabilities().contains(OPTION_DEEPEN_RELATIVE)) {
+ // TODO(jonathantanmy): Implement deepen-relative
throw new UnsupportedOperationException();
}
- int walkDepth = depth - 1;
+ int walkDepth = req.getDepth() == 0 ? Integer.MAX_VALUE
+ : req.getDepth() - 1;
try (DepthWalk.RevWalk depthWalk = new DepthWalk.RevWalk(
walk.getObjectReader(), walkDepth)) {
+ depthWalk.setDeepenSince(req.getDeepenSince());
+
// Find all the commits which will be shallow
- for (ObjectId o : wantIds) {
+ for (ObjectId o : req.getWantIds()) {
try {
depthWalk.markRoot(depthWalk.parseCommit(o));
} catch (IncorrectObjectTypeException notCommit) {
@@ -1256,35 +1295,32 @@ public class UploadPack {
}
}
+ depthWalk.setDeepenNots(deepenNots);
+
RevCommit o;
+ boolean atLeastOne = false;
while ((o = depthWalk.next()) != null) {
DepthWalk.Commit c = (DepthWalk.Commit) o;
+ atLeastOne = true;
+
+ boolean isBoundary = (c.getDepth() == walkDepth) || c.isBoundary();
// Commits at the boundary which aren't already shallow in
// the client need to be marked as such
- if (c.getDepth() == walkDepth
- && !clientShallowCommits.contains(c)) {
- if (shallowCommits != null) {
- shallowCommits.add(c.copy());
- }
- if (writeToPckOut) {
- pckOut.writeString("shallow " + o.name()); //$NON-NLS-1$
- }
+ if (isBoundary && !req.getClientShallowCommits().contains(c)) {
+ shallowFunc.accept(c.copy());
}
// Commits not on the boundary which are shallow in the client
// need to become unshallowed
- if (c.getDepth() < walkDepth
- && clientShallowCommits.remove(c)) {
- unshallowCommits.add(c.copy());
- if (writeToPckOut) {
- pckOut.writeString("unshallow " + c.name()); //$NON-NLS-1$
- }
+ if (!isBoundary && req.getClientShallowCommits().remove(c)) {
+ unshallowFunc.accept(c.copy());
}
}
- }
- if (writeToPckOut) {
- pckOut.end();
+ if (!atLeastOne) {
+ throw new PackProtocolException(
+ JGitText.get().noCommitsSelectedForShallow);
+ }
}
}
@@ -1436,67 +1472,6 @@ public class UploadPack {
return msgOut;
}
- private void recvWants() throws IOException {
- boolean isFirst = true;
- boolean filterReceived = false;
- for (;;) {
- String line;
- try {
- line = pckIn.readString();
- } catch (EOFException eof) {
- if (isFirst)
- break;
- throw eof;
- }
-
- if (line == PacketLineIn.END)
- break;
-
- if (line.startsWith("deepen ")) { //$NON-NLS-1$
- depth = Integer.parseInt(line.substring(7));
- if (depth <= 0) {
- throw new PackProtocolException(
- MessageFormat.format(JGitText.get().invalidDepth,
- Integer.valueOf(depth)));
- }
- continue;
- }
-
- if (line.startsWith("shallow ")) { //$NON-NLS-1$
- clientShallowCommits.add(ObjectId.fromString(line.substring(8)));
- continue;
- }
-
- if (transferConfig.isAllowFilter()
- && line.startsWith(OPTION_FILTER + " ")) { //$NON-NLS-1$
- String arg = line.substring(OPTION_FILTER.length() + 1);
-
- if (filterReceived) {
- throw new PackProtocolException(JGitText.get().tooManyFilters);
- }
- filterReceived = true;
-
- filterBlobLimit = ProtocolV2Parser.filterLine(arg);
- continue;
- }
-
- if (!line.startsWith("want ") || line.length() < 45) //$NON-NLS-1$
- throw new PackProtocolException(MessageFormat.format(JGitText.get().expectedGot, "want", line)); //$NON-NLS-1$
-
- if (isFirst) {
- if (line.length() > 45) {
- FirstLine firstLine = new FirstLine(line);
- options = firstLine.getOptions();
- line = firstLine.getLine();
- } else
- options = Collections.emptySet();
- }
-
- wantIds.add(ObjectId.fromString(line.substring(5)));
- isFirst = false;
- }
- }
-
/**
* Returns the clone/fetch depth. Valid only after calling recvWants(). A
* depth of 1 means return only the wants.
@@ -1505,9 +1480,9 @@ public class UploadPack {
* @since 4.0
*/
public int getDepth() {
- if (options == null)
+ if (currentRequest == null)
throw new RequestNotYetReadException();
- return depth;
+ return currentRequest.getDepth();
}
/**
@@ -1526,10 +1501,15 @@ public class UploadPack {
* @since 4.0
*/
public String getPeerUserAgent() {
- return UserAgent.getAgent(options, userAgent);
+ if (currentRequest != null && currentRequest.getAgent() != null) {
+ return currentRequest.getAgent();
+ }
+
+ return userAgent;
}
- private boolean negotiate(PackStatistics.Accumulator accumulator)
+ private boolean negotiate(FetchRequest req,
+ PackStatistics.Accumulator accumulator)
throws IOException {
okToGiveUp = Boolean.FALSE;
@@ -1545,7 +1525,7 @@ public class UploadPack {
// disconnected, and will try another request with actual want/have.
// Don't report the EOF here, its a bug in the protocol that the client
// just disconnects without sending an END.
- if (!biDirectionalPipe && depth > 0)
+ if (!biDirectionalPipe && req.getDepth() > 0)
return false;
throw eof;
}
@@ -1926,25 +1906,31 @@ public class UploadPack {
* Send the requested objects to the client.
*
* @param accumulator
- * where to write statistics about the content of the pack.
+ * where to write statistics about the content of the pack.
+ * @param req
+ * request in process
* @param allTags
- * refs to search for annotated tags to include in the pack
- * if the {@link #OPTION_INCLUDE_TAG} capability was
- * requested.
+ * refs to search for annotated tags to include in the pack if
+ * the {@link #OPTION_INCLUDE_TAG} capability was requested.
* @param unshallowCommits
- * shallow commits on the client that are now becoming
- * unshallow
+ * shallow commits on the client that are now becoming unshallow
+ * @param deepenNots
+ * objects that the client specified using --shallow-exclude
* @throws IOException
- * if an error occured while generating or writing the pack.
+ * if an error occurred while generating or writing the pack.
*/
private void sendPack(PackStatistics.Accumulator accumulator,
+ FetchRequest req,
@Nullable Collection<Ref> allTags,
- List<ObjectId> unshallowCommits) throws IOException {
- final boolean sideband = options.contains(OPTION_SIDE_BAND)
- || options.contains(OPTION_SIDE_BAND_64K);
+ List<ObjectId> unshallowCommits,
+ List<ObjectId> deepenNots) throws IOException {
+ Set<String> caps = req.getClientCapabilities();
+ boolean sideband = caps.contains(OPTION_SIDE_BAND)
+ || caps.contains(OPTION_SIDE_BAND_64K);
if (sideband) {
try {
- sendPack(true, accumulator, allTags, unshallowCommits);
+ sendPack(true, req, accumulator, allTags, unshallowCommits,
+ deepenNots);
} catch (ServiceMayNotContinueException noPack) {
// This was already reported on (below).
throw noPack;
@@ -1965,7 +1951,7 @@ public class UploadPack {
throw err;
}
} else {
- sendPack(false, accumulator, allTags, unshallowCommits);
+ sendPack(false, req, accumulator, allTags, unshallowCommits, deepenNots);
}
}
@@ -1989,35 +1975,39 @@ public class UploadPack {
* Send the requested objects to the client.
*
* @param sideband
- * whether to wrap the pack in side-band pkt-lines,
- * interleaved with progress messages and errors.
+ * whether to wrap the pack in side-band pkt-lines, interleaved
+ * with progress messages and errors.
+ * @param req
+ * request being processed
* @param accumulator
- * where to write statistics about the content of the pack.
+ * where to write statistics about the content of the pack.
* @param allTags
- * refs to search for annotated tags to include in the pack
- * if the {@link #OPTION_INCLUDE_TAG} capability was
- * requested.
+ * refs to search for annotated tags to include in the pack if
+ * the {@link #OPTION_INCLUDE_TAG} capability was requested.
* @param unshallowCommits
- * shallow commits on the client that are now becoming
- * unshallow
+ * shallow commits on the client that are now becoming unshallow
+ * @param deepenNots
+ * objects that the client specified using --shallow-exclude
* @throws IOException
- * if an error occured while generating or writing the pack.
+ * if an error occurred while generating or writing the pack.
*/
private void sendPack(final boolean sideband,
+ FetchRequest req,
PackStatistics.Accumulator accumulator,
@Nullable Collection<Ref> allTags,
- List<ObjectId> unshallowCommits) throws IOException {
+ List<ObjectId> unshallowCommits,
+ List<ObjectId> deepenNots) throws IOException {
ProgressMonitor pm = NullProgressMonitor.INSTANCE;
OutputStream packOut = rawOut;
if (sideband) {
int bufsz = SideBandOutputStream.SMALL_BUF;
- if (options.contains(OPTION_SIDE_BAND_64K))
+ if (req.getClientCapabilities().contains(OPTION_SIDE_BAND_64K))
bufsz = SideBandOutputStream.MAX_BUF;
packOut = new SideBandOutputStream(SideBandOutputStream.CH_DATA,
bufsz, rawOut);
- if (!options.contains(OPTION_NO_PROGRESS)) {
+ if (!req.getClientCapabilities().contains(OPTION_NO_PROGRESS)) {
msgOut = new SideBandOutputStream(
SideBandOutputStream.CH_PROGRESS, bufsz, rawOut);
pm = new SideBandProgressMonitor(msgOut);
@@ -2053,17 +2043,20 @@ public class UploadPack {
accumulator);
try {
pw.setIndexDisabled(true);
- if (filterBlobLimit >= 0) {
- pw.setFilterBlobLimit(filterBlobLimit);
+ if (req.getFilterBlobLimit() >= 0) {
+ pw.setFilterBlobLimit(req.getFilterBlobLimit());
pw.setUseCachedPacks(false);
} else {
pw.setUseCachedPacks(true);
}
- pw.setUseBitmaps(depth == 0 && clientShallowCommits.isEmpty());
- pw.setClientShallowCommits(clientShallowCommits);
+ pw.setUseBitmaps(
+ req.getDepth() == 0
+ && req.getClientShallowCommits().isEmpty());
+ pw.setClientShallowCommits(req.getClientShallowCommits());
pw.setReuseDeltaCommits(true);
- pw.setDeltaBaseAsOffset(options.contains(OPTION_OFS_DELTA));
- pw.setThin(options.contains(OPTION_THIN_PACK));
+ pw.setDeltaBaseAsOffset(
+ req.getClientCapabilities().contains(OPTION_OFS_DELTA));
+ pw.setThin(req.getClientCapabilities().contains(OPTION_THIN_PACK));
pw.setReuseValidatingObjects(false);
// Objects named directly by references go at the beginning
@@ -2082,14 +2075,22 @@ public class UploadPack {
}
RevWalk rw = walk;
- if (depth > 0) {
- pw.setShallowPack(depth, unshallowCommits);
- rw = new DepthWalk.RevWalk(walk.getObjectReader(), depth - 1);
- rw.assumeShallow(clientShallowCommits);
+ if (req.getDepth() > 0 || req.getDeepenSince() != 0 || !deepenNots.isEmpty()) {
+ int walkDepth = req.getDepth() == 0 ? Integer.MAX_VALUE
+ : req.getDepth() - 1;
+ pw.setShallowPack(req.getDepth(), unshallowCommits);
+
+ DepthWalk.RevWalk dw = new DepthWalk.RevWalk(
+ walk.getObjectReader(), walkDepth);
+ dw.setDeepenSince(req.getDeepenSince());
+ dw.setDeepenNots(deepenNots);
+ dw.assumeShallow(req.getClientShallowCommits());
+ rw = dw;
}
if (wantAll.isEmpty()) {
- pw.preparePack(pm, wantIds, commonBase, clientShallowCommits);
+ pw.preparePack(pm, wantIds, commonBase,
+ req.getClientShallowCommits());
} else {
walk.reset();
@@ -2098,7 +2099,8 @@ public class UploadPack {
rw = ow;
}
- if (options.contains(OPTION_INCLUDE_TAG) && allTags != null) {
+ if (req.getClientCapabilities().contains(OPTION_INCLUDE_TAG)
+ && allTags != null) {
for (Ref ref : allTags) {
ObjectId objectId = ref.getObjectId();
if (objectId == null) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/HttpConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/HttpConnection.java
index d815bc354e..c38b00287b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/HttpConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/HttpConnection.java
@@ -58,6 +58,8 @@ import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.KeyManager;
import javax.net.ssl.TrustManager;
+import org.eclipse.jgit.annotations.NonNull;
+
/**
* The interface of connections used during HTTP communication. This interface
* is that subset of the interface exposed by {@link java.net.HttpURLConnection}
@@ -69,25 +71,25 @@ public interface HttpConnection {
/**
* @see HttpURLConnection#HTTP_OK
*/
- public static final int HTTP_OK = java.net.HttpURLConnection.HTTP_OK;
+ int HTTP_OK = java.net.HttpURLConnection.HTTP_OK;
/**
* @see HttpURLConnection#HTTP_MOVED_PERM
* @since 4.7
*/
- public static final int HTTP_MOVED_PERM = java.net.HttpURLConnection.HTTP_MOVED_PERM;
+ int HTTP_MOVED_PERM = java.net.HttpURLConnection.HTTP_MOVED_PERM;
/**
* @see HttpURLConnection#HTTP_MOVED_TEMP
* @since 4.9
*/
- public static final int HTTP_MOVED_TEMP = java.net.HttpURLConnection.HTTP_MOVED_TEMP;
+ int HTTP_MOVED_TEMP = java.net.HttpURLConnection.HTTP_MOVED_TEMP;
/**
* @see HttpURLConnection#HTTP_SEE_OTHER
* @since 4.9
*/
- public static final int HTTP_SEE_OTHER = java.net.HttpURLConnection.HTTP_SEE_OTHER;
+ int HTTP_SEE_OTHER = java.net.HttpURLConnection.HTTP_SEE_OTHER;
/**
* HTTP 1.1 additional MOVED_TEMP status code; value = 307.
@@ -95,22 +97,22 @@ public interface HttpConnection {
* @see #HTTP_MOVED_TEMP
* @since 4.9
*/
- public static final int HTTP_11_MOVED_TEMP = 307;
+ int HTTP_11_MOVED_TEMP = 307;
/**
* @see HttpURLConnection#HTTP_NOT_FOUND
*/
- public static final int HTTP_NOT_FOUND = java.net.HttpURLConnection.HTTP_NOT_FOUND;
+ int HTTP_NOT_FOUND = java.net.HttpURLConnection.HTTP_NOT_FOUND;
/**
* @see HttpURLConnection#HTTP_UNAUTHORIZED
*/
- public static final int HTTP_UNAUTHORIZED = java.net.HttpURLConnection.HTTP_UNAUTHORIZED;
+ int HTTP_UNAUTHORIZED = java.net.HttpURLConnection.HTTP_UNAUTHORIZED;
/**
* @see HttpURLConnection#HTTP_FORBIDDEN
*/
- public static final int HTTP_FORBIDDEN = java.net.HttpURLConnection.HTTP_FORBIDDEN;
+ int HTTP_FORBIDDEN = java.net.HttpURLConnection.HTTP_FORBIDDEN;
/**
* Get response code
@@ -119,7 +121,7 @@ public interface HttpConnection {
* @return the HTTP Status-Code, or -1
* @throws java.io.IOException
*/
- public int getResponseCode() throws IOException;
+ int getResponseCode() throws IOException;
/**
* Get URL
@@ -127,7 +129,7 @@ public interface HttpConnection {
* @see HttpURLConnection#getURL()
* @return the URL.
*/
- public URL getURL();
+ URL getURL();
/**
* Get response message
@@ -136,15 +138,15 @@ public interface HttpConnection {
* @return the HTTP response message, or <code>null</code>
* @throws java.io.IOException
*/
- public String getResponseMessage() throws IOException;
+ String getResponseMessage() throws IOException;
/**
- * Get list of header fields
+ * Get map of header fields
*
* @see HttpURLConnection#getHeaderFields()
* @return a Map of header fields
*/
- public Map<String, List<String>> getHeaderFields();
+ Map<String, List<String>> getHeaderFields();
/**
* Set request property
@@ -156,7 +158,7 @@ public interface HttpConnection {
* @param value
* the value associated with it.
*/
- public void setRequestProperty(String key, String value);
+ void setRequestProperty(String key, String value);
/**
* Set request method
@@ -170,7 +172,7 @@ public interface HttpConnection {
* @throws java.net.ProtocolException
* if any.
*/
- public void setRequestMethod(String method)
+ void setRequestMethod(String method)
throws ProtocolException;
/**
@@ -181,7 +183,7 @@ public interface HttpConnection {
* a <code>boolean</code> indicating whether or not to allow
* caching
*/
- public void setUseCaches(boolean usecaches);
+ void setUseCaches(boolean usecaches);
/**
* Set connect timeout
@@ -191,7 +193,7 @@ public interface HttpConnection {
* an <code>int</code> that specifies the connect timeout value
* in milliseconds
*/
- public void setConnectTimeout(int timeout);
+ void setConnectTimeout(int timeout);
/**
* Set read timeout
@@ -201,7 +203,7 @@ public interface HttpConnection {
* an <code>int</code> that specifies the timeout value to be
* used in milliseconds
*/
- public void setReadTimeout(int timeout);
+ void setReadTimeout(int timeout);
/**
* Get content type
@@ -210,7 +212,7 @@ public interface HttpConnection {
* @return the content type of the resource that the URL references, or
* <code>null</code> if not known.
*/
- public String getContentType();
+ String getContentType();
/**
* Get input stream
@@ -222,10 +224,16 @@ public interface HttpConnection {
* @throws java.io.IOException
* if any.
*/
- public InputStream getInputStream() throws IOException;
+ InputStream getInputStream() throws IOException;
/**
- * Get header field
+ * Get header field. According to
+ * {@link <a href="https://tools.ietf.org/html/rfc2616#section-4.2">RFC
+ * 2616</a>} header field names are case insensitive. Header fields defined
+ * as a comma separated list can have multiple header fields with the same
+ * field name. This method only returns one of these header fields. If you
+ * want the union of all values of all multiple header fields with the same
+ * field name then use {@link #getHeaderFields(String)}
*
* @see HttpURLConnection#getHeaderField(String)
* @param name
@@ -233,7 +241,22 @@ public interface HttpConnection {
* @return the value of the named header field, or <code>null</code> if
* there is no such field in the header.
*/
- public String getHeaderField(String name);
+ String getHeaderField(@NonNull String name);
+
+ /**
+ * Get all values of given header field. According to
+ * {@link <a href="https://tools.ietf.org/html/rfc2616#section-4.2">RFC
+ * 2616</a>} header field names are case insensitive. Header fields defined
+ * as a comma separated list can have multiple header fields with the same
+ * field name. This method does not validate if the given header field is
+ * defined as a comma separated list.
+ *
+ * @param name
+ * the name of a header field.
+ * @return the list of values of the named header field
+ * @since 5.2
+ */
+ List<String> getHeaderFields(@NonNull String name);
/**
* Get content length
@@ -243,7 +266,7 @@ public interface HttpConnection {
* references, {@code -1} if the content length is not known, or if
* the content length is greater than Integer.MAX_VALUE.
*/
- public int getContentLength();
+ int getContentLength();
/**
* Set whether or not to follow HTTP redirects.
@@ -253,7 +276,7 @@ public interface HttpConnection {
* a <code>boolean</code> indicating whether or not to follow
* HTTP redirects.
*/
- public void setInstanceFollowRedirects(boolean followRedirects);
+ void setInstanceFollowRedirects(boolean followRedirects);
/**
* Set if to do output
@@ -262,7 +285,7 @@ public interface HttpConnection {
* @param dooutput
* the new value.
*/
- public void setDoOutput(boolean dooutput);
+ void setDoOutput(boolean dooutput);
/**
* Set fixed length streaming mode
@@ -271,7 +294,7 @@ public interface HttpConnection {
* @param contentLength
* The number of bytes which will be written to the OutputStream.
*/
- public void setFixedLengthStreamingMode(int contentLength);
+ void setFixedLengthStreamingMode(int contentLength);
/**
* Get output stream
@@ -280,7 +303,7 @@ public interface HttpConnection {
* @return an output stream that writes to this connection.
* @throws java.io.IOException
*/
- public OutputStream getOutputStream() throws IOException;
+ OutputStream getOutputStream() throws IOException;
/**
* Set chunked streaming mode
@@ -290,7 +313,7 @@ public interface HttpConnection {
* The number of bytes to write in each chunk. If chunklen is
* less than or equal to zero, a default value will be used.
*/
- public void setChunkedStreamingMode(int chunklen);
+ void setChunkedStreamingMode(int chunklen);
/**
* Get request method
@@ -298,7 +321,7 @@ public interface HttpConnection {
* @see HttpURLConnection#getRequestMethod()
* @return the HTTP request method
*/
- public String getRequestMethod();
+ String getRequestMethod();
/**
* Whether we use a proxy
@@ -306,7 +329,7 @@ public interface HttpConnection {
* @see HttpURLConnection#usingProxy()
* @return a boolean indicating if the connection is using a proxy.
*/
- public boolean usingProxy();
+ boolean usingProxy();
/**
* Connect
@@ -314,7 +337,7 @@ public interface HttpConnection {
* @see HttpURLConnection#connect()
* @throws java.io.IOException
*/
- public void connect() throws IOException;
+ void connect() throws IOException;
/**
* Configure the connection so that it can be used for https communication.
@@ -332,7 +355,7 @@ public interface HttpConnection {
* @throws java.security.NoSuchAlgorithmException
* @throws java.security.KeyManagementException
*/
- public void configure(KeyManager[] km, TrustManager[] tm,
+ void configure(KeyManager[] km, TrustManager[] tm,
SecureRandom random) throws NoSuchAlgorithmException,
KeyManagementException;
@@ -345,6 +368,6 @@ public interface HttpConnection {
* @throws java.security.NoSuchAlgorithmException
* @throws java.security.KeyManagementException
*/
- public void setHostnameVerifier(HostnameVerifier hostnameverifier)
+ void setHostnameVerifier(HostnameVerifier hostnameverifier)
throws NoSuchAlgorithmException, KeyManagementException;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/HttpConnectionFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/HttpConnectionFactory.java
index bd9d61fe66..11691451f2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/HttpConnectionFactory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/HttpConnectionFactory.java
@@ -62,7 +62,7 @@ public interface HttpConnectionFactory {
* @return a {@link org.eclipse.jgit.transport.http.HttpConnection}
* @throws java.io.IOException
*/
- public HttpConnection create(URL url) throws IOException;
+ HttpConnection create(URL url) throws IOException;
/**
* Creates a new connection to a destination defined by a
@@ -75,6 +75,6 @@ public interface HttpConnectionFactory {
* @return a {@link org.eclipse.jgit.transport.http.HttpConnection}
* @throws java.io.IOException
*/
- public HttpConnection create(URL url, Proxy proxy)
+ HttpConnection create(URL url, Proxy proxy)
throws IOException;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/JDKHttpConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/JDKHttpConnection.java
index 8241c59d2b..734b549294 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/JDKHttpConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/JDKHttpConnection.java
@@ -53,6 +53,7 @@ import java.net.URL;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@@ -62,6 +63,7 @@ import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
+import org.eclipse.jgit.annotations.NonNull;
/**
* A {@link org.eclipse.jgit.transport.http.HttpConnection} which simply
* delegates every call to a {@link java.net.HttpURLConnection}. This is the
@@ -72,6 +74,11 @@ import javax.net.ssl.TrustManager;
public class JDKHttpConnection implements HttpConnection {
HttpURLConnection wrappedUrlConnection;
+ // used for mock testing
+ JDKHttpConnection(HttpURLConnection urlConnection) {
+ this.wrappedUrlConnection = urlConnection;
+ }
+
/**
* Constructor for JDKHttpConnection.
*
@@ -170,10 +177,26 @@ public class JDKHttpConnection implements HttpConnection {
/** {@inheritDoc} */
@Override
- public String getHeaderField(String name) {
+ public String getHeaderField(@NonNull String name) {
return wrappedUrlConnection.getHeaderField(name);
}
+ @Override
+ public List<String> getHeaderFields(@NonNull String name) {
+ Map<String, List<String>> m = wrappedUrlConnection.getHeaderFields();
+ List<String> fields = mapValuesToListIgnoreCase(name, m);
+ return fields;
+ }
+
+ private static List<String> mapValuesToListIgnoreCase(String keyName,
+ Map<String, List<String>> m) {
+ List<String> fields = new LinkedList<>();
+ m.entrySet().stream().filter(e -> keyName.equalsIgnoreCase(e.getKey()))
+ .filter(e -> e.getValue() != null)
+ .forEach(e -> fields.addAll(e.getValue()));
+ return fields;
+ }
+
/** {@inheritDoc} */
@Override
public int getContentLength() {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/ReceivePackFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/ReceivePackFactory.java
index 4967169776..b850d1ef94 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/ReceivePackFactory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/ReceivePackFactory.java
@@ -57,7 +57,7 @@ public interface ReceivePackFactory<C> {
/**
* A factory disabling the ReceivePack service for all repositories
*/
- public static final ReceivePackFactory<?> DISABLED = new ReceivePackFactory<Object>() {
+ ReceivePackFactory<?> DISABLED = new ReceivePackFactory<Object>() {
@Override
public ReceivePack create(Object req, Repository db)
throws ServiceNotEnabledException {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/RepositoryResolver.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/RepositoryResolver.java
index a305e4cea3..4816f21bcc 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/RepositoryResolver.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/RepositoryResolver.java
@@ -57,7 +57,7 @@ public interface RepositoryResolver<C> {
/**
* Resolver configured to open nothing.
*/
- public static final RepositoryResolver<?> NONE = new RepositoryResolver<Object>() {
+ RepositoryResolver<?> NONE = new RepositoryResolver<Object>() {
@Override
public Repository open(Object req, String name)
throws RepositoryNotFoundException {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/UploadPackFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/UploadPackFactory.java
index 40d1ffdc56..bb43b136d8 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/UploadPackFactory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/UploadPackFactory.java
@@ -57,7 +57,7 @@ public interface UploadPackFactory<C> {
/**
* A factory disabling the UploadPack service for all repositories.
*/
- public static final UploadPackFactory<?> DISABLED = new UploadPackFactory<Object>() {
+ UploadPackFactory<?> DISABLED = new UploadPackFactory<Object>() {
@Override
public UploadPack create(Object req, Repository db)
throws ServiceNotEnabledException {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java
index 24b9ac086e..3d25c2314e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java
@@ -46,6 +46,8 @@
package org.eclipse.jgit.treewalk;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
@@ -412,7 +414,7 @@ public class FileTreeIterator extends WorkingTreeIterator {
public InputStream openInputStream() throws IOException {
if (attributes.isSymbolicLink()) {
return new ByteArrayInputStream(fs.readSymLink(getFile())
- .getBytes(Constants.CHARACTER_ENCODING));
+ .getBytes(UTF_8));
} else {
return new FileInputStream(getFile());
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
index 7c6bfb9d63..1fa1db5847 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
@@ -1045,7 +1045,7 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
}
}
if (FileMode.GITLINK == iMode
- && FileMode.TREE == wtMode) {
+ && FileMode.TREE == wtMode && !getOptions().isDirNoGitLinks()) {
return iMode;
}
if (FileMode.TREE == iMode
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
index 180123e091..fb958872ac 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
@@ -43,6 +43,8 @@
package org.eclipse.jgit.util;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.Closeable;
@@ -1222,6 +1224,13 @@ public abstract class FS {
hookPath);
ProcessBuilder hookProcess = runInShell(cmd, args);
hookProcess.directory(runDirectory);
+ Map<String, String> environment = hookProcess.environment();
+ environment.put(Constants.GIT_DIR_KEY,
+ repository.getDirectory().getAbsolutePath());
+ if (!repository.isBare()) {
+ environment.put(Constants.GIT_WORK_TREE_KEY,
+ repository.getWorkTree().getAbsolutePath());
+ }
try {
return new ProcessResult(runProcess(hookProcess, outRedirect,
errRedirect, stdinArgs), Status.OK);
@@ -1286,7 +1295,7 @@ public abstract class FS {
OutputStream outRedirect, OutputStream errRedirect, String stdinArgs)
throws IOException, InterruptedException {
InputStream in = (stdinArgs == null) ? null : new ByteArrayInputStream(
- stdinArgs.getBytes(Constants.CHARACTER_ENCODING));
+ stdinArgs.getBytes(UTF_8));
return runProcess(processBuilder, outRedirect, errRedirect, in);
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/HttpSupport.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/HttpSupport.java
index 6f92b37853..9190a5915a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/HttpSupport.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/HttpSupport.java
@@ -44,7 +44,7 @@
package org.eclipse.jgit.util;
-import static org.eclipse.jgit.lib.Constants.CHARACTER_ENCODING;
+import static java.nio.charset.StandardCharsets.UTF_8;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
@@ -181,7 +181,7 @@ public class HttpSupport {
if (key == null || key.length() == 0)
return;
try {
- urlstr.append(URLEncoder.encode(key, CHARACTER_ENCODING));
+ urlstr.append(URLEncoder.encode(key, UTF_8.name()));
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(JGitText.get().couldNotURLEncodeToUTF8, e);
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/LfsFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/LfsFactory.java
index 6d60ef3f4d..96636b7994 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/LfsFactory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/LfsFactory.java
@@ -152,7 +152,8 @@ public class LfsFactory {
* @param outputStream
* @return a {@link PrePushHook} implementation or <code>null</code>
*/
- public @Nullable PrePushHook getPrePushHook(Repository repo,
+ @Nullable
+ public PrePushHook getPrePushHook(Repository repo,
PrintStream outputStream) {
return null;
}
@@ -163,7 +164,8 @@ public class LfsFactory {
*
* @return a command to install LFS support.
*/
- public @Nullable LfsInstallCommand getInstallCommand() {
+ @Nullable
+ public LfsInstallCommand getInstallCommand() {
return null;
}
@@ -294,6 +296,11 @@ public class LfsFactory {
return stream.read();
}
+ @Override
+ public int read(byte b[], int off, int len) throws IOException {
+ return stream.read(b, off, len);
+ }
+
/**
* @return the length of the stream
*/
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/RawParseUtils.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/RawParseUtils.java
index 28f406a49e..a440cb275c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/RawParseUtils.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/RawParseUtils.java
@@ -677,10 +677,6 @@ public final class RawParseUtils {
* <p>
* The last element (index <code>map.size()-1</code>) always contains
* <code>end</code>.
- * <p>
- * If the data contains a '\0' anywhere, the whole region is considered
- * binary and a LineMap corresponding to a single line is returned.
- * </p>
*
* @param buf
* buffer to scan.
@@ -689,18 +685,15 @@ public final class RawParseUtils {
* line 1.
* @param end
* 1 past the end of the content within <code>buf</code>.
- * @return a line map indicating the starting position of each line, or a
- * map representing the entire buffer as a single line if
- * <code>buf</code> contains a NUL byte.
+ * @return a line map indicating the starting position of each line.
*/
public static final IntList lineMap(byte[] buf, int ptr, int end) {
- IntList map = lineMapOrNull(buf, ptr, end);
- if (map == null) {
- map = new IntList(3);
- map.add(Integer.MIN_VALUE);
+ IntList map = new IntList((end - ptr) / 36);
+ map.fillTo(1, Integer.MIN_VALUE);
+ for (; ptr < end; ptr = nextLF(buf, ptr)) {
map.add(ptr);
- map.add(end);
}
+ map.add(end);
return map;
}
@@ -729,7 +722,8 @@ public final class RawParseUtils {
return map;
}
- private static @Nullable IntList lineMapOrNull(byte[] buf, int ptr, int end) {
+ @Nullable
+ private static IntList lineMapOrNull(byte[] buf, int ptr, int end) {
// Experimentally derived from multiple source repositories
// the average number of bytes/line is 36. Its a rough guess
// to initially size our map close to the target.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/EolStreamTypeUtil.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/EolStreamTypeUtil.java
index 822961f8de..d7c6bec219 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/EolStreamTypeUtil.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/EolStreamTypeUtil.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2015, Ivan Motsch <ivan.motsch@bsiag.com>
+ * and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
* under the terms of the Eclipse Distribution License v1.0 which
@@ -49,6 +50,7 @@ import org.eclipse.jgit.attributes.Attributes;
import org.eclipse.jgit.lib.CoreConfig.EolStreamType;
import org.eclipse.jgit.treewalk.TreeWalk.OperationType;
import org.eclipse.jgit.treewalk.WorkingTreeOptions;
+import org.eclipse.jgit.util.SystemReader;
/**
* Utility used to create input and output stream wrappers for
@@ -57,7 +59,6 @@ import org.eclipse.jgit.treewalk.WorkingTreeOptions;
* @since 4.3
*/
public final class EolStreamTypeUtil {
- private static final boolean FORCE_EOL_LF_ON_CHECKOUT = false;
private EolStreamTypeUtil() {
}
@@ -164,11 +165,11 @@ public final class EolStreamTypeUtil {
// old git system
if (attrs.isSet("crlf")) {//$NON-NLS-1$
- return EolStreamType.TEXT_LF;
+ return EolStreamType.TEXT_LF; // Same as isSet("text")
} else if (attrs.isUnset("crlf")) {//$NON-NLS-1$
- return EolStreamType.DIRECT;
+ return EolStreamType.DIRECT; // Same as isUnset("text")
} else if ("input".equals(attrs.getValue("crlf"))) {//$NON-NLS-1$ //$NON-NLS-2$
- return EolStreamType.TEXT_LF;
+ return EolStreamType.TEXT_LF; // Same as eol=lf
}
// new git system
@@ -196,6 +197,28 @@ public final class EolStreamTypeUtil {
return EolStreamType.DIRECT;
}
+ private static EolStreamType getOutputFormat(WorkingTreeOptions options) {
+ switch (options.getAutoCRLF()) {
+ case TRUE:
+ return EolStreamType.TEXT_CRLF;
+ default:
+ // no decision
+ }
+ switch (options.getEOL()) {
+ case CRLF:
+ return EolStreamType.TEXT_CRLF;
+ case NATIVE:
+ if (SystemReader.getInstance().isWindows()) {
+ return EolStreamType.TEXT_CRLF;
+ }
+ return EolStreamType.TEXT_LF;
+ case LF:
+ default:
+ break;
+ }
+ return EolStreamType.DIRECT;
+ }
+
private static EolStreamType checkOutStreamType(WorkingTreeOptions options,
Attributes attrs) {
if (attrs.isUnset("text")) {//$NON-NLS-1$
@@ -205,57 +228,35 @@ public final class EolStreamTypeUtil {
// old git system
if (attrs.isSet("crlf")) {//$NON-NLS-1$
- return FORCE_EOL_LF_ON_CHECKOUT ? EolStreamType.TEXT_LF
- : EolStreamType.DIRECT;
+ return getOutputFormat(options); // Same as isSet("text")
} else if (attrs.isUnset("crlf")) {//$NON-NLS-1$
- return EolStreamType.DIRECT;
+ return EolStreamType.DIRECT; // Same as isUnset("text")
} else if ("input".equals(attrs.getValue("crlf"))) {//$NON-NLS-1$ //$NON-NLS-2$
- return EolStreamType.DIRECT;
+ return EolStreamType.DIRECT; // Same as eol=lf
}
// new git system
String eol = attrs.getValue("eol"); //$NON-NLS-1$
- if (eol != null && "crlf".equals(eol)) //$NON-NLS-1$
- return EolStreamType.TEXT_CRLF;
- if (eol != null && "lf".equals(eol)) //$NON-NLS-1$
- return FORCE_EOL_LF_ON_CHECKOUT ? EolStreamType.TEXT_LF
- : EolStreamType.DIRECT;
-
- if (attrs.isSet("text")) { //$NON-NLS-1$
- switch (options.getAutoCRLF()) {
- case TRUE:
- return EolStreamType.TEXT_CRLF;
- default:
- // no decision
- }
- switch (options.getEOL()) {
- case CRLF:
+ if (eol != null) {
+ if ("crlf".equals(eol)) {//$NON-NLS-1$
return EolStreamType.TEXT_CRLF;
- case LF:
- return FORCE_EOL_LF_ON_CHECKOUT ? EolStreamType.TEXT_LF
- : EolStreamType.DIRECT;
- case NATIVE:
- default:
+ } else if ("lf".equals(eol)) { //$NON-NLS-1$
return EolStreamType.DIRECT;
}
}
+ if (attrs.isSet("text")) { //$NON-NLS-1$
+ return getOutputFormat(options);
+ }
if ("auto".equals(attrs.getValue("text"))) { //$NON-NLS-1$ //$NON-NLS-2$
- switch (options.getAutoCRLF()) {
- case TRUE:
+ EolStreamType basic = getOutputFormat(options);
+ switch (basic) {
+ case TEXT_CRLF:
return EolStreamType.AUTO_CRLF;
+ case TEXT_LF:
+ return EolStreamType.AUTO_LF;
default:
- // no decision
- }
- switch (options.getEOL()) {
- case CRLF:
- return EolStreamType.AUTO_CRLF;
- case LF:
- return FORCE_EOL_LF_ON_CHECKOUT ? EolStreamType.TEXT_LF
- : EolStreamType.DIRECT;
- case NATIVE:
- default:
- return EolStreamType.DIRECT;
+ return basic;
}
}
diff --git a/pom.xml b/pom.xml
index 934f1b8958..25fa24d610 100644
--- a/pom.xml
+++ b/pom.xml
@@ -51,7 +51,7 @@
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
<packaging>pom</packaging>
- <version>5.1.9-SNAPSHOT</version>
+ <version>5.2.3-SNAPSHOT</version>
<name>JGit - Parent</name>
<url>${jgit-url}</url>
@@ -77,15 +77,12 @@
<developers>
<developer>
- <name>Chris Aniszczyk</name>
+ <name>Andrey Loskutov</name>
</developer>
<developer>
<name>Christian Halstrick</name>
</developer>
<developer>
- <name>Colby Ranger</name>
- </developer>
- <developer>
<name>Dave Borowitz</name>
</developer>
<developer>
@@ -98,28 +95,16 @@
<name>Jonathan Nieder</name>
</developer>
<developer>
- <name>Kevin Sawicki</name>
- </developer>
- <developer>
- <name>Mathias Kinzler</name>
+ <name>Jonathan Tan</name>
</developer>
<developer>
<name>Matthias Sohn</name>
</developer>
<developer>
- <name>Robin Rosenberg</name>
- </developer>
- <developer>
- <name>Robin Stocker</name>
- </developer>
- <developer>
<name>Sasa Zivkov</name>
</developer>
<developer>
- <name>Shawn Pearce</name>
- </developer>
- <developer>
- <name>Stefan Lay</name>
+ <name>Terry Parker</name>
</developer>
<developer>
<name>Thomas Wolf</name>
@@ -197,14 +182,15 @@
<maven.build.timestamp.format>yyyyMMddHHmm</maven.build.timestamp.format>
<bundle-manifest>${project.build.directory}/META-INF/MANIFEST.MF</bundle-manifest>
- <jgit-last-release-version>4.11.0.201803080745-r</jgit-last-release-version>
+ <jgit-last-release-version>5.1.0.201809111528-r</jgit-last-release-version>
+ <apache-sshd-version>2.0.0</apache-sshd-version>
<jsch-version>0.1.54</jsch-version>
<jzlib-version>1.1.1</jzlib-version>
<javaewah-version>1.1.6</javaewah-version>
<junit-version>4.12</junit-version>
<test-fork-count>1C</test-fork-count>
<args4j-version>2.33</args4j-version>
- <commons-compress-version>1.15</commons-compress-version>
+ <commons-compress-version>1.18</commons-compress-version>
<osgi-core-version>4.3.1</osgi-core-version>
<servlet-api-version>3.1.0</servlet-api-version>
<jetty-version>9.4.11.v20180605</jetty-version>
@@ -216,9 +202,11 @@
<maven-javadoc-plugin-version>3.0.1</maven-javadoc-plugin-version>
<tycho-extras-version>1.2.0</tycho-extras-version>
<gson-version>2.8.2</gson-version>
- <spotbugs-maven-plugin-version>3.1.6</spotbugs-maven-plugin-version>
- <maven-surefire-report-plugin-version>2.21.0</maven-surefire-report-plugin-version>
+ <spotbugs-maven-plugin-version>3.1.8</spotbugs-maven-plugin-version>
+ <maven-surefire-version>2.22.1</maven-surefire-version>
<maven-compiler-plugin-version>3.8.0</maven-compiler-plugin-version>
+ <maven-project-info-reports-plugin-version>3.0.0</maven-project-info-reports-plugin-version>
+ <maven-jxr-plugin-version>3.0.0</maven-jxr-plugin-version>
<!-- Properties to enable jacoco code coverage analysis -->
<sonar.core.codeCoveragePlugin>jacoco</sonar.core.codeCoveragePlugin>
@@ -275,7 +263,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
- <version>3.1.1</version>
+ <version>3.2.1</version>
</plugin>
<plugin>
@@ -305,7 +293,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
- <version>2.21.0</version>
+ <version>${maven-surefire-version}</version>
<configuration>
<forkCount>${test-fork-count}</forkCount>
<reuseForks>true</reuseForks>
@@ -338,7 +326,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-pmd-plugin</artifactId>
- <version>3.10.0</version>
+ <version>3.11.0</version>
<configuration>
<sourceEncoding>utf-8</sourceEncoding>
<minimumTokens>100</minimumTokens>
@@ -376,7 +364,7 @@
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
- <version>0.8.1</version>
+ <version>0.8.2</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
@@ -386,24 +374,24 @@
<dependency><!-- add support for ssh/scp -->
<groupId>org.apache.maven.wagon</groupId>
<artifactId>wagon-ssh</artifactId>
- <version>3.1.0</version>
+ <version>3.2.0</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-report-plugin</artifactId>
- <version>${maven-surefire-report-plugin-version}</version>
+ <version>${maven-surefire-version}</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jxr-plugin</artifactId>
- <version>2.5</version>
+ <version>${maven-jxr-plugin-version}</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-project-info-reports-plugin</artifactId>
- <version>2.9</version>
+ <version>${maven-project-info-reports-plugin-version}</version>
</plugin>
</plugins>
</pluginManagement>
@@ -412,7 +400,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
- <version>3.0.0-M1</version>
+ <version>3.0.0-M2</version>
<executions>
<execution>
<id>enforce-maven</id>
@@ -560,7 +548,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jxr-plugin</artifactId>
- <version>2.5</version>
+ <version>${maven-jxr-plugin-version}</version>
</plugin>
<plugin>
<groupId>com.github.spotbugs</groupId>
@@ -570,7 +558,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-report-plugin</artifactId>
- <version>${maven-surefire-report-plugin-version}</version>
+ <version>${maven-surefire-version}</version>
<configuration>
<aggregate>true</aggregate>
<alwaysGenerateSurefireReport>false</alwaysGenerateSurefireReport>
@@ -582,16 +570,20 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-project-info-reports-plugin</artifactId>
- <version>2.9</version>
+ <version>${maven-project-info-reports-plugin-version}</version>
<reportSets>
<reportSet>
<reports>
<report>dependencies</report>
- <report>project-team</report>
- <report>mailing-list</report>
- <report>cim</report>
- <report>issue-tracking</report>
- <report>license</report>
+ <report>dependency-convergence</report>
+ <report>dependency-management</report>
+ <report>index</report>
+ <report>summary</report>
+ <report>team</report>
+ <report>mailing-lists</report>
+ <report>ci-management</report>
+ <report>issue-management</report>
+ <report>licenses</report>
<report>scm</report>
</reports>
</reportSet>
@@ -798,7 +790,7 @@
<dependency>
<groupId>com.google.errorprone</groupId>
<artifactId>error_prone_core</artifactId>
- <version>2.3.1</version>
+ <version>2.3.2</version>
</dependency>
</dependencies>
</plugin>
@@ -836,7 +828,7 @@
<dependency>
<groupId>org.eclipse.jdt</groupId>
<artifactId>ecj</artifactId>
- <version>3.13.102</version>
+ <version>3.15.0</version>
</dependency>
</dependencies>
</plugin>
@@ -931,11 +923,13 @@
<module>org.eclipse.jgit.ui</module>
<module>org.eclipse.jgit.http.apache</module>
<module>org.eclipse.jgit.http.server</module>
+ <module>org.eclipse.jgit.ssh.apache</module>
<module>org.eclipse.jgit.pgm</module>
<module>org.eclipse.jgit.lfs</module>
<module>org.eclipse.jgit.lfs.server</module>
<module>org.eclipse.jgit.junit</module>
<module>org.eclipse.jgit.junit.http</module>
+ <module>org.eclipse.jgit.junit.ssh</module>
<module>org.eclipse.jgit.test</module>
<module>org.eclipse.jgit.ant.test</module>
@@ -943,6 +937,7 @@
<module>org.eclipse.jgit.pgm.test</module>
<module>org.eclipse.jgit.lfs.test</module>
<module>org.eclipse.jgit.lfs.server.test</module>
+ <module>org.eclipse.jgit.ssh.apache.test</module>
</modules>
</project>
diff --git a/tools/BUILD b/tools/BUILD
index e69de29bb2..9025e0a6d3 100644
--- a/tools/BUILD
+++ b/tools/BUILD
@@ -0,0 +1,107 @@
+load(
+ "@bazel_tools//tools/jdk:default_java_toolchain.bzl",
+ "JDK9_JVM_OPTS",
+ "default_java_toolchain",
+)
+
+default_java_toolchain(
+ name = "error_prone_warnings_toolchain",
+ bootclasspath = ["@bazel_tools//tools/jdk:platformclasspath.jar"],
+ jvm_opts = JDK9_JVM_OPTS,
+ package_configuration = [
+ ":error_prone",
+ ],
+ visibility = ["//visibility:public"],
+)
+
+# This EP warnings list borrowed from here:
+# https://github.com/bazelbuild/BUILD_file_generator/blob/master/tools/bazel_defs/java.bzl
+java_package_configuration(
+ name = "error_prone",
+ javacopts = [
+ "-XepDisableWarningsInGeneratedCode",
+ "-Xep:MissingCasesInEnumSwitch:ERROR",
+ "-Xep:ReferenceEquality:WARN",
+ "-Xep:StringEquality:WARN",
+ "-Xep:WildcardImport:WARN",
+ "-Xep:AmbiguousMethodReference:WARN",
+ "-Xep:BadAnnotationImplementation:WARN",
+ "-Xep:BadComparable:WARN",
+ "-Xep:BoxedPrimitiveConstructor:ERROR",
+ "-Xep:CannotMockFinalClass:WARN",
+ "-Xep:ClassCanBeStatic:ERROR",
+ "-Xep:ClassNewInstance:WARN",
+ "-Xep:DefaultCharset:ERROR",
+ "-Xep:DoubleCheckedLocking:WARN",
+ "-Xep:ElementsCountedInLoop:WARN",
+ "-Xep:EqualsHashCode:WARN",
+ "-Xep:EqualsIncompatibleType:WARN",
+ "-Xep:ExpectedExceptionChecker:ERROR",
+ "-Xep:Finally:WARN",
+ "-Xep:FloatingPointLiteralPrecision:WARN",
+ "-Xep:FragmentInjection:WARN",
+ "-Xep:FragmentNotInstantiable:WARN",
+ "-Xep:FunctionalInterfaceClash:WARN",
+ "-Xep:FutureReturnValueIgnored:WARN",
+ "-Xep:GetClassOnEnum:WARN",
+ "-Xep:ImmutableAnnotationChecker:WARN",
+ "-Xep:ImmutableEnumChecker:WARN",
+ "-Xep:IncompatibleModifiers:WARN",
+ "-Xep:InjectOnConstructorOfAbstractClass:WARN",
+ "-Xep:InputStreamSlowMultibyteRead:WARN",
+ "-Xep:IterableAndIterator:WARN",
+ "-Xep:JUnit3FloatingPointComparisonWithoutDelta:WARN",
+ "-Xep:JUnitAmbiguousTestClass:WARN",
+ "-Xep:LiteralClassName:WARN",
+ "-Xep:MissingFail:ERROR",
+ "-Xep:MissingOverride:WARN",
+ "-Xep:MutableConstantField:WARN",
+ "-Xep:NarrowingCompoundAssignment:WARN",
+ "-Xep:NonAtomicVolatileUpdate:WARN",
+ "-Xep:NonOverridingEquals:WARN",
+ "-Xep:NullableConstructor:WARN",
+ "-Xep:NullablePrimitive:WARN",
+ "-Xep:NullableVoid:WARN",
+ "-Xep:OperatorPrecedence:WARN",
+ "-Xep:OverridesGuiceInjectableMethod:WARN",
+ "-Xep:PreconditionsInvalidPlaceholder:WARN",
+ "-Xep:ProtoFieldPreconditionsCheckNotNull:WARN",
+ "-Xep:ProtocolBufferOrdinal:WARN",
+ "-Xep:RequiredModifiers:WARN",
+ "-Xep:ShortCircuitBoolean:WARN",
+ "-Xep:SimpleDateFormatConstant:WARN",
+ "-Xep:StaticGuardedByInstance:WARN",
+ "-Xep:SynchronizeOnNonFinalField:WARN",
+ "-Xep:TruthConstantAsserts:WARN",
+ "-Xep:TypeParameterShadowing:WARN",
+ "-Xep:TypeParameterUnusedInFormals:WARN",
+ "-Xep:URLEqualsHashCode:WARN",
+ "-Xep:UnsynchronizedOverridesSynchronized:WARN",
+ "-Xep:WaitNotInLoop:WARN",
+ ],
+ packages = ["error_prone_packages"],
+)
+
+package_group(
+ name = "error_prone_packages",
+ packages = [
+ "//org.eclipse.jgit.ant.test/...",
+ "//org.eclipse.jgit.ant/...",
+ "//org.eclipse.jgit.archive/...",
+ "//org.eclipse.jgit.http.apache/...",
+ "//org.eclipse.jgit.http.server/...",
+ "//org.eclipse.jgit.http.test/...",
+ "//org.eclipse.jgit.junit.http/...",
+ "//org.eclipse.jgit.junit/...",
+ "//org.eclipse.jgit.lfs.server.test/...",
+ "//org.eclipse.jgit.lfs.server/...",
+ "//org.eclipse.jgit.lfs.test/...",
+ "//org.eclipse.jgit.lfs/...",
+ "//org.eclipse.jgit.packaging/...",
+ "//org.eclipse.jgit.pgm.test/...",
+ "//org.eclipse.jgit.pgm/...",
+ "//org.eclipse.jgit.test/...",
+ "//org.eclipse.jgit.ui/...",
+ "//org.eclipse.jgit/...",
+ ],
+)
diff --git a/tools/maven-central/deploy.rb b/tools/maven-central/deploy.rb
index 2744e772a1..7cab32236b 100755
--- a/tools/maven-central/deploy.rb
+++ b/tools/maven-central/deploy.rb
@@ -55,9 +55,11 @@ artifacts = [group,
group + '.http.server',
group + '.junit',
group + '.junit.http',
+ group + '.junit.ssh',
group + '.lfs',
group + '.lfs.server',
group + '.pgm',
+ group + '.ssh.apache',
group + '.ui']
prefix = ["mvn", "gpg:sign-and-deploy-file", "-Dgpg.passphrase=#{passphrase}",
diff --git a/tools/maven-central/download.rb b/tools/maven-central/download.rb
index b6c2671742..543ae87a5d 100755
--- a/tools/maven-central/download.rb
+++ b/tools/maven-central/download.rb
@@ -15,9 +15,11 @@ artifacts = [group,
group + '.http.server',
group + '.junit',
group + '.junit.http',
+ group + '.junit.ssh',
group + '.lfs',
group + '.lfs.server',
group + '.pgm',
+ group + '.ssh.apache',
group + '.ui']
puts 'Deleting current files'