Add filtering with help of DirCacheCheckout.getContent()
see: https://git-scm.com/docs/git-mergetool * refactoring of content (FileElement) handling * now the temporary files are already filled with filtered content in the calling classes (PGM), that can be used with EGit content too TODO: * keep the temporaries when no change detected and the user answers no to the question if the merge was successful Bug: 356832 Change-Id: I86a0a052d059957d4d152c1bb94c262902c377d2 Signed-off-by: Andre Bossert <andre.bossert@siemens.com>
This commit is contained in:
parent
d128c3112d
commit
e81085944f
|
@ -21,10 +21,8 @@
|
|||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jgit.diff.DiffEntry;
|
||||
import org.eclipse.jgit.internal.diffmergetool.CommandLineDiffTool;
|
||||
import org.eclipse.jgit.lib.StoredConfig;
|
||||
import org.eclipse.jgit.revwalk.RevCommit;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
|
@ -49,9 +47,8 @@ public void testToolWithPrompt() throws Exception {
|
|||
"y", // accept launching diff tool
|
||||
};
|
||||
|
||||
RevCommit commit = createUnstagedChanges();
|
||||
List<DiffEntry> changes = getRepositoryChanges(commit);
|
||||
String[] expectedOutput = getExpectedCompareOutput(changes);
|
||||
String[] conflictingFilenames = createUnstagedChanges();
|
||||
String[] expectedOutput = getExpectedCompareOutput(conflictingFilenames);
|
||||
|
||||
String option = "--tool";
|
||||
|
||||
|
@ -68,10 +65,9 @@ public void testToolAbortLaunch() throws Exception {
|
|||
"n", // don't launch diff tool
|
||||
};
|
||||
|
||||
RevCommit commit = createUnstagedChanges();
|
||||
List<DiffEntry> changes = getRepositoryChanges(commit);
|
||||
String[] conflictingFilenames = createUnstagedChanges();
|
||||
int abortIndex = 1;
|
||||
String[] expectedOutput = getExpectedAbortOutput(changes, abortIndex);
|
||||
String[] expectedOutput = getExpectedAbortOutput(conflictingFilenames, abortIndex);
|
||||
|
||||
String option = "--tool";
|
||||
|
||||
|
@ -92,9 +88,8 @@ public void testNotDefinedTool() throws Exception {
|
|||
|
||||
@Test
|
||||
public void testTool() throws Exception {
|
||||
RevCommit commit = createUnstagedChanges();
|
||||
List<DiffEntry> changes = getRepositoryChanges(commit);
|
||||
String[] expectedOutput = getExpectedToolOutputNoPrompt(changes);
|
||||
String[] conflictFilenames = createUnstagedChanges();
|
||||
String[] expectedOutput = getExpectedToolOutputNoPrompt(conflictFilenames);
|
||||
|
||||
String[] options = {
|
||||
"--tool",
|
||||
|
@ -111,9 +106,8 @@ public void testTool() throws Exception {
|
|||
|
||||
@Test
|
||||
public void testToolTrustExitCode() throws Exception {
|
||||
RevCommit commit = createUnstagedChanges();
|
||||
List<DiffEntry> changes = getRepositoryChanges(commit);
|
||||
String[] expectedOutput = getExpectedToolOutputNoPrompt(changes);
|
||||
String[] conflictingFilenames = createUnstagedChanges();
|
||||
String[] expectedOutput = getExpectedToolOutputNoPrompt(conflictingFilenames);
|
||||
|
||||
String[] options = { "--tool", "-t", };
|
||||
|
||||
|
@ -126,9 +120,8 @@ expectedOutput, runAndCaptureUsingInitRaw(DIFF_TOOL,
|
|||
|
||||
@Test
|
||||
public void testToolNoGuiNoPromptNoTrustExitcode() throws Exception {
|
||||
RevCommit commit = createUnstagedChanges();
|
||||
List<DiffEntry> changes = getRepositoryChanges(commit);
|
||||
String[] expectedOutput = getExpectedToolOutputNoPrompt(changes);
|
||||
String[] conflictingFilenames = createUnstagedChanges();
|
||||
String[] expectedOutput = getExpectedToolOutputNoPrompt(conflictingFilenames);
|
||||
|
||||
String[] options = { "--tool", "-t", };
|
||||
|
||||
|
@ -142,9 +135,8 @@ expectedOutput, runAndCaptureUsingInitRaw(DIFF_TOOL,
|
|||
|
||||
@Test
|
||||
public void testToolCached() throws Exception {
|
||||
RevCommit commit = createStagedChanges();
|
||||
List<DiffEntry> changes = getRepositoryChanges(commit);
|
||||
String[] expectedOutput = getExpectedToolOutputNoPrompt(changes);
|
||||
String[] conflictingFilenames = createStagedChanges();
|
||||
String[] expectedOutput = getExpectedToolOutputNoPrompt(conflictingFilenames);
|
||||
|
||||
String[] options = { "--cached", "--staged", };
|
||||
|
||||
|
@ -201,23 +193,21 @@ private void configureEchoTool(String toolName) {
|
|||
String.valueOf(false));
|
||||
}
|
||||
|
||||
private static String[] getExpectedToolOutputNoPrompt(List<DiffEntry> changes) {
|
||||
String[] expectedToolOutput = new String[changes.size()];
|
||||
for (int i = 0; i < changes.size(); ++i) {
|
||||
DiffEntry change = changes.get(i);
|
||||
String newPath = change.getNewPath();
|
||||
private static String[] getExpectedToolOutputNoPrompt(String[] conflictingFilenames) {
|
||||
String[] expectedToolOutput = new String[conflictingFilenames.length];
|
||||
for (int i = 0; i < conflictingFilenames.length; ++i) {
|
||||
String newPath = conflictingFilenames[i];
|
||||
String expectedLine = newPath;
|
||||
expectedToolOutput[i] = expectedLine;
|
||||
}
|
||||
return expectedToolOutput;
|
||||
}
|
||||
|
||||
private static String[] getExpectedCompareOutput(List<DiffEntry> changes) {
|
||||
private static String[] getExpectedCompareOutput(String[] conflictingFilenames) {
|
||||
List<String> expected = new ArrayList<>();
|
||||
int n = changes.size();
|
||||
int n = conflictingFilenames.length;
|
||||
for (int i = 0; i < n; ++i) {
|
||||
DiffEntry change = changes.get(i);
|
||||
String newPath = change.getNewPath();
|
||||
String newPath = conflictingFilenames[i];
|
||||
expected.add(
|
||||
"Viewing (" + (i + 1) + "/" + n + "): '" + newPath + "'");
|
||||
expected.add("Launch '" + TOOL_NAME + "' [Y/n]?");
|
||||
|
@ -226,13 +216,12 @@ private static String[] getExpectedCompareOutput(List<DiffEntry> changes) {
|
|||
return expected.toArray(new String[0]);
|
||||
}
|
||||
|
||||
private static String[] getExpectedAbortOutput(List<DiffEntry> changes,
|
||||
private static String[] getExpectedAbortOutput(String[] conflictingFilenames,
|
||||
int abortIndex) {
|
||||
List<String> expected = new ArrayList<>();
|
||||
int n = changes.size();
|
||||
int n = conflictingFilenames.length;
|
||||
for (int i = 0; i < n; ++i) {
|
||||
DiffEntry change = changes.get(i);
|
||||
String newPath = change.getNewPath();
|
||||
String newPath = conflictingFilenames[i];
|
||||
expected.add(
|
||||
"Viewing (" + (i + 1) + "/" + n + "): '" + newPath + "'");
|
||||
expected.add("Launch '" + TOOL_NAME + "' [Y/n]?");
|
||||
|
|
|
@ -94,15 +94,15 @@ protected String[] runAndCaptureUsingInitRaw(InputStream inputStream,
|
|||
protected String[] createMergeConflict() throws Exception {
|
||||
// create files on initial branch
|
||||
git.checkout().setName(TEST_BRANCH_NAME).call();
|
||||
writeTrashFile("a", "Hello world a");
|
||||
writeTrashFile("b", "Hello world b");
|
||||
writeTrashFile("dir1/a", "Hello world a");
|
||||
writeTrashFile("dir2/b", "Hello world b");
|
||||
git.add().addFilepattern(".").call();
|
||||
git.commit().setMessage("files a & b added").call();
|
||||
// create another branch and change files
|
||||
git.branchCreate().setName("branch_1").call();
|
||||
git.checkout().setName("branch_1").call();
|
||||
writeTrashFile("a", "Hello world a 1");
|
||||
writeTrashFile("b", "Hello world b 1");
|
||||
writeTrashFile("dir1/a", "Hello world a 1");
|
||||
writeTrashFile("dir2/b", "Hello world b 1");
|
||||
git.add().addFilepattern(".").call();
|
||||
RevCommit commit1 = git.commit()
|
||||
.setMessage("files a & b modified commit 1").call();
|
||||
|
@ -111,28 +111,28 @@ protected String[] createMergeConflict() throws Exception {
|
|||
// create another branch and change files
|
||||
git.branchCreate().setName("branch_2").call();
|
||||
git.checkout().setName("branch_2").call();
|
||||
writeTrashFile("a", "Hello world a 2");
|
||||
writeTrashFile("b", "Hello world b 2");
|
||||
writeTrashFile("dir1/a", "Hello world a 2");
|
||||
writeTrashFile("dir2/b", "Hello world b 2");
|
||||
git.add().addFilepattern(".").call();
|
||||
git.commit().setMessage("files a & b modified commit 2").call();
|
||||
// cherry-pick conflicting changes
|
||||
git.cherryPick().include(commit1).call();
|
||||
String[] conflictingFilenames = { "a", "b" };
|
||||
String[] conflictingFilenames = { "dir1/a", "dir2/b" };
|
||||
return conflictingFilenames;
|
||||
}
|
||||
|
||||
protected String[] createDeletedConflict() throws Exception {
|
||||
// create files on initial branch
|
||||
git.checkout().setName(TEST_BRANCH_NAME).call();
|
||||
writeTrashFile("a", "Hello world a");
|
||||
writeTrashFile("b", "Hello world b");
|
||||
writeTrashFile("dir1/a", "Hello world a");
|
||||
writeTrashFile("dir2/b", "Hello world b");
|
||||
git.add().addFilepattern(".").call();
|
||||
git.commit().setMessage("files a & b added").call();
|
||||
// create another branch and change files
|
||||
git.branchCreate().setName("branch_1").call();
|
||||
git.checkout().setName("branch_1").call();
|
||||
writeTrashFile("a", "Hello world a 1");
|
||||
writeTrashFile("b", "Hello world b 1");
|
||||
writeTrashFile("dir1/a", "Hello world a 1");
|
||||
writeTrashFile("dir2/b", "Hello world b 1");
|
||||
git.add().addFilepattern(".").call();
|
||||
RevCommit commit1 = git.commit()
|
||||
.setMessage("files a & b modified commit 1").call();
|
||||
|
@ -141,29 +141,30 @@ protected String[] createDeletedConflict() throws Exception {
|
|||
// create another branch and change files
|
||||
git.branchCreate().setName("branch_2").call();
|
||||
git.checkout().setName("branch_2").call();
|
||||
git.rm().addFilepattern("a").call();
|
||||
git.rm().addFilepattern("b").call();
|
||||
git.rm().addFilepattern("dir1/a").call();
|
||||
git.rm().addFilepattern("dir2/b").call();
|
||||
git.commit().setMessage("files a & b deleted commit 2").call();
|
||||
// cherry-pick conflicting changes
|
||||
git.cherryPick().include(commit1).call();
|
||||
String[] conflictingFilenames = { "a", "b" };
|
||||
String[] conflictingFilenames = { "dir1/a", "dir2/b" };
|
||||
return conflictingFilenames;
|
||||
}
|
||||
|
||||
protected RevCommit createUnstagedChanges() throws Exception {
|
||||
writeTrashFile("a", "Hello world a");
|
||||
writeTrashFile("b", "Hello world b");
|
||||
protected String[] createUnstagedChanges() throws Exception {
|
||||
writeTrashFile("dir1/a", "Hello world a");
|
||||
writeTrashFile("dir2/b", "Hello world b");
|
||||
git.add().addFilepattern(".").call();
|
||||
RevCommit commit = git.commit().setMessage("files a & b").call();
|
||||
writeTrashFile("a", "New Hello world a");
|
||||
writeTrashFile("b", "New Hello world b");
|
||||
return commit;
|
||||
git.commit().setMessage("files a & b").call();
|
||||
writeTrashFile("dir1/a", "New Hello world a");
|
||||
writeTrashFile("dir2/b", "New Hello world b");
|
||||
String[] conflictingFilenames = { "dir1/a", "dir2/b" };
|
||||
return conflictingFilenames;
|
||||
}
|
||||
|
||||
protected RevCommit createStagedChanges() throws Exception {
|
||||
RevCommit commit = createUnstagedChanges();
|
||||
protected String[] createStagedChanges() throws Exception {
|
||||
String[] conflictingFilenames = createUnstagedChanges();
|
||||
git.add().addFilepattern(".").call();
|
||||
return commit;
|
||||
return conflictingFilenames;
|
||||
}
|
||||
|
||||
protected List<DiffEntry> getRepositoryChanges(RevCommit commit)
|
||||
|
|
|
@ -11,9 +11,11 @@
|
|||
package org.eclipse.jgit.pgm;
|
||||
|
||||
import static org.eclipse.jgit.lib.Constants.HEAD;
|
||||
import static org.eclipse.jgit.treewalk.TreeWalk.OperationType.CHECKOUT_OP;
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
@ -25,27 +27,36 @@
|
|||
import org.eclipse.jgit.diff.ContentSource.Pair;
|
||||
import org.eclipse.jgit.diff.DiffEntry;
|
||||
import org.eclipse.jgit.diff.DiffEntry.Side;
|
||||
import org.eclipse.jgit.diff.DiffFormatter;
|
||||
import org.eclipse.jgit.dircache.DirCacheIterator;
|
||||
import org.eclipse.jgit.errors.AmbiguousObjectException;
|
||||
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
|
||||
import org.eclipse.jgit.errors.RevisionSyntaxException;
|
||||
import org.eclipse.jgit.internal.diffmergetool.DiffTools;
|
||||
import org.eclipse.jgit.internal.diffmergetool.ExternalDiffTool;
|
||||
import org.eclipse.jgit.internal.diffmergetool.FileElement;
|
||||
import org.eclipse.jgit.internal.diffmergetool.ToolException;
|
||||
import org.eclipse.jgit.internal.diffmergetool.DiffTools;
|
||||
import org.eclipse.jgit.internal.diffmergetool.FileElement;
|
||||
import org.eclipse.jgit.internal.diffmergetool.ExternalDiffTool;
|
||||
import org.eclipse.jgit.diff.DiffFormatter;
|
||||
import org.eclipse.jgit.dircache.DirCacheCheckout;
|
||||
import org.eclipse.jgit.dircache.DirCacheIterator;
|
||||
import org.eclipse.jgit.dircache.DirCacheCheckout.CheckoutMetadata;
|
||||
import org.eclipse.jgit.errors.AmbiguousObjectException;
|
||||
import org.eclipse.jgit.errors.CorruptObjectException;
|
||||
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
|
||||
import org.eclipse.jgit.errors.NoWorkTreeException;
|
||||
import org.eclipse.jgit.errors.RevisionSyntaxException;
|
||||
import org.eclipse.jgit.lib.Constants;
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
import org.eclipse.jgit.lib.ObjectReader;
|
||||
import org.eclipse.jgit.lib.ObjectStream;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
import org.eclipse.jgit.lib.TextProgressMonitor;
|
||||
import org.eclipse.jgit.lib.CoreConfig.EolStreamType;
|
||||
import org.eclipse.jgit.lib.internal.BooleanTriState;
|
||||
import org.eclipse.jgit.pgm.internal.CLIText;
|
||||
import org.eclipse.jgit.pgm.opt.PathTreeFilterHandler;
|
||||
import org.eclipse.jgit.revwalk.RevWalk;
|
||||
import org.eclipse.jgit.treewalk.AbstractTreeIterator;
|
||||
import org.eclipse.jgit.treewalk.CanonicalTreeParser;
|
||||
import org.eclipse.jgit.treewalk.FileTreeIterator;
|
||||
import org.eclipse.jgit.treewalk.TreeWalk;
|
||||
import org.eclipse.jgit.treewalk.WorkingTreeIterator;
|
||||
import org.eclipse.jgit.treewalk.WorkingTreeOptions;
|
||||
import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
|
||||
import org.eclipse.jgit.treewalk.filter.TreeFilter;
|
||||
import org.eclipse.jgit.util.StringUtils;
|
||||
import org.eclipse.jgit.util.FS.ExecutionResult;
|
||||
|
@ -164,12 +175,6 @@ private void compare(List<DiffEntry> files, boolean showPrompt,
|
|||
if (mergedFilePath.equals(DiffEntry.DEV_NULL)) {
|
||||
mergedFilePath = ent.getOldPath();
|
||||
}
|
||||
FileElement local = new FileElement(ent.getOldPath(),
|
||||
ent.getOldId().name(),
|
||||
getObjectStream(sourcePair, Side.OLD, ent));
|
||||
FileElement remote = new FileElement(ent.getNewPath(),
|
||||
ent.getNewId().name(),
|
||||
getObjectStream(sourcePair, Side.NEW, ent));
|
||||
// check if user wants to launch compare
|
||||
boolean launchCompare = true;
|
||||
if (showPrompt) {
|
||||
|
@ -178,15 +183,20 @@ private void compare(List<DiffEntry> files, boolean showPrompt,
|
|||
}
|
||||
if (launchCompare) {
|
||||
try {
|
||||
// TODO: check how to return the exit-code of
|
||||
// the
|
||||
// tool
|
||||
// to
|
||||
// jgit / java runtime ?
|
||||
FileElement local = createFileElement(
|
||||
FileElement.Type.LOCAL, sourcePair, Side.OLD,
|
||||
ent);
|
||||
FileElement remote = createFileElement(
|
||||
FileElement.Type.REMOTE, sourcePair, Side.NEW,
|
||||
ent);
|
||||
FileElement merged = new FileElement(mergedFilePath,
|
||||
FileElement.Type.MERGED);
|
||||
// TODO: check how to return the exit-code of the tool
|
||||
// to jgit / java runtime ?
|
||||
// int rc =...
|
||||
ExecutionResult result = diffTools.compare(db, local,
|
||||
remote, mergedFilePath,
|
||||
toolName, prompt, gui, trustExitCode);
|
||||
ExecutionResult result = diffTools.compare(local,
|
||||
remote, merged, toolName, prompt, gui,
|
||||
trustExitCode);
|
||||
outw.println(new String(result.getStdout().toByteArray()));
|
||||
errw.println(
|
||||
new String(result.getStderr().toByteArray()));
|
||||
|
@ -278,16 +288,46 @@ private List<DiffEntry> getFiles()
|
|||
return files;
|
||||
}
|
||||
|
||||
private ObjectStream getObjectStream(Pair pair, Side side, DiffEntry ent) {
|
||||
ObjectStream stream = null;
|
||||
if (!pair.isWorkingTreeSource(side)) {
|
||||
try {
|
||||
stream = pair.open(side, ent).openStream();
|
||||
} catch (Exception e) {
|
||||
stream = null;
|
||||
private FileElement createFileElement(FileElement.Type elementType,
|
||||
Pair pair, Side side, DiffEntry entry)
|
||||
throws NoWorkTreeException, CorruptObjectException, IOException,
|
||||
ToolException {
|
||||
String entryPath = side == Side.NEW ? entry.getNewPath()
|
||||
: entry.getOldPath();
|
||||
FileElement fileElement = new FileElement(entryPath, elementType);
|
||||
if (!pair.isWorkingTreeSource(side) && !fileElement.isNullPath()) {
|
||||
try (RevWalk revWalk = new RevWalk(db);
|
||||
TreeWalk treeWalk = new TreeWalk(db,
|
||||
revWalk.getObjectReader())) {
|
||||
treeWalk.setFilter(
|
||||
PathFilterGroup.createFromStrings(entryPath));
|
||||
if (side == Side.NEW) {
|
||||
newTree.reset();
|
||||
treeWalk.addTree(newTree);
|
||||
} else {
|
||||
oldTree.reset();
|
||||
treeWalk.addTree(oldTree);
|
||||
}
|
||||
if (treeWalk.next()) {
|
||||
final EolStreamType eolStreamType = treeWalk
|
||||
.getEolStreamType(CHECKOUT_OP);
|
||||
final String filterCommand = treeWalk.getFilterCommand(
|
||||
Constants.ATTR_FILTER_TYPE_SMUDGE);
|
||||
WorkingTreeOptions opt = db.getConfig()
|
||||
.get(WorkingTreeOptions.KEY);
|
||||
CheckoutMetadata checkoutMetadata = new CheckoutMetadata(
|
||||
eolStreamType, filterCommand);
|
||||
DirCacheCheckout.getContent(db, entryPath,
|
||||
checkoutMetadata, pair.open(side, entry), opt,
|
||||
new FileOutputStream(
|
||||
fileElement.createTempFile(null)));
|
||||
} else {
|
||||
throw new ToolException("Cannot find path '" + entryPath //$NON-NLS-1$
|
||||
+ "' in staging area!", null); //$NON-NLS-1$
|
||||
}
|
||||
}
|
||||
}
|
||||
return stream;
|
||||
return fileElement;
|
||||
}
|
||||
|
||||
private ContentSource source(AbstractTreeIterator iterator) {
|
||||
|
|
|
@ -10,8 +10,11 @@
|
|||
|
||||
package org.eclipse.jgit.pgm;
|
||||
|
||||
import static org.eclipse.jgit.treewalk.TreeWalk.OperationType.CHECKOUT_OP;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.text.MessageFormat;
|
||||
|
@ -26,8 +29,12 @@
|
|||
import org.eclipse.jgit.api.StatusCommand;
|
||||
import org.eclipse.jgit.api.errors.GitAPIException;
|
||||
import org.eclipse.jgit.diff.ContentSource;
|
||||
import org.eclipse.jgit.internal.diffmergetool.FileElement.Type;
|
||||
import org.eclipse.jgit.dircache.DirCache;
|
||||
import org.eclipse.jgit.dircache.DirCacheCheckout;
|
||||
import org.eclipse.jgit.dircache.DirCacheEntry;
|
||||
import org.eclipse.jgit.dircache.DirCacheIterator;
|
||||
import org.eclipse.jgit.dircache.DirCacheCheckout.CheckoutMetadata;
|
||||
import org.eclipse.jgit.errors.NoWorkTreeException;
|
||||
import org.eclipse.jgit.errors.RevisionSyntaxException;
|
||||
import org.eclipse.jgit.internal.diffmergetool.ExternalMergeTool;
|
||||
|
@ -35,9 +42,15 @@
|
|||
import org.eclipse.jgit.internal.diffmergetool.MergeTools;
|
||||
import org.eclipse.jgit.internal.diffmergetool.ToolException;
|
||||
import org.eclipse.jgit.lib.IndexDiff.StageState;
|
||||
import org.eclipse.jgit.revwalk.RevWalk;
|
||||
import org.eclipse.jgit.treewalk.TreeWalk;
|
||||
import org.eclipse.jgit.treewalk.WorkingTreeOptions;
|
||||
import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
|
||||
import org.eclipse.jgit.lib.Constants;
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
import org.eclipse.jgit.lib.internal.BooleanTriState;
|
||||
import org.eclipse.jgit.lib.CoreConfig.EolStreamType;
|
||||
import org.eclipse.jgit.pgm.internal.CLIText;
|
||||
import org.eclipse.jgit.util.FS.ExecutionResult;
|
||||
import org.kohsuke.args4j.Argument;
|
||||
|
@ -188,32 +201,67 @@ private MergeResult mergeModified(String mergedFilePath, boolean showPrompt,
|
|||
ContentSource baseSource = ContentSource.create(db.newObjectReader());
|
||||
ContentSource localSource = ContentSource.create(db.newObjectReader());
|
||||
ContentSource remoteSource = ContentSource.create(db.newObjectReader());
|
||||
// temporary directory if mergetool.writeToTemp == true
|
||||
File tempDir = mergeTools.createTempDirectory();
|
||||
// the parent directory for temp files (can be same as tempDir or just
|
||||
// the worktree dir)
|
||||
File tempFilesParent = tempDir != null ? tempDir : db.getWorkTree();
|
||||
try {
|
||||
FileElement base = null;
|
||||
FileElement local = null;
|
||||
FileElement remote = null;
|
||||
FileElement merged = new FileElement(mergedFilePath,
|
||||
Type.MERGED);
|
||||
DirCache cache = db.readDirCache();
|
||||
int firstIndex = cache.findEntry(mergedFilePath);
|
||||
if (firstIndex >= 0) {
|
||||
int nextIndex = cache.nextEntry(firstIndex);
|
||||
for (; firstIndex < nextIndex; firstIndex++) {
|
||||
DirCacheEntry entry = cache.getEntry(firstIndex);
|
||||
try (RevWalk revWalk = new RevWalk(db);
|
||||
TreeWalk treeWalk = new TreeWalk(db,
|
||||
revWalk.getObjectReader())) {
|
||||
treeWalk.setFilter(
|
||||
PathFilterGroup.createFromStrings(mergedFilePath));
|
||||
DirCacheIterator cacheIter = new DirCacheIterator(cache);
|
||||
treeWalk.addTree(cacheIter);
|
||||
while (treeWalk.next()) {
|
||||
if (treeWalk.isSubtree()) {
|
||||
treeWalk.enterSubtree();
|
||||
continue;
|
||||
}
|
||||
final EolStreamType eolStreamType = treeWalk
|
||||
.getEolStreamType(CHECKOUT_OP);
|
||||
final String filterCommand = treeWalk.getFilterCommand(
|
||||
Constants.ATTR_FILTER_TYPE_SMUDGE);
|
||||
WorkingTreeOptions opt = db.getConfig()
|
||||
.get(WorkingTreeOptions.KEY);
|
||||
CheckoutMetadata checkoutMetadata = new CheckoutMetadata(
|
||||
eolStreamType, filterCommand);
|
||||
DirCacheEntry entry = treeWalk.getTree(DirCacheIterator.class).getDirCacheEntry();
|
||||
if (entry == null) {
|
||||
continue;
|
||||
}
|
||||
ObjectId id = entry.getObjectId();
|
||||
switch (entry.getStage()) {
|
||||
case DirCacheEntry.STAGE_1:
|
||||
base = new FileElement(mergedFilePath, id.name(),
|
||||
baseSource.open(mergedFilePath, id)
|
||||
.openStream());
|
||||
base = new FileElement(mergedFilePath, Type.BASE);
|
||||
DirCacheCheckout.getContent(db, mergedFilePath,
|
||||
checkoutMetadata,
|
||||
baseSource.open(mergedFilePath, id), opt,
|
||||
new FileOutputStream(
|
||||
base.createTempFile(tempFilesParent)));
|
||||
break;
|
||||
case DirCacheEntry.STAGE_2:
|
||||
local = new FileElement(mergedFilePath, id.name(),
|
||||
localSource.open(mergedFilePath, id)
|
||||
.openStream());
|
||||
local = new FileElement(mergedFilePath, Type.LOCAL);
|
||||
DirCacheCheckout.getContent(db, mergedFilePath,
|
||||
checkoutMetadata,
|
||||
localSource.open(mergedFilePath, id), opt,
|
||||
new FileOutputStream(
|
||||
local.createTempFile(tempFilesParent)));
|
||||
break;
|
||||
case DirCacheEntry.STAGE_3:
|
||||
remote = new FileElement(mergedFilePath, id.name(),
|
||||
remoteSource.open(mergedFilePath, id)
|
||||
.openStream());
|
||||
remote = new FileElement(mergedFilePath, Type.REMOTE);
|
||||
DirCacheCheckout.getContent(db, mergedFilePath,
|
||||
checkoutMetadata,
|
||||
remoteSource.open(mergedFilePath, id), opt,
|
||||
new FileOutputStream(remote
|
||||
.createTempFile(tempFilesParent)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -222,14 +270,13 @@ private MergeResult mergeModified(String mergedFilePath, boolean showPrompt,
|
|||
throw die(MessageFormat.format(CLIText.get().mergeToolDied,
|
||||
mergedFilePath));
|
||||
}
|
||||
File merged = new File(mergedFilePath);
|
||||
long modifiedBefore = merged.lastModified();
|
||||
long modifiedBefore = merged.getFile().lastModified();
|
||||
try {
|
||||
// TODO: check how to return the exit-code of the
|
||||
// tool to jgit / java runtime ?
|
||||
// int rc =...
|
||||
ExecutionResult executionResult = mergeTools.merge(db, local,
|
||||
remote, base, mergedFilePath, toolName, prompt, gui);
|
||||
ExecutionResult executionResult = mergeTools.merge(local,
|
||||
remote, merged, base, tempDir, toolName, prompt, gui);
|
||||
outw.println(
|
||||
new String(executionResult.getStdout().toByteArray()));
|
||||
outw.flush();
|
||||
|
@ -250,7 +297,7 @@ private MergeResult mergeModified(String mergedFilePath, boolean showPrompt,
|
|||
}
|
||||
// if merge was successful check file modified
|
||||
if (isMergeSuccessful) {
|
||||
long modifiedAfter = merged.lastModified();
|
||||
long modifiedAfter = merged.getFile().lastModified();
|
||||
if (modifiedBefore == modifiedAfter) {
|
||||
outw.println(MessageFormat.format(
|
||||
CLIText.get().mergeToolFileUnchanged,
|
||||
|
|
|
@ -54,8 +54,8 @@ public void testUserToolWithError() throws Exception {
|
|||
BooleanTriState gui = BooleanTriState.UNSET;
|
||||
BooleanTriState trustExitCode = BooleanTriState.TRUE;
|
||||
|
||||
manager.compare(db, local, remote, merged.getPath(), toolName, prompt,
|
||||
gui, trustExitCode);
|
||||
manager.compare(local, remote, merged, toolName, prompt, gui,
|
||||
trustExitCode);
|
||||
|
||||
fail("Expected exception to be thrown due to external tool exiting with error code: "
|
||||
+ errorReturnCode);
|
||||
|
@ -78,8 +78,8 @@ public void testUserToolWithCommandNotFoundError() throws Exception {
|
|||
BooleanTriState gui = BooleanTriState.UNSET;
|
||||
BooleanTriState trustExitCode = BooleanTriState.FALSE;
|
||||
|
||||
manager.compare(db, local, remote, merged.getPath(), toolName, prompt,
|
||||
gui, trustExitCode);
|
||||
manager.compare(local, remote, merged, toolName, prompt, gui,
|
||||
trustExitCode);
|
||||
|
||||
fail("Expected exception to be thrown due to external tool exiting with error code: "
|
||||
+ errorReturnCode);
|
||||
|
@ -183,8 +183,8 @@ public void testCompare() throws ToolException {
|
|||
DiffTools manager = new DiffTools(db);
|
||||
|
||||
int expectedCompareResult = 0;
|
||||
ExecutionResult compareResult = manager.compare(db, local, remote,
|
||||
merged.getPath(), toolName, prompt, gui, trustExitCode);
|
||||
ExecutionResult compareResult = manager.compare(local, remote, merged,
|
||||
toolName, prompt, gui, trustExitCode);
|
||||
assertEquals("Incorrect compare result for external diff tool",
|
||||
expectedCompareResult, compareResult.getRc());
|
||||
}
|
||||
|
@ -263,8 +263,8 @@ public void testUndefinedTool() throws Exception {
|
|||
BooleanTriState gui = BooleanTriState.UNSET;
|
||||
BooleanTriState trustExitCode = BooleanTriState.UNSET;
|
||||
|
||||
manager.compare(db, local, remote, merged.getPath(), toolName, prompt,
|
||||
gui, trustExitCode);
|
||||
manager.compare(local, remote, merged, toolName, prompt, gui,
|
||||
trustExitCode);
|
||||
fail("Expected exception to be thrown due to not defined external diff tool");
|
||||
}
|
||||
|
||||
|
|
|
@ -55,8 +55,7 @@ public void testUserToolWithError() throws Exception {
|
|||
BooleanTriState prompt = BooleanTriState.UNSET;
|
||||
BooleanTriState gui = BooleanTriState.UNSET;
|
||||
|
||||
manager.merge(db, local, remote, base, merged.getPath(), toolName,
|
||||
prompt, gui);
|
||||
manager.merge(local, remote, merged, base, null, toolName, prompt, gui);
|
||||
|
||||
fail("Expected exception to be thrown due to external tool exiting with error code: "
|
||||
+ errorReturnCode);
|
||||
|
@ -78,8 +77,7 @@ public void testUserToolWithCommandNotFoundError() throws Exception {
|
|||
BooleanTriState prompt = BooleanTriState.UNSET;
|
||||
BooleanTriState gui = BooleanTriState.UNSET;
|
||||
|
||||
manager.merge(db, local, remote, base, merged.getPath(), toolName,
|
||||
prompt, gui);
|
||||
manager.merge(local, remote, merged, base, null, toolName, prompt, gui);
|
||||
|
||||
fail("Expected exception to be thrown due to external tool exiting with error code: "
|
||||
+ errorReturnCode);
|
||||
|
@ -182,8 +180,8 @@ public void testCompare() throws ToolException {
|
|||
MergeTools manager = new MergeTools(db);
|
||||
|
||||
int expectedCompareResult = 0;
|
||||
ExecutionResult compareResult = manager.merge(db, local, remote, base,
|
||||
merged.getPath(), toolName, prompt, gui);
|
||||
ExecutionResult compareResult = manager.merge(local, remote, merged,
|
||||
base, null, toolName, prompt, gui);
|
||||
assertEquals("Incorrect compare result for external merge tool",
|
||||
expectedCompareResult, compareResult.getRc());
|
||||
}
|
||||
|
@ -262,8 +260,7 @@ public void testUndefinedTool() throws Exception {
|
|||
BooleanTriState prompt = BooleanTriState.UNSET;
|
||||
BooleanTriState gui = BooleanTriState.UNSET;
|
||||
|
||||
manager.merge(db, local, remote, base, merged.getPath(), toolName,
|
||||
prompt, gui);
|
||||
manager.merge(local, remote, merged, base, null, toolName, prompt, gui);
|
||||
fail("Expected exception to be thrown due to not defined external merge tool");
|
||||
}
|
||||
|
||||
|
|
|
@ -60,10 +60,14 @@ public void setUp() throws Exception {
|
|||
commandResult = writeTrashFile("commandResult.txt", "");
|
||||
commandResult.deleteOnExit();
|
||||
|
||||
local = new FileElement(localFile.getAbsolutePath(), "LOCAL");
|
||||
remote = new FileElement(remoteFile.getAbsolutePath(), "REMOTE");
|
||||
merged = new FileElement(mergedFile.getAbsolutePath(), "MERGED");
|
||||
base = new FileElement(baseFile.getAbsolutePath(), "BASE");
|
||||
local = new FileElement(localFile.getAbsolutePath(),
|
||||
FileElement.Type.LOCAL);
|
||||
remote = new FileElement(remoteFile.getAbsolutePath(),
|
||||
FileElement.Type.REMOTE);
|
||||
merged = new FileElement(mergedFile.getAbsolutePath(),
|
||||
FileElement.Type.MERGED);
|
||||
base = new FileElement(baseFile.getAbsolutePath(),
|
||||
FileElement.Type.BASE);
|
||||
}
|
||||
|
||||
@After
|
||||
|
|
|
@ -12,12 +12,10 @@
|
|||
|
||||
import java.util.TreeMap;
|
||||
import java.util.Collections;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jgit.lib.Constants;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
import org.eclipse.jgit.lib.internal.BooleanTriState;
|
||||
import org.eclipse.jgit.util.FS.ExecutionResult;
|
||||
|
@ -28,6 +26,8 @@
|
|||
*/
|
||||
public class DiffTools {
|
||||
|
||||
private final Repository repo;
|
||||
|
||||
private final DiffToolConfig config;
|
||||
|
||||
private final Map<String, ExternalDiffTool> predefinedTools;
|
||||
|
@ -41,6 +41,7 @@ public class DiffTools {
|
|||
* the repository
|
||||
*/
|
||||
public DiffTools(Repository repo) {
|
||||
this.repo = repo;
|
||||
config = repo.getConfig().get(DiffToolConfig.KEY);
|
||||
predefinedTools = setupPredefinedTools();
|
||||
userDefinedTools = setupUserDefinedTools(config, predefinedTools);
|
||||
|
@ -49,14 +50,13 @@ public DiffTools(Repository repo) {
|
|||
/**
|
||||
* Compare two versions of a file.
|
||||
*
|
||||
* @param repo
|
||||
* the repository
|
||||
* @param localFile
|
||||
* the local file element
|
||||
* @param remoteFile
|
||||
* the remote file element
|
||||
* @param mergedFilePath
|
||||
* the path of 'merged' file, it equals local or remote path
|
||||
* @param mergedFile
|
||||
* the merged file element, it's path equals local or remote
|
||||
* element path
|
||||
* @param toolName
|
||||
* the selected tool name (can be null)
|
||||
* @param prompt
|
||||
|
@ -68,36 +68,31 @@ public DiffTools(Repository repo) {
|
|||
* @return the execution result from tool
|
||||
* @throws ToolException
|
||||
*/
|
||||
public ExecutionResult compare(Repository repo, FileElement localFile,
|
||||
FileElement remoteFile, String mergedFilePath, String toolName,
|
||||
public ExecutionResult compare(FileElement localFile,
|
||||
FileElement remoteFile, FileElement mergedFile, String toolName,
|
||||
BooleanTriState prompt, BooleanTriState gui,
|
||||
BooleanTriState trustExitCode) throws ToolException {
|
||||
ExternalDiffTool tool = guessTool(toolName, gui);
|
||||
try {
|
||||
File workingDir = repo.getWorkTree();
|
||||
String localFilePath = localFile.getFile().getPath();
|
||||
String remoteFilePath = remoteFile.getFile().getPath();
|
||||
String command = tool.getCommand();
|
||||
command = command.replace("$LOCAL", localFilePath); //$NON-NLS-1$
|
||||
command = command.replace("$REMOTE", remoteFilePath); //$NON-NLS-1$
|
||||
command = command.replace("$MERGED", mergedFilePath); //$NON-NLS-1$
|
||||
Map<String, String> env = new TreeMap<>();
|
||||
env.put(Constants.GIT_DIR_KEY,
|
||||
repo.getDirectory().getAbsolutePath());
|
||||
env.put("LOCAL", localFilePath); //$NON-NLS-1$
|
||||
env.put("REMOTE", remoteFilePath); //$NON-NLS-1$
|
||||
env.put("MERGED", mergedFilePath); //$NON-NLS-1$
|
||||
// prepare the command (replace the file paths)
|
||||
String command = ExternalToolUtils.prepareCommand(
|
||||
guessTool(toolName, gui).getCommand(), localFile,
|
||||
remoteFile, mergedFile, null);
|
||||
// prepare the environment
|
||||
Map<String, String> env = ExternalToolUtils.prepareEnvironment(repo,
|
||||
localFile, remoteFile, mergedFile, null);
|
||||
boolean trust = config.isTrustExitCode();
|
||||
if (trustExitCode != BooleanTriState.UNSET) {
|
||||
trust = trustExitCode == BooleanTriState.TRUE;
|
||||
}
|
||||
// execute the tool
|
||||
CommandExecutor cmdExec = new CommandExecutor(repo.getFS(), trust);
|
||||
return cmdExec.run(command, workingDir, env);
|
||||
return cmdExec.run(command, repo.getWorkTree(), env);
|
||||
} catch (IOException | InterruptedException e) {
|
||||
throw new ToolException(e);
|
||||
} finally {
|
||||
localFile.cleanTemporaries();
|
||||
remoteFile.cleanTemporaries();
|
||||
mergedFile.cleanTemporaries();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* Copyright (C) 2018-2021, Andre Bossert <andre.bossert@siemens.com>
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Distribution License v. 1.0 which is available at
|
||||
* https://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
package org.eclipse.jgit.internal.diffmergetool;
|
||||
|
||||
import java.util.TreeMap;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import org.eclipse.jgit.lib.Constants;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
|
||||
/**
|
||||
* Utilities for diff- and merge-tools.
|
||||
*/
|
||||
public class ExternalToolUtils {
|
||||
|
||||
/**
|
||||
* Prepare command for execution.
|
||||
*
|
||||
* @param command
|
||||
* the input "command" string
|
||||
* @param localFile
|
||||
* the local file (ours)
|
||||
* @param remoteFile
|
||||
* the remote file (theirs)
|
||||
* @param mergedFile
|
||||
* the merged file (worktree)
|
||||
* @param baseFile
|
||||
* the base file (can be null)
|
||||
* @return the prepared (with replaced variables) command string
|
||||
* @throws IOException
|
||||
*/
|
||||
public static String prepareCommand(String command, FileElement localFile,
|
||||
FileElement remoteFile, FileElement mergedFile,
|
||||
FileElement baseFile) throws IOException {
|
||||
command = localFile.replaceVariable(command);
|
||||
command = remoteFile.replaceVariable(command);
|
||||
command = mergedFile.replaceVariable(command);
|
||||
if (baseFile != null) {
|
||||
command = baseFile.replaceVariable(command);
|
||||
}
|
||||
return command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare environment needed for execution.
|
||||
*
|
||||
* @param repo
|
||||
* the repository
|
||||
* @param localFile
|
||||
* the local file (ours)
|
||||
* @param remoteFile
|
||||
* the remote file (theirs)
|
||||
* @param mergedFile
|
||||
* the merged file (worktree)
|
||||
* @param baseFile
|
||||
* the base file (can be null)
|
||||
* @return the environment map with variables and values (file paths)
|
||||
* @throws IOException
|
||||
*/
|
||||
public static Map<String, String> prepareEnvironment(Repository repo,
|
||||
FileElement localFile, FileElement remoteFile,
|
||||
FileElement mergedFile, FileElement baseFile) throws IOException {
|
||||
Map<String, String> env = new TreeMap<>();
|
||||
env.put(Constants.GIT_DIR_KEY, repo.getDirectory().getAbsolutePath());
|
||||
localFile.addToEnv(env);
|
||||
remoteFile.addToEnv(env);
|
||||
mergedFile.addToEnv(env);
|
||||
if (baseFile != null) {
|
||||
baseFile.addToEnv(env);
|
||||
}
|
||||
return env;
|
||||
}
|
||||
|
||||
}
|
|
@ -14,10 +14,11 @@
|
|||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jgit.diff.DiffEntry;
|
||||
import org.eclipse.jgit.lib.ObjectStream;
|
||||
|
||||
/**
|
||||
* The element used as left or right file for compare.
|
||||
|
@ -25,36 +26,71 @@
|
|||
*/
|
||||
public class FileElement {
|
||||
|
||||
/**
|
||||
* The file element type.
|
||||
*
|
||||
*/
|
||||
public enum Type {
|
||||
/**
|
||||
* The local file element (ours).
|
||||
*/
|
||||
LOCAL,
|
||||
/**
|
||||
* The remote file element (theirs).
|
||||
*/
|
||||
REMOTE,
|
||||
/**
|
||||
* The merged file element (path in worktree).
|
||||
*/
|
||||
MERGED,
|
||||
/**
|
||||
* The base file element (of ours and theirs).
|
||||
*/
|
||||
BASE,
|
||||
/**
|
||||
* The backup file element (copy of merged / conflicted).
|
||||
*/
|
||||
BACKUP
|
||||
}
|
||||
|
||||
private final String path;
|
||||
|
||||
private final String id;
|
||||
private final Type type;
|
||||
|
||||
private ObjectStream stream;
|
||||
private InputStream stream;
|
||||
|
||||
private File tempFile;
|
||||
|
||||
/**
|
||||
* Creates file element for path.
|
||||
*
|
||||
* @param path
|
||||
* the file path
|
||||
* @param id
|
||||
* the file id
|
||||
* @param type
|
||||
* the element type
|
||||
*/
|
||||
public FileElement(final String path, final String id) {
|
||||
this(path, id, null);
|
||||
public FileElement(String path, Type type) {
|
||||
this(path, type, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates file element for path.
|
||||
*
|
||||
* @param path
|
||||
* the file path
|
||||
* @param id
|
||||
* the file id
|
||||
* @param type
|
||||
* the element type
|
||||
* @param tempFile
|
||||
* the temporary file to be used (can be null and will be created
|
||||
* then)
|
||||
* @param stream
|
||||
* the object stream to load instead of file
|
||||
*/
|
||||
public FileElement(final String path, final String id,
|
||||
ObjectStream stream) {
|
||||
public FileElement(String path, Type type, File tempFile,
|
||||
InputStream stream) {
|
||||
this.path = path;
|
||||
this.id = id;
|
||||
this.type = type;
|
||||
this.tempFile = tempFile;
|
||||
this.stream = stream;
|
||||
}
|
||||
|
||||
|
@ -66,71 +102,101 @@ public String getPath() {
|
|||
}
|
||||
|
||||
/**
|
||||
* @return the file id
|
||||
* @return the element type
|
||||
*/
|
||||
public String getId() {
|
||||
return id;
|
||||
public Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param stream
|
||||
* the object stream
|
||||
*/
|
||||
public void setStream(ObjectStream stream) {
|
||||
this.stream = stream;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a temporary file with in passed working directory and fills it
|
||||
* with stream if valid.
|
||||
* Return a temporary file within passed directory and fills it with stream
|
||||
* if valid.
|
||||
*
|
||||
* @param directory
|
||||
* the working directory where the temporary file is created
|
||||
* the directory where the temporary file is created
|
||||
* @param midName
|
||||
* name added in the middle of generated temporary file name
|
||||
* @return the object stream
|
||||
* @throws IOException
|
||||
*/
|
||||
public File getFile(File directory, String midName) throws IOException {
|
||||
if (tempFile != null) {
|
||||
if ((tempFile != null) && (stream == null)) {
|
||||
return tempFile;
|
||||
}
|
||||
String[] fileNameAndExtension = splitBaseFileNameAndExtension(
|
||||
new File(path));
|
||||
tempFile = File.createTempFile(
|
||||
fileNameAndExtension[0] + "_" + midName + "_", //$NON-NLS-1$ //$NON-NLS-2$
|
||||
fileNameAndExtension[1], directory);
|
||||
copyFromStream();
|
||||
return tempFile;
|
||||
tempFile = getTempFile(path, directory, midName);
|
||||
return copyFromStream(tempFile, stream);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a real file from work tree or a temporary file with content if
|
||||
* Return a real file from work tree or a temporary file with content if
|
||||
* stream is valid or if path is "/dev/null"
|
||||
*
|
||||
* @return the object stream
|
||||
* @throws IOException
|
||||
*/
|
||||
public File getFile() throws IOException {
|
||||
if (tempFile != null) {
|
||||
if ((tempFile != null) && (stream == null)) {
|
||||
return tempFile;
|
||||
}
|
||||
File file = new File(path);
|
||||
String name = file.getName();
|
||||
// if we have a stream or file is missing ("/dev/null") then create
|
||||
// temporary file
|
||||
if ((stream != null) || path.equals(DiffEntry.DEV_NULL)) {
|
||||
// TODO: avoid long random file name (number generated by
|
||||
// createTempFile)
|
||||
tempFile = File.createTempFile(".__", "__" + name); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
copyFromStream();
|
||||
return tempFile;
|
||||
if ((stream != null) || isNullPath()) {
|
||||
tempFile = getTempFile(file);
|
||||
return copyFromStream(tempFile, stream);
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes and invalidates temporary file if necessary.
|
||||
* Check if path id "/dev/null"
|
||||
*
|
||||
* @return true if path is "/dev/null"
|
||||
*/
|
||||
public boolean isNullPath() {
|
||||
return path.equals(DiffEntry.DEV_NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create temporary file in given or system temporary directory
|
||||
*
|
||||
* @param directory
|
||||
* the directory for the file (can be null); if null system
|
||||
* temporary directory is used
|
||||
* @return temporary file in directory or in the system temporary directory
|
||||
* @throws IOException
|
||||
*/
|
||||
public File createTempFile(File directory) throws IOException {
|
||||
if (tempFile == null) {
|
||||
File file = new File(path);
|
||||
if (directory != null) {
|
||||
tempFile = getTempFile(file, directory, type.name());
|
||||
} else {
|
||||
tempFile = getTempFile(file);
|
||||
}
|
||||
}
|
||||
return tempFile;
|
||||
}
|
||||
|
||||
private static File getTempFile(File file) throws IOException {
|
||||
return File.createTempFile(".__", "__" + file.getName()); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
}
|
||||
|
||||
private static File getTempFile(File file, File directory, String midName)
|
||||
throws IOException {
|
||||
String[] fileNameAndExtension = splitBaseFileNameAndExtension(file);
|
||||
return File.createTempFile(
|
||||
fileNameAndExtension[0] + "_" + midName + "_", //$NON-NLS-1$ //$NON-NLS-2$
|
||||
fileNameAndExtension[1], directory);
|
||||
}
|
||||
|
||||
private static File getTempFile(String path, File directory, String midName)
|
||||
throws IOException {
|
||||
return getTempFile(new File(path), directory, midName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete and invalidate temporary file if necessary.
|
||||
*/
|
||||
public void cleanTemporaries() {
|
||||
if (tempFile != null && tempFile.exists())
|
||||
|
@ -138,9 +204,10 @@ public void cleanTemporaries() {
|
|||
tempFile = null;
|
||||
}
|
||||
|
||||
private void copyFromStream() throws IOException, FileNotFoundException {
|
||||
private static File copyFromStream(File file, final InputStream stream)
|
||||
throws IOException, FileNotFoundException {
|
||||
if (stream != null) {
|
||||
try (OutputStream outStream = new FileOutputStream(tempFile)) {
|
||||
try (OutputStream outStream = new FileOutputStream(file)) {
|
||||
int read = 0;
|
||||
byte[] bytes = new byte[8 * 1024];
|
||||
while ((read = stream.read(bytes)) != -1) {
|
||||
|
@ -149,23 +216,46 @@ private void copyFromStream() throws IOException, FileNotFoundException {
|
|||
} finally {
|
||||
// stream can only be consumed once --> close it
|
||||
stream.close();
|
||||
stream = null;
|
||||
}
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
private static String[] splitBaseFileNameAndExtension(File file) {
|
||||
String[] result = new String[2];
|
||||
result[0] = file.getName();
|
||||
result[1] = ""; //$NON-NLS-1$
|
||||
if (!result[0].startsWith(".")) { //$NON-NLS-1$
|
||||
int idx = result[0].lastIndexOf("."); //$NON-NLS-1$
|
||||
if (idx != -1) {
|
||||
result[1] = result[0].substring(idx, result[0].length());
|
||||
result[0] = result[0].substring(0, idx);
|
||||
}
|
||||
int idx = result[0].lastIndexOf("."); //$NON-NLS-1$
|
||||
// if "." was found (>-1) and last-index is not first char (>0), then
|
||||
// split (same behavior like cgit)
|
||||
if (idx > 0) {
|
||||
result[1] = result[0].substring(idx, result[0].length());
|
||||
result[0] = result[0].substring(0, idx);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace variable in input
|
||||
*
|
||||
* @param input
|
||||
* the input string
|
||||
* @return the replaced input string
|
||||
* @throws IOException
|
||||
*/
|
||||
public String replaceVariable(String input) throws IOException {
|
||||
return input.replace("$" + type.name(), getFile().getPath()); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
/**
|
||||
* Add variable to environment map.
|
||||
*
|
||||
* @param env
|
||||
* the environment where this element should be added
|
||||
* @throws IOException
|
||||
*/
|
||||
public void addToEnv(Map<String, String> env) throws IOException {
|
||||
env.put(type.name(), getFile().getPath());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import org.eclipse.jgit.lib.Constants;
|
||||
import org.eclipse.jgit.internal.diffmergetool.FileElement.Type;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
import org.eclipse.jgit.lib.internal.BooleanTriState;
|
||||
import org.eclipse.jgit.util.FS.ExecutionResult;
|
||||
|
@ -29,6 +29,8 @@
|
|||
*/
|
||||
public class MergeTools {
|
||||
|
||||
Repository repo;
|
||||
|
||||
private final MergeToolConfig config;
|
||||
|
||||
private final Map<String, ExternalMergeTool> predefinedTools;
|
||||
|
@ -37,25 +39,27 @@ public class MergeTools {
|
|||
|
||||
/**
|
||||
* @param repo
|
||||
* the repository database
|
||||
* the repository
|
||||
*/
|
||||
public MergeTools(Repository repo) {
|
||||
this.repo = repo;
|
||||
config = repo.getConfig().get(MergeToolConfig.KEY);
|
||||
predefinedTools = setupPredefinedTools();
|
||||
userDefinedTools = setupUserDefinedTools(config, predefinedTools);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param repo
|
||||
* the repository
|
||||
* @param localFile
|
||||
* the local file element
|
||||
* @param remoteFile
|
||||
* the remote file element
|
||||
* @param mergedFile
|
||||
* the merged file element
|
||||
* @param baseFile
|
||||
* the base file element (can be null)
|
||||
* @param mergedFilePath
|
||||
* the path of 'merged' file
|
||||
* @param tempDir
|
||||
* the temporary directory (needed for backup and auto-remove,
|
||||
* can be null)
|
||||
* @param toolName
|
||||
* the selected tool name (can be null)
|
||||
* @param prompt
|
||||
|
@ -65,47 +69,35 @@ public MergeTools(Repository repo) {
|
|||
* @return the execution result from tool
|
||||
* @throws ToolException
|
||||
*/
|
||||
public ExecutionResult merge(Repository repo, FileElement localFile,
|
||||
FileElement remoteFile, FileElement baseFile, String mergedFilePath,
|
||||
public ExecutionResult merge(FileElement localFile, FileElement remoteFile,
|
||||
FileElement mergedFile, FileElement baseFile, File tempDir,
|
||||
String toolName, BooleanTriState prompt, BooleanTriState gui)
|
||||
throws ToolException {
|
||||
ExternalMergeTool tool = guessTool(toolName, gui);
|
||||
FileElement backup = null;
|
||||
File tempDir = null;
|
||||
ExecutionResult result = null;
|
||||
try {
|
||||
File workingDir = repo.getWorkTree();
|
||||
// crate temp-directory or use working directory
|
||||
tempDir = config.isWriteToTemp()
|
||||
? Files.createTempDirectory("jgit-mergetool-").toFile() //$NON-NLS-1$
|
||||
: workingDir;
|
||||
// create additional backup file (copy worktree file)
|
||||
backup = createBackupFile(mergedFilePath, tempDir);
|
||||
// get local, remote and base file paths
|
||||
String localFilePath = localFile.getFile(tempDir, "LOCAL") //$NON-NLS-1$
|
||||
.getPath();
|
||||
String remoteFilePath = remoteFile.getFile(tempDir, "REMOTE") //$NON-NLS-1$
|
||||
.getPath();
|
||||
String baseFilePath = ""; //$NON-NLS-1$
|
||||
if (baseFile != null) {
|
||||
baseFilePath = baseFile.getFile(tempDir, "BASE").getPath(); //$NON-NLS-1$
|
||||
}
|
||||
backup = createBackupFile(mergedFile.getPath(),
|
||||
tempDir != null ? tempDir : workingDir);
|
||||
// prepare the command (replace the file paths)
|
||||
boolean trust = tool.getTrustExitCode() == BooleanTriState.TRUE;
|
||||
String command = prepareCommand(mergedFilePath, localFilePath,
|
||||
remoteFilePath, baseFilePath,
|
||||
tool.getCommand(baseFile != null));
|
||||
String command = ExternalToolUtils.prepareCommand(
|
||||
tool.getCommand(baseFile != null), localFile, remoteFile,
|
||||
mergedFile, baseFile);
|
||||
// prepare the environment
|
||||
Map<String, String> env = prepareEnvironment(repo, mergedFilePath,
|
||||
localFilePath, remoteFilePath, baseFilePath);
|
||||
Map<String, String> env = ExternalToolUtils.prepareEnvironment(repo,
|
||||
localFile, remoteFile, mergedFile, baseFile);
|
||||
// execute the tool
|
||||
CommandExecutor cmdExec = new CommandExecutor(repo.getFS(), trust);
|
||||
result = cmdExec.run(command, workingDir, env);
|
||||
// keep backup as .orig file
|
||||
if (backup != null) {
|
||||
keepBackupFile(mergedFilePath, backup);
|
||||
keepBackupFile(mergedFile.getPath(), backup);
|
||||
}
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
} catch (IOException | InterruptedException e) {
|
||||
throw new ToolException(e);
|
||||
} finally {
|
||||
// always delete backup file (ignore that it was may be already
|
||||
|
@ -131,18 +123,29 @@ public ExecutionResult merge(Repository repo, FileElement localFile,
|
|||
}
|
||||
}
|
||||
|
||||
private static FileElement createBackupFile(String mergedFilePath,
|
||||
File tempDir) throws IOException {
|
||||
private FileElement createBackupFile(String filePath, File parentDir)
|
||||
throws IOException {
|
||||
FileElement backup = null;
|
||||
Path path = Paths.get(tempDir.getPath(), mergedFilePath);
|
||||
Path path = Paths.get(filePath);
|
||||
if (Files.exists(path)) {
|
||||
backup = new FileElement(mergedFilePath, "NOID", null); //$NON-NLS-1$
|
||||
Files.copy(path, backup.getFile(tempDir, "BACKUP").toPath(), //$NON-NLS-1$
|
||||
backup = new FileElement(filePath, Type.BACKUP);
|
||||
Files.copy(path, backup.createTempFile(parentDir).toPath(),
|
||||
StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
return backup;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the created temporary directory if (mergetol.writeToTemp == true)
|
||||
* or null if not configured or false.
|
||||
* @throws IOException
|
||||
*/
|
||||
public File createTempDirectory() throws IOException {
|
||||
return config.isWriteToTemp()
|
||||
? Files.createTempDirectory("jgit-mergetool-").toFile() //$NON-NLS-1$
|
||||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the tool names
|
||||
*/
|
||||
|
@ -208,27 +211,6 @@ private ExternalMergeTool getTool(final String name) {
|
|||
return tool;
|
||||
}
|
||||
|
||||
private String prepareCommand(String mergedFilePath, String localFilePath,
|
||||
String remoteFilePath, String baseFilePath, String command) {
|
||||
command = command.replace("$LOCAL", localFilePath); //$NON-NLS-1$
|
||||
command = command.replace("$REMOTE", remoteFilePath); //$NON-NLS-1$
|
||||
command = command.replace("$MERGED", mergedFilePath); //$NON-NLS-1$
|
||||
command = command.replace("$BASE", baseFilePath); //$NON-NLS-1$
|
||||
return command;
|
||||
}
|
||||
|
||||
private Map<String, String> prepareEnvironment(Repository repo,
|
||||
String mergedFilePath, String localFilePath, String remoteFilePath,
|
||||
String baseFilePath) {
|
||||
Map<String, String> env = new TreeMap<>();
|
||||
env.put(Constants.GIT_DIR_KEY, repo.getDirectory().getAbsolutePath());
|
||||
env.put("LOCAL", localFilePath); //$NON-NLS-1$
|
||||
env.put("REMOTE", remoteFilePath); //$NON-NLS-1$
|
||||
env.put("MERGED", mergedFilePath); //$NON-NLS-1$
|
||||
env.put("BASE", baseFilePath); //$NON-NLS-1$
|
||||
return env;
|
||||
}
|
||||
|
||||
private void keepBackupFile(String mergedFilePath, FileElement backup)
|
||||
throws IOException {
|
||||
if (config.isKeepBackup()) {
|
||||
|
|
|
@ -113,7 +113,7 @@ public String getResultStderr() {
|
|||
try {
|
||||
return new String(result.getStderr().toByteArray());
|
||||
} catch (Exception e) {
|
||||
LOG.warn(e.getMessage());
|
||||
LOG.warn("Failed to retrieve standard error output", e); //$NON-NLS-1$
|
||||
}
|
||||
return ""; //$NON-NLS-1$
|
||||
}
|
||||
|
@ -125,7 +125,7 @@ public String getResultStdout() {
|
|||
try {
|
||||
return new String(result.getStdout().toByteArray());
|
||||
} catch (Exception e) {
|
||||
LOG.warn(e.getMessage());
|
||||
LOG.warn("Failed to retrieve standard output", e); //$NON-NLS-1$
|
||||
}
|
||||
return ""; //$NON-NLS-1$
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue