diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/DiffToolTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/DiffToolTest.java index dc34c0d67..8daaa6ad9 100644 --- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/DiffToolTest.java +++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/DiffToolTest.java @@ -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 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 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 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 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 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 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 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 changes) { + private static String[] getExpectedCompareOutput(String[] conflictingFilenames) { List 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 changes) { return expected.toArray(new String[0]); } - private static String[] getExpectedAbortOutput(List changes, + private static String[] getExpectedAbortOutput(String[] conflictingFilenames, int abortIndex) { List 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]?"); diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ToolTestCase.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ToolTestCase.java index d13eeb7e4..933f19bcc 100644 --- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ToolTestCase.java +++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ToolTestCase.java @@ -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 getRepositoryChanges(RevCommit commit) diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/DiffTool.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/DiffTool.java index ffba36fe2..74d91cd3d 100644 --- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/DiffTool.java +++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/DiffTool.java @@ -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 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 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 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) { diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/MergeTool.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/MergeTool.java index dce5a7996..971277075 100644 --- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/MergeTool.java +++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/MergeTool.java @@ -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, diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/diffmergetool/ExternalDiffToolTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/diffmergetool/ExternalDiffToolTest.java index ebc67c81c..4fd55c6ca 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/diffmergetool/ExternalDiffToolTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/diffmergetool/ExternalDiffToolTest.java @@ -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"); } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/diffmergetool/ExternalMergeToolTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/diffmergetool/ExternalMergeToolTest.java index 1dea44eaa..50576682e 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/diffmergetool/ExternalMergeToolTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/diffmergetool/ExternalMergeToolTest.java @@ -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"); } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/diffmergetool/ExternalToolTestCase.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/diffmergetool/ExternalToolTestCase.java index 6757eb463..0fd85cb45 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/diffmergetool/ExternalToolTestCase.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/diffmergetool/ExternalToolTestCase.java @@ -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 diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/DiffTools.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/DiffTools.java index 2f2b9de81..1dcc523bf 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/DiffTools.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/DiffTools.java @@ -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 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 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 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(); } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/ExternalToolUtils.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/ExternalToolUtils.java new file mode 100644 index 000000000..3efb90c49 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/ExternalToolUtils.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2018-2021, Andre Bossert + * + * 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 prepareEnvironment(Repository repo, + FileElement localFile, FileElement remoteFile, + FileElement mergedFile, FileElement baseFile) throws IOException { + Map 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; + } + +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/FileElement.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/FileElement.java index 1ae87aaa6..5902c1e1b 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/FileElement.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/FileElement.java @@ -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 env) throws IOException { + env.put(type.name(), getFile().getPath()); + } + } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/MergeTools.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/MergeTools.java index c4c2ceccf..9a2a8304e 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/MergeTools.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/MergeTools.java @@ -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 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 env = prepareEnvironment(repo, mergedFilePath, - localFilePath, remoteFilePath, baseFilePath); + Map 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 prepareEnvironment(Repository repo, - String mergedFilePath, String localFilePath, String remoteFilePath, - String baseFilePath) { - Map 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()) { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/ToolException.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/ToolException.java index 1ae0780ac..27f7d12e6 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/ToolException.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/ToolException.java @@ -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$ }