ReceivePack: support quiet capability

git-core has supported this for a long time; allowing clients to
avoid progress messages from the server if they are dumping to a
pipe instead of a tty.

Avoid the two progress monitors going on side-band and expose
isQuiet() method to allow hooks to also reduce their output if
this is sensible for them.

Change-Id: I1df7e38d16765446b441366500b017a90b8ff958
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseReceivePack.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseReceivePack.java
index a18df0d..eb77012 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseReceivePack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseReceivePack.java
@@ -46,6 +46,7 @@
 import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_ATOMIC;
 import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_DELETE_REFS;
 import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_OFS_DELTA;
+import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_QUIET;
 import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_REPORT_STATUS;
 import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_SIDE_BAND_64K;
 import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_AGENT;
@@ -176,6 +177,7 @@ public Set<String> getCapabilities() {
 	private boolean allowNonFastForwards;
 
 	private boolean allowOfsDelta;
+	private boolean allowQuiet = true;
 
 	/** Identity to record action as within the reflog. */
 	private PersonIdent refLogIdent;
@@ -234,6 +236,8 @@ public Set<String> getCapabilities() {
 	/** If {@link BasePackPushConnection#CAPABILITY_SIDE_BAND_64K} is enabled. */
 	private boolean sideBand;
 
+	private boolean quiet;
+
 	/** Lock around the received pack file, while updating refs. */
 	private PackLock packLock;
 
@@ -741,6 +745,45 @@ public boolean isSideBand() throws RequestNotYetReadException {
 	}
 
 	/**
+	 * @return true if clients may request avoiding noisy progress messages.
+	 * @since 4.0
+	 */
+	public boolean isAllowQuiet() {
+		return allowQuiet;
+	}
+
+	/**
+	 * Configure if clients may request the server skip noisy messages.
+	 *
+	 * @param allow
+	 *            true to allow clients to request quiet behavior; false to
+	 *            refuse quiet behavior and send messages anyway. This may be
+	 *            necessary if processing is slow and the client-server network
+	 *            connection can timeout.
+	 * @since 4.0
+	 */
+	public void setAllowQuiet(boolean allow) {
+		allowQuiet = allow;
+	}
+
+	/**
+	 * True if the client wants less verbose output.
+	 *
+	 * @return true if the client has requested the server to be less verbose.
+	 * @throws RequestNotYetReadException
+	 *             if the client's request has not yet been read from the wire,
+	 *             so we do not know if they expect side-band. Note that the
+	 *             client may have already written the request, it just has not
+	 *             been read.
+	 * @since 4.0
+	 */
+	public boolean isQuiet() throws RequestNotYetReadException {
+		if (enabledCapabilities == null)
+			throw new RequestNotYetReadException();
+		return quiet;
+	}
+
+	/**
 	 * Get the user agent of the client.
 	 * <p>
 	 * If the client is new enough to use {@code agent=} capability that value
@@ -969,6 +1012,8 @@ public void sendAdvertisedRefs(final RefAdvertiser adv)
 		adv.advertiseCapability(CAPABILITY_SIDE_BAND_64K);
 		adv.advertiseCapability(CAPABILITY_DELETE_REFS);
 		adv.advertiseCapability(CAPABILITY_REPORT_STATUS);
+		if (allowQuiet)
+			adv.advertiseCapability(CAPABILITY_QUIET);
 		if (pushCertificateParser.enabled())
 			adv.advertiseCapability(
 				pushCertificateParser.getAdvertiseNonce());
@@ -1046,6 +1091,7 @@ protected void recvCommands() throws IOException {
 	/** Enable capabilities based on a previously read capabilities line. */
 	protected void enableCapabilities() {
 		sideBand = isCapabilityEnabled(CAPABILITY_SIDE_BAND_64K);
+		quiet = allowQuiet && isCapabilityEnabled(CAPABILITY_QUIET);
 		if (sideBand) {
 			OutputStream out = rawOut;
 
@@ -1093,7 +1139,7 @@ private void receivePack() throws IOException {
 
 		ProgressMonitor receiving = NullProgressMonitor.INSTANCE;
 		ProgressMonitor resolving = NullProgressMonitor.INSTANCE;
-		if (sideBand)
+		if (sideBand && !quiet)
 			resolving = new SideBandProgressMonitor(msgOut);
 
 		try (ObjectInserter ins = db.newObjectInserter()) {
@@ -1130,7 +1176,7 @@ private void checkConnectivity() throws IOException {
 		ObjectIdSubclassMap<ObjectId> baseObjects = null;
 		ObjectIdSubclassMap<ObjectId> providedObjects = null;
 		ProgressMonitor checking = NullProgressMonitor.INSTANCE;
-		if (sideBand) {
+		if (sideBand && !quiet) {
 			SideBandProgressMonitor m = new SideBandProgressMonitor(msgOut);
 			m.setDelayStart(750, TimeUnit.MILLISECONDS);
 			checking = m;
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 8d9d2b7..8d2d554 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java
@@ -152,6 +152,13 @@ public class GitProtocolConstants {
 	public static final String CAPABILITY_ATOMIC = "atomic"; //$NON-NLS-1$
 
 	/**
+	 * The client expects less noise, e.g. no progress.
+	 *
+	 * @since 4.0
+	 */
+	public static final String CAPABILITY_QUIET = "quiet"; //$NON-NLS-1$
+
+	/**
 	 * The client expects a status report after the server processes the pack.
 	 *
 	 * @since 3.2