diff options
author | Brian <bsilver16384@gmail.com> | 2015-08-19 18:52:13 +0000 |
---|---|---|
committer | Lukacs Berki <lberki@google.com> | 2015-08-20 14:50:03 +0000 |
commit | dfc44a4f14582fdfdebd7f0a6d56ec23fc2ed68f (patch) | |
tree | 73ebd2f4dbf5fd0e2f879451f6ec62a4bcafb050 /src/main/java/com/google/devtools/build | |
parent | f292aa028a63b549a7933d98ab89a2f3a66c110e (diff) |
Fix {new_,}git_repository after shutdown.
Previously, after a server shutdown they would fail because the
repository already exists. This change fixes that and avoids
re-downloading if the local repository is already the correct version
and unmodified.
An example of the error before:
$ bazel fetch ...
.......
INFO: Loading package: tools/cpp
java.lang.RuntimeException: Unrecoverable error while evaluating node 'GIT_CLONE:file:///tmp/gtest-1.7.0 -> /home/brian/.cache/bazel/_bazel_brian/46af2c6e62e13aca9af015360f8fb1da/external/gtest_repo (091e69312f2c133860e71e6b01118c1f5a8c943d) submodules: false' (requested by nodes 'NEW_GIT_REPOSITORY:@gtest_repo')
at com.google.devtools.build.skyframe.ParallelEvaluator$Evaluate.run(ParallelEvaluator.java:745)
at com.google.devtools.build.lib.concurrent.AbstractQueueVisitor$1.run(AbstractQueueVisitor.java:346)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
Caused by: org.eclipse.jgit.api.errors.JGitInternalException: Destination path "gtest_repo" already exists and is not an empty directory
at org.eclipse.jgit.api.CloneCommand.init(CloneCommand.java:152)
at org.eclipse.jgit.api.CloneCommand.call(CloneCommand.java:132)
at com.google.devtools.build.lib.bazel.repository.GitCloneFunction.compute(GitCloneFunction.java:68)
at com.google.devtools.build.skyframe.ParallelEvaluator$Evaluate.run(ParallelEvaluator.java:709)
... 4 more
--
Change-Id: Ifca46302537248106740941b70bc5bd11fa1eeb1
Reviewed-on: https://bazel-review.googlesource.com/#/c/1650
MOS_MIGRATED_REVID=101045419
Diffstat (limited to 'src/main/java/com/google/devtools/build')
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/bazel/repository/GitCloneFunction.java | 74 |
1 files changed, 65 insertions, 9 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/repository/GitCloneFunction.java b/src/main/java/com/google/devtools/build/lib/bazel/repository/GitCloneFunction.java index 2c8e50a684..29c7b729f6 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/repository/GitCloneFunction.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/repository/GitCloneFunction.java @@ -20,6 +20,7 @@ import com.google.devtools.build.lib.packages.AggregatingAttributeMapper; import com.google.devtools.build.lib.packages.Rule; import com.google.devtools.build.lib.packages.Type; import com.google.devtools.build.lib.syntax.EvalException; +import com.google.devtools.build.lib.vfs.FileSystemUtils; import com.google.devtools.build.lib.vfs.Path; import com.google.devtools.build.skyframe.SkyFunction; import com.google.devtools.build.skyframe.SkyFunctionException.Transience; @@ -28,14 +29,19 @@ import com.google.devtools.build.skyframe.SkyKey; import com.google.devtools.build.skyframe.SkyValue; import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.api.Status; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.api.errors.InvalidRefNameException; import org.eclipse.jgit.api.errors.InvalidRemoteException; import org.eclipse.jgit.api.errors.RefNotFoundException; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.storage.file.FileRepositoryBuilder; -import java.io.File; import java.io.IOException; import java.util.Objects; +import java.util.Set; import javax.annotation.Nullable; @@ -51,21 +57,71 @@ public class GitCloneFunction implements SkyFunction { this.reporter = reporter; } + private boolean isUpToDate(GitRepositoryDescriptor descriptor) { + // Initializing/checking status of/etc submodules cleanly is hard, so don't try for now. + if (descriptor.initSubmodules) { + return false; + } + Repository repository = null; + try { + repository = + new FileRepositoryBuilder() + .setGitDir(descriptor.directory.getChild(Constants.DOT_GIT).getPathFile()) + .setMustExist(true) + .build(); + ObjectId head = repository.resolve(Constants.HEAD); + ObjectId checkout = repository.resolve(descriptor.checkout); + if (head != null && checkout != null && head.equals(checkout)) { + Status status = Git.wrap(repository).status().call(); + if (!status.hasUncommittedChanges()) { + // new_git_repository puts (only) BUILD and WORKSPACE, and + // git_repository doesn't add any files. + Set<String> untracked = status.getUntracked(); + if (untracked.isEmpty() + || (untracked.size() == 2 + && untracked.contains("BUILD") + && untracked.contains("WORKSPACE"))) { + return true; + } + } + } + } catch (GitAPIException | IOException e) { + // Any exceptions here, we'll just blow it away and try cloning fresh. + // The fresh clone avoids any weirdness due to what's there and has nicer + // error reporting. + } finally { + if (repository != null) { + repository.close(); + } + } + return false; + } + @Nullable @Override public SkyValue compute(SkyKey skyKey, Environment env) throws RepositoryFunctionException { GitRepositoryDescriptor descriptor = (GitRepositoryDescriptor) skyKey.argument(); - String outputDirectory = descriptor.directory.toString(); Git git = null; try { - git = Git.cloneRepository() - .setURI(descriptor.remote) - .setDirectory(new File(outputDirectory)) - .setCloneSubmodules(false) - .setProgressMonitor( - new GitProgressMonitor("Cloning " + descriptor.remote, reporter)) - .call(); + if (descriptor.directory.exists()) { + if (isUpToDate(descriptor)) { + return new HttpDownloadValue(descriptor.directory); + } + try { + FileSystemUtils.deleteTree(descriptor.directory); + } catch (IOException e) { + throw new RepositoryFunctionException(e, Transience.TRANSIENT); + } + } + git = + Git.cloneRepository() + .setURI(descriptor.remote) + .setDirectory(descriptor.directory.getPathFile()) + .setCloneSubmodules(false) + .setNoCheckout(true) + .setProgressMonitor(new GitProgressMonitor("Cloning " + descriptor.remote, reporter)) + .call(); git.checkout() .setCreateBranch(true) .setName("bazel-checkout") |