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 20d6934069b2499c9b8c73f97cb6ea0adfcd1d9a..fe2afbe9b752b0c51ab085f41c7fdea640ebff52 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
@@ -103,6 +103,19 @@ public class UploadPack {
 	/** Timeout in seconds to wait for client interaction. */
 	private int timeout;
 
+	/**
+	 * Is the client connection a bi-directional socket or pipe?
+	 * <p>
+	 * If true, this class assumes it can perform multiple read and write cycles
+	 * with the client over the input and output streams. This matches the
+	 * functionality available with a standard TCP/IP connection, or a local
+	 * operating system or in-memory pipe.
+	 * <p>
+	 * If false, this class runs in a read everything then output results mode,
+	 * making it suitable for single round-trip systems RPCs such as HTTP.
+	 */
+	private boolean biDirectionalPipe = true;
+
 	/** Timer to manage {@link #timeout}. */
 	private InterruptTimer timer;
 
@@ -198,6 +211,27 @@ public void setTimeout(final int seconds) {
 		timeout = seconds;
 	}
 
+	/**
+	 * @return true if this class expects a bi-directional pipe opened between
+	 *         the client and itself. The default is true.
+	 */
+	public boolean isBiDirectionalPipe() {
+		return biDirectionalPipe;
+	}
+
+	/**
+	 * @param twoWay
+	 *            if true, this class will assume the socket is a fully
+	 *            bidirectional pipe between the two peers and takes advantage
+	 *            of that by first transmitting the known refs, then waiting to
+	 *            read commands. If false, this class assumes it must read the
+	 *            commands before writing output and does not perform the
+	 *            initial advertising.
+	 */
+	public void setBiDirectionalPipe(final boolean twoWay) {
+		biDirectionalPipe = twoWay;
+	}
+
 	/**
 	 * Execute the upload task on the socket.
 	 *
@@ -247,7 +281,19 @@ public void upload(final InputStream input, final OutputStream output,
 	}
 
 	private void service() throws IOException {
-		sendAdvertisedRefs();
+		if (biDirectionalPipe)
+			sendAdvertisedRefs();
+		else {
+			refs = db.getAllRefs();
+			for (Ref r : refs.values()) {
+				try {
+					walk.parseAny(r.getObjectId()).add(ADVERTISED);
+				} catch (IOException e) {
+					// Skip missing/corrupt objects
+				}
+			}
+		}
+
 		recvWants();
 		if (wantAll.isEmpty())
 			return;
@@ -259,8 +305,8 @@ else if (options.contains(OPTION_MULTI_ACK))
 		else
 			multiAck = MultiAck.OFF;
 
-		negotiate();
-		sendPack();
+		if (negotiate())
+			sendPack();
 	}
 
 	private void sendAdvertisedRefs() throws IOException {
@@ -336,7 +382,7 @@ else if (o instanceof RevTag) {
 		}
 	}
 
-	private void negotiate() throws IOException {
+	private boolean negotiate() throws IOException {
 		ObjectId last = ObjectId.zeroId();
 		for (;;) {
 			String line;
@@ -350,6 +396,9 @@ private void negotiate() throws IOException {
 				if (commonBase.isEmpty() || multiAck != MultiAck.OFF)
 					pckOut.writeString("NAK\n");
 				pckOut.flush();
+				if (!biDirectionalPipe)
+					return false;
+
 			} else if (line.startsWith("have ") && line.length() == 45) {
 				final ObjectId id = ObjectId.fromString(line.substring(5));
 				if (matchHave(id)) {
@@ -389,7 +438,8 @@ private void negotiate() throws IOException {
 
 				else if (multiAck != MultiAck.OFF)
 					pckOut.writeString("ACK " + last.name() + "\n");
-				break;
+
+				return true;
 
 			} else {
 				throw new PackProtocolException("expected have; got " + line);