summaryrefslogtreecommitdiffstats
path: root/subcmds/sync.py
diff options
context:
space:
mode:
authorDavid James <davidjames@google.com>2013-10-11 17:03:19 -0700
committerDavid James <davidjames@google.com>2013-10-14 15:34:32 -0700
commit8d20116038ff78b22069dd4e993b5819775f03d1 (patch)
tree4c7ec381f2452d3ae4ed5332230a8d7a61e6ec2b /subcmds/sync.py
parentb25ea555c39cd500740acb74fa9f1dab71588266 (diff)
downloadgit-repo-8d20116038ff78b22069dd4e993b5819775f03d1.tar.gz
repo: Support multiple branches for the same project.
It is often useful to be able to include the same project more than once, but with different branches and placed in different paths in the workspace. Add this feature. This CL adds the concept of an object directory. The object directory stores objects that can be shared amongst several working trees. For newly synced repositories, we set up the git repo now to share its objects with an object repo. Each worktree for a given repo shares objects, but has an independent set of references and branches. This ensures that repo only has to update the objects once; however the references for each worktree are updated separately. Storing the references separately is needed to ensure that commits to a branch on one worktree will not change the HEAD commits of the others. One nice side effect of sharing objects between different worktrees is that you can easily cherry-pick changes between the two worktrees without needing to fetch them. Bug: Issue 141 Change-Id: I5e2f4e1a7abb56f9d3f310fa6fd0c17019330ecd
Diffstat (limited to 'subcmds/sync.py')
-rw-r--r--subcmds/sync.py54
1 files changed, 42 insertions, 12 deletions
diff --git a/subcmds/sync.py b/subcmds/sync.py
index e9d52b7b..d1a06412 100644
--- a/subcmds/sync.py
+++ b/subcmds/sync.py
@@ -219,9 +219,25 @@ later is required to fix a server side protocol bug.
219 dest='repo_upgraded', action='store_true', 219 dest='repo_upgraded', action='store_true',
220 help=SUPPRESS_HELP) 220 help=SUPPRESS_HELP)
221 221
222 def _FetchHelper(self, opt, project, lock, fetched, pm, sem, err_event): 222 def _FetchProjectList(self, opt, projects, *args):
223 """Main function of the fetch threads when jobs are > 1. 223 """Main function of the fetch threads when jobs are > 1.
224 224
225 Delegates most of the work to _FetchHelper.
226
227 Args:
228 opt: Program options returned from optparse. See _Options().
229 projects: Projects to fetch.
230 *args: Remaining arguments to pass to _FetchHelper. See the
231 _FetchHelper docstring for details.
232 """
233 for project in projects:
234 success = self._FetchHelper(opt, project, *args)
235 if not success and not opt.force_broken:
236 break
237
238 def _FetchHelper(self, opt, project, lock, fetched, pm, sem, err_event):
239 """Fetch git objects for a single project.
240
225 Args: 241 Args:
226 opt: Program options returned from optparse. See _Options(). 242 opt: Program options returned from optparse. See _Options().
227 project: Project object for the project to fetch. 243 project: Project object for the project to fetch.
@@ -235,6 +251,9 @@ later is required to fix a server side protocol bug.
235 can be started up. 251 can be started up.
236 err_event: We'll set this event in the case of an error (after printing 252 err_event: We'll set this event in the case of an error (after printing
237 out info about the error). 253 out info about the error).
254
255 Returns:
256 Whether the fetch was successful.
238 """ 257 """
239 # We'll set to true once we've locked the lock. 258 # We'll set to true once we've locked the lock.
240 did_lock = False 259 did_lock = False
@@ -281,6 +300,8 @@ later is required to fix a server side protocol bug.
281 lock.release() 300 lock.release()
282 sem.release() 301 sem.release()
283 302
303 return success
304
284 def _Fetch(self, projects, opt): 305 def _Fetch(self, projects, opt):
285 fetched = set() 306 fetched = set()
286 pm = Progress('Fetching projects', len(projects)) 307 pm = Progress('Fetching projects', len(projects))
@@ -303,20 +324,24 @@ later is required to fix a server side protocol bug.
303 else: 324 else:
304 sys.exit(1) 325 sys.exit(1)
305 else: 326 else:
327 objdir_project_map = dict()
328 for project in projects:
329 objdir_project_map.setdefault(project.objdir, []).append(project)
330
306 threads = set() 331 threads = set()
307 lock = _threading.Lock() 332 lock = _threading.Lock()
308 sem = _threading.Semaphore(self.jobs) 333 sem = _threading.Semaphore(self.jobs)
309 err_event = _threading.Event() 334 err_event = _threading.Event()
310 for project in projects: 335 for project_list in objdir_project_map.values():
311 # Check for any errors before starting any new threads. 336 # Check for any errors before starting any new threads.
312 # ...we'll let existing threads finish, though. 337 # ...we'll let existing threads finish, though.
313 if err_event.isSet(): 338 if err_event.isSet():
314 break 339 break
315 340
316 sem.acquire() 341 sem.acquire()
317 t = _threading.Thread(target = self._FetchHelper, 342 t = _threading.Thread(target = self._FetchProjectList,
318 args = (opt, 343 args = (opt,
319 project, 344 project_list,
320 lock, 345 lock,
321 fetched, 346 fetched,
322 pm, 347 pm,
@@ -342,6 +367,10 @@ later is required to fix a server side protocol bug.
342 return fetched 367 return fetched
343 368
344 def _GCProjects(self, projects): 369 def _GCProjects(self, projects):
370 gitdirs = {}
371 for project in projects:
372 gitdirs[project.gitdir] = project.bare_git
373
345 has_dash_c = git_require((1, 7, 2)) 374 has_dash_c = git_require((1, 7, 2))
346 if multiprocessing and has_dash_c: 375 if multiprocessing and has_dash_c:
347 cpu_count = multiprocessing.cpu_count() 376 cpu_count = multiprocessing.cpu_count()
@@ -350,8 +379,8 @@ later is required to fix a server side protocol bug.
350 jobs = min(self.jobs, cpu_count) 379 jobs = min(self.jobs, cpu_count)
351 380
352 if jobs < 2: 381 if jobs < 2:
353 for project in projects: 382 for bare_git in gitdirs.values():
354 project.bare_git.gc('--auto') 383 bare_git.gc('--auto')
355 return 384 return
356 385
357 config = {'pack.threads': cpu_count / jobs if cpu_count > jobs else 1} 386 config = {'pack.threads': cpu_count / jobs if cpu_count > jobs else 1}
@@ -360,10 +389,10 @@ later is required to fix a server side protocol bug.
360 sem = _threading.Semaphore(jobs) 389 sem = _threading.Semaphore(jobs)
361 err_event = _threading.Event() 390 err_event = _threading.Event()
362 391
363 def GC(project): 392 def GC(bare_git):
364 try: 393 try:
365 try: 394 try:
366 project.bare_git.gc('--auto', config=config) 395 bare_git.gc('--auto', config=config)
367 except GitError: 396 except GitError:
368 err_event.set() 397 err_event.set()
369 except: 398 except:
@@ -372,11 +401,11 @@ later is required to fix a server side protocol bug.
372 finally: 401 finally:
373 sem.release() 402 sem.release()
374 403
375 for project in projects: 404 for bare_git in gitdirs.values():
376 if err_event.isSet(): 405 if err_event.isSet():
377 break 406 break
378 sem.acquire() 407 sem.acquire()
379 t = _threading.Thread(target=GC, args=(project,)) 408 t = _threading.Thread(target=GC, args=(bare_git,))
380 t.daemon = True 409 t.daemon = True
381 threads.add(t) 410 threads.add(t)
382 t.start() 411 t.start()
@@ -416,12 +445,13 @@ later is required to fix a server side protocol bug.
416 if path not in new_project_paths: 445 if path not in new_project_paths:
417 # If the path has already been deleted, we don't need to do it 446 # If the path has already been deleted, we don't need to do it
418 if os.path.exists(self.manifest.topdir + '/' + path): 447 if os.path.exists(self.manifest.topdir + '/' + path):
448 gitdir = os.path.join(self.manifest.topdir, path, '.git')
419 project = Project( 449 project = Project(
420 manifest = self.manifest, 450 manifest = self.manifest,
421 name = path, 451 name = path,
422 remote = RemoteSpec('origin'), 452 remote = RemoteSpec('origin'),
423 gitdir = os.path.join(self.manifest.topdir, 453 gitdir = gitdir,
424 path, '.git'), 454 objdir = gitdir,
425 worktree = os.path.join(self.manifest.topdir, path), 455 worktree = os.path.join(self.manifest.topdir, path),
426 relpath = path, 456 relpath = path,
427 revisionExpr = 'HEAD', 457 revisionExpr = 'HEAD',