summaryrefslogtreecommitdiffstats
path: root/manifest_xml.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 /manifest_xml.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 'manifest_xml.py')
-rw-r--r--manifest_xml.py64
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')