| /* |
| * 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 java.io.File; |
| import java.io.IOException; |
| import java.nio.file.Files; |
| import java.nio.file.Path; |
| import java.nio.file.Paths; |
| import java.nio.file.StandardCopyOption; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.TreeMap; |
| |
| import org.eclipse.jgit.internal.diffmergetool.FileElement.Type; |
| 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 { |
| |
| Repository repo; |
| |
| private final MergeToolConfig config; |
| |
| private final Map<String, ExternalMergeTool> predefinedTools; |
| |
| private final Map<String, ExternalMergeTool> userDefinedTools; |
| |
| /** |
| * @param repo |
| * the repository |
| */ |
| public MergeTools(Repository repo) { |
| this.repo = repo; |
| config = repo.getConfig().get(MergeToolConfig.KEY); |
| predefinedTools = setupPredefinedTools(); |
| userDefinedTools = setupUserDefinedTools(config, predefinedTools); |
| } |
| |
| /** |
| * @param localFile |
| * the local file element |
| * @param remoteFile |
| * the remote file element |
| * @param mergedFile |
| * the merged file element |
| * @param baseFile |
| * the base file element (can be null) |
| * @param tempDir |
| * the temporary directory (needed for backup and auto-remove, |
| * can be null) |
| * @param toolName |
| * the selected tool name (can be null) |
| * @param prompt |
| * the prompt option |
| * @param gui |
| * the GUI option |
| * @return the execution result from tool |
| * @throws ToolException |
| */ |
| public ExecutionResult merge(FileElement localFile, FileElement remoteFile, |
| FileElement mergedFile, FileElement baseFile, File tempDir, |
| String toolName, BooleanTriState prompt, BooleanTriState gui) |
| throws ToolException { |
| ExternalMergeTool tool = guessTool(toolName, gui); |
| FileElement backup = null; |
| ExecutionResult result = null; |
| try { |
| File workingDir = repo.getWorkTree(); |
| // create additional backup file (copy worktree file) |
| backup = createBackupFile(mergedFile.getPath(), |
| tempDir != null ? tempDir : workingDir); |
| // prepare the command (replace the file paths) |
| boolean trust = tool.getTrustExitCode() == BooleanTriState.TRUE; |
| String command = ExternalToolUtils.prepareCommand( |
| tool.getCommand(baseFile != null), localFile, remoteFile, |
| mergedFile, baseFile); |
| // prepare the environment |
| Map<String, String> env = ExternalToolUtils.prepareEnvironment(repo, |
| localFile, remoteFile, mergedFile, baseFile); |
| // execute the tool |
| CommandExecutor cmdExec = new CommandExecutor(repo.getFS(), trust); |
| result = cmdExec.run(command, workingDir, env); |
| // keep backup as .orig file |
| if (backup != null) { |
| keepBackupFile(mergedFile.getPath(), backup); |
| } |
| return result; |
| } catch (IOException | InterruptedException e) { |
| throw new ToolException(e); |
| } finally { |
| // always delete backup file (ignore that it was may be already |
| // moved to keep-backup file) |
| if (backup != null) { |
| backup.cleanTemporaries(); |
| } |
| // if the tool returns an error and keepTemporaries is set to true, |
| // then these temporary files will be preserved |
| if (!((result == null) && config.isKeepTemporaries())) { |
| // delete the files |
| localFile.cleanTemporaries(); |
| remoteFile.cleanTemporaries(); |
| if (baseFile != null) { |
| baseFile.cleanTemporaries(); |
| } |
| // delete temporary directory if needed |
| if (config.isWriteToTemp() && (tempDir != null) |
| && tempDir.exists()) { |
| tempDir.delete(); |
| } |
| } |
| } |
| } |
| |
| private FileElement createBackupFile(String filePath, File parentDir) |
| throws IOException { |
| FileElement backup = null; |
| Path path = Paths.get(filePath); |
| if (Files.exists(path)) { |
| backup = new FileElement(filePath, Type.BACKUP); |
| Files.copy(path, backup.createTempFile(parentDir).toPath(), |
| StandardCopyOption.REPLACE_EXISTING); |
| } |
| return backup; |
| } |
| |
| /** |
| * @return the created temporary directory if (mergetol.writeToTemp == true) |
| * or null if not configured or false. |
| * @throws IOException |
| */ |
| public File createTempDirectory() throws IOException { |
| return config.isWriteToTemp() |
| ? Files.createTempDirectory("jgit-mergetool-").toFile() //$NON-NLS-1$ |
| : null; |
| } |
| |
| /** |
| * @return the tool names |
| */ |
| public Set<String> getToolNames() { |
| return config.getToolNames(); |
| } |
| |
| /** |
| * @return the user defined tools |
| */ |
| public Map<String, ExternalMergeTool> getUserDefinedTools() { |
| return userDefinedTools; |
| } |
| |
| /** |
| * @return the available predefined tools |
| */ |
| public Map<String, ExternalMergeTool> getAvailableTools() { |
| return predefinedTools; |
| } |
| |
| /** |
| * @return the NOT available predefined tools |
| */ |
| public Map<String, ExternalMergeTool> getNotAvailableTools() { |
| return new TreeMap<>(); |
| } |
| |
| /** |
| * @param gui |
| * use the diff.guitool setting ? |
| * @return the default tool name |
| */ |
| public String getDefaultToolName(BooleanTriState gui) { |
| return gui != BooleanTriState.UNSET ? "my_gui_tool" //$NON-NLS-1$ |
| : config.getDefaultToolName(); |
| } |
| |
| /** |
| * @return is interactive (config prompt enabled) ? |
| */ |
| public boolean isInteractive() { |
| return config.isPrompt(); |
| } |
| |
| 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 ExternalMergeTool getTool(final String name) { |
| ExternalMergeTool tool = userDefinedTools.get(name); |
| if (tool == null) { |
| tool = predefinedTools.get(name); |
| } |
| return tool; |
| } |
| |
| private void keepBackupFile(String mergedFilePath, FileElement backup) |
| throws IOException { |
| if (config.isKeepBackup()) { |
| Path backupPath = backup.getFile().toPath(); |
| Files.move(backupPath, |
| backupPath.resolveSibling( |
| Paths.get(mergedFilePath).getFileName() + ".orig"), //$NON-NLS-1$ |
| StandardCopyOption.REPLACE_EXISTING); |
| } |
| } |
| |
| 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; |
| } |
| |
| } |