diff --git a/.bazelversion b/.bazelversion new file mode 100644 index 000000000..30f6cf8d9 --- /dev/null +++ b/.bazelversion @@ -0,0 +1 @@ +0.26.1 diff --git a/WORKSPACE b/WORKSPACE index 7ddd55463..d33c116e2 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -4,14 +4,14 @@ 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"], + sha256 = "2ea8a5ed2b448baf4a6855d3ce049c4c452a6470b1efd1504fdb7c1c134d220a", + strip_prefix = "bazel-skylib-0.8.0", + urls = ["https://github.com/bazelbuild/bazel-skylib/archive/0.8.0.tar.gz"], ) load("@bazel_skylib//lib:versions.bzl", "versions") -versions.check(minimum_bazel_version = "0.19.0") +versions.check(minimum_bazel_version = "0.26.1") load("//tools:bazlets.bzl", "load_bazlets") @@ -212,25 +212,25 @@ maven_jar( src_sha1 = "94e89a8c9f82e38555e95b9f7f58344a247e862c", ) -BOUNCYCASTLE_VER = "1.60" +BOUNCYCASTLE_VER = "1.61" maven_jar( name = "bcpg", artifact = "org.bouncycastle:bcpg-jdk15on:" + BOUNCYCASTLE_VER, - sha1 = "13c7a199c484127daad298996e95818478431a2c", - src_sha1 = "edcd9e86d95e39b4da39bb295efd93bc4f56266e", + sha1 = "422656435514ab8a28752b117d5d2646660a0ace", + src_sha1 = "836da34e11114cbce8fa99f54175f8f3278d1cce", ) maven_jar( name = "bcprov", artifact = "org.bouncycastle:bcprov-jdk15on:" + BOUNCYCASTLE_VER, - sha1 = "bd47ad3bd14b8e82595c7adaa143501e60842a84", - src_sha1 = "7c57a4d13fe53d9abb967bba600dd0b293dafd6a", + sha1 = "00df4b474e71be02c1349c3292d98886f888d1f7", + src_sha1 = "3bf88046a16098ea6cc41576dd50d512854d39e1", ) maven_jar( name = "bcpkix", artifact = "org.bouncycastle:bcpkix-jdk15on:" + BOUNCYCASTLE_VER, - sha1 = "d0c46320fbc07be3a24eb13a56cee4e3d38e0c75", - src_sha1 = "a25f041293f401af08efba63ff4bbdce98134a03", + sha1 = "89bb3aa5b98b48e584eee2a7401b7682a46779b4", + src_sha1 = "a0498d09200a18737eccc05aa81bbd05c1be0f8c", ) 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 b6d73b559..256279bfe 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 @@ -64,7 +64,6 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.eclipse.jgit.internal.storage.dfs.DfsRepository; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.Repository; @@ -276,12 +275,11 @@ private static String etag(byte[] content) { } static String identify(Repository git) { - if (git instanceof DfsRepository) { - return ((DfsRepository) git).getDescription().getRepositoryName(); - } else if (git.getDirectory() != null) { - return git.getDirectory().getPath(); + String identifier = git.getIdentifier(); + if (identifier == null) { + return "unknown"; } - return "unknown"; + return identifier; } private ServletUtils() { diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HttpClientTests.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HttpClientTests.java index 1660f14f3..8ec2f51cf 100644 --- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HttpClientTests.java +++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HttpClientTests.java @@ -44,7 +44,6 @@ package org.eclipse.jgit.http.test; import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.theInstance; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -366,7 +365,7 @@ public void testHttpClientWantsV2ButServerNotConfigured() throws Exception { // Check that we get a v0 response. assertThat(pckIn.readString(), is("# service=git-upload-pack")); - assertThat(pckIn.readString(), theInstance(PacketLineIn.END)); + assertTrue(PacketLineIn.isEnd(pckIn.readString())); assertTrue(pckIn.readString().matches("[0-9a-f]{40} HEAD.*")); } @@ -388,8 +387,7 @@ public void testV2HttpFirstResponse() throws Exception { // What remains are capabilities - ensure that all of them are // non-empty strings, and that we see END at the end. - String s; - while ((s = pckIn.readString()) != PacketLineIn.END) { + for (String s : pckIn.readStrings()) { assertTrue(!s.isEmpty()); } } @@ -422,8 +420,7 @@ public void testV2HttpSubsequentResponse() throws Exception { PacketLineIn pckIn = new PacketLineIn(c.getInputStream()); // Just check that we get what looks like a ref advertisement. - String s; - while ((s = pckIn.readString()) != PacketLineIn.END) { + for (String s : pckIn.readStrings()) { assertTrue(s.matches("[0-9a-f]{40} [A-Za-z/]*")); } diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/ProtocolErrorTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/ProtocolErrorTest.java index f0fac5ab5..6a3d8829b 100644 --- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/ProtocolErrorTest.java +++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/ProtocolErrorTest.java @@ -44,7 +44,7 @@ package org.eclipse.jgit.http.test; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -157,7 +157,7 @@ public void testPush_UnpackError_TruncatedPack() throws Exception { pckin.readString()); assertEquals("ng refs/objects/A n/a (unpacker error)", pckin.readString()); - assertSame(PacketLineIn.END, pckin.readString()); + assertTrue(PacketLineIn.isEnd(pckin.readString())); } } finally { c.disconnect(); diff --git a/org.eclipse.jgit.lfs.server.test/tst/org/eclipse/jgit/lfs/server/fs/UploadTest.java b/org.eclipse.jgit.lfs.server.test/tst/org/eclipse/jgit/lfs/server/fs/UploadTest.java index c6f953556..2334ec37a 100644 --- a/org.eclipse.jgit.lfs.server.test/tst/org/eclipse/jgit/lfs/server/fs/UploadTest.java +++ b/org.eclipse.jgit.lfs.server.test/tst/org/eclipse/jgit/lfs/server/fs/UploadTest.java @@ -44,6 +44,7 @@ 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.Assert.fail; @@ -56,6 +57,7 @@ import java.util.concurrent.CyclicBarrier; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import org.eclipse.jgit.lfs.lib.AnyLongObjectId; @@ -122,11 +124,12 @@ public void testParallelUploads() throws Exception { ExecutorService e = Executors.newFixedThreadPool(count); try { for (Path p : paths) { - e.submit(() -> { + Future result = e.submit(() -> { barrier.await(); putContent(p); return null; }); + assertNotNull(result); } } finally { e.shutdown(); diff --git a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/TransferHandler.java b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/TransferHandler.java index 4fea92e11..6c36661c3 100644 --- a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/TransferHandler.java +++ b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/TransferHandler.java @@ -84,7 +84,7 @@ private static class Upload extends TransferHandler { @Override Body process() throws IOException { Response.Body body = new Response.Body(); - if (objects.size() > 0) { + if (!objects.isEmpty()) { body.objects = new ArrayList<>(); for (LfsObject o : objects) { addObjectInfo(body, o); @@ -122,7 +122,7 @@ private static class Download extends TransferHandler { @Override Body process() throws IOException { Response.Body body = new Response.Body(); - if (objects.size() > 0) { + if (!objects.isEmpty()) { body.objects = new ArrayList<>(); for (LfsObject o : objects) { addObjectInfo(body, o); diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/AbbreviatedLongObjectId.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/AbbreviatedLongObjectId.java index bdd1b39c0..5109d9bbb 100644 --- a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/AbbreviatedLongObjectId.java +++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/AbbreviatedLongObjectId.java @@ -170,7 +170,7 @@ private static final long hexUInt64(final byte[] bs, int p, final int end) { r |= RawParseUtils.parseHexInt4(bs[p++]); n++; } - return r << (16 - n) * 4; + return r << ((16 - n) * 4); } static long mask(int nibbles, long word, long v) { diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.10.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.10.target index 440092a17..191574720 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.10.target +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.10.target @@ -1,7 +1,7 @@ - + @@ -23,8 +23,8 @@ - - + + @@ -37,12 +37,12 @@ - - - - - - + + + + + + @@ -82,7 +82,7 @@ - + diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.10.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.10.tpd index 5bfd81411..6beb42e33 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.10.tpd +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.10.tpd @@ -1,7 +1,7 @@ target "jgit-4.10" with source configurePhase include "projects/jetty-9.4.14.tpd" -include "orbit/S20190521195709.tpd" +include "orbit/R20190602212107-2019-06.tpd" location "http://download.eclipse.org/releases/2018-12/" { org.eclipse.osgi lazy diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.11.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.11.target index a3ad331a4..e5ea33101 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.11.target +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.11.target @@ -1,7 +1,7 @@ - + @@ -23,8 +23,8 @@ - - + + @@ -37,12 +37,12 @@ - - - - - - + + + + + + @@ -82,7 +82,7 @@ - + diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.11.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.11.tpd index 8b3b509fd..e09e8c387 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.11.tpd +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.11.tpd @@ -1,7 +1,7 @@ target "jgit-4.11" with source configurePhase include "projects/jetty-9.4.14.tpd" -include "orbit/S20190521195709.tpd" +include "orbit/R20190602212107-2019-06.tpd" location "http://download.eclipse.org/releases/2019-03/" { org.eclipse.osgi lazy diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.12-staging.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.12-staging.target index 2ea7b79f9..5daa8e8ad 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.12-staging.target +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.12-staging.target @@ -1,7 +1,7 @@ - + @@ -23,8 +23,8 @@ - - + + @@ -37,12 +37,12 @@ - - - - - - + + + + + + @@ -82,7 +82,7 @@ - + diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.12-staging.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.12-staging.tpd index f0cd3049f..43b64a030 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.12-staging.tpd +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.12-staging.tpd @@ -1,7 +1,7 @@ target "jgit-4.12-staging" with source configurePhase include "projects/jetty-9.4.14.tpd" -include "orbit/S20190521195709.tpd" +include "orbit/R20190602212107-2019-06.tpd" location "http://download.eclipse.org/staging/2019-06/" { 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 64cf3e7d2..0638b46a3 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 @@ - + @@ -23,8 +23,8 @@ - - + + @@ -37,12 +37,12 @@ - - - - - - + + + + + + @@ -82,7 +82,7 @@ - + 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 8ae721fed..1a2c9a3a1 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.14.tpd" -include "orbit/S20190521195709.tpd" +include "orbit/R20190602212107-2019-06.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 eb8c8e91d..1044299df 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 @@ - + @@ -23,8 +23,8 @@ - - + + @@ -37,12 +37,12 @@ - - - - - - + + + + + + @@ -82,7 +82,7 @@ - + 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 430506aee..fc1b046ed 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.14.tpd" -include "orbit/S20190521195709.tpd" +include "orbit/R20190602212107-2019-06.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 9764a6158..a5f948fde 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 @@ - + @@ -23,8 +23,8 @@ - - + + @@ -37,12 +37,12 @@ - - - - - - + + + + + + @@ -82,7 +82,7 @@ - + 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 e18fc7255..a8f837e9c 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.14.tpd" -include "orbit/S20190521195709.tpd" +include "orbit/R20190602212107-2019-06.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 index 331d141b6..e0afda3a6 100644 --- 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 @@ -1,7 +1,7 @@ - + @@ -23,8 +23,8 @@ - - + + @@ -37,12 +37,12 @@ - - - - - - + + + + + + @@ -82,7 +82,7 @@ - + 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 index 6b71dff4a..f91f53eb8 100644 --- 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 @@ -1,7 +1,7 @@ target "jgit-4.9" with source configurePhase include "projects/jetty-9.4.14.tpd" -include "orbit/S20190521195709.tpd" +include "orbit/R20190602212107-2019-06.tpd" location "http://download.eclipse.org/releases/2018-09/" { org.eclipse.osgi lazy diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/S20190521195709.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20190602212107-2019-06.tpd similarity index 83% rename from org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/S20190521195709.tpd rename to org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20190602212107-2019-06.tpd index d526ab432..df9a810e0 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/S20190521195709.tpd +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20190602212107-2019-06.tpd @@ -1,9 +1,9 @@ -target "S20190521195709" with source configurePhase +target "R20190602212107-2019-06" with source configurePhase // see http://download.eclipse.org/tools/orbit/downloads/ -location "http://download.eclipse.org/tools/orbit/downloads/drops/S20190521195709/repository" { - org.apache.ant [1.10.6.v20190516-0412,1.10.6.v20190516-0412] - org.apache.ant.source [1.10.6.v20190516-0412,1.10.6.v20190516-0412] +location "https://download.eclipse.org/tools/orbit/downloads/drops/R20190602212107/repository" { + org.apache.ant [1.10.5.v20190526-1402,1.10.5.v20190526-1402] + org.apache.ant.source [1.10.5.v20190526-1402,1.10.5.v20190526-1402] 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] @@ -16,12 +16,12 @@ location "http://download.eclipse.org/tools/orbit/downloads/drops/S2019052119570 org.apache.httpcomponents.httpcore.source [4.4.10.v20190123-2214,4.4.10.v20190123-2214] org.apache.log4j [1.2.15.v201012070815,1.2.15.v201012070815] org.apache.log4j.source [1.2.15.v201012070815,1.2.15.v201012070815] - org.bouncycastle.bcpg [1.60.0.v20181210-2057,1.60.0.v20181210-2057] - org.bouncycastle.bcpg.source [1.60.0.v20181210-2057,1.60.0.v20181210-2057] - org.bouncycastle.bcpkix [1.60.0.v20181210-2057,1.60.0.v20181210-2057] - org.bouncycastle.bcpkix.source [1.60.0.v20181210-2057,1.60.0.v20181210-2057] - org.bouncycastle.bcprov [1.60.0.v20181210-2057,1.60.0.v20181210-2057] - org.bouncycastle.bcprov.source [1.60.0.v20181210-2057,1.60.0.v20181210-2057] + org.bouncycastle.bcpg [1.61.0.v20190602-1335,1.61.0.v20190602-1335] + org.bouncycastle.bcpg.source [1.61.0.v20190602-1335,1.61.0.v20190602-1335] + org.bouncycastle.bcpkix [1.61.0.v20190602-1335,1.61.0.v20190602-1335] + org.bouncycastle.bcpkix.source [1.61.0.v20190602-1335,1.61.0.v20190602-1335] + org.bouncycastle.bcprov [1.61.0.v20190602-1335,1.61.0.v20190602-1335] + org.bouncycastle.bcprov.source [1.61.0.v20190602-1335,1.61.0.v20190602-1335] 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] 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 b9982fe6a..2a5a31eba 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 @@ -222,6 +222,7 @@ unsupportedOperation=Unsupported operation: {0} untrackedFiles=Untracked files: updating=Updating {0}..{1} usage_Aggressive=This option will cause gc to more aggressively optimize the repository at the expense of taking much more time +usage_AlwaysFallback=Show uniquely abbreviated commit object as fallback usage_bareClone=Make a bare Git repository. That is, instead of creating [DIRECTORY] and placing the administrative files in [DIRECTORY]/.git, make the [DIRECTORY] itself the $GIT_DIR. usage_Blame=Show what revision and author last modified each line usage_Clean=Remove untracked files from the working tree @@ -289,6 +290,7 @@ usage_Status=Show the working tree status usage_StopTrackingAFile=Stop tracking a file usage_TextHashFunctions=Scan repository to compute maximum number of collisions for hash functions usage_UpdateRemoteRepositoryFromLocalRefs=Update remote repository from local refs +usage_UseTags=Use any tag including lightweight tags usage_WriteDirCache=Write the DirCache usage_abbrevCommits=abbreviate commits to N + 1 digits usage_abortConnectionIfNoActivity=abort connection if no activity diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Checkout.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Checkout.java index 7e1737f87..3df37e51c 100644 --- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Checkout.java +++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Checkout.java @@ -96,7 +96,7 @@ protected void run() throws Exception { try (Git git = new Git(db)) { CheckoutCommand command = git.checkout() .setProgressMonitor(new TextProgressMonitor(errw)); - if (paths.size() > 0) { + if (!paths.isEmpty()) { command.setStartPoint(name); if (paths.size() == 1 && paths.get(0).equals(".")) { //$NON-NLS-1$ command.setAllPaths(true); diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Describe.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Describe.java index d89fee623..8a572f44b 100644 --- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Describe.java +++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Describe.java @@ -65,6 +65,12 @@ class Describe extends TextBuiltin { @Option(name = "--long", usage = "usage_LongFormat") private boolean longDesc; + @Option(name = "--tags", usage = "usage_UseTags") + private boolean useTags; + + @Option(name = "--always", usage = "usage_AlwaysFallback") + private boolean always; + @Option(name = "--match", usage = "usage_Match", metaVar = "metaVar_pattern") private List patterns = new ArrayList<>(); @@ -77,6 +83,8 @@ protected void run() { cmd.setTarget(tree); } cmd.setLong(longDesc); + cmd.setTags(useTags); + cmd.setAlways(always); cmd.setMatch(patterns.toArray(new String[0])); String result = null; try { diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/LsFiles.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/LsFiles.java index ef2584497..e3ed30dc4 100644 --- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/LsFiles.java +++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/LsFiles.java @@ -85,7 +85,7 @@ protected void run() { CanonicalTreeParser p = new CanonicalTreeParser(); p.reset(rw.getObjectReader(), c.getTree()); tw.reset(); // drop the first empty tree, which we do not need here - if (paths.size() > 0) { + if (!paths.isEmpty()) { tw.setFilter(PathFilterGroup.createFromStrings(paths)); } tw.addTree(p); diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/LsTree.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/LsTree.java index 2a2bb7cc9..19f2d7ae4 100644 --- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/LsTree.java +++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/LsTree.java @@ -76,7 +76,7 @@ class LsTree extends TextBuiltin { protected void run() { try (TreeWalk walk = new TreeWalk(db)) { walk.reset(); // drop the first empty tree, which we do not need here - if (paths.size() > 0) { + if (!paths.isEmpty()) { walk.setFilter(PathFilterGroup.createFromStrings(paths)); } walk.setRecursive(recursive); diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Reset.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Reset.java index b3e81c6d6..e1a290731 100644 --- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Reset.java +++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Reset.java @@ -80,7 +80,7 @@ protected void run() { try (Git git = new Git(db)) { ResetCommand command = git.reset(); command.setRef(commit); - if (paths.size() > 0) { + if (!paths.isEmpty()) { for (String path : paths) { command.addPath(path); } diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Status.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Status.java index 8b187587b..c3887fe9c 100644 --- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Status.java +++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Status.java @@ -93,7 +93,7 @@ class Status extends TextBuiltin { protected void run() { try (Git git = new Git(db)) { StatusCommand statusCommand = git.status(); - if (filterPaths != null && filterPaths.size() > 0) { + if (filterPaths != null) { for (String path : filterPaths) { statusCommand.addPath(path); } diff --git a/org.eclipse.jgit.test/BUILD b/org.eclipse.jgit.test/BUILD index be4fdbdea..5c0540fb9 100644 --- a/org.eclipse.jgit.test/BUILD +++ b/org.eclipse.jgit.test/BUILD @@ -28,6 +28,7 @@ HELPERS = glob( "treewalk/filter/AlwaysCloneTreeFilter.java", "test/resources/SampleDataRepositoryTestCase.java", "util/CPUTimeStopWatch.java", + "util/http/HttpCookiesMatcher.java", "util/io/Strings.java", ]] diff --git a/org.eclipse.jgit.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.test/META-INF/MANIFEST.MF index 73c8de852..1c6211d53 100644 --- a/org.eclipse.jgit.test/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.test/META-INF/MANIFEST.MF @@ -11,7 +11,7 @@ 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)", net.bytebuddy.dynamic.loading;version="[1.7.0,2.0.0)", - org.bouncycastle.util.encoders;version="[1.60.0,2.0.0)", + org.bouncycastle.util.encoders;version="[1.61.0,2.0.0)", org.eclipse.jgit.annotations;version="[5.4.0,5.5.0)", org.eclipse.jgit.api;version="[5.4.0,5.5.0)", org.eclipse.jgit.api.errors;version="[5.4.0,5.5.0)", @@ -35,6 +35,7 @@ Import-Package: com.googlecode.javaewah;version="[1.1.6,2.0.0)", org.eclipse.jgit.internal.storage.pack;version="[5.4.0,5.5.0)", org.eclipse.jgit.internal.storage.reftable;version="[5.4.0,5.5.0)", org.eclipse.jgit.internal.storage.reftree;version="[5.4.0,5.5.0)", + org.eclipse.jgit.internal.transport.http;version="[5.4.0,5.5.0)", org.eclipse.jgit.internal.transport.parser;version="[5.4.0,5.5.0)", org.eclipse.jgit.junit;version="[5.4.0,5.5.0)", org.eclipse.jgit.junit.ssh;version="[5.4.0,5.5.0)", diff --git a/org.eclipse.jgit.test/tests.bzl b/org.eclipse.jgit.test/tests.bzl index 6b5c7a1df..d639a5bea 100644 --- a/org.eclipse.jgit.test/tests.bzl +++ b/org.eclipse.jgit.test/tests.bzl @@ -7,6 +7,7 @@ def tests(tests): for src in tests: name = src[len("tst/"):len(src) - len(".java")].replace("/", "_") labels = [] + timeout = "moderate" if name.startswith("org_eclipse_jgit_"): l = name[len("org.eclipse.jgit_"):] if l.startswith("internal_storage_"): @@ -53,9 +54,15 @@ def tests(tests): additional_deps = [ "//lib:mockito", ] + if src.endswith("TransportHttpTest.java"): + additional_deps = [ + "//lib:mockito", + ] heap_size = "-Xmx256m" if src.endswith("HugeCommitMessageTest.java"): heap_size = "-Xmx512m" + if src.endswith("EolRepositoryTest.java") or src.endswith("GcCommitSelectionTest.java"): + timeout = "long" junit_tests( name = name, @@ -73,4 +80,5 @@ def tests(tests): ], flaky = flaky, jvm_flags = [heap_size, "-Dfile.encoding=UTF-8"], + timeout = timeout, ) diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/internal/transport/http/cookies-invalid.txt b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/internal/transport/http/cookies-invalid.txt new file mode 100644 index 000000000..bbc6a7329 --- /dev/null +++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/internal/transport/http/cookies-invalid.txt @@ -0,0 +1 @@ +some-domain /some/path1 FALSE 0 key1 value1 \ No newline at end of file diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/internal/transport/http/cookies-simple1.txt b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/internal/transport/http/cookies-simple1.txt new file mode 100644 index 000000000..e06b38c71 --- /dev/null +++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/internal/transport/http/cookies-simple1.txt @@ -0,0 +1,2 @@ +some-domain1 TRUE /some/path1 FALSE 1893499200000 key1 valueFromSimple1 +some-domain1 TRUE /some/path1 FALSE 1893499200000 key2 valueFromSimple1 \ No newline at end of file diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/internal/transport/http/cookies-simple2.txt b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/internal/transport/http/cookies-simple2.txt new file mode 100644 index 000000000..4bf6723fd --- /dev/null +++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/internal/transport/http/cookies-simple2.txt @@ -0,0 +1,2 @@ +some-domain1 TRUE /some/path1 FALSE 1893499200000 key1 valueFromSimple2 +some-domain1 TRUE /some/path1 FALSE 1893499200000 key3 valueFromSimple2 \ No newline at end of file diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/internal/transport/http/cookies-with-empty-and-comment-lines.txt b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/internal/transport/http/cookies-with-empty-and-comment-lines.txt new file mode 100644 index 000000000..a9b8a2815 --- /dev/null +++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/internal/transport/http/cookies-with-empty-and-comment-lines.txt @@ -0,0 +1,8 @@ +# first line is a comment +# the next cookie is supposed to be removed, because it has expired already +some-domain1 TRUE /some/path1 FALSE 0 key1 value1 + +# expires date is 01/01/2030 @ 12:00am (UTC) +#HttpOnly_.some-domain2 TRUE /some/path2 TRUE 1893499200000 key2 value2 + +some-domain3 TRUE /some/path3 FALSE 1893499200000 key3 value3 \ No newline at end of file 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 4eeaf4d6e..c9852e8b8 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 @@ -98,7 +98,7 @@ public void testClean() throws NoWorkTreeException, GitAPIException { StatusCommand command = git.status(); Status status = command.call(); Set files = status.getUntracked(); - assertTrue(files.size() > 0); + assertFalse(files.isEmpty()); // run clean Set cleanedFiles = git.clean().call(); @@ -120,7 +120,7 @@ public void testCleanDirs() throws NoWorkTreeException, GitAPIException { StatusCommand command = git.status(); Status status = command.call(); Set files = status.getUntracked(); - assertTrue(files.size() > 0); + assertFalse(files.isEmpty()); // run clean Set cleanedFiles = git.clean().setCleanDirectories(true).call(); @@ -143,7 +143,7 @@ public void testCleanWithPaths() throws NoWorkTreeException, StatusCommand command = git.status(); Status status = command.call(); Set files = status.getUntracked(); - assertTrue(files.size() > 0); + assertFalse(files.isEmpty()); // run clean with setPaths Set paths = new TreeSet<>(); @@ -164,7 +164,7 @@ public void testCleanWithDryRun() throws NoWorkTreeException, StatusCommand command = git.status(); Status status = command.call(); Set files = status.getUntracked(); - assertTrue(files.size() > 0); + assertFalse(files.isEmpty()); // run clean Set cleanedFiles = git.clean().setDryRun(true).call(); @@ -186,7 +186,7 @@ public void testCleanDirsWithDryRun() throws NoWorkTreeException, StatusCommand command = git.status(); Status status = command.call(); Set files = status.getUntracked(); - assertTrue(files.size() > 0); + assertFalse(files.isEmpty()); // run clean Set cleanedFiles = git.clean().setDryRun(true) 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 1523b49ec..74d13883e 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 @@ -718,6 +718,34 @@ public void testCloneWithAutoSetupRebase() throws Exception { } + @Test + public void testCloneWithPullMerge() throws Exception { + File directory = createTempDirectory("testCloneRepository1"); + try (Git g = Git.init().setDirectory(directory).setBare(false).call()) { + g.remoteAdd().setName(Constants.DEFAULT_REMOTE_NAME) + .setUri(new URIish(fileUri())).call(); + PullResult result = g.pull().setRebase(false).call(); + assertTrue(result.isSuccessful()); + assertEquals("refs/heads/master", + g.getRepository().getFullBranch()); + checkFile(new File(directory, "Test.txt"), "Hello world"); + } + } + + @Test + public void testCloneWithPullRebase() throws Exception { + File directory = createTempDirectory("testCloneRepository1"); + try (Git g = Git.init().setDirectory(directory).setBare(false).call()) { + g.remoteAdd().setName(Constants.DEFAULT_REMOTE_NAME) + .setUri(new URIish(fileUri())).call(); + PullResult result = g.pull().setRebase(true).call(); + assertTrue(result.isSuccessful()); + assertEquals("refs/heads/master", + g.getRepository().getFullBranch()); + checkFile(new File(directory, "Test.txt"), "Hello world"); + } + } + private String fileUri() { return "file://" + git.getRepository().getWorkTree().getAbsolutePath(); } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java index 4bfac1505..18fed4bff 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java @@ -661,6 +661,54 @@ public void commitWithAutoCrlfAndNonNormalizedIndex() throws Exception { } } + @Test + public void testDeletionConflictWithAutoCrlf() throws Exception { + try (Git git = new Git(db)) { + // Commit a file with CR/LF into the index + FileBasedConfig config = db.getConfig(); + config.setString("core", null, "autocrlf", "false"); + config.save(); + File file = writeTrashFile("file.txt", "foo\r\n"); + git.add().addFilepattern("file.txt").call(); + git.commit().setMessage("Initial").call(); + // Switch to side branch + git.checkout().setCreateBranch(true).setName("side").call(); + assertTrue(file.delete()); + git.rm().addFilepattern("file.txt").call(); + git.commit().setMessage("Side").call(); + // Switch on autocrlf=true + config.setString("core", null, "autocrlf", "true"); + config.save(); + // Switch back to master and commit a conflict + git.checkout().setName("master").call(); + writeTrashFile("file.txt", "foob\r\n"); + git.add().addFilepattern("file.txt").call(); + assertEquals("[file.txt, mode:100644, content:foob\r\n]", + indexState(CONTENT)); + writeTrashFile("g", "file2.txt", "anything"); + git.add().addFilepattern("g/file2.txt"); + RevCommit master = git.commit().setMessage("Second").call(); + // Switch to side branch again so that the deletion is "ours" + git.checkout().setName("side").call(); + // Cherry pick master: produces a delete-modify conflict. + CherryPickResult pick = git.cherryPick().include(master).call(); + assertEquals("Expected a cherry-pick conflict", + CherryPickStatus.CONFLICTING, pick.getStatus()); + // XXX: g/file2.txt should actually be staged already, but isn't. + git.add().addFilepattern("g/file2.txt").call(); + // Resolve the conflict by taking the master version + writeTrashFile("file.txt", "foob\r\n"); + git.add().addFilepattern("file.txt").call(); + git.commit().setMessage("Cherry").call(); + // We expect this to be committed with a single LF since there is no + // "ours" stage. + assertEquals( + "[file.txt, mode:100644, content:foob\n]" + + "[g/file2.txt, mode:100644, content:anything]", + indexState(CONTENT)); + } + } + private void testConflictWithAutoCrlf(String baseLf, String lf) throws Exception { try (Git git = new Git(db)) { 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 bc3ac7a72..df9ae6a0f 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 @@ -109,7 +109,7 @@ public void testDescribe() throws Exception { assertNameStartsWith(c4, "3e563c5"); assertNull(describe(c1)); - assertNull(describe(c1, true)); + assertNull(describe(c1, true, false)); assertNull(describe(c1, "a*", "b*", "c*")); assertNull(describe(c2, "bob*")); assertNull(describe(c2, "?ob*")); @@ -120,7 +120,7 @@ public void testDescribe() throws Exception { assertEquals("alice-t1", describe(c2, "a*", "b*", "c*")); assertEquals("bob-t2", describe(c3)); - assertEquals("bob-t2-0-g44579eb", describe(c3, true)); + assertEquals("bob-t2-0-g44579eb", describe(c3, true, false)); assertEquals("alice-t1-1-g44579eb", describe(c3, "alice*")); assertEquals("alice-t1-1-g44579eb", describe(c3, "a??c?-t*")); assertEquals("bob-t2", describe(c3, "bob*")); @@ -129,7 +129,7 @@ public void testDescribe() throws Exception { // the value verified with git-describe(1) assertEquals("bob-t2-1-g3e563c5", describe(c4)); - assertEquals("bob-t2-1-g3e563c5", describe(c4, true)); + assertEquals("bob-t2-1-g3e563c5", describe(c4, true, false)); assertEquals("alice-t1-2-g3e563c5", describe(c4, "alice*")); assertEquals("bob-t2-1-g3e563c5", describe(c4, "bob*")); assertEquals("bob-t2-1-g3e563c5", describe(c4, "a*", "b*", "c*")); @@ -137,6 +137,10 @@ public void testDescribe() throws Exception { assertEquals(null, describe(c2)); assertEquals(null, describe(c3)); assertEquals(null, describe(c4)); + + assertEquals("3747db3", describe(c2, false, true)); + assertEquals("44579eb", describe(c3, false, true)); + assertEquals("3e563c5", describe(c4, false, true)); } // test default target @@ -169,6 +173,10 @@ public void testDescribeMultiMatch() throws Exception { if (!useAnnotatedTags && !describeUseAllTags) { assertEquals(null, describe(c1)); assertEquals(null, describe(c2)); + + assertEquals("fd70040", describe(c1, false, true)); + assertEquals("b89dead", describe(c2, false, true)); + return; } @@ -235,9 +243,11 @@ public void testDescribeBranch() throws Exception { assertEquals("2 commits: c4 and c3", "t-2-g119892b", describe(c4)); } else { assertEquals(null, describe(c4)); + + assertEquals("119892b", describe(c4, false, true)); } assertNull(describe(c3)); - assertNull(describe(c3, true)); + assertNull(describe(c3, true, false)); } private void branch(String name, ObjectId base) throws GitAPIException { @@ -279,6 +289,9 @@ public void t1DominatesT2() throws Exception { } else { assertEquals(null, describe(c4)); assertEquals(null, describe(c3)); + + assertEquals("119892b", describe(c4, false, true)); + assertEquals("0244e7f", describe(c3, false, true)); } } @@ -368,6 +381,8 @@ public void t1nearerT2() throws Exception { assertEquals("t1-3-gbb389a4", describe(c4)); } else { assertEquals(null, describe(c4)); + + assertEquals("bb389a4", describe(c4, false, true)); } } @@ -401,6 +416,25 @@ public void t1sameDepthT2() throws Exception { assertEquals("t2-4-gbb389a4", describe(c4)); } else { assertEquals(null, describe(c4)); + + assertEquals("bb389a4", describe(c4, false, true)); + } + } + + @Test + public void globMatchWithSlashes() throws Exception { + ObjectId c1 = modify("aaa"); + tag("a/b/version"); + ObjectId c2 = modify("bbb"); + tag("a/b/version2"); + if (useAnnotatedTags || describeUseAllTags) { + assertEquals("a/b/version", describe(c1, "*/version*")); + assertEquals("a/b/version2", describe(c2, "*/version*")); + } else { + assertNull(describe(c1)); + assertNull(describe(c1, "*/version*")); + assertNull(describe(c2)); + assertNull(describe(c2, "*/version*")); } } @@ -433,14 +467,14 @@ private static void touch(File f, String contents) throws Exception { } } - private String describe(ObjectId c1, boolean longDesc) + private String describe(ObjectId c1, boolean longDesc, boolean always) throws GitAPIException, IOException { return git.describe().setTarget(c1).setTags(describeUseAllTags) - .setLong(longDesc).call(); + .setLong(longDesc).setAlways(always).call(); } private String describe(ObjectId c1) throws GitAPIException, IOException { - return describe(c1, false); + return describe(c1, false, false); } private String describe(ObjectId c1, String... patterns) throws Exception { diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPackRefsTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPackRefsTest.java index 1fa5aa09a..54708da03 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPackRefsTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPackRefsTest.java @@ -176,12 +176,13 @@ public boolean isForceUpdate() { return update.update(); }); - pool.submit(() -> { + Future result2 = pool.submit(() -> { refUpdateLockedRef.await(); gc.packRefs(); packRefsDone.await(); return null; }); + assertNull(result2.get()); assertSame(result.get(), Result.FORCED); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/transport/http/NetscapeCookieFileTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/transport/http/NetscapeCookieFileTest.java new file mode 100644 index 000000000..0a3f89e23 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/transport/http/NetscapeCookieFileTest.java @@ -0,0 +1,339 @@ +/* + * Copyright (C) 2018, Konrad Windszus + * 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.http; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Writer; +import java.net.HttpCookie; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.time.Instant; +import java.util.Arrays; +import java.util.Date; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import java.util.regex.Pattern; + +import org.eclipse.jgit.internal.storage.file.LockFile; +import org.eclipse.jgit.internal.transport.http.NetscapeCookieFile; +import org.eclipse.jgit.util.http.HttpCookiesMatcher; +import org.hamcrest.CoreMatchers; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +public class NetscapeCookieFileTest { + + @Rule + public TemporaryFolder folder = new TemporaryFolder(); + + private Path tmpFile; + + private URL baseUrl; + + /** + * This is the expiration date that is used in the test cookie files + */ + private static long JAN_01_2030_NOON = Instant + .parse("2030-01-01T12:00:00.000Z").toEpochMilli(); + + @Before + public void setUp() throws IOException { + // this will not only return a new file name but also create new empty + // file! + tmpFile = folder.newFile().toPath(); + baseUrl = new URL("http://domain.com/my/path"); + } + + @Test + public void testMergeCookies() { + Set cookieSet1 = new LinkedHashSet<>(); + HttpCookie cookie = new HttpCookie("key1", "valueFromSet1"); + cookieSet1.add(cookie); + cookie = new HttpCookie("key2", "valueFromSet1"); + cookieSet1.add(cookie); + + Set cookieSet2 = new LinkedHashSet<>(); + cookie = new HttpCookie("key1", "valueFromSet2"); + cookieSet2.add(cookie); + cookie = new HttpCookie("key3", "valueFromSet2"); + cookieSet2.add(cookie); + + Set cookiesExpectedMergedSet = new LinkedHashSet<>(); + cookie = new HttpCookie("key1", "valueFromSet1"); + cookiesExpectedMergedSet.add(cookie); + cookie = new HttpCookie("key2", "valueFromSet1"); + cookiesExpectedMergedSet.add(cookie); + cookie = new HttpCookie("key3", "valueFromSet2"); + cookiesExpectedMergedSet.add(cookie); + + Assert.assertThat( + NetscapeCookieFile.mergeCookies(cookieSet1, cookieSet2), + HttpCookiesMatcher.containsInOrder(cookiesExpectedMergedSet)); + + Assert.assertThat(NetscapeCookieFile.mergeCookies(cookieSet1, null), + HttpCookiesMatcher.containsInOrder(cookieSet1)); + } + + @Test + public void testWriteToNewFile() throws IOException { + Set cookies = new LinkedHashSet<>(); + cookies.add(new HttpCookie("key1", "value")); + // first cookie is a session cookie (and should be ignored) + + HttpCookie cookie = new HttpCookie("key2", "value"); + cookie.setSecure(true); + cookie.setDomain("mydomain.com"); + cookie.setPath("/"); + cookie.setMaxAge(1000); + cookies.add(cookie); + Date creationDate = new Date(); + try (Writer writer = Files.newBufferedWriter(tmpFile, + StandardCharsets.US_ASCII)) { + NetscapeCookieFile.write(writer, cookies, baseUrl, creationDate); + } + + String expectedExpiration = String + .valueOf(creationDate.getTime() + (cookie.getMaxAge() * 1000)); + + Assert.assertThat( + Files.readAllLines(tmpFile, StandardCharsets.US_ASCII), + CoreMatchers + .equalTo(Arrays.asList("mydomain.com\tTRUE\t/\tTRUE\t" + + expectedExpiration + "\tkey2\tvalue"))); + } + + @Test + public void testWriteToExistingFile() throws IOException { + try (InputStream input = this.getClass() + .getResourceAsStream("cookies-simple1.txt")) { + Files.copy(input, tmpFile, StandardCopyOption.REPLACE_EXISTING); + } + + Set cookies = new LinkedHashSet<>(); + HttpCookie cookie = new HttpCookie("key2", "value2"); + cookie.setMaxAge(1000); + cookies.add(cookie); + Date creationDate = new Date(); + try (Writer writer = Files.newBufferedWriter(tmpFile, + StandardCharsets.US_ASCII)) { + NetscapeCookieFile.write(writer, cookies, baseUrl, creationDate); + } + String expectedExpiration = String + .valueOf(creationDate.getTime() + (cookie.getMaxAge() * 1000)); + + Assert.assertThat( + Files.readAllLines(tmpFile, StandardCharsets.US_ASCII), + CoreMatchers.equalTo( + Arrays.asList("domain.com\tTRUE\t/my/path\tFALSE\t" + + expectedExpiration + "\tkey2\tvalue2"))); + } + + @Test(expected = IOException.class) + public void testWriteWhileSomeoneIsHoldingTheLock() + throws IllegalArgumentException, IOException, InterruptedException { + try (InputStream input = this.getClass() + .getResourceAsStream("cookies-simple1.txt")) { + Files.copy(input, tmpFile, StandardCopyOption.REPLACE_EXISTING); + } + NetscapeCookieFile cookieFile = new NetscapeCookieFile(tmpFile); + // now imitate another process/thread holding the lock file + LockFile lockFile = new LockFile(tmpFile.toFile()); + try { + Assert.assertTrue("Could not acquire lock", lockFile.lock()); + cookieFile.write(baseUrl); + } finally { + lockFile.unlock(); + } + } + + @Test + public void testWriteAfterAnotherJgitProcessModifiedTheFile() + throws IOException, InterruptedException { + try (InputStream input = this.getClass() + .getResourceAsStream("cookies-simple1.txt")) { + Files.copy(input, tmpFile, StandardCopyOption.REPLACE_EXISTING); + } + NetscapeCookieFile cookieFile = new NetscapeCookieFile(tmpFile); + cookieFile.getCookies(true); + // now modify file externally + try (InputStream input = this.getClass() + .getResourceAsStream("cookies-simple2.txt")) { + Files.copy(input, tmpFile, StandardCopyOption.REPLACE_EXISTING); + } + // now try to write + cookieFile.write(baseUrl); + + // validate that the external changes are there as well + // due to rounding errors (conversion from ms to sec to ms) + // the expiration date might not be exact + List lines = Files.readAllLines(tmpFile, + StandardCharsets.US_ASCII); + + Assert.assertEquals("Expected 3 lines", 3, lines.size()); + assertStringMatchesPatternWithInexactNumber(lines.get(0), + "some-domain1\tTRUE\t/some/path1\tFALSE\t(\\d*)\tkey1\tvalueFromSimple2", + JAN_01_2030_NOON, 1000); + assertStringMatchesPatternWithInexactNumber(lines.get(1), + "some-domain1\tTRUE\t/some/path1\tFALSE\t(\\d*)\tkey3\tvalueFromSimple2", + JAN_01_2030_NOON, 1000); + assertStringMatchesPatternWithInexactNumber(lines.get(2), + "some-domain1\tTRUE\t/some/path1\tFALSE\t(\\d*)\tkey2\tvalueFromSimple1", + JAN_01_2030_NOON, 1000); + } + + @SuppressWarnings("boxing") + private static final void assertStringMatchesPatternWithInexactNumber( + String string, String pattern, long expectedNumericValue, + long delta) { + java.util.regex.Matcher matcher = Pattern.compile(pattern) + .matcher(string); + Assert.assertTrue("Given string '" + string + "' does not match '" + + pattern + "'", matcher.matches()); + // extract numeric value + Long actualNumericValue = Long.decode(matcher.group(1)); + + Assert.assertTrue( + "Value is supposed to be close to " + expectedNumericValue + + " but is " + actualNumericValue + ".", + Math.abs(expectedNumericValue - actualNumericValue) <= delta); + } + + @Test + public void testWriteAndReadCycle() throws IOException { + Set cookies = new LinkedHashSet<>(); + + HttpCookie cookie = new HttpCookie("key1", "value1"); + cookie.setPath("/some/path1"); + cookie.setDomain("some-domain1"); + cookie.setMaxAge(1000); + cookies.add(cookie); + cookie = new HttpCookie("key2", "value2"); + cookie.setSecure(true); + cookie.setPath("/some/path2"); + cookie.setDomain("some-domain2"); + cookie.setMaxAge(1000); + cookie.setHttpOnly(true); + cookies.add(cookie); + + Date creationDate = new Date(); + + try (Writer writer = Files.newBufferedWriter(tmpFile, + StandardCharsets.US_ASCII)) { + NetscapeCookieFile.write(writer, cookies, baseUrl, creationDate); + } + Set actualCookies = new NetscapeCookieFile(tmpFile, + creationDate).getCookies(true); + Assert.assertThat(actualCookies, + HttpCookiesMatcher.containsInOrder(cookies)); + } + + @Test + public void testReadAndWriteCycle() throws IOException { + try (InputStream input = this.getClass() + .getResourceAsStream("cookies-simple1.txt")) { + Files.copy(input, tmpFile, StandardCopyOption.REPLACE_EXISTING); + } + // round up to the next second (to prevent rounding errors) + Date creationDate = new Date( + (System.currentTimeMillis() / 1000) * 1000); + Set cookies = new NetscapeCookieFile(tmpFile, creationDate) + .getCookies(true); + Path tmpFile2 = folder.newFile().toPath(); + try (Writer writer = Files.newBufferedWriter(tmpFile2, + StandardCharsets.US_ASCII)) { + NetscapeCookieFile.write(writer, cookies, baseUrl, creationDate); + } + // compare original file with newly written one, they should not differ + Assert.assertEquals(Files.readAllLines(tmpFile), + Files.readAllLines(tmpFile2)); + } + + @Test + public void testReadWithEmptyAndCommentLines() throws IOException { + try (InputStream input = this.getClass().getResourceAsStream( + "cookies-with-empty-and-comment-lines.txt")) { + Files.copy(input, tmpFile, StandardCopyOption.REPLACE_EXISTING); + } + + Date creationDate = new Date(); + Set cookies = new LinkedHashSet<>(); + + HttpCookie cookie = new HttpCookie("key2", "value2"); + cookie.setDomain("some-domain2"); + cookie.setPath("/some/path2"); + cookie.setMaxAge((JAN_01_2030_NOON - creationDate.getTime()) / 1000); + cookie.setSecure(true); + cookie.setHttpOnly(true); + cookies.add(cookie); + + cookie = new HttpCookie("key3", "value3"); + cookie.setDomain("some-domain3"); + cookie.setPath("/some/path3"); + cookie.setMaxAge((JAN_01_2030_NOON - creationDate.getTime()) / 1000); + cookies.add(cookie); + + Set actualCookies = new NetscapeCookieFile(tmpFile, creationDate) + .getCookies(true); + Assert.assertThat(actualCookies, + HttpCookiesMatcher.containsInOrder(cookies)); + } + + @Test + public void testReadInvalidFile() throws IOException { + try (InputStream input = this.getClass() + .getResourceAsStream("cookies-invalid.txt")) { + Files.copy(input, tmpFile, StandardCopyOption.REPLACE_EXISTING); + } + + new NetscapeCookieFile(tmpFile) + .getCookies(true); + } +} 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 5a4bd886a..e3985cc97 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 @@ -1457,6 +1457,41 @@ public void testInvalidGroupHeader() throws ConfigInvalidException { parse("[foo \"bar\" ]\nfoo=bar\n"); } + @Test + public void testCrLf() throws ConfigInvalidException { + assertEquals("true", parseEscapedValue("true\r\n")); + } + + @Test + public void testLfContinuation() throws ConfigInvalidException { + assertEquals("true", parseEscapedValue("tr\\\nue")); + } + + @Test + public void testCrCharContinuation() throws ConfigInvalidException { + expectedEx.expect(ConfigInvalidException.class); + expectedEx.expectMessage("Bad escape: \\u000d"); + parseEscapedValue("tr\\\rue"); + } + + @Test + public void testCrEOFContinuation() throws ConfigInvalidException { + expectedEx.expect(ConfigInvalidException.class); + expectedEx.expectMessage("Bad escape: \\u000d"); + parseEscapedValue("tr\\\r"); + } + + @Test + public void testCrLfContinuation() throws ConfigInvalidException { + assertEquals("true", parseEscapedValue("tr\\\r\nue")); + } + + @Test + public void testWhitespaceContinuation() throws ConfigInvalidException { + assertEquals("tr ue", parseEscapedValue("tr \\\n ue")); + assertEquals("tr ue", parseEscapedValue("tr \\\r\n ue")); + } + private static void assertValueRoundTrip(String value) throws ConfigInvalidException { assertValueRoundTrip(value, value); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RebaseTodoFileTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RebaseTodoFileTest.java new file mode 100644 index 000000000..5cfc75ca9 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RebaseTodoFileTest.java @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2019, Thomas Wolf + * 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.fail; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.List; + +import org.eclipse.jgit.junit.RepositoryTestCase; +import org.junit.Test; + +public class RebaseTodoFileTest extends RepositoryTestCase { + + private static final String TEST_TODO = "test.todo"; + + private void createTodoList(String... lines) throws IOException { + Path p = Paths.get(db.getDirectory().getAbsolutePath(), TEST_TODO); + Files.write(p, Arrays.asList(lines)); + } + + @Test + public void testReadTodoFile() throws Exception { + String[] expected = { "reword " + ObjectId.zeroId().name() + " Foo", + "# A comment in the the todo list", + "pick " + ObjectId.zeroId().name() + " Foo fie", + "squash " + ObjectId.zeroId().name() + " F", + "fixup " + ObjectId.zeroId().name(), + "edit " + ObjectId.zeroId().name() + " f", + "edit " + ObjectId.zeroId().name() + ' ' }; + createTodoList(expected); + RebaseTodoFile todo = new RebaseTodoFile(db); + List lines = todo.readRebaseTodo(TEST_TODO, true); + assertEquals("Expected 7 lines", 7, lines.size()); + int i = 0; + for (RebaseTodoLine line : lines) { + switch (i) { + case 0: + assertEquals("Expected REWORD", RebaseTodoLine.Action.REWORD, + line.getAction()); + assertEquals("Unexpected ID", ObjectId.zeroId().abbreviate(40), + line.getCommit()); + assertEquals("Unexpected Message", "Foo", + line.getShortMessage()); + break; + case 1: + assertEquals("Expected COMMENT", RebaseTodoLine.Action.COMMENT, + line.getAction()); + assertEquals("Unexpected Message", + "# A comment in the the todo list", + line.getComment()); + break; + case 2: + assertEquals("Expected PICK", RebaseTodoLine.Action.PICK, + line.getAction()); + assertEquals("Unexpected ID", ObjectId.zeroId().abbreviate(40), + line.getCommit()); + assertEquals("Unexpected Message", "Foo fie", + line.getShortMessage()); + break; + case 3: + assertEquals("Expected SQUASH", RebaseTodoLine.Action.SQUASH, + line.getAction()); + assertEquals("Unexpected ID", ObjectId.zeroId().abbreviate(40), + line.getCommit()); + assertEquals("Unexpected Message", "F", line.getShortMessage()); + break; + case 4: + assertEquals("Expected FIXUP", RebaseTodoLine.Action.FIXUP, + line.getAction()); + assertEquals("Unexpected ID", ObjectId.zeroId().abbreviate(40), + line.getCommit()); + assertEquals("Unexpected Message", "", line.getShortMessage()); + break; + case 5: + assertEquals("Expected EDIT", RebaseTodoLine.Action.EDIT, + line.getAction()); + assertEquals("Unexpected ID", ObjectId.zeroId().abbreviate(40), + line.getCommit()); + assertEquals("Unexpected Message", "f", line.getShortMessage()); + break; + case 6: + assertEquals("Expected EDIT", RebaseTodoLine.Action.EDIT, + line.getAction()); + assertEquals("Unexpected ID", ObjectId.zeroId().abbreviate(40), + line.getCommit()); + assertEquals("Unexpected Message", "", line.getShortMessage()); + break; + default: + fail("Too many lines"); + return; + } + i++; + } + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/BitmapCalculatorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/BitmapCalculatorTest.java index 3a78e1e62..c5d4d4238 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/BitmapCalculatorTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/BitmapCalculatorTest.java @@ -1,3 +1,45 @@ +/* + * Copyright (C) 2019, 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.revwalk; import static org.junit.Assert.assertEquals; diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PacketLineInTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PacketLineInTest.java index 8b1d86005..f512d2849 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PacketLineInTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PacketLineInTest.java @@ -44,7 +44,8 @@ package org.eclipse.jgit.transport; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertSame; import static org.junit.Assert.fail; @@ -142,21 +143,21 @@ public void testReadString_Len0004() throws IOException { init("0004"); final String act = in.readString(); assertEquals("", act); - assertNotSame(PacketLineIn.END, act); + assertFalse(PacketLineIn.isEnd(act)); assertEOF(); } @Test public void testReadString_End() throws IOException { init("0000"); - assertSame(PacketLineIn.END, in.readString()); + assertTrue(PacketLineIn.isEnd(in.readString())); assertEOF(); } @Test public void testReadString_Delim() throws IOException { init("0001"); - assertSame(PacketLineIn.DELIM, in.readString()); + assertTrue(PacketLineIn.isDelimiter(in.readString())); assertEOF(); } @@ -183,14 +184,14 @@ public void testReadStringRaw3() throws IOException { init("0004"); final String act = in.readStringRaw(); assertEquals("", act); - assertNotSame(PacketLineIn.END, act); + assertFalse(PacketLineIn.isEnd(act)); assertEOF(); } @Test public void testReadStringRaw_End() throws IOException { init("0000"); - assertSame(PacketLineIn.END, in.readStringRaw()); + assertTrue(PacketLineIn.isEnd(in.readString())); assertEOF(); } 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 index 28a9c03ec..6e8327c79 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ProtocolV0ParserTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ProtocolV0ParserTest.java @@ -64,9 +64,9 @@ private static PacketLineIn formatAsPacketLine(String... inputLines) ByteArrayOutputStream send = new ByteArrayOutputStream(); PacketLineOut pckOut = new PacketLineOut(send); for (String line : inputLines) { - if (line == PacketLineIn.END) { + if (PacketLineIn.isEnd(line)) { pckOut.end(); - } else if (line == PacketLineIn.DELIM) { + } else if (PacketLineIn.isDelimiter(line)) { pckOut.writeDelim(); } else { pckOut.writeString(line); @@ -90,7 +90,7 @@ public void testRecvWantsWithCapabilities() "4624442d68ee402a94364191085b77137618633e", "thin-pack", "no-progress", "include-tag", "ofs-delta", "\n"), "want f900c8326a43303685c46b279b9f70411bff1a4b\n", - PacketLineIn.END); + PacketLineIn.end()); ProtocolV0Parser parser = new ProtocolV0Parser(defaultConfig()); FetchV0Request request = parser.recvWants(pckIn); assertTrue(request.getClientCapabilities() @@ -114,7 +114,7 @@ public void testRecvWantsWithAgent() "4624442d68ee402a94364191085b77137618633e", "thin-pack", "agent=JGit.test/0.0.1", "\n"), "want f900c8326a43303685c46b279b9f70411bff1a4b\n", - PacketLineIn.END); + PacketLineIn.end()); ProtocolV0Parser parser = new ProtocolV0Parser(defaultConfig()); FetchV0Request request = parser.recvWants(pckIn); assertTrue(request.getClientCapabilities() @@ -136,7 +136,7 @@ public void testRecvWantsWithoutCapabilities() PacketLineIn pckIn = formatAsPacketLine( "want 4624442d68ee402a94364191085b77137618633e\n", "want f900c8326a43303685c46b279b9f70411bff1a4b\n", - PacketLineIn.END); + PacketLineIn.end()); ProtocolV0Parser parser = new ProtocolV0Parser(defaultConfig()); FetchV0Request request = parser.recvWants(pckIn); assertTrue(request.getClientCapabilities().isEmpty()); @@ -151,7 +151,7 @@ public void testRecvWantsDeepen() PacketLineIn pckIn = formatAsPacketLine( "want 4624442d68ee402a94364191085b77137618633e\n", "want f900c8326a43303685c46b279b9f70411bff1a4b\n", "deepen 3\n", - PacketLineIn.END); + PacketLineIn.end()); ProtocolV0Parser parser = new ProtocolV0Parser(defaultConfig()); FetchV0Request request = parser.recvWants(pckIn); assertTrue(request.getClientCapabilities().isEmpty()); @@ -168,7 +168,7 @@ public void testRecvWantsShallow() "want 4624442d68ee402a94364191085b77137618633e\n", "want f900c8326a43303685c46b279b9f70411bff1a4b\n", "shallow 4b643d0ef739a1b494e7d6926d8d8ed80d35edf4\n", - PacketLineIn.END); + PacketLineIn.end()); ProtocolV0Parser parser = new ProtocolV0Parser(defaultConfig()); FetchV0Request request = parser.recvWants(pckIn); assertTrue(request.getClientCapabilities().isEmpty()); @@ -186,7 +186,7 @@ public void testRecvWantsFilter() "want 4624442d68ee402a94364191085b77137618633e\n", "want f900c8326a43303685c46b279b9f70411bff1a4b\n", "filter blob:limit=13000\n", - PacketLineIn.END); + PacketLineIn.end()); ProtocolV0Parser parser = new ProtocolV0Parser(defaultConfig()); FetchV0Request request = parser.recvWants(pckIn); assertTrue(request.getClientCapabilities().isEmpty()); 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 740d92ef4..0d70cd612 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 @@ -123,9 +123,9 @@ private static PacketLineIn formatAsPacketLine(String... inputLines) ByteArrayOutputStream send = new ByteArrayOutputStream(); PacketLineOut pckOut = new PacketLineOut(send); for (String line : inputLines) { - if (line == PacketLineIn.END) { + if (PacketLineIn.isEnd(line)) { pckOut.end(); - } else if (line == PacketLineIn.DELIM) { + } else if (PacketLineIn.isDelimiter(line)) { pckOut.writeDelim(); } else { pckOut.writeString(line); @@ -136,19 +136,19 @@ private static PacketLineIn formatAsPacketLine(String... inputLines) } /* - * Succesful fetch with the basic core commands of the protocol. + * Successful fetch with the basic core commands of the protocol. */ @Test public void testFetchBasicArguments() throws PackProtocolException, IOException { PacketLineIn pckIn = formatAsPacketLine( - PacketLineIn.DELIM, + PacketLineIn.delimiter(), "thin-pack", "no-progress", "include-tag", "ofs-delta", "want 4624442d68ee402a94364191085b77137618633e", "want f900c8326a43303685c46b279b9f70411bff1a4b", "have 554f6e41067b9e3e565b6988a8294fac1cb78f4b", "have abc760ab9ad72f08209943251b36cb886a578f87", "done", - PacketLineIn.END); + PacketLineIn.end()); ProtocolV2Parser parser = new ProtocolV2Parser( ConfigBuilder.getDefault()); FetchV2Request request = parser.parseFetchRequest(pckIn); @@ -173,12 +173,12 @@ public void testFetchBasicArguments() @Test public void testFetchWithShallow_deepen() throws IOException { PacketLineIn pckIn = formatAsPacketLine( - PacketLineIn.DELIM, + PacketLineIn.delimiter(), "deepen 15", "deepen-relative", "shallow 28274d02c489f4c7e68153056e9061a46f62d7a0", "shallow 145e683b229dcab9d0e2ccb01b386f9ecc17d29d", - PacketLineIn.END); + PacketLineIn.end()); ProtocolV2Parser parser = new ProtocolV2Parser( ConfigBuilder.getDefault()); FetchV2Request request = parser.parseFetchRequest(pckIn); @@ -193,11 +193,11 @@ public void testFetchWithShallow_deepen() throws IOException { @Test public void testFetchWithShallow_deepenNot() throws IOException { - PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.DELIM, + PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.delimiter(), "shallow 28274d02c489f4c7e68153056e9061a46f62d7a0", "shallow 145e683b229dcab9d0e2ccb01b386f9ecc17d29d", "deepen-not a08595f76159b09d57553e37a5123f1091bb13e7", - PacketLineIn.END); + PacketLineIn.end()); ProtocolV2Parser parser = new ProtocolV2Parser( ConfigBuilder.getDefault()); FetchV2Request request = parser.parseFetchRequest(pckIn); @@ -210,11 +210,11 @@ public void testFetchWithShallow_deepenNot() throws IOException { @Test public void testFetchWithShallow_deepenSince() throws IOException { - PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.DELIM, + PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.delimiter(), "shallow 28274d02c489f4c7e68153056e9061a46f62d7a0", "shallow 145e683b229dcab9d0e2ccb01b386f9ecc17d29d", "deepen-since 123123123", - PacketLineIn.END); + PacketLineIn.end()); ProtocolV2Parser parser = new ProtocolV2Parser( ConfigBuilder.getDefault()); FetchV2Request request = parser.parseFetchRequest(pckIn); @@ -226,9 +226,9 @@ public void testFetchWithShallow_deepenSince() throws IOException { @Test public void testFetchWithNoneFilter() throws IOException { - PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.DELIM, + PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.delimiter(), "filter blob:none", - PacketLineIn.END); + PacketLineIn.end()); ProtocolV2Parser parser = new ProtocolV2Parser( ConfigBuilder.start().allowFilter().done()); FetchV2Request request = parser.parseFetchRequest(pckIn); @@ -238,9 +238,9 @@ public void testFetchWithNoneFilter() throws IOException { @Test public void testFetchWithBlobSizeFilter() throws IOException { - PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.DELIM, + PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.delimiter(), "filter blob:limit=15", - PacketLineIn.END); + PacketLineIn.end()); ProtocolV2Parser parser = new ProtocolV2Parser( ConfigBuilder.start().allowFilter().done()); FetchV2Request request = parser.parseFetchRequest(pckIn); @@ -250,9 +250,9 @@ public void testFetchWithBlobSizeFilter() throws IOException { @Test public void testFetchWithTreeDepthFilter() throws IOException { - PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.DELIM, + PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.delimiter(), "filter tree:3", - PacketLineIn.END); + PacketLineIn.end()); ProtocolV2Parser parser = new ProtocolV2Parser( ConfigBuilder.start().allowFilter().done()); FetchV2Request request = parser.parseFetchRequest(pckIn); @@ -262,10 +262,10 @@ public void testFetchWithTreeDepthFilter() throws IOException { @Test public void testFetchMustNotHaveMultipleFilters() throws IOException { - PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.DELIM, + PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.delimiter(), "filter blob:none", "filter blob:limit=12", - PacketLineIn.END); + PacketLineIn.end()); ProtocolV2Parser parser = new ProtocolV2Parser( ConfigBuilder.start().allowFilter().done()); @@ -275,8 +275,8 @@ public void testFetchMustNotHaveMultipleFilters() throws IOException { @Test public void testFetchFilterWithoutAllowFilter() throws IOException { - PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.DELIM, - "filter blob:limit=12", PacketLineIn.END); + PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.delimiter(), + "filter blob:limit=12", PacketLineIn.end()); ProtocolV2Parser parser = new ProtocolV2Parser( ConfigBuilder.getDefault()); @@ -291,10 +291,10 @@ public void testFetchWithRefInWant() throws Exception { testRepo.update("branchA", one); testRepo.update("branchB", two); - PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.DELIM, + PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.delimiter(), "want e4980cdc48cfa1301493ca94eb70523f6788b819", "want-ref refs/heads/branchA", - PacketLineIn.END); + PacketLineIn.end()); ProtocolV2Parser parser = new ProtocolV2Parser( ConfigBuilder.start().allowRefInWant().done()); @@ -309,10 +309,10 @@ public void testFetchWithRefInWant() throws Exception { @Test public void testFetchWithRefInWantUnknownRef() throws Exception { - PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.DELIM, + PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.delimiter(), "want e4980cdc48cfa1301493ca94eb70523f6788b819", "want-ref refs/heads/branchC", - PacketLineIn.END); + PacketLineIn.end()); ProtocolV2Parser parser = new ProtocolV2Parser( ConfigBuilder.start().allowRefInWant().done()); @@ -328,8 +328,8 @@ public void testFetchWithRefInWantUnknownRef() throws Exception { @Test public void testLsRefsMinimalReq() throws IOException { - PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.DELIM, - PacketLineIn.END); + PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.delimiter(), + PacketLineIn.end()); ProtocolV2Parser parser = new ProtocolV2Parser( ConfigBuilder.getDefault()); @@ -341,8 +341,8 @@ public void testLsRefsMinimalReq() throws IOException { @Test public void testLsRefsSymrefs() throws IOException { - PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.DELIM, "symrefs", - PacketLineIn.END); + PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.delimiter(), "symrefs", + PacketLineIn.end()); ProtocolV2Parser parser = new ProtocolV2Parser( ConfigBuilder.getDefault()); @@ -356,9 +356,9 @@ public void testLsRefsSymrefs() throws IOException { @Test public void testLsRefsPeel() throws IOException { PacketLineIn pckIn = formatAsPacketLine( - PacketLineIn.DELIM, + PacketLineIn.delimiter(), "peel", - PacketLineIn.END); + PacketLineIn.end()); ProtocolV2Parser parser = new ProtocolV2Parser( ConfigBuilder.getDefault()); @@ -370,9 +370,9 @@ public void testLsRefsPeel() throws IOException { @Test public void testLsRefsRefPrefixes() throws IOException { - PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.DELIM, + PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.delimiter(), "ref-prefix refs/for", "ref-prefix refs/heads", - PacketLineIn.END); + PacketLineIn.end()); ProtocolV2Parser parser = new ProtocolV2Parser( ConfigBuilder.getDefault()); 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 50c8a2954..5d208eea7 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 @@ -306,11 +306,11 @@ public void testCreateBranchAtHiddenCommitFails() throws Exception { int nul = master.indexOf('\0'); assertTrue("has capability list", nul > 0); assertEquals(B.name() + ' ' + R_MASTER, master.substring(0, nul)); - assertSame(PacketLineIn.END, r.readString()); + assertTrue(PacketLineIn.isEnd(r.readString())); assertEquals("unpack error Missing commit " + P.name(), r.readString()); assertEquals("ng refs/heads/s n/a (unpacker error)", r.readString()); - assertSame(PacketLineIn.END, r.readString()); + assertTrue(PacketLineIn.isEnd(r.readString())); } private static void receive(final ReceivePack rp, @@ -366,13 +366,13 @@ public void testUsingHiddenDeltaBaseFails() throws Exception { int nul = master.indexOf('\0'); assertTrue("has capability list", nul > 0); assertEquals(B.name() + ' ' + R_MASTER, master.substring(0, nul)); - assertSame(PacketLineIn.END, r.readString()); + assertTrue(PacketLineIn.isEnd(r.readString())); assertEquals("unpack error Missing blob " + b.name(), r.readString()); assertEquals("ng refs/heads/s n/a (unpacker error)", r.readString()); - assertSame(PacketLineIn.END, r.readString()); + assertTrue(PacketLineIn.isEnd(r.readString())); } } @@ -419,13 +419,13 @@ public void testUsingHiddenCommonBlobFails() throws Exception { int nul = master.indexOf('\0'); assertTrue("has capability list", nul > 0); assertEquals(B.name() + ' ' + R_MASTER, master.substring(0, nul)); - assertSame(PacketLineIn.END, r.readString()); + assertTrue(PacketLineIn.isEnd(r.readString())); assertEquals("unpack error Missing blob " + b.name(), r.readString()); assertEquals("ng refs/heads/s n/a (unpacker error)", r.readString()); - assertSame(PacketLineIn.END, r.readString()); + assertTrue(PacketLineIn.isEnd(r.readString())); } } @@ -473,13 +473,13 @@ public void testUsingUnknownBlobFails() throws Exception { int nul = master.indexOf('\0'); assertTrue("has capability list", nul > 0); assertEquals(B.name() + ' ' + R_MASTER, master.substring(0, nul)); - assertSame(PacketLineIn.END, r.readString()); + assertTrue(PacketLineIn.isEnd(r.readString())); assertEquals("unpack error Missing blob " + n.name(), r.readString()); assertEquals("ng refs/heads/s n/a (unpacker error)", r.readString()); - assertSame(PacketLineIn.END, r.readString()); + assertTrue(PacketLineIn.isEnd(r.readString())); } } @@ -504,13 +504,13 @@ public void testIncludesInvalidGitmodules() throws Exception { int nul = master.indexOf('\0'); assertTrue("has capability list", nul > 0); assertEquals(B.name() + ' ' + R_MASTER, master.substring(0, nul)); - assertSame(PacketLineIn.END, r.readString()); + assertTrue(PacketLineIn.isEnd(r.readString())); String errorLine = r.readString(); 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()); + assertTrue(PacketLineIn.isEnd(r.readString())); } private TemporaryBuffer.Heap setupSourceRepoInvalidGitmodules() @@ -589,13 +589,13 @@ public void testUsingUnknownTreeFails() throws Exception { int nul = master.indexOf('\0'); assertTrue("has capability list", nul > 0); assertEquals(B.name() + ' ' + R_MASTER, master.substring(0, nul)); - assertSame(PacketLineIn.END, r.readString()); + assertTrue(PacketLineIn.isEnd(r.readString())); assertEquals("unpack error Missing tree " + t.name(), r.readString()); assertEquals("ng refs/heads/s n/a (unpacker error)", r.readString()); - assertSame(PacketLineIn.END, r.readString()); + assertTrue(PacketLineIn.isEnd(r.readString())); } } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/RefAdvertiserTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/RefAdvertiserTest.java index ce69adf9c..ccb548d91 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/RefAdvertiserTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/RefAdvertiserTest.java @@ -45,7 +45,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 java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -95,7 +95,7 @@ public void advertiser() throws IOException { assertEquals(id(3).name() + " refs/Iñtërnâtiônàlizætiøn☃💩\n", s); s = pckIn.readStringRaw(); - assertSame(PacketLineIn.END, s); + assertTrue(PacketLineIn.isEnd(s)); } private static ObjectId id(int i) { diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TransportHttpTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TransportHttpTest.java new file mode 100644 index 000000000..ee0e0af8c --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TransportHttpTest.java @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2018, Konrad Windszus + * 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.File; +import java.io.IOException; +import java.net.HttpCookie; +import java.time.Instant; +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import java.util.LinkedHashSet; +import java.util.Set; + +import org.eclipse.jgit.internal.transport.http.NetscapeCookieFile; +import org.eclipse.jgit.lib.Config; +import org.eclipse.jgit.test.resources.SampleDataRepositoryTestCase; +import org.eclipse.jgit.transport.http.HttpConnection; +import org.eclipse.jgit.util.http.HttpCookiesMatcher; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentMatchers; +import org.mockito.Mockito; + +public class TransportHttpTest extends SampleDataRepositoryTestCase { + private URIish uri; + private File cookieFile; + + @Override + @Before + public void setUp() throws Exception { + super.setUp(); + uri = new URIish("https://everyones.loves.git/u/2"); + + final Config config = db.getConfig(); + config.setBoolean("http", null, "saveCookies", true); + cookieFile = createTempFile(); + config.setString("http", null, "cookieFile", + cookieFile.getAbsolutePath()); + } + + @Test + public void testMatchesCookieDomain() { + Assert.assertTrue(TransportHttp.matchesCookieDomain("example.com", + "example.com")); + Assert.assertTrue(TransportHttp.matchesCookieDomain("Example.Com", + "example.cOM")); + Assert.assertTrue(TransportHttp.matchesCookieDomain( + "some.subdomain.example.com", "example.com")); + Assert.assertFalse(TransportHttp + .matchesCookieDomain("someotherexample.com", "example.com")); + Assert.assertFalse(TransportHttp.matchesCookieDomain("example.com", + "example1.com")); + Assert.assertFalse(TransportHttp + .matchesCookieDomain("sub.sub.example.com", ".example.com")); + Assert.assertTrue(TransportHttp.matchesCookieDomain("host.example.com", + "example.com")); + Assert.assertTrue(TransportHttp.matchesCookieDomain( + "something.example.com", "something.example.com")); + Assert.assertTrue(TransportHttp.matchesCookieDomain( + "host.something.example.com", "something.example.com")); + } + + @Test + public void testMatchesCookiePath() { + Assert.assertTrue( + TransportHttp.matchesCookiePath("/some/path", "/some/path")); + Assert.assertTrue(TransportHttp.matchesCookiePath("/some/path/child", + "/some/path")); + Assert.assertTrue(TransportHttp.matchesCookiePath("/some/path/child", + "/some/path/")); + Assert.assertFalse(TransportHttp.matchesCookiePath("/some/pathother", + "/some/path")); + Assert.assertFalse( + TransportHttp.matchesCookiePath("otherpath", "/some/path")); + } + + @Test + public void testProcessResponseCookies() throws IOException { + HttpConnection connection = Mockito.mock(HttpConnection.class); + Mockito.when( + connection.getHeaderFields(ArgumentMatchers.eq("Set-Cookie"))) + .thenReturn(Arrays.asList( + "id=a3fWa; Expires=Fri, 01 Jan 2100 11:00:00 GMT; Secure; HttpOnly", + "sessionid=38afes7a8; HttpOnly; Path=/")); + Mockito.when( + connection.getHeaderFields(ArgumentMatchers.eq("Set-Cookie2"))) + .thenReturn(Collections + .singletonList("cookie2=some value; Max-Age=1234; Path=/")); + + try (TransportHttp transportHttp = new TransportHttp(db, uri)) { + Date creationDate = new Date(); + transportHttp.processResponseCookies(connection); + + // evaluate written cookie file + Set expectedCookies = new LinkedHashSet<>(); + + HttpCookie cookie = new HttpCookie("id", "a3fWa"); + cookie.setDomain("everyones.loves.git"); + cookie.setPath("/u/2/"); + + cookie.setMaxAge( + (Instant.parse("2100-01-01T11:00:00.000Z").toEpochMilli() + - creationDate.getTime()) / 1000); + cookie.setSecure(true); + cookie.setHttpOnly(true); + expectedCookies.add(cookie); + + cookie = new HttpCookie("cookie2", "some value"); + cookie.setDomain("everyones.loves.git"); + cookie.setPath("/"); + cookie.setMaxAge(1234); + expectedCookies.add(cookie); + + Assert.assertThat( + new NetscapeCookieFile(cookieFile.toPath()) + .getCookies(true), + HttpCookiesMatcher.containsInOrder(expectedCookies, 5)); + } + } + + @Test + public void testProcessResponseCookiesNotPersistingWithSaveCookiesFalse() + throws IOException { + HttpConnection connection = Mockito.mock(HttpConnection.class); + Mockito.when( + connection.getHeaderFields(ArgumentMatchers.eq("Set-Cookie"))) + .thenReturn(Arrays.asList( + "id=a3fWa; Expires=Thu, 21 Oct 2100 11:00:00 GMT; Secure; HttpOnly", + "sessionid=38afes7a8; HttpOnly; Path=/")); + Mockito.when( + connection.getHeaderFields(ArgumentMatchers.eq("Set-Cookie2"))) + .thenReturn(Collections.singletonList( + "cookie2=some value; Max-Age=1234; Path=/")); + + // tweak config + final Config config = db.getConfig(); + config.setBoolean("http", null, "saveCookies", false); + + try (TransportHttp transportHttp = new TransportHttp(db, uri)) { + transportHttp.processResponseCookies(connection); + + // evaluate written cookie file + Assert.assertFalse("Cookie file was not supposed to be written!", + cookieFile.exists()); + } + } +} 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 9ffcbccc8..260130b2b 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 @@ -4,7 +4,6 @@ import static org.hamcrest.Matchers.hasItems; import static org.hamcrest.Matchers.is; 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; @@ -428,9 +427,9 @@ private static ByteArrayInputStream linesAsInputStream(String... inputLines) try (ByteArrayOutputStream send = new ByteArrayOutputStream()) { PacketLineOut pckOut = new PacketLineOut(send); for (String line : inputLines) { - if (line == PacketLineIn.END) { + if (PacketLineIn.isEnd(line)) { pckOut.end(); - } else if (line == PacketLineIn.DELIM) { + } else if (PacketLineIn.isDelimiter(line)) { pckOut.writeDelim(); } else { pckOut.writeString(line); @@ -453,7 +452,7 @@ private ByteArrayInputStream uploadPackV2(RequestPolicy requestPolicy, PacketLineIn pckIn = new PacketLineIn(recvStream); // drain capabilities - while (pckIn.readString() != PacketLineIn.END) { + while (!PacketLineIn.isEnd(pckIn.readString())) { // do nothing } return recvStream; @@ -490,7 +489,7 @@ public void onFetch(FetchV2Request req) { public void testV2Capabilities() throws Exception { TestV2Hook hook = new TestV2Hook(); ByteArrayInputStream recvStream = - uploadPackV2Setup(null, null, hook, PacketLineIn.END); + uploadPackV2Setup(null, null, hook, PacketLineIn.end()); PacketLineIn pckIn = new PacketLineIn(recvStream); assertThat(hook.capabilitiesRequest, notNullValue()); assertThat(pckIn.readString(), is("version 2")); @@ -504,14 +503,14 @@ public void testV2Capabilities() throws Exception { // 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); + assertTrue(PacketLineIn.isEnd(pckIn.readString())); } @Test public void testV2CapabilitiesAllowFilter() throws Exception { server.getConfig().setBoolean("uploadpack", null, "allowfilter", true); ByteArrayInputStream recvStream = - uploadPackV2Setup(null, null, null, PacketLineIn.END); + uploadPackV2Setup(null, null, null, PacketLineIn.end()); PacketLineIn pckIn = new PacketLineIn(recvStream); assertThat(pckIn.readString(), is("version 2")); @@ -521,14 +520,14 @@ public void testV2CapabilitiesAllowFilter() throws Exception { // 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); + assertTrue(PacketLineIn.isEnd(pckIn.readString())); } @Test public void testV2CapabilitiesRefInWant() throws Exception { server.getConfig().setBoolean("uploadpack", null, "allowrefinwant", true); ByteArrayInputStream recvStream = - uploadPackV2Setup(null, null, null, PacketLineIn.END); + uploadPackV2Setup(null, null, null, PacketLineIn.end()); PacketLineIn pckIn = new PacketLineIn(recvStream); assertThat(pckIn.readString(), is("version 2")); @@ -539,14 +538,14 @@ public void testV2CapabilitiesRefInWant() throws Exception { // order of the capabilities of "fetch". hasItems("ls-refs", "fetch=ref-in-want shallow", "server-option")); - assertTrue(pckIn.readString() == PacketLineIn.END); + assertTrue(PacketLineIn.isEnd(pckIn.readString())); } @Test public void testV2CapabilitiesRefInWantNotAdvertisedIfUnallowed() throws Exception { server.getConfig().setBoolean("uploadpack", null, "allowrefinwant", false); ByteArrayInputStream recvStream = - uploadPackV2Setup(null, null, null, PacketLineIn.END); + uploadPackV2Setup(null, null, null, PacketLineIn.end()); PacketLineIn pckIn = new PacketLineIn(recvStream); assertThat(pckIn.readString(), is("version 2")); @@ -554,7 +553,7 @@ public void testV2CapabilitiesRefInWantNotAdvertisedIfUnallowed() throws Excepti Arrays.asList(pckIn.readString(), pckIn.readString(), pckIn.readString()), hasItems("ls-refs", "fetch=shallow", "server-option")); - assertTrue(pckIn.readString() == PacketLineIn.END); + assertTrue(PacketLineIn.isEnd(pckIn.readString())); } @Test @@ -562,7 +561,7 @@ public void testV2CapabilitiesRefInWantNotAdvertisedIfAdvertisingForbidden() thr server.getConfig().setBoolean("uploadpack", null, "allowrefinwant", true); server.getConfig().setBoolean("uploadpack", null, "advertiserefinwant", false); ByteArrayInputStream recvStream = - uploadPackV2Setup(null, null, null, PacketLineIn.END); + uploadPackV2Setup(null, null, null, PacketLineIn.end()); PacketLineIn pckIn = new PacketLineIn(recvStream); assertThat(pckIn.readString(), is("version 2")); @@ -570,12 +569,12 @@ public void testV2CapabilitiesRefInWantNotAdvertisedIfAdvertisingForbidden() thr Arrays.asList(pckIn.readString(), pckIn.readString(), pckIn.readString()), hasItems("ls-refs", "fetch=shallow", "server-option")); - assertTrue(pckIn.readString() == PacketLineIn.END); + assertTrue(PacketLineIn.isEnd(pckIn.readString())); } @Test public void testV2EmptyRequest() throws Exception { - ByteArrayInputStream recvStream = uploadPackV2(PacketLineIn.END); + ByteArrayInputStream recvStream = uploadPackV2(PacketLineIn.end()); // Verify that there is nothing more after the capability // advertisement. assertEquals(0, recvStream.available()); @@ -591,14 +590,14 @@ public void testV2LsRefs() throws Exception { TestV2Hook hook = new TestV2Hook(); ByteArrayInputStream recvStream = uploadPackV2(null, null, hook, - "command=ls-refs\n", PacketLineIn.END); + "command=ls-refs\n", PacketLineIn.end()); PacketLineIn pckIn = new PacketLineIn(recvStream); assertThat(hook.lsRefsRequest, notNullValue()); assertThat(pckIn.readString(), is(tip.toObjectId().getName() + " HEAD")); assertThat(pckIn.readString(), is(tip.toObjectId().getName() + " refs/heads/master")); assertThat(pckIn.readString(), is(tag.toObjectId().getName() + " refs/tags/tag")); - assertTrue(pckIn.readString() == PacketLineIn.END); + assertTrue(PacketLineIn.isEnd(pckIn.readString())); } @Test @@ -609,13 +608,14 @@ public void testV2LsRefsSymrefs() throws Exception { RevTag tag = remote.tag("tag", tip); remote.update("refs/tags/tag", tag); - ByteArrayInputStream recvStream = uploadPackV2("command=ls-refs\n", PacketLineIn.DELIM, "symrefs", PacketLineIn.END); + ByteArrayInputStream recvStream = uploadPackV2("command=ls-refs\n", + PacketLineIn.delimiter(), "symrefs", PacketLineIn.end()); PacketLineIn pckIn = new PacketLineIn(recvStream); assertThat(pckIn.readString(), is(tip.toObjectId().getName() + " HEAD symref-target:refs/heads/master")); assertThat(pckIn.readString(), is(tip.toObjectId().getName() + " refs/heads/master")); assertThat(pckIn.readString(), is(tag.toObjectId().getName() + " refs/tags/tag")); - assertTrue(pckIn.readString() == PacketLineIn.END); + assertTrue(PacketLineIn.isEnd(pckIn.readString())); } @Test @@ -626,7 +626,8 @@ public void testV2LsRefsPeel() throws Exception { RevTag tag = remote.tag("tag", tip); remote.update("refs/tags/tag", tag); - ByteArrayInputStream recvStream = uploadPackV2("command=ls-refs\n", PacketLineIn.DELIM, "peel", PacketLineIn.END); + ByteArrayInputStream recvStream = uploadPackV2("command=ls-refs\n", + PacketLineIn.delimiter(), "peel", PacketLineIn.end()); PacketLineIn pckIn = new PacketLineIn(recvStream); assertThat(pckIn.readString(), is(tip.toObjectId().getName() + " HEAD")); @@ -635,7 +636,7 @@ public void testV2LsRefsPeel() throws Exception { pckIn.readString(), is(tag.toObjectId().getName() + " refs/tags/tag peeled:" + tip.toObjectId().getName())); - assertTrue(pckIn.readString() == PacketLineIn.END); + assertTrue(PacketLineIn.isEnd(pckIn.readString())); } @Test @@ -647,8 +648,9 @@ public void testV2LsRefsMultipleCommands() throws Exception { remote.update("refs/tags/tag", tag); ByteArrayInputStream recvStream = uploadPackV2( - "command=ls-refs\n", PacketLineIn.DELIM, "symrefs", "peel", PacketLineIn.END, - "command=ls-refs\n", PacketLineIn.DELIM, PacketLineIn.END); + "command=ls-refs\n", PacketLineIn.delimiter(), "symrefs", + "peel", PacketLineIn.end(), "command=ls-refs\n", + PacketLineIn.delimiter(), PacketLineIn.end()); PacketLineIn pckIn = new PacketLineIn(recvStream); assertThat(pckIn.readString(), is(tip.toObjectId().getName() + " HEAD symref-target:refs/heads/master")); @@ -657,11 +659,11 @@ public void testV2LsRefsMultipleCommands() throws Exception { pckIn.readString(), is(tag.toObjectId().getName() + " refs/tags/tag peeled:" + tip.toObjectId().getName())); - assertTrue(pckIn.readString() == PacketLineIn.END); + assertTrue(PacketLineIn.isEnd(pckIn.readString())); assertThat(pckIn.readString(), is(tip.toObjectId().getName() + " HEAD")); assertThat(pckIn.readString(), is(tip.toObjectId().getName() + " refs/heads/master")); assertThat(pckIn.readString(), is(tag.toObjectId().getName() + " refs/tags/tag")); - assertTrue(pckIn.readString() == PacketLineIn.END); + assertTrue(PacketLineIn.isEnd(pckIn.readString())); } @Test @@ -673,15 +675,15 @@ public void testV2LsRefsRefPrefix() throws Exception { ByteArrayInputStream recvStream = uploadPackV2( "command=ls-refs\n", - PacketLineIn.DELIM, + PacketLineIn.delimiter(), "ref-prefix refs/heads/maste", "ref-prefix refs/heads/other", - PacketLineIn.END); + PacketLineIn.end()); PacketLineIn pckIn = new PacketLineIn(recvStream); assertThat(pckIn.readString(), is(tip.toObjectId().getName() + " refs/heads/master")); assertThat(pckIn.readString(), is(tip.toObjectId().getName() + " refs/heads/other")); - assertTrue(pckIn.readString() == PacketLineIn.END); + assertTrue(PacketLineIn.isEnd(pckIn.readString())); } @Test @@ -692,15 +694,15 @@ public void testV2LsRefsRefPrefixNoSlash() throws Exception { ByteArrayInputStream recvStream = uploadPackV2( "command=ls-refs\n", - PacketLineIn.DELIM, + PacketLineIn.delimiter(), "ref-prefix refs/heads/maste", "ref-prefix r", - PacketLineIn.END); + PacketLineIn.end()); PacketLineIn pckIn = new PacketLineIn(recvStream); assertThat(pckIn.readString(), is(tip.toObjectId().getName() + " refs/heads/master")); assertThat(pckIn.readString(), is(tip.toObjectId().getName() + " refs/heads/other")); - assertTrue(pckIn.readString() == PacketLineIn.END); + assertTrue(PacketLineIn.isEnd(pckIn.readString())); } @Test @@ -709,17 +711,17 @@ public void testV2LsRefsUnrecognizedArgument() throws Exception { thrown.expectMessage("unexpected invalid-argument"); uploadPackV2( "command=ls-refs\n", - PacketLineIn.DELIM, + PacketLineIn.delimiter(), "invalid-argument\n", - PacketLineIn.END); + 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 }; + PacketLineIn.delimiter(), + PacketLineIn.end() }; TestV2Hook testHook = new TestV2Hook(); uploadPackV2Setup(null, null, testHook, lines); @@ -763,9 +765,9 @@ public void testV2FetchRequestPolicyAdvertised() throws Exception { null, null, "command=fetch\n", - PacketLineIn.DELIM, + PacketLineIn.delimiter(), "want " + advertized.name() + "\n", - PacketLineIn.END); + PacketLineIn.end()); // This doesn't thrown.expect(TransportException.class); @@ -776,9 +778,9 @@ public void testV2FetchRequestPolicyAdvertised() throws Exception { null, null, "command=fetch\n", - PacketLineIn.DELIM, + PacketLineIn.delimiter(), "want " + unadvertized.name() + "\n", - PacketLineIn.END); + PacketLineIn.end()); } @Test @@ -794,9 +796,9 @@ public void testV2FetchRequestPolicyReachableCommit() throws Exception { null, null, "command=fetch\n", - PacketLineIn.DELIM, + PacketLineIn.delimiter(), "want " + reachable.name() + "\n", - PacketLineIn.END); + PacketLineIn.end()); // This doesn't thrown.expect(TransportException.class); @@ -807,9 +809,9 @@ public void testV2FetchRequestPolicyReachableCommit() throws Exception { null, null, "command=fetch\n", - PacketLineIn.DELIM, + PacketLineIn.delimiter(), "want " + unreachable.name() + "\n", - PacketLineIn.END); + PacketLineIn.end()); } @Test @@ -824,9 +826,9 @@ public void testV2FetchRequestPolicyTip() throws Exception { new RejectAllRefFilter(), null, "command=fetch\n", - PacketLineIn.DELIM, + PacketLineIn.delimiter(), "want " + tip.name() + "\n", - PacketLineIn.END); + PacketLineIn.end()); // This doesn't thrown.expect(TransportException.class); @@ -837,9 +839,9 @@ public void testV2FetchRequestPolicyTip() throws Exception { new RejectAllRefFilter(), null, "command=fetch\n", - PacketLineIn.DELIM, + PacketLineIn.delimiter(), "want " + parentOfTip.name() + "\n", - PacketLineIn.END); + PacketLineIn.end()); } @Test @@ -855,9 +857,9 @@ public void testV2FetchRequestPolicyReachableCommitTip() throws Exception { new RejectAllRefFilter(), null, "command=fetch\n", - PacketLineIn.DELIM, + PacketLineIn.delimiter(), "want " + parentOfTip.name() + "\n", - PacketLineIn.END); + PacketLineIn.end()); // This doesn't thrown.expect(TransportException.class); @@ -868,9 +870,9 @@ public void testV2FetchRequestPolicyReachableCommitTip() throws Exception { new RejectAllRefFilter(), null, "command=fetch\n", - PacketLineIn.DELIM, + PacketLineIn.delimiter(), "want " + unreachable.name() + "\n", - PacketLineIn.END); + PacketLineIn.end()); } @Test @@ -883,9 +885,9 @@ public void testV2FetchRequestPolicyAny() throws Exception { null, null, "command=fetch\n", - PacketLineIn.DELIM, + PacketLineIn.delimiter(), "want " + unreachable.name() + "\n", - PacketLineIn.END); + PacketLineIn.end()); } @Test @@ -899,16 +901,16 @@ public void testV2FetchServerDoesNotStopNegotiation() throws Exception { ByteArrayInputStream recvStream = uploadPackV2( "command=fetch\n", - PacketLineIn.DELIM, + PacketLineIn.delimiter(), "want " + fooChild.toObjectId().getName() + "\n", "want " + barChild.toObjectId().getName() + "\n", "have " + fooParent.toObjectId().getName() + "\n", - PacketLineIn.END); + PacketLineIn.end()); PacketLineIn pckIn = new PacketLineIn(recvStream); assertThat(pckIn.readString(), is("acknowledgments")); assertThat(pckIn.readString(), is("ACK " + fooParent.toObjectId().getName())); - assertThat(pckIn.readString(), theInstance(PacketLineIn.END)); + assertTrue(PacketLineIn.isEnd(pckIn.readString())); } @Test @@ -922,12 +924,12 @@ public void testV2FetchServerStopsNegotiation() throws Exception { ByteArrayInputStream recvStream = uploadPackV2( "command=fetch\n", - PacketLineIn.DELIM, + PacketLineIn.delimiter(), "want " + fooChild.toObjectId().getName() + "\n", "want " + barChild.toObjectId().getName() + "\n", "have " + fooParent.toObjectId().getName() + "\n", "have " + barParent.toObjectId().getName() + "\n", - PacketLineIn.END); + PacketLineIn.end()); PacketLineIn pckIn = new PacketLineIn(recvStream); assertThat(pckIn.readString(), is("acknowledgments")); @@ -937,7 +939,7 @@ public void testV2FetchServerStopsNegotiation() throws Exception { "ACK " + fooParent.toObjectId().getName(), "ACK " + barParent.toObjectId().getName())); assertThat(pckIn.readString(), is("ready")); - assertThat(pckIn.readString(), theInstance(PacketLineIn.DELIM)); + assertTrue(PacketLineIn.isDelimiter(pckIn.readString())); assertThat(pckIn.readString(), is("packfile")); parsePack(recvStream); assertFalse(client.getObjectDatabase().has(fooParent.toObjectId())); @@ -957,12 +959,12 @@ public void testV2FetchClientStopsNegotiation() throws Exception { ByteArrayInputStream recvStream = uploadPackV2( "command=fetch\n", - PacketLineIn.DELIM, + PacketLineIn.delimiter(), "want " + fooChild.toObjectId().getName() + "\n", "want " + barChild.toObjectId().getName() + "\n", "have " + fooParent.toObjectId().getName() + "\n", "done\n", - PacketLineIn.END); + PacketLineIn.end()); PacketLineIn pckIn = new PacketLineIn(recvStream); assertThat(pckIn.readString(), is("packfile")); @@ -986,12 +988,12 @@ public void testV2FetchThinPack() throws Exception { // Pretend that we have parent to get a thin pack based on it. ByteArrayInputStream recvStream = uploadPackV2( "command=fetch\n", - PacketLineIn.DELIM, + PacketLineIn.delimiter(), "want " + child.toObjectId().getName() + "\n", "have " + parent.toObjectId().getName() + "\n", "thin-pack\n", "done\n", - PacketLineIn.END); + PacketLineIn.end()); PacketLineIn pckIn = new PacketLineIn(recvStream); assertThat(pckIn.readString(), is("packfile")); @@ -1012,10 +1014,10 @@ public void testV2FetchNoProgress() throws Exception { StringWriter sw = new StringWriter(); ByteArrayInputStream recvStream = uploadPackV2( "command=fetch\n", - PacketLineIn.DELIM, + PacketLineIn.delimiter(), "want " + commit.toObjectId().getName() + "\n", "done\n", - PacketLineIn.END); + PacketLineIn.end()); PacketLineIn pckIn = new PacketLineIn(recvStream); assertThat(pckIn.readString(), is("packfile")); parsePack(recvStream, new TextProgressMonitor(sw)); @@ -1025,11 +1027,11 @@ public void testV2FetchNoProgress() throws Exception { sw = new StringWriter(); recvStream = uploadPackV2( "command=fetch\n", - PacketLineIn.DELIM, + PacketLineIn.delimiter(), "want " + commit.toObjectId().getName() + "\n", "no-progress\n", "done\n", - PacketLineIn.END); + PacketLineIn.end()); pckIn = new PacketLineIn(recvStream); assertThat(pckIn.readString(), is("packfile")); parsePack(recvStream, new TextProgressMonitor(sw)); @@ -1046,10 +1048,10 @@ public void testV2FetchIncludeTag() throws Exception { // Without include-tag. ByteArrayInputStream recvStream = uploadPackV2( "command=fetch\n", - PacketLineIn.DELIM, + PacketLineIn.delimiter(), "want " + commit.toObjectId().getName() + "\n", "done\n", - PacketLineIn.END); + PacketLineIn.end()); PacketLineIn pckIn = new PacketLineIn(recvStream); assertThat(pckIn.readString(), is("packfile")); parsePack(recvStream); @@ -1058,11 +1060,11 @@ public void testV2FetchIncludeTag() throws Exception { // With tag. recvStream = uploadPackV2( "command=fetch\n", - PacketLineIn.DELIM, + PacketLineIn.delimiter(), "want " + commit.toObjectId().getName() + "\n", "include-tag\n", "done\n", - PacketLineIn.END); + PacketLineIn.end()); pckIn = new PacketLineIn(recvStream); assertThat(pckIn.readString(), is("packfile")); parsePack(recvStream); @@ -1082,27 +1084,27 @@ public void testV2FetchOfsDelta() throws Exception { // Without ofs-delta. ByteArrayInputStream recvStream = uploadPackV2( "command=fetch\n", - PacketLineIn.DELIM, + PacketLineIn.delimiter(), "want " + child.toObjectId().getName() + "\n", "done\n", - PacketLineIn.END); + PacketLineIn.end()); PacketLineIn pckIn = new PacketLineIn(recvStream); assertThat(pckIn.readString(), is("packfile")); - ReceivedPackStatistics stats = parsePack(recvStream); - assertTrue(stats.getNumOfsDelta() == 0); + ReceivedPackStatistics receivedStats = parsePack(recvStream); + assertTrue(receivedStats.getNumOfsDelta() == 0); // With ofs-delta. recvStream = uploadPackV2( "command=fetch\n", - PacketLineIn.DELIM, + PacketLineIn.delimiter(), "want " + child.toObjectId().getName() + "\n", "ofs-delta\n", "done\n", - PacketLineIn.END); + PacketLineIn.end()); pckIn = new PacketLineIn(recvStream); assertThat(pckIn.readString(), is("packfile")); - stats = parsePack(recvStream); - assertTrue(stats.getNumOfsDelta() != 0); + receivedStats = parsePack(recvStream); + assertTrue(receivedStats.getNumOfsDelta() != 0); } @Test @@ -1116,11 +1118,11 @@ public void testV2FetchShallow() throws Exception { // commonParent, so it doesn't send it. ByteArrayInputStream recvStream = uploadPackV2( "command=fetch\n", - PacketLineIn.DELIM, + PacketLineIn.delimiter(), "want " + barChild.toObjectId().getName() + "\n", "have " + fooChild.toObjectId().getName() + "\n", "done\n", - PacketLineIn.END); + PacketLineIn.end()); PacketLineIn pckIn = new PacketLineIn(recvStream); assertThat(pckIn.readString(), is("packfile")); parsePack(recvStream); @@ -1131,12 +1133,12 @@ public void testV2FetchShallow() throws Exception { // commonParent, so it sends it. recvStream = uploadPackV2( "command=fetch\n", - PacketLineIn.DELIM, + PacketLineIn.delimiter(), "want " + barChild.toObjectId().getName() + "\n", "have " + fooChild.toObjectId().getName() + "\n", "shallow " + fooChild.toObjectId().getName() + "\n", "done\n", - PacketLineIn.END); + PacketLineIn.end()); pckIn = new PacketLineIn(recvStream); assertThat(pckIn.readString(), is("packfile")); parsePack(recvStream); @@ -1152,15 +1154,15 @@ public void testV2FetchDeepenAndDone() throws Exception { // "deepen 1" sends only the child. ByteArrayInputStream recvStream = uploadPackV2( "command=fetch\n", - PacketLineIn.DELIM, + PacketLineIn.delimiter(), "want " + child.toObjectId().getName() + "\n", "deepen 1\n", "done\n", - PacketLineIn.END); + PacketLineIn.end()); PacketLineIn pckIn = new PacketLineIn(recvStream); assertThat(pckIn.readString(), is("shallow-info")); assertThat(pckIn.readString(), is("shallow " + child.toObjectId().getName())); - assertThat(pckIn.readString(), theInstance(PacketLineIn.DELIM)); + assertTrue(PacketLineIn.isDelimiter(pckIn.readString())); assertThat(pckIn.readString(), is("packfile")); parsePack(recvStream); assertTrue(client.getObjectDatabase().has(child.toObjectId())); @@ -1169,10 +1171,10 @@ public void testV2FetchDeepenAndDone() throws Exception { // Without that, the parent is sent too. recvStream = uploadPackV2( "command=fetch\n", - PacketLineIn.DELIM, + PacketLineIn.delimiter(), "want " + child.toObjectId().getName() + "\n", "done\n", - PacketLineIn.END); + PacketLineIn.end()); pckIn = new PacketLineIn(recvStream); assertThat(pckIn.readString(), is("packfile")); parsePack(recvStream); @@ -1187,10 +1189,10 @@ public void testV2FetchDeepenWithoutDone() throws Exception { ByteArrayInputStream recvStream = uploadPackV2( "command=fetch\n", - PacketLineIn.DELIM, + PacketLineIn.delimiter(), "want " + child.toObjectId().getName() + "\n", "deepen 1\n", - PacketLineIn.END); + PacketLineIn.end()); PacketLineIn pckIn = new PacketLineIn(recvStream); // Verify that only the correct section is sent. "shallow-info" @@ -1198,7 +1200,7 @@ public void testV2FetchDeepenWithoutDone() throws Exception { // sent only if a packfile is sent. assertThat(pckIn.readString(), is("acknowledgments")); assertThat(pckIn.readString(), is("NAK")); - assertThat(pckIn.readString(), theInstance(PacketLineIn.END)); + assertTrue(PacketLineIn.isEnd(pckIn.readString())); } @Test @@ -1219,13 +1221,13 @@ public void testV2FetchShallowSince() throws Exception { // Report that we only have "boundary" as a shallow boundary. ByteArrayInputStream recvStream = uploadPackV2( "command=fetch\n", - PacketLineIn.DELIM, + PacketLineIn.delimiter(), "shallow " + boundary.toObjectId().getName() + "\n", "deepen-since 1510000\n", "want " + merge.toObjectId().getName() + "\n", "have " + boundary.toObjectId().getName() + "\n", "done\n", - PacketLineIn.END); + PacketLineIn.end()); PacketLineIn pckIn = new PacketLineIn(recvStream); assertThat(pckIn.readString(), is("shallow-info")); @@ -1237,7 +1239,7 @@ public void testV2FetchShallowSince() throws Exception { // later than the given deepen-since time. assertThat(pckIn.readString(), is("unshallow " + boundary.toObjectId().getName())); - assertThat(pckIn.readString(), theInstance(PacketLineIn.DELIM)); + assertTrue(PacketLineIn.isDelimiter(pckIn.readString())); assertThat(pckIn.readString(), is("packfile")); parsePack(recvStream); @@ -1270,12 +1272,12 @@ public void testV2FetchShallowSince_excludedParentWithMultipleChildren() throws ByteArrayInputStream recvStream = uploadPackV2( "command=fetch\n", - PacketLineIn.DELIM, + PacketLineIn.delimiter(), "deepen-since 1510000\n", "want " + child1.toObjectId().getName() + "\n", "want " + child2.toObjectId().getName() + "\n", "done\n", - PacketLineIn.END); + PacketLineIn.end()); PacketLineIn pckIn = new PacketLineIn(recvStream); assertThat(pckIn.readString(), is("shallow-info")); @@ -1286,7 +1288,7 @@ public void testV2FetchShallowSince_excludedParentWithMultipleChildren() throws "shallow " + child1.toObjectId().getName(), "shallow " + child2.toObjectId().getName())); - assertThat(pckIn.readString(), theInstance(PacketLineIn.DELIM)); + assertTrue(PacketLineIn.isDelimiter(pckIn.readString())); assertThat(pckIn.readString(), is("packfile")); parsePack(recvStream); @@ -1309,11 +1311,11 @@ public void testV2FetchShallowSince_noCommitsSelected() throws Exception { thrown.expectMessage("No commits selected for shallow request"); uploadPackV2( "command=fetch\n", - PacketLineIn.DELIM, + PacketLineIn.delimiter(), "deepen-since 1510000\n", "want " + tooOld.toObjectId().getName() + "\n", "done\n", - PacketLineIn.END); + PacketLineIn.end()); } @Test @@ -1332,13 +1334,13 @@ public void testV2FetchDeepenNot() throws Exception { // wants "merge" while excluding "side". ByteArrayInputStream recvStream = uploadPackV2( "command=fetch\n", - PacketLineIn.DELIM, + PacketLineIn.delimiter(), "shallow " + three.toObjectId().getName() + "\n", "deepen-not side\n", "want " + merge.toObjectId().getName() + "\n", "have " + three.toObjectId().getName() + "\n", "done\n", - PacketLineIn.END); + PacketLineIn.end()); PacketLineIn pckIn = new PacketLineIn(recvStream); assertThat(pckIn.readString(), is("shallow-info")); @@ -1353,7 +1355,7 @@ public void testV2FetchDeepenNot() throws Exception { // "three" is unshallow because its parent "two" is now available. assertThat(pckIn.readString(), is("unshallow " + three.toObjectId().getName())); - assertThat(pckIn.readString(), theInstance(PacketLineIn.DELIM)); + assertTrue(PacketLineIn.isDelimiter(pckIn.readString())); assertThat(pckIn.readString(), is("packfile")); parsePack(recvStream); @@ -1385,11 +1387,11 @@ public void testV2FetchDeepenNot_excludeDescendantOfWant() throws Exception { thrown.expectMessage("No commits selected for shallow request"); uploadPackV2( "command=fetch\n", - PacketLineIn.DELIM, + PacketLineIn.delimiter(), "deepen-not four\n", "want " + two.toObjectId().getName() + "\n", "done\n", - PacketLineIn.END); + PacketLineIn.end()); } @Test @@ -1405,15 +1407,15 @@ public void testV2FetchDeepenNot_supportAnnotatedTags() throws Exception { ByteArrayInputStream recvStream = uploadPackV2( "command=fetch\n", - PacketLineIn.DELIM, + PacketLineIn.delimiter(), "deepen-not twotag\n", "want " + four.toObjectId().getName() + "\n", "done\n", - PacketLineIn.END); + 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)); + assertTrue(PacketLineIn.isDelimiter(pckIn.readString())); assertThat(pckIn.readString(), is("packfile")); parsePack(recvStream); assertFalse(client.getObjectDatabase().has(one.toObjectId())); @@ -1439,12 +1441,12 @@ public void testV2FetchDeepenNot_excludedParentWithMultipleChildren() throws Exc ByteArrayInputStream recvStream = uploadPackV2( "command=fetch\n", - PacketLineIn.DELIM, + PacketLineIn.delimiter(), "deepen-not base\n", "want " + child1.toObjectId().getName() + "\n", "want " + child2.toObjectId().getName() + "\n", "done\n", - PacketLineIn.END); + PacketLineIn.end()); PacketLineIn pckIn = new PacketLineIn(recvStream); assertThat(pckIn.readString(), is("shallow-info")); @@ -1455,7 +1457,7 @@ public void testV2FetchDeepenNot_excludedParentWithMultipleChildren() throws Exc "shallow " + child1.toObjectId().getName(), "shallow " + child2.toObjectId().getName())); - assertThat(pckIn.readString(), theInstance(PacketLineIn.DELIM)); + assertTrue(PacketLineIn.isDelimiter(pckIn.readString())); assertThat(pckIn.readString(), is("packfile")); parsePack(recvStream); @@ -1471,16 +1473,16 @@ public void testV2FetchUnrecognizedArgument() throws Exception { thrown.expectMessage("unexpected invalid-argument"); uploadPackV2( "command=fetch\n", - PacketLineIn.DELIM, + PacketLineIn.delimiter(), "invalid-argument\n", - PacketLineIn.END); + PacketLineIn.end()); } @Test public void testV2FetchServerOptions() throws Exception { String[] lines = { "command=fetch\n", "server-option=one\n", - "server-option=two\n", PacketLineIn.DELIM, - PacketLineIn.END }; + "server-option=two\n", PacketLineIn.delimiter(), + PacketLineIn.end() }; TestV2Hook testHook = new TestV2Hook(); uploadPackV2Setup(null, null, testHook, lines); @@ -1504,11 +1506,11 @@ public void testV2FetchFilter() throws Exception { ByteArrayInputStream recvStream = uploadPackV2( "command=fetch\n", - PacketLineIn.DELIM, + PacketLineIn.delimiter(), "want " + commit.toObjectId().getName() + "\n", "filter blob:limit=5\n", "done\n", - PacketLineIn.END); + PacketLineIn.end()); PacketLineIn pckIn = new PacketLineIn(recvStream); assertThat(pckIn.readString(), is("packfile")); parsePack(recvStream); @@ -1557,15 +1559,15 @@ private void uploadV2WithTreeDepthFilter( long depth, ObjectId... wants) throws Exception { server.getConfig().setBoolean("uploadpack", null, "allowfilter", true); - List input = new ArrayList(); + List input = new ArrayList<>(); input.add("command=fetch\n"); - input.add(PacketLineIn.DELIM); + input.add(PacketLineIn.delimiter()); for (ObjectId want : wants) { input.add("want " + want.getName() + "\n"); } input.add("filter tree:" + depth + "\n"); input.add("done\n"); - input.add(PacketLineIn.END); + input.add(PacketLineIn.end()); ByteArrayInputStream recvStream = uploadPackV2(RequestPolicy.ANY, /*refFilter=*/null, /*hook=*/null, input.toArray(new String[0])); @@ -1846,11 +1848,11 @@ public void testV2FetchFilterWhenNotAllowed() throws Exception { thrown.expectMessage("unexpected filter blob:limit=5"); uploadPackV2( "command=fetch\n", - PacketLineIn.DELIM, + PacketLineIn.delimiter(), "want " + commit.toObjectId().getName() + "\n", "filter blob:limit=5\n", "done\n", - PacketLineIn.END); + PacketLineIn.end()); } @Test @@ -1861,10 +1863,10 @@ public void testV2FetchWantRefIfNotAllowed() throws Exception { try { uploadPackV2( "command=fetch\n", - PacketLineIn.DELIM, + PacketLineIn.delimiter(), "want-ref refs/heads/one\n", "done\n", - PacketLineIn.END); + PacketLineIn.end()); } catch (PackProtocolException e) { assertThat( e.getMessage(), @@ -1887,11 +1889,11 @@ public void testV2FetchWantRef() throws Exception { ByteArrayInputStream recvStream = uploadPackV2( "command=fetch\n", - PacketLineIn.DELIM, + PacketLineIn.delimiter(), "want-ref refs/heads/one\n", "want-ref refs/heads/two\n", "done\n", - PacketLineIn.END); + PacketLineIn.end()); PacketLineIn pckIn = new PacketLineIn(recvStream); assertThat(pckIn.readString(), is("wanted-refs")); assertThat( @@ -1899,7 +1901,7 @@ public void testV2FetchWantRef() throws Exception { hasItems( one.toObjectId().getName() + " refs/heads/one", two.toObjectId().getName() + " refs/heads/two")); - assertThat(pckIn.readString(), theInstance(PacketLineIn.DELIM)); + assertTrue(PacketLineIn.isDelimiter(pckIn.readString())); assertThat(pckIn.readString(), is("packfile")); parsePack(recvStream); @@ -1918,11 +1920,11 @@ public void testV2FetchBadWantRef() throws Exception { try { uploadPackV2( "command=fetch\n", - PacketLineIn.DELIM, + PacketLineIn.delimiter(), "want-ref refs/heads/one\n", "want-ref refs/heads/nonExistentRef\n", "done\n", - PacketLineIn.END); + PacketLineIn.end()); } catch (PackProtocolException e) { assertThat( e.getMessage(), @@ -1945,17 +1947,17 @@ public void testV2FetchMixedWantRef() throws Exception { ByteArrayInputStream recvStream = uploadPackV2( "command=fetch\n", - PacketLineIn.DELIM, + PacketLineIn.delimiter(), "want-ref refs/heads/one\n", "want " + two.toObjectId().getName() + "\n", "done\n", - PacketLineIn.END); + PacketLineIn.end()); PacketLineIn pckIn = new PacketLineIn(recvStream); assertThat(pckIn.readString(), is("wanted-refs")); assertThat( pckIn.readString(), is(one.toObjectId().getName() + " refs/heads/one")); - assertThat(pckIn.readString(), theInstance(PacketLineIn.DELIM)); + assertTrue(PacketLineIn.isDelimiter(pckIn.readString())); assertThat(pckIn.readString(), is("packfile")); parsePack(recvStream); @@ -1973,11 +1975,11 @@ public void testV2FetchWantRefWeAlreadyHave() throws Exception { ByteArrayInputStream recvStream = uploadPackV2( "command=fetch\n", - PacketLineIn.DELIM, + PacketLineIn.delimiter(), "want-ref refs/heads/one\n", "have " + one.toObjectId().getName(), "done\n", - PacketLineIn.END); + PacketLineIn.end()); PacketLineIn pckIn = new PacketLineIn(recvStream); // The client still needs to know the hash of the object that @@ -1987,7 +1989,7 @@ public void testV2FetchWantRefWeAlreadyHave() throws Exception { assertThat( pckIn.readString(), is(one.toObjectId().getName() + " refs/heads/one")); - assertThat(pckIn.readString(), theInstance(PacketLineIn.DELIM)); + assertTrue(PacketLineIn.isDelimiter(pckIn.readString())); // ... but the client does not need the object itself. assertThat(pckIn.readString(), is("packfile")); @@ -2005,20 +2007,20 @@ public void testV2FetchWantRefAndDeepen() throws Exception { ByteArrayInputStream recvStream = uploadPackV2( "command=fetch\n", - PacketLineIn.DELIM, + PacketLineIn.delimiter(), "want-ref refs/heads/branch1\n", "deepen 1\n", "done\n", - PacketLineIn.END); + PacketLineIn.end()); PacketLineIn pckIn = new PacketLineIn(recvStream); // shallow-info appears first, then wanted-refs. assertThat(pckIn.readString(), is("shallow-info")); assertThat(pckIn.readString(), is("shallow " + child.toObjectId().getName())); - assertThat(pckIn.readString(), theInstance(PacketLineIn.DELIM)); + assertTrue(PacketLineIn.isDelimiter(pckIn.readString())); assertThat(pckIn.readString(), is("wanted-refs")); assertThat(pckIn.readString(), is(child.toObjectId().getName() + " refs/heads/branch1")); - assertThat(pckIn.readString(), theInstance(PacketLineIn.DELIM)); + assertTrue(PacketLineIn.isDelimiter(pckIn.readString())); assertThat(pckIn.readString(), is("packfile")); parsePack(recvStream); assertTrue(client.getObjectDatabase().has(child.toObjectId())); @@ -2036,13 +2038,13 @@ public void testV2FetchMissingShallow() throws Exception { true); ByteArrayInputStream recvStream = uploadPackV2("command=fetch\n", - PacketLineIn.DELIM, + PacketLineIn.delimiter(), "want-ref refs/heads/three\n", "deepen 3", "shallow 0123012301230123012301230123012301230123", "shallow " + two.getName() + '\n', "done\n", - PacketLineIn.END); + PacketLineIn.end()); PacketLineIn pckIn = new PacketLineIn(recvStream); assertThat(pckIn.readString(), is("shallow-info")); @@ -2050,11 +2052,11 @@ public void testV2FetchMissingShallow() throws Exception { is("shallow " + one.toObjectId().getName())); assertThat(pckIn.readString(), is("unshallow " + two.toObjectId().getName())); - assertThat(pckIn.readString(), theInstance(PacketLineIn.DELIM)); + assertTrue(PacketLineIn.isDelimiter(pckIn.readString())); assertThat(pckIn.readString(), is("wanted-refs")); assertThat(pckIn.readString(), is(three.toObjectId().getName() + " refs/heads/three")); - assertThat(pckIn.readString(), theInstance(PacketLineIn.DELIM)); + assertTrue(PacketLineIn.isDelimiter(pckIn.readString())); assertThat(pckIn.readString(), is("packfile")); parsePack(recvStream); @@ -2071,7 +2073,7 @@ public void testGetPeerAgentProtocolV0() throws Exception { UploadPack up = new UploadPack(server); ByteArrayInputStream send = linesAsInputStream( "want " + one.getName() + " agent=JGit-test/1.2.3\n", - PacketLineIn.END, + PacketLineIn.end(), "have 11cedf1b796d44207da702f7d420684022fc0f09\n", "done\n"); ByteArrayOutputStream recv = new ByteArrayOutputStream(); @@ -2092,9 +2094,9 @@ public void testGetPeerAgentProtocolV2() throws Exception { ByteArrayInputStream send = linesAsInputStream( "command=fetch\n", "agent=JGit-test/1.2.4\n", - PacketLineIn.DELIM, "want " + one.getName() + "\n", + PacketLineIn.delimiter(), "want " + one.getName() + "\n", "have 11cedf1b796d44207da702f7d420684022fc0f09\n", "done\n", - PacketLineIn.END); + PacketLineIn.end()); ByteArrayOutputStream recv = new ByteArrayOutputStream(); up.upload(send, recv, null); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FileUtilsTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FileUtilsTest.java index c0f496566..9ee9613f7 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FileUtilsTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FileUtilsTest.java @@ -137,7 +137,6 @@ public void testDeleteRecursive() throws IOException { } @Test - public void testDeleteRecursiveEmpty() throws IOException { File f1 = new File(trash, "test/test/a"); File f2 = new File(trash, "test/a"); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/LRUMapTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/LRUMapTest.java new file mode 100644 index 000000000..da59533ae --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/LRUMapTest.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2018, Konrad Windszus + * 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.util; + +import java.util.LinkedHashMap; +import java.util.Map; + +import org.hamcrest.collection.IsIterableContainingInOrder; +import org.junit.Assert; +import org.junit.Test; + +public class LRUMapTest { + + @SuppressWarnings("boxing") + @Test + public void testLRUEntriesAreEvicted() { + Map map = new LRUMap<>(3, 3); + for (int i = 0; i < 3; i++) { + map.put(i, i); + } + // access the last ones + map.get(2); + map.get(0); + + // put another one which exceeds the limit (entry with key "1" is + // evicted) + map.put(3, 3); + + Map expectedMap = new LinkedHashMap<>(); + expectedMap.put(2, 2); + expectedMap.put(0, 0); + expectedMap.put(3, 3); + + Assert.assertThat(map.entrySet(), + IsIterableContainingInOrder + .contains(expectedMap.entrySet().toArray())); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/http/HttpCookiesMatcher.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/http/HttpCookiesMatcher.java new file mode 100644 index 000000000..8c5b127de --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/http/HttpCookiesMatcher.java @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2018, Konrad Windszus + * 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.util.http; + +import java.net.HttpCookie; +import java.util.LinkedList; +import java.util.List; + +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.hamcrest.TypeSafeMatcher; +import org.hamcrest.collection.IsIterableContainingInOrder; + +public final class HttpCookiesMatcher { + public static Matcher> containsInOrder( + Iterable expectedCookies) { + return containsInOrder(expectedCookies, 0); + } + + public static Matcher> containsInOrder( + Iterable expectedCookies, int allowedMaxAgeDelta) { + final List> cookieMatchers = new LinkedList<>(); + for (HttpCookie cookie : expectedCookies) { + cookieMatchers + .add(new HttpCookieMatcher(cookie, allowedMaxAgeDelta)); + } + return new IsIterableContainingInOrder<>(cookieMatchers); + } + + /** + * The default {@link HttpCookie#equals(Object)} is not good enough for + * testing purposes. Also the {@link HttpCookie#toString()} only emits some + * of the cookie attributes. For testing a dedicated matcher is needed which + * takes into account all attributes. + */ + private static final class HttpCookieMatcher + extends TypeSafeMatcher { + + private final HttpCookie cookie; + + private final int allowedMaxAgeDelta; + + public HttpCookieMatcher(HttpCookie cookie, int allowedMaxAgeDelta) { + this.cookie = cookie; + this.allowedMaxAgeDelta = allowedMaxAgeDelta; + } + + @Override + public void describeTo(Description description) { + describeCookie(description, cookie); + } + + @Override + protected void describeMismatchSafely(HttpCookie item, + Description mismatchDescription) { + mismatchDescription.appendText("was "); + describeCookie(mismatchDescription, item); + } + + @Override + protected boolean matchesSafely(HttpCookie otherCookie) { + // the equals method in HttpCookie is not specific enough, we want + // to consider all attributes! + return (equals(cookie.getName(), otherCookie.getName()) + && equals(cookie.getValue(), otherCookie.getValue()) + && equals(cookie.getDomain(), otherCookie.getDomain()) + && equals(cookie.getPath(), otherCookie.getPath()) + && (cookie.getMaxAge() >= otherCookie.getMaxAge() + - allowedMaxAgeDelta) + && (cookie.getMaxAge() <= otherCookie.getMaxAge() + + allowedMaxAgeDelta) + && cookie.isHttpOnly() == otherCookie.isHttpOnly() + && cookie.getSecure() == otherCookie.getSecure() + && cookie.getVersion() == otherCookie.getVersion()); + } + + private static boolean equals(String value1, String value2) { + if (value1 == null && value2 == null) { + return true; + } + if (value1 == null || value2 == null) { + return false; + } + return value1.equals(value2); + } + + @SuppressWarnings("boxing") + protected static void describeCookie(Description description, + HttpCookie cookie) { + description.appendText("HttpCookie["); + description.appendText("name: ").appendValue(cookie.getName()) + .appendText(", "); + description.appendText("value: ").appendValue(cookie.getValue()) + .appendText(", "); + description.appendText("domain: ").appendValue(cookie.getDomain()) + .appendText(", "); + description.appendText("path: ").appendValue(cookie.getPath()) + .appendText(", "); + description.appendText("maxAge: ").appendValue(cookie.getMaxAge()) + .appendText(", "); + description.appendText("httpOnly: ") + .appendValue(cookie.isHttpOnly()).appendText(", "); + description.appendText("secure: ").appendValue(cookie.getSecure()) + .appendText(", "); + description.appendText("version: ").appendValue(cookie.getVersion()) + .appendText(", "); + description.appendText("]"); + } + } +} \ No newline at end of file diff --git a/org.eclipse.jgit/.settings/.api_filters b/org.eclipse.jgit/.settings/.api_filters index 6719570e0..fc326d446 100644 --- a/org.eclipse.jgit/.settings/.api_filters +++ b/org.eclipse.jgit/.settings/.api_filters @@ -22,6 +22,14 @@ + + + + + + + + @@ -94,6 +102,26 @@ + + + + + + + + + + + + + + + + + + + + @@ -132,4 +160,24 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/org.eclipse.jgit/META-INF/MANIFEST.MF b/org.eclipse.jgit/META-INF/MANIFEST.MF index 95594f29e..893f0d430 100644 --- a/org.eclipse.jgit/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit/META-INF/MANIFEST.MF @@ -86,6 +86,7 @@ Export-Package: org.eclipse.jgit.annotations;version="5.4.0", org.eclipse.jgit.pgm", org.eclipse.jgit.internal.storage.reftree;version="5.4.0";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm", org.eclipse.jgit.internal.submodule;version="5.4.0";x-internal:=true, + org.eclipse.jgit.internal.transport.http;version="5.4.0";x-friends:="org.eclipse.jgit.test", org.eclipse.jgit.internal.transport.parser;version="5.4.0";x-friends:="org.eclipse.jgit.http.server,org.eclipse.jgit.test", org.eclipse.jgit.internal.transport.ssh;version="5.4.0";x-friends:="org.eclipse.jgit.ssh.apache", org.eclipse.jgit.lib;version="5.4.0"; @@ -160,16 +161,17 @@ Import-Package: com.googlecode.javaewah;version="[1.1.6,2.0.0)", com.jcraft.jsch;version="[0.1.37,0.2.0)", javax.crypto, javax.net.ssl, - org.bouncycastle;version="[1.60.0,2.0.0)", - org.bouncycastle.bcpg;version="[1.60.0,2.0.0)", - org.bouncycastle.gpg;version="[1.60.0,2.0.0)", - org.bouncycastle.gpg.keybox;version="[1.60.0,2.0.0)", - org.bouncycastle.jce.provider;version="[1.60.0,2.0.0)", - org.bouncycastle.openpgp;version="[1.60.0,2.0.0)", - org.bouncycastle.openpgp.jcajce;version="[1.60.0,2.0.0)", - org.bouncycastle.openpgp.operator;version="[1.60.0,2.0.0)", - org.bouncycastle.openpgp.operator.jcajce;version="[1.60.0,2.0.0)", - org.bouncycastle.util.encoders;version="[1.60.0,2.0.0)", + org.bouncycastle;version="[1.61.0,2.0.0)", + org.bouncycastle.bcpg;version="[1.61.0,2.0.0)", + org.bouncycastle.gpg;version="[1.61.0,2.0.0)", + org.bouncycastle.gpg.keybox;version="[1.61.0,2.0.0)", + org.bouncycastle.gpg.keybox.jcajce;version="[1.61.0,2.0.0)", + org.bouncycastle.jce.provider;version="[1.61.0,2.0.0)", + org.bouncycastle.openpgp;version="[1.61.0,2.0.0)", + org.bouncycastle.openpgp.jcajce;version="[1.61.0,2.0.0)", + org.bouncycastle.openpgp.operator;version="[1.61.0,2.0.0)", + org.bouncycastle.openpgp.operator.jcajce;version="[1.61.0,2.0.0)", + org.bouncycastle.util.encoders;version="[1.61.0,2.0.0)", org.slf4j;version="[1.7.0,2.0.0)", org.xml.sax, org.xml.sax.helpers 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 fc2a26f0d..df42dc757 100644 --- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties +++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties @@ -139,6 +139,7 @@ configSubsectionContainsNewline=config subsection name contains newline configSubsectionContainsNullByte=config subsection name contains byte 0x00 configValueContainsNullByte=config value contains byte 0x00 configHandleIsStale=config file handle is stale, {0}. retry +configHandleMayBeLocked=config file handle may be locked by other process, {0}. retry connectionFailed=connection failed connectionTimeOut=Connection time out: {0} contextMustBeNonNegative=context must be >= 0 @@ -208,6 +209,10 @@ couldNotDeleteTemporaryIndexFileShouldNotHappen=Could not delete temporary index couldNotGetAdvertisedRef=Remote {0} did not advertise Ref for branch {1}. This Ref may not exist in the remote or may be hidden by permission settings. couldNotGetRepoStatistics=Could not get repository statistics couldNotLockHEAD=Could not lock HEAD +couldNotFindTabInLine=Could not find tab in line {0}. Tab is the mandatory separator for the Netscape Cookie File Format. +couldNotFindSixTabsInLine=Could not find 6 tabs but only {0} in line '{1}'. 7 tab separated columns per line are mandatory for the Netscape Cookie File Format. +couldNotPersistCookies=Could not persist received cookies in file ''{0}'' +couldNotReadCookieFile=Could not read cookie file ''{0}'' couldNotReadIndexInOneGo=Could not read index in one go, only {0} out of {1} read couldNotReadObjectWhileParsingCommit=Could not read an object while parsing commit {0} couldNotRenameDeleteOldIndex=Could not rename delete old index @@ -455,6 +460,7 @@ mismatchOffset=mismatch offset for object {0} mismatchCRC=mismatch CRC for object {0} missingAccesskey=Missing accesskey. missingConfigurationForKey=No value for key {0} found in configuration +missingCookieFile=Configured http.cookieFile ''{0}'' is missing missingCRC=missing CRC for object {0} missingDeltaBase=delta base missingForwardImageInGITBinaryPatch=Missing forward-image in GIT binary patch @@ -625,6 +631,7 @@ rewinding=Rewinding to commit {0} s3ActionDeletion=Deletion s3ActionReading=Reading s3ActionWriting=Writing +searchForReachableBranches=Finding reachable branches searchForReuse=Finding sources searchForSizes=Getting sizes secondsAgo={0} seconds ago diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/DescribeCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/DescribeCommand.java index 9ebcf9fd0..9ad77e65f 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/DescribeCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/DescribeCommand.java @@ -63,8 +63,7 @@ import org.eclipse.jgit.errors.IncorrectObjectTypeException; import org.eclipse.jgit.errors.InvalidPatternException; import org.eclipse.jgit.errors.MissingObjectException; -import org.eclipse.jgit.ignore.internal.IMatcher; -import org.eclipse.jgit.ignore.internal.PathMatcher; +import org.eclipse.jgit.fnmatch.FileNameMatcher; import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.ObjectId; @@ -104,12 +103,17 @@ public class DescribeCommand extends GitCommand { /** * Pattern matchers to be applied to tags under consideration. */ - private List matchers = new ArrayList<>(); + private List matchers = new ArrayList<>(); /** * Whether to use all tags (incl. lightweight) or not. */ - private boolean useTags = false; + private boolean useTags; + + /** + * Whether to show a uniquely abbreviated commit hash as a fallback or not. + */ + private boolean always; /** * Constructor for DescribeCommand. @@ -197,6 +201,21 @@ public DescribeCommand setTags(boolean tags) { return this; } + /** + * Always describe the commit by eventually falling back to a uniquely + * abbreviated commit hash if no other name matches. + * + * @param always + * true enables falling back to a uniquely + * abbreviated commit hash + * @return {@code this} + * @since 5.4 + */ + public DescribeCommand setAlways(boolean always) { + this.always = always; + return this; + } + private String longDescription(Ref tag, int depth, ObjectId tip) throws IOException { return String.format( @@ -222,7 +241,7 @@ private String longDescription(Ref tag, int depth, ObjectId tip) */ public DescribeCommand setMatch(String... patterns) throws InvalidPatternException { for (String p : patterns) { - matchers.add(PathMatcher.createPathMatcher(p, null, false)); + matchers.add(new FileNameMatcher(p, null)); } return this; } @@ -255,9 +274,15 @@ private Optional getBestMatch(List tags) { // Find the first tag that matches in the stream of all tags // filtered by matchers ordered by tie break order Stream matchingTags = Stream.empty(); - for (IMatcher matcher : matchers) { + for (FileNameMatcher matcher : matchers) { Stream m = tags.stream().filter( - tag -> matcher.matches(tag.getName(), false, false)); + tag -> { + matcher.append( + tag.getName().substring(R_TAGS.length())); + boolean result = matcher.isMatch(); + matcher.reset(); + return result; + }); matchingTags = Stream.of(matchingTags, m).flatMap(i -> i); } return matchingTags.sorted(TAG_TIE_BREAKER).findFirst(); @@ -399,8 +424,9 @@ String describe(ObjectId tip) throws IOException { } // if all the nodes are dominated by all the tags, the walk stops - if (candidates.isEmpty()) - return null; + if (candidates.isEmpty()) { + return always ? w.getObjectReader().abbreviate(target).name() : null; + } Candidate best = Collections.min(candidates, (Candidate o1, Candidate o2) -> o1.depth - o2.depth); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/LogCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/LogCommand.java index 9b8016ce2..66de8ae13 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/LogCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/LogCommand.java @@ -133,7 +133,7 @@ protected LogCommand(Repository repo) { @Override public Iterable call() throws GitAPIException, NoHeadException { checkCallable(); - if (pathFilters.size() > 0) + if (!pathFilters.isEmpty()) walk.setTreeFilter(AndTreeFilter.create( PathFilterGroup.create(pathFilters), TreeFilter.ANY_DIFF)); if (skip > -1 && maxCount > -1) diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java index f0ad29db4..bdb2d1bbc 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java @@ -60,6 +60,7 @@ import org.eclipse.jgit.api.errors.RefNotAdvertisedException; import org.eclipse.jgit.api.errors.RefNotFoundException; import org.eclipse.jgit.api.errors.WrongRepositoryStateException; +import org.eclipse.jgit.dircache.DirCacheCheckout; import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.lib.AnyObjectId; import org.eclipse.jgit.lib.BranchConfig.BranchRebaseMode; @@ -67,12 +68,17 @@ import org.eclipse.jgit.lib.ConfigConstants; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.NullProgressMonitor; +import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ProgressMonitor; import org.eclipse.jgit.lib.Ref; +import org.eclipse.jgit.lib.RefUpdate; +import org.eclipse.jgit.lib.RefUpdate.Result; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.RepositoryState; import org.eclipse.jgit.lib.SubmoduleConfig.FetchRecurseSubmodulesMode; import org.eclipse.jgit.merge.MergeStrategy; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.revwalk.RevWalk; import org.eclipse.jgit.transport.FetchResult; import org.eclipse.jgit.transport.TagOpt; @@ -339,6 +345,45 @@ public PullResult call() throws GitAPIException, PullResult result; if (pullRebaseMode != BranchRebaseMode.NONE) { + try { + Ref head = repo.exactRef(Constants.HEAD); + if (head == null) { + throw new NoHeadException(JGitText + .get().commitOnRepoWithoutHEADCurrentlyNotSupported); + } + ObjectId headId = head.getObjectId(); + if (headId == null) { + // Pull on an unborn branch: checkout + try (RevWalk revWalk = new RevWalk(repo)) { + RevCommit srcCommit = revWalk + .parseCommit(commitToMerge); + DirCacheCheckout dco = new DirCacheCheckout(repo, + repo.lockDirCache(), srcCommit.getTree()); + dco.setFailOnConflict(true); + dco.setProgressMonitor(monitor); + dco.checkout(); + RefUpdate refUpdate = repo + .updateRef(head.getTarget().getName()); + refUpdate.setNewObjectId(commitToMerge); + refUpdate.setExpectedOldObjectId(null); + refUpdate.setRefLogMessage("initial pull", false); //$NON-NLS-1$ + if (refUpdate.update() != Result.NEW) { + throw new NoHeadException(JGitText + .get().commitOnRepoWithoutHEADCurrentlyNotSupported); + } + monitor.endTask(); + return new PullResult(fetchRes, remote, + RebaseResult.result( + RebaseResult.Status.FAST_FORWARD, + srcCommit)); + } + } + } catch (NoHeadException e) { + throw e; + } catch (IOException e) { + throw new JGitInternalException(JGitText + .get().exceptionCaughtDuringExecutionOfPullCommand, e); + } RebaseCommand rebase = new RebaseCommand(repo); RebaseResult rebaseRes = rebase.setUpstream(commitToMerge) .setUpstreamName(upstreamName).setProgressMonitor(monitor) 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 cdd1b80bb..593874c12 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java @@ -490,7 +490,7 @@ private RebaseResult processStep(RebaseTodoLine step, boolean shouldPick) resetSoftToParent(); List steps = repo.readRebaseTodo( rebaseState.getPath(GIT_REBASE_TODO), false); - RebaseTodoLine nextStep = steps.size() > 0 ? steps.get(0) : null; + RebaseTodoLine nextStep = steps.isEmpty() ? null : steps.get(0); File messageFixupFile = rebaseState.getFile(MESSAGE_FIXUP); File messageSquashFile = rebaseState.getFile(MESSAGE_SQUASH); if (isSquash && messageFixupFile.exists()) @@ -1083,7 +1083,7 @@ private void popSteps(int numSteps) throws IOException { repo.writeRebaseTodoFile(rebaseState.getPath(GIT_REBASE_TODO), todoLines, false); - if (poppedLines.size() > 0) { + if (!poppedLines.isEmpty()) { repo.writeRebaseTodoFile(rebaseState.getPath(DONE), poppedLines, true); } 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 ca0024d1c..bdaef5a36 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java @@ -200,6 +200,7 @@ public static JGitText get() { /***/ public String configSubsectionContainsNullByte; /***/ public String configValueContainsNullByte; /***/ public String configHandleIsStale; + /***/ public String configHandleMayBeLocked; /***/ public String connectionFailed; /***/ public String connectionTimeOut; /***/ public String contextMustBeNonNegative; @@ -267,9 +268,13 @@ public static JGitText get() { /***/ public String couldNotCheckOutBecauseOfConflicts; /***/ public String couldNotDeleteLockFileShouldNotHappen; /***/ public String couldNotDeleteTemporaryIndexFileShouldNotHappen; + /***/ public String couldNotFindTabInLine; + /***/ public String couldNotFindSixTabsInLine; /***/ public String couldNotGetAdvertisedRef; /***/ public String couldNotGetRepoStatistics; /***/ public String couldNotLockHEAD; + /***/ public String couldNotPersistCookies; + /***/ public String couldNotReadCookieFile; /***/ public String couldNotReadIndexInOneGo; /***/ public String couldNotReadObjectWhileParsingCommit; /***/ public String couldNotRenameDeleteOldIndex; @@ -516,6 +521,7 @@ public static JGitText get() { /***/ public String mismatchCRC; /***/ public String missingAccesskey; /***/ public String missingConfigurationForKey; + /***/ public String missingCookieFile; /***/ public String missingCRC; /***/ public String missingDeltaBase; /***/ public String missingForwardImageInGITBinaryPatch; @@ -686,6 +692,7 @@ public static JGitText get() { /***/ public String s3ActionDeletion; /***/ public String s3ActionReading; /***/ public String s3ActionWriting; + /***/ public String searchForReachableBranches; /***/ public String searchForReuse; /***/ public String searchForSizes; /***/ public String secondsAgo; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRepository.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRepository.java index 5169e929e..8e5c5a7f7 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRepository.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRepository.java @@ -124,6 +124,12 @@ public StoredConfig getConfig() { return config; } + /** {@inheritDoc} */ + @Override + public String getIdentifier() { + return getDescription().getRepositoryName(); + } + /** {@inheritDoc} */ @Override public void scanForRepoChanges() throws IOException { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java index d82d29e4c..90772970a 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java @@ -388,6 +388,17 @@ public RefDatabase getRefDatabase() { return refs; } + /** {@inheritDoc} */ + @Override + public String getIdentifier() { + File directory = getDirectory(); + if (directory != null) { + return directory.getPath(); + } else { + throw new IllegalStateException(); + } + } + /** {@inheritDoc} */ @Override public FileBasedConfig getConfig() { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java index 00124bcf2..4540860a0 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java @@ -244,7 +244,7 @@ public GC(FileRepository repo) { * If the configuration parameter "gc.pruneexpire" couldn't be * parsed */ - // TODO(ms): in 5.0 change signature and return Future> + // TODO(ms): change signature and return Future> @SuppressWarnings("FutureReturnValueIgnored") public Collection gc() throws IOException, ParseException { if (!background) { @@ -281,7 +281,7 @@ public Collection gc() throws IOException, ParseException { } return Collections.emptyList(); }; - // TODO(ms): in 5.0 change signature and return the Future + // TODO(ms): change signature and return the Future executor().submit(gcTask); return Collections.emptyList(); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/http/NetscapeCookieFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/http/NetscapeCookieFile.java new file mode 100644 index 000000000..075f55c73 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/http/NetscapeCookieFile.java @@ -0,0 +1,476 @@ +/* + * Copyright (C) 2018, Konrad Windszus + * 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.http; + +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.StringReader; +import java.io.Writer; +import java.net.HttpCookie; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.text.MessageFormat; +import java.util.Arrays; +import java.util.Collection; +import java.util.Date; +import java.util.LinkedHashSet; +import java.util.Set; + +import org.eclipse.jgit.annotations.NonNull; +import org.eclipse.jgit.annotations.Nullable; +import org.eclipse.jgit.internal.JGitText; +import org.eclipse.jgit.internal.storage.file.FileSnapshot; +import org.eclipse.jgit.internal.storage.file.LockFile; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.storage.file.FileBasedConfig; +import org.eclipse.jgit.util.FileUtils; +import org.eclipse.jgit.util.IO; +import org.eclipse.jgit.util.RawParseUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Wraps all cookies persisted in a Netscape Cookie File Format + * being referenced via the git config http.cookieFile. + * + * It will only load the cookies lazily, i.e. before calling + * {@link #getCookies(boolean)} the file is not evaluated. This class also + * allows persisting cookies in that file format. + *

+ * In general this class is not thread-safe. So any consumer needs to take care + * of synchronization! + * + * @see Netscape Cookie File + * Format + * @see Cookie + * format for wget + * @see libcurl + * Cookie file parsing + * @see libcurl + * Cookie file writing + * @see NetscapeCookieFileCache + */ +public final class NetscapeCookieFile { + + private static final String HTTP_ONLY_PREAMBLE = "#HttpOnly_"; //$NON-NLS-1$ + + private static final String COLUMN_SEPARATOR = "\t"; //$NON-NLS-1$ + + private static final String LINE_SEPARATOR = "\n"; //$NON-NLS-1$ + + /** + * Maximum number of retries to acquire the lock for writing to the + * underlying file. + */ + private static final int LOCK_ACQUIRE_MAX_RETRY_COUNT = 4; + + /** + * Sleep time in milliseconds between retries to acquire the lock for + * writing to the underlying file. + */ + private static final int LOCK_ACQUIRE_RETRY_SLEEP = 500; + + private final Path path; + + private FileSnapshot snapshot; + + private byte[] hash; + + final Date creationDate; + + private Set cookies = null; + + private static final Logger LOG = LoggerFactory + .getLogger(NetscapeCookieFile.class); + + /** + * @param path + */ + public NetscapeCookieFile(Path path) { + this(path, new Date()); + } + + NetscapeCookieFile(Path path, Date creationDate) { + this.path = path; + this.snapshot = FileSnapshot.DIRTY; + this.creationDate = creationDate; + } + + /** + * @return the path to the underlying cookie file + */ + public Path getPath() { + return path; + } + + /** + * @param refresh + * if {@code true} updates the list from the underlying cookie + * file if it has been modified since the last read otherwise + * returns the current transient state. In case the cookie file + * has never been read before will always read from the + * underlying file disregarding the value of this parameter. + * @return all cookies (may contain session cookies as well). This does not + * return a copy of the list but rather the original one. Every + * addition to the returned list can afterwards be persisted via + * {@link #write(URL)}. Errors in the underlying file will not lead + * to exceptions but rather to an empty set being returned and the + * underlying error being logged. + */ + public Set getCookies(boolean refresh) { + if (cookies == null || refresh) { + try { + byte[] in = getFileContentIfModified(); + Set newCookies = parseCookieFile(in, creationDate); + if (cookies != null) { + cookies = mergeCookies(newCookies, cookies); + } else { + cookies = newCookies; + } + return cookies; + } catch (IOException | IllegalArgumentException e) { + LOG.warn( + MessageFormat.format( + JGitText.get().couldNotReadCookieFile, path), + e); + if (cookies == null) { + cookies = new LinkedHashSet<>(); + } + } + } + return cookies; + + } + + /** + * Parses the given file and extracts all cookie information from it. + * + * @param input + * the file content to parse + * @param creationDate + * the date for the creation of the cookies (used to calculate + * the maxAge based on the expiration date given within the file) + * @return the set of parsed cookies from the given file (even expired + * ones). If there is more than one cookie with the same name in + * this file the last one overwrites the first one! + * @throws IOException + * if the given file could not be read for some reason + * @throws IllegalArgumentException + * if the given file does not have a proper format. + */ + private static Set parseCookieFile(@NonNull byte[] input, + @NonNull Date creationDate) + throws IOException, IllegalArgumentException { + + String decoded = RawParseUtils.decode(StandardCharsets.US_ASCII, input); + + Set cookies = new LinkedHashSet<>(); + try (BufferedReader reader = new BufferedReader( + new StringReader(decoded))) { + String line; + while ((line = reader.readLine()) != null) { + HttpCookie cookie = parseLine(line, creationDate); + if (cookie != null) { + cookies.add(cookie); + } + } + } + return cookies; + } + + private static HttpCookie parseLine(@NonNull String line, + @NonNull Date creationDate) { + if (line.isEmpty() || (line.startsWith("#") //$NON-NLS-1$ + && !line.startsWith(HTTP_ONLY_PREAMBLE))) { + return null; + } + String[] cookieLineParts = line.split(COLUMN_SEPARATOR, 7); + if (cookieLineParts == null) { + throw new IllegalArgumentException(MessageFormat + .format(JGitText.get().couldNotFindTabInLine, line)); + } + if (cookieLineParts.length < 7) { + throw new IllegalArgumentException(MessageFormat.format( + JGitText.get().couldNotFindSixTabsInLine, + Integer.valueOf(cookieLineParts.length), line)); + } + String name = cookieLineParts[5]; + String value = cookieLineParts[6]; + HttpCookie cookie = new HttpCookie(name, value); + + String domain = cookieLineParts[0]; + if (domain.startsWith(HTTP_ONLY_PREAMBLE)) { + cookie.setHttpOnly(true); + domain = domain.substring(HTTP_ONLY_PREAMBLE.length()); + } + // strip off leading "." + // (https://tools.ietf.org/html/rfc6265#section-5.2.3) + if (domain.startsWith(".")) { //$NON-NLS-1$ + domain = domain.substring(1); + } + cookie.setDomain(domain); + // domain evaluation as boolean flag not considered (i.e. always assumed + // to be true) + cookie.setPath(cookieLineParts[2]); + cookie.setSecure(Boolean.parseBoolean(cookieLineParts[3])); + + long expires = Long.parseLong(cookieLineParts[4]); + long maxAge = (expires - creationDate.getTime()) / 1000; + if (maxAge <= 0) { + return null; // skip expired cookies + } + cookie.setMaxAge(maxAge); + return cookie; + } + + /** + * Writes all the cookies being maintained in the set being returned by + * {@link #getCookies(boolean)} to the underlying file. + * + * Session-cookies will not be persisted. + * + * @param url + * url for which to write the cookies (important to derive + * default values for non-explicitly set attributes) + * @throws IOException + * @throws IllegalArgumentException + * @throws InterruptedException + */ + public void write(URL url) + throws IllegalArgumentException, IOException, InterruptedException { + try { + byte[] cookieFileContent = getFileContentIfModified(); + if (cookieFileContent != null) { + LOG.debug( + "Reading the underlying cookie file '{}' as it has been modified since the last access", //$NON-NLS-1$ + path); + // reread new changes if necessary + Set cookiesFromFile = NetscapeCookieFile + .parseCookieFile(cookieFileContent, creationDate); + this.cookies = mergeCookies(cookiesFromFile, cookies); + } + } catch (FileNotFoundException e) { + // ignore if file previously did not exist yet! + } + + ByteArrayOutputStream output = new ByteArrayOutputStream(); + try (Writer writer = new OutputStreamWriter(output, + StandardCharsets.US_ASCII)) { + write(writer, cookies, url, creationDate); + } + LockFile lockFile = new LockFile(path.toFile()); + for (int retryCount = 0; retryCount < LOCK_ACQUIRE_MAX_RETRY_COUNT; retryCount++) { + if (lockFile.lock()) { + try { + lockFile.setNeedSnapshot(true); + lockFile.write(output.toByteArray()); + if (!lockFile.commit()) { + throw new IOException(MessageFormat.format( + JGitText.get().cannotCommitWriteTo, path)); + } + } finally { + lockFile.unlock(); + } + return; + } + Thread.sleep(LOCK_ACQUIRE_RETRY_SLEEP); + } + throw new IOException( + MessageFormat.format(JGitText.get().cannotLock, lockFile)); + + } + + /** + * Read the underying file and return its content but only in case it has + * been modified since the last access. Internally calculates the hash and + * maintains {@link FileSnapshot}s to prevent issues described as "Racy + * Git problem". Inspired by {@link FileBasedConfig#load()}. + * + * @return the file contents in case the file has been modified since the + * last access, otherwise {@code null} + * @throws IOException + */ + private byte[] getFileContentIfModified() throws IOException { + final int maxStaleRetries = 5; + int retries = 0; + File file = getPath().toFile(); + if (!file.exists()) { + LOG.warn(MessageFormat.format(JGitText.get().missingCookieFile, + file.getAbsolutePath())); + return new byte[0]; + } + while (true) { + final FileSnapshot oldSnapshot = snapshot; + final FileSnapshot newSnapshot = FileSnapshot.save(file); + try { + final byte[] in = IO.readFully(file); + byte[] newHash = hash(in); + if (Arrays.equals(hash, newHash)) { + if (oldSnapshot.equals(newSnapshot)) { + oldSnapshot.setClean(newSnapshot); + } else { + snapshot = newSnapshot; + } + } else { + snapshot = newSnapshot; + hash = newHash; + } + return in; + } catch (FileNotFoundException e) { + throw e; + } catch (IOException e) { + if (FileUtils.isStaleFileHandle(e) + && retries < maxStaleRetries) { + if (LOG.isDebugEnabled()) { + LOG.debug(MessageFormat.format( + JGitText.get().configHandleIsStale, + Integer.valueOf(retries)), e); + } + retries++; + continue; + } + throw new IOException(MessageFormat + .format(JGitText.get().cannotReadFile, getPath()), e); + } + } + + } + + private byte[] hash(final byte[] in) { + return Constants.newMessageDigest().digest(in); + } + + /** + * Writes the given cookies to the file in the Netscape Cookie File Format + * (also used by curl) + * + * @param writer + * the writer to use to persist the cookies. + * @param cookies + * the cookies to write into the file + * @param url + * the url for which to write the cookie (to derive the default + * values for certain cookie attributes) + * @param creationDate + * the date when the cookie has been created. Important for + * calculation the cookie expiration time (calculated from + * cookie's maxAge and this creation time). + * @throws IOException + */ + static void write(@NonNull Writer writer, + @NonNull Collection cookies, @NonNull URL url, + @NonNull Date creationDate) throws IOException { + for (HttpCookie cookie : cookies) { + writeCookie(writer, cookie, url, creationDate); + } + } + + private static void writeCookie(@NonNull Writer writer, + @NonNull HttpCookie cookie, @NonNull URL url, + @NonNull Date creationDate) throws IOException { + if (cookie.getMaxAge() <= 0) { + return; // skip expired cookies + } + String domain = ""; //$NON-NLS-1$ + if (cookie.isHttpOnly()) { + domain = HTTP_ONLY_PREAMBLE; + } + if (cookie.getDomain() != null) { + domain += cookie.getDomain(); + } else { + domain += url.getHost(); + } + writer.write(domain); + writer.write(COLUMN_SEPARATOR); + writer.write("TRUE"); //$NON-NLS-1$ + writer.write(COLUMN_SEPARATOR); + String path = cookie.getPath(); + if (path == null) { + path = url.getPath(); + } + writer.write(path); + writer.write(COLUMN_SEPARATOR); + writer.write(Boolean.toString(cookie.getSecure()).toUpperCase()); + writer.write(COLUMN_SEPARATOR); + final String expirationDate; + // whenCreated field is not accessible in HttpCookie + expirationDate = String + .valueOf(creationDate.getTime() + (cookie.getMaxAge() * 1000)); + writer.write(expirationDate); + writer.write(COLUMN_SEPARATOR); + writer.write(cookie.getName()); + writer.write(COLUMN_SEPARATOR); + writer.write(cookie.getValue()); + writer.write(LINE_SEPARATOR); + } + + /** + * Merge the given sets in the following way. All cookies from + * {@code cookies1} and {@code cookies2} are contained in the resulting set + * which have unique names. If there is a duplicate entry for one name only + * the entry from set {@code cookies1} ends up in the resulting set. + * + * @param cookies1 + * @param cookies2 + * + * @return the merged cookies + */ + static Set mergeCookies(Set cookies1, + @Nullable Set cookies2) { + Set mergedCookies = new LinkedHashSet<>(cookies1); + if (cookies2 != null) { + mergedCookies.addAll(cookies2); + } + return mergedCookies; + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/http/NetscapeCookieFileCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/http/NetscapeCookieFileCache.java new file mode 100644 index 000000000..882b2d055 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/http/NetscapeCookieFileCache.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2018, Konrad Windszus + * 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.http; + +import java.nio.file.Path; + +import org.eclipse.jgit.transport.HttpConfig; +import org.eclipse.jgit.util.LRUMap; + +/** + * A cache of all known cookie files ({@link NetscapeCookieFile}). May contain + * at most {@code n} entries, where the least-recently used one is evicted as + * soon as more entries are added. The maximum number of entries (={@code n}) + * can be set via the git config key {@code http.cookieFileCacheLimit}. By + * default it is set to 10. + *

+ * The cache is global, i.e. it is shared among all consumers within the same + * Java process. + * + * @see NetscapeCookieFile + * + */ +public class NetscapeCookieFileCache { + + private final LRUMap cookieFileMap; + + private static NetscapeCookieFileCache instance; + + private NetscapeCookieFileCache(HttpConfig config) { + cookieFileMap = new LRUMap<>(config.getCookieFileCacheLimit(), + config.getCookieFileCacheLimit()); + } + + /** + * @param config + * the config which defines the limit for this cache + * @return the singleton instance of the cookie file cache. If the cache has + * already been created the given config is ignored (even if it + * differs from the config, with which the cache has originally been + * created) + */ + public static NetscapeCookieFileCache getInstance(HttpConfig config) { + if (instance == null) { + return new NetscapeCookieFileCache(config); + } else { + return instance; + } + } + + /** + * @param path + * the path of the cookie file to retrieve + * @return the cache entry belonging to the requested file + */ + public NetscapeCookieFile getEntry(Path path) { + if (!cookieFileMap.containsKey(path)) { + synchronized (NetscapeCookieFileCache.class) { + if (!cookieFileMap.containsKey(path)) { + cookieFileMap.put(path, new NetscapeCookieFile(path)); + } + } + } + return cookieFileMap.get(path); + } + +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/AbbreviatedObjectId.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/AbbreviatedObjectId.java index d105d0d20..b0339c677 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/AbbreviatedObjectId.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/AbbreviatedObjectId.java @@ -162,7 +162,7 @@ private static final int hexUInt32(final byte[] bs, int p, final int end) { r |= RawParseUtils.parseHexInt4(bs[p++]); n++; } - return r << (8 - n) * 4; + return r << ((8 - n) * 4); } static int mask(int nibbles, int word, int v) { 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 4726975d0..1032fd0df 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java @@ -107,7 +107,7 @@ public class Config { * must ensure it is a special copy of the empty string. It also must * be treated like the empty string. */ - static final String MAGIC_EMPTY_VALUE = new String(); + private static final String MISSING_ENTRY = new String(); /** * Create a configuration with no default fallback. @@ -128,6 +128,17 @@ public Config(Config defaultConfig) { state = new AtomicReference<>(newState()); } + /** + * Check if a given string is the "missing" value. + * + * @param value + * @return true if the given string is the "missing" value. + * @since 5.4 + */ + public static boolean isMissing(String value) { + return value == MISSING_ENTRY; + } + /** * Globally sets a {@link org.eclipse.jgit.lib.TypedConfigGetter} that is * subsequently used to read typed values from all git configs. @@ -1041,7 +1052,7 @@ public String toText() { if (e.prefix == null || "".equals(e.prefix)) //$NON-NLS-1$ out.append('\t'); out.append(e.name); - if (MAGIC_EMPTY_VALUE != e.value) { + if (MISSING_ENTRY != e.value) { out.append(" ="); //$NON-NLS-1$ if (e.value != null) { out.append(' '); @@ -1132,7 +1143,7 @@ private List fromTextRecurse(String text, int depth, e.name = readKeyName(in); if (e.name.endsWith("\n")) { //$NON-NLS-1$ e.name = e.name.substring(0, e.name.length() - 1); - e.value = MAGIC_EMPTY_VALUE; + e.value = MISSING_ENTRY; } else e.value = readValue(in); @@ -1165,7 +1176,7 @@ protected byte[] readIncludedConfig(String relPath) private void addIncludedConfig(final List newEntries, ConfigLine line, int depth) throws ConfigInvalidException { if (!line.name.equalsIgnoreCase("path") || //$NON-NLS-1$ - line.value == null || line.value.equals(MAGIC_EMPTY_VALUE)) { + line.value == null || line.value.equals(MISSING_ENTRY)) { throw new ConfigInvalidException(MessageFormat.format( JGitText.get().invalidLineInConfigFileWithParam, line)); } @@ -1413,11 +1424,23 @@ private static String readValue(StringReader in) case '"': value.append('"'); continue; - default: - throw new ConfigInvalidException(MessageFormat.format( - JGitText.get().badEscape, - Character.valueOf(((char) c)))); + case '\r': { + int next = in.read(); + if (next == '\n') { + continue; // CR-LF + } else if (next >= 0) { + in.reset(); + } + break; } + default: + break; + } + throw new ConfigInvalidException( + MessageFormat.format(JGitText.get().badEscape, + Character.isAlphabetic(c) + ? Character.valueOf(((char) c)) + : toUnicodeLiteral(c))); } if ('"' == c) { @@ -1430,6 +1453,11 @@ private static String readValue(StringReader in) return value.length() > 0 ? value.toString() : null; } + private static String toUnicodeLiteral(int c) { + return String.format("\\u%04x", //$NON-NLS-1$ + Integer.valueOf(c)); + } + /** * Parses a section of the configuration into an application model object. *

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 6a66cf682..4c70d20d6 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/DefaultTypedConfigGetter.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/DefaultTypedConfigGetter.java @@ -72,7 +72,7 @@ public boolean getBoolean(Config config, String section, String subsection, if (n == null) { return defaultValue; } - if (Config.MAGIC_EMPTY_VALUE == n) { + if (Config.isMissing(n)) { return true; } try { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RebaseTodoFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RebaseTodoFile.java index 06b4b227c..c0fcd4161 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RebaseTodoFile.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RebaseTodoFile.java @@ -179,7 +179,7 @@ private static RebaseTodoLine parseLine(byte[] buf, int tokenBegin, int nextSpace = RawParseUtils.next(buf, tokenBegin, ' '); int tokenCount = 0; - while (tokenCount < 3 && nextSpace < lineEnd) { + while (tokenCount < 3 && nextSpace <= lineEnd) { switch (tokenCount) { case 0: String actionToken = new String(buf, tokenBegin, @@ -191,8 +191,14 @@ private static RebaseTodoLine parseLine(byte[] buf, int tokenBegin, break; case 1: nextSpace = RawParseUtils.next(buf, tokenBegin, ' '); - String commitToken = new String(buf, tokenBegin, - nextSpace - tokenBegin - 1, UTF_8); + String commitToken; + if (nextSpace > lineEnd + 1) { + commitToken = new String(buf, tokenBegin, + lineEnd - tokenBegin + 1, UTF_8); + } else { + commitToken = new String(buf, tokenBegin, + nextSpace - tokenBegin - 1, UTF_8); + } tokenBegin = nextSpace; commit = AbbreviatedObjectId.fromString(commitToken); break; 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 aac63e9d2..d53b0c926 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java @@ -239,6 +239,15 @@ public File getDirectory() { return gitDir; } + /** + * Get repository identifier. + * + * @return repository identifier. The returned identifier has to be unique + * within a given Git server. + * @since 5.4 + */ + public abstract String getIdentifier(); + /** * Get the object database which stores this repository's data. * diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgKeyLocator.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgKeyLocator.java index df9615fc9..0d4431765 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgKeyLocator.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgKeyLocator.java @@ -54,6 +54,8 @@ import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.nio.file.Paths; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; import java.text.MessageFormat; import java.util.Iterator; import java.util.Locale; @@ -67,6 +69,7 @@ import org.bouncycastle.gpg.keybox.KeyInformation; import org.bouncycastle.gpg.keybox.PublicKeyRingBlob; import org.bouncycastle.gpg.keybox.UserID; +import org.bouncycastle.gpg.keybox.jcajce.JcaKeyBoxBuilder; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPPublicKey; import org.bouncycastle.openpgp.PGPSecretKey; @@ -210,9 +213,12 @@ private PGPPublicKey findPublicKeyByUserId(KeyBlob keyBlob) * @return publicKey the public key (maybe null) * @throws IOException * in case of problems reading the file + * @throws NoSuchAlgorithmException + * @throws NoSuchProviderException */ private PGPPublicKey findPublicKeyInKeyBox(Path keyboxFile) - throws IOException { + throws IOException, NoSuchAlgorithmException, + NoSuchProviderException { KeyBox keyBox = readKeyBoxFile(keyboxFile); for (KeyBlob keyBlob : keyBox.getKeyBlobs()) { if (keyBlob.getType() == BlobType.OPEN_PGP_BLOB) { @@ -236,15 +242,17 @@ private PGPPublicKey findPublicKeyInKeyBox(Path keyboxFile) * @return the secret key * @throws IOException * in case of issues reading key files + * @throws NoSuchAlgorithmException + * @throws NoSuchProviderException * @throws PGPException * in case of issues finding a key * @throws CanceledException * @throws URISyntaxException * @throws UnsupportedCredentialItem */ - public BouncyCastleGpgKey findSecretKey() - throws IOException, PGPException, CanceledException, - UnsupportedCredentialItem, URISyntaxException { + public BouncyCastleGpgKey findSecretKey() throws IOException, + NoSuchAlgorithmException, NoSuchProviderException, PGPException, + CanceledException, UnsupportedCredentialItem, URISyntaxException { if (exists(USER_KEYBOX_PATH)) { PGPPublicKey publicKey = // findPublicKeyInKeyBox(USER_KEYBOX_PATH); @@ -376,14 +384,12 @@ private PGPPublicKey getFirstPublicKey(KeyBlob keyBlob) throws IOException { .getPublicKey(); } - private KeyBox readKeyBoxFile(Path keyboxFile) throws IOException { + private KeyBox readKeyBoxFile(Path keyboxFile) throws IOException, + NoSuchAlgorithmException, NoSuchProviderException { KeyBox keyBox; try (InputStream in = new BufferedInputStream( newInputStream(keyboxFile))) { - // note: KeyBox constructor reads in the whole InputStream at once - // this code will change in 1.61 to - // either 'new BcKeyBox(in)' or 'new JcaKeyBoxBuilder().build(in)' - keyBox = new KeyBox(in, new JcaKeyFingerprintCalculator()); + keyBox = new JcaKeyBoxBuilder().build(in); } return keyBox; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgSigner.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgSigner.java index 4d696dd9e..cfe0931b4 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgSigner.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgSigner.java @@ -45,6 +45,8 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.net.URISyntaxException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; import java.security.Security; import org.bouncycastle.bcpg.ArmoredOutputStream; @@ -100,7 +102,8 @@ public boolean canLocateSigningKey(@Nullable String gpgSigningKey, BouncyCastleGpgKey gpgKey = locateSigningKey(gpgSigningKey, committer, passphrasePrompt); return gpgKey != null; - } catch (PGPException | IOException | URISyntaxException e) { + } catch (PGPException | IOException | NoSuchAlgorithmException + | NoSuchProviderException | URISyntaxException e) { return false; } } @@ -109,7 +112,8 @@ private BouncyCastleGpgKey locateSigningKey(@Nullable String gpgSigningKey, PersonIdent committer, BouncyCastleGpgKeyPassphrasePrompt passphrasePrompt) throws CanceledException, UnsupportedCredentialItem, IOException, - PGPException, URISyntaxException { + NoSuchAlgorithmException, NoSuchProviderException, PGPException, + URISyntaxException { if (gpgSigningKey == null || gpgSigningKey.isEmpty()) { gpgSigningKey = committer.getEmailAddress(); } @@ -153,7 +157,8 @@ public void sign(@NonNull CommitBuilder commit, signatureGenerator.generate().encode(out); } commit.setGpgSignature(new GpgSignature(buffer.toByteArray())); - } catch (PGPException | IOException | URISyntaxException e) { + } catch (PGPException | IOException | NoSuchAlgorithmException + | NoSuchProviderException | URISyntaxException e) { throw new JGitInternalException(e.getMessage(), e); } } 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 b69327a9b..0c3d3fec2 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java @@ -1190,7 +1190,7 @@ public Map getFailingPaths() { * otherwise */ public boolean failed() { - return failingPaths.size() > 0; + return !failingPaths.isEmpty(); } /** diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BitmapCalculator.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BitmapCalculator.java index e1d5d4ada..14e95670a 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BitmapCalculator.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BitmapCalculator.java @@ -1,3 +1,45 @@ +/* + * Copyright (C) 2019, 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.revwalk; import static java.util.Objects.requireNonNull; @@ -52,8 +94,9 @@ class BitmapCalculator { * @throws MissingObjectException * the supplied id doesn't exist * @throws IncorrectObjectTypeException - * the supplied id doens't refer to a commit or a tag + * the supplied id doesn't refer to a commit or a tag * @throws IOException + * if the walk cannot open a packfile or loose object */ BitmapBuilder getBitmap(RevCommit start, ProgressMonitor pm) throws MissingObjectException, diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BitmappedReachabilityChecker.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BitmappedReachabilityChecker.java index ab453433d..6e510f677 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BitmappedReachabilityChecker.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BitmappedReachabilityChecker.java @@ -55,10 +55,8 @@ /** * Checks the reachability using bitmaps. - * - * @since 5.4 */ -public class BitmappedReachabilityChecker implements ReachabilityChecker { +class BitmappedReachabilityChecker implements ReachabilityChecker { private final RevWalk walk; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/PedestrianReachabilityChecker.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/PedestrianReachabilityChecker.java index 4012a45d3..bba3c5cff 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/PedestrianReachabilityChecker.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/PedestrianReachabilityChecker.java @@ -52,10 +52,8 @@ /** * Checks the reachability walking the graph from the starters towards the * target. - * - * @since 5.4 */ -public class PedestrianReachabilityChecker implements ReachabilityChecker { +class PedestrianReachabilityChecker implements ReachabilityChecker { private final boolean topoSort; 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 f12eb2ff8..80fc81073 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java @@ -249,6 +249,23 @@ public ObjectReader getObjectReader() { return reader; } + /** + * Get a reachability checker for commits over this revwalk. + * + * @return the most efficient reachability checker for this repository. + * @throws IOException + * if it cannot open any of the underlying indices. + * + * @since 5.4 + */ + public ReachabilityChecker createReachabilityChecker() throws IOException { + if (reader.getBitmapIndex() != null) { + return new BitmappedReachabilityChecker(this); + } + + return new PedestrianReachabilityChecker(true, this); + } + /** * {@inheritDoc} *

diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalkUtils.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalkUtils.java index fabf7075d..2b721b887 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalkUtils.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalkUtils.java @@ -50,6 +50,9 @@ import org.eclipse.jgit.errors.IncorrectObjectTypeException; import org.eclipse.jgit.errors.MissingObjectException; +import org.eclipse.jgit.internal.JGitText; +import org.eclipse.jgit.lib.NullProgressMonitor; +import org.eclipse.jgit.lib.ProgressMonitor; import org.eclipse.jgit.lib.Ref; /** @@ -153,15 +156,51 @@ public static List findBranchesReachableFrom(RevCommit commit, RevWalk revWalk, Collection refs) throws MissingObjectException, IncorrectObjectTypeException, IOException { + return findBranchesReachableFrom(commit, revWalk, refs, + NullProgressMonitor.INSTANCE); + } + + /** + * Find the list of branches a given commit is reachable from when following + * parents. + *

+ * Note that this method calls + * {@link org.eclipse.jgit.revwalk.RevWalk#reset()} at the beginning. + *

+ * In order to improve performance this method assumes clock skew among + * committers is never larger than 24 hours. + * + * @param commit + * the commit we are looking at + * @param revWalk + * The RevWalk to be used. + * @param refs + * the set of branches we want to see reachability from + * @param monitor + * the callback for progress and cancellation + * @return the list of branches a given commit is reachable from + * @throws org.eclipse.jgit.errors.MissingObjectException + * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException + * @throws java.io.IOException + * @since 5.4 + */ + public static List findBranchesReachableFrom(RevCommit commit, + RevWalk revWalk, Collection refs, ProgressMonitor monitor) + throws MissingObjectException, IncorrectObjectTypeException, + IOException { // Make sure commit is from the same RevWalk commit = revWalk.parseCommit(commit.getId()); revWalk.reset(); List result = new ArrayList<>(); - + monitor.beginTask(JGitText.get().searchForReachableBranches, + refs.size()); final int SKEW = 24*3600; // one day clock skew for (Ref ref : refs) { + if (monitor.isCancelled()) + return result; + monitor.update(1); RevObject maybehead = revWalk.parseAny(ref.getObjectId()); if (!(maybehead instanceof RevCommit)) continue; @@ -176,6 +215,7 @@ public static List findBranchesReachableFrom(RevCommit commit, if (revWalk.isMergedInto(commit, headCommit)) result.add(ref); } + monitor.endTask(); return result; } 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 2b31ebd8e..fc6f4a39c 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 @@ -148,7 +148,8 @@ public final File getFile() { */ @Override public void load() throws IOException, ConfigInvalidException { - final int maxStaleRetries = 5; + final int maxRetries = 5; + int retryDelayMillis = 20; int retries = 0; while (true) { final FileSnapshot oldSnapshot = snapshot; @@ -177,6 +178,22 @@ public void load() throws IOException, ConfigInvalidException { } return; } catch (FileNotFoundException noFile) { + // might be locked by another process (see exception Javadoc) + if (retries < maxRetries && configFile.exists()) { + if (LOG.isDebugEnabled()) { + LOG.debug(MessageFormat.format( + JGitText.get().configHandleMayBeLocked, + Integer.valueOf(retries)), noFile); + } + try { + Thread.sleep(retryDelayMillis); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + retries++; + retryDelayMillis *= 2; // max wait 1260 ms + continue; + } if (configFile.exists()) { throw noFile; } @@ -185,7 +202,7 @@ public void load() throws IOException, ConfigInvalidException { return; } catch (IOException e) { if (FileUtils.isStaleFileHandle(e) - && retries < maxStaleRetries) { + && retries < maxRetries) { if (LOG.isDebugEnabled()) { LOG.debug(MessageFormat.format( JGitText.get().configHandleIsStale, diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackConnection.java index 20a5d9da7..e8724b72d 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackConnection.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackConnection.java @@ -201,7 +201,7 @@ private void readAdvertisedRefsImpl() throws IOException { throw noRepository(); throw eof; } - if (line == PacketLineIn.END) + if (PacketLineIn.isEnd(line)) break; if (line.startsWith("ERR ")) { //$NON-NLS-1$ diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java index 847e90198..35ea35ecb 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java @@ -383,8 +383,7 @@ private void readStatusReport(Map refUpdates) JGitText.get().errorOccurredDuringUnpackingOnTheRemoteEnd, unpackStatus)); } - String refLine; - while ((refLine = pckIn.readString()) != PacketLineIn.END) { + for (String refLine : pckIn.readStrings()) { boolean ok = false; int refNameEnd = -1; if (refLine.startsWith("ok ")) { //$NON-NLS-1$ 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 1741db97f..e402de015 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseReceivePack.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseReceivePack.java @@ -1282,7 +1282,7 @@ protected void recvCommands() throws IOException { return; throw eof; } - if (line == PacketLineIn.END) { + if (PacketLineIn.isEnd(line)) { break; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleWriter.java index a09b1ff1d..d1db51eca 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleWriter.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleWriter.java @@ -227,7 +227,7 @@ public void writeBundle(ProgressMonitor monitor, OutputStream os) exc.add(r.getId()); packWriter.setIndexDisabled(true); packWriter.setDeltaBaseAsOffset(true); - packWriter.setThin(exc.size() > 0); + packWriter.setThin(!exc.isEmpty()); packWriter.setReuseValidatingObjects(false); if (exc.isEmpty()) { packWriter.setTagTargets(tagTargets); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/HMACSHA1NonceGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/HMACSHA1NonceGenerator.java index 53eaa6a7f..01f6fec7e 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/HMACSHA1NonceGenerator.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/HMACSHA1NonceGenerator.java @@ -45,14 +45,12 @@ import static java.nio.charset.StandardCharsets.ISO_8859_1; import static java.nio.charset.StandardCharsets.UTF_8; -import java.io.File; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; -import org.eclipse.jgit.internal.storage.dfs.DfsRepository; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.transport.PushCertificate.NonceStatus; @@ -87,19 +85,7 @@ public HMACSHA1NonceGenerator(String seed) throws IllegalStateException { @Override public synchronized String createNonce(Repository repo, long timestamp) throws IllegalStateException { - String path; - if (repo instanceof DfsRepository) { - path = ((DfsRepository) repo).getDescription().getRepositoryName(); - } else { - File directory = repo.getDirectory(); - if (directory != null) { - path = directory.getPath(); - } else { - throw new IllegalStateException(); - } - } - - String input = path + ":" + String.valueOf(timestamp); //$NON-NLS-1$ + String input = repo.getIdentifier() + ":" + String.valueOf(timestamp); //$NON-NLS-1$ byte[] rawHmac = mac.doFinal(input.getBytes(UTF_8)); return Long.toString(timestamp) + "-" + toHex(rawHmac); //$NON-NLS-1$ } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpConfig.java index 101ce3568..54c21cbc8 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpConfig.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpConfig.java @@ -89,6 +89,30 @@ public class HttpConfig { /** git config key for the "sslVerify" setting. */ public static final String SSL_VERIFY_KEY = "sslVerify"; //$NON-NLS-1$ + /** + * git config key for the "cookieFile" setting. + * + * @since 5.4 + */ + public static final String COOKIE_FILE_KEY = "cookieFile"; //$NON-NLS-1$ + + /** + * git config key for the "saveCookies" setting. + * + * @since 5.4 + */ + public static final String SAVE_COOKIES_KEY = "saveCookies"; //$NON-NLS-1$ + + /** + * Custom JGit config key which holds the maximum number of cookie files to + * keep in the cache. + * + * @since 5.4 + */ + public static final String COOKIE_FILE_CACHE_LIMIT_KEY = "cookieFileCacheLimit"; //$NON-NLS-1$ + + private static final int DEFAULT_COOKIE_FILE_CACHE_LIMIT = 10; + private static final String MAX_REDIRECT_SYSTEM_PROPERTY = "http.maxRedirects"; //$NON-NLS-1$ private static final int DEFAULT_MAX_REDIRECTS = 5; @@ -153,6 +177,12 @@ public boolean matchConfigValue(String s) { private int maxRedirects; + private String cookieFile; + + private boolean saveCookies; + + private int cookieFileCacheLimit; + /** * Get the "http.postBuffer" setting * @@ -189,6 +219,40 @@ public int getMaxRedirects() { return maxRedirects; } + /** + * Get the "http.cookieFile" setting + * + * @return the value of the "http.cookieFile" setting + * + * @since 5.4 + */ + public String getCookieFile() { + return cookieFile; + } + + /** + * Get the "http.saveCookies" setting + * + * @return the value of the "http.saveCookies" setting + * + * @since 5.4 + */ + public boolean getSaveCookies() { + return saveCookies; + } + + /** + * Get the "http.cookieFileCacheLimit" setting (gives the maximum number of + * cookie files to keep in the LRU cache) + * + * @return the value of the "http.cookieFileCacheLimit" setting + * + * @since 5.4 + */ + public int getCookieFileCacheLimit() { + return cookieFileCacheLimit; + } + /** * Creates a new {@link org.eclipse.jgit.transport.HttpConfig} tailored to * the given {@link org.eclipse.jgit.transport.URIish}. @@ -237,6 +301,10 @@ private void init(Config config, URIish uri) { if (redirectLimit < 0) { redirectLimit = MAX_REDIRECTS; } + cookieFile = config.getString(HTTP, null, COOKIE_FILE_KEY); + saveCookies = config.getBoolean(HTTP, SAVE_COOKIES_KEY, false); + cookieFileCacheLimit = config.getInt(HTTP, COOKIE_FILE_CACHE_LIMIT_KEY, + DEFAULT_COOKIE_FILE_CACHE_LIMIT); String match = findMatch(config.getSubsections(HTTP), uri); if (match != null) { // Override with more specific items @@ -251,6 +319,13 @@ private void init(Config config, URIish uri) { if (newMaxRedirects >= 0) { redirectLimit = newMaxRedirects; } + String urlSpecificCookieFile = config.getString(HTTP, match, + COOKIE_FILE_KEY); + if (urlSpecificCookieFile != null) { + cookieFile = urlSpecificCookieFile; + } + saveCookies = config.getBoolean(HTTP, match, SAVE_COOKIES_KEY, + saveCookies); } postBuffer = postBufferSize; sslVerify = sslVerifyFlag; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PacketLineIn.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PacketLineIn.java index c6e19d576..90f1b373b 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PacketLineIn.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PacketLineIn.java @@ -49,7 +49,9 @@ import java.io.IOException; import java.io.InputStream; +import java.io.UncheckedIOException; import java.text.MessageFormat; +import java.util.Iterator; import org.eclipse.jgit.errors.PackProtocolException; import org.eclipse.jgit.internal.JGitText; @@ -72,14 +74,25 @@ public class PacketLineIn { private static final Logger log = LoggerFactory.getLogger(PacketLineIn.class); - /** Magic return from {@link #readString()} when a flush packet is found. */ + /** + * Magic return from {@link #readString()} when a flush packet is found. + * + * @deprecated Callers should use {@link #isEnd(String)} to check if a + * string is the end marker, or + * {@link PacketLineIn#readStrings()} to iterate over all + * strings in the input stream until the marker is reached. + */ + @Deprecated public static final String END = new StringBuilder(0).toString(); /* must not string pool */ /** * Magic return from {@link #readString()} when a delim packet is found. * * @since 5.0 + * @deprecated Callers should use {@link #isDelimiter(String)} to check if a + * string is the delimiter. */ + @Deprecated public static final String DELIM = new StringBuilder(0).toString(); /* must not string pool */ static enum AckNackResult { @@ -192,6 +205,20 @@ public String readString() throws IOException { return s; } + /** + * Get an iterator to read strings from the input stream. + * + * @return an iterator that calls {@link #readString()} until {@link #END} + * is encountered. + * + * @throws IOException + * on failure to read the initial packet line. + * @since 5.4 + */ + public PacketLineInIterator readStrings() throws IOException { + return new PacketLineInIterator(this); + } + /** * Read a single UTF-8 encoded string packet from the input stream. *

@@ -224,6 +251,52 @@ public String readStringRaw() throws IOException { return s; } + /** + * Check if a string is the delimiter marker. + * + * @param s + * the string to check + * @return true if the given string is {@link #DELIM}, otherwise false. + * @since 5.4 + */ + public static boolean isDelimiter(String s) { + return s == DELIM; + } + + /** + * Get the delimiter marker. + *

+ * Intended for use only in tests. + * + * @return The delimiter marker. + */ + static String delimiter() { + return DELIM; + } + + /** + * Get the end marker. + *

+ * Intended for use only in tests. + * + * @return The end marker. + */ + static String end() { + return END; + } + + /** + * Check if a string is the packet end marker. + * + * @param s + * the string to check + * @return true if the given string is {@link #END}, otherwise false. + * @since 5.4 + */ + public static boolean isEnd(String s) { + return s == END; + } + void discardUntilEnd() throws IOException { for (;;) { int n = readLength(); @@ -282,4 +355,46 @@ private IOException invalidHeader() { public static class InputOverLimitIOException extends IOException { private static final long serialVersionUID = 1L; } + + /** + * Iterator over packet lines. + *

+ * Calls {@link #readString()} on the {@link PacketLineIn} until + * {@link #END} is encountered. + * + * @since 5.4 + * + */ + public static class PacketLineInIterator implements Iterable { + private PacketLineIn in; + + private String current; + + PacketLineInIterator(PacketLineIn in) throws IOException { + this.in = in; + current = in.readString(); + } + + @Override + public Iterator iterator() { + return new Iterator() { + @Override + public boolean hasNext() { + return !PacketLineIn.isEnd(current); + } + + @Override + public String next() { + String next = current; + try { + current = in.readString(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + return next; + } + }; + } + + } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV0Parser.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV0Parser.java index 396327aab..428a45c09 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV0Parser.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV0Parser.java @@ -99,7 +99,7 @@ FetchV0Request recvWants(PacketLineIn pckIn) throw eof; } - if (line == PacketLineIn.END) { + if (PacketLineIn.isEnd(line)) { break; } 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 cb04ff69a..caba15fc5 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2Parser.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2Parser.java @@ -92,7 +92,7 @@ private static String consumeCapabilities(PacketLineIn pckIn, String agentPrefix = OPTION_AGENT + '='; String line = pckIn.readString(); - while (line != PacketLineIn.DELIM && line != PacketLineIn.END) { + while (!PacketLineIn.isDelimiter(line) && !PacketLineIn.isEnd(line)) { if (line.startsWith(serverOptionPrefix)) { serverOptionConsumer .accept(line.substring(serverOptionPrefix.length())); @@ -133,40 +133,41 @@ FetchV2Request parseFetchRequest(PacketLineIn pckIn) serverOption -> reqBuilder.addServerOption(serverOption), agent -> reqBuilder.setAgent(agent)); - if (line == PacketLineIn.END) { + if (PacketLineIn.isEnd(line)) { return reqBuilder.build(); } - if (line != PacketLineIn.DELIM) { + if (!PacketLineIn.isDelimiter(line)) { 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.addWantId(ObjectId.fromString(line.substring(5))); + for (String line2 : pckIn.readStrings()) { + if (line2.startsWith("want ")) { //$NON-NLS-1$ + reqBuilder.addWantId(ObjectId.fromString(line2.substring(5))); } else if (transferConfig.isAllowRefInWant() - && line.startsWith(OPTION_WANT_REF + " ")) { //$NON-NLS-1$ - reqBuilder.addWantedRef(line.substring(OPTION_WANT_REF.length() + 1)); - } else if (line.startsWith("have ")) { //$NON-NLS-1$ - reqBuilder.addPeerHas(ObjectId.fromString(line.substring(5))); - } else if (line.equals("done")) { //$NON-NLS-1$ + && line2.startsWith(OPTION_WANT_REF + " ")) { //$NON-NLS-1$ + reqBuilder.addWantedRef( + line2.substring(OPTION_WANT_REF.length() + 1)); + } else if (line2.startsWith("have ")) { //$NON-NLS-1$ + reqBuilder.addPeerHas(ObjectId.fromString(line2.substring(5))); + } else if (line2.equals("done")) { //$NON-NLS-1$ reqBuilder.setDoneReceived(); - } else if (line.equals(OPTION_THIN_PACK)) { + } else if (line2.equals(OPTION_THIN_PACK)) { reqBuilder.addClientCapability(OPTION_THIN_PACK); - } else if (line.equals(OPTION_NO_PROGRESS)) { + } else if (line2.equals(OPTION_NO_PROGRESS)) { reqBuilder.addClientCapability(OPTION_NO_PROGRESS); - } else if (line.equals(OPTION_INCLUDE_TAG)) { + } else if (line2.equals(OPTION_INCLUDE_TAG)) { reqBuilder.addClientCapability(OPTION_INCLUDE_TAG); - } else if (line.equals(OPTION_OFS_DELTA)) { + } else if (line2.equals(OPTION_OFS_DELTA)) { reqBuilder.addClientCapability(OPTION_OFS_DELTA); - } else if (line.startsWith("shallow ")) { //$NON-NLS-1$ + } else if (line2.startsWith("shallow ")) { //$NON-NLS-1$ reqBuilder.addClientShallowCommit( - ObjectId.fromString(line.substring(8))); - } else if (line.startsWith("deepen ")) { //$NON-NLS-1$ - int parsedDepth = Integer.parseInt(line.substring(7)); + ObjectId.fromString(line2.substring(8))); + } else if (line2.startsWith("deepen ")) { //$NON-NLS-1$ + int parsedDepth = Integer.parseInt(line2.substring(7)); if (parsedDepth <= 0) { throw new PackProtocolException( MessageFormat.format(JGitText.get().invalidDepth, @@ -181,19 +182,19 @@ FetchV2Request parseFetchRequest(PacketLineIn pckIn) JGitText.get().deepenNotWithDeepen); } reqBuilder.setDepth(parsedDepth); - } else if (line.startsWith("deepen-not ")) { //$NON-NLS-1$ - reqBuilder.addDeepenNotRef(line.substring(11)); + } else if (line2.startsWith("deepen-not ")) { //$NON-NLS-1$ + reqBuilder.addDeepenNotRef(line2.substring(11)); if (reqBuilder.getDepth() != 0) { throw new PackProtocolException( JGitText.get().deepenNotWithDeepen); } - } else if (line.equals(OPTION_DEEPEN_RELATIVE)) { + } else if (line2.equals(OPTION_DEEPEN_RELATIVE)) { reqBuilder.addClientCapability(OPTION_DEEPEN_RELATIVE); - } else if (line.startsWith("deepen-since ")) { //$NON-NLS-1$ - int ts = Integer.parseInt(line.substring(13)); + } else if (line2.startsWith("deepen-since ")) { //$NON-NLS-1$ + int ts = Integer.parseInt(line2.substring(13)); if (ts <= 0) { throw new PackProtocolException(MessageFormat - .format(JGitText.get().invalidTimestamp, line)); + .format(JGitText.get().invalidTimestamp, line2)); } if (reqBuilder.getDepth() != 0) { throw new PackProtocolException( @@ -201,17 +202,17 @@ FetchV2Request parseFetchRequest(PacketLineIn pckIn) } reqBuilder.setDeepenSince(ts); } else if (transferConfig.isAllowFilter() - && line.startsWith(OPTION_FILTER + ' ')) { + && line2.startsWith(OPTION_FILTER + ' ')) { if (filterReceived) { throw new PackProtocolException( JGitText.get().tooManyFilters); } filterReceived = true; reqBuilder.setFilterSpec(FilterSpec.fromFilterLine( - line.substring(OPTION_FILTER.length() + 1))); + line2.substring(OPTION_FILTER.length() + 1))); } else { throw new PackProtocolException(MessageFormat - .format(JGitText.get().unexpectedPacketLine, line)); + .format(JGitText.get().unexpectedPacketLine, line2)); } } @@ -244,25 +245,25 @@ LsRefsV2Request parseLsRefsRequest(PacketLineIn pckIn) serverOption -> builder.addServerOption(serverOption), agent -> builder.setAgent(agent)); - if (line == PacketLineIn.END) { + if (PacketLineIn.isEnd(line)) { return builder.build(); } - if (line != PacketLineIn.DELIM) { + if (!PacketLineIn.isDelimiter(line)) { throw new PackProtocolException(MessageFormat .format(JGitText.get().unexpectedPacketLine, line)); } - while ((line = pckIn.readString()) != PacketLineIn.END) { - if (line.equals("peel")) { //$NON-NLS-1$ + for (String line2 : pckIn.readStrings()) { + if (line2.equals("peel")) { //$NON-NLS-1$ builder.setPeel(true); - } else if (line.equals("symrefs")) { //$NON-NLS-1$ + } else if (line2.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 if (line2.startsWith("ref-prefix ")) { //$NON-NLS-1$ + prefixes.add(line2.substring("ref-prefix ".length())); //$NON-NLS-1$ } else { throw new PackProtocolException(MessageFormat - .format(JGitText.get().unexpectedPacketLine, line)); + .format(JGitText.get().unexpectedPacketLine, line2)); } } 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 4652c3fda..d6adf1e0d 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java @@ -348,7 +348,7 @@ void readPostCommands(PacketLineIn in) throws IOException { pushOptions = new ArrayList<>(4); for (;;) { String option = in.readString(); - if (option == PacketLineIn.END) { + if (PacketLineIn.isEnd(option)) { break; } pushOptions.add(option); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java index b752a6527..27ab87951 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java @@ -54,8 +54,11 @@ import static org.eclipse.jgit.util.HttpSupport.HDR_ACCEPT_ENCODING; import static org.eclipse.jgit.util.HttpSupport.HDR_CONTENT_ENCODING; import static org.eclipse.jgit.util.HttpSupport.HDR_CONTENT_TYPE; +import static org.eclipse.jgit.util.HttpSupport.HDR_COOKIE; import static org.eclipse.jgit.util.HttpSupport.HDR_LOCATION; import static org.eclipse.jgit.util.HttpSupport.HDR_PRAGMA; +import static org.eclipse.jgit.util.HttpSupport.HDR_SET_COOKIE; +import static org.eclipse.jgit.util.HttpSupport.HDR_SET_COOKIE2; import static org.eclipse.jgit.util.HttpSupport.HDR_USER_AGENT; import static org.eclipse.jgit.util.HttpSupport.HDR_WWW_AUTHENTICATE; import static org.eclipse.jgit.util.HttpSupport.METHOD_GET; @@ -68,11 +71,15 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; +import java.net.HttpCookie; import java.net.MalformedURLException; import java.net.Proxy; import java.net.ProxySelector; import java.net.URISyntaxException; import java.net.URL; +import java.nio.file.InvalidPathException; +import java.nio.file.Path; +import java.nio.file.Paths; import java.security.cert.CertPathBuilderException; import java.security.cert.CertPathValidatorException; import java.security.cert.CertificateException; @@ -84,6 +91,8 @@ import java.util.EnumSet; import java.util.HashSet; import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; @@ -100,6 +109,8 @@ import org.eclipse.jgit.errors.TransportException; import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.internal.storage.file.RefDirectory; +import org.eclipse.jgit.internal.transport.http.NetscapeCookieFile; +import org.eclipse.jgit.internal.transport.http.NetscapeCookieFileCache; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectIdRef; @@ -116,6 +127,7 @@ import org.eclipse.jgit.util.HttpSupport; import org.eclipse.jgit.util.IO; import org.eclipse.jgit.util.RawParseUtils; +import org.eclipse.jgit.util.StringUtils; import org.eclipse.jgit.util.SystemReader; import org.eclipse.jgit.util.TemporaryBuffer; import org.eclipse.jgit.util.io.DisabledOutputStream; @@ -274,6 +286,19 @@ public Transport open(URIish uri, Repository local, String remoteName) private boolean sslFailure = false; + /** + * All stored cookies bound to this repo (independent of the baseUrl) + */ + private final NetscapeCookieFile cookieFile; + + /** + * The cookies to be sent with each request to the given {@link #baseUrl}. + * Filtered view on top of {@link #cookieFile} where only cookies which + * apply to the current url are left. This set needs to be filtered for + * expired entries each time prior to sending them. + */ + private final Set relevantCookies; + TransportHttp(Repository local, URIish uri) throws NotSupportedException { super(local, uri); @@ -281,6 +306,8 @@ public Transport open(URIish uri, Repository local, String remoteName) http = new HttpConfig(local.getConfig(), uri); proxySelector = ProxySelector.getDefault(); sslVerify = http.isSslVerify(); + cookieFile = getCookieFileFromConfig(http); + relevantCookies = filterCookies(cookieFile, baseUrl); } private URL toURL(URIish urish) throws MalformedURLException { @@ -321,6 +348,8 @@ protected void setURI(URIish uri) throws NotSupportedException { http = new HttpConfig(uri); proxySelector = ProxySelector.getDefault(); sslVerify = http.isSslVerify(); + cookieFile = getCookieFileFromConfig(http); + relevantCookies = filterCookies(cookieFile, baseUrl); } /** @@ -508,6 +537,7 @@ private HttpConnection connect(String service) conn.setRequestProperty(HDR_ACCEPT, "*/*"); //$NON-NLS-1$ } final int status = HttpSupport.response(conn); + processResponseCookies(conn); switch (status) { case HttpConnection.HTTP_OK: // Check if HttpConnection did some authentication in the @@ -596,6 +626,57 @@ private HttpConnection connect(String service) } } + void processResponseCookies(HttpConnection conn) { + if (cookieFile != null && http.getSaveCookies()) { + List foundCookies = new LinkedList<>(); + + List cookieHeaderValues = conn + .getHeaderFields(HDR_SET_COOKIE); + if (!cookieHeaderValues.isEmpty()) { + foundCookies.addAll( + extractCookies(HDR_SET_COOKIE, cookieHeaderValues)); + } + cookieHeaderValues = conn.getHeaderFields(HDR_SET_COOKIE2); + if (!cookieHeaderValues.isEmpty()) { + foundCookies.addAll( + extractCookies(HDR_SET_COOKIE2, cookieHeaderValues)); + } + if (!foundCookies.isEmpty()) { + try { + // update cookie lists with the newly received cookies! + Set cookies = cookieFile.getCookies(false); + cookies.addAll(foundCookies); + cookieFile.write(baseUrl); + relevantCookies.addAll(foundCookies); + } catch (IOException | IllegalArgumentException + | InterruptedException e) { + LOG.warn(MessageFormat.format( + JGitText.get().couldNotPersistCookies, + cookieFile.getPath()), e); + } + } + } + } + + private List extractCookies(String headerKey, + List headerValues) { + List foundCookies = new LinkedList<>(); + for (String headerValue : headerValues) { + foundCookies + .addAll(HttpCookie.parse(headerKey + ':' + headerValue)); + } + // HttpCookies.parse(...) is only compliant with RFC 2965. Make it RFC + // 6265 compliant by applying the logic from + // https://tools.ietf.org/html/rfc6265#section-5.2.3 + for (HttpCookie foundCookie : foundCookies) { + String domain = foundCookie.getDomain(); + if (domain != null && domain.startsWith(".")) { //$NON-NLS-1$ + foundCookie.setDomain(domain.substring(1)); + } + } + return foundCookies; + } + private static class CredentialItems { CredentialItem.InformationalMessage message; @@ -847,14 +928,35 @@ protected HttpConnection httpOpen(String method, URL u, conn.setConnectTimeout(effTimeOut); conn.setReadTimeout(effTimeOut); } + // set cookie header if necessary + if (!relevantCookies.isEmpty()) { + setCookieHeader(conn); + } + if (this.headers != null && !this.headers.isEmpty()) { - for (Map.Entry entry : this.headers.entrySet()) + for (Map.Entry entry : this.headers.entrySet()) { conn.setRequestProperty(entry.getKey(), entry.getValue()); + } } authMethod.configureRequest(conn); return conn; } + private void setCookieHeader(HttpConnection conn) { + StringBuilder cookieHeaderValue = new StringBuilder(); + for (HttpCookie cookie : relevantCookies) { + if (!cookie.hasExpired()) { + if (cookieHeaderValue.length() > 0) { + cookieHeaderValue.append(';'); + } + cookieHeaderValue.append(cookie.toString()); + } + } + if (cookieHeaderValue.length() > 0) { + conn.setRequestProperty(HDR_COOKIE, cookieHeaderValue.toString()); + } + } + final InputStream openInputStream(HttpConnection conn) throws IOException { InputStream input = conn.getInputStream(); @@ -868,6 +970,150 @@ IOException wrongContentType(String expType, String actType) { return new TransportException(uri, why); } + private static NetscapeCookieFile getCookieFileFromConfig( + HttpConfig config) { + if (!StringUtils.isEmptyOrNull(config.getCookieFile())) { + try { + Path cookieFilePath = Paths.get(config.getCookieFile()); + return NetscapeCookieFileCache.getInstance(config) + .getEntry(cookieFilePath); + } catch (InvalidPathException e) { + LOG.warn(MessageFormat.format( + JGitText.get().couldNotReadCookieFile, + config.getCookieFile()), e); + } + } + return null; + } + + private static Set filterCookies(NetscapeCookieFile cookieFile, + URL url) { + if (cookieFile != null) { + return filterCookies(cookieFile.getCookies(true), url); + } + return Collections.emptySet(); + } + + /** + * + * @param allCookies + * a list of cookies. + * @param url + * the url for which to filter the list of cookies. + * @return only the cookies from {@code allCookies} which are relevant (i.e. + * are not expired, have a matching domain, have a matching path and + * have a matching secure attribute) + */ + private static Set filterCookies(Set allCookies, + URL url) { + Set filteredCookies = new HashSet<>(); + for (HttpCookie cookie : allCookies) { + if (cookie.hasExpired()) { + continue; + } + if (!matchesCookieDomain(url.getHost(), cookie.getDomain())) { + continue; + } + if (!matchesCookiePath(url.getPath(), cookie.getPath())) { + continue; + } + if (cookie.getSecure() && !"https".equals(url.getProtocol())) { //$NON-NLS-1$ + continue; + } + filteredCookies.add(cookie); + } + return filteredCookies; + } + + /** + * + * The utility method to check whether a host name is in a cookie's domain + * or not. Similar to {@link HttpCookie#domainMatches(String, String)} but + * implements domain matching rules according to + * RFC 6265, + * section 5.1.3 instead of the rules from + * RFC 2965, + * section 3.3.1. + *

+ * The former rules are also used by libcurl internally. + *

+ * The rules are as follows + * + * A string matches another domain string if at least one of the following + * conditions holds: + *

    + *
  • The domain string and the string are identical. (Note that both the + * domain string and the string will have been canonicalized to lower case + * at this point.)
  • + *
  • All of the following conditions hold + *
      + *
    • The domain string is a suffix of the string.
    • + *
    • The last character of the string that is not included in the domain + * string is a %x2E (".") character.
    • + *
    • The string is a host name (i.e., not an IP address).
    • + *
    + *
  • + *
+ * + * @param host + * the host to compare against the cookieDomain + * @param cookieDomain + * the domain to compare against + * @return {@code true} if they domain-match; {@code false} if not + * + * @see RFC + * 6265, section 5.1.3 (Domain Matching) + * @see JDK-8206092 + * : HttpCookie.domainMatches() does not match to sub-sub-domain + */ + static boolean matchesCookieDomain(String host, String cookieDomain) { + cookieDomain = cookieDomain.toLowerCase(Locale.ROOT); + host = host.toLowerCase(Locale.ROOT); + if (host.equals(cookieDomain)) { + return true; + } else { + if (!host.endsWith(cookieDomain)) { + return false; + } + return host + .charAt(host.length() - cookieDomain.length() - 1) == '.'; + } + } + + /** + * The utility method to check whether a path is matching a cookie path + * domain or not. The rules are defined by + * RFC 6265, + * section 5.1.4: + * + * A request-path path-matches a given cookie-path if at least one of the + * following conditions holds: + *
    + *
  • The cookie-path and the request-path are identical.
  • + *
  • The cookie-path is a prefix of the request-path, and the last + * character of the cookie-path is %x2F ("/").
  • + *
  • The cookie-path is a prefix of the request-path, and the first + * character of the request-path that is not included in the cookie- path is + * a %x2F ("/") character.
  • + *
+ * @param path + * the path to check + * @param cookiePath + * the cookie's path + * + * @return {@code true} if they path-match; {@code false} if not + */ + static boolean matchesCookiePath(String path, String cookiePath) { + if (cookiePath.equals(path)) { + return true; + } + if (!cookiePath.endsWith("/")) { //$NON-NLS-1$ + cookiePath += "/"; //$NON-NLS-1$ + } + return path.startsWith(cookiePath); + } + private boolean isSmartHttp(HttpConnection c, String service) { final String expType = "application/x-" + service + "-advertisement"; //$NON-NLS-1$ //$NON-NLS-2$ final String actType = c.getContentType(); @@ -902,7 +1148,7 @@ private void readSmartHeaders(InputStream in, String service) JGitText.get().expectedGot, exp, act)); } - while (pckIn.readString() != PacketLineIn.END) { + while (!PacketLineIn.isEnd(pckIn.readString())) { // for now, ignore the remaining header lines } } 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 3ed9886c4..9278f42ad 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java @@ -106,10 +106,8 @@ import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.AsyncRevObjectQueue; import org.eclipse.jgit.revwalk.BitmapWalker; -import org.eclipse.jgit.revwalk.BitmappedReachabilityChecker; import org.eclipse.jgit.revwalk.DepthWalk; import org.eclipse.jgit.revwalk.ObjectWalk; -import org.eclipse.jgit.revwalk.PedestrianReachabilityChecker; import org.eclipse.jgit.revwalk.ReachabilityChecker; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevFlag; @@ -1231,7 +1229,7 @@ private boolean serveOneCommandV2() throws IOException { /* EOF when awaiting command is fine */ return true; } - if (command == PacketLineIn.END) { + if (PacketLineIn.isEnd(command)) { // A blank request is valid according // to the protocol; do nothing in this // case. @@ -1602,7 +1600,7 @@ private boolean negotiate(FetchRequest req, throw eof; } - if (line == PacketLineIn.END) { + if (PacketLineIn.isEnd(line)) { last = processHaveLines(peerHas, last, pckOut); if (commonBase.isEmpty() || multiAck != MultiAck.OFF) pckOut.writeString("NAK\n"); //$NON-NLS-1$ @@ -1909,9 +1907,8 @@ private static void checkNotAdvertisedWants(UploadPack up, } // All wants are commits, we can use ReachabilityChecker - ReachabilityChecker reachabilityChecker = repoHasBitmaps - ? new BitmappedReachabilityChecker(walk) - : new PedestrianReachabilityChecker(true, walk); + ReachabilityChecker reachabilityChecker = walk + .createReachabilityChecker(); List starters = objectIdsToRevCommits(walk, reachableFrom); 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 c0c24872b..3efa66459 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java @@ -1516,6 +1516,7 @@ private boolean hasCrLfInIndex(DirCacheIterator dirCache) { ObjectId blobId = entry.getObjectId(); if (entry.getStage() > 0 && entry.getStage() != DirCacheEntry.STAGE_2) { + blobId = null; // Merge conflict: check ours (stage 2) byte[] name = entry.getRawPath(); int i = 0; @@ -1523,7 +1524,8 @@ private boolean hasCrLfInIndex(DirCacheIterator dirCache) { dirCache.next(1); i++; entry = dirCache.getDirCacheEntry(); - if (!Arrays.equals(name, entry.getRawPath())) { + if (entry == null + || !Arrays.equals(name, entry.getRawPath())) { break; } if (entry.getStage() == DirCacheEntry.STAGE_2) { @@ -1533,17 +1535,20 @@ private boolean hasCrLfInIndex(DirCacheIterator dirCache) { } dirCache.back(i); } - try (ObjectReader reader = repository.newObjectReader()) { - ObjectLoader loader = reader.open(blobId, Constants.OBJ_BLOB); - try { - return RawText.isCrLfText(loader.getCachedBytes()); - } catch (LargeObjectException e) { - try (InputStream in = loader.openStream()) { - return RawText.isCrLfText(in); + if (blobId != null) { + try (ObjectReader reader = repository.newObjectReader()) { + ObjectLoader loader = reader.open(blobId, + Constants.OBJ_BLOB); + try { + return RawText.isCrLfText(loader.getCachedBytes()); + } catch (LargeObjectException e) { + try (InputStream in = loader.openStream()) { + return RawText.isCrLfText(in); + } } + } catch (IOException e) { + // Ignore and return false below } - } catch (IOException e) { - // Ignore and return false below } } return false; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java index 716711e06..faef9fd0f 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java @@ -48,7 +48,8 @@ import java.io.InputStreamReader; import java.io.PrintStream; import java.nio.charset.Charset; -import java.nio.file.AccessDeniedException; +import java.nio.file.FileStore; +import java.nio.file.FileSystemException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -57,9 +58,11 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; import org.eclipse.jgit.annotations.Nullable; import org.eclipse.jgit.api.errors.JGitInternalException; @@ -84,7 +87,7 @@ public class FS_POSIX extends FS { private static final int DEFAULT_UMASK = 0022; private volatile int umask = -1; - private volatile boolean supportsUnixNLink = true; + private static final Map CAN_HARD_LINK = new ConcurrentHashMap<>(); private volatile AtomicFileCreation supportsAtomicCreateNewFile = AtomicFileCreation.UNDEFINED; @@ -388,12 +391,18 @@ public boolean createNewFile(File lock) throws IOException { if (!lock.createNewFile()) { return false; } - if (supportsAtomicCreateNewFile() || !supportsUnixNLink) { + if (supportsAtomicCreateNewFile()) { return true; } Path lockPath = lock.toPath(); Path link = null; + FileStore store = Files.getFileStore(lockPath); try { + Boolean canLink = CAN_HARD_LINK.computeIfAbsent(store, + s -> Boolean.TRUE); + if (Boolean.FALSE.equals(canLink)) { + return true; + } link = Files.createLink( Paths.get(lock.getAbsolutePath() + ".lnk"), //$NON-NLS-1$ lockPath); @@ -405,11 +414,11 @@ public boolean createNewFile(File lock) throws IOException { nlink)); return false; } else if (nlink < 2) { - supportsUnixNLink = false; + CAN_HARD_LINK.put(store, Boolean.FALSE); } return true; } catch (UnsupportedOperationException | IllegalArgumentException e) { - supportsUnixNLink = false; + CAN_HARD_LINK.put(store, Boolean.FALSE); return true; } finally { if (link != null) { @@ -448,12 +457,18 @@ public LockToken createNewFileAtomic(File file) throws IOException { if (!file.createNewFile()) { return token(false, null); } - if (supportsAtomicCreateNewFile() || !supportsUnixNLink) { + if (supportsAtomicCreateNewFile()) { return token(true, null); } Path link = null; Path path = file.toPath(); + FileStore store = Files.getFileStore(path); try { + Boolean canLink = CAN_HARD_LINK.computeIfAbsent(store, + s -> Boolean.TRUE); + if (Boolean.FALSE.equals(canLink)) { + return token(true, null); + } link = Files.createLink(Paths.get(uniqueLinkPath(file)), path); Integer nlink = (Integer) (Files.getAttribute(path, "unix:nlink")); //$NON-NLS-1$ @@ -462,12 +477,12 @@ public LockToken createNewFileAtomic(File file) throws IOException { JGitText.get().failedAtomicFileCreation, path, nlink)); return token(false, link); } else if (nlink.intValue() < 2) { - supportsUnixNLink = false; + CAN_HARD_LINK.put(store, Boolean.FALSE); } return token(true, link); } catch (UnsupportedOperationException | IllegalArgumentException - | AccessDeniedException | SecurityException e) { - supportsUnixNLink = false; + | FileSystemException | SecurityException e) { + CAN_HARD_LINK.put(store, Boolean.FALSE); return token(true, link); } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java index 98797dc64..3ccbd7280 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java @@ -205,18 +205,21 @@ protected File discoverGitExe() { @Override protected File userHomeImpl() { String home = SystemReader.getInstance().getenv("HOME"); //$NON-NLS-1$ - if (home != null) + if (home != null) { return resolve(null, home); + } String homeDrive = SystemReader.getInstance().getenv("HOMEDRIVE"); //$NON-NLS-1$ if (homeDrive != null) { String homePath = SystemReader.getInstance().getenv("HOMEPATH"); //$NON-NLS-1$ - if (homePath != null) + if (homePath != null) { return new File(homeDrive, homePath); + } } String homeShare = SystemReader.getInstance().getenv("HOMESHARE"); //$NON-NLS-1$ - if (homeShare != null) + if (homeShare != null) { return new File(homeShare); + } return super.userHomeImpl(); } @@ -237,8 +240,9 @@ public ProcessBuilder runInShell(String cmd, String[] args) { /** {@inheritDoc} */ @Override public boolean supportsSymlinks() { - if (supportSymlinks == null) + if (supportSymlinks == null) { detectSymlinkSupport(); + } return Boolean.TRUE.equals(supportSymlinks); } @@ -254,12 +258,13 @@ private void detectSymlinkSupport() { | InternalError e) { supportSymlinks = Boolean.FALSE; } finally { - if (tempFile != null) + if (tempFile != null) { try { FileUtils.delete(tempFile); } catch (IOException e) { throw new RuntimeException(e); // panic } + } } } 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 54e4ee01f..640670deb 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/HttpSupport.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/HttpSupport.java @@ -169,6 +169,27 @@ public class HttpSupport { /** The {@code WWW-Authenticate} header. */ public static final String HDR_WWW_AUTHENTICATE = "WWW-Authenticate"; //$NON-NLS-1$ + /** + * The {@code Cookie} header. + * + * @since 5.4 + */ + public static final String HDR_COOKIE = "Cookie"; //$NON-NLS-1$ + + /** + * The {@code Set-Cookie} header. + * + * @since 5.4 + */ + public static final String HDR_SET_COOKIE = "Set-Cookie"; //$NON-NLS-1$ + + /** + * The {@code Set-Cookie2} header. + * + * @since 5.4 + */ + public static final String HDR_SET_COOKIE2 = "Set-Cookie2"; //$NON-NLS-1$ + /** * URL encode a value string into an output buffer. * diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/LRUMap.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/LRUMap.java new file mode 100644 index 000000000..41c15363f --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/LRUMap.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2018, Konrad Windszus + * 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.util; + +import java.util.LinkedHashMap; + +/** + * Map with only up to n entries. If a new entry is added so that the map + * contains more than those n entries the least-recently used entry is removed + * from the map. + * + * @param + * the type of keys maintained by this map + * @param + * the type of mapped values + * + * @since 5.4 + */ +public class LRUMap extends LinkedHashMap { + + private static final long serialVersionUID = 4329609127403759486L; + + private final int limit; + + /** + * Constructs an empty map which may contain at most the given amount of + * entries. + * + * @param initialCapacity + * the initial capacity + * @param limit + * the number of entries the map should have at most + */ + public LRUMap(int initialCapacity, int limit) { + super(initialCapacity, 0.75f, true); + this.limit = limit; + } + + @Override + protected boolean removeEldestEntry(java.util.Map.Entry eldest) { + return size() > limit; + } +} diff --git a/pom.xml b/pom.xml index 5377b9e40..48d31c4db 100644 --- a/pom.xml +++ b/pom.xml @@ -202,8 +202,8 @@ 3.1.0 1.3.0 2.8.2 - 1.60 - 3.1.11 + 1.61 + 3.1.12 2.22.2 3.8.1 3.0.0 @@ -239,7 +239,7 @@ org.apache.maven.plugins maven-jar-plugin - 3.1.1 + 3.1.2 @@ -282,7 +282,7 @@ org.apache.maven.plugins maven-source-plugin - 3.0.1 + 3.1.0 @@ -366,7 +366,7 @@ org.jacoco jacoco-maven-plugin - 0.8.3 + 0.8.4 org.apache.maven.plugins diff --git a/tools/BUILD b/tools/BUILD index f0342ad75..38daececb 100644 --- a/tools/BUILD +++ b/tools/BUILD @@ -44,7 +44,7 @@ java_package_configuration( "-Xep:FragmentInjection:ERROR", "-Xep:FragmentNotInstantiable:ERROR", "-Xep:FunctionalInterfaceClash:ERROR", - "-Xep:FutureReturnValueIgnored:WARN", + "-Xep:FutureReturnValueIgnored:ERROR", "-Xep:GetClassOnEnum:ERROR", "-Xep:ImmutableAnnotationChecker:ERROR", "-Xep:ImmutableEnumChecker:WARN",