summaryrefslogtreecommitdiffstats
path: root/org.eclipse.jgit
diff options
context:
space:
mode:
authorAndrey Loskutov <loskutov@gmx.de>2022-05-27 01:02:15 -0400
committerGerrit Code Review @ Eclipse.org <gerrit@eclipse.org>2022-05-27 01:02:15 -0400
commitd2489ffa7080e8353b34f22484e5c6b371747f5a (patch)
tree53006175cc6925ce9894563c3599c615e07a3edd /org.eclipse.jgit
parent5828ca160233550bc8f6d05ec9819c46fbffe5f3 (diff)
parenteaf4d500b886a7e776f50bf53497fe463e714b25 (diff)
downloadjgit-d2489ffa7080e8353b34f22484e5c6b371747f5a.tar.gz
jgit-d2489ffa7080e8353b34f22484e5c6b371747f5a.zip
Merge "Add mergetool merge feature (execute external tool)"
Diffstat (limited to 'org.eclipse.jgit')
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/CommandExecutor.java16
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/FileElement.java83
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/MergeTools.java121
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/ToolException.java23
4 files changed, 178 insertions, 65 deletions
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/CommandExecutor.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/CommandExecutor.java
index 0dde9b5f39..ad79fe8fc6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/CommandExecutor.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/CommandExecutor.java
@@ -72,10 +72,18 @@ public class CommandExecutor {
}
ExecutionResult result = fs.execute(pb, null);
int rc = result.getRc();
- if ((rc != 0) && (checkExitCode
- || isCommandExecutionError(rc))) {
- throw new ToolException(
- new String(result.getStderr().toByteArray()), result);
+ if (rc != 0) {
+ boolean execError = isCommandExecutionError(rc);
+ if (checkExitCode || execError) {
+ throw new ToolException(
+ "JGit: tool execution return code: " + rc + "\n" //$NON-NLS-1$ //$NON-NLS-2$
+ + "checkExitCode: " + checkExitCode + "\n" //$NON-NLS-1$ //$NON-NLS-2$
+ + "execError: " + execError + "\n" //$NON-NLS-1$ //$NON-NLS-2$
+ + "stderr: \n" //$NON-NLS-1$
+ + new String(
+ result.getStderr().toByteArray()),
+ result, execError);
+ }
}
return result;
} finally {
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 cdc8f015f6..1ae87aaa62 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
@@ -11,6 +11,7 @@
package org.eclipse.jgit.internal.diffmergetool;
import java.io.File;
+import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
@@ -80,35 +81,27 @@ public class FileElement {
}
/**
- * @param workingDir the working directory used if file cannot be found (e.g. /dev/null)
+ * Returns a temporary file with in passed working directory and fills it
+ * with stream if valid.
+ *
+ * @param directory
+ * the working 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 workingDir) throws IOException {
+ public File getFile(File directory, String midName) throws IOException {
if (tempFile != null) {
return tempFile;
}
- File file = new File(path);
- String name = file.getName();
- if (path.equals(DiffEntry.DEV_NULL)) {
- file = new File(workingDir, "nul"); //$NON-NLS-1$
- }
- else if (stream != null) {
- tempFile = File.createTempFile(".__", "__" + name); //$NON-NLS-1$ //$NON-NLS-2$
- try (OutputStream outStream = new FileOutputStream(tempFile)) {
- int read = 0;
- byte[] bytes = new byte[8 * 1024];
- while ((read = stream.read(bytes)) != -1) {
- outStream.write(bytes, 0, read);
- }
- } finally {
- // stream can only be consumed once --> close it
- stream.close();
- stream = null;
- }
- return tempFile;
- }
- return file;
+ String[] fileNameAndExtension = splitBaseFileNameAndExtension(
+ new File(path));
+ tempFile = File.createTempFile(
+ fileNameAndExtension[0] + "_" + midName + "_", //$NON-NLS-1$ //$NON-NLS-2$
+ fileNameAndExtension[1], directory);
+ copyFromStream();
+ return tempFile;
}
/**
@@ -130,19 +123,7 @@ public class FileElement {
// TODO: avoid long random file name (number generated by
// createTempFile)
tempFile = File.createTempFile(".__", "__" + name); //$NON-NLS-1$ //$NON-NLS-2$
- if (stream != null) {
- try (OutputStream outStream = new FileOutputStream(tempFile)) {
- int read = 0;
- byte[] bytes = new byte[8 * 1024];
- while ((read = stream.read(bytes)) != -1) {
- outStream.write(bytes, 0, read);
- }
- } finally {
- // stream can only be consumed once --> close it
- stream.close();
- stream = null;
- }
- }
+ copyFromStream();
return tempFile;
}
return file;
@@ -157,4 +138,34 @@ public class FileElement {
tempFile = null;
}
+ private void copyFromStream() throws IOException, FileNotFoundException {
+ if (stream != null) {
+ try (OutputStream outStream = new FileOutputStream(tempFile)) {
+ int read = 0;
+ byte[] bytes = new byte[8 * 1024];
+ while ((read = stream.read(bytes)) != -1) {
+ outStream.write(bytes, 0, read);
+ }
+ } finally {
+ // stream can only be consumed once --> close it
+ stream.close();
+ stream = null;
+ }
+ }
+ }
+
+ 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);
+ }
+ }
+ return result;
+ }
+
}
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 cefefb8e75..c4c2ceccff 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
@@ -10,6 +10,11 @@
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;
@@ -48,7 +53,7 @@ public class MergeTools {
* @param remoteFile
* the remote file element
* @param baseFile
- * the base file element
+ * the base file element (can be null)
* @param mergedFilePath
* the path of 'merged' file
* @param toolName
@@ -65,33 +70,77 @@ public class MergeTools {
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();
- String localFilePath = localFile.getFile().getPath();
- String remoteFilePath = remoteFile.getFile().getPath();
- String baseFilePath = baseFile.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$
- command = command.replace("$BASE", baseFilePath); //$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$
- env.put("BASE", baseFilePath); //$NON-NLS-1$
+ // 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$
+ }
+ // prepare the command (replace the file paths)
boolean trust = tool.getTrustExitCode() == BooleanTriState.TRUE;
+ String command = prepareCommand(mergedFilePath, localFilePath,
+ remoteFilePath, baseFilePath,
+ tool.getCommand(baseFile != null));
+ // prepare the environment
+ Map<String, String> env = prepareEnvironment(repo, mergedFilePath,
+ localFilePath, remoteFilePath, baseFilePath);
CommandExecutor cmdExec = new CommandExecutor(repo.getFS(), trust);
- return cmdExec.run(command, workingDir, env);
+ result = cmdExec.run(command, workingDir, env);
+ // keep backup as .orig file
+ if (backup != null) {
+ keepBackupFile(mergedFilePath, backup);
+ }
+ return result;
} catch (Exception e) {
throw new ToolException(e);
} finally {
- localFile.cleanTemporaries();
- remoteFile.cleanTemporaries();
- baseFile.cleanTemporaries();
+ // 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 static FileElement createBackupFile(String mergedFilePath,
+ File tempDir) throws IOException {
+ FileElement backup = null;
+ Path path = Paths.get(tempDir.getPath(), mergedFilePath);
+ if (Files.exists(path)) {
+ backup = new FileElement(mergedFilePath, "NOID", null); //$NON-NLS-1$
+ Files.copy(path, backup.getFile(tempDir, "BACKUP").toPath(), //$NON-NLS-1$
+ StandardCopyOption.REPLACE_EXISTING);
}
+ return backup;
}
/**
@@ -159,6 +208,38 @@ 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()) {
+ 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()) {
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 7862cf5967..1ae0780ac8 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
@@ -26,6 +26,8 @@ public class ToolException extends Exception {
private final ExecutionResult result;
+ private final boolean commandExecutionError;
+
/**
* the serial version UID
*/
@@ -35,8 +37,7 @@ public class ToolException extends Exception {
*
*/
public ToolException() {
- super();
- result = null;
+ this(null, null, false);
}
/**
@@ -44,8 +45,7 @@ public class ToolException extends Exception {
* the exception message
*/
public ToolException(String message) {
- super(message);
- result = null;
+ this(message, null, false);
}
/**
@@ -53,10 +53,14 @@ public class ToolException extends Exception {
* the exception message
* @param result
* the execution result
+ * @param commandExecutionError
+ * is command execution error happened ?
*/
- public ToolException(String message, ExecutionResult result) {
+ public ToolException(String message, ExecutionResult result,
+ boolean commandExecutionError) {
super(message);
this.result = result;
+ this.commandExecutionError = commandExecutionError;
}
/**
@@ -68,6 +72,7 @@ public class ToolException extends Exception {
public ToolException(String message, Throwable cause) {
super(message, cause);
result = null;
+ commandExecutionError = false;
}
/**
@@ -77,6 +82,7 @@ public class ToolException extends Exception {
public ToolException(Throwable cause) {
super(cause);
result = null;
+ commandExecutionError = false;
}
/**
@@ -94,6 +100,13 @@ public class ToolException extends Exception {
}
/**
+ * @return true if command execution error appears, false otherwise
+ */
+ public boolean isCommandExecutionError() {
+ return commandExecutionError;
+ }
+
+ /**
* @return the result Stderr
*/
public String getResultStderr() {