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. 92
      org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java

92
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,8 +873,10 @@ 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 {
rawMerged = doMerge(result);
File mergedFile = inCore ? null : writeMergedFile(rawMerged);
if (result.containsConflicts()) { if (result.containsConflicts()) {
// A conflict occurred, the file will contain conflict markers // A conflict occurred, the file will contain conflict markers
// the index will be populated with the three stages and the // the index will be populated with the three stages and the
@ -893,75 +894,34 @@ public class ResolveMerger extends ThreeWayMerger {
// Set the mode for the new content. Fall back to REGULAR_FILE if // Set the mode for the new content. Fall back to REGULAR_FILE if
// we can't merge modes of OURS and THEIRS. // we can't merge modes of OURS and THEIRS.
int newMode = mergeFileModes( int newMode = mergeFileModes(tw.getRawMode(0), tw.getRawMode(1),
tw.getRawMode(0),
tw.getRawMode(1),
tw.getRawMode(2)); tw.getRawMode(2));
dce.setFileMode(newMode == FileMode.MISSING.getBits() dce.setFileMode(newMode == FileMode.MISSING.getBits()
? FileMode.REGULAR_FILE ? FileMode.REGULAR_FILE : FileMode.fromBits(newMode));
: FileMode.fromBits(newMode));
if (mergedFile != null) { if (mergedFile != null) {
long len = mergedFile.length(); dce.setLastModified(
dce.setLastModified(FS.DETECTED.lastModified(mergedFile)); nonNullRepo().getFS().lastModified(mergedFile));
dce.setLength((int) len); dce.setLength((int) mergedFile.length());
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);
} }
dce.setObjectId(insertMergeResult(rawMerged));
/** builder.add(dce);
* Computes the length of the index blob for a given file. } finally {
* if (rawMerged != null) {
* @param file rawMerged.destroy();
* 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;
} }
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,12 +949,17 @@ 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();
} catch (IOException e) {
buf.destroy();
throw e;
}
return buf;
}
private ObjectId insertMergeResult(TemporaryBuffer buf) throws IOException {
try (InputStream in = buf.openInputStream()) { try (InputStream in = buf.openInputStream()) {
return getObjectInserter().insert(OBJ_BLOB, buf.length(), in); return getObjectInserter().insert(OBJ_BLOB, buf.length(), in);
} }
} finally {
buf.destroy();
}
} }
/** /**

Loading…
Cancel
Save