| Index: utils/pub/git_source.dart
|
| diff --git a/utils/pub/git_source.dart b/utils/pub/git_source.dart
|
| index ae7f53b1a2489d61a1cf4f9c7fefc5ba08121597..90f619bb500e26dd166af3e997f6cd4b4898bc2d 100644
|
| --- a/utils/pub/git_source.dart
|
| +++ b/utils/pub/git_source.dart
|
| @@ -7,6 +7,7 @@
|
| #import('io.dart');
|
| #import('package.dart');
|
| #import('source.dart');
|
| +#import('source_registry.dart');
|
| #import('utils.dart');
|
|
|
| /**
|
| @@ -15,32 +16,44 @@
|
| class GitSource extends Source {
|
| final String name = "git";
|
|
|
| - // TODO(rnystrom): Git packages could in theory be cached, but that adds a
|
| - // lot of complexity. When you install a git package, you are installing
|
| - // and pinning to a specific commit. That means different installs of the
|
| - // same git path but at different commits need to be disambiguated in the
|
| - // system cache. It may also lead to a lot of garbage in the system cache.
|
| - // For now, we are punting and simply not caching them.
|
| - final bool shouldCache = false;
|
| + final bool shouldCache = true;
|
|
|
| GitSource();
|
|
|
| /**
|
| * Clones a Git repo to the local filesystem.
|
| + *
|
| + * The Git cache directory is a little idiosyncratic. At the top level, it
|
| + * contains a directory for each commit of each repository, named `<package
|
| + * name>-<commit hash>`. These are the canonical package directories that are
|
| + * linked to from the `packages/` directory.
|
| + *
|
| + * In addition, the Git system cache contains a subdirectory named `cache/`
|
| + * which contains a directory for each separate repository URL, named
|
| + * `<package name>-<url hash>`. These are used to check out the repository
|
| + * itself; each of the commit-specific directories are clones of a directory
|
| + * in `cache/`.
|
| */
|
| - Future<bool> install(PackageId id, String destPath) {
|
| + Future<Package> installToSystemCache(PackageId id) {
|
| + var revisionCachePath;
|
| +
|
| return isGitInstalled.chain((installed) {
|
| - if (installed) {
|
| - return runProcess("git",
|
| - ["clone", "--progress", id.description, destPath],
|
| - pipeStdout: true, pipeStderr: true).
|
| - transform((result) => result.success);
|
| - } else {
|
| + if (!installed) {
|
| throw new Exception(
|
| "Cannot install '${id.name}' from Git (${id.description}).\n"
|
| "Please ensure Git is correctly installed.");
|
| }
|
| - });
|
| +
|
| + return ensureDir(join(systemCacheRoot, 'cache'));
|
| + }).chain((_) => _ensureRepoCache(id))
|
| + .chain((_) => _revisionCachePath(id, "HEAD"))
|
| + .chain((path) {
|
| + revisionCachePath = path;
|
| + return exists(revisionCachePath);
|
| + }).chain((exists) {
|
| + if (exists) return new Future.immediate(null);
|
| + return _clone(_repoCachePath(id), revisionCachePath);
|
| + }).chain((_) => Package.load(revisionCachePath, systemCache.sources));
|
| }
|
|
|
| /**
|
| @@ -58,4 +71,66 @@ class GitSource extends Source {
|
| throw new FormatException("The description must be a git URL.");
|
| }
|
| }
|
| +
|
| + /**
|
| + * Ensure that the canonical clone of the repository referred to by [id] (the
|
| + * one in `<system cache>/git/cache`) exists and is up-to-date. Returns a
|
| + * future that completes once this is finished and throws an exception if it
|
| + * fails.
|
| + */
|
| + Future _ensureRepoCache(PackageId id) {
|
| + var path = _repoCachePath(id);
|
| + return exists(path).chain((exists) {
|
| + if (!exists) return _clone(id.description, path);
|
| +
|
| + return runProcess("git", ["pull", "--force"], workingDir: path,
|
| + pipeStdout: true, pipeStderr: true).transform((result) {
|
| + if (!result.success) throw 'Git failed.';
|
| + return null;
|
| + });
|
| + });
|
| + }
|
| +
|
| + /**
|
| + * Returns a future that completes to the revision hash of the repository for
|
| + * [id] at [ref], which can be any Git ref.
|
| + */
|
| + Future<String> _revisionAt(PackageId id, String ref) {
|
| + return runProcess("git", ["rev-parse", ref],
|
| + workingDir: _repoCachePath(id), pipeStderr: true).transform((result) {
|
| + if (!result.success) throw 'Git failed.';
|
| + return result.stdout[0];
|
| + });
|
| + }
|
| +
|
| + /**
|
| + * Returns the path to the revision-specific cache of [id] at [ref], which can
|
| + * be any Git ref.
|
| + */
|
| + Future<String> _revisionCachePath(PackageId id, String ref) {
|
| + return _revisionAt(id, ref).transform((rev) {
|
| + var revisionCacheName = '${id.name}-$rev';
|
| + return join(systemCacheRoot, revisionCacheName);
|
| + });
|
| + }
|
| +
|
| + /**
|
| + * Clones the repo at the URI [from] to the path [to] on the local filesystem.
|
| + */
|
| + Future _clone(String from, String to) {
|
| + return runProcess("git", ["clone", from, to], pipeStdout: true,
|
| + pipeStderr: true).transform((result) {
|
| + if (!result.success) throw 'Git failed.';
|
| + return null;
|
| + });
|
| + }
|
| +
|
| + /**
|
| + * Returns the path to the canonical clone of the repository referred to by
|
| + * [id] (the one in `<system cache>/git/cache`).
|
| + */
|
| + String _repoCachePath(PackageId id) {
|
| + var repoCacheName = '${id.name}-${sha1(id.description)}';
|
| + return join(systemCacheRoot, 'cache', repoCacheName);
|
| + }
|
| }
|
|
|