Inspired by the series[1], this implements the possibility to have atomic ref transactions. If the database supports atomic ref update capabilities, we'll advertise these. If the client wishes to use this feature, either all refs will be updated or none at all. [1] http://thread.gmane.org/gmane.comp.version-control.git/259019/focus=259024 Change-Id: I7b5d19c21f3b5557e41b9bcb5d359a65ff1a493d Signed-off-by: Stefan Beller <sbeller@google.com>tags/v3.6.0.201411121045-m1
@@ -500,6 +500,7 @@ tagOnRepoWithoutHEADCurrentlyNotSupported=Tag on repository without HEAD current | |||
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} |
@@ -556,6 +556,7 @@ public class JGitText extends TranslationBundle { | |||
/***/ public String tagAlreadyExists; | |||
/***/ public String tagNameInvalid; | |||
/***/ public String tagOnRepoWithoutHEADCurrentlyNotSupported; | |||
/***/ public String transactionAborted; | |||
/***/ public String theFactoryMustNotBeNull; | |||
/***/ public String timerAlreadyTerminated; | |||
/***/ public String topologicalSortRequired; |
@@ -196,6 +196,14 @@ public abstract class RefDatabase { | |||
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> |
@@ -43,6 +43,7 @@ | |||
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; | |||
@@ -908,6 +909,8 @@ public abstract class BaseReceivePack { | |||
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()); | |||
@@ -1251,6 +1254,23 @@ public abstract class BaseReceivePack { | |||
} | |||
} | |||
/** @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. | |||
* |
@@ -129,6 +129,14 @@ public class GitProtocolConstants { | |||
*/ | |||
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. | |||
* |
@@ -43,6 +43,7 @@ | |||
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; | |||
@@ -199,8 +200,14 @@ public class ReceivePack extends BaseReceivePack { | |||
} | |||
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(); |