Add mergetool merge feature (execute external tool)
see: https://git-scm.com/docs/git-mergetool * implement mergetool merge function (execute external tool) * add ExecutionResult and commandExecutionError to ToolException * handle "base not present" case (empty or null base file path) * handle deleted (rm) and modified (add) conflicts * handle settings * keepBackup * keepTemporaries * writeToTemp Bug: 356832 Change-Id: Id323c2fcb1c24d12ceb299801df8bac51a6d463f Signed-off-by: Andre Bossert <andre.bossert@siemens.com>
This commit is contained in:
parent
8573435635
commit
eaf4d500b8
|
@ -16,6 +16,7 @@
|
|||
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_TOOL;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
@ -30,7 +31,7 @@
|
|||
/**
|
||||
* Testing the {@code difftool} command.
|
||||
*/
|
||||
public class DiffToolTest extends ExternalToolTestCase {
|
||||
public class DiffToolTest extends ToolTestCase {
|
||||
|
||||
private static final String DIFF_TOOL = CONFIG_DIFFTOOL_SECTION;
|
||||
|
||||
|
@ -41,6 +42,46 @@ public void setUp() throws Exception {
|
|||
configureEchoTool(TOOL_NAME);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToolWithPrompt() throws Exception {
|
||||
String[] inputLines = {
|
||||
"y", // accept launching diff tool
|
||||
"y", // accept launching diff tool
|
||||
};
|
||||
|
||||
RevCommit commit = createUnstagedChanges();
|
||||
List<DiffEntry> changes = getRepositoryChanges(commit);
|
||||
String[] expectedOutput = getExpectedCompareOutput(changes);
|
||||
|
||||
String option = "--tool";
|
||||
|
||||
InputStream inputStream = createInputStream(inputLines);
|
||||
assertArrayOfLinesEquals("Incorrect output for option: " + option,
|
||||
expectedOutput, runAndCaptureUsingInitRaw(inputStream,
|
||||
DIFF_TOOL, "--prompt", option, TOOL_NAME));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToolAbortLaunch() throws Exception {
|
||||
String[] inputLines = {
|
||||
"y", // accept launching diff tool
|
||||
"n", // don't launch diff tool
|
||||
};
|
||||
|
||||
RevCommit commit = createUnstagedChanges();
|
||||
List<DiffEntry> changes = getRepositoryChanges(commit);
|
||||
int abortIndex = 1;
|
||||
String[] expectedOutput = getExpectedAbortOutput(changes, abortIndex);
|
||||
|
||||
String option = "--tool";
|
||||
|
||||
InputStream inputStream = createInputStream(inputLines);
|
||||
assertArrayOfLinesEquals("Incorrect output for option: " + option,
|
||||
expectedOutput,
|
||||
runAndCaptureUsingInitRaw(inputStream, DIFF_TOOL, "--prompt", option,
|
||||
TOOL_NAME));
|
||||
}
|
||||
|
||||
@Test(expected = Die.class)
|
||||
public void testNotDefinedTool() throws Exception {
|
||||
createUnstagedChanges();
|
||||
|
@ -53,7 +94,7 @@ public void testNotDefinedTool() throws Exception {
|
|||
public void testTool() throws Exception {
|
||||
RevCommit commit = createUnstagedChanges();
|
||||
List<DiffEntry> changes = getRepositoryChanges(commit);
|
||||
String[] expectedOutput = getExpectedToolOutput(changes);
|
||||
String[] expectedOutput = getExpectedToolOutputNoPrompt(changes);
|
||||
|
||||
String[] options = {
|
||||
"--tool",
|
||||
|
@ -72,7 +113,7 @@ public void testTool() throws Exception {
|
|||
public void testToolTrustExitCode() throws Exception {
|
||||
RevCommit commit = createUnstagedChanges();
|
||||
List<DiffEntry> changes = getRepositoryChanges(commit);
|
||||
String[] expectedOutput = getExpectedToolOutput(changes);
|
||||
String[] expectedOutput = getExpectedToolOutputNoPrompt(changes);
|
||||
|
||||
String[] options = { "--tool", "-t", };
|
||||
|
||||
|
@ -87,7 +128,7 @@ expectedOutput, runAndCaptureUsingInitRaw(DIFF_TOOL,
|
|||
public void testToolNoGuiNoPromptNoTrustExitcode() throws Exception {
|
||||
RevCommit commit = createUnstagedChanges();
|
||||
List<DiffEntry> changes = getRepositoryChanges(commit);
|
||||
String[] expectedOutput = getExpectedToolOutput(changes);
|
||||
String[] expectedOutput = getExpectedToolOutputNoPrompt(changes);
|
||||
|
||||
String[] options = { "--tool", "-t", };
|
||||
|
||||
|
@ -103,7 +144,7 @@ expectedOutput, runAndCaptureUsingInitRaw(DIFF_TOOL,
|
|||
public void testToolCached() throws Exception {
|
||||
RevCommit commit = createStagedChanges();
|
||||
List<DiffEntry> changes = getRepositoryChanges(commit);
|
||||
String[] expectedOutput = getExpectedToolOutput(changes);
|
||||
String[] expectedOutput = getExpectedToolOutputNoPrompt(changes);
|
||||
|
||||
String[] options = { "--cached", "--staged", };
|
||||
|
||||
|
@ -118,7 +159,8 @@ expectedOutput, runAndCaptureUsingInitRaw(DIFF_TOOL,
|
|||
public void testToolHelp() throws Exception {
|
||||
CommandLineDiffTool[] defaultTools = CommandLineDiffTool.values();
|
||||
List<String> expectedOutput = new ArrayList<>();
|
||||
expectedOutput.add("git difftool --tool=<tool> may be set to one of the following:");
|
||||
expectedOutput.add(
|
||||
"'git difftool --tool=<tool>' may be set to one of the following:");
|
||||
for (CommandLineDiffTool defaultTool : defaultTools) {
|
||||
String toolName = defaultTool.name();
|
||||
expectedOutput.add(toolName);
|
||||
|
@ -159,7 +201,7 @@ private void configureEchoTool(String toolName) {
|
|||
String.valueOf(false));
|
||||
}
|
||||
|
||||
private String[] getExpectedToolOutput(List<DiffEntry> changes) {
|
||||
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);
|
||||
|
@ -169,4 +211,36 @@ private String[] getExpectedToolOutput(List<DiffEntry> changes) {
|
|||
}
|
||||
return expectedToolOutput;
|
||||
}
|
||||
|
||||
private static String[] getExpectedCompareOutput(List<DiffEntry> changes) {
|
||||
List<String> expected = new ArrayList<>();
|
||||
int n = changes.size();
|
||||
for (int i = 0; i < n; ++i) {
|
||||
DiffEntry change = changes.get(i);
|
||||
String newPath = change.getNewPath();
|
||||
expected.add(
|
||||
"Viewing (" + (i + 1) + "/" + n + "): '" + newPath + "'");
|
||||
expected.add("Launch '" + TOOL_NAME + "' [Y/n]?");
|
||||
expected.add(newPath);
|
||||
}
|
||||
return expected.toArray(new String[0]);
|
||||
}
|
||||
|
||||
private static String[] getExpectedAbortOutput(List<DiffEntry> changes,
|
||||
int abortIndex) {
|
||||
List<String> expected = new ArrayList<>();
|
||||
int n = changes.size();
|
||||
for (int i = 0; i < n; ++i) {
|
||||
DiffEntry change = changes.get(i);
|
||||
String newPath = change.getNewPath();
|
||||
expected.add(
|
||||
"Viewing (" + (i + 1) + "/" + n + "): '" + newPath + "'");
|
||||
expected.add("Launch '" + TOOL_NAME + "' [Y/n]?");
|
||||
if (i == abortIndex) {
|
||||
break;
|
||||
}
|
||||
expected.add(newPath);
|
||||
}
|
||||
return expected.toArray(new String[0]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_MERGETOOL_SECTION;
|
||||
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_MERGE_SECTION;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
@ -27,7 +28,7 @@
|
|||
/**
|
||||
* Testing the {@code mergetool} command.
|
||||
*/
|
||||
public class MergeToolTest extends ExternalToolTestCase {
|
||||
public class MergeToolTest extends ToolTestCase {
|
||||
|
||||
private static final String MERGE_TOOL = CONFIG_MERGETOOL_SECTION;
|
||||
|
||||
|
@ -39,37 +40,121 @@ public void setUp() throws Exception {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testTool() throws Exception {
|
||||
createMergeConflict();
|
||||
String[] expectedOutput = getExpectedToolOutput();
|
||||
|
||||
String[] options = {
|
||||
"--tool",
|
||||
"-t",
|
||||
public void testAbortMerge() throws Exception {
|
||||
String[] inputLines = {
|
||||
"y", // start tool for merge resolution
|
||||
"n", // don't accept merge tool result
|
||||
"n", // don't continue resolution
|
||||
};
|
||||
String[] conflictingFilenames = createMergeConflict();
|
||||
int abortIndex = 1;
|
||||
String[] expectedOutput = getExpectedAbortMergeOutput(
|
||||
conflictingFilenames,
|
||||
abortIndex);
|
||||
|
||||
for (String option : options) {
|
||||
assertArrayOfLinesEquals("Incorrect output for option: " + option,
|
||||
expectedOutput,
|
||||
runAndCaptureUsingInitRaw(MERGE_TOOL, option,
|
||||
TOOL_NAME));
|
||||
}
|
||||
String option = "--tool";
|
||||
|
||||
InputStream inputStream = createInputStream(inputLines);
|
||||
assertArrayOfLinesEquals("Incorrect output for option: " + option,
|
||||
expectedOutput, runAndCaptureUsingInitRaw(inputStream,
|
||||
MERGE_TOOL, "--prompt", option, TOOL_NAME));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToolNoGuiNoPrompt() throws Exception {
|
||||
createMergeConflict();
|
||||
String[] expectedOutput = getExpectedToolOutput();
|
||||
public void testAbortLaunch() throws Exception {
|
||||
String[] inputLines = {
|
||||
"n", // abort merge tool launch
|
||||
};
|
||||
String[] conflictingFilenames = createMergeConflict();
|
||||
String[] expectedOutput = getExpectedAbortLaunchOutput(
|
||||
conflictingFilenames);
|
||||
|
||||
String option = "--tool";
|
||||
|
||||
InputStream inputStream = createInputStream(inputLines);
|
||||
assertArrayOfLinesEquals("Incorrect output for option: " + option,
|
||||
expectedOutput, runAndCaptureUsingInitRaw(inputStream,
|
||||
MERGE_TOOL, "--prompt", option, TOOL_NAME));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMergeConflict() throws Exception {
|
||||
String[] inputLines = {
|
||||
"y", // start tool for merge resolution
|
||||
"y", // accept merge result as successful
|
||||
"y", // start tool for merge resolution
|
||||
"y", // accept merge result as successful
|
||||
};
|
||||
String[] conflictingFilenames = createMergeConflict();
|
||||
String[] expectedOutput = getExpectedMergeConflictOutput(
|
||||
conflictingFilenames);
|
||||
|
||||
String option = "--tool";
|
||||
|
||||
InputStream inputStream = createInputStream(inputLines);
|
||||
assertArrayOfLinesEquals("Incorrect output for option: " + option,
|
||||
expectedOutput, runAndCaptureUsingInitRaw(inputStream,
|
||||
MERGE_TOOL, "--prompt", option, TOOL_NAME));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeletedConflict() throws Exception {
|
||||
String[] inputLines = {
|
||||
"d", // choose delete option to resolve conflict
|
||||
"m", // choose merge option to resolve conflict
|
||||
};
|
||||
String[] conflictingFilenames = createDeletedConflict();
|
||||
String[] expectedOutput = getExpectedDeletedConflictOutput(
|
||||
conflictingFilenames);
|
||||
|
||||
String option = "--tool";
|
||||
|
||||
InputStream inputStream = createInputStream(inputLines);
|
||||
assertArrayOfLinesEquals("Incorrect output for option: " + option,
|
||||
expectedOutput, runAndCaptureUsingInitRaw(inputStream,
|
||||
MERGE_TOOL, "--prompt", option, TOOL_NAME));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoConflict() throws Exception {
|
||||
createStagedChanges();
|
||||
String[] expectedOutput = { "No files need merging" };
|
||||
|
||||
String[] options = { "--tool", "-t", };
|
||||
|
||||
for (String option : options) {
|
||||
assertArrayOfLinesEquals("Incorrect output for option: " + option,
|
||||
expectedOutput, runAndCaptureUsingInitRaw(MERGE_TOOL,
|
||||
"--no-gui", "--no-prompt", option, TOOL_NAME));
|
||||
expectedOutput,
|
||||
runAndCaptureUsingInitRaw(MERGE_TOOL, option, TOOL_NAME));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMergeConflictNoPrompt() throws Exception {
|
||||
String[] conflictingFilenames = createMergeConflict();
|
||||
String[] expectedOutput = getExpectedMergeConflictOutputNoPrompt(
|
||||
conflictingFilenames);
|
||||
|
||||
String option = "--tool";
|
||||
|
||||
assertArrayOfLinesEquals("Incorrect output for option: " + option,
|
||||
expectedOutput,
|
||||
runAndCaptureUsingInitRaw(MERGE_TOOL, option, TOOL_NAME));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMergeConflictNoGuiNoPrompt() throws Exception {
|
||||
String[] conflictingFilenames = createMergeConflict();
|
||||
String[] expectedOutput = getExpectedMergeConflictOutputNoPrompt(
|
||||
conflictingFilenames);
|
||||
|
||||
String option = "--tool";
|
||||
|
||||
assertArrayOfLinesEquals("Incorrect output for option: " + option,
|
||||
expectedOutput, runAndCaptureUsingInitRaw(MERGE_TOOL,
|
||||
"--no-gui", "--no-prompt", option, TOOL_NAME));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToolHelp() throws Exception {
|
||||
CommandLineMergeTool[] defaultTools = CommandLineMergeTool.values();
|
||||
|
@ -87,8 +172,7 @@ public void testToolHelp() throws Exception {
|
|||
String[] userDefinedToolsHelp = {
|
||||
"The following tools are valid, but not currently available:",
|
||||
"Some of the tools listed above only work in a windowed",
|
||||
"environment. If run in a terminal-only session, they will fail.",
|
||||
};
|
||||
"environment. If run in a terminal-only session, they will fail.", };
|
||||
expectedOutput.addAll(Arrays.asList(userDefinedToolsHelp));
|
||||
|
||||
String option = "--tool-help";
|
||||
|
@ -116,21 +200,111 @@ private void configureEchoTool(String toolName) {
|
|||
String.valueOf(false));
|
||||
}
|
||||
|
||||
private String[] getExpectedToolOutput() {
|
||||
String[] mergeConflictFilenames = { "a", "b", };
|
||||
List<String> expectedOutput = new ArrayList<>();
|
||||
expectedOutput.add("Merging:");
|
||||
for (String mergeConflictFilename : mergeConflictFilenames) {
|
||||
expectedOutput.add(mergeConflictFilename);
|
||||
private static String[] getExpectedMergeConflictOutputNoPrompt(
|
||||
String[] conflictFilenames) {
|
||||
List<String> expected = new ArrayList<>();
|
||||
expected.add("Merging:");
|
||||
for (String conflictFilename : conflictFilenames) {
|
||||
expected.add(conflictFilename);
|
||||
}
|
||||
for (String mergeConflictFilename : mergeConflictFilenames) {
|
||||
expectedOutput.add("Normal merge conflict for '"
|
||||
+ mergeConflictFilename + "':");
|
||||
expectedOutput.add("{local}: modified file");
|
||||
expectedOutput.add("{remote}: modified file");
|
||||
expectedOutput.add("TODO: Launch mergetool '" + TOOL_NAME
|
||||
+ "' for path '" + mergeConflictFilename + "'...");
|
||||
for (String conflictFilename : conflictFilenames) {
|
||||
expected.add("Normal merge conflict for '" + conflictFilename
|
||||
+ "':");
|
||||
expected.add("{local}: modified file");
|
||||
expected.add("{remote}: modified file");
|
||||
expected.add(conflictFilename);
|
||||
expected.add(conflictFilename + " seems unchanged.");
|
||||
}
|
||||
return expectedOutput.toArray(new String[0]);
|
||||
return expected.toArray(new String[0]);
|
||||
}
|
||||
|
||||
private static String[] getExpectedAbortLaunchOutput(
|
||||
String[] conflictFilenames) {
|
||||
List<String> expected = new ArrayList<>();
|
||||
expected.add("Merging:");
|
||||
for (String conflictFilename : conflictFilenames) {
|
||||
expected.add(conflictFilename);
|
||||
}
|
||||
if (conflictFilenames.length > 1) {
|
||||
String conflictFilename = conflictFilenames[0];
|
||||
expected.add(
|
||||
"Normal merge conflict for '" + conflictFilename + "':");
|
||||
expected.add("{local}: modified file");
|
||||
expected.add("{remote}: modified file");
|
||||
expected.add("Hit return to start merge resolution tool ("
|
||||
+ TOOL_NAME + "):");
|
||||
}
|
||||
return expected.toArray(new String[0]);
|
||||
}
|
||||
|
||||
private static String[] getExpectedAbortMergeOutput(
|
||||
String[] conflictFilenames, int abortIndex) {
|
||||
List<String> expected = new ArrayList<>();
|
||||
expected.add("Merging:");
|
||||
for (String conflictFilename : conflictFilenames) {
|
||||
expected.add(conflictFilename);
|
||||
}
|
||||
for (int i = 0; i < conflictFilenames.length; ++i) {
|
||||
if (i == abortIndex) {
|
||||
break;
|
||||
}
|
||||
|
||||
String conflictFilename = conflictFilenames[i];
|
||||
expected.add(
|
||||
"Normal merge conflict for '" + conflictFilename + "':");
|
||||
expected.add("{local}: modified file");
|
||||
expected.add("{remote}: modified file");
|
||||
expected.add("Hit return to start merge resolution tool ("
|
||||
+ TOOL_NAME + "): " + conflictFilename);
|
||||
expected.add(conflictFilename + " seems unchanged.");
|
||||
expected.add("Was the merge successful [y/n]?");
|
||||
if (i < conflictFilenames.length - 1) {
|
||||
expected.add(
|
||||
"\tContinue merging other unresolved paths [y/n]?");
|
||||
}
|
||||
}
|
||||
return expected.toArray(new String[0]);
|
||||
}
|
||||
|
||||
private static String[] getExpectedMergeConflictOutput(
|
||||
String[] conflictFilenames) {
|
||||
List<String> expected = new ArrayList<>();
|
||||
expected.add("Merging:");
|
||||
for (String conflictFilename : conflictFilenames) {
|
||||
expected.add(conflictFilename);
|
||||
}
|
||||
for (int i = 0; i < conflictFilenames.length; ++i) {
|
||||
String conflictFilename = conflictFilenames[i];
|
||||
expected.add("Normal merge conflict for '" + conflictFilename
|
||||
+ "':");
|
||||
expected.add("{local}: modified file");
|
||||
expected.add("{remote}: modified file");
|
||||
expected.add("Hit return to start merge resolution tool ("
|
||||
+ TOOL_NAME + "): " + conflictFilename);
|
||||
expected.add(conflictFilename + " seems unchanged.");
|
||||
expected.add("Was the merge successful [y/n]?");
|
||||
if (i < conflictFilenames.length - 1) {
|
||||
// expected.add(
|
||||
// "\tContinue merging other unresolved paths [y/n]?");
|
||||
}
|
||||
}
|
||||
return expected.toArray(new String[0]);
|
||||
}
|
||||
|
||||
private static String[] getExpectedDeletedConflictOutput(
|
||||
String[] conflictFilenames) {
|
||||
List<String> expected = new ArrayList<>();
|
||||
expected.add("Merging:");
|
||||
for (String mergeConflictFilename : conflictFilenames) {
|
||||
expected.add(mergeConflictFilename);
|
||||
}
|
||||
for (int i = 0; i < conflictFilenames.length; ++i) {
|
||||
String conflictFilename = conflictFilenames[i];
|
||||
expected.add(conflictFilename + " seems unchanged.");
|
||||
expected.add("{local}: deleted");
|
||||
expected.add("{remote}: modified file");
|
||||
expected.add("Use (m)odified or (d)eleted file, or (a)bort?");
|
||||
}
|
||||
return expected.toArray(new String[0]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,10 +11,14 @@
|
|||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.jgit.api.CherryPickResult;
|
||||
import org.eclipse.jgit.api.Git;
|
||||
import org.eclipse.jgit.diff.DiffEntry;
|
||||
import org.eclipse.jgit.lib.CLIRepositoryTestCase;
|
||||
|
@ -29,7 +33,7 @@
|
|||
/**
|
||||
* Base test case for the {@code difftool} and {@code mergetool} commands.
|
||||
*/
|
||||
public abstract class ExternalToolTestCase extends CLIRepositoryTestCase {
|
||||
public abstract class ToolTestCase extends CLIRepositoryTestCase {
|
||||
|
||||
public static class GitCliJGitWrapperParser {
|
||||
@Argument(index = 0, metaVar = "metaVar_command", required = true, handler = SubcommandHandler.class)
|
||||
|
@ -56,6 +60,12 @@ public void setUp() throws Exception {
|
|||
|
||||
protected String[] runAndCaptureUsingInitRaw(String... args)
|
||||
throws Exception {
|
||||
InputStream inputStream = null; // no input stream
|
||||
return runAndCaptureUsingInitRaw(inputStream, args);
|
||||
}
|
||||
|
||||
protected String[] runAndCaptureUsingInitRaw(InputStream inputStream,
|
||||
String... args) throws Exception {
|
||||
CLIGitCommand.Result result = new CLIGitCommand.Result();
|
||||
|
||||
GitCliJGitWrapperParser bean = new GitCliJGitWrapperParser();
|
||||
|
@ -63,7 +73,7 @@ protected String[] runAndCaptureUsingInitRaw(String... args)
|
|||
clp.parseArgument(args);
|
||||
|
||||
TextBuiltin cmd = bean.subcommand;
|
||||
cmd.initRaw(db, null, null, result.out, result.err);
|
||||
cmd.initRaw(db, null, inputStream, result.out, result.err);
|
||||
cmd.execute(bean.arguments.toArray(new String[bean.arguments.size()]));
|
||||
if (cmd.getOutputWriter() != null) {
|
||||
cmd.getOutputWriter().flush();
|
||||
|
@ -71,28 +81,73 @@ protected String[] runAndCaptureUsingInitRaw(String... args)
|
|||
if (cmd.getErrorWriter() != null) {
|
||||
cmd.getErrorWriter().flush();
|
||||
}
|
||||
|
||||
List<String> errLines = result.errLines().stream()
|
||||
.filter(l -> !l.isBlank()) // we care only about error messages
|
||||
.collect(Collectors.toList());
|
||||
assertEquals("Expected no standard error output from tool",
|
||||
Collections.EMPTY_LIST.toString(), errLines.toString());
|
||||
|
||||
return result.outLines().toArray(new String[0]);
|
||||
}
|
||||
|
||||
protected CherryPickResult createMergeConflict() throws Exception {
|
||||
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");
|
||||
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");
|
||||
git.add().addFilepattern(".").call();
|
||||
RevCommit commit1 = git.commit().setMessage("files a & b commit 1")
|
||||
.call();
|
||||
git.branchCreate().setName("branch_1").call();
|
||||
RevCommit commit1 = git.commit()
|
||||
.setMessage("files a & b modified commit 1").call();
|
||||
// checkout initial branch
|
||||
git.checkout().setName(TEST_BRANCH_NAME).call();
|
||||
// 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");
|
||||
git.add().addFilepattern(".").call();
|
||||
git.commit().setMessage("files a & b commit 2").call();
|
||||
git.commit().setMessage("files a & b modified commit 2").call();
|
||||
// cherry-pick conflicting changes
|
||||
git.cherryPick().include(commit1).call();
|
||||
String[] conflictingFilenames = { "a", "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");
|
||||
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");
|
||||
git.add().addFilepattern(".").call();
|
||||
RevCommit commit1 = git.commit()
|
||||
.setMessage("files a & b modified commit 1").call();
|
||||
// checkout initial branch
|
||||
git.checkout().setName(TEST_BRANCH_NAME).call();
|
||||
// create another branch and change files
|
||||
git.branchCreate().setName("branch_2").call();
|
||||
CherryPickResult result = git.cherryPick().include(commit1).call();
|
||||
return result;
|
||||
git.checkout().setName("branch_2").call();
|
||||
git.rm().addFilepattern("a").call();
|
||||
git.rm().addFilepattern("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" };
|
||||
return conflictingFilenames;
|
||||
}
|
||||
|
||||
protected RevCommit createUnstagedChanges() throws Exception {
|
||||
|
@ -121,6 +176,16 @@ protected List<DiffEntry> getRepositoryChanges(RevCommit commit)
|
|||
return changes;
|
||||
}
|
||||
|
||||
protected static InputStream createInputStream(String[] inputLines) {
|
||||
return createInputStream(Arrays.asList(inputLines));
|
||||
}
|
||||
|
||||
protected static InputStream createInputStream(List<String> inputLines) {
|
||||
String input = String.join(System.lineSeparator(), inputLines);
|
||||
InputStream inputStream = new ByteArrayInputStream(input.getBytes());
|
||||
return inputStream;
|
||||
}
|
||||
|
||||
protected static void assertArrayOfLinesEquals(String failMessage,
|
||||
String[] expected, String[] actual) {
|
||||
assertEquals(failMessage, toString(expected), toString(actual));
|
|
@ -58,8 +58,8 @@ couldNotCreateBranch=Could not create branch {0}: {1}
|
|||
dateInfo=Date: {0}
|
||||
deletedBranch=Deleted branch {0}
|
||||
deletedRemoteBranch=Deleted remote branch {0}
|
||||
diffToolHelpSetToFollowing='git difftool --tool=<tool>' may be set to one of the following:\n{0}\n\tuser-defined:\n{1}\nThe following tools are valid, but not currently available:\n{2}\nSome of the tools listed above only work in a windowed\nenvironment. If run in a terminal-only session, they will fail.
|
||||
diffToolLaunch=Viewing ({0}/{1}): '{2}'\nLaunch '{3}' [Y/n]?
|
||||
diffToolHelpSetToFollowing=''git difftool --tool=<tool>'' may be set to one of the following:\n{0}\n\tuser-defined:\n{1}\nThe following tools are valid, but not currently available:\n{2}\nSome of the tools listed above only work in a windowed\nenvironment. If run in a terminal-only session, they will fail.
|
||||
diffToolLaunch=Viewing ({0}/{1}): ''{2}''\nLaunch ''{3}'' [Y/n]?
|
||||
diffToolDied=external diff died, stopping at path ''{0}'' due to exception: {1}
|
||||
doesNotExist={0} does not exist
|
||||
dontOverwriteLocalChanges=error: Your local changes to the following file would be overwritten by merge:
|
||||
|
@ -91,6 +91,22 @@ listeningOn=Listening on {0}
|
|||
logNoSignatureVerifier="No signature verifier available"
|
||||
mergeConflict=CONFLICT(content): Merge conflict in {0}
|
||||
mergeCheckoutConflict=error: Your local changes to the following files would be overwritten by merge:
|
||||
mergeToolHelpSetToFollowing=''git mergetool --tool=<tool>'' may be set to one of the following:\n{0}\n\tuser-defined:\n{1}\nThe following tools are valid, but not currently available:\n{2}\nSome of the tools listed above only work in a windowed\nenvironment. If run in a terminal-only session, they will fail.
|
||||
mergeToolLaunch=Hit return to start merge resolution tool ({0}):
|
||||
mergeToolDied=local or remote cannot be found in cache, stopping at {0}
|
||||
mergeToolNoFiles=No files need merging
|
||||
mergeToolMerging=Merging:\n{0}
|
||||
mergeToolUnknownConflict=\nUnknown merge conflict for ''{0}'':
|
||||
mergeToolNormalConflict=\nNormal merge conflict for ''{0}'':\n '{'local'}': modified file\n '{'remote'}': modified file
|
||||
mergeToolMergeFailed=merge of {0} failed
|
||||
mergeToolExecutionError=excution error
|
||||
mergeToolFileUnchanged=\n{0} seems unchanged.
|
||||
mergeToolDeletedConflict=\nDeleted merge conflict for ''{0}'':
|
||||
mergeToolDeletedConflictByUs= {local}: deleted\n {remote}: modified file
|
||||
mergeToolDeletedConflictByThem= {local}: modified file\n {remote}: deleted
|
||||
mergeToolContinueUnresolvedPaths=\nContinue merging other unresolved paths [y/n]?
|
||||
mergeToolWasMergeSuccessfull=Was the merge successful [y/n]?
|
||||
mergeToolDeletedMergeDecision=Use (m)odified or (d)eleted file, or (a)bort?
|
||||
mergeFailed=Automatic merge failed; fix conflicts and then commit the result
|
||||
mergeCheckoutFailed=Please, commit your changes or stash them before you can merge.
|
||||
mergeMadeBy=Merge made by the ''{0}'' strategy.
|
||||
|
|
|
@ -113,11 +113,14 @@ void noTrustExitCode(@SuppressWarnings("unused") boolean on) {
|
|||
@Option(name = "--", metaVar = "metaVar_paths", handler = PathTreeFilterHandler.class)
|
||||
private TreeFilter pathFilter = TreeFilter.ALL;
|
||||
|
||||
private BufferedReader inputReader;
|
||||
|
||||
@Override
|
||||
protected void init(Repository repository, String gitDir) {
|
||||
super.init(repository, gitDir);
|
||||
diffFmt = new DiffFormatter(new BufferedOutputStream(outs));
|
||||
diffTools = new DiffTools(repository);
|
||||
inputReader = new BufferedReader(new InputStreamReader(ins, StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -208,10 +211,9 @@ private boolean isLaunchCompare(int fileIndex, int fileCount,
|
|||
String fileName, String toolNamePrompt) throws IOException {
|
||||
boolean launchCompare = true;
|
||||
outw.println(MessageFormat.format(CLIText.get().diffToolLaunch,
|
||||
fileIndex, fileCount, fileName, toolNamePrompt));
|
||||
fileIndex, fileCount, fileName, toolNamePrompt) + " "); //$NON-NLS-1$
|
||||
outw.flush();
|
||||
BufferedReader br = new BufferedReader(
|
||||
new InputStreamReader(ins, StandardCharsets.UTF_8));
|
||||
BufferedReader br = inputReader;
|
||||
String line = null;
|
||||
if ((line = br.readLine()) != null) {
|
||||
if (!line.equalsIgnoreCase("Y")) { //$NON-NLS-1$
|
||||
|
@ -224,17 +226,18 @@ private boolean isLaunchCompare(int fileIndex, int fileCount,
|
|||
private void showToolHelp() throws IOException {
|
||||
StringBuilder availableToolNames = new StringBuilder();
|
||||
for (String name : diffTools.getAvailableTools().keySet()) {
|
||||
availableToolNames.append(String.format("\t\t%s\n", name)); //$NON-NLS-1$
|
||||
availableToolNames.append(MessageFormat.format("\t\t{0}\n", name)); //$NON-NLS-1$
|
||||
}
|
||||
StringBuilder notAvailableToolNames = new StringBuilder();
|
||||
for (String name : diffTools.getNotAvailableTools().keySet()) {
|
||||
notAvailableToolNames.append(String.format("\t\t%s\n", name)); //$NON-NLS-1$
|
||||
notAvailableToolNames
|
||||
.append(MessageFormat.format("\t\t{0}\n", name)); //$NON-NLS-1$
|
||||
}
|
||||
StringBuilder userToolNames = new StringBuilder();
|
||||
Map<String, ExternalDiffTool> userTools = diffTools
|
||||
.getUserDefinedTools();
|
||||
for (String name : userTools.keySet()) {
|
||||
userToolNames.append(String.format("\t\t%s.cmd %s\n", //$NON-NLS-1$
|
||||
userToolNames.append(MessageFormat.format("\t\t{0}.cmd {1}\n", //$NON-NLS-1$
|
||||
name, userTools.get(name).getCommand()));
|
||||
}
|
||||
outw.println(MessageFormat.format(
|
||||
|
|
|
@ -11,26 +11,35 @@
|
|||
package org.eclipse.jgit.pgm;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import org.eclipse.jgit.api.Git;
|
||||
import org.eclipse.jgit.api.Status;
|
||||
import org.eclipse.jgit.api.StatusCommand;
|
||||
import org.eclipse.jgit.api.errors.GitAPIException;
|
||||
import org.eclipse.jgit.internal.diffmergetool.ExternalMergeTool;
|
||||
import org.eclipse.jgit.diff.ContentSource;
|
||||
import org.eclipse.jgit.dircache.DirCache;
|
||||
import org.eclipse.jgit.dircache.DirCacheEntry;
|
||||
import org.eclipse.jgit.errors.NoWorkTreeException;
|
||||
import org.eclipse.jgit.errors.RevisionSyntaxException;
|
||||
import org.eclipse.jgit.internal.diffmergetool.ExternalMergeTool;
|
||||
import org.eclipse.jgit.internal.diffmergetool.FileElement;
|
||||
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.lib.internal.BooleanTriState;
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
import org.eclipse.jgit.lib.internal.BooleanTriState;
|
||||
import org.eclipse.jgit.pgm.internal.CLIText;
|
||||
import org.eclipse.jgit.util.FS.ExecutionResult;
|
||||
import org.kohsuke.args4j.Argument;
|
||||
import org.kohsuke.args4j.Option;
|
||||
import org.kohsuke.args4j.spi.RestOfArgumentsHandler;
|
||||
|
@ -43,16 +52,16 @@ class MergeTool extends TextBuiltin {
|
|||
"-t" }, metaVar = "metaVar_tool", usage = "usage_ToolForMerge")
|
||||
private String toolName;
|
||||
|
||||
private Optional<Boolean> prompt = Optional.empty();
|
||||
private BooleanTriState prompt = BooleanTriState.UNSET;
|
||||
|
||||
@Option(name = "--prompt", usage = "usage_prompt")
|
||||
void setPrompt(@SuppressWarnings("unused") boolean on) {
|
||||
prompt = Optional.of(Boolean.TRUE);
|
||||
prompt = BooleanTriState.TRUE;
|
||||
}
|
||||
|
||||
@Option(name = "--no-prompt", aliases = { "-y" }, usage = "usage_noPrompt")
|
||||
void noPrompt(@SuppressWarnings("unused") boolean on) {
|
||||
prompt = Optional.of(Boolean.FALSE);
|
||||
prompt = BooleanTriState.FALSE;
|
||||
}
|
||||
|
||||
@Option(name = "--tool-help", usage = "usage_toolHelp")
|
||||
|
@ -74,10 +83,17 @@ void noGui(@SuppressWarnings("unused") boolean on) {
|
|||
@Option(name = "--", metaVar = "metaVar_paths", handler = RestOfArgumentsHandler.class)
|
||||
protected List<String> filterPaths;
|
||||
|
||||
private BufferedReader inputReader;
|
||||
|
||||
@Override
|
||||
protected void init(Repository repository, String gitDir) {
|
||||
super.init(repository, gitDir);
|
||||
mergeTools = new MergeTools(repository);
|
||||
inputReader = new BufferedReader(new InputStreamReader(ins));
|
||||
}
|
||||
|
||||
enum MergeResult {
|
||||
SUCCESSFUL, FAILED, ABORTED
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -88,8 +104,8 @@ protected void run() {
|
|||
} else {
|
||||
// get prompt
|
||||
boolean showPrompt = mergeTools.isInteractive();
|
||||
if (prompt.isPresent()) {
|
||||
showPrompt = prompt.get().booleanValue();
|
||||
if (prompt != BooleanTriState.UNSET) {
|
||||
showPrompt = prompt == BooleanTriState.TRUE;
|
||||
}
|
||||
// get passed or default tool name
|
||||
String toolNameSelected = toolName;
|
||||
|
@ -101,7 +117,7 @@ protected void run() {
|
|||
if (files.size() > 0) {
|
||||
merge(files, showPrompt, toolNameSelected);
|
||||
} else {
|
||||
outw.println("No files need merging"); //$NON-NLS-1$
|
||||
outw.println(CLIText.get().mergeToolNoFiles);
|
||||
}
|
||||
}
|
||||
outw.flush();
|
||||
|
@ -113,88 +129,273 @@ protected void run() {
|
|||
private void merge(Map<String, StageState> files, boolean showPrompt,
|
||||
String toolNamePrompt) throws Exception {
|
||||
// sort file names
|
||||
List<String> fileNames = new ArrayList<>(files.keySet());
|
||||
Collections.sort(fileNames);
|
||||
List<String> mergedFilePaths = new ArrayList<>(files.keySet());
|
||||
Collections.sort(mergedFilePaths);
|
||||
// show the files
|
||||
outw.println("Merging:"); //$NON-NLS-1$
|
||||
for (String fileName : fileNames) {
|
||||
outw.println(fileName);
|
||||
StringBuilder mergedFiles = new StringBuilder();
|
||||
for (String mergedFilePath : mergedFilePaths) {
|
||||
mergedFiles.append(MessageFormat.format("{0}\n", mergedFilePath)); //$NON-NLS-1$
|
||||
}
|
||||
outw.println(MessageFormat.format(CLIText.get().mergeToolMerging,
|
||||
mergedFiles));
|
||||
outw.flush();
|
||||
for (String fileName : fileNames) {
|
||||
StageState fileState = files.get(fileName);
|
||||
// only both-modified is valid for mergetool
|
||||
if (fileState == StageState.BOTH_MODIFIED) {
|
||||
outw.println("\nNormal merge conflict for '" + fileName + "':"); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
outw.println(" {local}: modified file"); //$NON-NLS-1$
|
||||
outw.println(" {remote}: modified file"); //$NON-NLS-1$
|
||||
// check if user wants to launch merge resolution tool
|
||||
boolean launch = true;
|
||||
if (showPrompt) {
|
||||
launch = isLaunch(toolNamePrompt);
|
||||
// merge the files
|
||||
MergeResult mergeResult = MergeResult.SUCCESSFUL;
|
||||
for (String mergedFilePath : mergedFilePaths) {
|
||||
// if last merge failed...
|
||||
if (mergeResult == MergeResult.FAILED) {
|
||||
// check if user wants to continue
|
||||
if (showPrompt && !isContinueUnresolvedPaths()) {
|
||||
mergeResult = MergeResult.ABORTED;
|
||||
}
|
||||
if (launch) {
|
||||
outw.println("TODO: Launch mergetool '" + toolNamePrompt //$NON-NLS-1$
|
||||
+ "' for path '" + fileName + "'..."); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else if ((fileState == StageState.DELETED_BY_US) || (fileState == StageState.DELETED_BY_THEM)) {
|
||||
outw.println("\nDeleted merge conflict for '" + fileName + "':"); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
} else {
|
||||
outw.println(
|
||||
"\nUnknown merge conflict for '" + fileName + "':"); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
}
|
||||
// aborted ?
|
||||
if (mergeResult == MergeResult.ABORTED) {
|
||||
break;
|
||||
}
|
||||
// get file stage state and merge
|
||||
StageState fileState = files.get(mergedFilePath);
|
||||
if (fileState == StageState.BOTH_MODIFIED) {
|
||||
mergeResult = mergeModified(mergedFilePath, showPrompt,
|
||||
toolNamePrompt);
|
||||
} else if ((fileState == StageState.DELETED_BY_US)
|
||||
|| (fileState == StageState.DELETED_BY_THEM)) {
|
||||
mergeResult = mergeDeleted(mergedFilePath,
|
||||
fileState == StageState.DELETED_BY_US);
|
||||
} else {
|
||||
outw.println(MessageFormat.format(
|
||||
CLIText.get().mergeToolUnknownConflict,
|
||||
mergedFilePath));
|
||||
mergeResult = MergeResult.ABORTED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isLaunch(String toolNamePrompt)
|
||||
throws IOException {
|
||||
boolean launch = true;
|
||||
outw.println("Hit return to start merge resolution tool (" //$NON-NLS-1$
|
||||
+ toolNamePrompt + "): "); //$NON-NLS-1$
|
||||
private MergeResult mergeModified(String mergedFilePath, boolean showPrompt,
|
||||
String toolNamePrompt) throws Exception {
|
||||
outw.println(MessageFormat.format(CLIText.get().mergeToolNormalConflict,
|
||||
mergedFilePath));
|
||||
outw.flush();
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(ins));
|
||||
// check if user wants to launch merge resolution tool
|
||||
boolean launch = true;
|
||||
if (showPrompt) {
|
||||
launch = isLaunch(toolNamePrompt);
|
||||
}
|
||||
if (!launch) {
|
||||
return MergeResult.ABORTED; // abort
|
||||
}
|
||||
boolean isMergeSuccessful = true;
|
||||
ContentSource baseSource = ContentSource.create(db.newObjectReader());
|
||||
ContentSource localSource = ContentSource.create(db.newObjectReader());
|
||||
ContentSource remoteSource = ContentSource.create(db.newObjectReader());
|
||||
try {
|
||||
FileElement base = null;
|
||||
FileElement local = null;
|
||||
FileElement remote = null;
|
||||
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);
|
||||
ObjectId id = entry.getObjectId();
|
||||
switch (entry.getStage()) {
|
||||
case DirCacheEntry.STAGE_1:
|
||||
base = new FileElement(mergedFilePath, id.name(),
|
||||
baseSource.open(mergedFilePath, id)
|
||||
.openStream());
|
||||
break;
|
||||
case DirCacheEntry.STAGE_2:
|
||||
local = new FileElement(mergedFilePath, id.name(),
|
||||
localSource.open(mergedFilePath, id)
|
||||
.openStream());
|
||||
break;
|
||||
case DirCacheEntry.STAGE_3:
|
||||
remote = new FileElement(mergedFilePath, id.name(),
|
||||
remoteSource.open(mergedFilePath, id)
|
||||
.openStream());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((local == null) || (remote == null)) {
|
||||
throw die(MessageFormat.format(CLIText.get().mergeToolDied,
|
||||
mergedFilePath));
|
||||
}
|
||||
File merged = new File(mergedFilePath);
|
||||
long modifiedBefore = merged.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);
|
||||
outw.println(
|
||||
new String(executionResult.getStdout().toByteArray()));
|
||||
outw.flush();
|
||||
errw.println(
|
||||
new String(executionResult.getStderr().toByteArray()));
|
||||
errw.flush();
|
||||
} catch (ToolException e) {
|
||||
isMergeSuccessful = false;
|
||||
outw.println(e.getResultStdout());
|
||||
outw.flush();
|
||||
errw.println(MessageFormat.format(
|
||||
CLIText.get().mergeToolMergeFailed, mergedFilePath));
|
||||
errw.flush();
|
||||
if (e.isCommandExecutionError()) {
|
||||
errw.println(e.getMessage());
|
||||
throw die(CLIText.get().mergeToolExecutionError, e);
|
||||
}
|
||||
}
|
||||
// if merge was successful check file modified
|
||||
if (isMergeSuccessful) {
|
||||
long modifiedAfter = merged.lastModified();
|
||||
if (modifiedBefore == modifiedAfter) {
|
||||
outw.println(MessageFormat.format(
|
||||
CLIText.get().mergeToolFileUnchanged,
|
||||
mergedFilePath));
|
||||
isMergeSuccessful = !showPrompt || isMergeSuccessful();
|
||||
}
|
||||
}
|
||||
// if automatically or manually successful
|
||||
// -> add the file to the index
|
||||
if (isMergeSuccessful) {
|
||||
addFile(mergedFilePath);
|
||||
}
|
||||
} finally {
|
||||
baseSource.close();
|
||||
localSource.close();
|
||||
remoteSource.close();
|
||||
}
|
||||
return isMergeSuccessful ? MergeResult.SUCCESSFUL : MergeResult.FAILED;
|
||||
}
|
||||
|
||||
private MergeResult mergeDeleted(String mergedFilePath, boolean deletedByUs)
|
||||
throws Exception {
|
||||
outw.println(MessageFormat.format(CLIText.get().mergeToolFileUnchanged,
|
||||
mergedFilePath));
|
||||
if (deletedByUs) {
|
||||
outw.println(CLIText.get().mergeToolDeletedConflictByUs);
|
||||
} else {
|
||||
outw.println(CLIText.get().mergeToolDeletedConflictByThem);
|
||||
}
|
||||
int mergeDecision = getDeletedMergeDecision();
|
||||
if (mergeDecision == 1) {
|
||||
// add modified file
|
||||
addFile(mergedFilePath);
|
||||
} else if (mergeDecision == -1) {
|
||||
// remove deleted file
|
||||
rmFile(mergedFilePath);
|
||||
} else {
|
||||
return MergeResult.ABORTED;
|
||||
}
|
||||
return MergeResult.SUCCESSFUL;
|
||||
}
|
||||
|
||||
private void addFile(String fileName) throws Exception {
|
||||
try (Git git = new Git(db)) {
|
||||
git.add().addFilepattern(fileName).call();
|
||||
}
|
||||
}
|
||||
|
||||
private void rmFile(String fileName) throws Exception {
|
||||
try (Git git = new Git(db)) {
|
||||
git.rm().addFilepattern(fileName).call();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasUserAccepted(String message) throws IOException {
|
||||
boolean yes = true;
|
||||
outw.print(message + " "); //$NON-NLS-1$
|
||||
outw.flush();
|
||||
BufferedReader br = inputReader;
|
||||
String line = null;
|
||||
while ((line = br.readLine()) != null) {
|
||||
if (line.equalsIgnoreCase("y")) { //$NON-NLS-1$
|
||||
yes = true;
|
||||
break;
|
||||
} else if (line.equalsIgnoreCase("n")) { //$NON-NLS-1$
|
||||
yes = false;
|
||||
break;
|
||||
}
|
||||
outw.print(message);
|
||||
outw.flush();
|
||||
}
|
||||
return yes;
|
||||
}
|
||||
|
||||
private boolean isContinueUnresolvedPaths() throws IOException {
|
||||
return hasUserAccepted(CLIText.get().mergeToolContinueUnresolvedPaths);
|
||||
}
|
||||
|
||||
private boolean isMergeSuccessful() throws IOException {
|
||||
return hasUserAccepted(CLIText.get().mergeToolWasMergeSuccessfull);
|
||||
}
|
||||
|
||||
private boolean isLaunch(String toolNamePrompt) throws IOException {
|
||||
boolean launch = true;
|
||||
outw.print(MessageFormat.format(CLIText.get().mergeToolLaunch,
|
||||
toolNamePrompt) + " "); //$NON-NLS-1$
|
||||
outw.flush();
|
||||
BufferedReader br = inputReader;
|
||||
String line = null;
|
||||
if ((line = br.readLine()) != null) {
|
||||
if (!line.equalsIgnoreCase("Y") && !line.equalsIgnoreCase("")) { //$NON-NLS-1$ //$NON-NLS-2$
|
||||
if (!line.equalsIgnoreCase("y") && !line.equalsIgnoreCase("")) { //$NON-NLS-1$ //$NON-NLS-2$
|
||||
launch = false;
|
||||
}
|
||||
}
|
||||
return launch;
|
||||
}
|
||||
|
||||
private void showToolHelp() throws IOException {
|
||||
outw.println(
|
||||
"'git mergetool --tool=<tool>' may be set to one of the following:"); //$NON-NLS-1$
|
||||
for (String name : mergeTools.getAvailableTools().keySet()) {
|
||||
outw.println("\t\t" + name); //$NON-NLS-1$
|
||||
private int getDeletedMergeDecision() throws IOException {
|
||||
int ret = 0; // abort
|
||||
final String message = CLIText.get().mergeToolDeletedMergeDecision
|
||||
+ " "; //$NON-NLS-1$
|
||||
outw.print(message);
|
||||
outw.flush();
|
||||
BufferedReader br = inputReader;
|
||||
String line = null;
|
||||
while ((line = br.readLine()) != null) {
|
||||
if (line.equalsIgnoreCase("m")) { //$NON-NLS-1$
|
||||
ret = 1; // modified
|
||||
break;
|
||||
} else if (line.equalsIgnoreCase("d")) { //$NON-NLS-1$
|
||||
ret = -1; // deleted
|
||||
break;
|
||||
} else if (line.equalsIgnoreCase("a")) { //$NON-NLS-1$
|
||||
break;
|
||||
}
|
||||
outw.print(message);
|
||||
outw.flush();
|
||||
}
|
||||
outw.println(""); //$NON-NLS-1$
|
||||
outw.println("\tuser-defined:"); //$NON-NLS-1$
|
||||
return ret;
|
||||
}
|
||||
|
||||
private void showToolHelp() throws IOException {
|
||||
StringBuilder availableToolNames = new StringBuilder();
|
||||
for (String name : mergeTools.getAvailableTools().keySet()) {
|
||||
availableToolNames.append(MessageFormat.format("\t\t{0}\n", name)); //$NON-NLS-1$
|
||||
}
|
||||
StringBuilder notAvailableToolNames = new StringBuilder();
|
||||
for (String name : mergeTools.getNotAvailableTools().keySet()) {
|
||||
notAvailableToolNames
|
||||
.append(MessageFormat.format("\t\t{0}\n", name)); //$NON-NLS-1$
|
||||
}
|
||||
StringBuilder userToolNames = new StringBuilder();
|
||||
Map<String, ExternalMergeTool> userTools = mergeTools
|
||||
.getUserDefinedTools();
|
||||
for (String name : userTools.keySet()) {
|
||||
outw.println("\t\t" + name + ".cmd " //$NON-NLS-1$ //$NON-NLS-2$
|
||||
+ userTools.get(name).getCommand());
|
||||
userToolNames.append(MessageFormat.format("\t\t{0}.cmd {1}\n", //$NON-NLS-1$
|
||||
name, userTools.get(name).getCommand()));
|
||||
}
|
||||
outw.println(""); //$NON-NLS-1$
|
||||
outw.println(
|
||||
"The following tools are valid, but not currently available:"); //$NON-NLS-1$
|
||||
for (String name : mergeTools.getNotAvailableTools().keySet()) {
|
||||
outw.println("\t\t" + name); //$NON-NLS-1$
|
||||
}
|
||||
outw.println(""); //$NON-NLS-1$
|
||||
outw.println("Some of the tools listed above only work in a windowed"); //$NON-NLS-1$
|
||||
outw.println(
|
||||
"environment. If run in a terminal-only session, they will fail."); //$NON-NLS-1$
|
||||
return;
|
||||
outw.println(MessageFormat.format(
|
||||
CLIText.get().mergeToolHelpSetToFollowing, availableToolNames,
|
||||
userToolNames, notAvailableToolNames));
|
||||
}
|
||||
|
||||
private Map<String, StageState> getFiles()
|
||||
throws RevisionSyntaxException, NoWorkTreeException,
|
||||
GitAPIException {
|
||||
private Map<String, StageState> getFiles() throws RevisionSyntaxException,
|
||||
NoWorkTreeException, GitAPIException {
|
||||
Map<String, StageState> files = new TreeMap<>();
|
||||
try (Git git = new Git(db)) {
|
||||
StatusCommand statusCommand = git.status();
|
||||
|
|
|
@ -169,6 +169,22 @@ public static String fatalError(String message) {
|
|||
/***/ public String logNoSignatureVerifier;
|
||||
/***/ public String mergeCheckoutConflict;
|
||||
/***/ public String mergeConflict;
|
||||
/***/ public String mergeToolHelpSetToFollowing;
|
||||
/***/ public String mergeToolLaunch;
|
||||
/***/ public String mergeToolDied;
|
||||
/***/ public String mergeToolNoFiles;
|
||||
/***/ public String mergeToolMerging;
|
||||
/***/ public String mergeToolUnknownConflict;
|
||||
/***/ public String mergeToolNormalConflict;
|
||||
/***/ public String mergeToolMergeFailed;
|
||||
/***/ public String mergeToolExecutionError;
|
||||
/***/ public String mergeToolFileUnchanged;
|
||||
/***/ public String mergeToolDeletedConflict;
|
||||
/***/ public String mergeToolDeletedConflictByUs;
|
||||
/***/ public String mergeToolDeletedConflictByThem;
|
||||
/***/ public String mergeToolContinueUnresolvedPaths;
|
||||
/***/ public String mergeToolWasMergeSuccessfull;
|
||||
/***/ public String mergeToolDeletedMergeDecision;
|
||||
/***/ public String mergeFailed;
|
||||
/***/ public String mergeCheckoutFailed;
|
||||
/***/ public String mergeMadeBy;
|
||||
|
|
|
@ -72,10 +72,18 @@ public ExecutionResult run(String command, File workingDir,
|
|||
}
|
||||
ExecutionResult result = fs.execute(pb, null);
|
||||
int rc = result.getRc();
|
||||
if ((rc != 0) && (checkExitCode
|
||||
|| isCommandExecutionError(rc))) {
|
||||
throw new ToolException(
|
||||
new String(result.getStderr().toByteArray()), result);
|
||||
if (rc != 0) {
|
||||
boolean execError = isCommandExecutionError(rc);
|
||||
if (checkExitCode || execError) {
|
||||
throw new ToolException(
|
||||
"JGit: tool execution return code: " + rc + "\n" //$NON-NLS-1$ //$NON-NLS-2$
|
||||
+ "checkExitCode: " + checkExitCode + "\n" //$NON-NLS-1$ //$NON-NLS-2$
|
||||
+ "execError: " + execError + "\n" //$NON-NLS-1$ //$NON-NLS-2$
|
||||
+ "stderr: \n" //$NON-NLS-1$
|
||||
+ new String(
|
||||
result.getStderr().toByteArray()),
|
||||
result, execError);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
} finally {
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
package org.eclipse.jgit.internal.diffmergetool;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
@ -80,35 +81,27 @@ public void setStream(ObjectStream stream) {
|
|||
}
|
||||
|
||||
/**
|
||||
* @param workingDir the working directory used if file cannot be found (e.g. /dev/null)
|
||||
* Returns a temporary file with in passed working directory and fills it
|
||||
* with stream if valid.
|
||||
*
|
||||
* @param directory
|
||||
* the working 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 workingDir) throws IOException {
|
||||
public File getFile(File directory, String midName) throws IOException {
|
||||
if (tempFile != null) {
|
||||
return tempFile;
|
||||
}
|
||||
File file = new File(path);
|
||||
String name = file.getName();
|
||||
if (path.equals(DiffEntry.DEV_NULL)) {
|
||||
file = new File(workingDir, "nul"); //$NON-NLS-1$
|
||||
}
|
||||
else if (stream != null) {
|
||||
tempFile = File.createTempFile(".__", "__" + name); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
try (OutputStream outStream = new FileOutputStream(tempFile)) {
|
||||
int read = 0;
|
||||
byte[] bytes = new byte[8 * 1024];
|
||||
while ((read = stream.read(bytes)) != -1) {
|
||||
outStream.write(bytes, 0, read);
|
||||
}
|
||||
} finally {
|
||||
// stream can only be consumed once --> close it
|
||||
stream.close();
|
||||
stream = null;
|
||||
}
|
||||
return tempFile;
|
||||
}
|
||||
return file;
|
||||
String[] fileNameAndExtension = splitBaseFileNameAndExtension(
|
||||
new File(path));
|
||||
tempFile = File.createTempFile(
|
||||
fileNameAndExtension[0] + "_" + midName + "_", //$NON-NLS-1$ //$NON-NLS-2$
|
||||
fileNameAndExtension[1], directory);
|
||||
copyFromStream();
|
||||
return tempFile;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -130,19 +123,7 @@ public File getFile() throws IOException {
|
|||
// TODO: avoid long random file name (number generated by
|
||||
// createTempFile)
|
||||
tempFile = File.createTempFile(".__", "__" + name); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
if (stream != null) {
|
||||
try (OutputStream outStream = new FileOutputStream(tempFile)) {
|
||||
int read = 0;
|
||||
byte[] bytes = new byte[8 * 1024];
|
||||
while ((read = stream.read(bytes)) != -1) {
|
||||
outStream.write(bytes, 0, read);
|
||||
}
|
||||
} finally {
|
||||
// stream can only be consumed once --> close it
|
||||
stream.close();
|
||||
stream = null;
|
||||
}
|
||||
}
|
||||
copyFromStream();
|
||||
return tempFile;
|
||||
}
|
||||
return file;
|
||||
|
@ -157,4 +138,34 @@ public void cleanTemporaries() {
|
|||
tempFile = null;
|
||||
}
|
||||
|
||||
private void copyFromStream() throws IOException, FileNotFoundException {
|
||||
if (stream != null) {
|
||||
try (OutputStream outStream = new FileOutputStream(tempFile)) {
|
||||
int read = 0;
|
||||
byte[] bytes = new byte[8 * 1024];
|
||||
while ((read = stream.read(bytes)) != -1) {
|
||||
outStream.write(bytes, 0, read);
|
||||
}
|
||||
} finally {
|
||||
// stream can only be consumed once --> close it
|
||||
stream.close();
|
||||
stream = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -10,6 +10,11 @@
|
|||
package org.eclipse.jgit.internal.diffmergetool;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
|
@ -48,7 +53,7 @@ public MergeTools(Repository repo) {
|
|||
* @param remoteFile
|
||||
* the remote file element
|
||||
* @param baseFile
|
||||
* the base file element
|
||||
* the base file element (can be null)
|
||||
* @param mergedFilePath
|
||||
* the path of 'merged' file
|
||||
* @param toolName
|
||||
|
@ -65,35 +70,79 @@ public ExecutionResult merge(Repository repo, FileElement localFile,
|
|||
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();
|
||||
String localFilePath = localFile.getFile().getPath();
|
||||
String remoteFilePath = remoteFile.getFile().getPath();
|
||||
String baseFilePath = baseFile.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$
|
||||
command = command.replace("$BASE", baseFilePath); //$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$
|
||||
env.put("BASE", baseFilePath); //$NON-NLS-1$
|
||||
// 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$
|
||||
}
|
||||
// prepare the command (replace the file paths)
|
||||
boolean trust = tool.getTrustExitCode() == BooleanTriState.TRUE;
|
||||
String command = prepareCommand(mergedFilePath, localFilePath,
|
||||
remoteFilePath, baseFilePath,
|
||||
tool.getCommand(baseFile != null));
|
||||
// prepare the environment
|
||||
Map<String, String> env = prepareEnvironment(repo, mergedFilePath,
|
||||
localFilePath, remoteFilePath, baseFilePath);
|
||||
CommandExecutor cmdExec = new CommandExecutor(repo.getFS(), trust);
|
||||
return cmdExec.run(command, workingDir, env);
|
||||
result = cmdExec.run(command, workingDir, env);
|
||||
// keep backup as .orig file
|
||||
if (backup != null) {
|
||||
keepBackupFile(mergedFilePath, backup);
|
||||
}
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
throw new ToolException(e);
|
||||
} finally {
|
||||
localFile.cleanTemporaries();
|
||||
remoteFile.cleanTemporaries();
|
||||
baseFile.cleanTemporaries();
|
||||
// always delete backup file (ignore that it was may be already
|
||||
// moved to keep-backup file)
|
||||
if (backup != null) {
|
||||
backup.cleanTemporaries();
|
||||
}
|
||||
// if the tool returns an error and keepTemporaries is set to true,
|
||||
// then these temporary files will be preserved
|
||||
if (!((result == null) && config.isKeepTemporaries())) {
|
||||
// delete the files
|
||||
localFile.cleanTemporaries();
|
||||
remoteFile.cleanTemporaries();
|
||||
if (baseFile != null) {
|
||||
baseFile.cleanTemporaries();
|
||||
}
|
||||
// delete temporary directory if needed
|
||||
if (config.isWriteToTemp() && (tempDir != null)
|
||||
&& tempDir.exists()) {
|
||||
tempDir.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static FileElement createBackupFile(String mergedFilePath,
|
||||
File tempDir) throws IOException {
|
||||
FileElement backup = null;
|
||||
Path path = Paths.get(tempDir.getPath(), mergedFilePath);
|
||||
if (Files.exists(path)) {
|
||||
backup = new FileElement(mergedFilePath, "NOID", null); //$NON-NLS-1$
|
||||
Files.copy(path, backup.getFile(tempDir, "BACKUP").toPath(), //$NON-NLS-1$
|
||||
StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
return backup;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the tool names
|
||||
*/
|
||||
|
@ -159,6 +208,38 @@ 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()) {
|
||||
Path backupPath = backup.getFile().toPath();
|
||||
Files.move(backupPath,
|
||||
backupPath.resolveSibling(
|
||||
Paths.get(mergedFilePath).getFileName() + ".orig"), //$NON-NLS-1$
|
||||
StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, ExternalMergeTool> setupPredefinedTools() {
|
||||
Map<String, ExternalMergeTool> tools = new TreeMap<>();
|
||||
for (CommandLineMergeTool tool : CommandLineMergeTool.values()) {
|
||||
|
|
|
@ -26,6 +26,8 @@ public class ToolException extends Exception {
|
|||
|
||||
private final ExecutionResult result;
|
||||
|
||||
private final boolean commandExecutionError;
|
||||
|
||||
/**
|
||||
* the serial version UID
|
||||
*/
|
||||
|
@ -35,8 +37,7 @@ public class ToolException extends Exception {
|
|||
*
|
||||
*/
|
||||
public ToolException() {
|
||||
super();
|
||||
result = null;
|
||||
this(null, null, false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -44,8 +45,7 @@ public ToolException() {
|
|||
* the exception message
|
||||
*/
|
||||
public ToolException(String message) {
|
||||
super(message);
|
||||
result = null;
|
||||
this(message, null, false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -53,10 +53,14 @@ public ToolException(String message) {
|
|||
* the exception message
|
||||
* @param result
|
||||
* the execution result
|
||||
* @param commandExecutionError
|
||||
* is command execution error happened ?
|
||||
*/
|
||||
public ToolException(String message, ExecutionResult result) {
|
||||
public ToolException(String message, ExecutionResult result,
|
||||
boolean commandExecutionError) {
|
||||
super(message);
|
||||
this.result = result;
|
||||
this.commandExecutionError = commandExecutionError;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -68,6 +72,7 @@ public ToolException(String message, ExecutionResult result) {
|
|||
public ToolException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
result = null;
|
||||
commandExecutionError = false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -77,6 +82,7 @@ public ToolException(String message, Throwable cause) {
|
|||
public ToolException(Throwable cause) {
|
||||
super(cause);
|
||||
result = null;
|
||||
commandExecutionError = false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -93,6 +99,13 @@ public ExecutionResult getResult() {
|
|||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if command execution error appears, false otherwise
|
||||
*/
|
||||
public boolean isCommandExecutionError() {
|
||||
return commandExecutionError;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the result Stderr
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue