Add config reader for user-defined difftools
see: http://git-scm.com/docs/git-difftool * add config reader for user-defined difftools * diff.tool * diff.guitool * difftool.prompt * difftool.trustExitCode * difftool.<tool>.path * difftool.<tool>.cmd * add pre-defined difftools * implemented "git difftool --tool-help" to verify config reader and pre-defined difftools Bug: 356832 Change-Id: Idde8fddbef61f3378ee565c6321570b3962d0e1d Signed-off-by: Andre Bossert <andre.bossert@siemens.com> Signed-off-by: Simeon Andreev <simeon.danailov.andreev@gmail.com>
This commit is contained in:
parent
caea5a26f0
commit
14a59bdc7b
|
@ -12,6 +12,7 @@ Import-Package: org.eclipse.jgit.api;version="[6.1.0,6.2.0)",
|
||||||
org.eclipse.jgit.api.errors;version="[6.1.0,6.2.0)",
|
org.eclipse.jgit.api.errors;version="[6.1.0,6.2.0)",
|
||||||
org.eclipse.jgit.diff;version="[6.1.0,6.2.0)",
|
org.eclipse.jgit.diff;version="[6.1.0,6.2.0)",
|
||||||
org.eclipse.jgit.dircache;version="[6.1.0,6.2.0)",
|
org.eclipse.jgit.dircache;version="[6.1.0,6.2.0)",
|
||||||
|
org.eclipse.jgit.internal.diffmergetool;version="6.1.0",
|
||||||
org.eclipse.jgit.internal.storage.file;version="6.1.0",
|
org.eclipse.jgit.internal.storage.file;version="6.1.0",
|
||||||
org.eclipse.jgit.junit;version="[6.1.0,6.2.0)",
|
org.eclipse.jgit.junit;version="[6.1.0,6.2.0)",
|
||||||
org.eclipse.jgit.lib;version="[6.1.0,6.2.0)",
|
org.eclipse.jgit.lib;version="[6.1.0,6.2.0)",
|
||||||
|
|
|
@ -12,10 +12,12 @@
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.eclipse.jgit.api.Git;
|
import org.eclipse.jgit.api.Git;
|
||||||
import org.eclipse.jgit.diff.DiffEntry;
|
import org.eclipse.jgit.diff.DiffEntry;
|
||||||
|
import org.eclipse.jgit.internal.diffmergetool.CommandLineDiffTool;
|
||||||
import org.eclipse.jgit.lib.CLIRepositoryTestCase;
|
import org.eclipse.jgit.lib.CLIRepositoryTestCase;
|
||||||
import org.eclipse.jgit.pgm.opt.CmdLineParser;
|
import org.eclipse.jgit.pgm.opt.CmdLineParser;
|
||||||
import org.eclipse.jgit.pgm.opt.SubcommandHandler;
|
import org.eclipse.jgit.pgm.opt.SubcommandHandler;
|
||||||
|
@ -135,16 +137,24 @@ expectedOutput, runAndCaptureUsingInitRaw("difftool",
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testToolHelp() throws Exception {
|
public void testToolHelp() throws Exception {
|
||||||
String[] expectedOutput = {
|
CommandLineDiffTool[] defaultTools = CommandLineDiffTool.values();
|
||||||
"git difftool --tool=<tool> may be set to one of the following:",
|
List<String> expectedOutput = new ArrayList<>();
|
||||||
|
expectedOutput.add("git difftool --tool=<tool> may be set to one of the following:");
|
||||||
|
for (CommandLineDiffTool defaultTool : defaultTools) {
|
||||||
|
String toolName = defaultTool.name();
|
||||||
|
expectedOutput.add(toolName);
|
||||||
|
}
|
||||||
|
String[] userDefinedToolsHelp = {
|
||||||
"user-defined:",
|
"user-defined:",
|
||||||
"The following tools are valid, but not currently available:",
|
"The following tools are valid, but not currently available:",
|
||||||
"Some of the tools listed above only work in a windowed",
|
"Some of the tools listed above only work in a windowed",
|
||||||
"environment. If run in a terminal-only session, they will fail.", };
|
"environment. If run in a terminal-only session, they will fail.",
|
||||||
|
};
|
||||||
|
expectedOutput.addAll(Arrays.asList(userDefinedToolsHelp));
|
||||||
|
|
||||||
String option = "--tool-help";
|
String option = "--tool-help";
|
||||||
assertArrayOfLinesEquals("Incorrect output for option: " + option,
|
assertArrayOfLinesEquals("Incorrect output for option: " + option,
|
||||||
expectedOutput, runAndCaptureUsingInitRaw("difftool", option));
|
expectedOutput.toArray(new String[0]), runAndCaptureUsingInitRaw("difftool", option));
|
||||||
}
|
}
|
||||||
|
|
||||||
private RevCommit createUnstagedChanges() throws Exception {
|
private RevCommit createUnstagedChanges() throws Exception {
|
||||||
|
|
|
@ -199,20 +199,20 @@ private boolean isLaunchCompare(int fileIndex, int fileCount,
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showToolHelp() throws IOException {
|
private void showToolHelp() throws IOException {
|
||||||
String availableToolNames = new String();
|
StringBuilder availableToolNames = new StringBuilder();
|
||||||
for (String name : diffTools.getAvailableTools().keySet()) {
|
for (String name : diffTools.getAvailableTools().keySet()) {
|
||||||
availableToolNames += String.format("\t\t{0}\n", name); //$NON-NLS-1$
|
availableToolNames.append(String.format("\t\t%s\n", name)); //$NON-NLS-1$
|
||||||
}
|
}
|
||||||
String notAvailableToolNames = new String();
|
StringBuilder notAvailableToolNames = new StringBuilder();
|
||||||
for (String name : diffTools.getNotAvailableTools().keySet()) {
|
for (String name : diffTools.getNotAvailableTools().keySet()) {
|
||||||
notAvailableToolNames += String.format("\t\t{0}\n", name); //$NON-NLS-1$
|
notAvailableToolNames.append(String.format("\t\t%s\n", name)); //$NON-NLS-1$
|
||||||
}
|
}
|
||||||
String userToolNames = new String();
|
StringBuilder userToolNames = new StringBuilder();
|
||||||
Map<String, ExternalDiffTool> userTools = diffTools
|
Map<String, ExternalDiffTool> userTools = diffTools
|
||||||
.getUserDefinedTools();
|
.getUserDefinedTools();
|
||||||
for (String name : userTools.keySet()) {
|
for (String name : userTools.keySet()) {
|
||||||
availableToolNames += String.format("\t\t{0}.cmd {1}\n", //$NON-NLS-1$
|
userToolNames.append(String.format("\t\t%s.cmd %s\n", //$NON-NLS-1$
|
||||||
name, userTools.get(name).getCommand());
|
name, userTools.get(name).getCommand()));
|
||||||
}
|
}
|
||||||
outw.println(MessageFormat.format(
|
outw.println(MessageFormat.format(
|
||||||
CLIText.get().diffToolHelpSetToFollowing, availableToolNames,
|
CLIText.get().diffToolHelpSetToFollowing, availableToolNames,
|
||||||
|
|
|
@ -9,9 +9,18 @@
|
||||||
*/
|
*/
|
||||||
package org.eclipse.jgit.internal.diffmergetool;
|
package org.eclipse.jgit.internal.diffmergetool;
|
||||||
|
|
||||||
|
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_DIFFTOOL_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_TRUST_EXIT_CODE;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.eclipse.jgit.lib.internal.BooleanTriState;
|
import org.eclipse.jgit.lib.internal.BooleanTriState;
|
||||||
|
@ -29,27 +38,64 @@ public void testToolNames() {
|
||||||
Set<String> actualToolNames = manager.getToolNames();
|
Set<String> actualToolNames = manager.getToolNames();
|
||||||
Set<String> expectedToolNames = Collections.emptySet();
|
Set<String> expectedToolNames = Collections.emptySet();
|
||||||
assertEquals("Incorrect set of external diff tool names",
|
assertEquals("Incorrect set of external diff tool names",
|
||||||
expectedToolNames,
|
expectedToolNames, actualToolNames);
|
||||||
actualToolNames);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAllTools() {
|
public void testAllTools() {
|
||||||
DiffTools manager = new DiffTools(db);
|
DiffTools manager = new DiffTools(db);
|
||||||
Set<String> actualToolNames = manager.getAvailableTools().keySet();
|
Set<String> actualToolNames = manager.getAvailableTools().keySet();
|
||||||
Set<String> expectedToolNames = Collections.emptySet();
|
Set<String> expectedToolNames = new LinkedHashSet<>();
|
||||||
assertEquals("Incorrect set of available external diff tools",
|
CommandLineDiffTool[] defaultTools = CommandLineDiffTool.values();
|
||||||
expectedToolNames,
|
for (CommandLineDiffTool defaultTool : defaultTools) {
|
||||||
|
String toolName = defaultTool.name();
|
||||||
|
expectedToolNames.add(toolName);
|
||||||
|
}
|
||||||
|
assertEquals("Incorrect set of external diff tools", expectedToolNames,
|
||||||
actualToolNames);
|
actualToolNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOverridePredefinedToolPath() {
|
||||||
|
String toolName = CommandLineDiffTool.guiffy.name();
|
||||||
|
String customToolPath = "/usr/bin/echo";
|
||||||
|
|
||||||
|
FileBasedConfig config = db.getConfig();
|
||||||
|
config.setString(CONFIG_DIFFTOOL_SECTION, toolName, CONFIG_KEY_CMD,
|
||||||
|
"echo");
|
||||||
|
config.setString(CONFIG_DIFFTOOL_SECTION, toolName, CONFIG_KEY_PATH,
|
||||||
|
customToolPath);
|
||||||
|
|
||||||
|
DiffTools manager = new DiffTools(db);
|
||||||
|
Map<String, ExternalDiffTool> tools = manager.getUserDefinedTools();
|
||||||
|
ExternalDiffTool diffTool = tools.get(toolName);
|
||||||
|
assertNotNull("Expected tool \"" + toolName + "\" to be user defined",
|
||||||
|
diffTool);
|
||||||
|
|
||||||
|
String toolPath = diffTool.getPath();
|
||||||
|
assertEquals("Expected external diff tool to have an overriden path",
|
||||||
|
customToolPath, toolPath);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUserDefinedTools() {
|
public void testUserDefinedTools() {
|
||||||
|
FileBasedConfig config = db.getConfig();
|
||||||
|
String customToolname = "customTool";
|
||||||
|
config.setString(CONFIG_DIFFTOOL_SECTION, customToolname,
|
||||||
|
CONFIG_KEY_CMD, "echo");
|
||||||
|
config.setString(CONFIG_DIFFTOOL_SECTION, customToolname,
|
||||||
|
CONFIG_KEY_PATH, "/usr/bin/echo");
|
||||||
|
config.setString(CONFIG_DIFFTOOL_SECTION, customToolname,
|
||||||
|
CONFIG_KEY_PROMPT, "--no-prompt");
|
||||||
|
config.setString(CONFIG_DIFFTOOL_SECTION, customToolname,
|
||||||
|
CONFIG_KEY_GUITOOL, "--no-gui");
|
||||||
|
config.setString(CONFIG_DIFFTOOL_SECTION, customToolname,
|
||||||
|
CONFIG_KEY_TRUST_EXIT_CODE, "--no-trust-exit-code");
|
||||||
DiffTools manager = new DiffTools(db);
|
DiffTools manager = new DiffTools(db);
|
||||||
Set<String> actualToolNames = manager.getUserDefinedTools().keySet();
|
Set<String> actualToolNames = manager.getUserDefinedTools().keySet();
|
||||||
Set<String> expectedToolNames = Collections.emptySet();
|
Set<String> expectedToolNames = new LinkedHashSet<>();
|
||||||
assertEquals("Incorrect set of user defined external diff tools",
|
expectedToolNames.add(customToolname);
|
||||||
expectedToolNames,
|
assertEquals("Incorrect set of external diff tools", expectedToolNames,
|
||||||
actualToolNames);
|
actualToolNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,8 +105,7 @@ public void testNotAvailableTools() {
|
||||||
Set<String> actualToolNames = manager.getNotAvailableTools().keySet();
|
Set<String> actualToolNames = manager.getNotAvailableTools().keySet();
|
||||||
Set<String> expectedToolNames = Collections.emptySet();
|
Set<String> expectedToolNames = Collections.emptySet();
|
||||||
assertEquals("Incorrect set of not available external diff tools",
|
assertEquals("Incorrect set of not available external diff tools",
|
||||||
expectedToolNames,
|
expectedToolNames, actualToolNames);
|
||||||
actualToolNames);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -80,8 +125,7 @@ public void testCompare() {
|
||||||
int compareResult = manager.compare(newPath, oldPath, newId, oldId,
|
int compareResult = manager.compare(newPath, oldPath, newId, oldId,
|
||||||
toolName, prompt, gui, trustExitCode);
|
toolName, prompt, gui, trustExitCode);
|
||||||
assertEquals("Incorrect compare result for external diff tool",
|
assertEquals("Incorrect compare result for external diff tool",
|
||||||
expectedCompareResult,
|
expectedCompareResult, compareResult);
|
||||||
compareResult);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -72,6 +72,7 @@ Export-Package: org.eclipse.jgit.annotations;version="6.1.0",
|
||||||
org.eclipse.jgit.http.test",
|
org.eclipse.jgit.http.test",
|
||||||
org.eclipse.jgit.internal.diffmergetool;version="6.1.0";
|
org.eclipse.jgit.internal.diffmergetool;version="6.1.0";
|
||||||
x-friends:="org.eclipse.jgit.test,
|
x-friends:="org.eclipse.jgit.test,
|
||||||
|
org.eclipse.jgit.pgm.test,
|
||||||
org.eclipse.jgit.pgm",
|
org.eclipse.jgit.pgm",
|
||||||
org.eclipse.jgit.internal.fsck;version="6.1.0";
|
org.eclipse.jgit.internal.fsck;version="6.1.0";
|
||||||
x-friends:="org.eclipse.jgit.test",
|
x-friends:="org.eclipse.jgit.test",
|
||||||
|
|
|
@ -0,0 +1,221 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2018-2021, 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 command line diff tools.
|
||||||
|
*
|
||||||
|
* Adds same diff tools as also pre-defined in C-Git
|
||||||
|
* <p>
|
||||||
|
* see "git-core\mergetools\"
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* see links to command line parameter description for the tools
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* araxis
|
||||||
|
* bc
|
||||||
|
* bc3
|
||||||
|
* codecompare
|
||||||
|
* deltawalker
|
||||||
|
* diffmerge
|
||||||
|
* diffuse
|
||||||
|
* ecmerge
|
||||||
|
* emerge
|
||||||
|
* examdiff
|
||||||
|
* guiffy
|
||||||
|
* gvimdiff
|
||||||
|
* gvimdiff2
|
||||||
|
* gvimdiff3
|
||||||
|
* kdiff3
|
||||||
|
* kompare
|
||||||
|
* meld
|
||||||
|
* opendiff
|
||||||
|
* p4merge
|
||||||
|
* tkdiff
|
||||||
|
* vimdiff
|
||||||
|
* vimdiff2
|
||||||
|
* vimdiff3
|
||||||
|
* winmerge
|
||||||
|
* xxdiff
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("nls")
|
||||||
|
public enum CommandLineDiffTool {
|
||||||
|
/**
|
||||||
|
* 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 -2 \"$LOCAL\" \"$REMOTE\""),
|
||||||
|
/**
|
||||||
|
* 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\""),
|
||||||
|
/**
|
||||||
|
* 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?comparing_via_command_line.htm">https://www.devart.com/codecompare/docs/index.html?comparing_via_command_line.htm</a>
|
||||||
|
*/
|
||||||
|
codecompare("CodeCompare", "\"$LOCAL\" \"$REMOTE\""),
|
||||||
|
/**
|
||||||
|
* See: <a href=
|
||||||
|
* "https://www.deltawalker.com/integrate/command-line">https://www.deltawalker.com/integrate/command-line</a>
|
||||||
|
*/
|
||||||
|
deltawalker("DeltaWalker", "\"$LOCAL\" \"$REMOTE\""),
|
||||||
|
/**
|
||||||
|
* See: <a href=
|
||||||
|
* "https://sourcegear.com/diffmerge/webhelp/sec__clargs__diff.html">https://sourcegear.com/diffmerge/webhelp/sec__clargs__diff.html</a>
|
||||||
|
*/
|
||||||
|
diffmerge("diffmerge", "\"$LOCAL\" \"$REMOTE\""),
|
||||||
|
/**
|
||||||
|
* See: <a href=
|
||||||
|
* "http://diffuse.sourceforge.net/manual.html#introduction-usage">http://diffuse.sourceforge.net/manual.html#introduction-usage</a>
|
||||||
|
*/
|
||||||
|
diffuse("diffuse", "\"$LOCAL\" \"$REMOTE\""),
|
||||||
|
/**
|
||||||
|
* 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=diff2 \"$LOCAL\" \"$REMOTE\""),
|
||||||
|
/**
|
||||||
|
* 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>
|
||||||
|
*/
|
||||||
|
emerge("emacs", "-f emerge-files-command \"$LOCAL\" \"$REMOTE\""),
|
||||||
|
/**
|
||||||
|
* 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", "\"$LOCAL\" \"$REMOTE\" -nh"),
|
||||||
|
/**
|
||||||
|
* See: <a href=
|
||||||
|
* "https://www.guiffy.com/help/GuiffyHelp/GuiffyCmd.html">https://www.guiffy.com/help/GuiffyHelp/GuiffyCmd.html</a>
|
||||||
|
*/
|
||||||
|
guiffy("guiffy", "\"$LOCAL\" \"$REMOTE\""),
|
||||||
|
/**
|
||||||
|
* See: <a href=
|
||||||
|
* "http://vimdoc.sourceforge.net/htmldoc/diff.html">http://vimdoc.sourceforge.net/htmldoc/diff.html</a>
|
||||||
|
*/
|
||||||
|
gvimdiff("gviewdiff", "\"$LOCAL\" \"$REMOTE\""),
|
||||||
|
/**
|
||||||
|
* See: <a href=
|
||||||
|
* "http://vimdoc.sourceforge.net/htmldoc/diff.html">http://vimdoc.sourceforge.net/htmldoc/diff.html</a>
|
||||||
|
*/
|
||||||
|
gvimdiff2(gvimdiff),
|
||||||
|
/**
|
||||||
|
* See: <a href=
|
||||||
|
* "http://vimdoc.sourceforge.net/htmldoc/diff.html">http://vimdoc.sourceforge.net/htmldoc/diff.html</a>
|
||||||
|
*/
|
||||||
|
gvimdiff3(gvimdiff),
|
||||||
|
/**
|
||||||
|
* See: <a href=
|
||||||
|
* "http://kdiff3.sourceforge.net/doc/documentation.html">http://kdiff3.sourceforge.net/doc/documentation.html</a>
|
||||||
|
*/
|
||||||
|
kdiff3("kdiff3",
|
||||||
|
"--L1 \"$MERGED (A)\" --L2 \"$MERGED (B)\" \"$LOCAL\" \"$REMOTE\""),
|
||||||
|
/**
|
||||||
|
* See: <a href=
|
||||||
|
* "https://docs.kde.org/trunk5/en/kdesdk/kompare/commandline-options.html">https://docs.kde.org/trunk5/en/kdesdk/kompare/commandline-options.html</a>
|
||||||
|
*/
|
||||||
|
kompare("kompare", "\"$LOCAL\" \"$REMOTE\""),
|
||||||
|
/**
|
||||||
|
* See: <a href=
|
||||||
|
* "ttp://meldmerge.org/help/file-mode.html">http://meldmerge.org/help/file-mode.html</a>
|
||||||
|
*/
|
||||||
|
meld("meld", "\"$LOCAL\" \"$REMOTE\""),
|
||||||
|
/**
|
||||||
|
* 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\""),
|
||||||
|
/**
|
||||||
|
* 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>
|
||||||
|
*/
|
||||||
|
p4merge("p4merge", "\"$LOCAL\" \"$REMOTE\""),
|
||||||
|
/**
|
||||||
|
* 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", "\"$LOCAL\" \"$REMOTE\""),
|
||||||
|
/**
|
||||||
|
* See: <a href=
|
||||||
|
* "http://vimdoc.sourceforge.net/htmldoc/diff.html">http://vimdoc.sourceforge.net/htmldoc/diff.html</a>
|
||||||
|
*/
|
||||||
|
vimdiff("viewdiff", gvimdiff),
|
||||||
|
/**
|
||||||
|
* See: <a href=
|
||||||
|
* "http://vimdoc.sourceforge.net/htmldoc/diff.html">http://vimdoc.sourceforge.net/htmldoc/diff.html</a>
|
||||||
|
*/
|
||||||
|
vimdiff2(vimdiff),
|
||||||
|
/**
|
||||||
|
* See: <a href=
|
||||||
|
* "http://vimdoc.sourceforge.net/htmldoc/diff.html">http://vimdoc.sourceforge.net/htmldoc/diff.html</a>
|
||||||
|
*/
|
||||||
|
vimdiff3(vimdiff),
|
||||||
|
/**
|
||||||
|
* 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 \"$LOCAL\" \"$REMOTE\""),
|
||||||
|
/**
|
||||||
|
* See: <a href=
|
||||||
|
* "http://furius.ca/xxdiff/doc/xxdiff-doc.html">http://furius.ca/xxdiff/doc/xxdiff-doc.html</a>
|
||||||
|
*/
|
||||||
|
xxdiff("xxdiff",
|
||||||
|
"-R 'Accel.Search: \"Ctrl+F\"' -R 'Accel.SearchForward: \"Ctrl+G\"' \"$LOCAL\" \"$REMOTE\"");
|
||||||
|
|
||||||
|
CommandLineDiffTool(String path, String parameters) {
|
||||||
|
this.path = path;
|
||||||
|
this.parameters = parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
CommandLineDiffTool(CommandLineDiffTool from) {
|
||||||
|
this(from.getPath(), from.getParameters());
|
||||||
|
}
|
||||||
|
|
||||||
|
CommandLineDiffTool(String path, CommandLineDiffTool from) {
|
||||||
|
this(path, from.getParameters());
|
||||||
|
}
|
||||||
|
|
||||||
|
private final String path;
|
||||||
|
|
||||||
|
private final String parameters;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return path
|
||||||
|
*/
|
||||||
|
public String getPath() {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return parameters as one string
|
||||||
|
*/
|
||||||
|
public String getParameters() {
|
||||||
|
return parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,117 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2018-2021, 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 static org.eclipse.jgit.lib.ConfigConstants.CONFIG_DIFFTOOL_SECTION;
|
||||||
|
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_DIFF_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 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.internal.BooleanTriState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keeps track of difftool related configuration options.
|
||||||
|
*/
|
||||||
|
public class DiffToolConfig {
|
||||||
|
|
||||||
|
/** Key for {@link Config#get(SectionParser)}. */
|
||||||
|
public static final Config.SectionParser<DiffToolConfig> KEY = DiffToolConfig::new;
|
||||||
|
|
||||||
|
private final String toolName;
|
||||||
|
|
||||||
|
private final String guiToolName;
|
||||||
|
|
||||||
|
private final boolean prompt;
|
||||||
|
|
||||||
|
private final BooleanTriState trustExitCode;
|
||||||
|
|
||||||
|
private final Map<String, ExternalDiffTool> tools;
|
||||||
|
|
||||||
|
private DiffToolConfig(Config rc) {
|
||||||
|
toolName = rc.getString(CONFIG_DIFF_SECTION, null, CONFIG_KEY_TOOL);
|
||||||
|
guiToolName = rc.getString(CONFIG_DIFF_SECTION, null,
|
||||||
|
CONFIG_KEY_GUITOOL);
|
||||||
|
prompt = rc.getBoolean(CONFIG_DIFFTOOL_SECTION, CONFIG_KEY_PROMPT,
|
||||||
|
true);
|
||||||
|
String trustStr = rc.getString(CONFIG_DIFFTOOL_SECTION, null,
|
||||||
|
CONFIG_KEY_TRUST_EXIT_CODE);
|
||||||
|
if (trustStr != null) {
|
||||||
|
trustExitCode = Boolean.parseBoolean(trustStr)
|
||||||
|
? BooleanTriState.TRUE
|
||||||
|
: BooleanTriState.FALSE;
|
||||||
|
} else {
|
||||||
|
trustExitCode = BooleanTriState.UNSET;
|
||||||
|
}
|
||||||
|
tools = new HashMap<>();
|
||||||
|
Set<String> subsections = rc.getSubsections(CONFIG_DIFFTOOL_SECTION);
|
||||||
|
for (String name : subsections) {
|
||||||
|
String cmd = rc.getString(CONFIG_DIFFTOOL_SECTION, name,
|
||||||
|
CONFIG_KEY_CMD);
|
||||||
|
String path = rc.getString(CONFIG_DIFFTOOL_SECTION, name,
|
||||||
|
CONFIG_KEY_PATH);
|
||||||
|
if ((cmd != null) || (path != null)) {
|
||||||
|
tools.put(name, new UserDefinedDiffTool(name, path, cmd));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the default diff tool name (diff.tool)
|
||||||
|
*/
|
||||||
|
public String getDefaultToolName() {
|
||||||
|
return toolName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the default GUI diff tool name (diff.guitool)
|
||||||
|
*/
|
||||||
|
public String getDefaultGuiToolName() {
|
||||||
|
return guiToolName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the diff tool "prompt" option (difftool.prompt)
|
||||||
|
*/
|
||||||
|
public boolean isPrompt() {
|
||||||
|
return prompt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the diff tool "trust exit code" option (difftool.trustExitCode)
|
||||||
|
*/
|
||||||
|
public boolean isTrustExitCode() {
|
||||||
|
return trustExitCode == BooleanTriState.TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the tools map
|
||||||
|
*/
|
||||||
|
public Map<String, ExternalDiffTool> getTools() {
|
||||||
|
return tools;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the tool names
|
||||||
|
*/
|
||||||
|
public Set<String> getToolNames() {
|
||||||
|
return tools.keySet();
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,6 +23,8 @@
|
||||||
*/
|
*/
|
||||||
public class DiffTools {
|
public class DiffTools {
|
||||||
|
|
||||||
|
private final DiffToolConfig config;
|
||||||
|
|
||||||
private Map<String, ExternalDiffTool> predefinedTools;
|
private Map<String, ExternalDiffTool> predefinedTools;
|
||||||
|
|
||||||
private Map<String, ExternalDiffTool> userDefinedTools;
|
private Map<String, ExternalDiffTool> userDefinedTools;
|
||||||
|
@ -31,9 +33,10 @@ public class DiffTools {
|
||||||
* Creates the external diff-tools manager for given repository.
|
* Creates the external diff-tools manager for given repository.
|
||||||
*
|
*
|
||||||
* @param repo
|
* @param repo
|
||||||
* the repository database
|
* the repository
|
||||||
*/
|
*/
|
||||||
public DiffTools(Repository repo) {
|
public DiffTools(Repository repo) {
|
||||||
|
config = repo.getConfig().get(DiffToolConfig.KEY);
|
||||||
setupPredefinedTools();
|
setupPredefinedTools();
|
||||||
setupUserDefinedTools();
|
setupUserDefinedTools();
|
||||||
}
|
}
|
||||||
|
@ -69,7 +72,7 @@ public int compare(String newPath, String oldPath, String newId,
|
||||||
* @return the tool names
|
* @return the tool names
|
||||||
*/
|
*/
|
||||||
public Set<String> getToolNames() {
|
public Set<String> getToolNames() {
|
||||||
return Collections.emptySet();
|
return config.getToolNames();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -112,10 +115,29 @@ public boolean isInteractive() {
|
||||||
|
|
||||||
private void setupPredefinedTools() {
|
private void setupPredefinedTools() {
|
||||||
predefinedTools = new TreeMap<>();
|
predefinedTools = new TreeMap<>();
|
||||||
|
for (CommandLineDiffTool tool : CommandLineDiffTool.values()) {
|
||||||
|
predefinedTools.put(tool.name(), new PreDefinedDiffTool(tool));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupUserDefinedTools() {
|
private void setupUserDefinedTools() {
|
||||||
userDefinedTools = new TreeMap<>();
|
userDefinedTools = new TreeMap<>();
|
||||||
|
Map<String, ExternalDiffTool> userTools = config.getTools();
|
||||||
|
for (String name : userTools.keySet()) {
|
||||||
|
ExternalDiffTool userTool = userTools.get(name);
|
||||||
|
// if difftool.<name>.cmd is defined we have user defined tool
|
||||||
|
if (userTool.getCommand() != null) {
|
||||||
|
userDefinedTools.put(name, userTool);
|
||||||
|
} else if (userTool.getPath() != null) {
|
||||||
|
// if difftool.<name>.path is defined we just overload the path
|
||||||
|
// of predefined tool
|
||||||
|
PreDefinedDiffTool predefTool = (PreDefinedDiffTool) predefinedTools
|
||||||
|
.get(name);
|
||||||
|
if (predefTool != null) {
|
||||||
|
predefTool.setPath(userTool.getPath());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2018-2021, 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The pre-defined diff tool.
|
||||||
|
*/
|
||||||
|
public class PreDefinedDiffTool extends UserDefinedDiffTool {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a pre-defined diff tool
|
||||||
|
*
|
||||||
|
* @param name
|
||||||
|
* the name
|
||||||
|
* @param path
|
||||||
|
* the path
|
||||||
|
* @param parameters
|
||||||
|
* the tool parameters as one string that is used together with
|
||||||
|
* path as command
|
||||||
|
*/
|
||||||
|
public PreDefinedDiffTool(String name, String path, String parameters) {
|
||||||
|
super(name, path, parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the pre-defined diff tool
|
||||||
|
*
|
||||||
|
* @param tool
|
||||||
|
* the command line diff tool
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public PreDefinedDiffTool(CommandLineDiffTool tool) {
|
||||||
|
this(tool.name(), tool.getPath(), tool.getParameters());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param path
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setPath(String path) {
|
||||||
|
// handling of spaces in path
|
||||||
|
if (path.contains(" ")) { //$NON-NLS-1$
|
||||||
|
// add quotes before if needed
|
||||||
|
if (!path.startsWith("\"")) { //$NON-NLS-1$
|
||||||
|
path = "\"" + path; //$NON-NLS-1$
|
||||||
|
}
|
||||||
|
// add quotes after if needed
|
||||||
|
if (!path.endsWith("\"")) { //$NON-NLS-1$
|
||||||
|
path = path + "\""; //$NON-NLS-1$
|
||||||
|
}
|
||||||
|
}
|
||||||
|
super.setPath(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*
|
||||||
|
* @return the concatenated path and command of the pre-defined diff tool
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getCommand() {
|
||||||
|
return getPath() + " " + super.getCommand(); //$NON-NLS-1$
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,114 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2018-2021, 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The user-defined diff tool.
|
||||||
|
*/
|
||||||
|
public class UserDefinedDiffTool implements ExternalDiffTool {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the diff tool name
|
||||||
|
*/
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the diff tool path
|
||||||
|
*/
|
||||||
|
private String path;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the diff tool command
|
||||||
|
*/
|
||||||
|
private final String cmd;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the diff tool
|
||||||
|
*
|
||||||
|
* @param name
|
||||||
|
* the name
|
||||||
|
* @param path
|
||||||
|
* the path
|
||||||
|
* @param cmd
|
||||||
|
* the command
|
||||||
|
*/
|
||||||
|
public UserDefinedDiffTool(String name, String path, String cmd) {
|
||||||
|
this.name = name;
|
||||||
|
this.path = path;
|
||||||
|
this.cmd = cmd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the diff tool name
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The path of the diff tool.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* The path to a pre-defined external diff tool can be overridden by
|
||||||
|
* specifying {@code difftool.<tool>.path} in a configuration file.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* For a user defined diff tool (that does not override a pre-defined diff
|
||||||
|
* tool), the path is ignored when invoking the tool.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @return the diff tool path
|
||||||
|
*
|
||||||
|
* @see <a href=
|
||||||
|
* "https://git-scm.com/docs/git-difftool">https://git-scm.com/docs/git-difftool</a>
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getPath() {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The command of the diff tool.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* A pre-defined external diff tool can be overridden using the tools name
|
||||||
|
* in a configuration file. The overwritten tool is then a user defined tool
|
||||||
|
* and the command of the diff tool is specified with
|
||||||
|
* {@code difftool.<tool>.cmd}. This command must work without prepending
|
||||||
|
* the value of {@link #getPath()} and can sometimes include tool
|
||||||
|
* parameters.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @return the diff tool command
|
||||||
|
*
|
||||||
|
* @see <a href=
|
||||||
|
* "https://git-scm.com/docs/git-difftool">https://git-scm.com/docs/git-difftool</a>
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getCommand() {
|
||||||
|
return cmd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overrides the path for the given tool. Equivalent to setting
|
||||||
|
* {@code difftool.<tool>.path}.
|
||||||
|
*
|
||||||
|
* @param path
|
||||||
|
* the new diff tool path
|
||||||
|
*
|
||||||
|
* @see <a href=
|
||||||
|
* "https://git-scm.com/docs/git-difftool">https://git-scm.com/docs/git-difftool</a>
|
||||||
|
*/
|
||||||
|
public void setPath(String path) {
|
||||||
|
this.path = path;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,8 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2010, Mathias Kinzler <mathias.kinzler@sap.com>
|
* Copyright (C) 2010, Mathias Kinzler <mathias.kinzler@sap.com>
|
||||||
* Copyright (C) 2010, Chris Aniszczyk <caniszczyk@gmail.com>
|
* Copyright (C) 2010, Chris Aniszczyk <caniszczyk@gmail.com>
|
||||||
* Copyright (C) 2012, 2020, Robin Rosenberg and others
|
* Copyright (C) 2012-2013, Robin Rosenberg
|
||||||
|
* Copyright (C) 2018-2021, Andre Bossert <andre.bossert@siemens.com> and others
|
||||||
*
|
*
|
||||||
* This program and the accompanying materials are made available under the
|
* This program and the accompanying materials are made available under the
|
||||||
* terms of the Eclipse Distribution License v. 1.0 which is available at
|
* terms of the Eclipse Distribution License v. 1.0 which is available at
|
||||||
|
@ -29,6 +30,48 @@ public final class ConfigConstants {
|
||||||
/** The "diff" section */
|
/** The "diff" section */
|
||||||
public static final String CONFIG_DIFF_SECTION = "diff";
|
public static final String CONFIG_DIFF_SECTION = "diff";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The "tool" key within "diff" section
|
||||||
|
*
|
||||||
|
* @since 6.1
|
||||||
|
*/
|
||||||
|
public static final String CONFIG_KEY_TOOL = "tool";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The "guitool" key within "diff" section
|
||||||
|
*
|
||||||
|
* @since 6.1
|
||||||
|
*/
|
||||||
|
public static final String CONFIG_KEY_GUITOOL = "guitool";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The "difftool" section
|
||||||
|
*
|
||||||
|
* @since 6.1
|
||||||
|
*/
|
||||||
|
public static final String CONFIG_DIFFTOOL_SECTION = "difftool";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The "prompt" key within "difftool" section
|
||||||
|
*
|
||||||
|
* @since 6.1
|
||||||
|
*/
|
||||||
|
public static final String CONFIG_KEY_PROMPT = "prompt";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The "trustExitCode" key within "difftool" section
|
||||||
|
*
|
||||||
|
* @since 6.1
|
||||||
|
*/
|
||||||
|
public static final String CONFIG_KEY_TRUST_EXIT_CODE = "trustExitCode";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The "cmd" key within "difftool.*." section
|
||||||
|
*
|
||||||
|
* @since 6.1
|
||||||
|
*/
|
||||||
|
public static final String CONFIG_KEY_CMD = "cmd";
|
||||||
|
|
||||||
/** The "dfs" section */
|
/** The "dfs" section */
|
||||||
public static final String CONFIG_DFS_SECTION = "dfs";
|
public static final String CONFIG_DFS_SECTION = "dfs";
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue