Browse Source

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 <thomas.wolf@paranor.ch>
stable-4.11
Thomas Wolf 7 years ago
parent
commit
f22a162c47
  1. 132
      org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java

132
org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java

@ -3,6 +3,7 @@
* Copyright (C) 2010-2012, Matthias Sohn <matthias.sohn@sap.com> * Copyright (C) 2010-2012, Matthias Sohn <matthias.sohn@sap.com>
* Copyright (C) 2012, Research In Motion Limited * Copyright (C) 2012, Research In Motion Limited
* Copyright (C) 2017, Obeo (mathieu.cartaud@obeo.fr) * Copyright (C) 2017, Obeo (mathieu.cartaud@obeo.fr)
* Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
* and other copyright owners as documented in the project's IP log. * and other copyright owners as documented in the project's IP log.
* *
* This program and the accompanying materials are made available * 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.CHARACTER_ENCODING;
import static org.eclipse.jgit.lib.Constants.OBJ_BLOB; import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream; import java.io.BufferedOutputStream;
import java.io.File; import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
@ -874,94 +873,55 @@ public class ResolveMerger extends ThreeWayMerger {
CanonicalTreeParser ours, CanonicalTreeParser theirs, CanonicalTreeParser ours, CanonicalTreeParser theirs,
MergeResult<RawText> result) throws FileNotFoundException, MergeResult<RawText> result) throws FileNotFoundException,
IOException { IOException {
File mergedFile = !inCore ? writeMergedFile(result) : null; TemporaryBuffer rawMerged = null;
try {
if (result.containsConflicts()) { rawMerged = doMerge(result);
// A conflict occurred, the file will contain conflict markers File mergedFile = inCore ? null : writeMergedFile(rawMerged);
// the index will be populated with the three stages and the if (result.containsConflicts()) {
// workdir (if used) contains the halfway merged content. // A conflict occurred, the file will contain conflict markers
add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, 0, 0); // the index will be populated with the three stages and the
add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, 0, 0); // workdir (if used) contains the halfway merged content.
add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3, 0, 0); add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, 0, 0);
mergeResults.put(tw.getPathString(), result); add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, 0, 0);
return; 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));
} }
} else
dce.setObjectId(insertMergeResult(result));
builder.add(dce);
}
/** // No conflict occurred, the file will contain fully merged content.
* Computes the length of the index blob for a given file. // The index will be populated with the new merged version.
* DirCacheEntry dce = new DirCacheEntry(tw.getPathString());
* @param file
* on disk // Set the mode for the new content. Fall back to REGULAR_FILE if
* @param streamType // we can't merge modes of OURS and THEIRS.
* specifying CRLF translation int newMode = mergeFileModes(tw.getRawMode(0), tw.getRawMode(1),
* @return the number of bytes after CRLF translations have been done. tw.getRawMode(2));
* @throws IOException dce.setFileMode(newMode == FileMode.MISSING.getBits()
* if the file cannot be read ? FileMode.REGULAR_FILE : FileMode.fromBits(newMode));
*/ if (mergedFile != null) {
private long getEntryContentLength(File file, EolStreamType streamType) dce.setLastModified(
throws IOException { nonNullRepo().getFS().lastModified(mergedFile));
if (streamType == EolStreamType.DIRECT) { dce.setLength((int) mergedFile.length());
return file.length(); }
} dce.setObjectId(insertMergeResult(rawMerged));
long length = 0; builder.add(dce);
try (InputStream is = EolStreamTypeUtil.wrapInputStream( } finally {
new BufferedInputStream(new FileInputStream(file)), if (rawMerged != null) {
streamType)) { rawMerged.destroy();
for (;;) {
long n = is.skip(1 << 20);
if (n <= 0) {
break;
}
length += n;
} }
return length;
} }
} }
/** /**
* Writes merged file content to the working tree. * Writes merged file content to the working tree.
* *
* @param result * @param rawMerged
* the result of the content merge * the raw merged content
* @return the working tree file to which the merged content was written. * @return the working tree file to which the merged content was written.
* @throws FileNotFoundException * @throws FileNotFoundException
* @throws IOException * @throws IOException
*/ */
private File writeMergedFile(MergeResult<RawText> result) private File writeMergedFile(TemporaryBuffer rawMerged)
throws FileNotFoundException, IOException { throws FileNotFoundException, IOException {
File workTree = nonNullRepo().getWorkTree(); File workTree = nonNullRepo().getWorkTree();
FS fs = nonNullRepo().getFS(); FS fs = nonNullRepo().getFS();
@ -976,13 +936,12 @@ public class ResolveMerger extends ThreeWayMerger {
try (OutputStream os = EolStreamTypeUtil.wrapOutputStream( try (OutputStream os = EolStreamTypeUtil.wrapOutputStream(
new BufferedOutputStream(new FileOutputStream(of)), new BufferedOutputStream(new FileOutputStream(of)),
streamType)) { streamType)) {
new MergeFormatter().formatMerge(os, result, rawMerged.writeTo(os, null);
Arrays.asList(commitNames), CHARACTER_ENCODING);
} }
return of; return of;
} }
private ObjectId insertMergeResult(MergeResult<RawText> result) private TemporaryBuffer doMerge(MergeResult<RawText> result)
throws IOException { throws IOException {
TemporaryBuffer.LocalFile buf = new TemporaryBuffer.LocalFile( TemporaryBuffer.LocalFile buf = new TemporaryBuffer.LocalFile(
db != null ? nonNullRepo().getDirectory() : null, inCoreLimit); db != null ? nonNullRepo().getDirectory() : null, inCoreLimit);
@ -990,11 +949,16 @@ public class ResolveMerger extends ThreeWayMerger {
new MergeFormatter().formatMerge(buf, result, new MergeFormatter().formatMerge(buf, result,
Arrays.asList(commitNames), CHARACTER_ENCODING); Arrays.asList(commitNames), CHARACTER_ENCODING);
buf.close(); buf.close();
try (InputStream in = buf.openInputStream()) { } catch (IOException e) {
return getObjectInserter().insert(OBJ_BLOB, buf.length(), in);
}
} finally {
buf.destroy(); 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);
} }
} }

Loading…
Cancel
Save