summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/manifest-format.txt12
-rw-r--r--git_command.py2
-rw-r--r--git_config.py4
-rw-r--r--git_refs.py2
-rwxr-xr-xhooks/commit-msg4
-rw-r--r--manifest_xml.py28
-rw-r--r--project.py75
-rwxr-xr-xrepo36
-rw-r--r--subcmds/branches.py2
-rw-r--r--subcmds/init.py21
-rw-r--r--subcmds/sync.py13
-rw-r--r--subcmds/upload.py5
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
54class _XmlRemote(object): 60class _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
98class XmlManifest(object): 104class 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
diff --git a/project.py b/project.py
index f9f1f75d..46f3b8f7 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
@@ -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():
diff --git a/repo b/repo
index eeb8920b..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
@@ -138,10 +139,9 @@ def _print(*objects, **kwargs):
138# Python version check 139# Python version check
139ver = sys.version_info 140ver = sys.version_info
140if ver[0] == 3: 141if 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)
145if (ver[0], ver[1]) < MIN_PYTHON_VERSION: 145if (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',
181group.add_option('--depth', type='int', default=None, 181group.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')
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.')
184group.add_option('-g', '--groups', 188group.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
324def SetupGnuPG(quiet): 328def 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.')