From: Thomas Wolf Date: Fri, 2 Feb 2018 09:42:54 +0000 (+0100) Subject: Use a TemporaryBuffer to do a content merge X-Git-Tag: v4.11.0.201803080745-r~131 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=refs%2Fchanges%2F84%2F116584%2F1;p=jgit.git Use a TemporaryBuffer to do a content merge This avoids having to re-read the merged file (twice even!) to update the index. Change-Id: Id13e0fd38906ed6f859604f86ca352761dca9ffe Signed-off-by: Thomas Wolf --- diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java old mode 100644 new mode 100755 index 837528e873..c3b3295c79 --- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java @@ -3,6 +3,7 @@ * Copyright (C) 2010-2012, Matthias Sohn * Copyright (C) 2012, Research In Motion Limited * Copyright (C) 2017, Obeo (mathieu.cartaud@obeo.fr) + * Copyright (C) 2018, Thomas Wolf * and other copyright owners as documented in the project's IP log. * * This program and the accompanying materials are made available @@ -51,10 +52,8 @@ import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_ALGORITHM; import static org.eclipse.jgit.lib.Constants.CHARACTER_ENCODING; import static org.eclipse.jgit.lib.Constants.OBJ_BLOB; -import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; -import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; @@ -874,94 +873,55 @@ public class ResolveMerger extends ThreeWayMerger { CanonicalTreeParser ours, CanonicalTreeParser theirs, MergeResult result) throws FileNotFoundException, IOException { - File mergedFile = !inCore ? writeMergedFile(result) : null; - - if (result.containsConflicts()) { - // A conflict occurred, the file will contain conflict markers - // the index will be populated with the three stages and the - // workdir (if used) contains the halfway merged content. - 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); - return; - } - - // No conflict occurred, the file will contain fully merged content. - // The index will be populated with the new merged version. - DirCacheEntry dce = new DirCacheEntry(tw.getPathString()); - - // Set the mode for the new content. Fall back to REGULAR_FILE if - // we can't merge modes of OURS and THEIRS. - int newMode = mergeFileModes( - tw.getRawMode(0), - tw.getRawMode(1), - tw.getRawMode(2)); - dce.setFileMode(newMode == FileMode.MISSING.getBits() - ? FileMode.REGULAR_FILE - : FileMode.fromBits(newMode)); - if (mergedFile != null) { - long len = mergedFile.length(); - dce.setLastModified(FS.DETECTED.lastModified(mergedFile)); - dce.setLength((int) len); - EolStreamType streamType = EolStreamTypeUtil.detectStreamType( - OperationType.CHECKIN_OP, workingTreeOptions, - tw.getAttributes()); - long blobLen = len == 0 ? 0 - : getEntryContentLength(mergedFile, streamType); - // TODO: we read the file twice because insert() needs the blob - // length up front. C.f. AddCommand. - try (InputStream is = EolStreamTypeUtil.wrapInputStream( - new FileInputStream(mergedFile), streamType)) { - dce.setObjectId( - getObjectInserter().insert(OBJ_BLOB, blobLen, is)); + TemporaryBuffer rawMerged = null; + try { + rawMerged = doMerge(result); + File mergedFile = inCore ? null : writeMergedFile(rawMerged); + if (result.containsConflicts()) { + // A conflict occurred, the file will contain conflict markers + // the index will be populated with the three stages and the + // workdir (if used) contains the halfway merged content. + 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); + return; } - } else - dce.setObjectId(insertMergeResult(result)); - builder.add(dce); - } - /** - * Computes the length of the index blob for a given file. - * - * @param file - * on disk - * @param streamType - * specifying CRLF translation - * @return the number of bytes after CRLF translations have been done. - * @throws IOException - * if the file cannot be read - */ - private long getEntryContentLength(File file, EolStreamType streamType) - throws IOException { - if (streamType == EolStreamType.DIRECT) { - return file.length(); - } - long length = 0; - try (InputStream is = EolStreamTypeUtil.wrapInputStream( - new BufferedInputStream(new FileInputStream(file)), - streamType)) { - for (;;) { - long n = is.skip(1 << 20); - if (n <= 0) { - break; - } - length += n; + // No conflict occurred, the file will contain fully merged content. + // The index will be populated with the new merged version. + DirCacheEntry dce = new DirCacheEntry(tw.getPathString()); + + // Set the mode for the new content. Fall back to REGULAR_FILE if + // we can't merge modes of OURS and THEIRS. + int newMode = mergeFileModes(tw.getRawMode(0), tw.getRawMode(1), + tw.getRawMode(2)); + dce.setFileMode(newMode == FileMode.MISSING.getBits() + ? FileMode.REGULAR_FILE : FileMode.fromBits(newMode)); + if (mergedFile != null) { + dce.setLastModified( + nonNullRepo().getFS().lastModified(mergedFile)); + dce.setLength((int) mergedFile.length()); + } + dce.setObjectId(insertMergeResult(rawMerged)); + builder.add(dce); + } finally { + if (rawMerged != null) { + rawMerged.destroy(); } - return length; } } /** * Writes merged file content to the working tree. * - * @param result - * the result of the content merge + * @param rawMerged + * the raw merged content * @return the working tree file to which the merged content was written. * @throws FileNotFoundException * @throws IOException */ - private File writeMergedFile(MergeResult result) + private File writeMergedFile(TemporaryBuffer rawMerged) throws FileNotFoundException, IOException { File workTree = nonNullRepo().getWorkTree(); FS fs = nonNullRepo().getFS(); @@ -976,13 +936,12 @@ public class ResolveMerger extends ThreeWayMerger { try (OutputStream os = EolStreamTypeUtil.wrapOutputStream( new BufferedOutputStream(new FileOutputStream(of)), streamType)) { - new MergeFormatter().formatMerge(os, result, - Arrays.asList(commitNames), CHARACTER_ENCODING); + rawMerged.writeTo(os, null); } return of; } - private ObjectId insertMergeResult(MergeResult result) + private TemporaryBuffer doMerge(MergeResult result) throws IOException { TemporaryBuffer.LocalFile buf = new TemporaryBuffer.LocalFile( db != null ? nonNullRepo().getDirectory() : null, inCoreLimit); @@ -990,11 +949,16 @@ public class ResolveMerger extends ThreeWayMerger { new MergeFormatter().formatMerge(buf, result, Arrays.asList(commitNames), CHARACTER_ENCODING); buf.close(); - try (InputStream in = buf.openInputStream()) { - return getObjectInserter().insert(OBJ_BLOB, buf.length(), in); - } - } finally { + } catch (IOException e) { buf.destroy(); + throw e; + } + return buf; + } + + private ObjectId insertMergeResult(TemporaryBuffer buf) throws IOException { + try (InputStream in = buf.openInputStream()) { + return getObjectInserter().insert(OBJ_BLOB, buf.length(), in); } }