private void markReachable(final Set<ObjectId> have, final int maxTime) throws IOException { for (final Ref r : local.getAllRefs().values()) { try { final RevCommit o = walk.parseCommit(r.getObjectId()); o.add(REACHABLE); reachableCommits.add(o); } catch (IOException readError) { // If we cannot read the value of the ref skip it. } } for (final ObjectId id : have) { try { final RevCommit o = walk.parseCommit(id); o.add(REACHABLE); reachableCommits.add(o); } catch (IOException readError) { // If we cannot read the value of the ref skip it. } } if (maxTime > 0) { // Mark reachable commits until we reach maxTime. These may // wind up later matching up against things we want and we // can avoid asking for something we already happen to have. // final Date maxWhen = new Date(maxTime * 1000L); walk.sort(RevSort.COMMIT_TIME_DESC); walk.markStart(reachableCommits); walk.setRevFilter(CommitTimeRevFilter.after(maxWhen)); for (; ; ) { final RevCommit c = walk.next(); if (c == null) break; if (c.has(ADVERTISED) && !c.has(COMMON)) { // This is actually going to be a common commit, but // our peer doesn't know that fact yet. // c.add(COMMON); c.carry(COMMON); reachableCommits.add(c); } } } }
private void verifyPrerequisites() throws TransportException { if (prereqs.isEmpty()) return; final RevWalk rw = new RevWalk(local); final RevFlag PREREQ = rw.newFlag("PREREQ"); final RevFlag SEEN = rw.newFlag("SEEN"); final List<ObjectId> missing = new ArrayList<ObjectId>(); final List<RevObject> commits = new ArrayList<RevObject>(); for (final ObjectId p : prereqs) { try { final RevCommit c = rw.parseCommit(p); if (!c.has(PREREQ)) { c.add(PREREQ); commits.add(c); } } catch (MissingObjectException notFound) { missing.add(p); } catch (IOException err) { throw new TransportException(uri, "Cannot read commit " + p.name(), err); } } if (!missing.isEmpty()) throw new MissingBundlePrerequisiteException(uri, missing); for (final Ref r : local.getAllRefs().values()) { try { rw.markStart(rw.parseCommit(r.getObjectId())); } catch (IOException readError) { // If we cannot read the value of the ref skip it. } } int remaining = commits.size(); try { RevCommit c; while ((c = rw.next()) != null) { if (c.has(PREREQ)) { c.add(SEEN); if (--remaining == 0) break; } } } catch (IOException err) { throw new TransportException(uri, "Cannot read object", err); } if (remaining > 0) { for (final RevObject o : commits) { if (!o.has(SEEN)) missing.add(o); } throw new MissingBundlePrerequisiteException(uri, missing); } }
private void markCommon(final RevObject obj) { obj.add(COMMON); if (obj instanceof RevCommit) ((RevCommit) obj).carry(COMMON); }
private void negotiate(final 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 sendHaves = true; negotiateBegin(); while (sendHaves) { final RevCommit c = walk.next(); if (c == null) break; pckOut.writeString("have " + c.getId().name() + "\n"); 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) { // 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; } while (resultsPending > 0) { final PacketLineIn.AckNackResult anr; anr = pckIn.readACK(ackId); resultsPending--; if (anr == PacketLineIn.AckNackResult.NAK) { // More have lines are necessary to compute the // pack on the remote side. Keep doing that. // break; } if (anr == PacketLineIn.AckNackResult.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 = false; resultsPending = 0; receivedAck = true; sendHaves = false; break; } if (anr == PacketLineIn.AckNackResult.ACK_CONTINUE) { // 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)); receivedAck = true; receivedContinue = true; havesSinceLastContinue = 0; } if (monitor.isCancelled()) throw new CancelledException(); } if (receivedContinue && havesSinceLastContinue > MAX_HAVES) { // 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; } } // Tell the remote side we have run out of things to talk about. // if (monitor.isCancelled()) throw new CancelledException(); pckOut.writeString("done\n"); 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 = false; resultsPending++; } while (resultsPending > 0 || multiAck) { final PacketLineIn.AckNackResult anr; anr = pckIn.readACK(ackId); resultsPending--; if (anr == PacketLineIn.AckNackResult.ACK) break; // commit negotiation is finished. if (anr == PacketLineIn.AckNackResult.ACK_CONTINUE) { // There must be a normal ACK following this. // multiAck = true; } if (monitor.isCancelled()) throw new CancelledException(); } }