diff options
author | David James <davidjames@google.com> | 2013-10-11 17:03:19 -0700 |
---|---|---|
committer | David James <davidjames@google.com> | 2013-10-14 15:34:32 -0700 |
commit | 8d20116038ff78b22069dd4e993b5819775f03d1 (patch) | |
tree | 4c7ec381f2452d3ae4ed5332230a8d7a61e6ec2b /subcmds/sync.py | |
parent | b25ea555c39cd500740acb74fa9f1dab71588266 (diff) | |
download | git-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.py | 54 |
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', |