diff --git a/src/main/java/com/neo4j/sandbox/git/CommandIOException.java b/src/main/java/com/neo4j/sandbox/git/CommandIOException.java new file mode 100644 index 0000000..60f7dac --- /dev/null +++ b/src/main/java/com/neo4j/sandbox/git/CommandIOException.java @@ -0,0 +1,36 @@ +package com.neo4j.sandbox.git; + +import java.io.IOException; + +class CommandIOException extends IOException { + + private final String stdout; + private final String stderr; + + public CommandIOException(String faultyCommand, int status, String stdout, String stderr) { + super(String.format("Expected Git operation %s to succeed but got exit status %d. See error below%n%s", + faultyCommand, status, combinedOutputs(stdout, stderr))); + + this.stdout = stdout; + this.stderr = stderr; + } + + public boolean commandOutputContains(String msg) { + return this.stdout.contains(msg) || this.stderr.contains(msg); + } + + private static String combinedOutputs(String stdout, String stderr) { + StringBuilder builder = new StringBuilder(); + builder.append(String.format("=====%n")); + builder.append(String.format("Error stream%n")); + builder.append(String.format("=====%n")); + builder.append(stderr); + builder.append("\n"); + builder.append(String.format("=====%n")); + builder.append(String.format("Output stream%n")); + builder.append(String.format("=====%n")); + builder.append(stdout); + builder.append("\n"); + return builder.toString(); + } +} diff --git a/src/main/java/com/neo4j/sandbox/git/GitCli.java b/src/main/java/com/neo4j/sandbox/git/GitCli.java index 4f580d2..4796714 100644 --- a/src/main/java/com/neo4j/sandbox/git/GitCli.java +++ b/src/main/java/com/neo4j/sandbox/git/GitCli.java @@ -2,12 +2,12 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Service; import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.concurrent.TimeUnit; // @Service @@ -15,6 +15,8 @@ public class GitCli implements GitOperations { private static final Logger LOGGER = LoggerFactory.getLogger(GitCli.class); + private static final String GIT_LOCALE = "en_US"; + @Override public void clone(Path cloneLocation, String repositoryUri, String token) throws IOException { File workingDirectory = cloneLocation.toFile(); @@ -25,6 +27,9 @@ public void clone(Path cloneLocation, String repositoryUri, String token) throws @Override public void checkoutNewBranch(Path cloneLocation, String branchName) throws IOException { + if (branchExists(cloneLocation, branchName)) { + throw new DuplicateBranchException(); + } executeCommand(cloneLocation.toFile(), "git", "checkout", "-b", branchName); } @@ -34,7 +39,10 @@ public void commitAll(Path cloneLocation, String message) throws IOException { executeCommand(workingDirectory, "git", "add", "--all"); try { executeCommand(workingDirectory, "git", "commit", "-m", message); - } catch (IOException e) { + } catch (CommandIOException e) { + if (e.commandOutputContains("nothing to commit")) { + throw new BlankCommitException(e); + } throw new CommitException(e); } } @@ -57,14 +65,22 @@ public String currentBranch(Path cloneLocation) throws IOException { return stdout.substring(stdout.lastIndexOf("/") + 1); } - private static void executeCommand(File workingDirectory, String... commands) throws IOException { + private boolean branchExists(Path cloneLocation, String branchName) throws IOException { + String remoteUrl = executeCommand(cloneLocation.toFile(), "git", "remote", "get-url", "origin"); + String output = executeCommand(cloneLocation.toFile(), "git", "ls-remote", "--heads", remoteUrl.trim(), branchName); + return !output.isBlank(); + } + + private static String executeCommand(File workingDirectory, String... commands) throws IOException { Process process = runProcess(workingDirectory, commands); assertSuccessfulExitCode(process, commands); + return stdout(process); } private static Process runProcess(File workingDirectory, String... commands) throws IOException { String command = String.join(" ", commands); ProcessBuilder processBuilder = new ProcessBuilder(commands); + processBuilder.environment().put("LC_ALL", GIT_LOCALE); processBuilder.directory(workingDirectory); Process process = processBuilder.start(); waitForProcess(command, process); @@ -76,10 +92,7 @@ private static void assertSuccessfulExitCode(Process process, String[] commands) String command = String.join(" ", commands); LOGGER.trace("Git command {} exited with status code {}", command, exitStatus); if (exitStatus != 0) { - String error = combinedOutputs(process); - throw new IOException( - String.format("Expected Git operation %s to succeed but got exit status %d. See error below%n%s", - command, exitStatus, error)); + throw new CommandIOException(command, exitStatus, stdout(process), stderr(process)); } } @@ -93,17 +106,6 @@ private static void waitForProcess(String command, Process process) throws IOExc } } - private static String combinedOutputs(Process process) throws IOException { - StringBuilder builder = new StringBuilder(); - builder.append(String.format("Error stream%n")); - builder.append(stderr(process)); - builder.append("\n"); - builder.append(String.format("Output stream%n")); - builder.append(stdout(process)); - builder.append("\n"); - return builder.toString(); - } - private static String stdout(Process process) throws IOException { return new String(process.getInputStream().readAllBytes(), StandardCharsets.UTF_8); }