Index: pynacl/repo_tools.py |
diff --git a/pynacl/repo_tools.py b/pynacl/repo_tools.py |
index d8e22f76d2d26f10933046af1e463668a335cae9..78da44823558af09769f818b810ba8fe4abbf827 100644 |
--- a/pynacl/repo_tools.py |
+++ b/pynacl/repo_tools.py |
@@ -5,6 +5,7 @@ |
import logging |
import os |
+import posixpath |
import subprocess |
import sys |
import urlparse |
@@ -13,6 +14,9 @@ import file_tools |
import log_tools |
import platform |
+GIT_ALTERNATES_PATH = os.path.join('.git', 'objects', 'info', 'alternates') |
+ |
+ |
class InvalidRepoException(Exception): |
def __init__(self, expected_repo, msg, *args): |
Exception.__init__(self, msg % args) |
@@ -111,44 +115,57 @@ def SyncGitRepo(url, destination, revision, reclone=False, clean=False, |
file_tools.RemoveDirectoryIfPresent(destination) |
if git_cache: |
- fetch_url = GetGitCacheURL(git_cache, url) |
+ git_cache_url = GetGitCacheURL(git_cache, url) |
else: |
- fetch_url = url |
+ git_cache_url = None |
# If the destination is a git repository, validate the tracked origin. |
git_dir = os.path.join(destination, '.git') |
if os.path.exists(git_dir): |
- if not IsURLInRemoteRepoList(fetch_url, destination, include_fetch=True, |
+ if not IsURLInRemoteRepoList(url, destination, include_fetch=True, |
include_push=False): |
- # If the original URL is being tracked instead of the fetch URL, we |
+ # If the git cache URL is being tracked instead of the fetch URL, we |
# can safely redirect it to the fetch URL instead. |
- if (fetch_url != url and IsURLInRemoteRepoList(url, destination, |
- include_fetch=True, |
- include_push=False)): |
- GitSetRemoteRepo(fetch_url, destination, push_url=push_url) |
+ if git_cache_url and IsURLInRemoteRepoList(git_cache_url, destination, |
+ include_fetch=True, |
+ include_push=False): |
+ GitSetRemoteRepo(url, destination, push_url=push_url) |
else: |
logging.error('Git Repo (%s) does not track URL: %s', |
- destination, fetch_url) |
- raise InvalidRepoException(fetch_url, 'Could not sync git repo: %s', |
+ destination, url) |
+ raise InvalidRepoException(url, 'Could not sync git repo: %s', |
destination) |
+ # Make sure the push URL is set correctly as well. |
+ if not IsURLInRemoteRepoList(push_url, destination, include_fetch=False, |
+ include_push=True): |
+ GitSetRemoteRepo(url, destination, push_url=push_url) |
+ |
git = GitCmd() |
if not os.path.exists(git_dir): |
logging.info('Cloning %s...' % url) |
- clone_args = ['clone', '-n'] |
- if git_cache: |
- clone_args.append('-s') |
file_tools.MakeDirectoryIfAbsent(destination) |
- log_tools.CheckCall(git + clone_args + [fetch_url, '.'], cwd=destination) |
+ clone_args = ['clone', '-n'] |
+ if git_cache_url: |
+ clone_args.extend(['--reference', git_cache_url]) |
- if fetch_url != url: |
- GitSetRemoteRepo(fetch_url, destination, push_url=push_url) |
+ log_tools.CheckCall(git + clone_args + [url, '.'], cwd=destination) |
+ if url != push_url: |
+ GitSetRemoteRepo(url, destination, push_url=push_url) |
elif clean: |
log_tools.CheckCall(git + ['clean', '-dffx'], cwd=destination) |
log_tools.CheckCall(git + ['reset', '--hard', 'HEAD'], cwd=destination) |
+ # If a git cache URL is supplied, make sure it is setup as a git alternate. |
+ if git_cache_url: |
+ git_alternates = [git_cache_url] |
+ else: |
+ git_alternates = [] |
+ |
+ GitSetRepoAlternates(destination, git_alternates, append=False) |
+ |
if revision is not None: |
logging.info('Checking out pinned revision...') |
log_tools.CheckCall(git + ['fetch', '--all'], cwd=destination) |
@@ -213,8 +230,8 @@ def GetGitCacheURL(cache_dir, url): |
'-c', cache_dir, |
url]).strip() |
- # For cygwin paths, convert forward slashes to backslashes to mimic URLs. |
- if cygwin_path: |
+ # For windows, make sure the git cache URL is a posix path. |
+ if platform.IsWindows(): |
git_url = git_url.replace('\\', '/') |
return git_url |
@@ -335,3 +352,55 @@ def IsURLInRemoteRepoList(url, directory, include_fetch=True, include_push=True, |
return len([repo_name for |
repo_name, repo_url in remote_repo_list |
if repo_url in valid_urls]) > 0 |
+ |
+ |
+def GitGetRepoAlternates(directory): |
+ """Gets the list of git alternates for a local git repo. |
+ |
+ Args: |
+ directory: Local git repository to get the git alternate for. |
+ |
+ Returns: |
+ List of git alternates set for the local git repository. |
+ """ |
+ git_alternates_file = os.path.join(directory, GIT_ALTERNATES_PATH) |
+ if os.path.isfile(git_alternates_file): |
+ with open(git_alternates_file, 'rt') as f: |
+ alternates_list = [] |
+ for line in f.readlines(): |
+ line = line.strip() |
+ if line: |
+ if posixpath.basename(line) == 'objects': |
+ line = posixpath.dirname(line) |
+ alternates_list.append(line) |
+ |
+ return alternates_list |
+ |
+ return [] |
+ |
+ |
+def GitSetRepoAlternates(directory, alternates_list, append=True): |
+ """Sets the list of git alternates for a local git repo. |
+ |
+ Args: |
+ directory: Local git repository. |
+ alternates_list: List of local git repositories for the git alternates. |
+ append: If True, will append the list to currently set list of alternates. |
+ """ |
+ git_alternates_file = os.path.join(directory, GIT_ALTERNATES_PATH) |
+ git_alternates_dir = os.path.dirname(git_alternates_file) |
+ if not os.path.isdir(git_alternates_dir): |
+ raise InvalidRepoException(directory, |
+ 'Invalid local git repo: %s', directory) |
+ |
+ original_alternates_list = GitGetRepoAlternates(directory) |
+ if append: |
+ alternates_list.extend(original_alternates_list) |
+ alternates_list = sorted(set(alternates_list)) |
+ |
+ if set(original_alternates_list) != set(alternates_list): |
+ lines = [posixpath.join(line, 'objects') + '\n' for line in alternates_list] |
+ logging.info('Setting git alternates:\n\t%s', '\t'.join(lines)) |
+ |
+ with open(git_alternates_file, 'wb') as f: |
+ f.writelines(lines) |