summaryrefslogtreecommitdiffstats
path: root/subcmds/sync.py
diff options
context:
space:
mode:
authorShawn O. Pearce <sop@google.com>2010-12-07 10:31:19 -0800
committerShawn O. Pearce <sop@google.com>2010-12-07 11:13:29 -0800
commit13f3da50d40b89ee5b05f5f3de9542c20edac6d1 (patch)
treed085b6f6b498bde85a1969fce884dd24e88d03d5 /subcmds/sync.py
parent3218c13205694434edb2375ab8a8515554eed366 (diff)
parent2b8db3ce3e7344b9f3b5216637c5af0d54be5656 (diff)
downloadgit-repo-13f3da50d40b89ee5b05f5f3de9542c20edac6d1.tar.gz
Merge branch 'stable'
* stable: (33 commits) Added feature to print a <notice> from manifest at the end of a sync. sync: Use --force-broken to continue other projects upload: Remove --replace option sync --quiet: be more quiet sync: Enable use of git clone --reference Only delete corrupt pickle config files if they exist Don't allow git fetch to start ControlMaster Check for existing SSH ControlMaster Fix for handling values of EDITOR which contain a space. upload: Fix --replace flag rebase: Pass through more options upload: Allow review.HOST.username to override email upload -t: Automatically include local branch name Warn users before uploading if there are local changes sync: Try fetching a tag as a last resort before giving up rebase: Automatically rebase branch on upstrea upload: Automatically --cc folks in review.URL.autocopy Fix format string bugs in grep Do not invoke ssh with -p argument when no port has been specified. Allow files to be copied into new folders ... Conflicts: git_config.py manifest_xml.py subcmds/init.py subcmds/sync.py subcmds/upload.py Change-Id: I4756a6908277e91505c35287a122a775b68f4df5
Diffstat (limited to 'subcmds/sync.py')
-rw-r--r--subcmds/sync.py196
1 files changed, 159 insertions, 37 deletions
diff --git a/subcmds/sync.py b/subcmds/sync.py
index d89c2b8c..7b77388b 100644
--- a/subcmds/sync.py
+++ b/subcmds/sync.py
@@ -17,11 +17,19 @@ from optparse import SUPPRESS_HELP
17import os 17import os
18import re 18import re
19import shutil 19import shutil
20import socket
20import subprocess 21import subprocess
21import sys 22import sys
22import time 23import time
24import xmlrpclib
25
26try:
27 import threading as _threading
28except ImportError:
29 import dummy_threading as _threading
23 30
24from git_command import GIT 31from git_command import GIT
32from git_refs import R_HEADS
25from project import HEAD 33from project import HEAD
26from project import Project 34from project import Project
27from project import RemoteSpec 35from project import RemoteSpec
@@ -32,6 +40,7 @@ from project import SyncBuffer
32from progress import Progress 40from progress import Progress
33 41
34class Sync(Command, MirrorSafeCommand): 42class Sync(Command, MirrorSafeCommand):
43 jobs = 1
35 common = True 44 common = True
36 helpSummary = "Update working tree to the latest revision" 45 helpSummary = "Update working tree to the latest revision"
37 helpUsage = """ 46 helpUsage = """
@@ -57,6 +66,13 @@ back to the manifest revision. This option is especially helpful
57if the project is currently on a topic branch, but the manifest 66if the project is currently on a topic branch, but the manifest
58revision is temporarily needed. 67revision is temporarily needed.
59 68
69The -s/--smart-sync option can be used to sync to a known good
70build as specified by the manifest-server element in the current
71manifest.
72
73The -f/--force-broken option can be used to proceed with syncing
74other projects if a project sync fails.
75
60SSH Connections 76SSH Connections
61--------------- 77---------------
62 78
@@ -87,7 +103,10 @@ later is required to fix a server side protocol bug.
87 103
88""" 104"""
89 105
90 def _Options(self, p): 106 def _Options(self, p, show_smart=True):
107 p.add_option('-f', '--force-broken',
108 dest='force_broken', action='store_true',
109 help="continue sync even if a project fails to sync")
91 p.add_option('-l','--local-only', 110 p.add_option('-l','--local-only',
92 dest='local_only', action='store_true', 111 dest='local_only', action='store_true',
93 help="only update working tree, don't fetch") 112 help="only update working tree, don't fetch")
@@ -97,6 +116,16 @@ later is required to fix a server side protocol bug.
97 p.add_option('-d','--detach', 116 p.add_option('-d','--detach',
98 dest='detach_head', action='store_true', 117 dest='detach_head', action='store_true',
99 help='detach projects back to manifest revision') 118 help='detach projects back to manifest revision')
119 p.add_option('-q','--quiet',
120 dest='quiet', action='store_true',
121 help='be more quiet')
122 p.add_option('-j','--jobs',
123 dest='jobs', action='store', type='int',
124 help="number of projects to fetch simultaneously")
125 if show_smart:
126 p.add_option('-s', '--smart-sync',
127 dest='smart_sync', action='store_true',
128 help='smart sync using manifest from a known good build')
100 129
101 g = p.add_option_group('repo Version options') 130 g = p.add_option_group('repo Version options')
102 g.add_option('--no-repo-verify', 131 g.add_option('--no-repo-verify',
@@ -106,16 +135,55 @@ later is required to fix a server side protocol bug.
106 dest='repo_upgraded', action='store_true', 135 dest='repo_upgraded', action='store_true',
107 help=SUPPRESS_HELP) 136 help=SUPPRESS_HELP)
108 137
109 def _Fetch(self, projects): 138 def _FetchHelper(self, opt, project, lock, fetched, pm, sem):
139 if not project.Sync_NetworkHalf(quiet=opt.quiet):
140 print >>sys.stderr, 'error: Cannot fetch %s' % project.name
141 if opt.force_broken:
142 print >>sys.stderr, 'warn: --force-broken, continuing to sync'
143 else:
144 sem.release()
145 sys.exit(1)
146
147 lock.acquire()
148 fetched.add(project.gitdir)
149 pm.update()
150 lock.release()
151 sem.release()
152
153 def _Fetch(self, projects, opt):
110 fetched = set() 154 fetched = set()
111 pm = Progress('Fetching projects', len(projects)) 155 pm = Progress('Fetching projects', len(projects))
112 for project in projects: 156
113 pm.update() 157 if self.jobs == 1:
114 if project.Sync_NetworkHalf(): 158 for project in projects:
115 fetched.add(project.gitdir) 159 pm.update()
116 else: 160 if project.Sync_NetworkHalf(quiet=opt.quiet):
117 print >>sys.stderr, 'error: Cannot fetch %s' % project.name 161 fetched.add(project.gitdir)
118 sys.exit(1) 162 else:
163 print >>sys.stderr, 'error: Cannot fetch %s' % project.name
164 if opt.force_broken:
165 print >>sys.stderr, 'warn: --force-broken, continuing to sync'
166 else:
167 sys.exit(1)
168 else:
169 threads = set()
170 lock = _threading.Lock()
171 sem = _threading.Semaphore(self.jobs)
172 for project in projects:
173 sem.acquire()
174 t = _threading.Thread(target = self._FetchHelper,
175 args = (opt,
176 project,
177 lock,
178 fetched,
179 pm,
180 sem))
181 threads.add(t)
182 t.start()
183
184 for t in threads:
185 t.join()
186
119 pm.end() 187 pm.end()
120 for project in projects: 188 for project in projects:
121 project.bare_git.gc('--auto') 189 project.bare_git.gc('--auto')
@@ -140,32 +208,36 @@ later is required to fix a server side protocol bug.
140 if not path: 208 if not path:
141 continue 209 continue
142 if path not in new_project_paths: 210 if path not in new_project_paths:
143 project = Project( 211 """If the path has already been deleted, we don't need to do it
144 manifest = self.manifest, 212 """
145 name = path, 213 if os.path.exists(self.manifest.topdir + '/' + path):
146 remote = RemoteSpec('origin'), 214 project = Project(
147 gitdir = os.path.join(self.manifest.topdir, 215 manifest = self.manifest,
148 path, '.git'), 216 name = path,
149 worktree = os.path.join(self.manifest.topdir, path), 217 remote = RemoteSpec('origin'),
150 relpath = path, 218 gitdir = os.path.join(self.manifest.topdir,
151 revisionExpr = 'HEAD', 219 path, '.git'),
152 revisionId = None) 220 worktree = os.path.join(self.manifest.topdir, path),
153 if project.IsDirty(): 221 relpath = path,
154 print >>sys.stderr, 'error: Cannot remove project "%s": \ 222 revisionExpr = 'HEAD',
223 revisionId = None)
224
225 if project.IsDirty():
226 print >>sys.stderr, 'error: Cannot remove project "%s": \
155uncommitted changes are present' % project.relpath 227uncommitted changes are present' % project.relpath
156 print >>sys.stderr, ' commit changes, then run sync again' 228 print >>sys.stderr, ' commit changes, then run sync again'
157 return -1 229 return -1
158 else: 230 else:
159 print >>sys.stderr, 'Deleting obsolete path %s' % project.worktree 231 print >>sys.stderr, 'Deleting obsolete path %s' % project.worktree
160 shutil.rmtree(project.worktree) 232 shutil.rmtree(project.worktree)
161 # Try deleting parent subdirs if they are empty 233 # Try deleting parent subdirs if they are empty
162 dir = os.path.dirname(project.worktree) 234 dir = os.path.dirname(project.worktree)
163 while dir != self.manifest.topdir: 235 while dir != self.manifest.topdir:
164 try: 236 try:
165 os.rmdir(dir) 237 os.rmdir(dir)
166 except OSError: 238 except OSError:
167 break 239 break
168 dir = os.path.dirname(dir) 240 dir = os.path.dirname(dir)
169 241
170 new_project_paths.sort() 242 new_project_paths.sort()
171 fd = open(file_path, 'w') 243 fd = open(file_path, 'w')
@@ -177,6 +249,8 @@ uncommitted changes are present' % project.relpath
177 return 0 249 return 0
178 250
179 def Execute(self, opt, args): 251 def Execute(self, opt, args):
252 if opt.jobs:
253 self.jobs = opt.jobs
180 if opt.network_only and opt.detach_head: 254 if opt.network_only and opt.detach_head:
181 print >>sys.stderr, 'error: cannot combine -n and -d' 255 print >>sys.stderr, 'error: cannot combine -n and -d'
182 sys.exit(1) 256 sys.exit(1)
@@ -184,6 +258,51 @@ uncommitted changes are present' % project.relpath
184 print >>sys.stderr, 'error: cannot combine -n and -l' 258 print >>sys.stderr, 'error: cannot combine -n and -l'
185 sys.exit(1) 259 sys.exit(1)
186 260
261 if opt.smart_sync:
262 if not self.manifest.manifest_server:
263 print >>sys.stderr, \
264 'error: cannot smart sync: no manifest server defined in manifest'
265 sys.exit(1)
266 try:
267 server = xmlrpclib.Server(self.manifest.manifest_server)
268 p = self.manifest.manifestProject
269 b = p.GetBranch(p.CurrentBranch)
270 branch = b.merge
271 if branch.startswith(R_HEADS):
272 branch = branch[len(R_HEADS):]
273
274 env = dict(os.environ)
275 if (env.has_key('TARGET_PRODUCT') and
276 env.has_key('TARGET_BUILD_VARIANT')):
277 target = '%s-%s' % (env['TARGET_PRODUCT'],
278 env['TARGET_BUILD_VARIANT'])
279 [success, manifest_str] = server.GetApprovedManifest(branch, target)
280 else:
281 [success, manifest_str] = server.GetApprovedManifest(branch)
282
283 if success:
284 manifest_name = "smart_sync_override.xml"
285 manifest_path = os.path.join(self.manifest.manifestProject.worktree,
286 manifest_name)
287 try:
288 f = open(manifest_path, 'w')
289 try:
290 f.write(manifest_str)
291 finally:
292 f.close()
293 except IOError:
294 print >>sys.stderr, 'error: cannot write manifest to %s' % \
295 manifest_path
296 sys.exit(1)
297 self.manifest.Override(manifest_name)
298 else:
299 print >>sys.stderr, 'error: %s' % manifest_str
300 sys.exit(1)
301 except socket.error:
302 print >>sys.stderr, 'error: cannot connect to manifest server %s' % (
303 self.manifest.manifest_server)
304 sys.exit(1)
305
187 rp = self.manifest.repoProject 306 rp = self.manifest.repoProject
188 rp.PreSync() 307 rp.PreSync()
189 308
@@ -194,7 +313,7 @@ uncommitted changes are present' % project.relpath
194 _PostRepoUpgrade(self.manifest) 313 _PostRepoUpgrade(self.manifest)
195 314
196 if not opt.local_only: 315 if not opt.local_only:
197 mp.Sync_NetworkHalf() 316 mp.Sync_NetworkHalf(quiet=opt.quiet)
198 317
199 if mp.HasChanges: 318 if mp.HasChanges:
200 syncbuf = SyncBuffer(mp.config) 319 syncbuf = SyncBuffer(mp.config)
@@ -211,7 +330,7 @@ uncommitted changes are present' % project.relpath
211 to_fetch.append(rp) 330 to_fetch.append(rp)
212 to_fetch.extend(all) 331 to_fetch.extend(all)
213 332
214 fetched = self._Fetch(to_fetch) 333 fetched = self._Fetch(to_fetch, opt)
215 _PostRepoFetch(rp, opt.no_repo_verify) 334 _PostRepoFetch(rp, opt.no_repo_verify)
216 if opt.network_only: 335 if opt.network_only:
217 # bail out now; the rest touches the working tree 336 # bail out now; the rest touches the working tree
@@ -230,7 +349,7 @@ uncommitted changes are present' % project.relpath
230 for project in all: 349 for project in all:
231 if project.gitdir not in fetched: 350 if project.gitdir not in fetched:
232 missing.append(project) 351 missing.append(project)
233 self._Fetch(missing) 352 self._Fetch(missing, opt)
234 353
235 if self.manifest.IsMirror: 354 if self.manifest.IsMirror:
236 # bail out now, we have no working tree 355 # bail out now, we have no working tree
@@ -258,6 +377,9 @@ def _ReloadManifest(cmd):
258 if old.__class__ != new.__class__: 377 if old.__class__ != new.__class__:
259 print >>sys.stderr, 'NOTICE: manifest format has changed ***' 378 print >>sys.stderr, 'NOTICE: manifest format has changed ***'
260 new.Upgrade_Local(old) 379 new.Upgrade_Local(old)
380 else:
381 if new.notice:
382 print new.notice
261 383
262def _PostRepoUpgrade(manifest): 384def _PostRepoUpgrade(manifest):
263 for project in manifest.projects.values(): 385 for project in manifest.projects.values():