DiffTools.java
- /*
- * Copyright (C) 2018-2022, Andre Bossert <andre.bossert@siemens.com>
- * Copyright (C) 2019, Tim Neumann <tim.neumann@advantest.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 java.io.File;
- import java.io.IOException;
- import java.util.Collections;
- import java.util.LinkedHashSet;
- import java.util.Map;
- import java.util.Map.Entry;
- import java.util.Optional;
- import java.util.Set;
- import java.util.TreeMap;
- import org.eclipse.jgit.internal.JGitText;
- import org.eclipse.jgit.lib.Repository;
- import org.eclipse.jgit.lib.StoredConfig;
- 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.ExecutionResult;
- import org.eclipse.jgit.util.StringUtils;
- /**
- * Manages diff tools.
- */
- public class DiffTools {
- private final FS fs;
- private final File gitDir;
- private final File workTree;
- private final DiffToolConfig config;
- private final Repository repo;
- private final Map<String, ExternalDiffTool> predefinedTools;
- private final Map<String, ExternalDiffTool> userDefinedTools;
- /**
- * Creates the external diff-tools manager for given repository.
- *
- * @param repo
- * the repository
- */
- public DiffTools(Repository repo) {
- this(repo, repo.getConfig());
- }
- /**
- * Creates the external merge-tools manager for given configuration.
- *
- * @param config
- * the git configuration
- */
- public DiffTools(StoredConfig config) {
- this(null, config);
- }
- private DiffTools(Repository repo, StoredConfig config) {
- this.repo = repo;
- this.config = config.get(DiffToolConfig.KEY);
- this.gitDir = repo == null ? null : repo.getDirectory();
- this.fs = repo == null ? FS.DETECTED : repo.getFS();
- this.workTree = repo == null ? null : repo.getWorkTree();
- predefinedTools = setupPredefinedTools();
- userDefinedTools = setupUserDefinedTools(predefinedTools);
- }
- /**
- * Compare two versions of a file.
- *
- * @param localFile
- * The local/left version of the file.
- * @param remoteFile
- * The remote/right version of the file.
- * @param toolName
- * Optionally the name of the tool to use. If not given the
- * default tool will be used.
- * @param prompt
- * Optionally a flag whether to prompt the user before compare.
- * If not given the default will be used.
- * @param gui
- * A flag whether to prefer a gui tool.
- * @param trustExitCode
- * Optionally a flag whether to trust the exit code of the tool.
- * If not given the default will be used.
- * @param promptHandler
- * The handler to use when needing to prompt the user if he wants
- * to continue.
- * @param noToolHandler
- * The handler to use when needing to inform the user, that no
- * tool is configured.
- * @return the optional result of executing the tool if it was executed
- * @throws ToolException
- * when the tool fails
- */
- public Optional<ExecutionResult> compare(FileElement localFile,
- FileElement remoteFile, Optional<String> toolName,
- BooleanTriState prompt, boolean gui, BooleanTriState trustExitCode,
- PromptContinueHandler promptHandler,
- InformNoToolHandler noToolHandler) throws ToolException {
- String toolNameToUse;
- if (toolName == null) {
- throw new ToolException(JGitText.get().diffToolNullError);
- }
- if (toolName.isPresent()) {
- toolNameToUse = toolName.get();
- } else {
- toolNameToUse = getDefaultToolName(gui);
- }
- if (StringUtils.isEmptyOrNull(toolNameToUse)) {
- throw new ToolException(JGitText.get().diffToolNotGivenError);
- }
- boolean doPrompt;
- if (prompt != BooleanTriState.UNSET) {
- doPrompt = prompt == BooleanTriState.TRUE;
- } else {
- doPrompt = isInteractive();
- }
- if (doPrompt) {
- if (!promptHandler.prompt(toolNameToUse)) {
- return Optional.empty();
- }
- }
- boolean trust;
- if (trustExitCode != BooleanTriState.UNSET) {
- trust = trustExitCode == BooleanTriState.TRUE;
- } else {
- trust = config.isTrustExitCode();
- }
- ExternalDiffTool tool = getTool(toolNameToUse);
- if (tool == null) {
- throw new ToolException(
- "External diff tool is not defined: " + toolNameToUse); //$NON-NLS-1$
- }
- return Optional.of(
- compare(localFile, remoteFile, tool, trust));
- }
- /**
- * Compare two versions of a file.
- *
- * @param localFile
- * the local file element
- * @param remoteFile
- * the remote file element
- * @param tool
- * the selected tool
- * @param trustExitCode
- * the "trust exit code" option
- * @return the execution result from tool
- * @throws ToolException
- */
- public ExecutionResult compare(FileElement localFile,
- FileElement remoteFile, ExternalDiffTool tool,
- boolean trustExitCode) throws ToolException {
- try {
- if (tool == null) {
- throw new ToolException(JGitText
- .get().diffToolNotSpecifiedInGitAttributesError);
- }
- // prepare the command (replace the file paths)
- String command = ExternalToolUtils.prepareCommand(tool.getCommand(),
- localFile, remoteFile, null, null);
- // prepare the environment
- Map<String, String> env = ExternalToolUtils.prepareEnvironment(
- gitDir, localFile, remoteFile, null, null);
- // execute the tool
- CommandExecutor cmdExec = new CommandExecutor(fs, trustExitCode);
- return cmdExec.run(command, workTree, env);
- } catch (IOException | InterruptedException e) {
- throw new ToolException(e);
- } finally {
- localFile.cleanTemporaries();
- remoteFile.cleanTemporaries();
- }
- }
- /**
- * Get user defined tool names.
- *
- * @return the user defined tool names
- */
- public Set<String> getUserDefinedToolNames() {
- return userDefinedTools.keySet();
- }
- /**
- * Get predefined tool names.
- *
- * @return the predefined tool names
- */
- public Set<String> getPredefinedToolNames() {
- return predefinedTools.keySet();
- }
- /**
- * Get all tool names.
- *
- * @return the all tool names (default or available tool name is the first
- * in the set)
- */
- public Set<String> getAllToolNames() {
- String defaultName = getDefaultToolName(false);
- if (defaultName == null) {
- defaultName = getFirstAvailableTool();
- }
- return ExternalToolUtils.createSortedToolSet(defaultName,
- 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.
- *
- * @return the user defined tools
- */
- public Map<String, ExternalDiffTool> getUserDefinedTools() {
- return Collections.unmodifiableMap(userDefinedTools);
- }
- /**
- * Get predefined tools map.
- *
- * @param checkAvailability
- * true: for checking if tools can be executed; ATTENTION: this
- * check took some time, do not execute often (store the map for
- * other actions); false: availability is NOT checked:
- * isAvailable() returns default false is this case!
- * @return the predefined tools with optionally checked availability (long
- * running operation)
- */
- public Map<String, ExternalDiffTool> getPredefinedTools(
- boolean checkAvailability) {
- if (checkAvailability) {
- for (ExternalDiffTool tool : predefinedTools.values()) {
- PreDefinedDiffTool predefTool = (PreDefinedDiffTool) tool;
- predefTool.setAvailable(ExternalToolUtils.isToolAvailable(fs,
- gitDir, workTree, predefTool.getPath()));
- }
- }
- return Collections.unmodifiableMap(predefinedTools);
- }
- /**
- * Get first available tool name.
- *
- * @return the name of first available predefined tool or null
- */
- public String getFirstAvailableTool() {
- for (ExternalDiffTool tool : predefinedTools.values()) {
- if (ExternalToolUtils.isToolAvailable(fs, gitDir, workTree,
- tool.getPath())) {
- return tool.getName();
- }
- }
- return null;
- }
- /**
- * Get default (gui-)tool name.
- *
- * @param gui
- * use the diff.guitool setting ?
- * @return the default tool name
- */
- public String getDefaultToolName(boolean gui) {
- String guiToolName;
- if (gui) {
- guiToolName = config.getDefaultGuiToolName();
- if (guiToolName != null) {
- return guiToolName;
- }
- }
- return config.getDefaultToolName();
- }
- /**
- * Is interactive diff (prompt enabled) ?
- *
- * @return is interactive (config prompt enabled) ?
- */
- public boolean isInteractive() {
- return config.isPrompt();
- }
- private ExternalDiffTool getTool(final String name) {
- ExternalDiffTool tool = userDefinedTools.get(name);
- if (tool == null) {
- tool = predefinedTools.get(name);
- }
- return tool;
- }
- private static Map<String, ExternalDiffTool> setupPredefinedTools() {
- Map<String, ExternalDiffTool> tools = new TreeMap<>();
- for (CommandLineDiffTool tool : CommandLineDiffTool.values()) {
- tools.put(tool.name(), new PreDefinedDiffTool(tool));
- }
- return tools;
- }
- private Map<String, ExternalDiffTool> setupUserDefinedTools(
- Map<String, ExternalDiffTool> predefTools) {
- Map<String, ExternalDiffTool> tools = 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) {
- tools.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) predefTools
- .get(name);
- if (predefTool != null) {
- predefTool.setPath(userTool.getPath());
- }
- }
- }
- return tools;
- }
- }