diff options
| -rw-r--r-- | docs/manifest-format.txt | 12 | ||||
| -rw-r--r-- | git_command.py | 2 | ||||
| -rw-r--r-- | git_config.py | 4 | ||||
| -rw-r--r-- | git_refs.py | 2 | ||||
| -rwxr-xr-x | hooks/commit-msg | 4 | ||||
| -rw-r--r-- | manifest_xml.py | 28 | ||||
| -rw-r--r-- | project.py | 75 | ||||
| -rwxr-xr-x | repo | 36 | ||||
| -rw-r--r-- | subcmds/branches.py | 2 | ||||
| -rw-r--r-- | subcmds/init.py | 21 | ||||
| -rw-r--r-- | subcmds/sync.py | 13 | ||||
| -rw-r--r-- | subcmds/upload.py | 5 |
12 files changed, 154 insertions, 50 deletions
diff --git a/docs/manifest-format.txt b/docs/manifest-format.txt index dcc90d07..e48b75fe 100644 --- a/docs/manifest-format.txt +++ b/docs/manifest-format.txt | |||
| @@ -27,15 +27,15 @@ following DTD: | |||
| 27 | remove-project*, | 27 | remove-project*, |
| 28 | project*, | 28 | project*, |
| 29 | repo-hooks?)> | 29 | repo-hooks?)> |
| 30 | 30 | ||
| 31 | <!ELEMENT notice (#PCDATA)> | 31 | <!ELEMENT notice (#PCDATA)> |
| 32 | 32 | ||
| 33 | <!ELEMENT remote (EMPTY)> | 33 | <!ELEMENT remote (EMPTY)> |
| 34 | <!ATTLIST remote name ID #REQUIRED> | 34 | <!ATTLIST remote name ID #REQUIRED> |
| 35 | <!ATTLIST remote alias CDATA #IMPLIED> | 35 | <!ATTLIST remote alias CDATA #IMPLIED> |
| 36 | <!ATTLIST remote fetch CDATA #REQUIRED> | 36 | <!ATTLIST remote fetch CDATA #REQUIRED> |
| 37 | <!ATTLIST remote review CDATA #IMPLIED> | 37 | <!ATTLIST remote review CDATA #IMPLIED> |
| 38 | 38 | ||
| 39 | <!ELEMENT default (EMPTY)> | 39 | <!ELEMENT default (EMPTY)> |
| 40 | <!ATTLIST default remote IDREF #IMPLIED> | 40 | <!ATTLIST default remote IDREF #IMPLIED> |
| 41 | <!ATTLIST default revision CDATA #IMPLIED> | 41 | <!ATTLIST default revision CDATA #IMPLIED> |
| @@ -46,8 +46,8 @@ following DTD: | |||
| 46 | 46 | ||
| 47 | <!ELEMENT manifest-server (EMPTY)> | 47 | <!ELEMENT manifest-server (EMPTY)> |
| 48 | <!ATTLIST url CDATA #REQUIRED> | 48 | <!ATTLIST url CDATA #REQUIRED> |
| 49 | 49 | ||
| 50 | <!ELEMENT project (annotation?, | 50 | <!ELEMENT project (annotation*, |
| 51 | project*)> | 51 | project*)> |
| 52 | <!ATTLIST project name CDATA #REQUIRED> | 52 | <!ATTLIST project name CDATA #REQUIRED> |
| 53 | <!ATTLIST project path CDATA #IMPLIED> | 53 | <!ATTLIST project path CDATA #IMPLIED> |
| @@ -65,7 +65,7 @@ following DTD: | |||
| 65 | <!ATTLIST annotation name CDATA #REQUIRED> | 65 | <!ATTLIST annotation name CDATA #REQUIRED> |
| 66 | <!ATTLIST annotation value CDATA #REQUIRED> | 66 | <!ATTLIST annotation value CDATA #REQUIRED> |
| 67 | <!ATTLIST annotation keep CDATA "true"> | 67 | <!ATTLIST annotation keep CDATA "true"> |
| 68 | 68 | ||
| 69 | <!ELEMENT remove-project (EMPTY)> | 69 | <!ELEMENT remove-project (EMPTY)> |
| 70 | <!ATTLIST remove-project name CDATA #REQUIRED> | 70 | <!ATTLIST remove-project name CDATA #REQUIRED> |
| 71 | 71 | ||
diff --git a/git_command.py b/git_command.py index d347dd61..51f5e3c0 100644 --- a/git_command.py +++ b/git_command.py | |||
| @@ -86,7 +86,7 @@ class _GitCall(object): | |||
| 86 | global _git_version | 86 | global _git_version |
| 87 | 87 | ||
| 88 | if _git_version is None: | 88 | if _git_version is None: |
| 89 | ver_str = git.version() | 89 | ver_str = git.version().decode('utf-8') |
| 90 | if ver_str.startswith('git version '): | 90 | if ver_str.startswith('git version '): |
| 91 | _git_version = tuple( | 91 | _git_version = tuple( |
| 92 | map(int, | 92 | map(int, |
diff --git a/git_config.py b/git_config.py index a294a0b6..f6093a25 100644 --- a/git_config.py +++ b/git_config.py | |||
| @@ -304,8 +304,8 @@ class GitConfig(object): | |||
| 304 | d = self._do('--null', '--list') | 304 | d = self._do('--null', '--list') |
| 305 | if d is None: | 305 | if d is None: |
| 306 | return c | 306 | return c |
| 307 | for line in d.rstrip('\0').split('\0'): # pylint: disable=W1401 | 307 | for line in d.decode('utf-8').rstrip('\0').split('\0'): # pylint: disable=W1401 |
| 308 | # Backslash is not anomalous | 308 | # Backslash is not anomalous |
| 309 | if '\n' in line: | 309 | if '\n' in line: |
| 310 | key, val = line.split('\n', 1) | 310 | key, val = line.split('\n', 1) |
| 311 | else: | 311 | else: |
diff --git a/git_refs.py b/git_refs.py index 4dd68769..3c266061 100644 --- a/git_refs.py +++ b/git_refs.py | |||
| @@ -100,7 +100,7 @@ class GitRefs(object): | |||
| 100 | def _ReadPackedRefs(self): | 100 | def _ReadPackedRefs(self): |
| 101 | path = os.path.join(self._gitdir, 'packed-refs') | 101 | path = os.path.join(self._gitdir, 'packed-refs') |
| 102 | try: | 102 | try: |
| 103 | fd = open(path, 'rb') | 103 | fd = open(path, 'r') |
| 104 | mtime = os.path.getmtime(path) | 104 | mtime = os.path.getmtime(path) |
| 105 | except IOError: | 105 | except IOError: |
| 106 | return | 106 | return |
diff --git a/hooks/commit-msg b/hooks/commit-msg index b37dfaa4..5ca2b112 100755 --- a/hooks/commit-msg +++ b/hooks/commit-msg | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | #!/bin/sh | 1 | #!/bin/sh |
| 2 | # From Gerrit Code Review 2.5.2 | 2 | # From Gerrit Code Review 2.6 |
| 3 | # | 3 | # |
| 4 | # Part of Gerrit Code Review (http://code.google.com/p/gerrit/) | 4 | # Part of Gerrit Code Review (http://code.google.com/p/gerrit/) |
| 5 | # | 5 | # |
| @@ -154,7 +154,7 @@ add_ChangeId() { | |||
| 154 | if (unprinted) { | 154 | if (unprinted) { |
| 155 | print "Change-Id: I'"$id"'" | 155 | print "Change-Id: I'"$id"'" |
| 156 | } | 156 | } |
| 157 | }' "$MSG" > $T && mv $T "$MSG" || rm -f $T | 157 | }' "$MSG" > "$T" && mv "$T" "$MSG" || rm -f "$T" |
| 158 | } | 158 | } |
| 159 | _gen_ChangeIdInput() { | 159 | _gen_ChangeIdInput() { |
| 160 | echo "tree `git write-tree`" | 160 | echo "tree `git write-tree`" |
diff --git a/manifest_xml.py b/manifest_xml.py index 647e89f9..c5f3bcc9 100644 --- a/manifest_xml.py +++ b/manifest_xml.py | |||
| @@ -51,6 +51,12 @@ class _Default(object): | |||
| 51 | sync_c = False | 51 | sync_c = False |
| 52 | sync_s = False | 52 | sync_s = False |
| 53 | 53 | ||
| 54 | def __eq__(self, other): | ||
| 55 | return self.__dict__ == other.__dict__ | ||
| 56 | |||
| 57 | def __ne__(self, other): | ||
| 58 | return self.__dict__ != other.__dict__ | ||
| 59 | |||
| 54 | class _XmlRemote(object): | 60 | class _XmlRemote(object): |
| 55 | def __init__(self, | 61 | def __init__(self, |
| 56 | name, | 62 | name, |
| @@ -92,7 +98,7 @@ class _XmlRemote(object): | |||
| 92 | url = self.resolvedFetchUrl.rstrip('/') + '/' + projectName | 98 | url = self.resolvedFetchUrl.rstrip('/') + '/' + projectName |
| 93 | remoteName = self.name | 99 | remoteName = self.name |
| 94 | if self.remoteAlias: | 100 | if self.remoteAlias: |
| 95 | remoteName = self.remoteAlias | 101 | remoteName = self.remoteAlias |
| 96 | return RemoteSpec(remoteName, url, self.reviewUrl) | 102 | return RemoteSpec(remoteName, url, self.reviewUrl) |
| 97 | 103 | ||
| 98 | class XmlManifest(object): | 104 | class XmlManifest(object): |
| @@ -228,7 +234,9 @@ class XmlManifest(object): | |||
| 228 | e.setAttribute('name', name) | 234 | e.setAttribute('name', name) |
| 229 | if relpath != name: | 235 | if relpath != name: |
| 230 | e.setAttribute('path', relpath) | 236 | e.setAttribute('path', relpath) |
| 231 | remoteName = d.remote.remoteAlias or d.remote.name | 237 | remoteName = None |
| 238 | if d.remote: | ||
| 239 | remoteName = d.remote.remoteAlias or d.remote.name | ||
| 232 | if not d.remote or p.remote.name != remoteName: | 240 | if not d.remote or p.remote.name != remoteName: |
| 233 | e.setAttribute('remote', p.remote.name) | 241 | e.setAttribute('remote', p.remote.name) |
| 234 | if peg_rev: | 242 | if peg_rev: |
| @@ -325,6 +333,10 @@ class XmlManifest(object): | |||
| 325 | def IsMirror(self): | 333 | def IsMirror(self): |
| 326 | return self.manifestProject.config.GetBoolean('repo.mirror') | 334 | return self.manifestProject.config.GetBoolean('repo.mirror') |
| 327 | 335 | ||
| 336 | @property | ||
| 337 | def IsArchive(self): | ||
| 338 | return self.manifestProject.config.GetBoolean('repo.archive') | ||
| 339 | |||
| 328 | def _Unload(self): | 340 | def _Unload(self): |
| 329 | self._loaded = False | 341 | self._loaded = False |
| 330 | self._projects = {} | 342 | self._projects = {} |
| @@ -432,11 +444,13 @@ class XmlManifest(object): | |||
| 432 | 444 | ||
| 433 | for node in itertools.chain(*node_list): | 445 | for node in itertools.chain(*node_list): |
| 434 | if node.nodeName == 'default': | 446 | if node.nodeName == 'default': |
| 435 | if self._default is not None: | 447 | new_default = self._ParseDefault(node) |
| 436 | raise ManifestParseError( | 448 | if self._default is None: |
| 437 | 'duplicate default in %s' % | 449 | self._default = new_default |
| 438 | (self.manifestFile)) | 450 | elif new_default != self._default: |
| 439 | self._default = self._ParseDefault(node) | 451 | raise ManifestParseError('duplicate default in %s' % |
| 452 | (self.manifestFile)) | ||
| 453 | |||
| 440 | if self._default is None: | 454 | if self._default is None: |
| 441 | self._default = _Default() | 455 | self._default = _Default() |
| 442 | 456 | ||
| @@ -23,6 +23,7 @@ import shutil | |||
| 23 | import stat | 23 | import stat |
| 24 | import subprocess | 24 | import subprocess |
| 25 | import sys | 25 | import sys |
| 26 | import tarfile | ||
| 26 | import tempfile | 27 | import tempfile |
| 27 | import time | 28 | import time |
| 28 | 29 | ||
| @@ -82,7 +83,7 @@ def _ProjectHooks(): | |||
| 82 | """ | 83 | """ |
| 83 | global _project_hook_list | 84 | global _project_hook_list |
| 84 | if _project_hook_list is None: | 85 | if _project_hook_list is None: |
| 85 | d = os.path.abspath(os.path.dirname(__file__)) | 86 | d = os.path.realpath(os.path.abspath(os.path.dirname(__file__))) |
| 86 | d = os.path.join(d , 'hooks') | 87 | d = os.path.join(d , 'hooks') |
| 87 | _project_hook_list = [os.path.join(d, x) for x in os.listdir(d)] | 88 | _project_hook_list = [os.path.join(d, x) for x in os.listdir(d)] |
| 88 | return _project_hook_list | 89 | return _project_hook_list |
| @@ -986,15 +987,62 @@ class Project(object): | |||
| 986 | 987 | ||
| 987 | ## Sync ## | 988 | ## Sync ## |
| 988 | 989 | ||
| 990 | def _ExtractArchive(self, tarpath, path=None): | ||
| 991 | """Extract the given tar on its current location | ||
| 992 | |||
| 993 | Args: | ||
| 994 | - tarpath: The path to the actual tar file | ||
| 995 | |||
| 996 | """ | ||
| 997 | try: | ||
| 998 | with tarfile.open(tarpath, 'r') as tar: | ||
| 999 | tar.extractall(path=path) | ||
| 1000 | return True | ||
| 1001 | except (IOError, tarfile.TarError) as e: | ||
| 1002 | print("error: Cannot extract archive %s: " | ||
| 1003 | "%s" % (tarpath, str(e)), file=sys.stderr) | ||
| 1004 | return False | ||
| 1005 | |||
| 989 | def Sync_NetworkHalf(self, | 1006 | def Sync_NetworkHalf(self, |
| 990 | quiet=False, | 1007 | quiet=False, |
| 991 | is_new=None, | 1008 | is_new=None, |
| 992 | current_branch_only=False, | 1009 | current_branch_only=False, |
| 993 | clone_bundle=True, | 1010 | clone_bundle=True, |
| 994 | no_tags=False): | 1011 | no_tags=False, |
| 1012 | archive=False): | ||
| 995 | """Perform only the network IO portion of the sync process. | 1013 | """Perform only the network IO portion of the sync process. |
| 996 | Local working directory/branch state is not affected. | 1014 | Local working directory/branch state is not affected. |
| 997 | """ | 1015 | """ |
| 1016 | if archive and not isinstance(self, MetaProject): | ||
| 1017 | if self.remote.url.startswith(('http://', 'https://')): | ||
| 1018 | print("error: %s: Cannot fetch archives from http/https " | ||
| 1019 | "remotes." % self.name, file=sys.stderr) | ||
| 1020 | return False | ||
| 1021 | |||
| 1022 | name = self.relpath.replace('\\', '/') | ||
| 1023 | name = name.replace('/', '_') | ||
| 1024 | tarpath = '%s.tar' % name | ||
| 1025 | topdir = self.manifest.topdir | ||
| 1026 | |||
| 1027 | try: | ||
| 1028 | self._FetchArchive(tarpath, cwd=topdir) | ||
| 1029 | except GitError as e: | ||
| 1030 | print('error: %s' % str(e), file=sys.stderr) | ||
| 1031 | return False | ||
| 1032 | |||
| 1033 | # From now on, we only need absolute tarpath | ||
| 1034 | tarpath = os.path.join(topdir, tarpath) | ||
| 1035 | |||
| 1036 | if not self._ExtractArchive(tarpath, path=topdir): | ||
| 1037 | return False | ||
| 1038 | try: | ||
| 1039 | os.remove(tarpath) | ||
| 1040 | except OSError as e: | ||
| 1041 | print("warn: Cannot remove archive %s: " | ||
| 1042 | "%s" % (tarpath, str(e)), file=sys.stderr) | ||
| 1043 | self._CopyFiles() | ||
| 1044 | return True | ||
| 1045 | |||
| 998 | if is_new is None: | 1046 | if is_new is None: |
| 999 | is_new = not self.Exists | 1047 | is_new = not self.Exists |
| 1000 | if is_new: | 1048 | if is_new: |
| @@ -1169,7 +1217,7 @@ class Project(object): | |||
| 1169 | last_mine = None | 1217 | last_mine = None |
| 1170 | cnt_mine = 0 | 1218 | cnt_mine = 0 |
| 1171 | for commit in local_changes: | 1219 | for commit in local_changes: |
| 1172 | commit_id, committer_email = commit.split(' ', 1) | 1220 | commit_id, committer_email = commit.decode('utf-8').split(' ', 1) |
| 1173 | if committer_email == self.UserEmail: | 1221 | if committer_email == self.UserEmail: |
| 1174 | last_mine = commit_id | 1222 | last_mine = commit_id |
| 1175 | cnt_mine += 1 | 1223 | cnt_mine += 1 |
| @@ -1580,6 +1628,19 @@ class Project(object): | |||
| 1580 | 1628 | ||
| 1581 | ## Direct Git Commands ## | 1629 | ## Direct Git Commands ## |
| 1582 | 1630 | ||
| 1631 | def _FetchArchive(self, tarpath, cwd=None): | ||
| 1632 | cmd = ['archive', '-v', '-o', tarpath] | ||
| 1633 | cmd.append('--remote=%s' % self.remote.url) | ||
| 1634 | cmd.append('--prefix=%s/' % self.relpath) | ||
| 1635 | cmd.append(self.revisionExpr) | ||
| 1636 | |||
| 1637 | command = GitCommand(self, cmd, cwd=cwd, | ||
| 1638 | capture_stdout=True, | ||
| 1639 | capture_stderr=True) | ||
| 1640 | |||
| 1641 | if command.Wait() != 0: | ||
| 1642 | raise GitError('git archive %s: %s' % (self.name, command.stderr)) | ||
| 1643 | |||
| 1583 | def _RemoteFetch(self, name=None, | 1644 | def _RemoteFetch(self, name=None, |
| 1584 | current_branch_only=False, | 1645 | current_branch_only=False, |
| 1585 | initial=False, | 1646 | initial=False, |
| @@ -1847,11 +1908,11 @@ class Project(object): | |||
| 1847 | cookiefile = line[len(prefix):] | 1908 | cookiefile = line[len(prefix):] |
| 1848 | break | 1909 | break |
| 1849 | if p.wait(): | 1910 | if p.wait(): |
| 1850 | line = iter(p.stderr).next() | 1911 | err_msg = p.stderr.read() |
| 1851 | if ' -print_config' in line: | 1912 | if ' -print_config' in err_msg: |
| 1852 | pass # Persistent proxy doesn't support -print_config. | 1913 | pass # Persistent proxy doesn't support -print_config. |
| 1853 | else: | 1914 | else: |
| 1854 | print(line + p.stderr.read(), file=sys.stderr) | 1915 | print(err_msg, file=sys.stderr) |
| 1855 | if cookiefile: | 1916 | if cookiefile: |
| 1856 | return cookiefile | 1917 | return cookiefile |
| 1857 | except OSError as e: | 1918 | except OSError as e: |
| @@ -1971,7 +2032,7 @@ class Project(object): | |||
| 1971 | self._InitHooks() | 2032 | self._InitHooks() |
| 1972 | 2033 | ||
| 1973 | def _InitHooks(self): | 2034 | def _InitHooks(self): |
| 1974 | hooks = self._gitdir_path('hooks') | 2035 | hooks = os.path.realpath(self._gitdir_path('hooks')) |
| 1975 | if not os.path.exists(hooks): | 2036 | if not os.path.exists(hooks): |
| 1976 | os.makedirs(hooks) | 2037 | os.makedirs(hooks) |
| 1977 | for stock_hook in _ProjectHooks(): | 2038 | for stock_hook in _ProjectHooks(): |
| @@ -110,6 +110,7 @@ REPO_MAIN = S_repo + '/main.py' # main script | |||
| 110 | MIN_PYTHON_VERSION = (2, 6) # minimum supported python version | 110 | MIN_PYTHON_VERSION = (2, 6) # minimum supported python version |
| 111 | 111 | ||
| 112 | 112 | ||
| 113 | import errno | ||
| 113 | import optparse | 114 | import optparse |
| 114 | import os | 115 | import os |
| 115 | import re | 116 | import re |
| @@ -138,10 +139,9 @@ def _print(*objects, **kwargs): | |||
| 138 | # Python version check | 139 | # Python version check |
| 139 | ver = sys.version_info | 140 | ver = sys.version_info |
| 140 | if ver[0] == 3: | 141 | if ver[0] == 3: |
| 141 | _print('error: Python 3 support is not fully implemented in repo yet.\n' | 142 | _print('warning: Python 3 support is currently experimental. YMMV.\n' |
| 142 | 'Please use Python 2.6 - 2.7 instead.', | 143 | 'Please use Python 2.6 - 2.7 instead.', |
| 143 | file=sys.stderr) | 144 | file=sys.stderr) |
| 144 | sys.exit(1) | ||
| 145 | if (ver[0], ver[1]) < MIN_PYTHON_VERSION: | 145 | if (ver[0], ver[1]) < MIN_PYTHON_VERSION: |
| 146 | _print('error: Python version %s unsupported.\n' | 146 | _print('error: Python version %s unsupported.\n' |
| 147 | 'Please use Python 2.6 - 2.7 instead.' | 147 | 'Please use Python 2.6 - 2.7 instead.' |
| @@ -181,6 +181,10 @@ group.add_option('--reference', | |||
| 181 | group.add_option('--depth', type='int', default=None, | 181 | group.add_option('--depth', type='int', default=None, |
| 182 | dest='depth', | 182 | dest='depth', |
| 183 | help='create a shallow clone with given depth; see git clone') | 183 | help='create a shallow clone with given depth; see git clone') |
| 184 | group.add_option('--archive', | ||
| 185 | dest='archive', action='store_true', | ||
| 186 | help='checkout an archive instead of a git repository for ' | ||
| 187 | 'each project. See git archive.') | ||
| 184 | group.add_option('-g', '--groups', | 188 | group.add_option('-g', '--groups', |
| 185 | dest='groups', default='default', | 189 | dest='groups', default='default', |
| 186 | help='restrict manifest projects to ones with specified ' | 190 | help='restrict manifest projects to ones with specified ' |
| @@ -240,10 +244,10 @@ def _Init(args): | |||
| 240 | _print("fatal: invalid branch name '%s'" % branch, file=sys.stderr) | 244 | _print("fatal: invalid branch name '%s'" % branch, file=sys.stderr) |
| 241 | raise CloneFailure() | 245 | raise CloneFailure() |
| 242 | 246 | ||
| 243 | if not os.path.isdir(repodir): | 247 | try: |
| 244 | try: | 248 | os.mkdir(repodir) |
| 245 | os.mkdir(repodir) | 249 | except OSError as e: |
| 246 | except OSError as e: | 250 | if e.errno != errno.EEXIST: |
| 247 | _print('fatal: cannot make %s directory: %s' | 251 | _print('fatal: cannot make %s directory: %s' |
| 248 | % (repodir, e.strerror), file=sys.stderr) | 252 | % (repodir, e.strerror), file=sys.stderr) |
| 249 | # Don't raise CloneFailure; that would delete the | 253 | # Don't raise CloneFailure; that would delete the |
| @@ -322,18 +326,18 @@ def NeedSetupGnuPG(): | |||
| 322 | 326 | ||
| 323 | 327 | ||
| 324 | def SetupGnuPG(quiet): | 328 | def SetupGnuPG(quiet): |
| 325 | if not os.path.isdir(home_dot_repo): | 329 | try: |
| 326 | try: | 330 | os.mkdir(home_dot_repo) |
| 327 | os.mkdir(home_dot_repo) | 331 | except OSError as e: |
| 328 | except OSError as e: | 332 | if e.errno != errno.EEXIST: |
| 329 | _print('fatal: cannot make %s directory: %s' | 333 | _print('fatal: cannot make %s directory: %s' |
| 330 | % (home_dot_repo, e.strerror), file=sys.stderr) | 334 | % (home_dot_repo, e.strerror), file=sys.stderr) |
| 331 | sys.exit(1) | 335 | sys.exit(1) |
| 332 | 336 | ||
| 333 | if not os.path.isdir(gpg_dir): | 337 | try: |
| 334 | try: | 338 | os.mkdir(gpg_dir, stat.S_IRWXU) |
| 335 | os.mkdir(gpg_dir, stat.S_IRWXU) | 339 | except OSError as e: |
| 336 | except OSError as e: | 340 | if e.errno != errno.EEXIST: |
| 337 | _print('fatal: cannot make %s directory: %s' % (gpg_dir, e.strerror), | 341 | _print('fatal: cannot make %s directory: %s' % (gpg_dir, e.strerror), |
| 338 | file=sys.stderr) | 342 | file=sys.stderr) |
| 339 | sys.exit(1) | 343 | sys.exit(1) |
| @@ -739,7 +743,7 @@ def main(orig_args): | |||
| 739 | repo_main = my_main | 743 | repo_main = my_main |
| 740 | 744 | ||
| 741 | ver_str = '.'.join(map(str, VERSION)) | 745 | ver_str = '.'.join(map(str, VERSION)) |
| 742 | me = [repo_main, | 746 | me = [sys.executable, repo_main, |
| 743 | '--repo-dir=%s' % rel_repo_dir, | 747 | '--repo-dir=%s' % rel_repo_dir, |
| 744 | '--wrapper-version=%s' % ver_str, | 748 | '--wrapper-version=%s' % ver_str, |
| 745 | '--wrapper-path=%s' % wrapper_path, | 749 | '--wrapper-path=%s' % wrapper_path, |
| @@ -747,7 +751,7 @@ def main(orig_args): | |||
| 747 | me.extend(orig_args) | 751 | me.extend(orig_args) |
| 748 | me.extend(extra_args) | 752 | me.extend(extra_args) |
| 749 | try: | 753 | try: |
| 750 | os.execv(repo_main, me) | 754 | os.execv(sys.executable, me) |
| 751 | except OSError as e: | 755 | except OSError as e: |
| 752 | _print("fatal: unable to start %s" % repo_main, file=sys.stderr) | 756 | _print("fatal: unable to start %s" % repo_main, file=sys.stderr) |
| 753 | _print("fatal: %s" % e, file=sys.stderr) | 757 | _print("fatal: %s" % e, file=sys.stderr) |
diff --git a/subcmds/branches.py b/subcmds/branches.py index c2e7c4b9..f714c1e8 100644 --- a/subcmds/branches.py +++ b/subcmds/branches.py | |||
| @@ -139,7 +139,7 @@ is shown, then the branch appears in all projects. | |||
| 139 | if in_cnt < project_cnt: | 139 | if in_cnt < project_cnt: |
| 140 | fmt = out.write | 140 | fmt = out.write |
| 141 | paths = [] | 141 | paths = [] |
| 142 | if in_cnt < project_cnt - in_cnt: | 142 | if in_cnt < project_cnt - in_cnt: |
| 143 | in_type = 'in' | 143 | in_type = 'in' |
| 144 | for b in i.projects: | 144 | for b in i.projects: |
| 145 | paths.append(b.project.relpath) | 145 | paths.append(b.project.relpath) |
diff --git a/subcmds/init.py b/subcmds/init.py index a44fb7a9..b1fcb69c 100644 --- a/subcmds/init.py +++ b/subcmds/init.py | |||
| @@ -99,6 +99,10 @@ to update the working directory files. | |||
| 99 | g.add_option('--depth', type='int', default=None, | 99 | g.add_option('--depth', type='int', default=None, |
| 100 | dest='depth', | 100 | dest='depth', |
| 101 | help='create a shallow clone with given depth; see git clone') | 101 | help='create a shallow clone with given depth; see git clone') |
| 102 | g.add_option('--archive', | ||
| 103 | dest='archive', action='store_true', | ||
| 104 | help='checkout an archive instead of a git repository for ' | ||
| 105 | 'each project. See git archive.') | ||
| 102 | g.add_option('-g', '--groups', | 106 | g.add_option('-g', '--groups', |
| 103 | dest='groups', default='default', | 107 | dest='groups', default='default', |
| 104 | help='restrict manifest projects to ones with specified ' | 108 | help='restrict manifest projects to ones with specified ' |
| @@ -198,6 +202,16 @@ to update the working directory files. | |||
| 198 | if opt.reference: | 202 | if opt.reference: |
| 199 | m.config.SetString('repo.reference', opt.reference) | 203 | m.config.SetString('repo.reference', opt.reference) |
| 200 | 204 | ||
| 205 | if opt.archive: | ||
| 206 | if is_new: | ||
| 207 | m.config.SetString('repo.archive', 'true') | ||
| 208 | else: | ||
| 209 | print('fatal: --archive is only supported when initializing a new ' | ||
| 210 | 'workspace.', file=sys.stderr) | ||
| 211 | print('Either delete the .repo folder in this workspace, or initialize ' | ||
| 212 | 'in another location.', file=sys.stderr) | ||
| 213 | sys.exit(1) | ||
| 214 | |||
| 201 | if opt.mirror: | 215 | if opt.mirror: |
| 202 | if is_new: | 216 | if is_new: |
| 203 | m.config.SetString('repo.mirror', 'true') | 217 | m.config.SetString('repo.mirror', 'true') |
| @@ -366,6 +380,13 @@ to update the working directory files. | |||
| 366 | if opt.reference: | 380 | if opt.reference: |
| 367 | opt.reference = os.path.expanduser(opt.reference) | 381 | opt.reference = os.path.expanduser(opt.reference) |
| 368 | 382 | ||
| 383 | # Check this here, else manifest will be tagged "not new" and init won't be | ||
| 384 | # possible anymore without removing the .repo/manifests directory. | ||
| 385 | if opt.archive and opt.mirror: | ||
| 386 | print('fatal: --mirror and --archive cannot be used together.', | ||
| 387 | file=sys.stderr) | ||
| 388 | sys.exit(1) | ||
| 389 | |||
| 369 | self._SyncManifest(opt) | 390 | self._SyncManifest(opt) |
| 370 | self._LinkManifest(opt.manifest_name) | 391 | self._LinkManifest(opt.manifest_name) |
| 371 | 392 | ||
diff --git a/subcmds/sync.py b/subcmds/sync.py index d1a06412..5e7385db 100644 --- a/subcmds/sync.py +++ b/subcmds/sync.py | |||
| @@ -272,7 +272,7 @@ later is required to fix a server side protocol bug. | |||
| 272 | quiet=opt.quiet, | 272 | quiet=opt.quiet, |
| 273 | current_branch_only=opt.current_branch_only, | 273 | current_branch_only=opt.current_branch_only, |
| 274 | clone_bundle=not opt.no_clone_bundle, | 274 | clone_bundle=not opt.no_clone_bundle, |
| 275 | no_tags=opt.no_tags) | 275 | no_tags=opt.no_tags, archive=self.manifest.IsArchive) |
| 276 | self._fetch_times.Set(project, time.time() - start) | 276 | self._fetch_times.Set(project, time.time() - start) |
| 277 | 277 | ||
| 278 | # Lock around all the rest of the code, since printing, updating a set | 278 | # Lock around all the rest of the code, since printing, updating a set |
| @@ -315,7 +315,8 @@ later is required to fix a server side protocol bug. | |||
| 315 | quiet=opt.quiet, | 315 | quiet=opt.quiet, |
| 316 | current_branch_only=opt.current_branch_only, | 316 | current_branch_only=opt.current_branch_only, |
| 317 | clone_bundle=not opt.no_clone_bundle, | 317 | clone_bundle=not opt.no_clone_bundle, |
| 318 | no_tags=opt.no_tags): | 318 | no_tags=opt.no_tags, |
| 319 | archive=self.manifest.IsArchive): | ||
| 319 | fetched.add(project.gitdir) | 320 | fetched.add(project.gitdir) |
| 320 | else: | 321 | else: |
| 321 | print('error: Cannot fetch %s' % project.name, file=sys.stderr) | 322 | print('error: Cannot fetch %s' % project.name, file=sys.stderr) |
| @@ -363,7 +364,9 @@ later is required to fix a server side protocol bug. | |||
| 363 | pm.end() | 364 | pm.end() |
| 364 | self._fetch_times.Save() | 365 | self._fetch_times.Save() |
| 365 | 366 | ||
| 366 | self._GCProjects(projects) | 367 | if not self.manifest.IsArchive: |
| 368 | self._GCProjects(projects) | ||
| 369 | |||
| 367 | return fetched | 370 | return fetched |
| 368 | 371 | ||
| 369 | def _GCProjects(self, projects): | 372 | def _GCProjects(self, projects): |
| @@ -671,7 +674,7 @@ later is required to fix a server side protocol bug. | |||
| 671 | previously_missing_set = missing_set | 674 | previously_missing_set = missing_set |
| 672 | fetched.update(self._Fetch(missing, opt)) | 675 | fetched.update(self._Fetch(missing, opt)) |
| 673 | 676 | ||
| 674 | if self.manifest.IsMirror: | 677 | if self.manifest.IsMirror or self.manifest.IsArchive: |
| 675 | # bail out now, we have no working tree | 678 | # bail out now, we have no working tree |
| 676 | return | 679 | return |
| 677 | 680 | ||
| @@ -791,7 +794,7 @@ class _FetchTimes(object): | |||
| 791 | def _Load(self): | 794 | def _Load(self): |
| 792 | if self._times is None: | 795 | if self._times is None: |
| 793 | try: | 796 | try: |
| 794 | f = open(self._path) | 797 | f = open(self._path, 'rb') |
| 795 | except IOError: | 798 | except IOError: |
| 796 | self._times = {} | 799 | self._times = {} |
| 797 | return self._times | 800 | return self._times |
diff --git a/subcmds/upload.py b/subcmds/upload.py index 9ad55d79..56212408 100644 --- a/subcmds/upload.py +++ b/subcmds/upload.py | |||
| @@ -349,8 +349,9 @@ Gerrit Code Review: http://code.google.com/p/gerrit/ | |||
| 349 | 349 | ||
| 350 | # Make sure our local branch is not setup to track a different remote branch | 350 | # Make sure our local branch is not setup to track a different remote branch |
| 351 | merge_branch = self._GetMergeBranch(branch.project) | 351 | merge_branch = self._GetMergeBranch(branch.project) |
| 352 | full_dest = 'refs/heads/%s' % destination | 352 | if destination: |
| 353 | if not opt.dest_branch and merge_branch and merge_branch != full_dest: | 353 | full_dest = 'refs/heads/%s' % destination |
| 354 | if not opt.dest_branch and merge_branch and merge_branch != full_dest: | ||
| 354 | print('merge branch %s does not match destination branch %s' | 355 | print('merge branch %s does not match destination branch %s' |
| 355 | % (merge_branch, full_dest)) | 356 | % (merge_branch, full_dest)) |
| 356 | print('skipping upload.') | 357 | print('skipping upload.') |
