aboutsummaryrefslogtreecommitdiffstats
path: root/org.eclipse.jgit
diff options
context:
space:
mode:
Diffstat (limited to 'org.eclipse.jgit')
-rw-r--r--org.eclipse.jgit/.settings/.api_filters20
-rw-r--r--org.eclipse.jgit/META-INF/MANIFEST.MF1
-rw-r--r--org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties1
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/MergeResult.java6
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java1
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/diff/Edit.java15
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java15
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/CharacterHead.java5
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/FileNameMatcher.java16
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/RestrictedWildCardHead.java5
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java86
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/ignore/IgnoreRule.java25
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java1
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeResult.java13
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/merge/RecursiveMerger.java15
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java52
16 files changed, 228 insertions, 49 deletions
diff --git a/org.eclipse.jgit/.settings/.api_filters b/org.eclipse.jgit/.settings/.api_filters
index 0b8473e719..01c243417a 100644
--- a/org.eclipse.jgit/.settings/.api_filters
+++ b/org.eclipse.jgit/.settings/.api_filters
@@ -8,4 +8,24 @@
</message_arguments>
</filter>
</resource>
+ <resource path="src/org/eclipse/jgit/merge/ResolveMerger.java" type="org.eclipse.jgit.merge.ResolveMerger">
+ <filter comment="Doesn't break consumers. Breaking providers is allowed also in minor versions." id="338792546">
+ <message_arguments>
+ <message_argument value="org.eclipse.jgit.merge.ResolveMerger"/>
+ <message_argument value="mergeTreeWalk(TreeWalk)"/>
+ </message_arguments>
+ </filter>
+ <filter comment="Doesn't break consumers. Breaking providers is allowed also in minor versions." id="338792546">
+ <message_arguments>
+ <message_argument value="org.eclipse.jgit.merge.ResolveMerger"/>
+ <message_argument value="mergeTrees(AbstractTreeIterator, RevTree, RevTree)"/>
+ </message_arguments>
+ </filter>
+ <filter comment="Doesn't break consumers. Breaking providers is allowed also in minor versions." id="338792546">
+ <message_arguments>
+ <message_argument value="org.eclipse.jgit.merge.ResolveMerger"/>
+ <message_argument value="processEntry(CanonicalTreeParser, CanonicalTreeParser, CanonicalTreeParser, DirCacheBuildIterator, WorkingTreeIterator)"/>
+ </message_arguments>
+ </filter>
+ </resource>
</component>
diff --git a/org.eclipse.jgit/META-INF/MANIFEST.MF b/org.eclipse.jgit/META-INF/MANIFEST.MF
index 36acfb6710..210316b6a1 100644
--- a/org.eclipse.jgit/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit/META-INF/MANIFEST.MF
@@ -48,6 +48,7 @@ Export-Package: org.eclipse.jgit.api;version="3.5.0";
org.eclipse.jgit.gitrepo;version="3.5.0";
uses:="org.eclipse.jgit.api,
org.eclipse.jgit.lib",
+ org.eclipse.jgit.gitrepo.internal;version="3.5.0";x-internal:=true,
org.eclipse.jgit.ignore;version="3.5.0",
org.eclipse.jgit.internal;version="3.5.0";x-friends:="org.eclipse.jgit.test,org.eclipse.jgit.http.test",
org.eclipse.jgit.internal.storage.dfs;version="3.5.0";x-friends:="org.eclipse.jgit.test",
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 fe431319ff..750b65c651 100644
--- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
+++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
@@ -311,6 +311,7 @@ mergeConflictOnNotes=Merge conflict on note {0}. base = {1}, ours = {2}, theirs
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 of revisions {0} with base {1} using strategy {2} resulted in: {3}. {4}
+mergeRecursiveConflictsWhenMergingCommonAncestors=Multiple common ancestors were found and merging them resulted in a conflict: {0}, {1}
mergeRecursiveReturnedNoCommit=Merge returned no commit:\n Depth {0}\n Head one {1}\n Head two {2}
mergeRecursiveTooManyMergeBasesFor = "More than {0} merge bases for:\n a {1}\n b {2} found:\n count {3}"
messageAndTaggerNotAllowedInUnannotatedTags = Unannotated tags cannot have a message or tagger
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeResult.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeResult.java
index 2def12f1a2..9dc33b5ad5 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeResult.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeResult.java
@@ -483,10 +483,10 @@ public class MergeResult {
* 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) {
+ * for (int i = 0; i &lt; c.length; ++i) {
* System.out.println(" Conflict #" + i);
- * for (int j = 0; j &lt; (c[i].length) - 1; ++j) {
- * if (c[i][j] >= 0)
+ * for (int j = 0; j &lt; (c[i].length) - 1; ++j) {
+ * if (c[i][j] &gt;= 0)
* System.out.println(" Chunk for "
* + m.getMergedCommits()[j] + " starts on line #"
* + c[i][j]);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java
index 52327d76c7..470d823aca 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java
@@ -186,6 +186,7 @@ public class RevertCommand extends GitCommand<RevCommit> {
.setMessage(newMessage)
.setReflogComment("revert: " + shortMessage).call(); //$NON-NLS-1$
revertedRefs.add(src);
+ headCommit = newHead;
} else {
unmergedPaths = merger.getUnmergedPaths();
Map<String, MergeFailureReason> failingPaths = merger
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/Edit.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/Edit.java
index 57a2018abf..7eecd13513 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/Edit.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/Edit.java
@@ -51,16 +51,15 @@ package org.eclipse.jgit.diff;
* Regions should be specified using 0 based notation, so add 1 to the start and
* end marks for line numbers in a file.
* <p>
- * An edit where <code>beginA == endA && beginB &lt; endB</code> is an insert
- * edit, that is sequence B inserted the elements in region
- * <code>[beginB, endB)</code> at <code>beginA</code>.
+ * An edit where {@code beginA == endA && beginB < endB} is an insert edit, that
+ * is sequence B inserted the elements in region <code>[beginB, endB)</code> at
+ * <code>beginA</code>.
* <p>
- * An edit where <code>beginA &lt; endA && beginB == endB</code> is a delete
- * edit, that is sequence B has removed the elements between
- * <code>[beginA, endA)</code>.
+ * An edit where {@code beginA < endA && beginB == endB} is a delete edit, that
+ * is sequence B has removed the elements between <code>[beginA, endA)</code>.
* <p>
- * An edit where <code>beginA &lt; endA && beginB &lt; endB</code> is a replace
- * edit, that is sequence B has replaced the range of elements between
+ * An edit where {@code beginA < endA && beginB < endB} is a replace edit, that
+ * is sequence B has replaced the range of elements between
* <code>[beginA, endA)</code> with those found in <code>[beginB, endB)</code>.
*/
public class Edit {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
index 80dda8eb83..4b0d58600f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
@@ -110,6 +110,8 @@ public class DirCacheCheckout {
private ArrayList<String> toBeDeleted = new ArrayList<String>();
+ private boolean emptyDirCache;
+
/**
* @return a list of updated paths and objectIds
*/
@@ -168,6 +170,7 @@ public class DirCacheCheckout {
this.headCommitTree = headCommitTree;
this.mergeCommitTree = mergeCommitTree;
this.workingTree = workingTree;
+ this.emptyDirCache = (dc == null) || (dc.getEntryCount() == 0);
}
/**
@@ -716,7 +719,8 @@ public class DirCacheCheckout {
* 0 nothing nothing nothing (does not happen)
* 1 nothing nothing exists use M
* 2 nothing exists nothing remove path from index
- * 3 nothing exists exists yes keep index
+ * 3 nothing exists exists yes keep index if not in initial checkout
+ * , otherwise use M
* nothing exists exists no fail
* </pre>
*/
@@ -743,9 +747,12 @@ public class DirCacheCheckout {
// in the index there is nothing (e.g. 'git rm ...' was
// called before). Ignore the cached deletion and use what we
// find in Merge. Potentially updates the file.
- if (equalIdAndMode(hId, hMode, mId, mMode))
- keep(dce);
- else
+ if (equalIdAndMode(hId, hMode, mId, mMode)) {
+ if (emptyDirCache)
+ update(name, mId, mMode);
+ else
+ keep(dce);
+ } else
conflict(name, dce, h, m);
}
} else {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/CharacterHead.java b/org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/CharacterHead.java
index 699eca9683..6211b246f8 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/CharacterHead.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/CharacterHead.java
@@ -57,4 +57,9 @@ final class CharacterHead extends AbstractHead {
return c == expectedCharacter;
}
+ @Override
+ public String toString() {
+ return String.valueOf(expectedCharacter);
+ }
+
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/FileNameMatcher.java b/org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/FileNameMatcher.java
index 92a4837b2e..f9c239431a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/FileNameMatcher.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/FileNameMatcher.java
@@ -307,7 +307,11 @@ public class FileNameMatcher {
return new WildCardHead(star);
}
- private void extendStringToMatchByOneCharacter(final char c) {
+ /**
+ * @param c new character to append
+ * @return true to continue, false if the matcher can stop appending
+ */
+ private boolean extendStringToMatchByOneCharacter(final char c) {
final List<Head> newHeads = listForLocalUseage;
newHeads.clear();
List<Head> lastAddedHeads = null;
@@ -320,12 +324,14 @@ public class FileNameMatcher {
// This is the case with the heads "a" and "*" of "a*b" which
// both can return the list ["*","b"]
if (headsToAdd != lastAddedHeads) {
- newHeads.addAll(headsToAdd);
+ if (!headsToAdd.isEmpty())
+ newHeads.addAll(headsToAdd);
lastAddedHeads = headsToAdd;
}
}
listForLocalUseage = heads;
heads = newHeads;
+ return !newHeads.isEmpty();
}
private static int indexOfUnescaped(final String searchString,
@@ -349,7 +355,8 @@ public class FileNameMatcher {
public void append(final String stringToMatch) {
for (int i = 0; i < stringToMatch.length(); i++) {
final char c = stringToMatch.charAt(i);
- extendStringToMatchByOneCharacter(c);
+ if (!extendStringToMatchByOneCharacter(c))
+ break;
}
}
@@ -378,6 +385,9 @@ public class FileNameMatcher {
* @return true, if the string currently being matched does match.
*/
public boolean isMatch() {
+ if (heads.isEmpty())
+ return false;
+
final ListIterator<Head> headIterator = heads
.listIterator(heads.size());
while (headIterator.hasPrevious()) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/RestrictedWildCardHead.java b/org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/RestrictedWildCardHead.java
index 6d527d2b2d..4a0a03df25 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/RestrictedWildCardHead.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/RestrictedWildCardHead.java
@@ -56,4 +56,9 @@ final class RestrictedWildCardHead extends AbstractHead {
protected final boolean matches(final char c) {
return c != excludedCharacter;
}
+
+ @Override
+ public String toString() {
+ return isStar() ? "*" : "?"; //$NON-NLS-1$ //$NON-NLS-2$
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java
index ee814300ae..12d4d311f3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java
@@ -53,8 +53,10 @@ import java.nio.channels.FileChannel;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -104,6 +106,11 @@ import org.xml.sax.helpers.XMLReaderFactory;
* If called against a bare repository, it will replace all the existing content
* of the repository with the contents populated from the manifest.
*
+ * repo manifest allows projects overlapping, e.g. one project's path is
+ * &quot;foo&quot; and another project's path is &quot;foo/bar&quot;. This won't
+ * work in git submodule, so we'll skip all the sub projects
+ * (&quot;foo/bar&quot; in the example) while converting.
+ *
* @see <a href="https://code.google.com/p/git-repo/">git-repo project page</a>
* @since 3.4
*/
@@ -249,7 +256,7 @@ public class RepoCommand extends GitCommand<RevCommit> {
}
}
- private static class Project {
+ private static class Project implements Comparable<Project> {
final String name;
final String path;
final String revision;
@@ -269,6 +276,35 @@ public class RepoCommand extends GitCommand<RevCommit> {
void addCopyFile(CopyFile copyfile) {
copyfiles.add(copyfile);
}
+
+ String getPathWithSlash() {
+ if (path.endsWith("/")) //$NON-NLS-1$
+ return path;
+ else
+ return path + "/"; //$NON-NLS-1$
+ }
+
+ boolean isAncestorOf(Project that) {
+ return that.getPathWithSlash().startsWith(this.getPathWithSlash());
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof Project) {
+ Project that = (Project) o;
+ return this.getPathWithSlash().equals(that.getPathWithSlash());
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return this.getPathWithSlash().hashCode();
+ }
+
+ public int compareTo(Project that) {
+ return this.getPathWithSlash().compareTo(that.getPathWithSlash());
+ }
}
private static class XmlManifest extends DefaultHandler {
@@ -277,9 +313,9 @@ public class RepoCommand extends GitCommand<RevCommit> {
private final String filename;
private final String baseUrl;
private final Map<String, String> remotes;
- private final List<Project> projects;
private final Set<String> plusGroups;
private final Set<String> minusGroups;
+ private List<Project> projects;
private String defaultRemote;
private String defaultRevision;
private Project currentProject;
@@ -289,7 +325,13 @@ public class RepoCommand extends GitCommand<RevCommit> {
this.command = command;
this.inputStream = inputStream;
this.filename = filename;
- this.baseUrl = baseUrl;
+
+ // Strip trailing /s to match repo behavior.
+ int lastIndex = baseUrl.length() - 1;
+ while (lastIndex >= 0 && baseUrl.charAt(lastIndex) == '/')
+ lastIndex--;
+ this.baseUrl = baseUrl.substring(0, lastIndex + 1);
+
remotes = new HashMap<String, String>();
projects = new ArrayList<Project>();
plusGroups = new HashSet<String>();
@@ -383,14 +425,38 @@ public class RepoCommand extends GitCommand<RevCommit> {
} catch (URISyntaxException e) {
throw new SAXException(e);
}
+ removeNotInGroup();
+ removeOverlaps();
for (Project proj : projects) {
- if (inGroups(proj)) {
- command.addSubmodule(remoteUrl + proj.name,
- proj.path,
- proj.revision == null
- ? defaultRevision : proj.revision,
- proj.copyfiles);
- }
+ command.addSubmodule(remoteUrl + proj.name,
+ proj.path,
+ proj.revision == null
+ ? defaultRevision : proj.revision,
+ proj.copyfiles);
+ }
+ }
+
+ /** Remove projects that are not in our desired groups. */
+ void removeNotInGroup() {
+ Iterator<Project> iter = projects.iterator();
+ while (iter.hasNext())
+ if (!inGroups(iter.next()))
+ iter.remove();
+ }
+
+ /** Remove projects that sits in a subdirectory of any other project. */
+ void removeOverlaps() {
+ Collections.sort(projects);
+ Iterator<Project> iter = projects.iterator();
+ if (!iter.hasNext())
+ return;
+ Project last = iter.next();
+ while (iter.hasNext()) {
+ Project p = iter.next();
+ if (last.isAncestorOf(p))
+ iter.remove();
+ else
+ last = p;
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/IgnoreRule.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/IgnoreRule.java
index 980f2094bd..42bbd9e9b8 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/IgnoreRule.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/IgnoreRule.java
@@ -191,6 +191,9 @@ public class IgnoreRule {
final String[] segments = target.split("/"); //$NON-NLS-1$
for (int idx = 0; idx < segments.length; idx++) {
final String segmentName = segments[idx];
+ // String.split("/") creates empty segment for leading slash
+ if (segmentName.length() == 0)
+ continue;
if (segmentName.equals(pattern) &&
doesMatchDirectoryExpectations(isDirectory, idx, segments.length))
return true;
@@ -207,6 +210,9 @@ public class IgnoreRule {
if (nameOnly) {
for (int idx = 0; idx < segments.length; idx++) {
final String segmentName = segments[idx];
+ // String.split("/") creates empty segment for leading slash
+ if (segmentName.length() == 0)
+ continue;
//Iterate through each sub-directory
matcher.reset();
matcher.append(segmentName);
@@ -218,14 +224,18 @@ public class IgnoreRule {
//TODO: This is the slowest operation
//This matches e.g. "/src/ne?" to "/src/new/file.c"
matcher.reset();
+
for (int idx = 0; idx < segments.length; idx++) {
final String segmentName = segments[idx];
- if (segmentName.length() > 0) {
- matcher.append("/" + segmentName); //$NON-NLS-1$
- }
+ // String.split("/") creates empty segment for leading slash
+ if (segmentName.length() == 0)
+ continue;
- if (matcher.isMatch() &&
- doesMatchDirectoryExpectations(isDirectory, idx, segments.length))
+ matcher.append("/" + segmentName); //$NON-NLS-1$
+
+ if (matcher.isMatch()
+ && doesMatchDirectoryExpectations(isDirectory, idx,
+ segments.length))
return true;
}
}
@@ -255,4 +265,9 @@ public class IgnoreRule {
// We are checking the last part of the segment for which isDirectory has to be considered.
return !dirOnly || isDirectory;
}
+
+ @Override
+ public String toString() {
+ return pattern;
+ }
} \ No newline at end of file
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 f075db3e57..ed94514924 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
@@ -370,6 +370,7 @@ public class JGitText extends TranslationBundle {
/***/ public String mergeStrategyAlreadyExistsAsDefault;
/***/ public String mergeStrategyDoesNotSupportHeads;
/***/ public String mergeUsingStrategyResultedInDescription;
+ /***/ public String mergeRecursiveConflictsWhenMergingCommonAncestors;
/***/ public String mergeRecursiveReturnedNoCommit;
/***/ public String mergeRecursiveTooManyMergeBasesFor;
/***/ public String messageAndTaggerNotAllowedInUnannotatedTags;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeResult.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeResult.java
index 2dd4426e11..dc3c772efb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeResult.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeResult.java
@@ -161,4 +161,17 @@ public class MergeResult<S extends Sequence> implements Iterable<MergeChunk> {
public boolean containsConflicts() {
return containsConflicts;
}
+
+ /**
+ * Sets explicitly whether this merge should be seen as containing a
+ * conflict or not. Needed because during RecursiveMerger we want to do
+ * content-merges and take the resulting content (even with conflict
+ * markers!) as new conflict-free content
+ *
+ * @param containsConflicts
+ * @since 3.5
+ */
+ protected void setContainsConflicts(boolean containsConflicts) {
+ this.containsConflicts = containsConflicts;
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/RecursiveMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/RecursiveMerger.java
index 5802850a30..af6c1f9647 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/RecursiveMerger.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/RecursiveMerger.java
@@ -196,22 +196,25 @@ public class RecursiveMerger extends ResolveMerger {
if (mergeTrees(
openTree(getBaseCommit(currentBase, nextBase,
callDepth + 1).getTree()),
- currentBase.getTree(),
- nextBase.getTree()))
+ currentBase.getTree(), nextBase.getTree(), true))
currentBase = createCommitForTree(resultTree, parents);
else
throw new NoMergeBaseException(
NoMergeBaseException.MergeBaseFailureReason.CONFLICTS_DURING_MERGE_BASE_CALCULATION,
MessageFormat.format(
- JGitText.get().mergeRecursiveTooManyMergeBasesFor,
- Integer.valueOf(MAX_BASES), a.name(),
- b.name(),
- Integer.valueOf(baseCommits.size())));
+ JGitText.get().mergeRecursiveConflictsWhenMergingCommonAncestors,
+ currentBase.getName(), nextBase.getName()));
}
} finally {
inCore = oldIncore;
dircache = oldDircache;
workingTreeIterator = oldWTreeIt;
+ toBeCheckedOut.clear();
+ toBeDeleted.clear();
+ modifiedFiles.clear();
+ unmergedPaths.clear();
+ mergeResults.clear();
+ failingPaths.clear();
}
return currentBase;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
index 28d42a6161..712bb0d35e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
@@ -297,7 +297,8 @@ public class ResolveMerger extends ThreeWayMerger {
dircache = getRepository().lockDirCache();
try {
- return mergeTrees(mergeBase(), sourceTrees[0], sourceTrees[1]);
+ return mergeTrees(mergeBase(), sourceTrees[0], sourceTrees[1],
+ false);
} finally {
if (implicitDirCache)
dircache.unlock();
@@ -443,6 +444,7 @@ public class ResolveMerger extends ThreeWayMerger {
* conflict is detected the content-merge algorithm will try to write a
* merged version into the working-tree. If the file is dirty we would
* override unsaved data.</li>
+ * </ul>
*
* @param base
* the common base for ours and theirs
@@ -457,6 +459,9 @@ public class ResolveMerger extends ThreeWayMerger {
* the index entry
* @param work
* the file in the working tree
+ * @param ignoreConflicts
+ * see
+ * {@link ResolveMerger#mergeTrees(AbstractTreeIterator, RevTree, RevTree, boolean)}
* @return <code>false</code> if the merge will fail because the index entry
* didn't match ours or the working-dir file was dirty and a
* conflict occurred
@@ -464,11 +469,12 @@ public class ResolveMerger extends ThreeWayMerger {
* @throws IncorrectObjectTypeException
* @throws CorruptObjectException
* @throws IOException
- * @since 3.4
+ * @since 3.5
*/
protected boolean processEntry(CanonicalTreeParser base,
CanonicalTreeParser ours, CanonicalTreeParser theirs,
- DirCacheBuildIterator index, WorkingTreeIterator work)
+ DirCacheBuildIterator index, WorkingTreeIterator work,
+ boolean ignoreConflicts)
throws MissingObjectException, IncorrectObjectTypeException,
CorruptObjectException, IOException {
enterSubtree = true;
@@ -627,9 +633,11 @@ public class ResolveMerger extends ThreeWayMerger {
}
MergeResult<RawText> result = contentMerge(base, ours, theirs);
+ if (ignoreConflicts)
+ result.setContainsConflicts(false);
File of = writeMergedFile(result);
updateIndex(base, ours, theirs, result, of);
- if (result.containsConflicts())
+ if (result.containsConflicts() && !ignoreConflicts)
unmergedPaths.add(tw.getPathString());
modifiedFiles.add(tw.getPathString());
} else if (modeO != modeT) {
@@ -993,12 +1001,32 @@ public class ResolveMerger extends ThreeWayMerger {
* @param baseTree
* @param headTree
* @param mergeTree
+ * @param ignoreConflicts
+ * Controls what to do in case a content-merge is done and a
+ * conflict is detected. The default setting for this should be
+ * <code>false</code>. In this case the working tree file is
+ * filled with new content (containing conflict markers) and the
+ * index is filled with multiple stages containing BASE, OURS and
+ * THEIRS content. Having such non-0 stages is the sign to git
+ * tools that there are still conflicts for that path.
+ * <p>
+ * If <code>true</code> is specified the behavior is different.
+ * In case a conflict is detected the working tree file is again
+ * filled with new content (containing conflict markers). But
+ * also stage 0 of the index is filled with that content. No
+ * other stages are filled. Means: there is no conflict on that
+ * path but the new content (including conflict markers) is
+ * stored as successful merge result. This is needed in the
+ * context of {@link RecursiveMerger} where when determining
+ * merge bases we don't want to deal with content-merge
+ * conflicts.
* @return whether the trees merged cleanly
* @throws IOException
- * @since 3.0
+ * @since 3.5
*/
protected boolean mergeTrees(AbstractTreeIterator baseTree,
- RevTree headTree, RevTree mergeTree) throws IOException {
+ RevTree headTree, RevTree mergeTree, boolean ignoreConflicts)
+ throws IOException {
builder = dircache.builder();
DirCacheBuildIterator buildIt = new DirCacheBuildIterator(builder);
@@ -1011,7 +1039,7 @@ public class ResolveMerger extends ThreeWayMerger {
if (workingTreeIterator != null)
tw.addTree(workingTreeIterator);
- if (!mergeTreeWalk(tw)) {
+ if (!mergeTreeWalk(tw, ignoreConflicts)) {
return false;
}
@@ -1050,11 +1078,15 @@ public class ResolveMerger extends ThreeWayMerger {
*
* @param treeWalk
* The walk to iterate over.
+ * @param ignoreConflicts
+ * see
+ * {@link ResolveMerger#mergeTrees(AbstractTreeIterator, RevTree, RevTree, boolean)}
* @return Whether the trees merged cleanly.
* @throws IOException
- * @since 3.4
+ * @since 3.5
*/
- protected boolean mergeTreeWalk(TreeWalk treeWalk) throws IOException {
+ protected boolean mergeTreeWalk(TreeWalk treeWalk, boolean ignoreConflicts)
+ throws IOException {
boolean hasWorkingTreeIterator = tw.getTreeCount() > T_FILE;
while (treeWalk.next()) {
if (!processEntry(
@@ -1063,7 +1095,7 @@ public class ResolveMerger extends ThreeWayMerger {
treeWalk.getTree(T_THEIRS, CanonicalTreeParser.class),
treeWalk.getTree(T_INDEX, DirCacheBuildIterator.class),
hasWorkingTreeIterator ? treeWalk.getTree(T_FILE,
- WorkingTreeIterator.class) : null)) {
+ WorkingTreeIterator.class) : null, ignoreConflicts)) {
cleanUp();
return false;
}