summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/manifest-format.txt2
-rwxr-xr-xhooks/commit-msg4
-rw-r--r--manifest_xml.py4
-rw-r--r--project.py63
-rwxr-xr-xrepo29
-rw-r--r--subcmds/init.py21
-rw-r--r--subcmds/sync.py11
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 = {}
diff --git a/project.py b/project.py
index 1fb5d586..eb8bad3e 100644
--- a/project.py
+++ b/project.py
@@ -23,6 +23,7 @@ import shutil
23import stat 23import stat
24import subprocess 24import subprocess
25import sys 25import sys
26import tarfile
26import tempfile 27import tempfile
27import time 28import 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,
diff --git a/repo b/repo
index 277bbc6e..56d784fb 100755
--- a/repo
+++ b/repo
@@ -110,6 +110,7 @@ REPO_MAIN = S_repo + '/main.py' # main script
110MIN_PYTHON_VERSION = (2, 6) # minimum supported python version 110MIN_PYTHON_VERSION = (2, 6) # minimum supported python version
111 111
112 112
113import errno
113import optparse 114import optparse
114import os 115import os
115import re 116import re
@@ -180,6 +181,10 @@ group.add_option('--reference',
180group.add_option('--depth', type='int', default=None, 181group.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')
184group.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.')
183group.add_option('-g', '--groups', 188group.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
323def SetupGnuPG(quiet): 328def 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