]> source.dussan.org Git - jgit.git/commitdiff
Implement atomic refs update, if possible by database 97/36097/6
authorStefan Beller <sbeller@google.com>
Sat, 8 Nov 2014 02:51:18 +0000 (18:51 -0800)
committerStefan Beller <sbeller@google.com>
Sat, 8 Nov 2014 02:51:18 +0000 (18:51 -0800)
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>
org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java
org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseReceivePack.java
org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java
org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java

index 45021e847cd3c60bcec2ea451c34413af1b5f575..524aa3e6acc81696c990ae9ced6e3ef123a8288f 100644 (file)
@@ -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}
index f2a1b948cccbecc9b6afbbb81abcc5acfd39ae4d..dd1be0d5f7c0bc699e1e4e0625cf079f72e0ef6b 100644 (file)
@@ -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;
index 682cac162c089c310f66902daada03052a913672..0458ac4911b16cc0886f72719f887ef329f4c99e 100644 (file)
@@ -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>
index 72c1697593e8546ead3506fcb5f79a8de5d8d32f..475ba352835bf92539babd502534e99a7d19d05c 100644 (file)
@@ -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.
         *
index c0a70d0437bb77c14e3052aa3195d1144d727803..43fd07944cd0dae40e092e342a47d9780d098380 100644 (file)
@@ -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.
         *
index 4d931dd5df48ff58ee330b77e9049aa710a7a326..e5eb822418124b0fb951adf5d796d31603d91c20 100644 (file)
@@ -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();