diff options
author | Christian Halstrick <christian.halstrick@sap.com> | 2012-07-26 16:20:38 +0200 |
---|---|---|
committer | Christian Halstrick <christian.halstrick@sap.com> | 2012-07-26 16:20:38 +0200 |
commit | 778fdfaec1d1f5b16775264ebf728ee882000154 (patch) | |
tree | fdc5b632557b206b1e0ecdafd70a29b46145be3c /org.eclipse.jgit/src/org/eclipse/jgit/merge | |
parent | d87e56adddb03c9eb731ee835fdb7f2a59824f46 (diff) | |
download | jgit-778fdfaec1d1f5b16775264ebf728ee882000154.tar.gz jgit-778fdfaec1d1f5b16775264ebf728ee882000154.zip |
Again teach ResolveMerger to create more correct DirCacheEntry's
Currently, after a merge/cherry-pick/rebase, all index entries are
smudged as the ResolveMerger never sets entry lengths and/or
modification times. This change teaches it to re-set them at least for
things it did not touch. The other entries are then repaired when the
index is persisted, or entries are checked out.
The first attempt to get this in was commit
3ea694c2523d909190b5350e13254a62e94ec5d5 which has been reverted.
Since then some fixes to ResolveMerger and a few more tests have
been added which check situations where the index is not matching
HEAD before we merge.
Change-Id: I648fda30846615b3bf688c34274c6cf4bc857832
Signed-off-by: Christian Halstrick <christian.halstrick@sap.com>
Also-by: Markus Duft <markus.duft@salomon.at>
Diffstat (limited to 'org.eclipse.jgit/src/org/eclipse/jgit/merge')
-rw-r--r-- | org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java | 116 |
1 files changed, 81 insertions, 35 deletions
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 2410d6fe04..212938efe8 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java @@ -204,6 +204,11 @@ public class ResolveMerger extends ThreeWayMerger { } if (!inCore) { + // No problem found. The only thing left to be done is to + // checkout all files from "theirs" which have been selected to + // go into the new index. + checkout(); + // All content-merges are successfully done. If we can now write the // new index we are on quite safe ground. Even if the checkout of // files coming from "theirs" fails the user can work around such @@ -214,10 +219,6 @@ public class ResolveMerger extends ThreeWayMerger { } builder = null; - // No problem found. The only thing left to be done is to checkout - // all files from "theirs" which have been selected to go into the - // new index. - checkout(); } else { builder.finish(); builder = null; @@ -313,13 +314,18 @@ public class ResolveMerger extends ThreeWayMerger { * @param path * @param p * @param stage + * @param lastMod + * @param len * @return the entry which was added to the index */ - private DirCacheEntry add(byte[] path, CanonicalTreeParser p, int stage) { + private DirCacheEntry add(byte[] path, CanonicalTreeParser p, int stage, + long lastMod, long len) { if (p != null && !p.getEntryFileMode().equals(FileMode.TREE)) { DirCacheEntry e = new DirCacheEntry(path, stage); e.setFileMode(p.getEntryFileMode()); e.setObjectId(p.getEntryObjectId()); + e.setLastModified(lastMod); + e.setLength(len); builder.add(e); return e; } @@ -327,6 +333,26 @@ public class ResolveMerger extends ThreeWayMerger { } /** + * adds a entry to the index builder which is a copy of the specified + * DirCacheEntry + * + * @param e + * the entry which should be copied + * + * @return the entry which was added to the index + */ + private DirCacheEntry keep(DirCacheEntry e) { + DirCacheEntry newEntry = new DirCacheEntry(e.getPathString(), + e.getStage()); + newEntry.setFileMode(e.getFileMode()); + newEntry.setObjectId(e.getObjectId()); + newEntry.setLastModified(e.getLastModified()); + newEntry.setLength(e.getLength()); + builder.add(newEntry); + return newEntry; + } + + /** * Processes one path and tries to merge. This method will do all do all * trivial (not content) merges and will also detect if a merge will fail. * The merge will fail when one of the following is true @@ -382,12 +408,27 @@ public class ResolveMerger extends ThreeWayMerger { if (isIndexDirty()) return false; + DirCacheEntry ourDce = null; + + if (index == null || index.getDirCacheEntry() == null) { + // create a fake DCE, but only if ours is valid. ours is kept only + // in case it is valid, so a null ourDce is ok in all other cases. + if (nonTree(modeO)) { + ourDce = new DirCacheEntry(tw.getRawPath()); + ourDce.setObjectId(tw.getObjectId(T_OURS)); + ourDce.setFileMode(tw.getFileMode(T_OURS)); + } + } else { + ourDce = index.getDirCacheEntry(); + } + if (nonTree(modeO) && nonTree(modeT) && tw.idEqual(T_OURS, T_THEIRS)) { // OURS and THEIRS have equal content. Check the file mode if (modeO == modeT) { // content and mode of OURS and THEIRS are equal: it doesn't - // matter which one we choose. OURS is chosen. - add(tw.getRawPath(), ours, DirCacheEntry.STAGE_0); + // matter which one we choose. OURS is chosen. Since the index + // is clean (the index matches already OURS) we can keep the existing one + keep(ourDce); // no checkout needed! return true; } else { @@ -398,22 +439,25 @@ public class ResolveMerger extends ThreeWayMerger { if (newMode != FileMode.MISSING.getBits()) { if (newMode == modeO) // ours version is preferred - add(tw.getRawPath(), ours, DirCacheEntry.STAGE_0); + keep(ourDce); else { // the preferred version THEIRS has a different mode // than ours. Check it out! if (isWorktreeDirty(work)) return false; + // we know about length and lastMod only after we have written the new content. + // This will happen later. Set these values to 0 for know. DirCacheEntry e = add(tw.getRawPath(), theirs, - DirCacheEntry.STAGE_0); + DirCacheEntry.STAGE_0, 0, 0); toBeCheckedOut.put(tw.getPathString(), e); } return true; } else { - // FileModes are not mergeable. We found a conflict on modes - add(tw.getRawPath(), base, DirCacheEntry.STAGE_1); - add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2); - add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3); + // FileModes are not mergeable. We found a conflict on modes. + // For conflicting entries we don't know lastModified and length. + add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, 0, 0); + add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, 0, 0); + add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3, 0, 0); unmergedPaths.add(tw.getPathString()); mergeResults.put( tw.getPathString(), @@ -426,8 +470,8 @@ public class ResolveMerger extends ThreeWayMerger { if (nonTree(modeO) && modeB == modeT && tw.idEqual(T_BASE, T_THEIRS)) { // THEIRS was not changed compared to BASE. All changes must be in - // OURS. OURS is chosen. - add(tw.getRawPath(), ours, DirCacheEntry.STAGE_0); + // OURS. OURS is chosen. We can keep the existing entry. + keep(ourDce); // no checkout needed! return true; } @@ -440,8 +484,11 @@ public class ResolveMerger extends ThreeWayMerger { if (isWorktreeDirty(work)) return false; if (nonTree(modeT)) { + // we know about length and lastMod only after we have written + // the new content. + // This will happen later. Set these values to 0 for know. DirCacheEntry e = add(tw.getRawPath(), theirs, - DirCacheEntry.STAGE_0); + DirCacheEntry.STAGE_0, 0, 0); if (e != null) toBeCheckedOut.put(tw.getPathString(), e); return true; @@ -460,16 +507,16 @@ public class ResolveMerger extends ThreeWayMerger { // detected later if (nonTree(modeO) && !nonTree(modeT)) { if (nonTree(modeB)) - add(tw.getRawPath(), base, DirCacheEntry.STAGE_1); - add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2); + add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, 0, 0); + add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, 0, 0); unmergedPaths.add(tw.getPathString()); enterSubtree = false; return true; } if (nonTree(modeT) && !nonTree(modeO)) { if (nonTree(modeB)) - add(tw.getRawPath(), base, DirCacheEntry.STAGE_1); - add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3); + add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, 0, 0); + add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3, 0, 0); unmergedPaths.add(tw.getPathString()); enterSubtree = false; return true; @@ -502,10 +549,10 @@ public class ResolveMerger extends ThreeWayMerger { if (((modeO != 0 && !tw.idEqual(T_BASE, T_OURS)) || (modeT != 0 && !tw .idEqual(T_BASE, T_THEIRS)))) { - add(tw.getRawPath(), base, DirCacheEntry.STAGE_1); - add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2); + add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, 0, 0); + add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, 0, 0); DirCacheEntry e = add(tw.getRawPath(), theirs, - DirCacheEntry.STAGE_3); + DirCacheEntry.STAGE_3, 0, 0); // OURS was deleted checkout THEIRS if (modeO == 0) { @@ -567,19 +614,16 @@ public class ResolveMerger extends ThreeWayMerger { } private boolean isWorktreeDirty(WorkingTreeIterator work) { - if (inCore) + if (inCore || work == null) return false; final int modeF = tw.getRawMode(T_FILE); final int modeO = tw.getRawMode(T_OURS); // Worktree entry has to match ours to be considered clean - final boolean isDirty; - if (nonTree(modeF)) - isDirty = work.isModeDifferent(modeO) - || !tw.idEqual(T_FILE, T_OURS); - else - isDirty = false; + boolean isDirty = work.isModeDifferent(modeO); + if (!isDirty && nonTree(modeF)) + isDirty = !tw.idEqual(T_FILE, T_OURS); if (isDirty) failingPaths.put(tw.getPathString(), @@ -609,9 +653,9 @@ public class ResolveMerger extends ThreeWayMerger { // a conflict occurred, the file will contain conflict markers // the index will be populated with the three stages and only the // workdir (if used) contains the halfways merged content - add(tw.getRawPath(), base, DirCacheEntry.STAGE_1); - add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2); - add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3); + add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, 0, 0); + add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, 0, 0); + add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3, 0, 0); mergeResults.put(tw.getPathString(), result); } else { // no conflict occurred, the file will contain fully merged content. @@ -662,6 +706,9 @@ public class ResolveMerger extends ThreeWayMerger { throw new UnsupportedOperationException(); of = new File(workTree, tw.getPathString()); + File parentFolder = of.getParentFile(); + if (!parentFolder.exists()) + parentFolder.mkdirs(); fos = new FileOutputStream(of); try { fmt.formatMerge(fos, result, Arrays.asList(commitNames), @@ -669,8 +716,7 @@ public class ResolveMerger extends ThreeWayMerger { } finally { fos.close(); } - } - else if (!result.containsConflicts()) { + } else if (!result.containsConflicts()) { // When working inCore, only trivial merges can be handled, // so we generate objects only in conflict free cases of = File.createTempFile("merge_", "_temp", null); |