Merge branch 'stable-5.12'
* stable-5.12:
Prepare 5.12.1-SNAPSHOT builds
JGit v5.12.0.202106070339-r
[license-check] Update list of project dependencies
[errorprone] Fix warning InputStreamSlowMultibyteRead
[errorprone] Make operator precedence explicit in OpenSshConfigFile
Update jetty to 9.4.41.v20210516
Prepare 5.1.17-SNAPSHOT builds
JGit v5.1.16.202106041830-r
BatchRefUpdate: Skip saving conflicting ref names and prefixes in memory
BatchRefUpdateTest: Accurately assert RefsChangedEvent(s) fired
Optimize RefDirectory.isNameConflicting()
Update bazlets and bazel version
Change-Id: I9bd4dfc3796a5034be7b3e86b35ef591bb516a2c
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 5045e94..b0b5f68 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
@@ -999,6 +999,108 @@
}
@Test
+ public void testV2FetchWithoutWaitForDoneReceivesPackfile()
+ throws Exception {
+ String commonInBlob = "abcdefghijklmnopqrstuvwxyz";
+
+ RevBlob parentBlob = remote.blob(commonInBlob + "a");
+ RevCommit parent = remote
+ .commit(remote.tree(remote.file("foo", parentBlob)));
+ remote.update("branch1", parent);
+
+ RevCommit localParent = null;
+ RevCommit localChild = null;
+ try (TestRepository<InMemoryRepository> local = new TestRepository<>(
+ client)) {
+ RevBlob localParentBlob = local.blob(commonInBlob + "a");
+ localParent = local
+ .commit(local.tree(local.file("foo", localParentBlob)));
+ RevBlob localChildBlob = local.blob(commonInBlob + "b");
+ localChild = local.commit(
+ local.tree(local.file("foo", localChildBlob)), localParent);
+ local.update("branch1", localChild);
+ }
+
+ ByteArrayInputStream recvStream = uploadPackV2("command=fetch\n",
+ PacketLineIn.delimiter(),
+ "have " + localParent.toObjectId().getName() + "\n",
+ "have " + localChild.toObjectId().getName() + "\n",
+ PacketLineIn.end());
+ PacketLineIn pckIn = new PacketLineIn(recvStream);
+ assertThat(pckIn.readString(), is("acknowledgments"));
+ assertThat(Arrays.asList(pckIn.readString()),
+ hasItems("ACK " + parent.toObjectId().getName()));
+ assertThat(pckIn.readString(), is("ready"));
+ assertTrue(PacketLineIn.isDelimiter(pckIn.readString()));
+ assertThat(pckIn.readString(), is("packfile"));
+ parsePack(recvStream);
+ }
+
+ @Test
+ public void testV2FetchWithWaitForDoneOnlyDoesNegotiation()
+ throws Exception {
+ String commonInBlob = "abcdefghijklmnopqrstuvwxyz";
+
+ RevBlob parentBlob = remote.blob(commonInBlob + "a");
+ RevCommit parent = remote
+ .commit(remote.tree(remote.file("foo", parentBlob)));
+ remote.update("branch1", parent);
+
+ RevCommit localParent = null;
+ RevCommit localChild = null;
+ try (TestRepository<InMemoryRepository> local = new TestRepository<>(
+ client)) {
+ RevBlob localParentBlob = local.blob(commonInBlob + "a");
+ localParent = local
+ .commit(local.tree(local.file("foo", localParentBlob)));
+ RevBlob localChildBlob = local.blob(commonInBlob + "b");
+ localChild = local.commit(
+ local.tree(local.file("foo", localChildBlob)), localParent);
+ local.update("branch1", localChild);
+ }
+
+ ByteArrayInputStream recvStream = uploadPackV2("command=fetch\n",
+ PacketLineIn.delimiter(), "wait-for-done\n",
+ "have " + localParent.toObjectId().getName() + "\n",
+ "have " + localChild.toObjectId().getName() + "\n",
+ PacketLineIn.end());
+ PacketLineIn pckIn = new PacketLineIn(recvStream);
+ assertThat(pckIn.readString(), is("acknowledgments"));
+ assertThat(Arrays.asList(pckIn.readString()),
+ hasItems("ACK " + parent.toObjectId().getName()));
+ assertTrue(PacketLineIn.isEnd(pckIn.readString()));
+ }
+
+ @Test
+ public void testV2FetchWithWaitForDoneOnlyDoesNegotiationAndNothingToAck()
+ throws Exception {
+ String commonInBlob = "abcdefghijklmnopqrstuvwxyz";
+
+ RevCommit localParent = null;
+ RevCommit localChild = null;
+ try (TestRepository<InMemoryRepository> local = new TestRepository<>(
+ client)) {
+ RevBlob localParentBlob = local.blob(commonInBlob + "a");
+ localParent = local
+ .commit(local.tree(local.file("foo", localParentBlob)));
+ RevBlob localChildBlob = local.blob(commonInBlob + "b");
+ localChild = local.commit(
+ local.tree(local.file("foo", localChildBlob)), localParent);
+ local.update("branch1", localChild);
+ }
+
+ ByteArrayInputStream recvStream = uploadPackV2("command=fetch\n",
+ PacketLineIn.delimiter(), "wait-for-done\n",
+ "have " + localParent.toObjectId().getName() + "\n",
+ "have " + localChild.toObjectId().getName() + "\n",
+ PacketLineIn.end());
+ PacketLineIn pckIn = new PacketLineIn(recvStream);
+ assertThat(pckIn.readString(), is("acknowledgments"));
+ assertThat(pckIn.readString(), is("NAK"));
+ assertTrue(PacketLineIn.isEnd(pckIn.readString()));
+ }
+
+ @Test
public void testV2FetchThinPack() throws Exception {
String commonInBlob = "abcdefghijklmnopqrstuvwxyz";
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/ssh/OpenSshConfigFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/ssh/OpenSshConfigFile.java
index 6fbb4c5..228c25f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/ssh/OpenSshConfigFile.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/ssh/OpenSshConfigFile.java
@@ -482,12 +482,18 @@
private final List<String> patterns;
- // Constructor used to build the merged entry; never matches anything
- HostEntry() {
+ /**
+ * Constructor used to build the merged entry; never matches anything
+ */
+ public HostEntry() {
this.patterns = Collections.emptyList();
}
- HostEntry(List<String> patterns) {
+ /**
+ * @param patterns
+ * to be used in matching against host name.
+ */
+ public HostEntry(List<String> patterns) {
this.patterns = patterns;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV2Request.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV2Request.java
index ea63933..50fb9d2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV2Request.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV2Request.java
@@ -36,6 +36,8 @@
private final boolean doneReceived;
+ private final boolean waitForDone;
+
@NonNull
private final List<String> serverOptions;
@@ -50,7 +52,8 @@
@NonNull Set<ObjectId> clientShallowCommits, int deepenSince,
@NonNull List<String> deepenNotRefs, int depth,
@NonNull FilterSpec filterSpec,
- boolean doneReceived, @NonNull Set<String> clientCapabilities,
+ boolean doneReceived, boolean waitForDone,
+ @NonNull Set<String> clientCapabilities,
@Nullable String agent, @NonNull List<String> serverOptions,
boolean sidebandAll, @NonNull List<String> packfileUriProtocols) {
super(wantIds, depth, clientShallowCommits, filterSpec,
@@ -59,6 +62,7 @@
this.peerHas = requireNonNull(peerHas);
this.wantedRefs = requireNonNull(wantedRefs);
this.doneReceived = doneReceived;
+ this.waitForDone = waitForDone;
this.serverOptions = requireNonNull(serverOptions);
this.sidebandAll = sidebandAll;
this.packfileUriProtocols = packfileUriProtocols;
@@ -90,7 +94,14 @@
}
/**
- * Options received in server-option lines. The caller can choose to act on
+ * @return true if the request had a "wait-for-done" line
+ */
+ boolean wasWaitForDoneReceived() {
+ return waitForDone;
+ }
+
+ /**
+ * Options received in server-option lines. The caller can choose to act on
* these in an application-specific way
*
* @return Immutable list of server options received in the request
@@ -141,6 +152,8 @@
boolean doneReceived;
+ boolean waitForDone;
+
@Nullable
String agent;
@@ -280,6 +293,16 @@
}
/**
+ * Mark that the "wait-for-done" line has been received.
+ *
+ * @return this builder
+ */
+ Builder setWaitForDone() {
+ waitForDone = true;
+ return this;
+ }
+
+ /**
* Value of an agent line received after the command and before the
* arguments. E.g. "agent=a.b.c/1.0" should set "a.b.c/1.0".
*
@@ -328,7 +351,7 @@
FetchV2Request build() {
return new FetchV2Request(peerHas, wantedRefs, wantIds,
clientShallowCommits, deepenSince, deepenNotRefs,
- depth, filterSpec, doneReceived, clientCapabilities,
+ depth, filterSpec, doneReceived, waitForDone, clientCapabilities,
agent, Collections.unmodifiableList(serverOptions),
sidebandAll,
Collections.unmodifiableList(packfileUriProtocols));
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java
index 36fce7a..0480971 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java
@@ -149,6 +149,13 @@
public static final String OPTION_SIDEBAND_ALL = "sideband-all"; //$NON-NLS-1$
/**
+ * The server waits for client to send "done" before sending any packs back.
+ *
+ * @since 5.12
+ */
+ public static final String OPTION_WAIT_FOR_DONE = "wait-for-done"; //$NON-NLS-1$
+
+ /**
* The client supports atomic pushes. If this option is used, the server
* will update all refs within one atomic transaction.
*
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 faccc25..92f0133 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2Parser.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2Parser.java
@@ -19,6 +19,7 @@
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDEBAND_ALL;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDE_BAND_64K;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_THIN_PACK;
+import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_WAIT_FOR_DONE;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_WANT_REF;
import java.io.IOException;
@@ -123,6 +124,8 @@
reqBuilder.addPeerHas(ObjectId.fromString(line2.substring(5)));
} else if (line2.equals("done")) { //$NON-NLS-1$
reqBuilder.setDoneReceived();
+ } else if (line2.equals(OPTION_WAIT_FOR_DONE)) {
+ reqBuilder.setWaitForDone();
} else if (line2.equals(OPTION_THIN_PACK)) {
reqBuilder.addClientCapability(OPTION_THIN_PACK);
} else if (line2.equals(OPTION_NO_PROGRESS)) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransferConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransferConfig.java
index 83ffd41..c1d630c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransferConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransferConfig.java
@@ -120,7 +120,10 @@
private final boolean allowReachableSha1InWant;
private final boolean allowFilter;
private final boolean allowSidebandAll;
+
private final boolean advertiseSidebandAll;
+ private final boolean advertiseWaitForDone;
+
final @Nullable ProtocolVersion protocolVersion;
final String[] hideRefs;
@@ -206,6 +209,8 @@
"uploadpack", "allowsidebandall", false);
advertiseSidebandAll = rc.getBoolean("uploadpack",
"advertisesidebandall", false);
+ advertiseWaitForDone = rc.getBoolean("uploadpack",
+ "advertisewaitfordone", false);
}
/**
@@ -305,6 +310,14 @@
}
/**
+ * @return true to advertise wait-for-done all to the clients
+ * @since 5.12
+ */
+ public boolean isAdvertiseWaitForDone() {
+ return advertiseWaitForDone;
+ }
+
+ /**
* Get {@link org.eclipse.jgit.transport.RefFilter} respecting configured
* hidden refs.
*
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 7f1ddaa..ecf1751 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
@@ -33,6 +33,7 @@
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDE_BAND;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDE_BAND_64K;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_THIN_PACK;
+import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_WAIT_FOR_DONE;
import static org.eclipse.jgit.transport.GitProtocolConstants.VERSION_2_REQUEST;
import static org.eclipse.jgit.util.RefMap.toRefMap;
@@ -1192,9 +1193,10 @@
walk.assumeShallow(req.getClientShallowCommits());
if (req.wasDoneReceived()) {
- processHaveLines(req.getPeerHas(), ObjectId.zeroId(),
+ processHaveLines(
+ req.getPeerHas(), ObjectId.zeroId(),
new PacketLineOut(NullOutputStream.INSTANCE, false),
- accumulator);
+ accumulator, req.wasWaitForDoneReceived() ? Option.WAIT_FOR_DONE : Option.NONE);
} else {
pckOut.writeString(
GitProtocolConstants.SECTION_ACKNOWLEDGMENTS + '\n');
@@ -1205,8 +1207,8 @@
}
processHaveLines(req.getPeerHas(), ObjectId.zeroId(),
new PacketLineOut(NullOutputStream.INSTANCE, false),
- accumulator);
- if (okToGiveUp()) {
+ accumulator, Option.NONE);
+ if (!req.wasWaitForDoneReceived() && okToGiveUp()) {
pckOut.writeString("ready\n"); //$NON-NLS-1$
} else if (commonBase.isEmpty()) {
pckOut.writeString("NAK\n"); //$NON-NLS-1$
@@ -1214,7 +1216,7 @@
sectionSent = true;
}
- if (req.wasDoneReceived() || okToGiveUp()) {
+ if (req.wasDoneReceived() || (!req.wasWaitForDoneReceived() && okToGiveUp())) {
if (mayHaveShallow) {
if (sectionSent)
pckOut.writeDelim();
@@ -1312,6 +1314,9 @@
? OPTION_SIDEBAND_ALL + ' '
: "")
+ (cachedPackUriProvider != null ? "packfile-uris " : "")
+ + (transferConfig.isAdvertiseWaitForDone()
+ ? OPTION_WAIT_FOR_DONE + ' '
+ : "")
+ OPTION_SHALLOW);
caps.add(CAPABILITY_SERVER_OPTION);
return caps;
@@ -1656,7 +1661,7 @@
}
if (PacketLineIn.isEnd(line)) {
- last = processHaveLines(peerHas, last, pckOut, accumulator);
+ last = processHaveLines(peerHas, last, pckOut, accumulator, Option.NONE);
if (commonBase.isEmpty() || multiAck != MultiAck.OFF)
pckOut.writeString("NAK\n"); //$NON-NLS-1$
if (noDone && sentReady) {
@@ -1671,7 +1676,7 @@
peerHas.add(ObjectId.fromString(line.substring(5)));
accumulator.haves++;
} else if (line.equals("done")) { //$NON-NLS-1$
- last = processHaveLines(peerHas, last, pckOut, accumulator);
+ last = processHaveLines(peerHas, last, pckOut, accumulator, Option.NONE);
if (commonBase.isEmpty())
pckOut.writeString("NAK\n"); //$NON-NLS-1$
@@ -1687,8 +1692,14 @@
}
}
+ private enum Option {
+ WAIT_FOR_DONE,
+ NONE;
+ }
+
private ObjectId processHaveLines(List<ObjectId> peerHas, ObjectId last,
- PacketLineOut out, PackStatistics.Accumulator accumulator)
+ PacketLineOut out, PackStatistics.Accumulator accumulator,
+ Option option)
throws IOException {
preUploadHook.onBeginNegotiateRound(this, wantIds, peerHas.size());
if (wantAll.isEmpty() && !wantIds.isEmpty())
@@ -1754,6 +1765,18 @@
// create a pack at this point, let the client know so it stops
// telling us about its history.
//
+ if (option != Option.WAIT_FOR_DONE) {
+ sentReady = shouldGiveUp(peerHas, out, missCnt);
+ }
+
+ preUploadHook.onEndNegotiateRound(this, wantAll, haveCnt, missCnt, sentReady);
+ peerHas.clear();
+ return last;
+ }
+
+ private boolean shouldGiveUp(List<ObjectId> peerHas, PacketLineOut out, int missCnt)
+ throws IOException {
+ boolean sentReady = false;
boolean didOkToGiveUp = false;
if (0 < missCnt) {
for (int i = peerHas.size() - 1; i >= 0; i--) {
@@ -1765,10 +1788,12 @@
case OFF:
break;
case CONTINUE:
- out.writeString("ACK " + id.name() + " continue\n"); //$NON-NLS-1$ //$NON-NLS-2$
+ out.writeString(
+ "ACK " + id.name() + " continue\n"); //$NON-NLS-1$ //$NON-NLS-2$
break;
case DETAILED:
- out.writeString("ACK " + id.name() + " ready\n"); //$NON-NLS-1$ //$NON-NLS-2$
+ out.writeString(
+ "ACK " + id.name() + " ready\n"); //$NON-NLS-1$ //$NON-NLS-2$
sentReady = true;
break;
}
@@ -1778,15 +1803,14 @@
}
}
- if (multiAck == MultiAck.DETAILED && !didOkToGiveUp && okToGiveUp()) {
+ if (multiAck == MultiAck.DETAILED && !didOkToGiveUp
+ && okToGiveUp()) {
ObjectId id = peerHas.get(peerHas.size() - 1);
out.writeString("ACK " + id.name() + " ready\n"); //$NON-NLS-1$ //$NON-NLS-2$
sentReady = true;
}
- preUploadHook.onEndNegotiateRound(this, wantAll, haveCnt, missCnt, sentReady);
- peerHas.clear();
- return last;
+ return sentReady;
}
private void parseWants(PackStatistics.Accumulator accumulator) throws IOException {