diff options
author | Andrey Loskutov <loskutov@gmx.de> | 2022-05-30 12:42:20 -0400 |
---|---|---|
committer | Gerrit Code Review @ Eclipse.org <gerrit@eclipse.org> | 2022-05-30 12:42:20 -0400 |
commit | bb30be6b3335c67ee59d3705d5e04e2dbb688128 (patch) | |
tree | 6bb2c4188f1fac42120407a425774ad713e42780 /org.eclipse.jgit | |
parent | 8c681aac500d0755bd68f8b322f7fdb20cd2b8a1 (diff) | |
parent | e81085944f1a039566f2972c863d189724988b46 (diff) | |
download | jgit-bb30be6b3335c67ee59d3705d5e04e2dbb688128.tar.gz jgit-bb30be6b3335c67ee59d3705d5e04e2dbb688128.zip |
Merge "Add filtering with help of DirCacheCheckout.getContent()"
Diffstat (limited to 'org.eclipse.jgit')
5 files changed, 282 insertions, 134 deletions
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/DiffTools.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/DiffTools.java index 2f2b9de818..1dcc523bf8 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/DiffTools.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/DiffTools.java @@ -12,12 +12,10 @@ package org.eclipse.jgit.internal.diffmergetool; import java.util.TreeMap; import java.util.Collections; -import java.io.File; import java.io.IOException; import java.util.Map; import java.util.Set; -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; @@ -28,6 +26,8 @@ import org.eclipse.jgit.util.StringUtils; */ public class DiffTools { + private final Repository repo; + private final DiffToolConfig config; private final Map<String, ExternalDiffTool> predefinedTools; @@ -41,6 +41,7 @@ public class DiffTools { * the repository */ public DiffTools(Repository repo) { + this.repo = repo; config = repo.getConfig().get(DiffToolConfig.KEY); predefinedTools = setupPredefinedTools(); userDefinedTools = setupUserDefinedTools(config, predefinedTools); @@ -49,14 +50,13 @@ public class DiffTools { /** * Compare two versions of a file. * - * @param repo - * the repository * @param localFile * the local file element * @param remoteFile * the remote file element - * @param mergedFilePath - * the path of 'merged' file, it equals local or remote path + * @param mergedFile + * the merged file element, it's path equals local or remote + * element path * @param toolName * the selected tool name (can be null) * @param prompt @@ -68,36 +68,31 @@ public class DiffTools { * @return the execution result from tool * @throws ToolException */ - public ExecutionResult compare(Repository repo, FileElement localFile, - FileElement remoteFile, String mergedFilePath, String toolName, + public ExecutionResult compare(FileElement localFile, + FileElement remoteFile, FileElement mergedFile, String toolName, BooleanTriState prompt, BooleanTriState gui, BooleanTriState trustExitCode) throws ToolException { - ExternalDiffTool tool = guessTool(toolName, gui); try { - File workingDir = repo.getWorkTree(); - String localFilePath = localFile.getFile().getPath(); - String remoteFilePath = remoteFile.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$ - 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$ + // prepare the command (replace the file paths) + String command = ExternalToolUtils.prepareCommand( + guessTool(toolName, gui).getCommand(), localFile, + remoteFile, mergedFile, null); + // prepare the environment + Map<String, String> env = ExternalToolUtils.prepareEnvironment(repo, + localFile, remoteFile, mergedFile, null); boolean trust = config.isTrustExitCode(); if (trustExitCode != BooleanTriState.UNSET) { trust = trustExitCode == BooleanTriState.TRUE; } + // execute the tool CommandExecutor cmdExec = new CommandExecutor(repo.getFS(), trust); - return cmdExec.run(command, workingDir, env); + return cmdExec.run(command, repo.getWorkTree(), env); } catch (IOException | InterruptedException e) { throw new ToolException(e); } finally { localFile.cleanTemporaries(); remoteFile.cleanTemporaries(); + mergedFile.cleanTemporaries(); } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/ExternalToolUtils.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/ExternalToolUtils.java new file mode 100644 index 0000000000..3efb90c490 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/ExternalToolUtils.java @@ -0,0 +1,81 @@ +/* + * 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 java.util.TreeMap; +import java.io.IOException; +import java.util.Map; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.Repository; + +/** + * Utilities for diff- and merge-tools. + */ +public class ExternalToolUtils { + + /** + * Prepare command for execution. + * + * @param command + * the input "command" string + * @param localFile + * the local file (ours) + * @param remoteFile + * the remote file (theirs) + * @param mergedFile + * the merged file (worktree) + * @param baseFile + * the base file (can be null) + * @return the prepared (with replaced variables) command string + * @throws IOException + */ + public static String prepareCommand(String command, FileElement localFile, + FileElement remoteFile, FileElement mergedFile, + FileElement baseFile) throws IOException { + command = localFile.replaceVariable(command); + command = remoteFile.replaceVariable(command); + command = mergedFile.replaceVariable(command); + if (baseFile != null) { + command = baseFile.replaceVariable(command); + } + return command; + } + + /** + * Prepare environment needed for execution. + * + * @param repo + * the repository + * @param localFile + * the local file (ours) + * @param remoteFile + * the remote file (theirs) + * @param mergedFile + * the merged file (worktree) + * @param baseFile + * the base file (can be null) + * @return the environment map with variables and values (file paths) + * @throws IOException + */ + public static Map<String, String> prepareEnvironment(Repository repo, + FileElement localFile, FileElement remoteFile, + FileElement mergedFile, FileElement baseFile) throws IOException { + Map<String, String> env = new TreeMap<>(); + env.put(Constants.GIT_DIR_KEY, repo.getDirectory().getAbsolutePath()); + localFile.addToEnv(env); + remoteFile.addToEnv(env); + mergedFile.addToEnv(env); + if (baseFile != null) { + baseFile.addToEnv(env); + } + return env; + } + +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/FileElement.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/FileElement.java index 1ae87aaa62..5902c1e1b8 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/FileElement.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/FileElement.java @@ -14,10 +14,11 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; import java.io.OutputStream; +import java.util.Map; import org.eclipse.jgit.diff.DiffEntry; -import org.eclipse.jgit.lib.ObjectStream; /** * The element used as left or right file for compare. @@ -25,36 +26,71 @@ import org.eclipse.jgit.lib.ObjectStream; */ public class FileElement { + /** + * The file element type. + * + */ + public enum Type { + /** + * The local file element (ours). + */ + LOCAL, + /** + * The remote file element (theirs). + */ + REMOTE, + /** + * The merged file element (path in worktree). + */ + MERGED, + /** + * The base file element (of ours and theirs). + */ + BASE, + /** + * The backup file element (copy of merged / conflicted). + */ + BACKUP + } + private final String path; - private final String id; + private final Type type; - private ObjectStream stream; + private InputStream stream; private File tempFile; /** + * Creates file element for path. + * * @param path * the file path - * @param id - * the file id + * @param type + * the element type */ - public FileElement(final String path, final String id) { - this(path, id, null); + public FileElement(String path, Type type) { + this(path, type, null, null); } /** + * Creates file element for path. + * * @param path * the file path - * @param id - * the file id + * @param type + * the element type + * @param tempFile + * the temporary file to be used (can be null and will be created + * then) * @param stream * the object stream to load instead of file */ - public FileElement(final String path, final String id, - ObjectStream stream) { + public FileElement(String path, Type type, File tempFile, + InputStream stream) { this.path = path; - this.id = id; + this.type = type; + this.tempFile = tempFile; this.stream = stream; } @@ -66,71 +102,101 @@ public class FileElement { } /** - * @return the file id + * @return the element type */ - public String getId() { - return id; + public Type getType() { + return type; } /** - * @param stream - * the object stream - */ - public void setStream(ObjectStream stream) { - this.stream = stream; - } - - /** - * Returns a temporary file with in passed working directory and fills it - * with stream if valid. + * Return a temporary file within passed directory and fills it with stream + * if valid. * * @param directory - * the working directory where the temporary file is created + * the directory where the temporary file is created * @param midName * name added in the middle of generated temporary file name * @return the object stream * @throws IOException */ public File getFile(File directory, String midName) throws IOException { - if (tempFile != null) { + if ((tempFile != null) && (stream == null)) { return tempFile; } - String[] fileNameAndExtension = splitBaseFileNameAndExtension( - new File(path)); - tempFile = File.createTempFile( - fileNameAndExtension[0] + "_" + midName + "_", //$NON-NLS-1$ //$NON-NLS-2$ - fileNameAndExtension[1], directory); - copyFromStream(); - return tempFile; + tempFile = getTempFile(path, directory, midName); + return copyFromStream(tempFile, stream); } /** - * Returns a real file from work tree or a temporary file with content if + * Return a real file from work tree or a temporary file with content if * stream is valid or if path is "/dev/null" * * @return the object stream * @throws IOException */ public File getFile() throws IOException { - if (tempFile != null) { + if ((tempFile != null) && (stream == null)) { return tempFile; } File file = new File(path); - String name = file.getName(); // if we have a stream or file is missing ("/dev/null") then create // temporary file - if ((stream != null) || path.equals(DiffEntry.DEV_NULL)) { - // TODO: avoid long random file name (number generated by - // createTempFile) - tempFile = File.createTempFile(".__", "__" + name); //$NON-NLS-1$ //$NON-NLS-2$ - copyFromStream(); - return tempFile; + if ((stream != null) || isNullPath()) { + tempFile = getTempFile(file); + return copyFromStream(tempFile, stream); } return file; } /** - * Deletes and invalidates temporary file if necessary. + * Check if path id "/dev/null" + * + * @return true if path is "/dev/null" + */ + public boolean isNullPath() { + return path.equals(DiffEntry.DEV_NULL); + } + + /** + * Create temporary file in given or system temporary directory + * + * @param directory + * the directory for the file (can be null); if null system + * temporary directory is used + * @return temporary file in directory or in the system temporary directory + * @throws IOException + */ + public File createTempFile(File directory) throws IOException { + if (tempFile == null) { + File file = new File(path); + if (directory != null) { + tempFile = getTempFile(file, directory, type.name()); + } else { + tempFile = getTempFile(file); + } + } + return tempFile; + } + + private static File getTempFile(File file) throws IOException { + return File.createTempFile(".__", "__" + file.getName()); //$NON-NLS-1$ //$NON-NLS-2$ + } + + private static File getTempFile(File file, File directory, String midName) + throws IOException { + String[] fileNameAndExtension = splitBaseFileNameAndExtension(file); + return File.createTempFile( + fileNameAndExtension[0] + "_" + midName + "_", //$NON-NLS-1$ //$NON-NLS-2$ + fileNameAndExtension[1], directory); + } + + private static File getTempFile(String path, File directory, String midName) + throws IOException { + return getTempFile(new File(path), directory, midName); + } + + /** + * Delete and invalidate temporary file if necessary. */ public void cleanTemporaries() { if (tempFile != null && tempFile.exists()) @@ -138,9 +204,10 @@ public class FileElement { tempFile = null; } - private void copyFromStream() throws IOException, FileNotFoundException { + private static File copyFromStream(File file, final InputStream stream) + throws IOException, FileNotFoundException { if (stream != null) { - try (OutputStream outStream = new FileOutputStream(tempFile)) { + try (OutputStream outStream = new FileOutputStream(file)) { int read = 0; byte[] bytes = new byte[8 * 1024]; while ((read = stream.read(bytes)) != -1) { @@ -149,23 +216,46 @@ public class FileElement { } finally { // stream can only be consumed once --> close it stream.close(); - stream = null; } } + return file; } private static String[] splitBaseFileNameAndExtension(File file) { String[] result = new String[2]; result[0] = file.getName(); result[1] = ""; //$NON-NLS-1$ - if (!result[0].startsWith(".")) { //$NON-NLS-1$ - int idx = result[0].lastIndexOf("."); //$NON-NLS-1$ - if (idx != -1) { - result[1] = result[0].substring(idx, result[0].length()); - result[0] = result[0].substring(0, idx); - } + int idx = result[0].lastIndexOf("."); //$NON-NLS-1$ + // if "." was found (>-1) and last-index is not first char (>0), then + // split (same behavior like cgit) + if (idx > 0) { + result[1] = result[0].substring(idx, result[0].length()); + result[0] = result[0].substring(0, idx); } return result; } + /** + * Replace variable in input + * + * @param input + * the input string + * @return the replaced input string + * @throws IOException + */ + public String replaceVariable(String input) throws IOException { + return input.replace("$" + type.name(), getFile().getPath()); //$NON-NLS-1$ + } + + /** + * Add variable to environment map. + * + * @param env + * the environment where this element should be added + * @throws IOException + */ + public void addToEnv(Map<String, String> env) throws IOException { + env.put(type.name(), getFile().getPath()); + } + } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/MergeTools.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/MergeTools.java index c4c2ceccff..9a2a8304eb 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/MergeTools.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/MergeTools.java @@ -19,7 +19,7 @@ import java.util.Map; import java.util.Set; import java.util.TreeMap; -import org.eclipse.jgit.lib.Constants; +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; @@ -29,6 +29,8 @@ import org.eclipse.jgit.util.FS.ExecutionResult; */ public class MergeTools { + Repository repo; + private final MergeToolConfig config; private final Map<String, ExternalMergeTool> predefinedTools; @@ -37,25 +39,27 @@ public class MergeTools { /** * @param repo - * the repository database + * the repository */ public MergeTools(Repository repo) { + this.repo = repo; config = repo.getConfig().get(MergeToolConfig.KEY); predefinedTools = setupPredefinedTools(); userDefinedTools = setupUserDefinedTools(config, predefinedTools); } /** - * @param repo - * the repository * @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 mergedFilePath - * the path of 'merged' file + * @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 @@ -65,47 +69,35 @@ public class MergeTools { * @return the execution result from tool * @throws ToolException */ - public ExecutionResult merge(Repository repo, FileElement localFile, - FileElement remoteFile, FileElement baseFile, String mergedFilePath, + 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; - File tempDir = null; ExecutionResult result = null; try { File workingDir = repo.getWorkTree(); - // crate temp-directory or use working directory - tempDir = config.isWriteToTemp() - ? Files.createTempDirectory("jgit-mergetool-").toFile() //$NON-NLS-1$ - : workingDir; // create additional backup file (copy worktree file) - backup = createBackupFile(mergedFilePath, tempDir); - // get local, remote and base file paths - String localFilePath = localFile.getFile(tempDir, "LOCAL") //$NON-NLS-1$ - .getPath(); - String remoteFilePath = remoteFile.getFile(tempDir, "REMOTE") //$NON-NLS-1$ - .getPath(); - String baseFilePath = ""; //$NON-NLS-1$ - if (baseFile != null) { - baseFilePath = baseFile.getFile(tempDir, "BASE").getPath(); //$NON-NLS-1$ - } + backup = createBackupFile(mergedFile.getPath(), + tempDir != null ? tempDir : workingDir); // prepare the command (replace the file paths) boolean trust = tool.getTrustExitCode() == BooleanTriState.TRUE; - String command = prepareCommand(mergedFilePath, localFilePath, - remoteFilePath, baseFilePath, - tool.getCommand(baseFile != null)); + String command = ExternalToolUtils.prepareCommand( + tool.getCommand(baseFile != null), localFile, remoteFile, + mergedFile, baseFile); // prepare the environment - Map<String, String> env = prepareEnvironment(repo, mergedFilePath, - localFilePath, remoteFilePath, baseFilePath); + 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(mergedFilePath, backup); + keepBackupFile(mergedFile.getPath(), backup); } return result; - } catch (Exception e) { + } catch (IOException | InterruptedException e) { throw new ToolException(e); } finally { // always delete backup file (ignore that it was may be already @@ -131,19 +123,30 @@ public class MergeTools { } } - private static FileElement createBackupFile(String mergedFilePath, - File tempDir) throws IOException { + private FileElement createBackupFile(String filePath, File parentDir) + throws IOException { FileElement backup = null; - Path path = Paths.get(tempDir.getPath(), mergedFilePath); + Path path = Paths.get(filePath); if (Files.exists(path)) { - backup = new FileElement(mergedFilePath, "NOID", null); //$NON-NLS-1$ - Files.copy(path, backup.getFile(tempDir, "BACKUP").toPath(), //$NON-NLS-1$ + 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() { @@ -208,27 +211,6 @@ public class MergeTools { return tool; } - private String prepareCommand(String mergedFilePath, String localFilePath, - String remoteFilePath, String baseFilePath, String command) { - 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$ - return command; - } - - private Map<String, String> prepareEnvironment(Repository repo, - String mergedFilePath, String localFilePath, String remoteFilePath, - String baseFilePath) { - 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$ - return env; - } - private void keepBackupFile(String mergedFilePath, FileElement backup) throws IOException { if (config.isKeepBackup()) { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/ToolException.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/ToolException.java index 1ae0780ac8..27f7d12e66 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/ToolException.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/ToolException.java @@ -113,7 +113,7 @@ public class ToolException extends Exception { try { return new String(result.getStderr().toByteArray()); } catch (Exception e) { - LOG.warn(e.getMessage()); + LOG.warn("Failed to retrieve standard error output", e); //$NON-NLS-1$ } return ""; //$NON-NLS-1$ } @@ -125,7 +125,7 @@ public class ToolException extends Exception { try { return new String(result.getStdout().toByteArray()); } catch (Exception e) { - LOG.warn(e.getMessage()); + LOG.warn("Failed to retrieve standard output", e); //$NON-NLS-1$ } return ""; //$NON-NLS-1$ } |