summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Wolf <thomas.wolf@paranor.ch>2017-05-08 08:48:40 +0200
committerMatthias Sohn <matthias.sohn@sap.com>2017-05-12 05:11:13 -0400
commit09d96f8d46a31e23fbe5a7b5d8114ae9aaba5056 (patch)
tree18941cf18db8d56e5f552c22153fd2eedbd06b23
parent3f712aa9805940d3ea1dcafa9f4630d9c901d413 (diff)
downloadjgit-09d96f8d46a31e23fbe5a7b5d8114ae9aaba5056.tar.gz
jgit-09d96f8d46a31e23fbe5a7b5d8114ae9aaba5056.zip
Clean up the disk when cloning fails
CloneCommand.call() has three stages: preparation, then the actual clone (init/fetch), and finally maybe checking out the working directory. Restructure such that if we fail or are cancelled during the actual clone (middle phase), we do clean up the disk again. This prevents leaving behind a partial clone in an inconsistent state: either we have a fully successfully built clone, or nothing at all. Bug: 516303 Change-Id: I9b18c60f8f99816d42a3deb7d4a33a9f22eeb709 Signed-off-by: Thomas Wolf <thomas.wolf@paranor.ch>
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java90
1 files changed, 80 insertions, 10 deletions
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java
index 2deff07f1d..63ca4289a0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011, 2013 Chris Aniszczyk <caniszczyk@gmail.com>
+ * Copyright (C) 2011, 2017 Chris Aniszczyk <caniszczyk@gmail.com>
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -76,6 +76,8 @@ import org.eclipse.jgit.transport.RefSpec;
import org.eclipse.jgit.transport.RemoteConfig;
import org.eclipse.jgit.transport.TagOpt;
import org.eclipse.jgit.transport.URIish;
+import org.eclipse.jgit.util.FS;
+import org.eclipse.jgit.util.FileUtils;
/**
* Clone a repository into a new working directory
@@ -109,6 +111,10 @@ public class CloneCommand extends TransportCommand<CloneCommand, Git> {
private Callback callback;
+ private boolean directoryExistsInitially;
+
+ private boolean gitDirExistsInitially;
+
/**
* Callback for status of clone operation.
*
@@ -167,26 +173,51 @@ public class CloneCommand extends TransportCommand<CloneCommand, Git> {
@Override
public Git call() throws GitAPIException, InvalidRemoteException,
org.eclipse.jgit.api.errors.TransportException {
+ URIish u = null;
+ try {
+ u = new URIish(uri);
+ verifyDirectories(u);
+ } catch (URISyntaxException e) {
+ throw new InvalidRemoteException(
+ MessageFormat.format(JGitText.get().invalidURL, uri));
+ }
Repository repository = null;
+ FetchResult fetchResult = null;
try {
- URIish u = new URIish(uri);
- repository = init(u);
- FetchResult result = fetch(repository, u);
- if (!noCheckout)
- checkout(repository, result);
- return new Git(repository, true);
+ repository = init();
+ fetchResult = fetch(repository, u);
} catch (IOException ioe) {
if (repository != null) {
repository.close();
}
+ cleanup();
throw new JGitInternalException(ioe.getMessage(), ioe);
} catch (URISyntaxException e) {
if (repository != null) {
repository.close();
}
+ cleanup();
throw new InvalidRemoteException(MessageFormat.format(
JGitText.get().invalidRemote, remote));
+ } catch (GitAPIException | RuntimeException e) {
+ if (repository != null) {
+ repository.close();
+ }
+ cleanup();
+ throw e;
}
+ if (!noCheckout) {
+ try {
+ checkout(repository, fetchResult);
+ } catch (IOException ioe) {
+ repository.close();
+ throw new JGitInternalException(ioe.getMessage(), ioe);
+ } catch (GitAPIException | RuntimeException e) {
+ repository.close();
+ throw e;
+ }
+ }
+ return new Git(repository, true);
}
private static boolean isNonEmptyDirectory(File dir) {
@@ -197,12 +228,12 @@ public class CloneCommand extends TransportCommand<CloneCommand, Git> {
return false;
}
- private Repository init(URIish u) throws GitAPIException {
- InitCommand command = Git.init();
- command.setBare(bare);
+ private void verifyDirectories(URIish u) {
if (directory == null && gitDir == null) {
directory = new File(u.getHumanishName(), Constants.DOT_GIT);
}
+ directoryExistsInitially = directory != null && directory.exists();
+ gitDirExistsInitially = gitDir != null && gitDir.exists();
validateDirs(directory, gitDir, bare);
if (isNonEmptyDirectory(directory)) {
throw new JGitInternalException(MessageFormat.format(
@@ -212,6 +243,11 @@ public class CloneCommand extends TransportCommand<CloneCommand, Git> {
throw new JGitInternalException(MessageFormat.format(
JGitText.get().cloneNonEmptyDirectory, gitDir.getName()));
}
+ }
+
+ private Repository init() throws GitAPIException {
+ InitCommand command = Git.init();
+ command.setBare(bare);
if (directory != null) {
command.setDirectory(directory);
}
@@ -602,4 +638,38 @@ public class CloneCommand extends TransportCommand<CloneCommand, Git> {
}
}
}
+
+ private void cleanup() {
+ try {
+ if (directory != null) {
+ if (!directoryExistsInitially) {
+ FileUtils.delete(directory, FileUtils.RECURSIVE
+ | FileUtils.SKIP_MISSING | FileUtils.IGNORE_ERRORS);
+ } else {
+ deleteChildren(directory);
+ }
+ }
+ if (gitDir != null) {
+ if (!gitDirExistsInitially) {
+ FileUtils.delete(gitDir, FileUtils.RECURSIVE
+ | FileUtils.SKIP_MISSING | FileUtils.IGNORE_ERRORS);
+ } else {
+ deleteChildren(directory);
+ }
+ }
+ } catch (IOException e) {
+ // Ignore; this is a best-effort cleanup in error cases, and
+ // IOException should not be raised anyway
+ }
+ }
+
+ private void deleteChildren(File file) throws IOException {
+ if (!FS.DETECTED.isDirectory(file)) {
+ return;
+ }
+ for (File child : file.listFiles()) {
+ FileUtils.delete(child, FileUtils.RECURSIVE | FileUtils.SKIP_MISSING
+ | FileUtils.IGNORE_ERRORS);
+ }
+ }
}