]> source.dussan.org Git - gitblit.git/commitdiff
Add integration strategy to merge tickes fast-forward or with commit.
authorFlorian Zschocke <florian.zschocke@cycos.com>
Sat, 21 Jun 2014 00:53:21 +0000 (02:53 +0200)
committerFlorian Zschocke <florian.zschocke@cycos.com>
Sun, 7 Sep 2014 20:40:17 +0000 (22:40 +0200)
Add the option to merge a ticket branch to the integration branch
only when it can be fast-forwarded, or
always with a merge commit, or
by fast-foward if possible, otherwise with a merge commit.

Adds a new property ticket.mergeType with the valid values
FAST_FOWARD_ONLY, MERGE_ALWAYS and MERGE_IF_NECESSARY.

Merging and canMerge were refactored to make use of a new
IntegrationStrategy class for each type of strategy.

src/main/distrib/data/gitblit.properties
src/main/java/com/gitblit/Constants.java
src/main/java/com/gitblit/git/PatchsetReceivePack.java
src/main/java/com/gitblit/manager/RepositoryManager.java
src/main/java/com/gitblit/models/RepositoryModel.java
src/main/java/com/gitblit/utils/JGitUtils.java
src/main/java/com/gitblit/wicket/pages/TicketPage.java

index f8d6c6d05024d8bffa558b6758c0f83eb72603b3..b29b1c7aaddd303835028aed97861239290a8c78 100644 (file)
@@ -579,6 +579,21 @@ tickets.acceptNewPatchsets = true
 # SINCE 1.4.0
 tickets.requireApproval = false
 
+# Default setting to control how patchsets are merged to the integration branch.
+# Valid values: 
+# MERGE_ALWAYS       - Always merge with a merge commit. Every ticket will show up as a branch,
+#                       even if it could have been fast-forward merged. This is the default.
+# MERGE_IF_NECESSARY - If possible, fast-forward the integration branch,
+#                       if not, merge with a merge commit.
+# FAST_FORWARD_ONLY  - Only merge when a fast-forward is possible. This produces a strictly
+#                       linear history of the integration branch.
+#
+# This setting can be overriden per-repository.
+#
+# RESTART REQUIRED
+# SINCE 1.7.0
+tickets.mergeType = MERGE_ALWAYS
+
 # The case-insensitive regular expression used to identify and close tickets on
 # push to the integration branch for commits that are NOT already referenced as
 # a patchset tip.
index 279d3c9217edf51cf383ce991b802571f663169f..1b1c24c5e1a85e3908d963c16378683718234d0f 100644 (file)
@@ -609,6 +609,37 @@ public class Constants {
                }\r
        }\r
 \r
+       /**\r
+        * The type of merge Gitblit will use when merging a ticket to the integration branch.\r
+        * <p>\r
+        * The default type is MERGE_ALWAYS.\r
+        * <p>\r
+        * This is modeled after the Gerrit SubmitType.\r
+        */\r
+       public static enum MergeType {\r
+               /** Allows a merge only if it can be fast-forward merged into the integration branch. */\r
+               FAST_FORWARD_ONLY,\r
+               /** Uses a fast-forward merge if possible, other wise a merge commit is created. */\r
+               MERGE_IF_NECESSARY,\r
+               // Future REBASE_IF_NECESSARY,\r
+               /** Always merge with a merge commit, even when a fast-forward would be possible. */\r
+               MERGE_ALWAYS,\r
+               // Future? CHERRY_PICK\r
+               ;\r
+\r
+               public static final MergeType DEFAULT_MERGE_TYPE = MERGE_ALWAYS;\r
+\r
+               public static MergeType fromName(String name) {\r
+                       for (MergeType type : values()) {\r
+                               if (type.name().equalsIgnoreCase(name)) {\r
+                                       return type;\r
+                               }\r
+                       }\r
+                       return DEFAULT_MERGE_TYPE;\r
+               }\r
+       }\r
+\r
+\r
        @Documented\r
        @Retention(RetentionPolicy.RUNTIME)\r
        public @interface Unused {\r
index 9e55524d1b4222d33ed6d13385cec576248578b9..7d81e61816246f05e108755193cd9a01347b8e5d 100644 (file)
@@ -574,7 +574,7 @@ public class PatchsetReceivePack extends GitblitReceivePack {
                }\r
 \r
                // ensure that the patchset can be cleanly merged right now\r
-               MergeStatus status = JGitUtils.canMerge(getRepository(), tipCommit.getName(), forBranch);\r
+               MergeStatus status = JGitUtils.canMerge(getRepository(), tipCommit.getName(), forBranch, repository.mergeType);\r
                switch (status) {\r
                case ALREADY_MERGED:\r
                        sendError("");\r
@@ -1180,6 +1180,7 @@ public class PatchsetReceivePack extends GitblitReceivePack {
                                getRepository(),\r
                                patchset.tip,\r
                                ticket.mergeTo,\r
+                               getRepositoryModel().mergeType,\r
                                committer,\r
                                message);\r
 \r
index 6a22db5969a5f64280bb2bf94ceed9b89ea23853..b967030f01d637b8ff316ac6df93ee71c98f7bcd 100644 (file)
@@ -63,6 +63,7 @@ import com.gitblit.Constants.AccessRestrictionType;
 import com.gitblit.Constants.AuthorizationControl;
 import com.gitblit.Constants.CommitMessageRenderer;
 import com.gitblit.Constants.FederationStrategy;
+import com.gitblit.Constants.MergeType;
 import com.gitblit.Constants.PermissionType;
 import com.gitblit.Constants.RegistrantType;
 import com.gitblit.GitBlitException;
@@ -878,6 +879,7 @@ public class RepositoryManager implements IRepositoryManager {
                        model.acceptNewTickets = getConfig(config, "acceptNewTickets", true);
                        model.requireApproval = getConfig(config, "requireApproval", settings.getBoolean(Keys.tickets.requireApproval, false));
                        model.mergeTo = getConfig(config, "mergeTo", null);
+                       model.mergeType = MergeType.fromName(getConfig(config, "mergeType", settings.getString(Keys.tickets.mergeType, null)));
                        model.useIncrementalPushTags = getConfig(config, "useIncrementalPushTags", false);
                        model.incrementalPushTagPrefix = getConfig(config, "incrementalPushTagPrefix", null);
                        model.allowForks = getConfig(config, "allowForks", true);
@@ -1519,6 +1521,13 @@ public class RepositoryManager implements IRepositoryManager {
                if (!StringUtils.isEmpty(repository.mergeTo)) {
                        config.setString(Constants.CONFIG_GITBLIT, null, "mergeTo", repository.mergeTo);
                }
+               if (repository.mergeType == null || repository.mergeType == MergeType.fromName(settings.getString(Keys.tickets.mergeType, null))) {
+                       // use default
+                       config.unset(Constants.CONFIG_GITBLIT, null, "mergeType");
+               } else {
+                       // override default
+                       config.setString(Constants.CONFIG_GITBLIT, null, "mergeType", repository.mergeType.name());
+               }
                config.setBoolean(Constants.CONFIG_GITBLIT, null, "useIncrementalPushTags", repository.useIncrementalPushTags);
                if (StringUtils.isEmpty(repository.incrementalPushTagPrefix) ||
                                repository.incrementalPushTagPrefix.equals(settings.getString(Keys.git.defaultIncrementalPushTagPrefix, "r"))) {
index a81c622af4d35f0d319b2802429e7447ea2aaad4..67ee1c7e0f4866a756fe930c045b239572762502 100644 (file)
@@ -28,6 +28,7 @@ import com.gitblit.Constants.AccessRestrictionType;
 import com.gitblit.Constants.AuthorizationControl;\r
 import com.gitblit.Constants.CommitMessageRenderer;\r
 import com.gitblit.Constants.FederationStrategy;\r
+import com.gitblit.Constants.MergeType;\r
 import com.gitblit.utils.ArrayUtils;\r
 import com.gitblit.utils.ModelUtils;\r
 import com.gitblit.utils.StringUtils;\r
@@ -89,6 +90,7 @@ public class RepositoryModel implements Serializable, Comparable<RepositoryModel
        public boolean acceptNewTickets;\r
        public boolean requireApproval;
        public String mergeTo;\r
+       public MergeType mergeType;\r
 \r
        public transient boolean isCollectingGarbage;\r
        public Date lastGC;\r
@@ -111,6 +113,7 @@ public class RepositoryModel implements Serializable, Comparable<RepositoryModel
                this.isBare = true;\r
                this.acceptNewTickets = true;\r
                this.acceptNewPatchsets = true;\r
+               this.mergeType = MergeType.DEFAULT_MERGE_TYPE;\r
 \r
                addOwner(owner);\r
        }\r
index da51ea98695e6bea4e88e02d444987000a390a09..7d7ef6d270d349918b68a5ad704c3907cf40e500 100644 (file)
@@ -84,6 +84,7 @@ import org.eclipse.jgit.util.FS;
 import org.slf4j.Logger;\r
 import org.slf4j.LoggerFactory;\r
 \r
+import com.gitblit.Constants.MergeType;\r
 import com.gitblit.GitBlitException;\r
 import com.gitblit.models.GitNote;\r
 import com.gitblit.models.PathModel;\r
@@ -2272,42 +2273,22 @@ public class JGitUtils {
        public static enum MergeStatus {
                NOT_MERGEABLE, FAILED, ALREADY_MERGED, MERGEABLE, MERGED;
        }
-
+\r
+\r
        /**
         * Determines if we can cleanly merge one branch into another.  Returns true
         * if we can merge without conflict, otherwise returns false.
         *
         * @param repository
         * @param src
-        * @param toBranch
+        * @param toBranch\r
+        * @param mergeType\r
+        *                              Defines the integration strategy to use for merging.
         * @return true if we can merge without conflict
         */
-       public static MergeStatus canMerge(Repository repository, String src, String toBranch) {
-               RevWalk revWalk = null;
-               try {
-                       revWalk = new RevWalk(repository);
-                       RevCommit branchTip = revWalk.lookupCommit(repository.resolve(toBranch));
-                       RevCommit srcTip = revWalk.lookupCommit(repository.resolve(src));
-                       if (revWalk.isMergedInto(srcTip, branchTip)) {
-                               // already merged
-                               return MergeStatus.ALREADY_MERGED;
-                       } else if (revWalk.isMergedInto(branchTip, srcTip)) {
-                               // fast-forward
-                               return MergeStatus.MERGEABLE;
-                       }
-                       RecursiveMerger merger = (RecursiveMerger) MergeStrategy.RECURSIVE.newMerger(repository, true);
-                       boolean canMerge = merger.merge(branchTip, srcTip);
-                       if (canMerge) {
-                               return MergeStatus.MERGEABLE;
-                       }
-               } catch (IOException e) {
-                       LOGGER.error("Failed to determine canMerge", e);
-               } finally {\r
-                       if (revWalk != null) {
-                               revWalk.release();\r
-                       }
-               }
-               return MergeStatus.NOT_MERGEABLE;
+       public static MergeStatus canMerge(Repository repository, String src, String toBranch, MergeType mergeType) {\r
+               IntegrationStrategy strategy = IntegrationStrategyFactory.create(mergeType, repository, src, toBranch);\r
+               return strategy.canMerge();
        }
 
 
@@ -2327,79 +2308,307 @@ public class JGitUtils {
         *
         * @param repository
         * @param src
-        * @param toBranch
+        * @param toBranch\r
+        * @param mergeType 
+        *                              Defines the integration strategy to use for merging.\r
         * @param committer
         * @param message
         * @return the merge result
         */
-       public static MergeResult merge(Repository repository, String src, String toBranch,
+       public static MergeResult merge(Repository repository, String src, String toBranch, MergeType mergeType,
                        PersonIdent committer, String message) {
 
                if (!toBranch.startsWith(Constants.R_REFS)) {
                        // branch ref doesn't start with ref, assume this is a branch head
                        toBranch = Constants.R_HEADS + toBranch;
                }
-
-               RevWalk revWalk = null;
-               try {
-                       revWalk = new RevWalk(repository);
-                       RevCommit branchTip = revWalk.lookupCommit(repository.resolve(toBranch));
-                       RevCommit srcTip = revWalk.lookupCommit(repository.resolve(src));
-                       if (revWalk.isMergedInto(srcTip, branchTip)) {
-                               // already merged
-                               return new MergeResult(MergeStatus.ALREADY_MERGED, null);
-                       }
-                       RecursiveMerger merger = (RecursiveMerger) MergeStrategy.RECURSIVE.newMerger(repository, true);
-                       boolean merged = merger.merge(branchTip, srcTip);
-                       if (merged) {
-                               // create a merge commit and a reference to track the merge commit
-                               ObjectId treeId = merger.getResultTreeId();
-                               ObjectInserter odi = repository.newObjectInserter();
-                               try {
-                                       // Create a commit object
-                                       CommitBuilder commitBuilder = new CommitBuilder();
-                                       commitBuilder.setCommitter(committer);
-                                       commitBuilder.setAuthor(committer);
-                                       commitBuilder.setEncoding(Constants.CHARSET);
-                                       if (StringUtils.isEmpty(message)) {
-                                               message = MessageFormat.format("merge {0} into {1}", srcTip.getName(), branchTip.getName());
-                                       }
-                                       commitBuilder.setMessage(message);
-                                       commitBuilder.setParentIds(branchTip.getId(), srcTip.getId());
-                                       commitBuilder.setTreeId(treeId);
-
-                                       // Insert the merge commit into the repository
-                                       ObjectId mergeCommitId = odi.insert(commitBuilder);
-                                       odi.flush();
-
-                                       // set the merge ref to the merge commit
-                                       RevCommit mergeCommit = revWalk.parseCommit(mergeCommitId);
-                                       RefUpdate mergeRefUpdate = repository.updateRef(toBranch);
-                                       mergeRefUpdate.setNewObjectId(mergeCommitId);
-                                       mergeRefUpdate.setRefLogMessage("commit: " + mergeCommit.getShortMessage(), false);
-                                       RefUpdate.Result rc = mergeRefUpdate.update();
-                                       switch (rc) {
-                                       case FAST_FORWARD:
-                                               // successful, clean merge
-                                               break;\r
-                                       default:
-                                               throw new GitBlitException(MessageFormat.format("Unexpected result \"{0}\" when merging commit {1} into {2} in {3}",
-                                                               rc.name(), srcTip.getName(), branchTip.getName(), repository.getDirectory()));
-                                       }
-
-                                       // return the merge commit id
-                                       return new MergeResult(MergeStatus.MERGED, mergeCommitId.getName());
-                               } finally {
-                                       odi.release();
-                               }
-                       }
-               } catch (IOException e) {
-                       LOGGER.error("Failed to merge", e);
-               } finally {\r
-                       if (revWalk != null) {
-                               revWalk.release();\r
-                       }
-               }
-               return new MergeResult(MergeStatus.FAILED, null);
+\r
+               IntegrationStrategy strategy = IntegrationStrategyFactory.create(mergeType, repository, src, toBranch);\r
+               MergeResult mergeResult = strategy.merge(committer, message);\r
+\r
+               if (mergeResult.status != MergeStatus.MERGED) {\r
+                       return mergeResult;\r
+               }\r
+\r
+               try {\r
+                       // Update the integration branch ref\r
+                       RefUpdate mergeRefUpdate = repository.updateRef(toBranch);\r
+                       mergeRefUpdate.setNewObjectId(strategy.getMergeCommit());\r
+                       mergeRefUpdate.setRefLogMessage(strategy.getRefLogMessage(), false);\r
+                       mergeRefUpdate.setExpectedOldObjectId(strategy.branchTip);\r
+                       RefUpdate.Result rc = mergeRefUpdate.update();\r
+                       switch (rc) {\r
+                       case FAST_FORWARD:\r
+                               // successful, clean merge\r
+                               break;\r
+                       default:\r
+                               mergeResult = new MergeResult(MergeStatus.FAILED, null);\r
+                               throw new GitBlitException(MessageFormat.format("Unexpected result \"{0}\" when {1} in {2}",\r
+                                               rc.name(), strategy.getOperationMessage(), repository.getDirectory()));\r
+                       }\r
+               } catch (IOException e) {\r
+                       LOGGER.error("Failed to merge", e);\r
+               }\r
+\r
+               return mergeResult;
+       }\r
+\r
+\r
+       private static abstract class IntegrationStrategy {\r
+               Repository repository;\r
+               String src;\r
+               String toBranch;\r
+\r
+               RevWalk revWalk;\r
+               RevCommit branchTip;\r
+               RevCommit srcTip;\r
+\r
+               RevCommit mergeCommit;\r
+               String refLogMessage;\r
+               String operationMessage;\r
+\r
+               RevCommit getMergeCommit() {\r
+                       return mergeCommit;\r
+               }\r
+\r
+               String getRefLogMessage() {\r
+                       return refLogMessage;\r
+               }\r
+\r
+               String getOperationMessage() {\r
+                       return operationMessage;\r
+               }\r
+\r
+               IntegrationStrategy(Repository repository, String src, String toBranch) {\r
+                       this.repository = repository;\r
+                       this.src = src;\r
+                       this.toBranch = toBranch;\r
+               }\r
+\r
+               void prepare() throws IOException {\r
+                       if (revWalk == null) revWalk = new RevWalk(repository);\r
+                       branchTip = revWalk.lookupCommit(repository.resolve(toBranch));\r
+                       srcTip = revWalk.lookupCommit(repository.resolve(src));\r
+               }\r
+\r
+\r
+               abstract MergeStatus _canMerge() throws IOException;\r
+\r
+\r
+               MergeStatus canMerge() {\r
+                       try {\r
+                               prepare();\r
+                               if (revWalk.isMergedInto(srcTip, branchTip)) {\r
+                                       // already merged\r
+                                       return MergeStatus.ALREADY_MERGED;\r
+                               }\r
+                               // determined by specific integration strategy\r
+                               return _canMerge();\r
+\r
+                       } catch (IOException e) {\r
+                               LOGGER.error("Failed to determine canMerge", e);\r
+                       } finally {\r
+                               if (revWalk != null) {\r
+                                       revWalk.release();\r
+                               }\r
+                       }\r
+\r
+                       return MergeStatus.NOT_MERGEABLE;\r
+               }\r
+\r
+\r
+               abstract MergeResult _merge(PersonIdent committer, String message) throws IOException;\r
+\r
+\r
+               MergeResult merge(PersonIdent committer, String message) {\r
+                       try {\r
+                               prepare();\r
+                               if (revWalk.isMergedInto(srcTip, branchTip)) {\r
+                                       // already merged\r
+                                       return new MergeResult(MergeStatus.ALREADY_MERGED, null);\r
+                               }\r
+                               // determined by specific integration strategy\r
+                               return _merge(committer, message);\r
+\r
+                       } catch (IOException e) {\r
+                               LOGGER.error("Failed to merge", e);\r
+                       } finally {\r
+                               if (revWalk != null) {\r
+                                       revWalk.release();\r
+                               }\r
+                       }\r
+\r
+                       return new MergeResult(MergeStatus.FAILED, null);\r
+               }\r
+       }\r
+\r
+\r
+       private static class FastForwardOnly extends IntegrationStrategy {\r
+               FastForwardOnly(Repository repository, String src, String toBranch) {\r
+                       super(repository, src, toBranch);\r
+               }\r
+\r
+               @Override\r
+               MergeStatus _canMerge() throws IOException {\r
+                       if (revWalk.isMergedInto(branchTip, srcTip)) {\r
+                               // fast-forward\r
+                               return MergeStatus.MERGEABLE;\r
+                       }\r
+\r
+                       return MergeStatus.NOT_MERGEABLE;\r
+               }\r
+\r
+               @Override\r
+               MergeResult _merge(PersonIdent committer, String message) throws IOException {\r
+                       if (! revWalk.isMergedInto(branchTip, srcTip)) {\r
+                               // is not fast-forward\r
+                               return new MergeResult(MergeStatus.FAILED, null);\r
+                       }\r
+\r
+                       mergeCommit = srcTip;\r
+                       refLogMessage = "merge " + src + ": Fast-forward";\r
+                       MessageFormat.format("fast-forwarding {0} to commit {1}", srcTip.getName(), branchTip.getName());\r
+\r
+                       return new MergeResult(MergeStatus.MERGED, srcTip.getName());\r
+               }\r
        }
+\r
+       private static class MergeIfNecessary extends IntegrationStrategy {\r
+               MergeIfNecessary(Repository repository, String src, String toBranch) {\r
+                       super(repository, src, toBranch);\r
+               }\r
+\r
+               @Override\r
+               MergeStatus _canMerge() throws IOException {\r
+                       if (revWalk.isMergedInto(branchTip, srcTip)) {\r
+                               // fast-forward\r
+                               return MergeStatus.MERGEABLE;\r
+                       }\r
+\r
+                       RecursiveMerger merger = (RecursiveMerger) MergeStrategy.RECURSIVE.newMerger(repository, true);\r
+                       boolean canMerge = merger.merge(branchTip, srcTip);\r
+                       if (canMerge) {\r
+                               return MergeStatus.MERGEABLE;\r
+                       }\r
+\r
+                       return MergeStatus.NOT_MERGEABLE;\r
+               }\r
+\r
+               @Override\r
+               MergeResult _merge(PersonIdent committer, String message) throws IOException {\r
+                       if (revWalk.isMergedInto(branchTip, srcTip)) {\r
+                               // fast-forward\r
+                               mergeCommit = srcTip;\r
+                               refLogMessage = "merge " + src + ": Fast-forward";\r
+                               MessageFormat.format("fast-forwarding {0} to commit {1}", branchTip.getName(), srcTip.getName());\r
+\r
+                               return new MergeResult(MergeStatus.MERGED, srcTip.getName());\r
+                       }\r
+\r
+                       RecursiveMerger merger = (RecursiveMerger) MergeStrategy.RECURSIVE.newMerger(repository, true);\r
+                       boolean merged = merger.merge(branchTip, srcTip);\r
+                       if (merged) {\r
+                               // create a merge commit and a reference to track the merge commit\r
+                               ObjectId treeId = merger.getResultTreeId();\r
+                               ObjectInserter odi = repository.newObjectInserter();\r
+                               try {\r
+                                       // Create a commit object\r
+                                       CommitBuilder commitBuilder = new CommitBuilder();\r
+                                       commitBuilder.setCommitter(committer);\r
+                                       commitBuilder.setAuthor(committer);\r
+                                       commitBuilder.setEncoding(Constants.CHARSET);\r
+                                       if (StringUtils.isEmpty(message)) {\r
+                                               message = MessageFormat.format("merge {0} into {1}", srcTip.getName(), branchTip.getName());\r
+                                       }\r
+                                       commitBuilder.setMessage(message);\r
+                                       commitBuilder.setParentIds(branchTip.getId(), srcTip.getId());\r
+                                       commitBuilder.setTreeId(treeId);\r
+\r
+                                       // Insert the merge commit into the repository\r
+                                       ObjectId mergeCommitId = odi.insert(commitBuilder);\r
+                                       odi.flush();\r
+\r
+                                       mergeCommit = revWalk.parseCommit(mergeCommitId);\r
+                                       refLogMessage = "commit: " + mergeCommit.getShortMessage();\r
+                                       MessageFormat.format("merging commit {0} into {1}", srcTip.getName(), branchTip.getName());\r
+\r
+                                       // return the merge commit id\r
+                                       return new MergeResult(MergeStatus.MERGED, mergeCommitId.getName());\r
+                               } finally {\r
+                                       odi.release();\r
+                               }\r
+                       }\r
+                       return new MergeResult(MergeStatus.FAILED, null);\r
+               }\r
+       }\r
+\r
+       private static class MergeAlways extends IntegrationStrategy {\r
+               MergeAlways(Repository repository, String src, String toBranch) {\r
+                       super(repository, src, toBranch);\r
+               }\r
+\r
+               @Override\r
+               MergeStatus _canMerge() throws IOException {\r
+                       RecursiveMerger merger = (RecursiveMerger) MergeStrategy.RECURSIVE.newMerger(repository, true);\r
+                       boolean canMerge = merger.merge(branchTip, srcTip);\r
+                       if (canMerge) {\r
+                               return MergeStatus.MERGEABLE;\r
+                       }\r
+\r
+                       return MergeStatus.NOT_MERGEABLE;\r
+               }\r
+\r
+               @Override\r
+               MergeResult _merge(PersonIdent committer, String message) throws IOException {\r
+                       RecursiveMerger merger = (RecursiveMerger) MergeStrategy.RECURSIVE.newMerger(repository, true);\r
+                       boolean merged = merger.merge(branchTip, srcTip);\r
+                       if (merged) {\r
+                               // create a merge commit and a reference to track the merge commit\r
+                               ObjectId treeId = merger.getResultTreeId();\r
+                               ObjectInserter odi = repository.newObjectInserter();\r
+                               try {\r
+                                       // Create a commit object\r
+                                       CommitBuilder commitBuilder = new CommitBuilder();\r
+                                       commitBuilder.setCommitter(committer);\r
+                                       commitBuilder.setAuthor(committer);\r
+                                       commitBuilder.setEncoding(Constants.CHARSET);\r
+                                       if (StringUtils.isEmpty(message)) {\r
+                                               message = MessageFormat.format("merge {0} into {1}", srcTip.getName(), branchTip.getName());\r
+                                       }\r
+                                       commitBuilder.setMessage(message);\r
+                                       commitBuilder.setParentIds(branchTip.getId(), srcTip.getId());\r
+                                       commitBuilder.setTreeId(treeId);\r
+\r
+                                       // Insert the merge commit into the repository\r
+                                       ObjectId mergeCommitId = odi.insert(commitBuilder);\r
+                                       odi.flush();\r
+\r
+                                       mergeCommit = revWalk.parseCommit(mergeCommitId);\r
+                                       refLogMessage = "commit: " + mergeCommit.getShortMessage();\r
+                                       MessageFormat.format("merging commit {0} into {1}", srcTip.getName(), branchTip.getName());\r
+\r
+                                       // return the merge commit id\r
+                                       return new MergeResult(MergeStatus.MERGED, mergeCommitId.getName());\r
+                               } finally {\r
+                                       odi.release();\r
+                               }\r
+                       }\r
+\r
+                       return new MergeResult(MergeStatus.FAILED, null);\r
+               }\r
+       }\r
+\r
+       private static class IntegrationStrategyFactory {\r
+               static IntegrationStrategy create(MergeType mergeType, Repository repository, String src, String toBranch) {\r
+                       switch(mergeType) {\r
+                       case FAST_FORWARD_ONLY:\r
+                               return new FastForwardOnly(repository, src, toBranch);\r
+                       case MERGE_IF_NECESSARY:\r
+                               return new MergeIfNecessary(repository, src, toBranch);\r
+                       case MERGE_ALWAYS:\r
+                               return new MergeAlways(repository, src, toBranch);\r
+                       }\r
+                       return null;\r
+               }\r
+       }\r
 }\r
index ca1bf31032c955a7f60dfcb0ecd028e45252cf81..9360766dc57a64431130058b61f5c1e2f174beda 100644 (file)
@@ -1349,14 +1349,14 @@ public class TicketPage extends RepositoryPage {
 \r
                boolean allowMerge;\r
                if (repository.requireApproval) {\r
-                       // rpeository requires approval\r
+                       // repository requires approval\r
                        allowMerge = ticket.isOpen() && ticket.isApproved(patchset);\r
                } else {\r
-                       // vetos are binding\r
+                       // vetoes are binding\r
                        allowMerge = ticket.isOpen() && !ticket.isVetoed(patchset);\r
                }\r
 \r
-               MergeStatus mergeStatus = JGitUtils.canMerge(getRepository(), patchset.tip, ticket.mergeTo);\r
+               MergeStatus mergeStatus = JGitUtils.canMerge(getRepository(), patchset.tip, ticket.mergeTo, repository.mergeType);\r
                if (allowMerge) {\r
                        if (MergeStatus.MERGEABLE == mergeStatus) {\r
                                // patchset can be cleanly merged to integration branch OR has already been merged\r