/** Push certificate associated with this update. */
private PushCertificate pushCert;
+ /** Whether updates should be atomic. */
+ private boolean atomic;
+
/**
* Initialize a new batch update.
*
protected BatchRefUpdate(RefDatabase refdb) {
this.refdb = refdb;
this.commands = new ArrayList<ReceiveCommand>();
+ this.atomic = refdb.performsAtomicTransactions();
}
/**
return refLogMessage == null;
}
+ /**
+ * Request that all updates in this batch be performed atomically.
+ * <p>
+ * When atomic updates are used, either all commands apply successfully, or
+ * none do. Commands that might have otherwise succeeded are rejected with
+ * {@code REJECTED_OTHER_REASON}.
+ * <p>
+ * This method only works if the underlying ref database supports atomic
+ * transactions, i.e. {@link RefDatabase#performsAtomicTransactions()} returns
+ * true. Calling this method with true if the underlying ref database does not
+ * support atomic transactions will cause all commands to fail with {@code
+ * REJECTED_OTHER_REASON}.
+ *
+ * @param atomic whether updates should be atomic.
+ * @return {@code this}
+ * @since 4.4
+ */
+ public BatchRefUpdate setAtomic(boolean atomic) {
+ this.atomic = atomic;
+ return this;
+ }
+
+ /**
+ * @return atomic whether updates should be atomic.
+ * @since 4.4
+ */
+ public boolean isAtomic() {
+ return atomic;
+ }
+
/**
* Set a push certificate associated with this update.
* <p>
* <p>
* The default implementation of this method performs a sequential reference
* update over each reference.
+ * <p>
+ * Implementations must respect the atomicity requirements of the underlying
+ * database as described in {@link #setAtomic(boolean)} and {@link
+ * RefDatabase#performsAtomicTransactions()}.
*
* @param walk
* a RevWalk to parse tags in case the storage system wants to
*/
public void execute(RevWalk walk, ProgressMonitor monitor)
throws IOException {
+
+ if (atomic && !refdb.performsAtomicTransactions()) {
+ for (ReceiveCommand c : commands) {
+ if (c.getResult() == NOT_ATTEMPTED) {
+ c.setResult(REJECTED_OTHER_REASON,
+ JGitText.get().atomicRefUpdatesNotSupported);
+ }
+ }
+ return;
+ }
+
monitor.beginTask(JGitText.get().updatingReferences, commands.size());
List<ReceiveCommand> commands2 = new ArrayList<ReceiveCommand>(
commands.size());
}
/**
- * @return if the database performs {@code newBatchUpdate()} as an atomic
- * transaction.
+ * Whether the database is capable of performing batch updates as atomic
+ * transactions.
+ * <p>
+ * If true, by default {@link BatchRefUpdate} instances will perform updates
+ * atomically, meaning either all updates will succeed, or all updates will
+ * fail. It is still possible to turn off this behavior on a per-batch basis
+ * by calling {@code update.setAtomic(false)}.
+ * <p>
+ * If false, {@link BatchRefUpdate} instances will never perform updates
+ * atomically, and calling {@code update.setAtomic(true)} will cause the
+ * entire batch to fail with {@code REJECTED_OTHER_REASON}.
+ * <p>
+ * This definition of atomicity is stronger than what is provided by
+ * {@link org.eclipse.jgit.transport.ReceivePack}. {@code ReceivePack} will
+ * attempt to reject all commands if it knows in advance some commands may
+ * fail, even if the storage layer does not support atomic transactions. Here,
+ * atomicity applies even in the case of unforeseeable errors.
+ *
+ * @return whether transactions are atomic by default.
* @since 3.6
*/
public boolean performsAtomicTransactions() {