diff options
| -rw-r--r-- | docs/manifest-format.txt | 2 | ||||
| -rwxr-xr-x | hooks/commit-msg | 4 | ||||
| -rw-r--r-- | manifest_xml.py | 4 | ||||
| -rw-r--r-- | project.py | 63 | ||||
| -rwxr-xr-x | repo | 29 | ||||
| -rw-r--r-- | subcmds/init.py | 21 | ||||
| -rw-r--r-- | subcmds/sync.py | 11 |
7 files changed, 114 insertions, 20 deletions
diff --git a/docs/manifest-format.txt b/docs/manifest-format.txt index f63c9d0e..e48b75fe 100644 --- a/docs/manifest-format.txt +++ b/docs/manifest-format.txt | |||
| @@ -47,7 +47,7 @@ following DTD: | |||
| 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> |
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 457d5ab0..785976bc 100644 --- a/manifest_xml.py +++ b/manifest_xml.py | |||
| @@ -329,6 +329,10 @@ class XmlManifest(object): | |||
| 329 | def IsMirror(self): | 329 | def IsMirror(self): |
| 330 | return self.manifestProject.config.GetBoolean('repo.mirror') | 330 | return self.manifestProject.config.GetBoolean('repo.mirror') |
| 331 | 331 | ||
| 332 | @property | ||
| 333 | def IsArchive(self): | ||
| 334 | return self.manifestProject.config.GetBoolean('repo.archive') | ||
| 335 | |||
| 332 | def _Unload(self): | 336 | def _Unload(self): |
| 333 | self._loaded = False | 337 | self._loaded = False |
| 334 | self._projects = {} | 338 | self._projects = {} |
| @@ -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 | ||
| @@ -982,15 +983,62 @@ class Project(object): | |||
| 982 | 983 | ||
| 983 | ## Sync ## | 984 | ## Sync ## |
| 984 | 985 | ||
| 986 | def _ExtractArchive(self, tarpath, path=None): | ||
| 987 | """Extract the given tar on its current location | ||
| 988 | |||
| 989 | Args: | ||
| 990 | - tarpath: The path to the actual tar file | ||
| 991 | |||
| 992 | """ | ||
| 993 | try: | ||
| 994 | with tarfile.open(tarpath, 'r') as tar: | ||
| 995 | tar.extractall(path=path) | ||
| 996 | return True | ||
| 997 | except (IOError, tarfile.TarError) as e: | ||
| 998 | print("error: Cannot extract archive %s: " | ||
| 999 | "%s" % (tarpath, str(e)), file=sys.stderr) | ||
| 1000 | return False | ||
| 1001 | |||
| 985 | def Sync_NetworkHalf(self, | 1002 | def Sync_NetworkHalf(self, |
| 986 | quiet=False, | 1003 | quiet=False, |
| 987 | is_new=None, | 1004 | is_new=None, |
| 988 | current_branch_only=False, | 1005 | current_branch_only=False, |
| 989 | clone_bundle=True, | 1006 | clone_bundle=True, |
| 990 | no_tags=False): | 1007 | no_tags=False, |
| 1008 | archive=False): | ||
| 991 | """Perform only the network IO portion of the sync process. | 1009 | """Perform only the network IO portion of the sync process. |
| 992 | Local working directory/branch state is not affected. | 1010 | Local working directory/branch state is not affected. |
| 993 | """ | 1011 | """ |
| 1012 | if archive and not isinstance(self, MetaProject): | ||
| 1013 | if self.remote.url.startswith(('http://', 'https://')): | ||
| 1014 | print("error: %s: Cannot fetch archives from http/https " | ||
| 1015 | "remotes." % self.name, file=sys.stderr) | ||
| 1016 | return False | ||
| 1017 | |||
| 1018 | name = self.relpath.replace('\\', '/') | ||
| 1019 | name = name.replace('/', '_') | ||
| 1020 | tarpath = '%s.tar' % name | ||
| 1021 | topdir = self.manifest.topdir | ||
| 1022 | |||
| 1023 | try: | ||
| 1024 | self._FetchArchive(tarpath, cwd=topdir) | ||
| 1025 | except GitError as e: | ||
| 1026 | print('error: %s' % str(e), file=sys.stderr) | ||
| 1027 | return False | ||
| 1028 | |||
| 1029 | # From now on, we only need absolute tarpath | ||
| 1030 | tarpath = os.path.join(topdir, tarpath) | ||
| 1031 | |||
| 1032 | if not self._ExtractArchive(tarpath, path=topdir): | ||
| 1033 | return False | ||
| 1034 | try: | ||
| 1035 | os.remove(tarpath) | ||
| 1036 | except OSError as e: | ||
| 1037 | print("warn: Cannot remove archive %s: " | ||
| 1038 | "%s" % (tarpath, str(e)), file=sys.stderr) | ||
| 1039 | self._CopyFiles() | ||
| 1040 | return True | ||
| 1041 | |||
| 994 | if is_new is None: | 1042 | if is_new is None: |
| 995 | is_new = not self.Exists | 1043 | is_new = not self.Exists |
| 996 | if is_new: | 1044 | if is_new: |
| @@ -1573,6 +1621,19 @@ class Project(object): | |||
| 1573 | 1621 | ||
| 1574 | ## Direct Git Commands ## | 1622 | ## Direct Git Commands ## |
| 1575 | 1623 | ||
| 1624 | def _FetchArchive(self, tarpath, cwd=None): | ||
| 1625 | cmd = ['archive', '-v', '-o', tarpath] | ||
| 1626 | cmd.append('--remote=%s' % self.remote.url) | ||
| 1627 | cmd.append('--prefix=%s/' % self.relpath) | ||
| 1628 | cmd.append(self.revisionExpr) | ||
| 1629 | |||
| 1630 | command = GitCommand(self, cmd, cwd=cwd, | ||
| 1631 | capture_stdout=True, | ||
| 1632 | capture_stderr=True) | ||
| 1633 | |||
| 1634 | if command.Wait() != 0: | ||
| 1635 | raise GitError('git archive %s: %s' % (self.name, command.stderr)) | ||
| 1636 | |||
| 1576 | def _RemoteFetch(self, name=None, | 1637 | def _RemoteFetch(self, name=None, |
| 1577 | current_branch_only=False, | 1638 | current_branch_only=False, |
| 1578 | initial=False, | 1639 | initial=False, |
| @@ -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 |
| @@ -180,6 +181,10 @@ group.add_option('--reference', | |||
| 180 | group.add_option('--depth', type='int', default=None, | 181 | group.add_option('--depth', type='int', default=None, |
| 181 | dest='depth', | 182 | dest='depth', |
| 182 | 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.') | ||
| 183 | group.add_option('-g', '--groups', | 188 | group.add_option('-g', '--groups', |
| 184 | dest='groups', default='default', | 189 | dest='groups', default='default', |
| 185 | help='restrict manifest projects to ones with specified ' | 190 | help='restrict manifest projects to ones with specified ' |
| @@ -239,10 +244,10 @@ def _Init(args): | |||
| 239 | _print("fatal: invalid branch name '%s'" % branch, file=sys.stderr) | 244 | _print("fatal: invalid branch name '%s'" % branch, file=sys.stderr) |
| 240 | raise CloneFailure() | 245 | raise CloneFailure() |
| 241 | 246 | ||
| 242 | if not os.path.isdir(repodir): | 247 | try: |
| 243 | try: | 248 | os.mkdir(repodir) |
| 244 | os.mkdir(repodir) | 249 | except OSError as e: |
| 245 | except OSError as e: | 250 | if e.errno != errno.EEXIST: |
| 246 | _print('fatal: cannot make %s directory: %s' | 251 | _print('fatal: cannot make %s directory: %s' |
| 247 | % (repodir, e.strerror), file=sys.stderr) | 252 | % (repodir, e.strerror), file=sys.stderr) |
| 248 | # Don't raise CloneFailure; that would delete the | 253 | # Don't raise CloneFailure; that would delete the |
| @@ -321,18 +326,18 @@ def NeedSetupGnuPG(): | |||
| 321 | 326 | ||
| 322 | 327 | ||
| 323 | def SetupGnuPG(quiet): | 328 | def SetupGnuPG(quiet): |
| 324 | if not os.path.isdir(home_dot_repo): | 329 | try: |
| 325 | try: | 330 | os.mkdir(home_dot_repo) |
| 326 | os.mkdir(home_dot_repo) | 331 | except OSError as e: |
| 327 | except OSError as e: | 332 | if e.errno != errno.EEXIST: |
| 328 | _print('fatal: cannot make %s directory: %s' | 333 | _print('fatal: cannot make %s directory: %s' |
| 329 | % (home_dot_repo, e.strerror), file=sys.stderr) | 334 | % (home_dot_repo, e.strerror), file=sys.stderr) |
| 330 | sys.exit(1) | 335 | sys.exit(1) |
| 331 | 336 | ||
| 332 | if not os.path.isdir(gpg_dir): | 337 | try: |
| 333 | try: | 338 | os.mkdir(gpg_dir, stat.S_IRWXU) |
| 334 | os.mkdir(gpg_dir, stat.S_IRWXU) | 339 | except OSError as e: |
| 335 | except OSError as e: | 340 | if e.errno != errno.EEXIST: |
| 336 | _print('fatal: cannot make %s directory: %s' % (gpg_dir, e.strerror), | 341 | _print('fatal: cannot make %s directory: %s' % (gpg_dir, e.strerror), |
| 337 | file=sys.stderr) | 342 | file=sys.stderr) |
| 338 | sys.exit(1) | 343 | sys.exit(1) |
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 d8aec59b..0279ff60 100644 --- a/subcmds/sync.py +++ b/subcmds/sync.py | |||
| @@ -253,7 +253,7 @@ later is required to fix a server side protocol bug. | |||
| 253 | quiet=opt.quiet, | 253 | quiet=opt.quiet, |
| 254 | current_branch_only=opt.current_branch_only, | 254 | current_branch_only=opt.current_branch_only, |
| 255 | clone_bundle=not opt.no_clone_bundle, | 255 | clone_bundle=not opt.no_clone_bundle, |
| 256 | no_tags=opt.no_tags) | 256 | no_tags=opt.no_tags, archive=self.manifest.IsArchive) |
| 257 | self._fetch_times.Set(project, time.time() - start) | 257 | self._fetch_times.Set(project, time.time() - start) |
| 258 | 258 | ||
| 259 | # Lock around all the rest of the code, since printing, updating a set | 259 | # Lock around all the rest of the code, since printing, updating a set |
| @@ -294,7 +294,8 @@ later is required to fix a server side protocol bug. | |||
| 294 | quiet=opt.quiet, | 294 | quiet=opt.quiet, |
| 295 | current_branch_only=opt.current_branch_only, | 295 | current_branch_only=opt.current_branch_only, |
| 296 | clone_bundle=not opt.no_clone_bundle, | 296 | clone_bundle=not opt.no_clone_bundle, |
| 297 | no_tags=opt.no_tags): | 297 | no_tags=opt.no_tags, |
| 298 | archive=self.manifest.IsArchive): | ||
| 298 | fetched.add(project.gitdir) | 299 | fetched.add(project.gitdir) |
| 299 | else: | 300 | else: |
| 300 | print('error: Cannot fetch %s' % project.name, file=sys.stderr) | 301 | print('error: Cannot fetch %s' % project.name, file=sys.stderr) |
| @@ -338,7 +339,9 @@ later is required to fix a server side protocol bug. | |||
| 338 | pm.end() | 339 | pm.end() |
| 339 | self._fetch_times.Save() | 340 | self._fetch_times.Save() |
| 340 | 341 | ||
| 341 | self._GCProjects(projects) | 342 | if not self.manifest.IsArchive: |
| 343 | self._GCProjects(projects) | ||
| 344 | |||
| 342 | return fetched | 345 | return fetched |
| 343 | 346 | ||
| 344 | def _GCProjects(self, projects): | 347 | def _GCProjects(self, projects): |
| @@ -641,7 +644,7 @@ later is required to fix a server side protocol bug. | |||
| 641 | previously_missing_set = missing_set | 644 | previously_missing_set = missing_set |
| 642 | fetched.update(self._Fetch(missing, opt)) | 645 | fetched.update(self._Fetch(missing, opt)) |
| 643 | 646 | ||
| 644 | if self.manifest.IsMirror: | 647 | if self.manifest.IsMirror or self.manifest.IsArchive: |
| 645 | # bail out now, we have no working tree | 648 | # bail out now, we have no working tree |
| 646 | return | 649 | return |
| 647 | 650 | ||
