]> source.dussan.org Git - jgit.git/commitdiff
Enhance MergeResult to report conflicts, etc 25/1225/5
authorChristian Halstrick <christian.halstrick@sap.com>
Thu, 19 Aug 2010 13:52:12 +0000 (15:52 +0200)
committerChris Aniszczyk <caniszczyk@gmail.com>
Thu, 19 Aug 2010 17:16:39 +0000 (12:16 -0500)
The MergeResult class is enhanced to report more data about a
three-way merge. Information about conflicts and the base, ours,
theirs commits can be retrived.

Change-Id: Iaaf41a1f4002b8fe3ddfa62dc73c787f363460c2
Signed-off-by: Christian Halstrick <christian.halstrick@sap.com>
Signed-off-by: Chris Aniszczyk <caniszczyk@gmail.com>
org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties
org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java
org.eclipse.jgit/src/org/eclipse/jgit/api/MergeResult.java

index a9878f8d297c8d21d343e584bc78121c79e08d61..faeb1d53f9c0221183cf9405c02013d9780fc2a0 100644 (file)
@@ -222,7 +222,7 @@ lockOnNotHeld=Lock on {0} not held.
 malformedpersonIdentString=Malformed PersonIdent string (no < was found): {0}
 mergeStrategyAlreadyExistsAsDefault=Merge strategy "{0}" already exists as a default strategy
 mergeStrategyDoesNotSupportHeads=merge strategy {0} does not support {1} heads to be merged into HEAD
-mergeUsingStrategyResultedInDescription=Merge using strategy {0} resulted in: {1}. {2}
+mergeUsingStrategyResultedInDescription=Merge of revisions {0} with base {1} using strategy {2} resulted in: {3}. {4}
 missingAccesskey=Missing accesskey.
 missingDeltaBase=delta base
 missingForwardImageInGITBinaryPatch=Missing forward-image in GIT binary patch
index 972aa618ad4e0131552278001c124faa7577105d..4d37c28e0a0a81e7362735a0a181e438c1b5f665 100644 (file)
@@ -114,7 +114,8 @@ public class MergeCommand extends GitCommand<MergeResult> {
                try {
                        Ref head = repo.getRef(Constants.HEAD);
                        if (head == null)
-                               throw new NoHeadException(JGitText.get().commitOnRepoWithoutHEADCurrentlyNotSupported);
+                               throw new NoHeadException(
+                                               JGitText.get().commitOnRepoWithoutHEADCurrentlyNotSupported);
                        StringBuilder refLogMessage = new StringBuilder("merge ");
 
                        // Check for FAST_FORWARD, ALREADY_UP_TO_DATE
@@ -134,7 +135,8 @@ public class MergeCommand extends GitCommand<MergeResult> {
                                RevCommit srcCommit = revWalk.lookupCommit(objectId);
                                if (revWalk.isMergedInto(srcCommit, headCommit)) {
                                        setCallable(false);
-                                       return new MergeResult(headCommit,
+                                       return new MergeResult(headCommit, srcCommit,
+                                                       new ObjectId[] { srcCommit, headCommit },
                                                        MergeStatus.ALREADY_UP_TO_DATE, mergeStrategy);
                                } else if (revWalk.isMergedInto(headCommit, srcCommit)) {
                                        // FAST_FORWARD detected: skip doing a real merge but only
@@ -143,11 +145,14 @@ public class MergeCommand extends GitCommand<MergeResult> {
                                        checkoutNewHead(revWalk, headCommit, srcCommit);
                                        updateHead(refLogMessage, srcCommit, head.getObjectId());
                                        setCallable(false);
-                                       return new MergeResult(srcCommit, MergeStatus.FAST_FORWARD,
-                                                       mergeStrategy);
+                                       return new MergeResult(srcCommit, headCommit,
+                                                       new ObjectId[] { srcCommit, headCommit },
+                                                       MergeStatus.FAST_FORWARD, mergeStrategy);
                                } else {
                                        return new MergeResult(
                                                        headCommit,
+                                                       null,
+                                                       new ObjectId[] { srcCommit, headCommit },
                                                        MergeResult.MergeStatus.NOT_SUPPORTED,
                                                        mergeStrategy,
                                                        JGitText.get().onlyAlreadyUpToDateAndFastForwardMergesAreAvailable);
@@ -164,7 +169,8 @@ public class MergeCommand extends GitCommand<MergeResult> {
        }
 
        private void checkoutNewHead(RevWalk revWalk, RevCommit headCommit,
-                       RevCommit newHeadCommit) throws IOException, CheckoutConflictException {
+                       RevCommit newHeadCommit) throws IOException,
+                       CheckoutConflictException {
                GitIndex index = repo.getIndex();
 
                File workDir = repo.getWorkTree();
@@ -184,8 +190,8 @@ public class MergeCommand extends GitCommand<MergeResult> {
                }
        }
 
-       private void updateHead(StringBuilder refLogMessage,
-                       ObjectId newHeadId, ObjectId oldHeadID) throws IOException,
+       private void updateHead(StringBuilder refLogMessage, ObjectId newHeadId,
+                       ObjectId oldHeadID) throws IOException,
                        ConcurrentRefUpdateException {
                RefUpdate refUpdate = repo.updateRef(Constants.HEAD);
                refUpdate.setNewObjectId(newHeadId);
@@ -221,8 +227,7 @@ public class MergeCommand extends GitCommand<MergeResult> {
 
        /**
         * @param commit
-        *            a reference to a commit which is merged with the current
-        *            head
+        *            a reference to a commit which is merged with the current head
         * @return {@code this}
         */
        public MergeCommand include(Ref commit) {
@@ -241,7 +246,8 @@ public class MergeCommand extends GitCommand<MergeResult> {
        }
 
        /**
-        * @param name a name given to the commit
+        * @param name
+        *            a name given to the commit
         * @param commit
         *            the Id of a commit which is merged with the current head
         * @return {@code this}
index a293ad0c959e5db1f6d2780c7083d8d73b66008d..6fcf2ee6d046c4b271f234b26a1c346417d1a963 100644 (file)
@@ -44,6 +44,7 @@
 package org.eclipse.jgit.api;
 
 import java.text.MessageFormat;
+import java.util.Map;
 
 import org.eclipse.jgit.JGitText;
 import org.eclipse.jgit.lib.ObjectId;
@@ -84,6 +85,12 @@ public class MergeResult {
                        }
                },
                /** */
+               CONFLICTING {
+                       public String toString() {
+                               return "Conflicting";
+                       }
+               },
+               /** */
                NOT_SUPPORTED {
                        public String toString() {
                                return "Not-yet-supported";
@@ -91,8 +98,14 @@ public class MergeResult {
                }
        }
 
+       private ObjectId[] mergedCommits;
+
+       private ObjectId base;
+
        private ObjectId newHead;
 
+       private Map<String, int[][]> conflicts;
+
        private MergeStatus mergeStatus;
 
        private String description;
@@ -100,26 +113,47 @@ public class MergeResult {
        private MergeStrategy mergeStrategy;
 
        /**
-        * @param newHead the object the head points at after the merge
-        * @param mergeStatus the status the merge resulted in
-        * @param mergeStrategy the used {@link MergeStrategy}
+        * @param newHead
+        *            the object the head points at after the merge
+        * @param base
+        *            the common base which was used to produce a content-merge. May
+        *            be <code>null</code> if the merge-result was produced without
+        *            computing a common base
+        * @param mergedCommits
+        *            all the commits which have been merged together
+        * @param mergeStatus
+        *            the status the merge resulted in
+        * @param mergeStrategy
+        *            the used {@link MergeStrategy}
         */
-       public MergeResult(ObjectId newHead, MergeStatus mergeStatus,
+       public MergeResult(ObjectId newHead, ObjectId base,
+                       ObjectId[] mergedCommits, MergeStatus mergeStatus,
                        MergeStrategy mergeStrategy) {
-               this.newHead = newHead;
-               this.mergeStatus = mergeStatus;
-               this.mergeStrategy = mergeStrategy;
+               this(newHead, base, mergedCommits, mergeStatus, mergeStrategy, null);
        }
 
        /**
-        * @param newHead the object the head points at after the merge
-        * @param mergeStatus the status the merge resulted in
-        * @param mergeStrategy the used {@link MergeStrategy}
-        * @param description a user friendly description of the merge result
+        * @param newHead
+        *            the object the head points at after the merge
+        * @param base
+        *            the common base which was used to produce a content-merge. May
+        *            be <code>null</code> if the merge-result was produced without
+        *            computing a common base
+        * @param mergedCommits
+        *            all the commits which have been merged together
+        * @param mergeStatus
+        *            the status the merge resulted in
+        * @param mergeStrategy
+        *            the used {@link MergeStrategy}
+        * @param description
+        *            a user friendly description of the merge result
         */
-       public MergeResult(ObjectId newHead, MergeStatus mergeStatus,
+       public MergeResult(ObjectId newHead, ObjectId base,
+                       ObjectId[] mergedCommits, MergeStatus mergeStatus,
                        MergeStrategy mergeStrategy, String description) {
                this.newHead = newHead;
+               this.mergedCommits = mergedCommits;
+               this.base = base;
                this.mergeStatus = mergeStatus;
                this.mergeStrategy = mergeStrategy;
                this.description = description;
@@ -139,12 +173,83 @@ public class MergeResult {
                return mergeStatus;
        }
 
+       /**
+        * @return all the commits which have been merged together
+        */
+       public ObjectId[] getMergedCommits() {
+               return mergedCommits;
+       }
+
+       /**
+        * @return base the common base which was used to produce a content-merge.
+        *         May be <code>null</code> if the merge-result was produced without
+        *         computing a common base
+        */
+       public ObjectId getBase() {
+               return base;
+       }
+
        @Override
        public String toString() {
+               boolean first = true;
+               StringBuilder commits = new StringBuilder();
+               for (ObjectId commit : mergedCommits) {
+                       if (!first)
+                               commits.append(", ");
+                       else
+                               first = false;
+                       commits.append(ObjectId.toString(commit));
+               }
                return MessageFormat.format(
                                JGitText.get().mergeUsingStrategyResultedInDescription,
-                               mergeStrategy.getName(), mergeStatus, (description == null ? ""
-                                               : ", " + description));
+                               commits, ObjectId.toString(base), mergeStrategy.getName(),
+                               mergeStatus, (description == null ? "" : ", " + description));
        }
 
+       /**
+        * @param conflicts
+        *            the conflicts to set
+        */
+       public void setConflicts(Map<String, int[][]> conflicts) {
+               this.conflicts = conflicts;
+       }
+
+       /**
+        * Returns information about the conflicts which occurred during a
+        * {@link MergeCommand}. The returned value maps the path of a conflicting
+        * file to a two-dimensional int-array of line-numbers telling where in the
+        * file conflict markers for which merged commit can be found.
+        * <p>
+        * If the returned value contains a mapping "path"->[x][y]=z then this means
+        * <ul>
+        * <li>the file with path "path" contains conflicts</li>
+        * <li>if y < "number of merged commits": for conflict number x in this file
+        * the chunk which was copied from commit number y starts on line number z.
+        * All numberings and line numbers start with 0.</li>
+        * <li>if y == "number of merged commits": the first non-conflicting line
+        * after conflict number x starts at line number z</li>
+        * </ul>
+        * <p>
+        * Example code how to parse this data:
+        * <pre> MergeResult m=...;
+        * Map<String, int[][]> allConflicts = m.getConflicts();
+        * for (String path : allConflicts.keySet()) {
+        *      int[][] c = allConflicts.get(path);
+        *      System.out.println("Conflicts in file " + path);
+        *      for (int i = 0; i < c.length; ++i) {
+        *              System.out.println("  Conflict #" + i);
+        *              for (int j = 0; j < (c[i].length) - 1; ++j) {
+        *                      if (c[i][j] >= 0)
+        *                              System.out.println("    Chunk for "
+        *                                              + m.getMergedCommits()[j] + " starts on line #"
+        *                                              + c[i][j]);
+        *              }
+        *      }
+        * }</pre>
+        *
+        * @return the conflicts or <code>null</code> if no conflict occured
+        */
+       public Map<String, int[][]> getConflicts() {
+               return conflicts;
+       }
 }