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 /manifest_xml.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 'manifest_xml.py')
-rw-r--r-- | manifest_xml.py | 64 |
1 files changed, 45 insertions, 19 deletions
diff --git a/manifest_xml.py b/manifest_xml.py index bdbb1d40..647e89f9 100644 --- a/manifest_xml.py +++ b/manifest_xml.py | |||
@@ -209,8 +209,9 @@ class XmlManifest(object): | |||
209 | root.appendChild(doc.createTextNode('')) | 209 | root.appendChild(doc.createTextNode('')) |
210 | 210 | ||
211 | def output_projects(parent, parent_node, projects): | 211 | def output_projects(parent, parent_node, projects): |
212 | for p in projects: | 212 | for project_name in projects: |
213 | output_project(parent, parent_node, self.projects[p]) | 213 | for project in self._projects[project_name]: |
214 | output_project(parent, parent_node, project) | ||
214 | 215 | ||
215 | def output_project(parent, parent_node, p): | 216 | def output_project(parent, parent_node, p): |
216 | if not p.MatchesGroups(groups): | 217 | if not p.MatchesGroups(groups): |
@@ -269,13 +270,11 @@ class XmlManifest(object): | |||
269 | e.setAttribute('sync-s', 'true') | 270 | e.setAttribute('sync-s', 'true') |
270 | 271 | ||
271 | if p.subprojects: | 272 | if p.subprojects: |
272 | sort_projects = list(sorted([subp.name for subp in p.subprojects])) | 273 | subprojects = set(subp.name for subp in p.subprojects) |
273 | output_projects(p, e, sort_projects) | 274 | output_projects(p, e, list(sorted(subprojects))) |
274 | 275 | ||
275 | sort_projects = list(sorted([key for key, value in self.projects.items() | 276 | projects = set(p.name for p in self._paths.values() if not p.parent) |
276 | if not value.parent])) | 277 | output_projects(None, root, list(sorted(projects))) |
277 | sort_projects.sort() | ||
278 | output_projects(None, root, sort_projects) | ||
279 | 278 | ||
280 | if self._repo_hooks_project: | 279 | if self._repo_hooks_project: |
281 | root.appendChild(doc.createTextNode('')) | 280 | root.appendChild(doc.createTextNode('')) |
@@ -288,9 +287,14 @@ class XmlManifest(object): | |||
288 | doc.writexml(fd, '', ' ', '\n', 'UTF-8') | 287 | doc.writexml(fd, '', ' ', '\n', 'UTF-8') |
289 | 288 | ||
290 | @property | 289 | @property |
290 | def paths(self): | ||
291 | self._Load() | ||
292 | return self._paths | ||
293 | |||
294 | @property | ||
291 | def projects(self): | 295 | def projects(self): |
292 | self._Load() | 296 | self._Load() |
293 | return self._projects | 297 | return self._paths.values() |
294 | 298 | ||
295 | @property | 299 | @property |
296 | def remotes(self): | 300 | def remotes(self): |
@@ -324,6 +328,7 @@ class XmlManifest(object): | |||
324 | def _Unload(self): | 328 | def _Unload(self): |
325 | self._loaded = False | 329 | self._loaded = False |
326 | self._projects = {} | 330 | self._projects = {} |
331 | self._paths = {} | ||
327 | self._remotes = {} | 332 | self._remotes = {} |
328 | self._default = None | 333 | self._default = None |
329 | self._repo_hooks_project = None | 334 | self._repo_hooks_project = None |
@@ -453,11 +458,17 @@ class XmlManifest(object): | |||
453 | self._manifest_server = url | 458 | self._manifest_server = url |
454 | 459 | ||
455 | def recursively_add_projects(project): | 460 | def recursively_add_projects(project): |
456 | if self._projects.get(project.name): | 461 | projects = self._projects.setdefault(project.name, []) |
462 | if project.relpath is None: | ||
457 | raise ManifestParseError( | 463 | raise ManifestParseError( |
458 | 'duplicate project %s in %s' % | 464 | 'missing path for %s in %s' % |
459 | (project.name, self.manifestFile)) | 465 | (project.name, self.manifestFile)) |
460 | self._projects[project.name] = project | 466 | if project.relpath in self._paths: |
467 | raise ManifestParseError( | ||
468 | 'duplicate path %s in %s' % | ||
469 | (project.relpath, self.manifestFile)) | ||
470 | self._paths[project.relpath] = project | ||
471 | projects.append(project) | ||
461 | for subproject in project.subprojects: | 472 | for subproject in project.subprojects: |
462 | recursively_add_projects(subproject) | 473 | recursively_add_projects(subproject) |
463 | 474 | ||
@@ -478,12 +489,18 @@ class XmlManifest(object): | |||
478 | 489 | ||
479 | # Store a reference to the Project. | 490 | # Store a reference to the Project. |
480 | try: | 491 | try: |
481 | self._repo_hooks_project = self._projects[repo_hooks_project] | 492 | repo_hooks_projects = self._projects[repo_hooks_project] |
482 | except KeyError: | 493 | except KeyError: |
483 | raise ManifestParseError( | 494 | raise ManifestParseError( |
484 | 'project %s not found for repo-hooks' % | 495 | 'project %s not found for repo-hooks' % |
485 | (repo_hooks_project)) | 496 | (repo_hooks_project)) |
486 | 497 | ||
498 | if len(repo_hooks_projects) != 1: | ||
499 | raise ManifestParseError( | ||
500 | 'internal error parsing repo-hooks in %s' % | ||
501 | (self.manifestFile)) | ||
502 | self._repo_hooks_project = repo_hooks_projects[0] | ||
503 | |||
487 | # Store the enabled hooks in the Project object. | 504 | # Store the enabled hooks in the Project object. |
488 | self._repo_hooks_project.enabled_repo_hooks = enabled_repo_hooks | 505 | self._repo_hooks_project.enabled_repo_hooks = enabled_repo_hooks |
489 | if node.nodeName == 'remove-project': | 506 | if node.nodeName == 'remove-project': |
@@ -530,11 +547,12 @@ class XmlManifest(object): | |||
530 | name = name, | 547 | name = name, |
531 | remote = remote.ToRemoteSpec(name), | 548 | remote = remote.ToRemoteSpec(name), |
532 | gitdir = gitdir, | 549 | gitdir = gitdir, |
550 | objdir = gitdir, | ||
533 | worktree = None, | 551 | worktree = None, |
534 | relpath = None, | 552 | relpath = None, |
535 | revisionExpr = m.revisionExpr, | 553 | revisionExpr = m.revisionExpr, |
536 | revisionId = None) | 554 | revisionId = None) |
537 | self._projects[project.name] = project | 555 | self._projects[project.name] = [project] |
538 | 556 | ||
539 | def _ParseRemote(self, node): | 557 | def _ParseRemote(self, node): |
540 | """ | 558 | """ |
@@ -694,9 +712,10 @@ class XmlManifest(object): | |||
694 | groups = [x for x in re.split(r'[,\s]+', groups) if x] | 712 | groups = [x for x in re.split(r'[,\s]+', groups) if x] |
695 | 713 | ||
696 | if parent is None: | 714 | if parent is None: |
697 | relpath, worktree, gitdir = self.GetProjectPaths(name, path) | 715 | relpath, worktree, gitdir, objdir = self.GetProjectPaths(name, path) |
698 | else: | 716 | else: |
699 | relpath, worktree, gitdir = self.GetSubprojectPaths(parent, path) | 717 | relpath, worktree, gitdir, objdir = \ |
718 | self.GetSubprojectPaths(parent, name, path) | ||
700 | 719 | ||
701 | default_groups = ['all', 'name:%s' % name, 'path:%s' % relpath] | 720 | default_groups = ['all', 'name:%s' % name, 'path:%s' % relpath] |
702 | groups.extend(set(default_groups).difference(groups)) | 721 | groups.extend(set(default_groups).difference(groups)) |
@@ -709,6 +728,7 @@ class XmlManifest(object): | |||
709 | name = name, | 728 | name = name, |
710 | remote = remote.ToRemoteSpec(name), | 729 | remote = remote.ToRemoteSpec(name), |
711 | gitdir = gitdir, | 730 | gitdir = gitdir, |
731 | objdir = objdir, | ||
712 | worktree = worktree, | 732 | worktree = worktree, |
713 | relpath = relpath, | 733 | relpath = relpath, |
714 | revisionExpr = revisionExpr, | 734 | revisionExpr = revisionExpr, |
@@ -737,10 +757,15 @@ class XmlManifest(object): | |||
737 | if self.IsMirror: | 757 | if self.IsMirror: |
738 | worktree = None | 758 | worktree = None |
739 | gitdir = os.path.join(self.topdir, '%s.git' % name) | 759 | gitdir = os.path.join(self.topdir, '%s.git' % name) |
760 | objdir = gitdir | ||
740 | else: | 761 | else: |
741 | worktree = os.path.join(self.topdir, path).replace('\\', '/') | 762 | worktree = os.path.join(self.topdir, path).replace('\\', '/') |
742 | gitdir = os.path.join(self.repodir, 'projects', '%s.git' % path) | 763 | gitdir = os.path.join(self.repodir, 'projects', '%s.git' % path) |
743 | return relpath, worktree, gitdir | 764 | objdir = os.path.join(self.repodir, 'project-objects', '%s.git' % name) |
765 | return relpath, worktree, gitdir, objdir | ||
766 | |||
767 | def GetProjectsWithName(self, name): | ||
768 | return self._projects.get(name, []) | ||
744 | 769 | ||
745 | def GetSubprojectName(self, parent, submodule_path): | 770 | def GetSubprojectName(self, parent, submodule_path): |
746 | return os.path.join(parent.name, submodule_path) | 771 | return os.path.join(parent.name, submodule_path) |
@@ -751,14 +776,15 @@ class XmlManifest(object): | |||
751 | def _UnjoinRelpath(self, parent_relpath, relpath): | 776 | def _UnjoinRelpath(self, parent_relpath, relpath): |
752 | return os.path.relpath(relpath, parent_relpath) | 777 | return os.path.relpath(relpath, parent_relpath) |
753 | 778 | ||
754 | def GetSubprojectPaths(self, parent, path): | 779 | def GetSubprojectPaths(self, parent, name, path): |
755 | relpath = self._JoinRelpath(parent.relpath, path) | 780 | relpath = self._JoinRelpath(parent.relpath, path) |
756 | gitdir = os.path.join(parent.gitdir, 'subprojects', '%s.git' % path) | 781 | gitdir = os.path.join(parent.gitdir, 'subprojects', '%s.git' % path) |
782 | objdir = os.path.join(parent.gitdir, 'subproject-objects', '%s.git' % name) | ||
757 | if self.IsMirror: | 783 | if self.IsMirror: |
758 | worktree = None | 784 | worktree = None |
759 | else: | 785 | else: |
760 | worktree = os.path.join(parent.worktree, path).replace('\\', '/') | 786 | worktree = os.path.join(parent.worktree, path).replace('\\', '/') |
761 | return relpath, worktree, gitdir | 787 | return relpath, worktree, gitdir, objdir |
762 | 788 | ||
763 | def _ParseCopyFile(self, project, node): | 789 | def _ParseCopyFile(self, project, node): |
764 | src = self._reqatt(node, 'src') | 790 | src = self._reqatt(node, 'src') |