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(); } }
private void recreateCommitGraph() throws IOException { final RevWalk rw = new RevWalk(db); final Map<ObjectId, ToRewrite> toRewrite = new HashMap<ObjectId, ToRewrite>(); List<ToRewrite> queue = new ArrayList<ToRewrite>(); final BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(graph), Constants.CHARSET)); try { String line; while ((line = br.readLine()) != null) { final String[] parts = line.split("[ \t]{1,}"); final ObjectId oldId = ObjectId.fromString(parts[0]); try { rw.parseCommit(oldId); // We have it already. Don't rewrite it. continue; } catch (MissingObjectException mue) { // Fall through and rewrite it. } final long time = Long.parseLong(parts[1]) * 1000L; final ObjectId[] parents = new ObjectId[parts.length - 2]; for (int i = 0; i < parents.length; i++) { parents[i] = ObjectId.fromString(parts[2 + i]); } final ToRewrite t = new ToRewrite(oldId, time, parents); toRewrite.put(oldId, t); queue.add(t); } } finally { br.close(); } pm.beginTask("Rewriting commits", queue.size()); final ObjectWriter ow = new ObjectWriter(db); final ObjectId emptyTree = ow.writeTree(new Tree(db)); final PersonIdent me = new PersonIdent("jgit rebuild-commitgraph", "rebuild-commitgraph@localhost"); while (!queue.isEmpty()) { final ListIterator<ToRewrite> itr = queue.listIterator(queue.size()); queue = new ArrayList<ToRewrite>(); REWRITE: while (itr.hasPrevious()) { final ToRewrite t = itr.previous(); final ObjectId[] newParents = new ObjectId[t.oldParents.length]; for (int k = 0; k < t.oldParents.length; k++) { final ToRewrite p = toRewrite.get(t.oldParents[k]); if (p != null) { if (p.newId == null) { // Must defer until after the parent is rewritten. queue.add(t); continue REWRITE; } else { newParents[k] = p.newId; } } else { // We have the old parent object. Use it. // newParents[k] = t.oldParents[k]; } } final Commit newc = new Commit(db); newc.setTreeId(emptyTree); newc.setAuthor(new PersonIdent(me, new Date(t.commitTime))); newc.setCommitter(newc.getAuthor()); newc.setParentIds(newParents); newc.setMessage("ORIGINAL " + t.oldId.name() + "\n"); t.newId = ow.writeCommit(newc); rewrites.put(t.oldId, t.newId); pm.update(1); } } pm.endTask(); }