Teach JGit to handle external diff/merge tools defined in .gitattributes
Adds API that allows UI to find (and handle) diff/merge tools, specific for the given path. The assumption is that user can specify file type specific diff/merge tools via gitattributes. Bug: 552840 Change-Id: I1daa091e9afa542a9ebb5417853dff0452ed52dd Signed-off-by: Mykola Zakharchuk <zakharchuk.vn@gmail.com> Signed-off-by: Andrey Loskutov <loskutov@gmx.de> Signed-off-by: Andre Bossert <andre.bossert@siemens.com>
This commit is contained in:
parent
ff77d412a9
commit
c32694e5ae
|
@ -79,7 +79,7 @@ public void testUserToolWithCommandNotFoundError() throws Exception {
|
||||||
+ errorReturnCode);
|
+ errorReturnCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test(expected = Die.class)
|
||||||
public void testEmptyToolName() throws Exception {
|
public void testEmptyToolName() throws Exception {
|
||||||
String emptyToolName = "";
|
String emptyToolName = "";
|
||||||
|
|
||||||
|
@ -95,6 +95,7 @@ public void testEmptyToolName() throws Exception {
|
||||||
String[] expectedErrorOutput = { araxisErrorLine, araxisErrorLine, };
|
String[] expectedErrorOutput = { araxisErrorLine, araxisErrorLine, };
|
||||||
runAndCaptureUsingInitRaw(Arrays.asList(expectedErrorOutput), DIFF_TOOL,
|
runAndCaptureUsingInitRaw(Arrays.asList(expectedErrorOutput), DIFF_TOOL,
|
||||||
"--no-prompt");
|
"--no-prompt");
|
||||||
|
fail("Expected exception to be thrown due to external tool exiting with an error");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
@ -34,6 +35,8 @@
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.eclipse.jgit.junit.TestRepository;
|
||||||
|
import org.eclipse.jgit.lib.Repository;
|
||||||
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.eclipse.jgit.util.FS.ExecutionResult;
|
||||||
|
@ -127,13 +130,20 @@ public void testUserDefinedToolWithPrompt() throws Exception {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUserDefinedToolWithCancelledPrompt() throws Exception {
|
public void testUserDefinedToolWithCancelledPrompt() throws Exception {
|
||||||
|
String command = getEchoCommand();
|
||||||
|
|
||||||
|
FileBasedConfig config = db.getConfig();
|
||||||
|
String customToolName = "customTool";
|
||||||
|
config.setString(CONFIG_DIFFTOOL_SECTION, customToolName,
|
||||||
|
CONFIG_KEY_CMD, command);
|
||||||
|
|
||||||
DiffTools manager = new DiffTools(db);
|
DiffTools manager = new DiffTools(db);
|
||||||
|
|
||||||
PromptHandler promptHandler = PromptHandler.cancelPrompt();
|
PromptHandler promptHandler = PromptHandler.cancelPrompt();
|
||||||
MissingToolHandler noToolHandler = new MissingToolHandler();
|
MissingToolHandler noToolHandler = new MissingToolHandler();
|
||||||
|
|
||||||
Optional<ExecutionResult> result = manager.compare(local, remote,
|
Optional<ExecutionResult> result = manager.compare(local, remote,
|
||||||
Optional.empty(), BooleanTriState.TRUE, false,
|
Optional.of(customToolName), BooleanTriState.TRUE, false,
|
||||||
BooleanTriState.TRUE, promptHandler, noToolHandler);
|
BooleanTriState.TRUE, promptHandler, noToolHandler);
|
||||||
assertFalse("Expected no result if user cancels the operation",
|
assertFalse("Expected no result if user cancels the operation",
|
||||||
result.isPresent());
|
result.isPresent());
|
||||||
|
@ -245,8 +255,9 @@ public void testDefaultTool() throws Exception {
|
||||||
|
|
||||||
gui = true;
|
gui = true;
|
||||||
String defaultGuiToolName = manager.getDefaultToolName(gui);
|
String defaultGuiToolName = manager.getDefaultToolName(gui);
|
||||||
assertNull("Expected default difftool to not be set",
|
assertEquals(
|
||||||
defaultGuiToolName);
|
"Expected default gui difftool to be the default tool if no gui tool is set",
|
||||||
|
toolName, defaultGuiToolName);
|
||||||
|
|
||||||
config.setString(CONFIG_DIFF_SECTION, subsection, CONFIG_KEY_GUITOOL,
|
config.setString(CONFIG_DIFF_SECTION, subsection, CONFIG_KEY_GUITOOL,
|
||||||
guiToolName);
|
guiToolName);
|
||||||
|
@ -296,6 +307,119 @@ public void testUndefinedTool() throws Exception {
|
||||||
fail("Expected exception to be thrown due to not defined external diff tool");
|
fail("Expected exception to be thrown due to not defined external diff tool");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDefaultToolExecutionWithPrompt() throws Exception {
|
||||||
|
FileBasedConfig config = db.getConfig();
|
||||||
|
// the default diff tool is configured without a subsection
|
||||||
|
String subsection = null;
|
||||||
|
config.setString("diff", subsection, "tool", "customTool");
|
||||||
|
|
||||||
|
String command = getEchoCommand();
|
||||||
|
|
||||||
|
config.setString("difftool", "customTool", "cmd", command);
|
||||||
|
|
||||||
|
DiffTools manager = new DiffTools(db);
|
||||||
|
|
||||||
|
PromptHandler promptHandler = PromptHandler.acceptPrompt();
|
||||||
|
MissingToolHandler noToolHandler = new MissingToolHandler();
|
||||||
|
|
||||||
|
manager.compare(local, remote, Optional.empty(), BooleanTriState.TRUE,
|
||||||
|
false, BooleanTriState.TRUE, promptHandler, noToolHandler);
|
||||||
|
|
||||||
|
assertEchoCommandHasCorrectOutput();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNoDefaultToolName() {
|
||||||
|
DiffTools manager = new DiffTools(db);
|
||||||
|
boolean gui = false;
|
||||||
|
String defaultToolName = manager.getDefaultToolName(gui);
|
||||||
|
assertNull("Expected no default tool when none is configured",
|
||||||
|
defaultToolName);
|
||||||
|
|
||||||
|
gui = true;
|
||||||
|
defaultToolName = manager.getDefaultToolName(gui);
|
||||||
|
assertNull("Expected no default tool when none is configured",
|
||||||
|
defaultToolName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExternalToolInGitAttributes() throws Exception {
|
||||||
|
String content = "attributes:\n*.txt difftool=customTool";
|
||||||
|
File gitattributes = writeTrashFile(".gitattributes", content);
|
||||||
|
gitattributes.deleteOnExit();
|
||||||
|
try (TestRepository<Repository> testRepository = new TestRepository<>(
|
||||||
|
db)) {
|
||||||
|
FileBasedConfig config = db.getConfig();
|
||||||
|
config.setString("difftool", "customTool", "cmd", "echo");
|
||||||
|
testRepository.git().add().addFilepattern(localFile.getName())
|
||||||
|
.call();
|
||||||
|
|
||||||
|
testRepository.git().add().addFilepattern(".gitattributes").call();
|
||||||
|
|
||||||
|
testRepository.branch("master").commit().message("first commit")
|
||||||
|
.create();
|
||||||
|
|
||||||
|
DiffTools manager = new DiffTools(db);
|
||||||
|
Optional<String> tool = manager
|
||||||
|
.getExternalToolFromAttributes(localFile.getName());
|
||||||
|
assertTrue("Failed to find user defined tool", tool.isPresent());
|
||||||
|
assertEquals("Failed to find user defined tool", "customTool",
|
||||||
|
tool.get());
|
||||||
|
} finally {
|
||||||
|
Files.delete(gitattributes.toPath());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNotExternalToolInGitAttributes() throws Exception {
|
||||||
|
String content = "";
|
||||||
|
File gitattributes = writeTrashFile(".gitattributes", content);
|
||||||
|
gitattributes.deleteOnExit();
|
||||||
|
try (TestRepository<Repository> testRepository = new TestRepository<>(
|
||||||
|
db)) {
|
||||||
|
FileBasedConfig config = db.getConfig();
|
||||||
|
config.setString("difftool", "customTool", "cmd", "echo");
|
||||||
|
testRepository.git().add().addFilepattern(localFile.getName())
|
||||||
|
.call();
|
||||||
|
|
||||||
|
testRepository.git().add().addFilepattern(".gitattributes").call();
|
||||||
|
|
||||||
|
testRepository.branch("master").commit().message("first commit")
|
||||||
|
.create();
|
||||||
|
|
||||||
|
DiffTools manager = new DiffTools(db);
|
||||||
|
Optional<String> tool = manager
|
||||||
|
.getExternalToolFromAttributes(localFile.getName());
|
||||||
|
assertFalse(
|
||||||
|
"Expected no external tool if no default tool is specified in .gitattributes",
|
||||||
|
tool.isPresent());
|
||||||
|
} finally {
|
||||||
|
Files.delete(gitattributes.toPath());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = ToolException.class)
|
||||||
|
public void testNullTool() throws Exception {
|
||||||
|
DiffTools manager = new DiffTools(db);
|
||||||
|
|
||||||
|
boolean trustExitCode = true;
|
||||||
|
ExternalDiffTool tool = null;
|
||||||
|
manager.compare(local, remote, tool, trustExitCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = ToolException.class)
|
||||||
|
public void testNullToolWithPrompt() throws Exception {
|
||||||
|
DiffTools manager = new DiffTools(db);
|
||||||
|
|
||||||
|
PromptHandler promptHandler = PromptHandler.cancelPrompt();
|
||||||
|
MissingToolHandler noToolHandler = new MissingToolHandler();
|
||||||
|
|
||||||
|
Optional<String> tool = null;
|
||||||
|
manager.compare(local, remote, tool, BooleanTriState.TRUE, false,
|
||||||
|
BooleanTriState.TRUE, promptHandler, noToolHandler);
|
||||||
|
}
|
||||||
|
|
||||||
private Optional<ExecutionResult> invokeCompare(String toolName)
|
private Optional<ExecutionResult> invokeCompare(String toolName)
|
||||||
throws ToolException {
|
throws ToolException {
|
||||||
DiffTools manager = new DiffTools(db);
|
DiffTools manager = new DiffTools(db);
|
||||||
|
|
|
@ -329,6 +329,68 @@ public void testUndefinedTool() throws Exception {
|
||||||
fail("Expected exception to be thrown due to not defined external merge tool");
|
fail("Expected exception to be thrown due to not defined external merge tool");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDefaultToolExecutionWithPrompt() throws Exception {
|
||||||
|
FileBasedConfig config = db.getConfig();
|
||||||
|
// the default diff tool is configured without a subsection
|
||||||
|
String subsection = null;
|
||||||
|
config.setString("merge", subsection, "tool", "customTool");
|
||||||
|
|
||||||
|
String command = getEchoCommand();
|
||||||
|
|
||||||
|
config.setString("mergetool", "customTool", "cmd", command);
|
||||||
|
|
||||||
|
MergeTools manager = new MergeTools(db);
|
||||||
|
|
||||||
|
PromptHandler promptHandler = PromptHandler.acceptPrompt();
|
||||||
|
MissingToolHandler noToolHandler = new MissingToolHandler();
|
||||||
|
|
||||||
|
manager.merge(local, remote, merged, base, null, Optional.empty(),
|
||||||
|
BooleanTriState.TRUE, false, promptHandler, noToolHandler);
|
||||||
|
|
||||||
|
assertEchoCommandHasCorrectOutput();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNoDefaultToolName() {
|
||||||
|
MergeTools manager = new MergeTools(db);
|
||||||
|
boolean gui = false;
|
||||||
|
String defaultToolName = manager.getDefaultToolName(gui);
|
||||||
|
assertNull("Expected no default tool when none is configured",
|
||||||
|
defaultToolName);
|
||||||
|
|
||||||
|
gui = true;
|
||||||
|
defaultToolName = manager.getDefaultToolName(gui);
|
||||||
|
assertNull("Expected no default tool when none is configured",
|
||||||
|
defaultToolName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = ToolException.class)
|
||||||
|
public void testNullTool() throws Exception {
|
||||||
|
MergeTools manager = new MergeTools(db);
|
||||||
|
|
||||||
|
PromptHandler promptHandler = null;
|
||||||
|
MissingToolHandler noToolHandler = null;
|
||||||
|
|
||||||
|
Optional<String> tool = null;
|
||||||
|
|
||||||
|
manager.merge(local, remote, merged, base, null, tool,
|
||||||
|
BooleanTriState.TRUE, false, promptHandler, noToolHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = ToolException.class)
|
||||||
|
public void testNullToolWithPrompt() throws Exception {
|
||||||
|
MergeTools manager = new MergeTools(db);
|
||||||
|
|
||||||
|
PromptHandler promptHandler = PromptHandler.cancelPrompt();
|
||||||
|
MissingToolHandler noToolHandler = new MissingToolHandler();
|
||||||
|
|
||||||
|
Optional<String> tool = null;
|
||||||
|
|
||||||
|
manager.merge(local, remote, merged, base, null, tool,
|
||||||
|
BooleanTriState.TRUE, false, promptHandler, noToolHandler);
|
||||||
|
}
|
||||||
|
|
||||||
private Optional<ExecutionResult> invokeMerge(String toolName)
|
private Optional<ExecutionResult> invokeMerge(String toolName)
|
||||||
throws ToolException {
|
throws ToolException {
|
||||||
BooleanTriState prompt = BooleanTriState.UNSET;
|
BooleanTriState prompt = BooleanTriState.UNSET;
|
||||||
|
|
|
@ -237,6 +237,9 @@ deleteTagUnexpectedResult=Delete tag returned unexpected result {0}
|
||||||
deletingNotSupported=Deleting {0} not supported.
|
deletingNotSupported=Deleting {0} not supported.
|
||||||
destinationIsNotAWildcard=Destination is not a wildcard.
|
destinationIsNotAWildcard=Destination is not a wildcard.
|
||||||
detachedHeadDetected=HEAD is detached
|
detachedHeadDetected=HEAD is detached
|
||||||
|
diffToolNotGivenError=No diff tool provided and no defaults configured.
|
||||||
|
diffToolNotSpecifiedInGitAttributesError=Diff tool specified in git attributes cannot be found.
|
||||||
|
diffToolNullError=Parameter for diff tool cannot be null.
|
||||||
dirCacheDoesNotHaveABackingFile=DirCache does not have a backing file
|
dirCacheDoesNotHaveABackingFile=DirCache does not have a backing file
|
||||||
dirCacheFileIsNotLocked=DirCache {0} not locked
|
dirCacheFileIsNotLocked=DirCache {0} not locked
|
||||||
dirCacheIsNotLocked=DirCache is not locked
|
dirCacheIsNotLocked=DirCache is not locked
|
||||||
|
@ -457,6 +460,8 @@ mergeStrategyDoesNotSupportHeads=merge strategy {0} does not support {1} heads t
|
||||||
mergeUsingStrategyResultedInDescription=Merge of revisions {0} with base {1} using strategy {2} resulted in: {3}. {4}
|
mergeUsingStrategyResultedInDescription=Merge of revisions {0} with base {1} using strategy {2} resulted in: {3}. {4}
|
||||||
mergeRecursiveConflictsWhenMergingCommonAncestors=Multiple common ancestors were found and merging them resulted in a conflict: {0}, {1}
|
mergeRecursiveConflictsWhenMergingCommonAncestors=Multiple common ancestors were found and merging them resulted in a conflict: {0}, {1}
|
||||||
mergeRecursiveTooManyMergeBasesFor = "More than {0} merge bases for:\n a {1}\n b {2} found:\n count {3}"
|
mergeRecursiveTooManyMergeBasesFor = "More than {0} merge bases for:\n a {1}\n b {2} found:\n count {3}"
|
||||||
|
mergeToolNotGivenError=No merge tool provided and no defaults configured.
|
||||||
|
mergeToolNullError=Parameter for merge tool cannot be null.
|
||||||
messageAndTaggerNotAllowedInUnannotatedTags = Unannotated tags cannot have a message or tagger
|
messageAndTaggerNotAllowedInUnannotatedTags = Unannotated tags cannot have a message or tagger
|
||||||
minutesAgo={0} minutes ago
|
minutesAgo={0} minutes ago
|
||||||
mismatchOffset=mismatch offset for object {0}
|
mismatchOffset=mismatch offset for object {0}
|
||||||
|
|
|
@ -265,6 +265,9 @@ public static JGitText get() {
|
||||||
/***/ public String deletingNotSupported;
|
/***/ public String deletingNotSupported;
|
||||||
/***/ public String destinationIsNotAWildcard;
|
/***/ public String destinationIsNotAWildcard;
|
||||||
/***/ public String detachedHeadDetected;
|
/***/ public String detachedHeadDetected;
|
||||||
|
/***/ public String diffToolNotGivenError;
|
||||||
|
/***/ public String diffToolNotSpecifiedInGitAttributesError;
|
||||||
|
/***/ public String diffToolNullError;
|
||||||
/***/ public String dirCacheDoesNotHaveABackingFile;
|
/***/ public String dirCacheDoesNotHaveABackingFile;
|
||||||
/***/ public String dirCacheFileIsNotLocked;
|
/***/ public String dirCacheFileIsNotLocked;
|
||||||
/***/ public String dirCacheIsNotLocked;
|
/***/ public String dirCacheIsNotLocked;
|
||||||
|
@ -485,6 +488,8 @@ public static JGitText get() {
|
||||||
/***/ public String mergeUsingStrategyResultedInDescription;
|
/***/ public String mergeUsingStrategyResultedInDescription;
|
||||||
/***/ public String mergeRecursiveConflictsWhenMergingCommonAncestors;
|
/***/ public String mergeRecursiveConflictsWhenMergingCommonAncestors;
|
||||||
/***/ public String mergeRecursiveTooManyMergeBasesFor;
|
/***/ public String mergeRecursiveTooManyMergeBasesFor;
|
||||||
|
/***/ public String mergeToolNotGivenError;
|
||||||
|
/***/ public String mergeToolNullError;
|
||||||
/***/ public String messageAndTaggerNotAllowedInUnannotatedTags;
|
/***/ public String messageAndTaggerNotAllowedInUnannotatedTags;
|
||||||
/***/ public String minutesAgo;
|
/***/ public String minutesAgo;
|
||||||
/***/ public String mismatchOffset;
|
/***/ public String mismatchOffset;
|
||||||
|
|
|
@ -13,18 +13,22 @@
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
|
|
||||||
|
import org.eclipse.jgit.internal.JGitText;
|
||||||
import org.eclipse.jgit.lib.Repository;
|
import org.eclipse.jgit.lib.Repository;
|
||||||
import org.eclipse.jgit.lib.StoredConfig;
|
import org.eclipse.jgit.lib.StoredConfig;
|
||||||
import org.eclipse.jgit.lib.internal.BooleanTriState;
|
import org.eclipse.jgit.lib.internal.BooleanTriState;
|
||||||
|
import org.eclipse.jgit.treewalk.TreeWalk;
|
||||||
import org.eclipse.jgit.util.FS;
|
import org.eclipse.jgit.util.FS;
|
||||||
import org.eclipse.jgit.util.FS.ExecutionResult;
|
import org.eclipse.jgit.util.FS.ExecutionResult;
|
||||||
|
import org.eclipse.jgit.util.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages diff tools.
|
* Manages diff tools.
|
||||||
|
@ -39,6 +43,8 @@ public class DiffTools {
|
||||||
|
|
||||||
private final DiffToolConfig config;
|
private final DiffToolConfig config;
|
||||||
|
|
||||||
|
private final Repository repo;
|
||||||
|
|
||||||
private final Map<String, ExternalDiffTool> predefinedTools;
|
private final Map<String, ExternalDiffTool> predefinedTools;
|
||||||
|
|
||||||
private final Map<String, ExternalDiffTool> userDefinedTools;
|
private final Map<String, ExternalDiffTool> userDefinedTools;
|
||||||
|
@ -64,6 +70,7 @@ public DiffTools(StoredConfig config) {
|
||||||
}
|
}
|
||||||
|
|
||||||
private DiffTools(Repository repo, StoredConfig config) {
|
private DiffTools(Repository repo, StoredConfig config) {
|
||||||
|
this.repo = repo;
|
||||||
this.config = config.get(DiffToolConfig.KEY);
|
this.config = config.get(DiffToolConfig.KEY);
|
||||||
this.gitDir = repo == null ? null : repo.getDirectory();
|
this.gitDir = repo == null ? null : repo.getDirectory();
|
||||||
this.fs = repo == null ? FS.DETECTED : repo.getFS();
|
this.fs = repo == null ? FS.DETECTED : repo.getFS();
|
||||||
|
@ -108,15 +115,18 @@ public Optional<ExecutionResult> compare(FileElement localFile,
|
||||||
|
|
||||||
String toolNameToUse;
|
String toolNameToUse;
|
||||||
|
|
||||||
|
if (toolName == null) {
|
||||||
|
throw new ToolException(JGitText.get().diffToolNullError);
|
||||||
|
}
|
||||||
|
|
||||||
if (toolName.isPresent()) {
|
if (toolName.isPresent()) {
|
||||||
toolNameToUse = toolName.get();
|
toolNameToUse = toolName.get();
|
||||||
} else {
|
} else {
|
||||||
toolNameToUse = getDefaultToolName(gui);
|
toolNameToUse = getDefaultToolName(gui);
|
||||||
|
|
||||||
if (toolNameToUse == null || toolNameToUse.isEmpty()) {
|
|
||||||
noToolHandler.inform(new ArrayList<>(predefinedTools.keySet()));
|
|
||||||
toolNameToUse = getFirstAvailableTool();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (StringUtils.isEmptyOrNull(toolNameToUse)) {
|
||||||
|
throw new ToolException(JGitText.get().diffToolNotGivenError);
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean doPrompt;
|
boolean doPrompt;
|
||||||
|
@ -167,6 +177,10 @@ public ExecutionResult compare(FileElement localFile,
|
||||||
FileElement remoteFile, ExternalDiffTool tool,
|
FileElement remoteFile, ExternalDiffTool tool,
|
||||||
boolean trustExitCode) throws ToolException {
|
boolean trustExitCode) throws ToolException {
|
||||||
try {
|
try {
|
||||||
|
if (tool == null) {
|
||||||
|
throw new ToolException(JGitText
|
||||||
|
.get().diffToolNotSpecifiedInGitAttributesError);
|
||||||
|
}
|
||||||
// prepare the command (replace the file paths)
|
// prepare the command (replace the file paths)
|
||||||
String command = ExternalToolUtils.prepareCommand(tool.getCommand(),
|
String command = ExternalToolUtils.prepareCommand(tool.getCommand(),
|
||||||
localFile, remoteFile, null, null);
|
localFile, remoteFile, null, null);
|
||||||
|
@ -217,6 +231,42 @@ public Set<String> getAllToolNames() {
|
||||||
getUserDefinedToolNames(), getPredefinedToolNames());
|
getUserDefinedToolNames(), getPredefinedToolNames());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides {@link Optional} with the name of an external diff tool if
|
||||||
|
* specified in git configuration for a path.
|
||||||
|
*
|
||||||
|
* The formed git configuration results from global rules as well as merged
|
||||||
|
* rules from info and worktree attributes.
|
||||||
|
*
|
||||||
|
* Triggers {@link TreeWalk} until specified path found in the tree.
|
||||||
|
*
|
||||||
|
* @param path
|
||||||
|
* path to the node in repository to parse git attributes for
|
||||||
|
* @return name of the difftool if set
|
||||||
|
* @throws ToolException
|
||||||
|
*/
|
||||||
|
public Optional<String> getExternalToolFromAttributes(final String path)
|
||||||
|
throws ToolException {
|
||||||
|
return ExternalToolUtils.getExternalToolFromAttributes(repo, path,
|
||||||
|
ExternalToolUtils.KEY_DIFF_TOOL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks the availability of the predefined tools in the system.
|
||||||
|
*
|
||||||
|
* @return set of predefined available tools
|
||||||
|
*/
|
||||||
|
public Set<String> getPredefinedAvailableTools() {
|
||||||
|
Map<String, ExternalDiffTool> defTools = getPredefinedTools(true);
|
||||||
|
Set<String> availableTools = new LinkedHashSet<>();
|
||||||
|
for (Entry<String, ExternalDiffTool> elem : defTools.entrySet()) {
|
||||||
|
if (elem.getValue().isAvailable()) {
|
||||||
|
availableTools.add(elem.getKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return availableTools;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get user defined tools map.
|
* Get user defined tools map.
|
||||||
*
|
*
|
||||||
|
@ -272,8 +322,14 @@ public String getFirstAvailableTool() {
|
||||||
* @return the default tool name
|
* @return the default tool name
|
||||||
*/
|
*/
|
||||||
public String getDefaultToolName(boolean gui) {
|
public String getDefaultToolName(boolean gui) {
|
||||||
return gui ? config.getDefaultGuiToolName()
|
String guiToolName;
|
||||||
: config.getDefaultToolName();
|
if (gui) {
|
||||||
|
guiToolName = config.getDefaultGuiToolName();
|
||||||
|
if (guiToolName != null) {
|
||||||
|
return guiToolName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return config.getDefaultToolName();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -14,9 +14,17 @@
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.eclipse.jgit.attributes.Attributes;
|
||||||
|
import org.eclipse.jgit.errors.RevisionSyntaxException;
|
||||||
import org.eclipse.jgit.lib.Constants;
|
import org.eclipse.jgit.lib.Constants;
|
||||||
|
import org.eclipse.jgit.lib.Repository;
|
||||||
|
import org.eclipse.jgit.treewalk.FileTreeIterator;
|
||||||
|
import org.eclipse.jgit.treewalk.TreeWalk;
|
||||||
|
import org.eclipse.jgit.treewalk.WorkingTreeIterator;
|
||||||
|
import org.eclipse.jgit.treewalk.filter.NotIgnoredFilter;
|
||||||
import org.eclipse.jgit.util.FS;
|
import org.eclipse.jgit.util.FS;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -24,6 +32,16 @@
|
||||||
*/
|
*/
|
||||||
public class ExternalToolUtils {
|
public class ExternalToolUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Key for merge tool git configuration section
|
||||||
|
*/
|
||||||
|
public static final String KEY_MERGE_TOOL = "mergetool"; //$NON-NLS-1$
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Key for diff tool git configuration section
|
||||||
|
*/
|
||||||
|
public static final String KEY_DIFF_TOOL = "difftool"; //$NON-NLS-1$
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prepare command for execution.
|
* Prepare command for execution.
|
||||||
*
|
*
|
||||||
|
@ -174,4 +192,51 @@ public static Set<String> createSortedToolSet(String defaultName,
|
||||||
return names;
|
return names;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides {@link Optional} with the name of an external tool if specified
|
||||||
|
* in git configuration for a path.
|
||||||
|
*
|
||||||
|
* The formed git configuration results from global rules as well as merged
|
||||||
|
* rules from info and worktree attributes.
|
||||||
|
*
|
||||||
|
* Triggers {@link TreeWalk} until specified path found in the tree.
|
||||||
|
*
|
||||||
|
* @param repository
|
||||||
|
* target repository to traverse into
|
||||||
|
* @param path
|
||||||
|
* path to the node in repository to parse git attributes for
|
||||||
|
* @param toolKey
|
||||||
|
* config key name for the tool
|
||||||
|
* @return attribute value for the given tool key if set
|
||||||
|
* @throws ToolException
|
||||||
|
*/
|
||||||
|
public static Optional<String> getExternalToolFromAttributes(
|
||||||
|
final Repository repository, final String path,
|
||||||
|
final String toolKey) throws ToolException {
|
||||||
|
try {
|
||||||
|
WorkingTreeIterator treeIterator = new FileTreeIterator(repository);
|
||||||
|
try (TreeWalk walk = new TreeWalk(repository)) {
|
||||||
|
walk.addTree(treeIterator);
|
||||||
|
walk.setFilter(new NotIgnoredFilter(0));
|
||||||
|
while (walk.next()) {
|
||||||
|
String treePath = walk.getPathString();
|
||||||
|
if (treePath.equals(path)) {
|
||||||
|
Attributes attrs = walk.getAttributes();
|
||||||
|
if (attrs.containsKey(toolKey)) {
|
||||||
|
return Optional.of(attrs.getValue(toolKey));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (walk.isSubtree()) {
|
||||||
|
walk.enterSubtree();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// no external tool specified
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (RevisionSyntaxException | IOException e) {
|
||||||
|
throw new ToolException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,16 +18,21 @@
|
||||||
import java.nio.file.StandardCopyOption;
|
import java.nio.file.StandardCopyOption;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
|
|
||||||
|
import org.eclipse.jgit.internal.JGitText;
|
||||||
import org.eclipse.jgit.internal.diffmergetool.FileElement.Type;
|
import org.eclipse.jgit.internal.diffmergetool.FileElement.Type;
|
||||||
import org.eclipse.jgit.lib.Repository;
|
import org.eclipse.jgit.lib.Repository;
|
||||||
import org.eclipse.jgit.lib.StoredConfig;
|
import org.eclipse.jgit.lib.StoredConfig;
|
||||||
import org.eclipse.jgit.lib.internal.BooleanTriState;
|
import org.eclipse.jgit.lib.internal.BooleanTriState;
|
||||||
|
import org.eclipse.jgit.treewalk.TreeWalk;
|
||||||
import org.eclipse.jgit.util.FS;
|
import org.eclipse.jgit.util.FS;
|
||||||
|
import org.eclipse.jgit.util.StringUtils;
|
||||||
import org.eclipse.jgit.util.FS.ExecutionResult;
|
import org.eclipse.jgit.util.FS.ExecutionResult;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -43,6 +48,8 @@ public class MergeTools {
|
||||||
|
|
||||||
private final MergeToolConfig config;
|
private final MergeToolConfig config;
|
||||||
|
|
||||||
|
private final Repository repo;
|
||||||
|
|
||||||
private final Map<String, ExternalMergeTool> predefinedTools;
|
private final Map<String, ExternalMergeTool> predefinedTools;
|
||||||
|
|
||||||
private final Map<String, ExternalMergeTool> userDefinedTools;
|
private final Map<String, ExternalMergeTool> userDefinedTools;
|
||||||
|
@ -68,6 +75,7 @@ public MergeTools(StoredConfig config) {
|
||||||
}
|
}
|
||||||
|
|
||||||
private MergeTools(Repository repo, StoredConfig config) {
|
private MergeTools(Repository repo, StoredConfig config) {
|
||||||
|
this.repo = repo;
|
||||||
this.config = config.get(MergeToolConfig.KEY);
|
this.config = config.get(MergeToolConfig.KEY);
|
||||||
this.gitDir = repo == null ? null : repo.getDirectory();
|
this.gitDir = repo == null ? null : repo.getDirectory();
|
||||||
this.fs = repo == null ? FS.DETECTED : repo.getFS();
|
this.fs = repo == null ? FS.DETECTED : repo.getFS();
|
||||||
|
@ -116,17 +124,25 @@ public Optional<ExecutionResult> merge(FileElement localFile,
|
||||||
|
|
||||||
String toolNameToUse;
|
String toolNameToUse;
|
||||||
|
|
||||||
|
if (toolName == null) {
|
||||||
|
throw new ToolException(JGitText.get().diffToolNullError);
|
||||||
|
}
|
||||||
|
|
||||||
if (toolName.isPresent()) {
|
if (toolName.isPresent()) {
|
||||||
toolNameToUse = toolName.get();
|
toolNameToUse = toolName.get();
|
||||||
} else {
|
} else {
|
||||||
toolNameToUse = getDefaultToolName(gui);
|
toolNameToUse = getDefaultToolName(gui);
|
||||||
|
|
||||||
if (toolNameToUse == null || toolNameToUse.isEmpty()) {
|
if (StringUtils.isEmptyOrNull(toolNameToUse)) {
|
||||||
noToolHandler.inform(new ArrayList<>(predefinedTools.keySet()));
|
noToolHandler.inform(new ArrayList<>(predefinedTools.keySet()));
|
||||||
toolNameToUse = getFirstAvailableTool();
|
toolNameToUse = getFirstAvailableTool();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (StringUtils.isEmptyOrNull(toolNameToUse)) {
|
||||||
|
throw new ToolException(JGitText.get().diffToolNotGivenError);
|
||||||
|
}
|
||||||
|
|
||||||
boolean doPrompt;
|
boolean doPrompt;
|
||||||
if (prompt != BooleanTriState.UNSET) {
|
if (prompt != BooleanTriState.UNSET) {
|
||||||
doPrompt = prompt == BooleanTriState.TRUE;
|
doPrompt = prompt == BooleanTriState.TRUE;
|
||||||
|
@ -276,6 +292,42 @@ public Set<String> getAllToolNames() {
|
||||||
getUserDefinedToolNames(), getPredefinedToolNames());
|
getUserDefinedToolNames(), getPredefinedToolNames());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides {@link Optional} with the name of an external merge tool if
|
||||||
|
* specified in git configuration for a path.
|
||||||
|
*
|
||||||
|
* The formed git configuration results from global rules as well as merged
|
||||||
|
* rules from info and worktree attributes.
|
||||||
|
*
|
||||||
|
* Triggers {@link TreeWalk} until specified path found in the tree.
|
||||||
|
*
|
||||||
|
* @param path
|
||||||
|
* path to the node in repository to parse git attributes for
|
||||||
|
* @return name of the difftool if set
|
||||||
|
* @throws ToolException
|
||||||
|
*/
|
||||||
|
public Optional<String> getExternalToolFromAttributes(final String path)
|
||||||
|
throws ToolException {
|
||||||
|
return ExternalToolUtils.getExternalToolFromAttributes(repo, path,
|
||||||
|
ExternalToolUtils.KEY_MERGE_TOOL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks the availability of the predefined tools in the system.
|
||||||
|
*
|
||||||
|
* @return set of predefined available tools
|
||||||
|
*/
|
||||||
|
public Set<String> getPredefinedAvailableTools() {
|
||||||
|
Map<String, ExternalMergeTool> defTools = getPredefinedTools(true);
|
||||||
|
Set<String> availableTools = new LinkedHashSet<>();
|
||||||
|
for (Entry<String, ExternalMergeTool> elem : defTools.entrySet()) {
|
||||||
|
if (elem.getValue().isAvailable()) {
|
||||||
|
availableTools.add(elem.getKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return availableTools;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the user defined tools
|
* @return the user defined tools
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Reference in New Issue