diff options
Diffstat (limited to 'org.eclipse.jgit')
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 < c.length; ++i) { * System.out.println(" Conflict #" + i); - * for (int j = 0; j < (c[i].length) - 1; ++j) { - * if (c[i][j] >= 0) + * 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]); 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 < 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 < 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 < endA && beginB < 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 + * "foo" and another project's path is "foo/bar". This won't + * work in git submodule, so we'll skip all the sub projects + * ("foo/bar" 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; } |