theFactoryMustNotBeNull=The factory must not be null
timerAlreadyTerminated=Timer already terminated
topologicalSortRequired=Topological sort required.
+transactionAborted=transaction aborted
transportExceptionBadRef=Empty ref: {0}: {1}
transportExceptionEmptyRef=Empty ref: {0}
transportExceptionInvalid=Invalid {0} {1}:{2}
/***/ public String tagAlreadyExists;
/***/ public String tagNameInvalid;
/***/ public String tagOnRepoWithoutHEADCurrentlyNotSupported;
+ /***/ public String transactionAborted;
/***/ public String theFactoryMustNotBeNull;
/***/ public String timerAlreadyTerminated;
/***/ public String topologicalSortRequired;
return new BatchRefUpdate(this);
}
+ /**
+ * @return if the database performs {@code newBatchUpdate()} as an atomic
+ * transaction.
+ */
+ public boolean performsAtomicTransactions() {
+ return false;
+ }
+
/**
* Read a single reference.
* <p>
package org.eclipse.jgit.transport;
+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_REPORT_STATUS;
adv.advertiseCapability(CAPABILITY_SIDE_BAND_64K);
adv.advertiseCapability(CAPABILITY_DELETE_REFS);
adv.advertiseCapability(CAPABILITY_REPORT_STATUS);
+ if (db.getRefDatabase().performsAtomicTransactions())
+ adv.advertiseCapability(CAPABILITY_ATOMIC);
if (allowOfsDelta)
adv.advertiseCapability(CAPABILITY_OFS_DELTA);
adv.send(getAdvertisedOrDefaultRefs());
}
}
+ /** @return if any commands have been rejected so far. */
+ protected boolean anyRejects() {
+ for (ReceiveCommand cmd : commands) {
+ if (cmd.getResult() != Result.NOT_ATTEMPTED && cmd.getResult() != Result.OK)
+ return true;
+ }
+ return false;
+ }
+
+ /** Set the result to fail for any command that was not processed yet. */
+ protected void failPendingCommands() {
+ for (ReceiveCommand cmd : commands) {
+ if (cmd.getResult() == Result.NOT_ATTEMPTED)
+ cmd.setResult(Result.REJECTED_OTHER_REASON, JGitText.get().transactionAborted);
+ }
+ }
+
/**
* Filter the list of commands according to result.
*
*/
public static final String OPTION_ALLOW_TIP_SHA1_IN_WANT = "allow-tip-sha1-in-want"; //$NON-NLS-1$
+ /**
+ * The client supports atomic pushes. If this option is used, the server
+ * will update all refs within one atomic transaction.
+ *
+ * @since 3.6
+ */
+ public static final String CAPABILITY_ATOMIC = "atomic-push"; //$NON-NLS-1$
+
/**
* The client expects a status report after the server processes the pack.
*
package org.eclipse.jgit.transport;
+import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_ATOMIC;
import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_REPORT_STATUS;
import java.io.IOException;
}
if (unpackError == null) {
+ boolean atomic = isCapabilityEnabled(CAPABILITY_ATOMIC);
validateCommands();
+ if (atomic && anyRejects())
+ failPendingCommands();
+
preReceive.onPreReceive(this, filterCommands(Result.NOT_ATTEMPTED));
+ if (atomic && anyRejects())
+ failPendingCommands();
executeCommands();
}
unlockPack();