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
|
||||
* 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_PROMPT;
|
||||
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_TOOL;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jgit.api.Git;
|
||||
import org.eclipse.jgit.diff.DiffEntry;
|
||||
import org.eclipse.jgit.internal.diffmergetool.CommandLineDiffTool;
|
||||
import org.eclipse.jgit.lib.CLIRepositoryTestCase;
|
||||
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.treewalk.FileTreeIterator;
|
||||
import org.eclipse.jgit.treewalk.TreeWalk;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.kohsuke.args4j.Argument;
|
||||
|
||||
/**
|
||||
* Testing the {@code difftool} command.
|
||||
*/
|
||||
public class DiffToolTest extends CLIRepositoryTestCase {
|
||||
public static class GitCliJGitWrapperParser {
|
||||
@Argument(index = 0, metaVar = "metaVar_command", required = true, handler = SubcommandHandler.class)
|
||||
TextBuiltin subcommand;
|
||||
public class DiffToolTest extends ExternalToolTestCase {
|
||||
|
||||
@Argument(index = 1, metaVar = "metaVar_arg")
|
||||
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;
|
||||
private static final String DIFF_TOOL = CONFIG_DIFFTOOL_SECTION;
|
||||
|
||||
@Override
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
git = new Git(db);
|
||||
git.commit().setMessage("initial commit").call();
|
||||
configureEchoTool(TOOL_NAME);
|
||||
}
|
||||
|
||||
|
@ -83,7 +45,7 @@ public void setUp() throws Exception {
|
|||
public void testNotDefinedTool() throws Exception {
|
||||
createUnstagedChanges();
|
||||
|
||||
runAndCaptureUsingInitRaw("difftool", "--tool", "undefined");
|
||||
runAndCaptureUsingInitRaw(DIFF_TOOL, "--tool", "undefined");
|
||||
fail("Expected exception when trying to run undefined tool");
|
||||
}
|
||||
|
||||
|
@ -91,7 +53,7 @@ public void testNotDefinedTool() throws Exception {
|
|||
public void testTool() throws Exception {
|
||||
RevCommit commit = createUnstagedChanges();
|
||||
List<DiffEntry> changes = getRepositoryChanges(commit);
|
||||
String[] expectedOutput = getExpectedDiffToolOutput(changes);
|
||||
String[] expectedOutput = getExpectedToolOutput(changes);
|
||||
|
||||
String[] options = {
|
||||
"--tool",
|
||||
|
@ -101,7 +63,7 @@ public void testTool() throws Exception {
|
|||
for (String option : options) {
|
||||
assertArrayOfLinesEquals("Incorrect output for option: " + option,
|
||||
expectedOutput,
|
||||
runAndCaptureUsingInitRaw("difftool", option,
|
||||
runAndCaptureUsingInitRaw(DIFF_TOOL, option,
|
||||
TOOL_NAME));
|
||||
}
|
||||
}
|
||||
|
@ -110,13 +72,13 @@ public void testTool() throws Exception {
|
|||
public void testToolTrustExitCode() throws Exception {
|
||||
RevCommit commit = createUnstagedChanges();
|
||||
List<DiffEntry> changes = getRepositoryChanges(commit);
|
||||
String[] expectedOutput = getExpectedDiffToolOutput(changes);
|
||||
String[] expectedOutput = getExpectedToolOutput(changes);
|
||||
|
||||
String[] options = { "--tool", "-t", };
|
||||
|
||||
for (String option : options) {
|
||||
assertArrayOfLinesEquals("Incorrect output for option: " + option,
|
||||
expectedOutput, runAndCaptureUsingInitRaw("difftool",
|
||||
expectedOutput, runAndCaptureUsingInitRaw(DIFF_TOOL,
|
||||
"--trust-exit-code", option, TOOL_NAME));
|
||||
}
|
||||
}
|
||||
|
@ -125,13 +87,13 @@ expectedOutput, runAndCaptureUsingInitRaw("difftool",
|
|||
public void testToolNoGuiNoPromptNoTrustExitcode() throws Exception {
|
||||
RevCommit commit = createUnstagedChanges();
|
||||
List<DiffEntry> changes = getRepositoryChanges(commit);
|
||||
String[] expectedOutput = getExpectedDiffToolOutput(changes);
|
||||
String[] expectedOutput = getExpectedToolOutput(changes);
|
||||
|
||||
String[] options = { "--tool", "-t", };
|
||||
|
||||
for (String option : options) {
|
||||
assertArrayOfLinesEquals("Incorrect output for option: " + option,
|
||||
expectedOutput, runAndCaptureUsingInitRaw("difftool",
|
||||
expectedOutput, runAndCaptureUsingInitRaw(DIFF_TOOL,
|
||||
"--no-gui", "--no-prompt", "--no-trust-exit-code",
|
||||
option, TOOL_NAME));
|
||||
}
|
||||
|
@ -141,13 +103,13 @@ expectedOutput, runAndCaptureUsingInitRaw("difftool",
|
|||
public void testToolCached() throws Exception {
|
||||
RevCommit commit = createStagedChanges();
|
||||
List<DiffEntry> changes = getRepositoryChanges(commit);
|
||||
String[] expectedOutput = getExpectedDiffToolOutput(changes);
|
||||
String[] expectedOutput = getExpectedToolOutput(changes);
|
||||
|
||||
String[] options = { "--cached", "--staged", };
|
||||
|
||||
for (String option : options) {
|
||||
assertArrayOfLinesEquals("Incorrect output for option: " + option,
|
||||
expectedOutput, runAndCaptureUsingInitRaw("difftool",
|
||||
expectedOutput, runAndCaptureUsingInitRaw(DIFF_TOOL,
|
||||
option, "--tool", TOOL_NAME));
|
||||
}
|
||||
}
|
||||
|
@ -174,7 +136,8 @@ public void testToolHelp() throws Exception {
|
|||
|
||||
String option = "--tool-help";
|
||||
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) {
|
||||
|
@ -196,33 +159,7 @@ private void configureEchoTool(String toolName) {
|
|||
String.valueOf(false));
|
||||
}
|
||||
|
||||
private 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;
|
||||
}
|
||||
|
||||
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) {
|
||||
private String[] getExpectedToolOutput(List<DiffEntry> changes) {
|
||||
String[] expectedToolOutput = new String[changes.size()];
|
||||
for (int i = 0; i < changes.size(); ++i) {
|
||||
DiffEntry change = changes.get(i);
|
||||
|
@ -232,17 +169,4 @@ private String[] getExpectedDiffToolOutput(List<DiffEntry> changes) {
|
|||
}
|
||||
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.Merge
|
||||
org.eclipse.jgit.pgm.MergeBase
|
||||
org.eclipse.jgit.pgm.MergeTool
|
||||
org.eclipse.jgit.pgm.Push
|
||||
org.eclipse.jgit.pgm.ReceivePack
|
||||
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_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_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_IndexPack=Build pack index file for an existing packed archive
|
||||
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_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_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_UseAll=Use all refs found in refs/
|
||||
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_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_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_disableTheServiceInAllRepositories=disable the service in all repositories
|
||||
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
|
||||
* 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();
|
||||
errw.println(e.getMessage());
|
||||
throw die(MessageFormat.format(
|
||||
CLIText.get().diffToolDied, mergedFilePath, e));
|
||||
CLIText.get().diffToolDied, mergedFilePath), e);
|
||||
}
|
||||
} else {
|
||||
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;
|
||||
|
||||
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.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jgit.lib.internal.BooleanTriState;
|
||||
import org.eclipse.jgit.storage.file.FileBasedConfig;
|
||||
import org.eclipse.jgit.util.FS.ExecutionResult;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
|
@ -23,12 +37,60 @@
|
|||
*/
|
||||
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
|
||||
public void testToolNames() {
|
||||
MergeTools manager = new MergeTools(db);
|
||||
Set<String> actualToolNames = manager.getToolNames();
|
||||
Set<String> expectedToolNames = Collections.emptySet();
|
||||
assertEquals("Incorrect set of external diff tool names",
|
||||
assertEquals("Incorrect set of external merge tool names",
|
||||
expectedToolNames, actualToolNames);
|
||||
}
|
||||
|
||||
|
@ -36,18 +98,58 @@ public void testToolNames() {
|
|||
public void testAllTools() {
|
||||
MergeTools manager = new MergeTools(db);
|
||||
Set<String> actualToolNames = manager.getAvailableTools().keySet();
|
||||
Set<String> expectedToolNames = Collections.emptySet();
|
||||
assertEquals("Incorrect set of available external diff tools",
|
||||
expectedToolNames, actualToolNames);
|
||||
Set<String> expectedToolNames = new LinkedHashSet<>();
|
||||
CommandLineMergeTool[] defaultTools = CommandLineMergeTool.values();
|
||||
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
|
||||
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);
|
||||
Set<String> actualToolNames = manager.getUserDefinedTools().keySet();
|
||||
Set<String> expectedToolNames = Collections.emptySet();
|
||||
assertEquals("Incorrect set of user defined external diff tools",
|
||||
expectedToolNames, actualToolNames);
|
||||
Set<String> expectedToolNames = new LinkedHashSet<>();
|
||||
expectedToolNames.add(customToolname);
|
||||
assertEquals("Incorrect set of external merge tools", expectedToolNames,
|
||||
actualToolNames);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -55,55 +157,118 @@ public void testNotAvailableTools() {
|
|||
MergeTools manager = new MergeTools(db);
|
||||
Set<String> actualToolNames = manager.getNotAvailableTools().keySet();
|
||||
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);
|
||||
}
|
||||
|
||||
@Test
|
||||
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 gui = BooleanTriState.UNSET;
|
||||
BooleanTriState trustExitCode = BooleanTriState.UNSET;
|
||||
|
||||
MergeTools manager = new MergeTools(db);
|
||||
|
||||
int expectedCompareResult = 0;
|
||||
int compareResult = manager.merge(newPath, oldPath, newId, oldId,
|
||||
toolName, prompt, gui, trustExitCode);
|
||||
assertEquals("Incorrect compare result for external diff tool",
|
||||
expectedCompareResult, compareResult);
|
||||
ExecutionResult compareResult = manager.merge(db, local, remote, base,
|
||||
merged.getPath(), toolName, prompt, gui);
|
||||
assertEquals("Incorrect compare result for external merge tool",
|
||||
expectedCompareResult, compareResult.getRc());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultTool() throws Exception {
|
||||
String toolName = "customTool";
|
||||
String guiToolName = "customGuiTool";
|
||||
|
||||
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;
|
||||
config.setString("diff", subsection, "tool", "customTool");
|
||||
config.setString(CONFIG_MERGE_SECTION, subsection, CONFIG_KEY_TOOL,
|
||||
toolName);
|
||||
|
||||
MergeTools manager = new MergeTools(db);
|
||||
BooleanTriState gui = BooleanTriState.UNSET;
|
||||
String defaultToolName = manager.getDefaultToolName(gui);
|
||||
assertEquals(
|
||||
"Expected configured difftool to be the default external diff tool",
|
||||
"my_default_toolname", defaultToolName);
|
||||
"Expected configured mergetool to be the default external merge tool",
|
||||
toolName, defaultToolName);
|
||||
|
||||
gui = BooleanTriState.TRUE;
|
||||
String defaultGuiToolName = manager.getDefaultToolName(gui);
|
||||
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);
|
||||
|
||||
config.setString("diff", subsection, "guitool", "customGuiTool");
|
||||
config.setString(CONFIG_MERGE_SECTION, subsection, CONFIG_KEY_GUITOOL,
|
||||
guiToolName);
|
||||
manager = new MergeTools(db);
|
||||
defaultGuiToolName = manager.getDefaultToolName(gui);
|
||||
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);
|
||||
}
|
||||
|
||||
@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
|
||||
* terms of the Eclipse Distribution License v. 1.0 which is available at
|
||||
|
@ -56,8 +56,7 @@ public DiffTools(Repository repo) {
|
|||
* @param remoteFile
|
||||
* the remote file element
|
||||
* @param mergedFilePath
|
||||
* the path of 'merged' file, it equals local or remote path for
|
||||
* difftool
|
||||
* the path of 'merged' file, it equals local or remote path
|
||||
* @param toolName
|
||||
* the selected tool name (can be null)
|
||||
* @param prompt
|
||||
|
@ -66,7 +65,7 @@ public DiffTools(Repository repo) {
|
|||
* the GUI option
|
||||
* @param trustExitCode
|
||||
* the "trust exit code" option
|
||||
* @return the return code from executed tool
|
||||
* @return the execution result from tool
|
||||
* @throws ToolException
|
||||
*/
|
||||
public ExecutionResult compare(Repository repo, FileElement localFile,
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
|
||||
package org.eclipse.jgit.internal.diffmergetool;
|
||||
|
||||
import org.eclipse.jgit.lib.internal.BooleanTriState;
|
||||
|
||||
/**
|
||||
* The merge tool interface.
|
||||
*/
|
||||
|
@ -18,6 +20,14 @@ public interface ExternalMergeTool extends ExternalDiffTool {
|
|||
/**
|
||||
* @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;
|
||||
|
||||
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.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jgit.lib.Config;
|
||||
import org.eclipse.jgit.lib.Config.SectionParser;
|
||||
import org.eclipse.jgit.lib.ConfigConstants;
|
||||
import org.eclipse.jgit.lib.internal.BooleanTriState;
|
||||
|
||||
/**
|
||||
|
@ -42,31 +53,27 @@ public class MergeToolConfig {
|
|||
private final Map<String, ExternalMergeTool> tools;
|
||||
|
||||
private MergeToolConfig(Config rc) {
|
||||
toolName = rc.getString(ConfigConstants.CONFIG_MERGE_SECTION, null,
|
||||
ConfigConstants.CONFIG_KEY_TOOL);
|
||||
guiToolName = rc.getString(ConfigConstants.CONFIG_MERGE_SECTION, null,
|
||||
ConfigConstants.CONFIG_KEY_GUITOOL);
|
||||
prompt = rc.getBoolean(ConfigConstants.CONFIG_MERGETOOL_SECTION,
|
||||
ConfigConstants.CONFIG_KEY_PROMPT, true);
|
||||
keepBackup = rc.getBoolean(ConfigConstants.CONFIG_MERGETOOL_SECTION,
|
||||
ConfigConstants.CONFIG_KEY_KEEP_BACKUP, true);
|
||||
keepTemporaries = rc.getBoolean(
|
||||
ConfigConstants.CONFIG_MERGETOOL_SECTION,
|
||||
ConfigConstants.CONFIG_KEY_KEEP_TEMPORARIES, false);
|
||||
writeToTemp = rc.getBoolean(ConfigConstants.CONFIG_MERGETOOL_SECTION,
|
||||
ConfigConstants.CONFIG_KEY_WRITE_TO_TEMP, false);
|
||||
toolName = rc.getString(CONFIG_MERGE_SECTION, null, CONFIG_KEY_TOOL);
|
||||
guiToolName = rc.getString(CONFIG_MERGE_SECTION, null,
|
||||
CONFIG_KEY_GUITOOL);
|
||||
prompt = rc.getBoolean(CONFIG_MERGETOOL_SECTION, toolName,
|
||||
CONFIG_KEY_PROMPT, true);
|
||||
keepBackup = rc.getBoolean(CONFIG_MERGETOOL_SECTION,
|
||||
CONFIG_KEY_KEEP_BACKUP, true);
|
||||
keepTemporaries = rc.getBoolean(CONFIG_MERGETOOL_SECTION,
|
||||
CONFIG_KEY_KEEP_TEMPORARIES, false);
|
||||
writeToTemp = rc.getBoolean(CONFIG_MERGETOOL_SECTION,
|
||||
CONFIG_KEY_WRITE_TO_TEMP, false);
|
||||
tools = new HashMap<>();
|
||||
Set<String> subsections = rc
|
||||
.getSubsections(ConfigConstants.CONFIG_MERGETOOL_SECTION);
|
||||
Set<String> subsections = rc.getSubsections(CONFIG_MERGETOOL_SECTION);
|
||||
for (String name : subsections) {
|
||||
String cmd = rc.getString(ConfigConstants.CONFIG_MERGETOOL_SECTION,
|
||||
name, ConfigConstants.CONFIG_KEY_CMD);
|
||||
String path = rc.getString(ConfigConstants.CONFIG_MERGETOOL_SECTION,
|
||||
name, ConfigConstants.CONFIG_KEY_PATH);
|
||||
String cmd = rc.getString(CONFIG_MERGETOOL_SECTION, name,
|
||||
CONFIG_KEY_CMD);
|
||||
String path = rc.getString(CONFIG_MERGETOOL_SECTION, name,
|
||||
CONFIG_KEY_PATH);
|
||||
BooleanTriState trustExitCode = BooleanTriState.FALSE;
|
||||
String trustStr = rc.getString(
|
||||
ConfigConstants.CONFIG_MERGETOOL_SECTION, name,
|
||||
ConfigConstants.CONFIG_KEY_TRUST_EXIT_CODE);
|
||||
String trustStr = rc.getString(CONFIG_MERGETOOL_SECTION, name,
|
||||
CONFIG_KEY_TRUST_EXIT_CODE);
|
||||
if (trustStr != null) {
|
||||
trustExitCode = Boolean.valueOf(trustStr).booleanValue()
|
||||
? BooleanTriState.TRUE
|
||||
|
@ -75,9 +82,8 @@ private MergeToolConfig(Config rc) {
|
|||
trustExitCode = BooleanTriState.UNSET;
|
||||
}
|
||||
if ((cmd != null) || (path != null)) {
|
||||
tools.put(name,
|
||||
new UserDefinedMergeTool(name, path, cmd,
|
||||
trustExitCode));
|
||||
tools.put(name, new UserDefinedMergeTool(name, path, cmd,
|
||||
trustExitCode));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,17 +9,21 @@
|
|||
*/
|
||||
package org.eclipse.jgit.internal.diffmergetool;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import org.eclipse.jgit.lib.Constants;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
import org.eclipse.jgit.lib.internal.BooleanTriState;
|
||||
import org.eclipse.jgit.util.FS.ExecutionResult;
|
||||
|
||||
/**
|
||||
* Manages merge tools.
|
||||
*/
|
||||
public class MergeTools {
|
||||
|
||||
private final MergeToolConfig config;
|
||||
|
||||
private final Map<String, ExternalMergeTool> predefinedTools;
|
||||
|
@ -33,10 +37,12 @@ public class MergeTools {
|
|||
public MergeTools(Repository repo) {
|
||||
config = repo.getConfig().get(MergeToolConfig.KEY);
|
||||
predefinedTools = setupPredefinedTools();
|
||||
userDefinedTools = setupUserDefinedTools();
|
||||
userDefinedTools = setupUserDefinedTools(config, predefinedTools);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param repo
|
||||
* the repository
|
||||
* @param localFile
|
||||
* the local file element
|
||||
* @param remoteFile
|
||||
|
@ -49,19 +55,43 @@ public MergeTools(Repository repo) {
|
|||
* the selected tool name (can be null)
|
||||
* @param prompt
|
||||
* the prompt option
|
||||
* @param trustExitCode
|
||||
* the "trust exit code" option
|
||||
* @param gui
|
||||
* the GUI option
|
||||
* @return the execution result from tool
|
||||
* @throws ToolException
|
||||
*/
|
||||
public int merge(String localFile,
|
||||
String remoteFile, String baseFile, String mergedFilePath,
|
||||
String toolName, BooleanTriState prompt, BooleanTriState gui,
|
||||
BooleanTriState trustExitCode)
|
||||
public ExecutionResult merge(Repository repo, FileElement localFile,
|
||||
FileElement remoteFile, FileElement baseFile, String mergedFilePath,
|
||||
String toolName, BooleanTriState prompt, BooleanTriState gui)
|
||||
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) {
|
||||
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();
|
||||
}
|
||||
|
||||
private Map<String, ExternalMergeTool> setupPredefinedTools() {
|
||||
return new TreeMap<>();
|
||||
private ExternalMergeTool guessTool(String toolName, BooleanTriState gui)
|
||||
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() {
|
||||
return new TreeMap<>();
|
||||
private ExternalMergeTool getTool(final String name) {
|
||||
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
|
||||
*/
|
||||
private final BooleanTriState trustExitCode;
|
||||
private BooleanTriState trustExitCode;
|
||||
|
||||
/**
|
||||
* Creates the merge tool
|
||||
|
@ -40,20 +40,30 @@ public UserDefinedMergeTool(String name, String path, String cmd,
|
|||
super(name, path, cmd);
|
||||
this.trustExitCode = trustExitCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the "trust exit code" flag
|
||||
*/
|
||||
@Override
|
||||
public boolean isTrustExitCode() {
|
||||
return trustExitCode == BooleanTriState.TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the "trust exit code" option
|
||||
*/
|
||||
public BooleanTriState getTrustExitCode() {
|
||||
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
|
||||
*/
|
||||
|
||||
package org.eclipse.jgit.lib;
|
||||
|
||||
/**
|
||||
|
@ -66,7 +67,7 @@ public final class ConfigConstants {
|
|||
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
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue