in org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java [776:943]
private void negotiate(ProgressMonitor monitor) throws IOException,
CancelledException {
final MutableObjectId ackId = new MutableObjectId();
int resultsPending = 0;
int havesSent = 0;
int havesSinceLastContinue = 0;
boolean receivedContinue = false;
boolean receivedAck = false;
boolean receivedReady = false;
if (statelessRPC) {
state.writeTo(out, null);
}
negotiateBegin();
SEND_HAVES: for (;;) {
final RevCommit c = walk.next();
if (c == null) {
break SEND_HAVES;
}
ObjectId o = c.getId();
pckOut.writeString("have " + o.name() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
havesSent++;
havesSinceLastContinue++;
if ((31 & havesSent) != 0) {
// We group the have lines into blocks of 32, each marked
// with a flush (aka end). This one is within a block so
// continue with another have line.
//
continue;
}
if (monitor.isCancelled()) {
throw new CancelledException();
}
pckOut.end();
resultsPending++; // Each end will cause a result to come back.
if (havesSent == 32 && !statelessRPC) {
// On the first block we race ahead and try to send
// more of the second block while waiting for the
// remote to respond to our first block request.
// This keeps us one block ahead of the peer.
//
continue;
}
READ_RESULT: for (;;) {
final AckNackResult anr = pckIn.readACK(ackId);
switch (anr) {
case NAK:
// More have lines are necessary to compute the
// pack on the remote side. Keep doing that.
//
resultsPending--;
break READ_RESULT;
case ACK:
// The remote side is happy and knows exactly what
// to send us. There is no further negotiation and
// we can break out immediately.
//
multiAck = MultiAck.OFF;
resultsPending = 0;
receivedAck = true;
if (statelessRPC) {
state.writeTo(out, null);
}
break SEND_HAVES;
case ACK_CONTINUE:
case ACK_COMMON:
case ACK_READY:
// The server knows this commit (ackId). We don't
// need to send any further along its ancestry, but
// we need to continue to talk about other parts of
// our local history.
//
markCommon(walk.parseAny(ackId), anr, statelessRPC);
receivedAck = true;
receivedContinue = true;
havesSinceLastContinue = 0;
if (anr == AckNackResult.ACK_READY) {
receivedReady = true;
}
break;
}
if (monitor.isCancelled()) {
throw new CancelledException();
}
}
if (noDone && receivedReady) {
break SEND_HAVES;
}
if (statelessRPC) {
state.writeTo(out, null);
}
if ((receivedContinue && havesSinceLastContinue > MAX_HAVES)
|| havesSent >= maxHaves) {
// Our history must be really different from the remote's.
// We just sent a whole slew of have lines, and it did not
// recognize any of them. Avoid sending our entire history
// to them by giving up early.
//
break SEND_HAVES;
}
}
// Tell the remote side we have run out of things to talk about.
//
if (monitor.isCancelled()) {
throw new CancelledException();
}
if (!receivedReady || !noDone) {
// When statelessRPC is true we should always leave SEND_HAVES
// loop above while in the middle of a request. This allows us
// to just write done immediately.
//
pckOut.writeString("done\n"); //$NON-NLS-1$
pckOut.flush();
}
if (!receivedAck) {
// Apparently if we have never received an ACK earlier
// there is one more result expected from the done we
// just sent to the remote.
//
multiAck = MultiAck.OFF;
resultsPending++;
}
READ_RESULT: while (resultsPending > 0 || multiAck != MultiAck.OFF) {
final AckNackResult anr = pckIn.readACK(ackId);
resultsPending--;
switch (anr) {
case NAK:
// A NAK is a response to an end we queued earlier
// we eat it and look for another ACK/NAK message.
//
break;
case ACK:
// A solitary ACK at this point means the remote won't
// speak anymore, but is going to send us a pack now.
//
break READ_RESULT;
case ACK_CONTINUE:
case ACK_COMMON:
case ACK_READY:
// We will expect a normal ACK to break out of the loop.
//
multiAck = MultiAck.CONTINUE;
break;
}
if (monitor.isCancelled()) {
throw new CancelledException();
}
}
}