Add command line support for "git mergetool"
see: https://git-scm.com/docs/git-mergetool see: https://git-scm.com/docs/git-config * add command line support for "git mergetool" * add option handling for "--tool-help", "--tool=<mytool>", "--[no-]prompt", "--[no-]gui" * handle prompt * add MergeTools * add pre-defined mergetools * print merge actions --> no execute, will be done later Bug: 356832 Change-Id: I6e505ffc3d03f75ecf4bba452a25d25dfcf5793f Signed-off-by: Andre Bossert <andre.bossert@siemens.com>
This commit is contained in:
parent
24171b05f0
commit
8573435635
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2021, Simeon Andreev <simeon.danailov.andreev@gmail.com> and others.
|
* Copyright (C) 2021-2022, Simeon Andreev <simeon.danailov.andreev@gmail.com> and others.
|
||||||
*
|
*
|
||||||
* This program and the accompanying materials are made available under the
|
* This program and the accompanying materials are made available under the
|
||||||
* terms of the Eclipse Distribution License v. 1.0 which is available at
|
* terms of the Eclipse Distribution License v. 1.0 which is available at
|
||||||
|
@ -14,68 +14,30 @@
|
||||||
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_CMD;
|
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_CMD;
|
||||||
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_PROMPT;
|
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_PROMPT;
|
||||||
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_TOOL;
|
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_TOOL;
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.eclipse.jgit.api.Git;
|
|
||||||
import org.eclipse.jgit.diff.DiffEntry;
|
import org.eclipse.jgit.diff.DiffEntry;
|
||||||
import org.eclipse.jgit.internal.diffmergetool.CommandLineDiffTool;
|
import org.eclipse.jgit.internal.diffmergetool.CommandLineDiffTool;
|
||||||
import org.eclipse.jgit.lib.CLIRepositoryTestCase;
|
|
||||||
import org.eclipse.jgit.lib.StoredConfig;
|
import org.eclipse.jgit.lib.StoredConfig;
|
||||||
import org.eclipse.jgit.pgm.opt.CmdLineParser;
|
|
||||||
import org.eclipse.jgit.pgm.opt.SubcommandHandler;
|
|
||||||
import org.eclipse.jgit.revwalk.RevCommit;
|
import org.eclipse.jgit.revwalk.RevCommit;
|
||||||
import org.eclipse.jgit.treewalk.FileTreeIterator;
|
|
||||||
import org.eclipse.jgit.treewalk.TreeWalk;
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.kohsuke.args4j.Argument;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Testing the {@code difftool} command.
|
* Testing the {@code difftool} command.
|
||||||
*/
|
*/
|
||||||
public class DiffToolTest extends CLIRepositoryTestCase {
|
public class DiffToolTest extends ExternalToolTestCase {
|
||||||
public static class GitCliJGitWrapperParser {
|
|
||||||
@Argument(index = 0, metaVar = "metaVar_command", required = true, handler = SubcommandHandler.class)
|
|
||||||
TextBuiltin subcommand;
|
|
||||||
|
|
||||||
@Argument(index = 1, metaVar = "metaVar_arg")
|
private static final String DIFF_TOOL = CONFIG_DIFFTOOL_SECTION;
|
||||||
List<String> arguments = new ArrayList<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
private String[] runAndCaptureUsingInitRaw(String... args)
|
|
||||||
throws Exception {
|
|
||||||
CLIGitCommand.Result result = new CLIGitCommand.Result();
|
|
||||||
|
|
||||||
GitCliJGitWrapperParser bean = new GitCliJGitWrapperParser();
|
|
||||||
CmdLineParser clp = new CmdLineParser(bean);
|
|
||||||
clp.parseArgument(args);
|
|
||||||
|
|
||||||
TextBuiltin cmd = bean.subcommand;
|
|
||||||
cmd.initRaw(db, null, null, result.out, result.err);
|
|
||||||
cmd.execute(bean.arguments.toArray(new String[bean.arguments.size()]));
|
|
||||||
if (cmd.getOutputWriter() != null) {
|
|
||||||
cmd.getOutputWriter().flush();
|
|
||||||
}
|
|
||||||
if (cmd.getErrorWriter() != null) {
|
|
||||||
cmd.getErrorWriter().flush();
|
|
||||||
}
|
|
||||||
return result.outLines().toArray(new String[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final String TOOL_NAME = "some_tool";
|
|
||||||
private Git git;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
super.setUp();
|
super.setUp();
|
||||||
git = new Git(db);
|
|
||||||
git.commit().setMessage("initial commit").call();
|
|
||||||
configureEchoTool(TOOL_NAME);
|
configureEchoTool(TOOL_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,7 +45,7 @@ public void setUp() throws Exception {
|
||||||
public void testNotDefinedTool() throws Exception {
|
public void testNotDefinedTool() throws Exception {
|
||||||
createUnstagedChanges();
|
createUnstagedChanges();
|
||||||
|
|
||||||
runAndCaptureUsingInitRaw("difftool", "--tool", "undefined");
|
runAndCaptureUsingInitRaw(DIFF_TOOL, "--tool", "undefined");
|
||||||
fail("Expected exception when trying to run undefined tool");
|
fail("Expected exception when trying to run undefined tool");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,7 +53,7 @@ public void testNotDefinedTool() throws Exception {
|
||||||
public void testTool() throws Exception {
|
public void testTool() throws Exception {
|
||||||
RevCommit commit = createUnstagedChanges();
|
RevCommit commit = createUnstagedChanges();
|
||||||
List<DiffEntry> changes = getRepositoryChanges(commit);
|
List<DiffEntry> changes = getRepositoryChanges(commit);
|
||||||
String[] expectedOutput = getExpectedDiffToolOutput(changes);
|
String[] expectedOutput = getExpectedToolOutput(changes);
|
||||||
|
|
||||||
String[] options = {
|
String[] options = {
|
||||||
"--tool",
|
"--tool",
|
||||||
|
@ -101,7 +63,7 @@ public void testTool() throws Exception {
|
||||||
for (String option : options) {
|
for (String option : options) {
|
||||||
assertArrayOfLinesEquals("Incorrect output for option: " + option,
|
assertArrayOfLinesEquals("Incorrect output for option: " + option,
|
||||||
expectedOutput,
|
expectedOutput,
|
||||||
runAndCaptureUsingInitRaw("difftool", option,
|
runAndCaptureUsingInitRaw(DIFF_TOOL, option,
|
||||||
TOOL_NAME));
|
TOOL_NAME));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -110,13 +72,13 @@ public void testTool() throws Exception {
|
||||||
public void testToolTrustExitCode() throws Exception {
|
public void testToolTrustExitCode() throws Exception {
|
||||||
RevCommit commit = createUnstagedChanges();
|
RevCommit commit = createUnstagedChanges();
|
||||||
List<DiffEntry> changes = getRepositoryChanges(commit);
|
List<DiffEntry> changes = getRepositoryChanges(commit);
|
||||||
String[] expectedOutput = getExpectedDiffToolOutput(changes);
|
String[] expectedOutput = getExpectedToolOutput(changes);
|
||||||
|
|
||||||
String[] options = { "--tool", "-t", };
|
String[] options = { "--tool", "-t", };
|
||||||
|
|
||||||
for (String option : options) {
|
for (String option : options) {
|
||||||
assertArrayOfLinesEquals("Incorrect output for option: " + option,
|
assertArrayOfLinesEquals("Incorrect output for option: " + option,
|
||||||
expectedOutput, runAndCaptureUsingInitRaw("difftool",
|
expectedOutput, runAndCaptureUsingInitRaw(DIFF_TOOL,
|
||||||
"--trust-exit-code", option, TOOL_NAME));
|
"--trust-exit-code", option, TOOL_NAME));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -125,13 +87,13 @@ expectedOutput, runAndCaptureUsingInitRaw("difftool",
|
||||||
public void testToolNoGuiNoPromptNoTrustExitcode() throws Exception {
|
public void testToolNoGuiNoPromptNoTrustExitcode() throws Exception {
|
||||||
RevCommit commit = createUnstagedChanges();
|
RevCommit commit = createUnstagedChanges();
|
||||||
List<DiffEntry> changes = getRepositoryChanges(commit);
|
List<DiffEntry> changes = getRepositoryChanges(commit);
|
||||||
String[] expectedOutput = getExpectedDiffToolOutput(changes);
|
String[] expectedOutput = getExpectedToolOutput(changes);
|
||||||
|
|
||||||
String[] options = { "--tool", "-t", };
|
String[] options = { "--tool", "-t", };
|
||||||
|
|
||||||
for (String option : options) {
|
for (String option : options) {
|
||||||
assertArrayOfLinesEquals("Incorrect output for option: " + option,
|
assertArrayOfLinesEquals("Incorrect output for option: " + option,
|
||||||
expectedOutput, runAndCaptureUsingInitRaw("difftool",
|
expectedOutput, runAndCaptureUsingInitRaw(DIFF_TOOL,
|
||||||
"--no-gui", "--no-prompt", "--no-trust-exit-code",
|
"--no-gui", "--no-prompt", "--no-trust-exit-code",
|
||||||
option, TOOL_NAME));
|
option, TOOL_NAME));
|
||||||
}
|
}
|
||||||
|
@ -141,13 +103,13 @@ expectedOutput, runAndCaptureUsingInitRaw("difftool",
|
||||||
public void testToolCached() throws Exception {
|
public void testToolCached() throws Exception {
|
||||||
RevCommit commit = createStagedChanges();
|
RevCommit commit = createStagedChanges();
|
||||||
List<DiffEntry> changes = getRepositoryChanges(commit);
|
List<DiffEntry> changes = getRepositoryChanges(commit);
|
||||||
String[] expectedOutput = getExpectedDiffToolOutput(changes);
|
String[] expectedOutput = getExpectedToolOutput(changes);
|
||||||
|
|
||||||
String[] options = { "--cached", "--staged", };
|
String[] options = { "--cached", "--staged", };
|
||||||
|
|
||||||
for (String option : options) {
|
for (String option : options) {
|
||||||
assertArrayOfLinesEquals("Incorrect output for option: " + option,
|
assertArrayOfLinesEquals("Incorrect output for option: " + option,
|
||||||
expectedOutput, runAndCaptureUsingInitRaw("difftool",
|
expectedOutput, runAndCaptureUsingInitRaw(DIFF_TOOL,
|
||||||
option, "--tool", TOOL_NAME));
|
option, "--tool", TOOL_NAME));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -174,7 +136,8 @@ public void testToolHelp() throws Exception {
|
||||||
|
|
||||||
String option = "--tool-help";
|
String option = "--tool-help";
|
||||||
assertArrayOfLinesEquals("Incorrect output for option: " + option,
|
assertArrayOfLinesEquals("Incorrect output for option: " + option,
|
||||||
expectedOutput.toArray(new String[0]), runAndCaptureUsingInitRaw("difftool", option));
|
expectedOutput.toArray(new String[0]),
|
||||||
|
runAndCaptureUsingInitRaw(DIFF_TOOL, option));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void configureEchoTool(String toolName) {
|
private void configureEchoTool(String toolName) {
|
||||||
|
@ -196,33 +159,7 @@ private void configureEchoTool(String toolName) {
|
||||||
String.valueOf(false));
|
String.valueOf(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
private RevCommit createUnstagedChanges() throws Exception {
|
private String[] getExpectedToolOutput(List<DiffEntry> changes) {
|
||||||
writeTrashFile("a", "Hello world a");
|
|
||||||
writeTrashFile("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;
|
|
||||||
}
|
|
||||||
|
|
||||||
private RevCommit createStagedChanges() throws Exception {
|
|
||||||
RevCommit commit = createUnstagedChanges();
|
|
||||||
git.add().addFilepattern(".").call();
|
|
||||||
return commit;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<DiffEntry> getRepositoryChanges(RevCommit commit)
|
|
||||||
throws Exception {
|
|
||||||
TreeWalk tw = new TreeWalk(db);
|
|
||||||
tw.addTree(commit.getTree());
|
|
||||||
FileTreeIterator modifiedTree = new FileTreeIterator(db);
|
|
||||||
tw.addTree(modifiedTree);
|
|
||||||
List<DiffEntry> changes = DiffEntry.scan(tw);
|
|
||||||
return changes;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String[] getExpectedDiffToolOutput(List<DiffEntry> changes) {
|
|
||||||
String[] expectedToolOutput = new String[changes.size()];
|
String[] expectedToolOutput = new String[changes.size()];
|
||||||
for (int i = 0; i < changes.size(); ++i) {
|
for (int i = 0; i < changes.size(); ++i) {
|
||||||
DiffEntry change = changes.get(i);
|
DiffEntry change = changes.get(i);
|
||||||
|
@ -232,17 +169,4 @@ private String[] getExpectedDiffToolOutput(List<DiffEntry> changes) {
|
||||||
}
|
}
|
||||||
return expectedToolOutput;
|
return expectedToolOutput;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void assertArrayOfLinesEquals(String failMessage,
|
|
||||||
String[] expected, String[] actual) {
|
|
||||||
assertEquals(failMessage, toString(expected), toString(actual));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String getEchoCommand() {
|
|
||||||
/*
|
|
||||||
* use 'MERGED' placeholder, as both 'LOCAL' and 'REMOTE' will be
|
|
||||||
* replaced with full paths to a temporary file during some of the tests
|
|
||||||
*/
|
|
||||||
return "(echo \"$MERGED\")";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,136 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2022, Simeon Andreev <simeon.danailov.andreev@gmail.com> and others.
|
||||||
|
*
|
||||||
|
* 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.pgm;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.eclipse.jgit.api.CherryPickResult;
|
||||||
|
import org.eclipse.jgit.api.Git;
|
||||||
|
import org.eclipse.jgit.diff.DiffEntry;
|
||||||
|
import org.eclipse.jgit.lib.CLIRepositoryTestCase;
|
||||||
|
import org.eclipse.jgit.pgm.opt.CmdLineParser;
|
||||||
|
import org.eclipse.jgit.pgm.opt.SubcommandHandler;
|
||||||
|
import org.eclipse.jgit.revwalk.RevCommit;
|
||||||
|
import org.eclipse.jgit.treewalk.FileTreeIterator;
|
||||||
|
import org.eclipse.jgit.treewalk.TreeWalk;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.kohsuke.args4j.Argument;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base test case for the {@code difftool} and {@code mergetool} commands.
|
||||||
|
*/
|
||||||
|
public abstract class ExternalToolTestCase extends CLIRepositoryTestCase {
|
||||||
|
|
||||||
|
public static class GitCliJGitWrapperParser {
|
||||||
|
@Argument(index = 0, metaVar = "metaVar_command", required = true, handler = SubcommandHandler.class)
|
||||||
|
TextBuiltin subcommand;
|
||||||
|
|
||||||
|
@Argument(index = 1, metaVar = "metaVar_arg")
|
||||||
|
List<String> arguments = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static final String TOOL_NAME = "some_tool";
|
||||||
|
|
||||||
|
private static final String TEST_BRANCH_NAME = "test_branch";
|
||||||
|
|
||||||
|
private Git git;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
super.setUp();
|
||||||
|
git = new Git(db);
|
||||||
|
git.commit().setMessage("initial commit").call();
|
||||||
|
git.branchCreate().setName(TEST_BRANCH_NAME).call();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String[] runAndCaptureUsingInitRaw(String... args)
|
||||||
|
throws Exception {
|
||||||
|
CLIGitCommand.Result result = new CLIGitCommand.Result();
|
||||||
|
|
||||||
|
GitCliJGitWrapperParser bean = new GitCliJGitWrapperParser();
|
||||||
|
CmdLineParser clp = new CmdLineParser(bean);
|
||||||
|
clp.parseArgument(args);
|
||||||
|
|
||||||
|
TextBuiltin cmd = bean.subcommand;
|
||||||
|
cmd.initRaw(db, null, null, result.out, result.err);
|
||||||
|
cmd.execute(bean.arguments.toArray(new String[bean.arguments.size()]));
|
||||||
|
if (cmd.getOutputWriter() != null) {
|
||||||
|
cmd.getOutputWriter().flush();
|
||||||
|
}
|
||||||
|
if (cmd.getErrorWriter() != null) {
|
||||||
|
cmd.getErrorWriter().flush();
|
||||||
|
}
|
||||||
|
return result.outLines().toArray(new String[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected CherryPickResult createMergeConflict() throws Exception {
|
||||||
|
writeTrashFile("a", "Hello world a");
|
||||||
|
writeTrashFile("b", "Hello world b");
|
||||||
|
git.add().addFilepattern(".").call();
|
||||||
|
git.commit().setMessage("files a & b added").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();
|
||||||
|
git.checkout().setName(TEST_BRANCH_NAME).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.branchCreate().setName("branch_2").call();
|
||||||
|
CherryPickResult result = git.cherryPick().include(commit1).call();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected RevCommit createUnstagedChanges() throws Exception {
|
||||||
|
writeTrashFile("a", "Hello world a");
|
||||||
|
writeTrashFile("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;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected RevCommit createStagedChanges() throws Exception {
|
||||||
|
RevCommit commit = createUnstagedChanges();
|
||||||
|
git.add().addFilepattern(".").call();
|
||||||
|
return commit;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected List<DiffEntry> getRepositoryChanges(RevCommit commit)
|
||||||
|
throws Exception {
|
||||||
|
TreeWalk tw = new TreeWalk(db);
|
||||||
|
tw.addTree(commit.getTree());
|
||||||
|
FileTreeIterator modifiedTree = new FileTreeIterator(db);
|
||||||
|
tw.addTree(modifiedTree);
|
||||||
|
List<DiffEntry> changes = DiffEntry.scan(tw);
|
||||||
|
return changes;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static void assertArrayOfLinesEquals(String failMessage,
|
||||||
|
String[] expected, String[] actual) {
|
||||||
|
assertEquals(failMessage, toString(expected), toString(actual));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static String getEchoCommand() {
|
||||||
|
/*
|
||||||
|
* use 'MERGED' placeholder, as both 'LOCAL' and 'REMOTE' will be
|
||||||
|
* replaced with full paths to a temporary file during some of the tests
|
||||||
|
*/
|
||||||
|
return "(echo \"$MERGED\")";
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,136 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2022, Simeon Andreev <simeon.danailov.andreev@gmail.com> and others.
|
||||||
|
*
|
||||||
|
* 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.pgm;
|
||||||
|
|
||||||
|
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_CMD;
|
||||||
|
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_PROMPT;
|
||||||
|
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_TOOL;
|
||||||
|
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_MERGETOOL_SECTION;
|
||||||
|
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_MERGE_SECTION;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.eclipse.jgit.internal.diffmergetool.CommandLineMergeTool;
|
||||||
|
import org.eclipse.jgit.lib.StoredConfig;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Testing the {@code mergetool} command.
|
||||||
|
*/
|
||||||
|
public class MergeToolTest extends ExternalToolTestCase {
|
||||||
|
|
||||||
|
private static final String MERGE_TOOL = CONFIG_MERGETOOL_SECTION;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
super.setUp();
|
||||||
|
configureEchoTool(TOOL_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTool() throws Exception {
|
||||||
|
createMergeConflict();
|
||||||
|
String[] expectedOutput = getExpectedToolOutput();
|
||||||
|
|
||||||
|
String[] options = {
|
||||||
|
"--tool",
|
||||||
|
"-t",
|
||||||
|
};
|
||||||
|
|
||||||
|
for (String option : options) {
|
||||||
|
assertArrayOfLinesEquals("Incorrect output for option: " + option,
|
||||||
|
expectedOutput,
|
||||||
|
runAndCaptureUsingInitRaw(MERGE_TOOL, option,
|
||||||
|
TOOL_NAME));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testToolNoGuiNoPrompt() throws Exception {
|
||||||
|
createMergeConflict();
|
||||||
|
String[] expectedOutput = getExpectedToolOutput();
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testToolHelp() throws Exception {
|
||||||
|
CommandLineMergeTool[] defaultTools = CommandLineMergeTool.values();
|
||||||
|
List<String> expectedOutput = new ArrayList<>();
|
||||||
|
expectedOutput.add(
|
||||||
|
"'git mergetool --tool=<tool>' may be set to one of the following:");
|
||||||
|
for (CommandLineMergeTool defaultTool : defaultTools) {
|
||||||
|
String toolName = defaultTool.name();
|
||||||
|
expectedOutput.add(toolName);
|
||||||
|
}
|
||||||
|
String customToolHelpLine = TOOL_NAME + "." + CONFIG_KEY_CMD + " "
|
||||||
|
+ getEchoCommand();
|
||||||
|
expectedOutput.add("user-defined:");
|
||||||
|
expectedOutput.add(customToolHelpLine);
|
||||||
|
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.",
|
||||||
|
};
|
||||||
|
expectedOutput.addAll(Arrays.asList(userDefinedToolsHelp));
|
||||||
|
|
||||||
|
String option = "--tool-help";
|
||||||
|
assertArrayOfLinesEquals("Incorrect output for option: " + option,
|
||||||
|
expectedOutput.toArray(new String[0]),
|
||||||
|
runAndCaptureUsingInitRaw(MERGE_TOOL, option));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void configureEchoTool(String toolName) {
|
||||||
|
StoredConfig config = db.getConfig();
|
||||||
|
// the default merge tool is configured without a subsection
|
||||||
|
String subsection = null;
|
||||||
|
config.setString(CONFIG_MERGE_SECTION, subsection, CONFIG_KEY_TOOL,
|
||||||
|
toolName);
|
||||||
|
|
||||||
|
String command = getEchoCommand();
|
||||||
|
|
||||||
|
config.setString(CONFIG_MERGETOOL_SECTION, toolName, CONFIG_KEY_CMD,
|
||||||
|
command);
|
||||||
|
/*
|
||||||
|
* prevent prompts as we are running in tests and there is no user to
|
||||||
|
* interact with on the command line
|
||||||
|
*/
|
||||||
|
config.setString(CONFIG_MERGETOOL_SECTION, toolName, CONFIG_KEY_PROMPT,
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
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 + "'...");
|
||||||
|
}
|
||||||
|
return expectedOutput.toArray(new String[0]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,6 +25,7 @@ org.eclipse.jgit.pgm.LsRemote
|
||||||
org.eclipse.jgit.pgm.LsTree
|
org.eclipse.jgit.pgm.LsTree
|
||||||
org.eclipse.jgit.pgm.Merge
|
org.eclipse.jgit.pgm.Merge
|
||||||
org.eclipse.jgit.pgm.MergeBase
|
org.eclipse.jgit.pgm.MergeBase
|
||||||
|
org.eclipse.jgit.pgm.MergeTool
|
||||||
org.eclipse.jgit.pgm.Push
|
org.eclipse.jgit.pgm.Push
|
||||||
org.eclipse.jgit.pgm.ReceivePack
|
org.eclipse.jgit.pgm.ReceivePack
|
||||||
org.eclipse.jgit.pgm.Reflog
|
org.eclipse.jgit.pgm.Reflog
|
||||||
|
|
|
@ -255,6 +255,7 @@ usage_DisplayTheVersionOfJgit=Display the version of jgit
|
||||||
usage_Gc=Cleanup unnecessary files and optimize the local repository
|
usage_Gc=Cleanup unnecessary files and optimize the local repository
|
||||||
usage_Glog=View commit history as a graph
|
usage_Glog=View commit history as a graph
|
||||||
usage_DiffGuiTool=When git-difftool is invoked with the -g or --gui option the default diff tool will be read from the configured diff.guitool variable instead of diff.tool.
|
usage_DiffGuiTool=When git-difftool is invoked with the -g or --gui option the default diff tool will be read from the configured diff.guitool variable instead of diff.tool.
|
||||||
|
usage_MergeGuiTool=When git-mergetool is invoked with the -g or --gui option the default merge tool will be read from the configured merge.guitool variable instead of merge.tool.
|
||||||
usage_noGui=The --no-gui option can be used to override -g or --gui setting.
|
usage_noGui=The --no-gui option can be used to override -g or --gui setting.
|
||||||
usage_IndexPack=Build pack index file for an existing packed archive
|
usage_IndexPack=Build pack index file for an existing packed archive
|
||||||
usage_LFSDirectory=Directory to store large objects
|
usage_LFSDirectory=Directory to store large objects
|
||||||
|
@ -303,6 +304,7 @@ usage_Status=Show the working tree status
|
||||||
usage_StopTrackingAFile=Stop tracking a file
|
usage_StopTrackingAFile=Stop tracking a file
|
||||||
usage_TextHashFunctions=Scan repository to compute maximum number of collisions for hash functions
|
usage_TextHashFunctions=Scan repository to compute maximum number of collisions for hash functions
|
||||||
usage_ToolForDiff=Use the diff tool specified by <tool>. Run git difftool --tool-help for the list of valid <tool> settings.\nIf a diff tool is not specified, git difftool will use the configuration variable diff.tool.
|
usage_ToolForDiff=Use the diff tool specified by <tool>. Run git difftool --tool-help for the list of valid <tool> settings.\nIf a diff tool is not specified, git difftool will use the configuration variable diff.tool.
|
||||||
|
usage_ToolForMerge=Use the merge resolution program specified by <tool>. Run git mergetool --tool-help for the list of valid <tool> settings.\nIf a merge resolution program is not specified, git mergetool will use the configuration variable merge.tool.
|
||||||
usage_UpdateRemoteRepositoryFromLocalRefs=Update remote repository from local refs
|
usage_UpdateRemoteRepositoryFromLocalRefs=Update remote repository from local refs
|
||||||
usage_UseAll=Use all refs found in refs/
|
usage_UseAll=Use all refs found in refs/
|
||||||
usage_UseTags=Use any tag including lightweight tags
|
usage_UseTags=Use any tag including lightweight tags
|
||||||
|
@ -350,6 +352,7 @@ usage_date=date format, one of default, rfc, local, iso, short, raw (as defined
|
||||||
usage_detectRenames=detect renamed files
|
usage_detectRenames=detect renamed files
|
||||||
usage_diffAlgorithm=the diff algorithm to use. Currently supported are: 'myers', 'histogram'
|
usage_diffAlgorithm=the diff algorithm to use. Currently supported are: 'myers', 'histogram'
|
||||||
usage_DiffTool=git difftool is a Git command that allows you to compare and edit files between revisions using common diff tools.\ngit difftool is a frontend to git diff and accepts the same options and arguments.
|
usage_DiffTool=git difftool is a Git command that allows you to compare and edit files between revisions using common diff tools.\ngit difftool is a frontend to git diff and accepts the same options and arguments.
|
||||||
|
usage_MergeTool=git-mergetool - Run merge conflict resolution tools to resolve merge conflicts.\nUse git mergetool to run one of several merge utilities to resolve merge conflicts. It is typically run after git merge.
|
||||||
usage_directoriesToExport=directories to export
|
usage_directoriesToExport=directories to export
|
||||||
usage_disableTheServiceInAllRepositories=disable the service in all repositories
|
usage_disableTheServiceInAllRepositories=disable the service in all repositories
|
||||||
usage_displayAListOfAllRegisteredJgitCommands=Display a list of all registered jgit commands
|
usage_displayAListOfAllRegisteredJgitCommands=Display a list of all registered jgit commands
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2018-2021, Andre Bossert <andre.bossert@siemens.com>
|
* Copyright (C) 2018-2022, Andre Bossert <andre.bossert@siemens.com>
|
||||||
*
|
*
|
||||||
* This program and the accompanying materials are made available under the
|
* This program and the accompanying materials are made available under the
|
||||||
* terms of the Eclipse Distribution License v. 1.0 which is available at
|
* terms of the Eclipse Distribution License v. 1.0 which is available at
|
||||||
|
@ -192,7 +192,7 @@ private void compare(List<DiffEntry> files, boolean showPrompt,
|
||||||
outw.flush();
|
outw.flush();
|
||||||
errw.println(e.getMessage());
|
errw.println(e.getMessage());
|
||||||
throw die(MessageFormat.format(
|
throw die(MessageFormat.format(
|
||||||
CLIText.get().diffToolDied, mergedFilePath, e));
|
CLIText.get().diffToolDied, mergedFilePath), e);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -0,0 +1,212 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2018-2022, Andre Bossert <andre.bossert@siemens.com>
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Distribution License v. 1.0 which is available at
|
||||||
|
* https://www.eclipse.org/org/documents/edl-v10.php.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.eclipse.jgit.pgm;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
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.errors.NoWorkTreeException;
|
||||||
|
import org.eclipse.jgit.errors.RevisionSyntaxException;
|
||||||
|
import org.eclipse.jgit.internal.diffmergetool.MergeTools;
|
||||||
|
import org.eclipse.jgit.lib.IndexDiff.StageState;
|
||||||
|
import org.eclipse.jgit.lib.internal.BooleanTriState;
|
||||||
|
import org.eclipse.jgit.lib.Repository;
|
||||||
|
import org.kohsuke.args4j.Argument;
|
||||||
|
import org.kohsuke.args4j.Option;
|
||||||
|
import org.kohsuke.args4j.spi.RestOfArgumentsHandler;
|
||||||
|
|
||||||
|
@Command(name = "mergetool", common = true, usage = "usage_MergeTool")
|
||||||
|
class MergeTool extends TextBuiltin {
|
||||||
|
private MergeTools mergeTools;
|
||||||
|
|
||||||
|
@Option(name = "--tool", aliases = {
|
||||||
|
"-t" }, metaVar = "metaVar_tool", usage = "usage_ToolForMerge")
|
||||||
|
private String toolName;
|
||||||
|
|
||||||
|
private Optional<Boolean> prompt = Optional.empty();
|
||||||
|
|
||||||
|
@Option(name = "--prompt", usage = "usage_prompt")
|
||||||
|
void setPrompt(@SuppressWarnings("unused") boolean on) {
|
||||||
|
prompt = Optional.of(Boolean.TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Option(name = "--no-prompt", aliases = { "-y" }, usage = "usage_noPrompt")
|
||||||
|
void noPrompt(@SuppressWarnings("unused") boolean on) {
|
||||||
|
prompt = Optional.of(Boolean.FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Option(name = "--tool-help", usage = "usage_toolHelp")
|
||||||
|
private boolean toolHelp;
|
||||||
|
|
||||||
|
private BooleanTriState gui = BooleanTriState.UNSET;
|
||||||
|
|
||||||
|
@Option(name = "--gui", aliases = { "-g" }, usage = "usage_MergeGuiTool")
|
||||||
|
void setGui(@SuppressWarnings("unused") boolean on) {
|
||||||
|
gui = BooleanTriState.TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Option(name = "--no-gui", usage = "usage_noGui")
|
||||||
|
void noGui(@SuppressWarnings("unused") boolean on) {
|
||||||
|
gui = BooleanTriState.FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Argument(required = false, index = 0, metaVar = "metaVar_paths")
|
||||||
|
@Option(name = "--", metaVar = "metaVar_paths", handler = RestOfArgumentsHandler.class)
|
||||||
|
protected List<String> filterPaths;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void init(Repository repository, String gitDir) {
|
||||||
|
super.init(repository, gitDir);
|
||||||
|
mergeTools = new MergeTools(repository);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void run() {
|
||||||
|
try {
|
||||||
|
if (toolHelp) {
|
||||||
|
showToolHelp();
|
||||||
|
} else {
|
||||||
|
// get prompt
|
||||||
|
boolean showPrompt = mergeTools.isInteractive();
|
||||||
|
if (prompt.isPresent()) {
|
||||||
|
showPrompt = prompt.get().booleanValue();
|
||||||
|
}
|
||||||
|
// get passed or default tool name
|
||||||
|
String toolNameSelected = toolName;
|
||||||
|
if ((toolNameSelected == null) || toolNameSelected.isEmpty()) {
|
||||||
|
toolNameSelected = mergeTools.getDefaultToolName(gui);
|
||||||
|
}
|
||||||
|
// get the changed files
|
||||||
|
Map<String, StageState> files = getFiles();
|
||||||
|
if (files.size() > 0) {
|
||||||
|
merge(files, showPrompt, toolNameSelected);
|
||||||
|
} else {
|
||||||
|
outw.println("No files need merging"); //$NON-NLS-1$
|
||||||
|
}
|
||||||
|
}
|
||||||
|
outw.flush();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw die(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
// show the files
|
||||||
|
outw.println("Merging:"); //$NON-NLS-1$
|
||||||
|
for (String fileName : fileNames) {
|
||||||
|
outw.println(fileName);
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
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$
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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$
|
||||||
|
outw.flush();
|
||||||
|
BufferedReader br = new BufferedReader(new InputStreamReader(ins));
|
||||||
|
String line = null;
|
||||||
|
if ((line = br.readLine()) != null) {
|
||||||
|
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$
|
||||||
|
}
|
||||||
|
outw.println(""); //$NON-NLS-1$
|
||||||
|
outw.println("\tuser-defined:"); //$NON-NLS-1$
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
if (filterPaths != null && filterPaths.size() > 0) {
|
||||||
|
for (String path : filterPaths) {
|
||||||
|
statusCommand.addPath(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Status status = statusCommand.call();
|
||||||
|
files = status.getConflictingStageState();
|
||||||
|
}
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -9,13 +9,27 @@
|
||||||
*/
|
*/
|
||||||
package org.eclipse.jgit.internal.diffmergetool;
|
package org.eclipse.jgit.internal.diffmergetool;
|
||||||
|
|
||||||
|
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_MERGETOOL_SECTION;
|
||||||
|
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_MERGE_SECTION;
|
||||||
|
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_CMD;
|
||||||
|
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_GUITOOL;
|
||||||
|
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_PATH;
|
||||||
|
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_PROMPT;
|
||||||
|
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_TOOL;
|
||||||
|
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_TRUST_EXIT_CODE;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.eclipse.jgit.lib.internal.BooleanTriState;
|
import org.eclipse.jgit.lib.internal.BooleanTriState;
|
||||||
import org.eclipse.jgit.storage.file.FileBasedConfig;
|
import org.eclipse.jgit.storage.file.FileBasedConfig;
|
||||||
|
import org.eclipse.jgit.util.FS.ExecutionResult;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -23,12 +37,60 @@
|
||||||
*/
|
*/
|
||||||
public class ExternalMergeToolTest extends ExternalToolTestCase {
|
public class ExternalMergeToolTest extends ExternalToolTestCase {
|
||||||
|
|
||||||
|
@Test(expected = ToolException.class)
|
||||||
|
public void testUserToolWithError() throws Exception {
|
||||||
|
String toolName = "customTool";
|
||||||
|
|
||||||
|
int errorReturnCode = 1;
|
||||||
|
String command = "exit " + errorReturnCode;
|
||||||
|
|
||||||
|
FileBasedConfig config = db.getConfig();
|
||||||
|
config.setString(CONFIG_MERGETOOL_SECTION, toolName, CONFIG_KEY_CMD,
|
||||||
|
command);
|
||||||
|
config.setString(CONFIG_MERGETOOL_SECTION, toolName,
|
||||||
|
CONFIG_KEY_TRUST_EXIT_CODE, String.valueOf(Boolean.TRUE));
|
||||||
|
|
||||||
|
MergeTools manager = new MergeTools(db);
|
||||||
|
|
||||||
|
BooleanTriState prompt = BooleanTriState.UNSET;
|
||||||
|
BooleanTriState gui = BooleanTriState.UNSET;
|
||||||
|
|
||||||
|
manager.merge(db, local, remote, base, merged.getPath(), toolName,
|
||||||
|
prompt, gui);
|
||||||
|
|
||||||
|
fail("Expected exception to be thrown due to external tool exiting with error code: "
|
||||||
|
+ errorReturnCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = ToolException.class)
|
||||||
|
public void testUserToolWithCommandNotFoundError() throws Exception {
|
||||||
|
String toolName = "customTool";
|
||||||
|
|
||||||
|
int errorReturnCode = 127; // command not found
|
||||||
|
String command = "exit " + errorReturnCode;
|
||||||
|
|
||||||
|
FileBasedConfig config = db.getConfig();
|
||||||
|
config.setString(CONFIG_MERGETOOL_SECTION, toolName, CONFIG_KEY_CMD,
|
||||||
|
command);
|
||||||
|
|
||||||
|
MergeTools manager = new MergeTools(db);
|
||||||
|
|
||||||
|
BooleanTriState prompt = BooleanTriState.UNSET;
|
||||||
|
BooleanTriState gui = BooleanTriState.UNSET;
|
||||||
|
|
||||||
|
manager.merge(db, local, remote, base, merged.getPath(), toolName,
|
||||||
|
prompt, gui);
|
||||||
|
|
||||||
|
fail("Expected exception to be thrown due to external tool exiting with error code: "
|
||||||
|
+ errorReturnCode);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testToolNames() {
|
public void testToolNames() {
|
||||||
MergeTools manager = new MergeTools(db);
|
MergeTools manager = new MergeTools(db);
|
||||||
Set<String> actualToolNames = manager.getToolNames();
|
Set<String> actualToolNames = manager.getToolNames();
|
||||||
Set<String> expectedToolNames = Collections.emptySet();
|
Set<String> expectedToolNames = Collections.emptySet();
|
||||||
assertEquals("Incorrect set of external diff tool names",
|
assertEquals("Incorrect set of external merge tool names",
|
||||||
expectedToolNames, actualToolNames);
|
expectedToolNames, actualToolNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,18 +98,58 @@ public void testToolNames() {
|
||||||
public void testAllTools() {
|
public void testAllTools() {
|
||||||
MergeTools manager = new MergeTools(db);
|
MergeTools manager = new MergeTools(db);
|
||||||
Set<String> actualToolNames = manager.getAvailableTools().keySet();
|
Set<String> actualToolNames = manager.getAvailableTools().keySet();
|
||||||
Set<String> expectedToolNames = Collections.emptySet();
|
Set<String> expectedToolNames = new LinkedHashSet<>();
|
||||||
assertEquals("Incorrect set of available external diff tools",
|
CommandLineMergeTool[] defaultTools = CommandLineMergeTool.values();
|
||||||
expectedToolNames, actualToolNames);
|
for (CommandLineMergeTool defaultTool : defaultTools) {
|
||||||
|
String toolName = defaultTool.name();
|
||||||
|
expectedToolNames.add(toolName);
|
||||||
|
}
|
||||||
|
assertEquals("Incorrect set of external merge tools", expectedToolNames,
|
||||||
|
actualToolNames);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOverridePredefinedToolPath() {
|
||||||
|
String toolName = CommandLineMergeTool.guiffy.name();
|
||||||
|
String customToolPath = "/usr/bin/echo";
|
||||||
|
|
||||||
|
FileBasedConfig config = db.getConfig();
|
||||||
|
config.setString(CONFIG_MERGETOOL_SECTION, toolName, CONFIG_KEY_CMD,
|
||||||
|
"echo");
|
||||||
|
config.setString(CONFIG_MERGETOOL_SECTION, toolName, CONFIG_KEY_PATH,
|
||||||
|
customToolPath);
|
||||||
|
|
||||||
|
MergeTools manager = new MergeTools(db);
|
||||||
|
Map<String, ExternalMergeTool> tools = manager.getUserDefinedTools();
|
||||||
|
ExternalMergeTool mergeTool = tools.get(toolName);
|
||||||
|
assertNotNull("Expected tool \"" + toolName + "\" to be user defined",
|
||||||
|
mergeTool);
|
||||||
|
|
||||||
|
String toolPath = mergeTool.getPath();
|
||||||
|
assertEquals("Expected external merge tool to have an overriden path",
|
||||||
|
customToolPath, toolPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUserDefinedTools() {
|
public void testUserDefinedTools() {
|
||||||
|
FileBasedConfig config = db.getConfig();
|
||||||
|
String customToolname = "customTool";
|
||||||
|
config.setString(CONFIG_MERGETOOL_SECTION, customToolname,
|
||||||
|
CONFIG_KEY_CMD, "echo");
|
||||||
|
config.setString(CONFIG_MERGETOOL_SECTION, customToolname,
|
||||||
|
CONFIG_KEY_PATH, "/usr/bin/echo");
|
||||||
|
config.setString(CONFIG_MERGETOOL_SECTION, customToolname,
|
||||||
|
CONFIG_KEY_PROMPT, String.valueOf(false));
|
||||||
|
config.setString(CONFIG_MERGETOOL_SECTION, customToolname,
|
||||||
|
CONFIG_KEY_GUITOOL, String.valueOf(false));
|
||||||
|
config.setString(CONFIG_MERGETOOL_SECTION, customToolname,
|
||||||
|
CONFIG_KEY_TRUST_EXIT_CODE, String.valueOf(false));
|
||||||
MergeTools manager = new MergeTools(db);
|
MergeTools manager = new MergeTools(db);
|
||||||
Set<String> actualToolNames = manager.getUserDefinedTools().keySet();
|
Set<String> actualToolNames = manager.getUserDefinedTools().keySet();
|
||||||
Set<String> expectedToolNames = Collections.emptySet();
|
Set<String> expectedToolNames = new LinkedHashSet<>();
|
||||||
assertEquals("Incorrect set of user defined external diff tools",
|
expectedToolNames.add(customToolname);
|
||||||
expectedToolNames, actualToolNames);
|
assertEquals("Incorrect set of external merge tools", expectedToolNames,
|
||||||
|
actualToolNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -55,55 +157,118 @@ public void testNotAvailableTools() {
|
||||||
MergeTools manager = new MergeTools(db);
|
MergeTools manager = new MergeTools(db);
|
||||||
Set<String> actualToolNames = manager.getNotAvailableTools().keySet();
|
Set<String> actualToolNames = manager.getNotAvailableTools().keySet();
|
||||||
Set<String> expectedToolNames = Collections.emptySet();
|
Set<String> expectedToolNames = Collections.emptySet();
|
||||||
assertEquals("Incorrect set of not available external diff tools",
|
assertEquals("Incorrect set of not available external merge tools",
|
||||||
expectedToolNames, actualToolNames);
|
expectedToolNames, actualToolNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCompare() throws ToolException {
|
public void testCompare() throws ToolException {
|
||||||
MergeTools manager = new MergeTools(db);
|
String toolName = "customTool";
|
||||||
|
|
||||||
|
FileBasedConfig config = db.getConfig();
|
||||||
|
// the default merge tool is configured without a subsection
|
||||||
|
String subsection = null;
|
||||||
|
config.setString(CONFIG_MERGE_SECTION, subsection, CONFIG_KEY_TOOL,
|
||||||
|
toolName);
|
||||||
|
|
||||||
|
String command = getEchoCommand();
|
||||||
|
|
||||||
|
config.setString(CONFIG_MERGETOOL_SECTION, toolName, CONFIG_KEY_CMD,
|
||||||
|
command);
|
||||||
|
|
||||||
String newPath = "";
|
|
||||||
String oldPath = "";
|
|
||||||
String newId = "";
|
|
||||||
String oldId = "";
|
|
||||||
String toolName = "";
|
|
||||||
BooleanTriState prompt = BooleanTriState.UNSET;
|
BooleanTriState prompt = BooleanTriState.UNSET;
|
||||||
BooleanTriState gui = BooleanTriState.UNSET;
|
BooleanTriState gui = BooleanTriState.UNSET;
|
||||||
BooleanTriState trustExitCode = BooleanTriState.UNSET;
|
|
||||||
|
MergeTools manager = new MergeTools(db);
|
||||||
|
|
||||||
int expectedCompareResult = 0;
|
int expectedCompareResult = 0;
|
||||||
int compareResult = manager.merge(newPath, oldPath, newId, oldId,
|
ExecutionResult compareResult = manager.merge(db, local, remote, base,
|
||||||
toolName, prompt, gui, trustExitCode);
|
merged.getPath(), toolName, prompt, gui);
|
||||||
assertEquals("Incorrect compare result for external diff tool",
|
assertEquals("Incorrect compare result for external merge tool",
|
||||||
expectedCompareResult, compareResult);
|
expectedCompareResult, compareResult.getRc());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDefaultTool() throws Exception {
|
public void testDefaultTool() throws Exception {
|
||||||
|
String toolName = "customTool";
|
||||||
|
String guiToolName = "customGuiTool";
|
||||||
|
|
||||||
FileBasedConfig config = db.getConfig();
|
FileBasedConfig config = db.getConfig();
|
||||||
// the default diff tool is configured without a subsection
|
// the default merge tool is configured without a subsection
|
||||||
String subsection = null;
|
String subsection = null;
|
||||||
config.setString("diff", subsection, "tool", "customTool");
|
config.setString(CONFIG_MERGE_SECTION, subsection, CONFIG_KEY_TOOL,
|
||||||
|
toolName);
|
||||||
|
|
||||||
MergeTools manager = new MergeTools(db);
|
MergeTools manager = new MergeTools(db);
|
||||||
BooleanTriState gui = BooleanTriState.UNSET;
|
BooleanTriState gui = BooleanTriState.UNSET;
|
||||||
String defaultToolName = manager.getDefaultToolName(gui);
|
String defaultToolName = manager.getDefaultToolName(gui);
|
||||||
assertEquals(
|
assertEquals(
|
||||||
"Expected configured difftool to be the default external diff tool",
|
"Expected configured mergetool to be the default external merge tool",
|
||||||
"my_default_toolname", defaultToolName);
|
toolName, defaultToolName);
|
||||||
|
|
||||||
gui = BooleanTriState.TRUE;
|
gui = BooleanTriState.TRUE;
|
||||||
String defaultGuiToolName = manager.getDefaultToolName(gui);
|
String defaultGuiToolName = manager.getDefaultToolName(gui);
|
||||||
assertEquals(
|
assertEquals(
|
||||||
"Expected configured difftool to be the default external diff tool",
|
"Expected configured mergetool to be the default external merge tool",
|
||||||
"my_gui_tool", defaultGuiToolName);
|
"my_gui_tool", defaultGuiToolName);
|
||||||
|
|
||||||
config.setString("diff", subsection, "guitool", "customGuiTool");
|
config.setString(CONFIG_MERGE_SECTION, subsection, CONFIG_KEY_GUITOOL,
|
||||||
|
guiToolName);
|
||||||
manager = new MergeTools(db);
|
manager = new MergeTools(db);
|
||||||
defaultGuiToolName = manager.getDefaultToolName(gui);
|
defaultGuiToolName = manager.getDefaultToolName(gui);
|
||||||
assertEquals(
|
assertEquals(
|
||||||
"Expected configured difftool to be the default external diff guitool",
|
"Expected configured mergetool to be the default external merge guitool",
|
||||||
"my_gui_tool", defaultGuiToolName);
|
"my_gui_tool", defaultGuiToolName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOverridePreDefinedToolPath() {
|
||||||
|
String newToolPath = "/tmp/path/";
|
||||||
|
|
||||||
|
CommandLineMergeTool[] defaultTools = CommandLineMergeTool.values();
|
||||||
|
assertTrue("Expected to find pre-defined external merge tools",
|
||||||
|
defaultTools.length > 0);
|
||||||
|
|
||||||
|
CommandLineMergeTool overridenTool = defaultTools[0];
|
||||||
|
String overridenToolName = overridenTool.name();
|
||||||
|
String overridenToolPath = newToolPath + overridenToolName;
|
||||||
|
FileBasedConfig config = db.getConfig();
|
||||||
|
config.setString(CONFIG_MERGETOOL_SECTION, overridenToolName,
|
||||||
|
CONFIG_KEY_PATH, overridenToolPath);
|
||||||
|
|
||||||
|
MergeTools manager = new MergeTools(db);
|
||||||
|
Map<String, ExternalMergeTool> availableTools = manager
|
||||||
|
.getAvailableTools();
|
||||||
|
ExternalMergeTool externalMergeTool = availableTools
|
||||||
|
.get(overridenToolName);
|
||||||
|
String actualMergeToolPath = externalMergeTool.getPath();
|
||||||
|
assertEquals(
|
||||||
|
"Expected pre-defined external merge tool to have overriden path",
|
||||||
|
overridenToolPath, actualMergeToolPath);
|
||||||
|
boolean withBase = true;
|
||||||
|
String expectedMergeToolCommand = overridenToolPath + " "
|
||||||
|
+ overridenTool.getParameters(withBase);
|
||||||
|
String actualMergeToolCommand = externalMergeTool.getCommand();
|
||||||
|
assertEquals(
|
||||||
|
"Expected pre-defined external merge tool to have overriden command",
|
||||||
|
expectedMergeToolCommand, actualMergeToolCommand);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = ToolException.class)
|
||||||
|
public void testUndefinedTool() throws Exception {
|
||||||
|
MergeTools manager = new MergeTools(db);
|
||||||
|
|
||||||
|
String toolName = "undefined";
|
||||||
|
BooleanTriState prompt = BooleanTriState.UNSET;
|
||||||
|
BooleanTriState gui = BooleanTriState.UNSET;
|
||||||
|
|
||||||
|
manager.merge(db, local, remote, base, merged.getPath(), toolName,
|
||||||
|
prompt, gui);
|
||||||
|
fail("Expected exception to be thrown due to not defined external merge tool");
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getEchoCommand() {
|
||||||
|
return "(echo \"$LOCAL\" \"$REMOTE\") > "
|
||||||
|
+ commandResult.getAbsolutePath();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,327 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2018-2022, Andre Bossert <andre.bossert@siemens.com>
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Distribution License v. 1.0 which is available at
|
||||||
|
* https://www.eclipse.org/org/documents/edl-v10.php.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.eclipse.jgit.internal.diffmergetool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pre-defined merge tools.
|
||||||
|
*
|
||||||
|
* Adds same merge tools as also pre-defined in C-Git see "git-core\mergetools\"
|
||||||
|
* see links to command line parameter description for the tools
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* araxis
|
||||||
|
* bc
|
||||||
|
* bc3
|
||||||
|
* codecompare
|
||||||
|
* deltawalker
|
||||||
|
* diffmerge
|
||||||
|
* diffuse
|
||||||
|
* ecmerge
|
||||||
|
* emerge
|
||||||
|
* examdiff
|
||||||
|
* guiffy
|
||||||
|
* gvimdiff
|
||||||
|
* gvimdiff2
|
||||||
|
* gvimdiff3
|
||||||
|
* kdiff3
|
||||||
|
* kompare
|
||||||
|
* meld
|
||||||
|
* opendiff
|
||||||
|
* p4merge
|
||||||
|
* tkdiff
|
||||||
|
* tortoisemerge
|
||||||
|
* vimdiff
|
||||||
|
* vimdiff2
|
||||||
|
* vimdiff3
|
||||||
|
* winmerge
|
||||||
|
* xxdiff
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("nls")
|
||||||
|
public enum CommandLineMergeTool {
|
||||||
|
/**
|
||||||
|
* See: <a href=
|
||||||
|
* "https://www.araxis.com/merge/documentation-windows/command-line.en">https://www.araxis.com/merge/documentation-windows/command-line.en</a>
|
||||||
|
*/
|
||||||
|
araxis("compare",
|
||||||
|
"-wait -merge -3 -a1 \"$BASE\" \"$LOCAL\" \"$REMOTE\" \"$MERGED\"",
|
||||||
|
"-wait -2 \"$LOCAL\" \"$REMOTE\" \"$MERGED\"",
|
||||||
|
false),
|
||||||
|
/**
|
||||||
|
* See: <a href=
|
||||||
|
* "https://www.scootersoftware.com/v4help/index.html?command_line_reference.html">https://www.scootersoftware.com/v4help/index.html?command_line_reference.html</a>
|
||||||
|
*/
|
||||||
|
bc("bcomp", "\"$LOCAL\" \"$REMOTE\" \"$BASE\" --mergeoutput=\"$MERGED\"",
|
||||||
|
"\"$LOCAL\" \"$REMOTE\" --mergeoutput=\"$MERGED\"",
|
||||||
|
false),
|
||||||
|
/**
|
||||||
|
* See: <a href=
|
||||||
|
* "https://www.scootersoftware.com/v4help/index.html?command_line_reference.html">https://www.scootersoftware.com/v4help/index.html?command_line_reference.html</a>
|
||||||
|
*/
|
||||||
|
bc3("bcompare", bc),
|
||||||
|
/**
|
||||||
|
* See: <a href=
|
||||||
|
* "https://www.devart.com/codecompare/docs/index.html?merging_via_command_line.htm">https://www.devart.com/codecompare/docs/index.html?merging_via_command_line.htm</a>
|
||||||
|
*/
|
||||||
|
codecompare("CodeMerge",
|
||||||
|
"-MF=\"$LOCAL\" -TF=\"$REMOTE\" -BF=\"$BASE\" -RF=\"$MERGED\"",
|
||||||
|
"-MF=\"$LOCAL\" -TF=\"$REMOTE\" -RF=\"$MERGED\"",
|
||||||
|
false),
|
||||||
|
/**
|
||||||
|
* See: <a href=
|
||||||
|
* "https://www.deltawalker.com/integrate/command-line">https://www.deltawalker.com/integrate/command-line</a>
|
||||||
|
* <p>
|
||||||
|
* Hint: $(pwd) command must be defined
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
deltawalker("DeltaWalker",
|
||||||
|
"\"$LOCAL\" \"$REMOTE\" \"$BASE\" -pwd=\"$(pwd)\" -merged=\"$MERGED\"",
|
||||||
|
"\"$LOCAL\" \"$REMOTE\" -pwd=\"$(pwd)\" -merged=\"$MERGED\"",
|
||||||
|
true),
|
||||||
|
/**
|
||||||
|
* See: <a href=
|
||||||
|
* "https://sourcegear.com/diffmerge/webhelp/sec__clargs__diff.html">https://sourcegear.com/diffmerge/webhelp/sec__clargs__diff.html</a>
|
||||||
|
*/
|
||||||
|
diffmerge("diffmerge", //$NON-NLS-1$
|
||||||
|
"--merge --result=\"$MERGED\" \"$LOCAL\" \"$BASE\" \"$REMOTE\"",
|
||||||
|
"--merge --result=\"$MERGED\" \"$LOCAL\" \"$REMOTE\"",
|
||||||
|
true),
|
||||||
|
/**
|
||||||
|
* See: <a href=
|
||||||
|
* "http://diffuse.sourceforge.net/manual.html#introduction-usage">http://diffuse.sourceforge.net/manual.html#introduction-usage</a>
|
||||||
|
* <p>
|
||||||
|
* Hint: check the ' | cat' for the call
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
diffuse("diffuse", "\"$LOCAL\" \"$MERGED\" \"$REMOTE\" \"$BASE\"",
|
||||||
|
"\"$LOCAL\" \"$MERGED\" \"$REMOTE\"", false),
|
||||||
|
/**
|
||||||
|
* See: <a href=
|
||||||
|
* "http://www.elliecomputing.com/en/OnlineDoc/ecmerge_en/44205167.asp">http://www.elliecomputing.com/en/OnlineDoc/ecmerge_en/44205167.asp</a>
|
||||||
|
*/
|
||||||
|
ecmerge("ecmerge",
|
||||||
|
"--default --mode=merge3 \"$BASE\" \"$LOCAL\" \"$REMOTE\" --to=\"$MERGED\"",
|
||||||
|
"--default --mode=merge2 \"$LOCAL\" \"$REMOTE\" --to=\"$MERGED\"",
|
||||||
|
false),
|
||||||
|
/**
|
||||||
|
* See: <a href=
|
||||||
|
* "https://www.gnu.org/software/emacs/manual/html_node/emacs/Overview-of-Emerge.html">https://www.gnu.org/software/emacs/manual/html_node/emacs/Overview-of-Emerge.html</a>
|
||||||
|
* <p>
|
||||||
|
* Hint: $(basename) command must be defined
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
emerge("emacs",
|
||||||
|
"-f emerge-files-with-ancestor-command \"$LOCAL\" \"$REMOTE\" \"$BASE\" \"$(basename \"$MERGED\")\"",
|
||||||
|
"-f emerge-files-command \"$LOCAL\" \"$REMOTE\" \"$(basename \"$MERGED\")\"",
|
||||||
|
true),
|
||||||
|
/**
|
||||||
|
* See: <a href=
|
||||||
|
* "https://www.prestosoft.com/ps.asp?page=htmlhelp/edp/command_line_options">https://www.prestosoft.com/ps.asp?page=htmlhelp/edp/command_line_options</a>
|
||||||
|
*/
|
||||||
|
examdiff("ExamDiff",
|
||||||
|
"-merge \"$LOCAL\" \"$BASE\" \"$REMOTE\" -o:\"$MERGED\" -nh",
|
||||||
|
"-merge \"$LOCAL\" \"$REMOTE\" -o:\"$MERGED\" -nh",
|
||||||
|
false),
|
||||||
|
/**
|
||||||
|
* See: <a href=
|
||||||
|
* "https://www.guiffy.com/help/GuiffyHelp/GuiffyCmd.html">https://www.guiffy.com/help/GuiffyHelp/GuiffyCmd.html</a>
|
||||||
|
*/
|
||||||
|
guiffy("guiffy", "-s \"$LOCAL\" \"$REMOTE\" \"$BASE\" \"$MERGED\"",
|
||||||
|
"-m \"$LOCAL\" \"$REMOTE\" \"$MERGED\"", true),
|
||||||
|
/**
|
||||||
|
* See: <a href=
|
||||||
|
* "http://vimdoc.sourceforge.net/htmldoc/diff.html">http://vimdoc.sourceforge.net/htmldoc/diff.html</a>
|
||||||
|
*/
|
||||||
|
gvimdiff("gvim",
|
||||||
|
"-f -d -c '4wincmd w | wincmd J' \"$LOCAL\" \"$BASE\" \"$REMOTE\" \"$MERGED\"",
|
||||||
|
"-f -d -c 'wincmd l' \"$LOCAL\" \"$MERGED\" \"$REMOTE\"",
|
||||||
|
true),
|
||||||
|
/**
|
||||||
|
* See: <a href=
|
||||||
|
* "http://vimdoc.sourceforge.net/htmldoc/diff.html">http://vimdoc.sourceforge.net/htmldoc/diff.html</a>
|
||||||
|
*/
|
||||||
|
gvimdiff2("gvim", "-f -d -c 'wincmd l' \"$LOCAL\" \"$MERGED\" \"$REMOTE\"",
|
||||||
|
"-f -d -c 'wincmd l' \"$LOCAL\" \"$MERGED\" \"$REMOTE\"", true),
|
||||||
|
/**
|
||||||
|
* See: <a href= "http://vimdoc.sourceforge.net/htmldoc/diff.html"></a>
|
||||||
|
*/
|
||||||
|
gvimdiff3("gvim",
|
||||||
|
"-f -d -c 'hid | hid | hid' \"$LOCAL\" \"$REMOTE\" \"$BASE\" \"$MERGED\"",
|
||||||
|
"-f -d -c 'hid | hid' \"$LOCAL\" \"$REMOTE\" \"$MERGED\"", true),
|
||||||
|
/**
|
||||||
|
* See: <a href=
|
||||||
|
* "http://kdiff3.sourceforge.net/doc/documentation.html">http://kdiff3.sourceforge.net/doc/documentation.html</a>
|
||||||
|
*/
|
||||||
|
kdiff3("kdiff3",
|
||||||
|
"--auto --L1 \"$MERGED (Base)\" --L2 \"$MERGED (Local)\" --L3 \"$MERGED (Remote)\" -o \"$MERGED\" \"$BASE\" \"$LOCAL\" \"$REMOTE\"",
|
||||||
|
"--auto --L1 \"$MERGED (Local)\" --L2 \"$MERGED (Remote)\" -o \"$MERGED\" \"$LOCAL\" \"$REMOTE\"",
|
||||||
|
true),
|
||||||
|
/**
|
||||||
|
* See: <a href=
|
||||||
|
* "http://meldmerge.org/help/file-mode.html">http://meldmerge.org/help/file-mode.html</a>
|
||||||
|
* <p>
|
||||||
|
* Hint: use meld with output option only (new versions)
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
meld("meld", "--output=\"$MERGED\" \"$LOCAL\" \"$BASE\" \"$REMOTE\"",
|
||||||
|
"\"$LOCAL\" \"$MERGED\" \"$REMOTE\"",
|
||||||
|
false),
|
||||||
|
/**
|
||||||
|
* See: <a href=
|
||||||
|
* "http://www.manpagez.com/man/1/opendiff/">http://www.manpagez.com/man/1/opendiff/</a>
|
||||||
|
* <p>
|
||||||
|
* Hint: check the ' | cat' for the call
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
opendiff("opendiff",
|
||||||
|
"\"$LOCAL\" \"$REMOTE\" -ancestor \"$BASE\" -merge \"$MERGED\"",
|
||||||
|
"\"$LOCAL\" \"$REMOTE\" -merge \"$MERGED\"",
|
||||||
|
false),
|
||||||
|
/**
|
||||||
|
* See: <a href=
|
||||||
|
* "https://www.perforce.com/manuals/v15.1/cmdref/p4_merge.html">https://www.perforce.com/manuals/v15.1/cmdref/p4_merge.html</a>
|
||||||
|
* <p>
|
||||||
|
* Hint: check how to fix "no base present" / create_virtual_base problem
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
p4merge("p4merge", "\"$BASE\" \"$REMOTE\" \"$LOCAL\" \"$MERGED\"",
|
||||||
|
"\"$REMOTE\" \"$LOCAL\" \"$MERGED\"", false),
|
||||||
|
/**
|
||||||
|
* See: <a href=
|
||||||
|
* "http://linux.math.tifr.res.in/manuals/man/tkdiff.html">http://linux.math.tifr.res.in/manuals/man/tkdiff.html</a>
|
||||||
|
*/
|
||||||
|
tkdiff("tkdiff", "-a \"$BASE\" -o \"$MERGED\" \"$LOCAL\" \"$REMOTE\"",
|
||||||
|
"-o \"$MERGED\" \"$LOCAL\" \"$REMOTE\"",
|
||||||
|
true),
|
||||||
|
/**
|
||||||
|
* See: <a href=
|
||||||
|
* "https://tortoisegit.org/docs/tortoisegitmerge/tme-automation.html#tme-automation-basics">https://tortoisegit.org/docs/tortoisegitmerge/tme-automation.html#tme-automation-basics</a>
|
||||||
|
* <p>
|
||||||
|
* Hint: merge without base is not supported
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* Hint: cannot diff
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
tortoisegitmerge("tortoisegitmerge",
|
||||||
|
"-base \"$BASE\" -mine \"$LOCAL\" -theirs \"$REMOTE\" -merged \"$MERGED\"",
|
||||||
|
null, false),
|
||||||
|
/**
|
||||||
|
* See: <a href=
|
||||||
|
* "https://tortoisegit.org/docs/tortoisegitmerge/tme-automation.html#tme-automation-basics">https://tortoisegit.org/docs/tortoisegitmerge/tme-automation.html#tme-automation-basics</a>
|
||||||
|
* <p>
|
||||||
|
* Hint: merge without base is not supported
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* Hint: cannot diff
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
tortoisemerge("tortoisemerge",
|
||||||
|
"-base:\"$BASE\" -mine:\"$LOCAL\" -theirs:\"$REMOTE\" -merged:\"$MERGED\"",
|
||||||
|
null, false),
|
||||||
|
/**
|
||||||
|
* See: <a href=
|
||||||
|
* "http://vimdoc.sourceforge.net/htmldoc/diff.html">http://vimdoc.sourceforge.net/htmldoc/diff.html</a>
|
||||||
|
*/
|
||||||
|
vimdiff("vim", gvimdiff),
|
||||||
|
/**
|
||||||
|
* See: <a href=
|
||||||
|
* "http://vimdoc.sourceforge.net/htmldoc/diff.html">http://vimdoc.sourceforge.net/htmldoc/diff.html</a>
|
||||||
|
*/
|
||||||
|
vimdiff2("vim", gvimdiff2),
|
||||||
|
/**
|
||||||
|
* See: <a href=
|
||||||
|
* "http://vimdoc.sourceforge.net/htmldoc/diff.html">http://vimdoc.sourceforge.net/htmldoc/diff.html</a>
|
||||||
|
*/
|
||||||
|
vimdiff3("vim", gvimdiff3),
|
||||||
|
/**
|
||||||
|
* See: <a href=
|
||||||
|
* "http://manual.winmerge.org/Command_line.html">http://manual.winmerge.org/Command_line.html</a>
|
||||||
|
* <p>
|
||||||
|
* Hint: check how 'mergetool_find_win32_cmd "WinMergeU.exe" "WinMerge"'
|
||||||
|
* works
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
winmerge("WinMergeU",
|
||||||
|
"-u -e -dl Local -dr Remote \"$LOCAL\" \"$REMOTE\" \"$MERGED\"",
|
||||||
|
"-u -e -dl Local -dr Remote \"$LOCAL\" \"$REMOTE\" \"$MERGED\"",
|
||||||
|
false),
|
||||||
|
/**
|
||||||
|
* See: <a href=
|
||||||
|
* "http://furius.ca/xxdiff/doc/xxdiff-doc.html">http://furius.ca/xxdiff/doc/xxdiff-doc.html</a>
|
||||||
|
*/
|
||||||
|
xxdiff("xxdiff",
|
||||||
|
"-X --show-merged-pane -R 'Accel.SaveAsMerged: \"Ctrl+S\"' -R 'Accel.Search: \"Ctrl+F\"' -R 'Accel.SearchForward: \"Ctrl+G\"' --merged-file \"$MERGED\" \"$LOCAL\" \"$BASE\" \"$REMOTE\"",
|
||||||
|
"-X -R 'Accel.SaveAsMerged: \"Ctrl+S\"' -R 'Accel.Search: \"Ctrl+F\"' -R 'Accel.SearchForward: \"Ctrl+G\"' --merged-file \"$MERGED\" \"$LOCAL\" \"$REMOTE\"",
|
||||||
|
false);
|
||||||
|
|
||||||
|
CommandLineMergeTool(String path, String parametersWithBase,
|
||||||
|
String parametersWithoutBase,
|
||||||
|
boolean exitCodeTrustable) {
|
||||||
|
this.path = path;
|
||||||
|
this.parametersWithBase = parametersWithBase;
|
||||||
|
this.parametersWithoutBase = parametersWithoutBase;
|
||||||
|
this.exitCodeTrustable = exitCodeTrustable;
|
||||||
|
}
|
||||||
|
|
||||||
|
CommandLineMergeTool(CommandLineMergeTool from) {
|
||||||
|
this(from.getPath(), from.getParameters(true),
|
||||||
|
from.getParameters(false), from.isExitCodeTrustable());
|
||||||
|
}
|
||||||
|
|
||||||
|
CommandLineMergeTool(String path, CommandLineMergeTool from) {
|
||||||
|
this(path, from.getParameters(true), from.getParameters(false),
|
||||||
|
from.isExitCodeTrustable());
|
||||||
|
}
|
||||||
|
|
||||||
|
private final String path;
|
||||||
|
|
||||||
|
private final String parametersWithBase;
|
||||||
|
|
||||||
|
private final String parametersWithoutBase;
|
||||||
|
|
||||||
|
private final boolean exitCodeTrustable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return path
|
||||||
|
*/
|
||||||
|
public String getPath() {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param withBase
|
||||||
|
* return parameters with base present?
|
||||||
|
* @return parameters with or without base present
|
||||||
|
*/
|
||||||
|
public String getParameters(boolean withBase) {
|
||||||
|
if (withBase) {
|
||||||
|
return parametersWithBase;
|
||||||
|
}
|
||||||
|
return parametersWithoutBase;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return parameters
|
||||||
|
*/
|
||||||
|
public boolean isExitCodeTrustable() {
|
||||||
|
return exitCodeTrustable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true if command with base present is valid, false otherwise
|
||||||
|
*/
|
||||||
|
public boolean canMergeWithoutBasePresent() {
|
||||||
|
return parametersWithoutBase != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2018-2021, Andre Bossert <andre.bossert@siemens.com>
|
* Copyright (C) 2018-2022, Andre Bossert <andre.bossert@siemens.com>
|
||||||
*
|
*
|
||||||
* This program and the accompanying materials are made available under the
|
* This program and the accompanying materials are made available under the
|
||||||
* terms of the Eclipse Distribution License v. 1.0 which is available at
|
* terms of the Eclipse Distribution License v. 1.0 which is available at
|
||||||
|
@ -56,8 +56,7 @@ public DiffTools(Repository repo) {
|
||||||
* @param remoteFile
|
* @param remoteFile
|
||||||
* the remote file element
|
* the remote file element
|
||||||
* @param mergedFilePath
|
* @param mergedFilePath
|
||||||
* the path of 'merged' file, it equals local or remote path for
|
* the path of 'merged' file, it equals local or remote path
|
||||||
* difftool
|
|
||||||
* @param toolName
|
* @param toolName
|
||||||
* the selected tool name (can be null)
|
* the selected tool name (can be null)
|
||||||
* @param prompt
|
* @param prompt
|
||||||
|
@ -66,7 +65,7 @@ public DiffTools(Repository repo) {
|
||||||
* the GUI option
|
* the GUI option
|
||||||
* @param trustExitCode
|
* @param trustExitCode
|
||||||
* the "trust exit code" option
|
* the "trust exit code" option
|
||||||
* @return the return code from executed tool
|
* @return the execution result from tool
|
||||||
* @throws ToolException
|
* @throws ToolException
|
||||||
*/
|
*/
|
||||||
public ExecutionResult compare(Repository repo, FileElement localFile,
|
public ExecutionResult compare(Repository repo, FileElement localFile,
|
||||||
|
|
|
@ -10,6 +10,8 @@
|
||||||
|
|
||||||
package org.eclipse.jgit.internal.diffmergetool;
|
package org.eclipse.jgit.internal.diffmergetool;
|
||||||
|
|
||||||
|
import org.eclipse.jgit.lib.internal.BooleanTriState;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The merge tool interface.
|
* The merge tool interface.
|
||||||
*/
|
*/
|
||||||
|
@ -18,6 +20,14 @@ public interface ExternalMergeTool extends ExternalDiffTool {
|
||||||
/**
|
/**
|
||||||
* @return the tool "trust exit code" option
|
* @return the tool "trust exit code" option
|
||||||
*/
|
*/
|
||||||
boolean isTrustExitCode();
|
BooleanTriState getTrustExitCode();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param withBase
|
||||||
|
* get command with base present (true) or without base present
|
||||||
|
* (false)
|
||||||
|
* @return the tool command
|
||||||
|
*/
|
||||||
|
String getCommand(boolean withBase);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,13 +10,24 @@
|
||||||
|
|
||||||
package org.eclipse.jgit.internal.diffmergetool;
|
package org.eclipse.jgit.internal.diffmergetool;
|
||||||
|
|
||||||
|
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_CMD;
|
||||||
|
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_GUITOOL;
|
||||||
|
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_KEEP_BACKUP;
|
||||||
|
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_KEEP_TEMPORARIES;
|
||||||
|
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_PATH;
|
||||||
|
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_PROMPT;
|
||||||
|
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_TOOL;
|
||||||
|
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_TRUST_EXIT_CODE;
|
||||||
|
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_WRITE_TO_TEMP;
|
||||||
|
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_MERGETOOL_SECTION;
|
||||||
|
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_MERGE_SECTION;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.eclipse.jgit.lib.Config;
|
import org.eclipse.jgit.lib.Config;
|
||||||
import org.eclipse.jgit.lib.Config.SectionParser;
|
import org.eclipse.jgit.lib.Config.SectionParser;
|
||||||
import org.eclipse.jgit.lib.ConfigConstants;
|
|
||||||
import org.eclipse.jgit.lib.internal.BooleanTriState;
|
import org.eclipse.jgit.lib.internal.BooleanTriState;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -42,31 +53,27 @@ public class MergeToolConfig {
|
||||||
private final Map<String, ExternalMergeTool> tools;
|
private final Map<String, ExternalMergeTool> tools;
|
||||||
|
|
||||||
private MergeToolConfig(Config rc) {
|
private MergeToolConfig(Config rc) {
|
||||||
toolName = rc.getString(ConfigConstants.CONFIG_MERGE_SECTION, null,
|
toolName = rc.getString(CONFIG_MERGE_SECTION, null, CONFIG_KEY_TOOL);
|
||||||
ConfigConstants.CONFIG_KEY_TOOL);
|
guiToolName = rc.getString(CONFIG_MERGE_SECTION, null,
|
||||||
guiToolName = rc.getString(ConfigConstants.CONFIG_MERGE_SECTION, null,
|
CONFIG_KEY_GUITOOL);
|
||||||
ConfigConstants.CONFIG_KEY_GUITOOL);
|
prompt = rc.getBoolean(CONFIG_MERGETOOL_SECTION, toolName,
|
||||||
prompt = rc.getBoolean(ConfigConstants.CONFIG_MERGETOOL_SECTION,
|
CONFIG_KEY_PROMPT, true);
|
||||||
ConfigConstants.CONFIG_KEY_PROMPT, true);
|
keepBackup = rc.getBoolean(CONFIG_MERGETOOL_SECTION,
|
||||||
keepBackup = rc.getBoolean(ConfigConstants.CONFIG_MERGETOOL_SECTION,
|
CONFIG_KEY_KEEP_BACKUP, true);
|
||||||
ConfigConstants.CONFIG_KEY_KEEP_BACKUP, true);
|
keepTemporaries = rc.getBoolean(CONFIG_MERGETOOL_SECTION,
|
||||||
keepTemporaries = rc.getBoolean(
|
CONFIG_KEY_KEEP_TEMPORARIES, false);
|
||||||
ConfigConstants.CONFIG_MERGETOOL_SECTION,
|
writeToTemp = rc.getBoolean(CONFIG_MERGETOOL_SECTION,
|
||||||
ConfigConstants.CONFIG_KEY_KEEP_TEMPORARIES, false);
|
CONFIG_KEY_WRITE_TO_TEMP, false);
|
||||||
writeToTemp = rc.getBoolean(ConfigConstants.CONFIG_MERGETOOL_SECTION,
|
|
||||||
ConfigConstants.CONFIG_KEY_WRITE_TO_TEMP, false);
|
|
||||||
tools = new HashMap<>();
|
tools = new HashMap<>();
|
||||||
Set<String> subsections = rc
|
Set<String> subsections = rc.getSubsections(CONFIG_MERGETOOL_SECTION);
|
||||||
.getSubsections(ConfigConstants.CONFIG_MERGETOOL_SECTION);
|
|
||||||
for (String name : subsections) {
|
for (String name : subsections) {
|
||||||
String cmd = rc.getString(ConfigConstants.CONFIG_MERGETOOL_SECTION,
|
String cmd = rc.getString(CONFIG_MERGETOOL_SECTION, name,
|
||||||
name, ConfigConstants.CONFIG_KEY_CMD);
|
CONFIG_KEY_CMD);
|
||||||
String path = rc.getString(ConfigConstants.CONFIG_MERGETOOL_SECTION,
|
String path = rc.getString(CONFIG_MERGETOOL_SECTION, name,
|
||||||
name, ConfigConstants.CONFIG_KEY_PATH);
|
CONFIG_KEY_PATH);
|
||||||
BooleanTriState trustExitCode = BooleanTriState.FALSE;
|
BooleanTriState trustExitCode = BooleanTriState.FALSE;
|
||||||
String trustStr = rc.getString(
|
String trustStr = rc.getString(CONFIG_MERGETOOL_SECTION, name,
|
||||||
ConfigConstants.CONFIG_MERGETOOL_SECTION, name,
|
CONFIG_KEY_TRUST_EXIT_CODE);
|
||||||
ConfigConstants.CONFIG_KEY_TRUST_EXIT_CODE);
|
|
||||||
if (trustStr != null) {
|
if (trustStr != null) {
|
||||||
trustExitCode = Boolean.valueOf(trustStr).booleanValue()
|
trustExitCode = Boolean.valueOf(trustStr).booleanValue()
|
||||||
? BooleanTriState.TRUE
|
? BooleanTriState.TRUE
|
||||||
|
@ -75,9 +82,8 @@ private MergeToolConfig(Config rc) {
|
||||||
trustExitCode = BooleanTriState.UNSET;
|
trustExitCode = BooleanTriState.UNSET;
|
||||||
}
|
}
|
||||||
if ((cmd != null) || (path != null)) {
|
if ((cmd != null) || (path != null)) {
|
||||||
tools.put(name,
|
tools.put(name, new UserDefinedMergeTool(name, path, cmd,
|
||||||
new UserDefinedMergeTool(name, path, cmd,
|
trustExitCode));
|
||||||
trustExitCode));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,17 +9,21 @@
|
||||||
*/
|
*/
|
||||||
package org.eclipse.jgit.internal.diffmergetool;
|
package org.eclipse.jgit.internal.diffmergetool;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
|
|
||||||
|
import org.eclipse.jgit.lib.Constants;
|
||||||
import org.eclipse.jgit.lib.Repository;
|
import org.eclipse.jgit.lib.Repository;
|
||||||
import org.eclipse.jgit.lib.internal.BooleanTriState;
|
import org.eclipse.jgit.lib.internal.BooleanTriState;
|
||||||
|
import org.eclipse.jgit.util.FS.ExecutionResult;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages merge tools.
|
* Manages merge tools.
|
||||||
*/
|
*/
|
||||||
public class MergeTools {
|
public class MergeTools {
|
||||||
|
|
||||||
private final MergeToolConfig config;
|
private final MergeToolConfig config;
|
||||||
|
|
||||||
private final Map<String, ExternalMergeTool> predefinedTools;
|
private final Map<String, ExternalMergeTool> predefinedTools;
|
||||||
|
@ -33,10 +37,12 @@ public class MergeTools {
|
||||||
public MergeTools(Repository repo) {
|
public MergeTools(Repository repo) {
|
||||||
config = repo.getConfig().get(MergeToolConfig.KEY);
|
config = repo.getConfig().get(MergeToolConfig.KEY);
|
||||||
predefinedTools = setupPredefinedTools();
|
predefinedTools = setupPredefinedTools();
|
||||||
userDefinedTools = setupUserDefinedTools();
|
userDefinedTools = setupUserDefinedTools(config, predefinedTools);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param repo
|
||||||
|
* the repository
|
||||||
* @param localFile
|
* @param localFile
|
||||||
* the local file element
|
* the local file element
|
||||||
* @param remoteFile
|
* @param remoteFile
|
||||||
|
@ -49,19 +55,43 @@ public MergeTools(Repository repo) {
|
||||||
* the selected tool name (can be null)
|
* the selected tool name (can be null)
|
||||||
* @param prompt
|
* @param prompt
|
||||||
* the prompt option
|
* the prompt option
|
||||||
* @param trustExitCode
|
|
||||||
* the "trust exit code" option
|
|
||||||
* @param gui
|
* @param gui
|
||||||
* the GUI option
|
* the GUI option
|
||||||
* @return the execution result from tool
|
* @return the execution result from tool
|
||||||
* @throws ToolException
|
* @throws ToolException
|
||||||
*/
|
*/
|
||||||
public int merge(String localFile,
|
public ExecutionResult merge(Repository repo, FileElement localFile,
|
||||||
String remoteFile, String baseFile, String mergedFilePath,
|
FileElement remoteFile, FileElement baseFile, String mergedFilePath,
|
||||||
String toolName, BooleanTriState prompt, BooleanTriState gui,
|
String toolName, BooleanTriState prompt, BooleanTriState gui)
|
||||||
BooleanTriState trustExitCode)
|
|
||||||
throws ToolException {
|
throws ToolException {
|
||||||
return 0;
|
ExternalMergeTool tool = guessTool(toolName, gui);
|
||||||
|
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$
|
||||||
|
boolean trust = tool.getTrustExitCode() == BooleanTriState.TRUE;
|
||||||
|
CommandExecutor cmdExec = new CommandExecutor(repo.getFS(), trust);
|
||||||
|
return cmdExec.run(command, workingDir, env);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new ToolException(e);
|
||||||
|
} finally {
|
||||||
|
localFile.cleanTemporaries();
|
||||||
|
remoteFile.cleanTemporaries();
|
||||||
|
baseFile.cleanTemporaries();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -99,7 +129,7 @@ public Map<String, ExternalMergeTool> getNotAvailableTools() {
|
||||||
*/
|
*/
|
||||||
public String getDefaultToolName(BooleanTriState gui) {
|
public String getDefaultToolName(BooleanTriState gui) {
|
||||||
return gui != BooleanTriState.UNSET ? "my_gui_tool" //$NON-NLS-1$
|
return gui != BooleanTriState.UNSET ? "my_gui_tool" //$NON-NLS-1$
|
||||||
: "my_default_toolname"; //$NON-NLS-1$
|
: config.getDefaultToolName();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -109,11 +139,58 @@ public boolean isInteractive() {
|
||||||
return config.isPrompt();
|
return config.isPrompt();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, ExternalMergeTool> setupPredefinedTools() {
|
private ExternalMergeTool guessTool(String toolName, BooleanTriState gui)
|
||||||
return new TreeMap<>();
|
throws ToolException {
|
||||||
|
if ((toolName == null) || toolName.isEmpty()) {
|
||||||
|
toolName = getDefaultToolName(gui);
|
||||||
|
}
|
||||||
|
ExternalMergeTool tool = getTool(toolName);
|
||||||
|
if (tool == null) {
|
||||||
|
throw new ToolException("Unknown diff tool " + toolName); //$NON-NLS-1$
|
||||||
|
}
|
||||||
|
return tool;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, ExternalMergeTool> setupUserDefinedTools() {
|
private ExternalMergeTool getTool(final String name) {
|
||||||
return new TreeMap<>();
|
ExternalMergeTool tool = userDefinedTools.get(name);
|
||||||
|
if (tool == null) {
|
||||||
|
tool = predefinedTools.get(name);
|
||||||
|
}
|
||||||
|
return tool;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Map<String, ExternalMergeTool> setupPredefinedTools() {
|
||||||
|
Map<String, ExternalMergeTool> tools = new TreeMap<>();
|
||||||
|
for (CommandLineMergeTool tool : CommandLineMergeTool.values()) {
|
||||||
|
tools.put(tool.name(), new PreDefinedMergeTool(tool));
|
||||||
|
}
|
||||||
|
return tools;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, ExternalMergeTool> setupUserDefinedTools(
|
||||||
|
MergeToolConfig cfg, Map<String, ExternalMergeTool> predefTools) {
|
||||||
|
Map<String, ExternalMergeTool> tools = new TreeMap<>();
|
||||||
|
Map<String, ExternalMergeTool> userTools = cfg.getTools();
|
||||||
|
for (String name : userTools.keySet()) {
|
||||||
|
ExternalMergeTool userTool = userTools.get(name);
|
||||||
|
// if mergetool.<name>.cmd is defined we have user defined tool
|
||||||
|
if (userTool.getCommand() != null) {
|
||||||
|
tools.put(name, userTool);
|
||||||
|
} else if (userTool.getPath() != null) {
|
||||||
|
// if mergetool.<name>.path is defined we just overload the path
|
||||||
|
// of predefined tool
|
||||||
|
PreDefinedMergeTool predefTool = (PreDefinedMergeTool) predefTools
|
||||||
|
.get(name);
|
||||||
|
if (predefTool != null) {
|
||||||
|
predefTool.setPath(userTool.getPath());
|
||||||
|
if (userTool.getTrustExitCode() != BooleanTriState.UNSET) {
|
||||||
|
predefTool
|
||||||
|
.setTrustExitCode(userTool.getTrustExitCode());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tools;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -0,0 +1,91 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2018-2022, Andre Bossert <andre.bossert@siemens.com>
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Distribution License v. 1.0 which is available at
|
||||||
|
* https://www.eclipse.org/org/documents/edl-v10.php.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.eclipse.jgit.internal.diffmergetool;
|
||||||
|
|
||||||
|
import org.eclipse.jgit.lib.internal.BooleanTriState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The pre-defined merge tool.
|
||||||
|
*/
|
||||||
|
public class PreDefinedMergeTool extends UserDefinedMergeTool {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the tool parameters without base
|
||||||
|
*/
|
||||||
|
private final String parametersWithoutBase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the pre-defined merge tool
|
||||||
|
*
|
||||||
|
* @param name
|
||||||
|
* the name
|
||||||
|
* @param path
|
||||||
|
* the path
|
||||||
|
* @param parametersWithBase
|
||||||
|
* the tool parameters that are used together with path as
|
||||||
|
* command and "base is present" ($BASE)
|
||||||
|
* @param parametersWithoutBase
|
||||||
|
* the tool parameters that are used together with path as
|
||||||
|
* command and "base is present" ($BASE)
|
||||||
|
* @param trustExitCode
|
||||||
|
* the "trust exit code" option
|
||||||
|
*/
|
||||||
|
public PreDefinedMergeTool(String name, String path,
|
||||||
|
String parametersWithBase, String parametersWithoutBase,
|
||||||
|
BooleanTriState trustExitCode) {
|
||||||
|
super(name, path, parametersWithBase, trustExitCode);
|
||||||
|
this.parametersWithoutBase = parametersWithoutBase;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the pre-defined merge tool
|
||||||
|
*
|
||||||
|
* @param tool
|
||||||
|
* the command line merge tool
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public PreDefinedMergeTool(CommandLineMergeTool tool) {
|
||||||
|
this(tool.name(), tool.getPath(), tool.getParameters(true),
|
||||||
|
tool.getParameters(false),
|
||||||
|
tool.isExitCodeTrustable() ? BooleanTriState.TRUE
|
||||||
|
: BooleanTriState.FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param trustExitCode
|
||||||
|
* the "trust exit code" option
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setTrustExitCode(BooleanTriState trustExitCode) {
|
||||||
|
super.setTrustExitCode(trustExitCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the tool command (with base present)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getCommand() {
|
||||||
|
return getCommand(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param withBase
|
||||||
|
* get command with base present (true) or without base present
|
||||||
|
* (false)
|
||||||
|
* @return the tool command
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getCommand(boolean withBase) {
|
||||||
|
return getPath() + " " //$NON-NLS-1$
|
||||||
|
+ (withBase ? super.getCommand() : parametersWithoutBase);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -21,7 +21,7 @@ public class UserDefinedMergeTool extends UserDefinedDiffTool
|
||||||
/**
|
/**
|
||||||
* the merge tool "trust exit code" option
|
* the merge tool "trust exit code" option
|
||||||
*/
|
*/
|
||||||
private final BooleanTriState trustExitCode;
|
private BooleanTriState trustExitCode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates the merge tool
|
* Creates the merge tool
|
||||||
|
@ -40,20 +40,30 @@ public UserDefinedMergeTool(String name, String path, String cmd,
|
||||||
super(name, path, cmd);
|
super(name, path, cmd);
|
||||||
this.trustExitCode = trustExitCode;
|
this.trustExitCode = trustExitCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the "trust exit code" flag
|
* @return the "trust exit code" flag
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean isTrustExitCode() {
|
|
||||||
return trustExitCode == BooleanTriState.TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the "trust exit code" option
|
|
||||||
*/
|
|
||||||
public BooleanTriState getTrustExitCode() {
|
public BooleanTriState getTrustExitCode() {
|
||||||
return trustExitCode;
|
return trustExitCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param trustExitCode
|
||||||
|
* the new "trust exit code" flag
|
||||||
|
*/
|
||||||
|
protected void setTrustExitCode(BooleanTriState trustExitCode) {
|
||||||
|
this.trustExitCode = trustExitCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param withBase
|
||||||
|
* not used, because user-defined merge tool can only define one
|
||||||
|
* cmd -> it must handle with and without base present (empty)
|
||||||
|
* @return the tool command
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getCommand(boolean withBase) {
|
||||||
|
return getCommand();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-3-Clause
|
* SPDX-License-Identifier: BSD-3-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.eclipse.jgit.lib;
|
package org.eclipse.jgit.lib;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -66,7 +67,7 @@ public final class ConfigConstants {
|
||||||
public static final String CONFIG_KEY_TRUST_EXIT_CODE = "trustExitCode";
|
public static final String CONFIG_KEY_TRUST_EXIT_CODE = "trustExitCode";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The "cmd" key within "difftool.*." section
|
* The "cmd" key within "difftool.*." or "mergetool.*." section
|
||||||
*
|
*
|
||||||
* @since 6.1
|
* @since 6.1
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Reference in New Issue