diff options
author | Shawn Pearce <sop@google.com> | 2014-11-07 22:04:33 -0500 |
---|---|---|
committer | Gerrit Code Review @ Eclipse.org <gerrit@eclipse.org> | 2014-11-07 22:04:34 -0500 |
commit | cba306096967a3cd85f76838b1efd5e9799cb0c0 (patch) | |
tree | da6a8bae27f41414f635feb01966e81fc60d68f6 | |
parent | 8456927b1bbb344cc6a768b1f491dddd50d4ce7c (diff) | |
parent | 88c1b82e7a27a5e25c9be9cba246f076825da4c6 (diff) | |
download | jgit-cba306096967a3cd85f76838b1efd5e9799cb0c0.tar.gz jgit-cba306096967a3cd85f76838b1efd5e9799cb0c0.zip |
Merge "Implement atomic refs update, if possible by database"
6 files changed, 45 insertions, 0 deletions
diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties index 45021e847c..524aa3e6ac 100644 --- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties +++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties @@ -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} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java index f2a1b948cc..dd1be0d5f7 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java @@ -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; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java index 682cac162c..0458ac4911 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java @@ -197,6 +197,14 @@ public abstract class RefDatabase { } /** + * @return if the database performs {@code newBatchUpdate()} as an atomic + * transaction. + */ + public boolean performsAtomicTransactions() { + return false; + } + + /** * Read a single reference. * <p> * Aside from taking advantage of {@link #SEARCH_PATH}, this method may be 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 72c1697593..475ba35283 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseReceivePack.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseReceivePack.java @@ -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. * 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 c0a70d0437..43fd07944c 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java @@ -130,6 +130,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. * * @since 3.2 diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java index 4d931dd5df..e5eb822418 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java @@ -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(); |