summaryrefslogtreecommitdiffstats
path: root/manifest_xml.py
diff options
context:
space:
mode:
authorChe-Liang Chiou <clchiou@google.com>2012-01-11 11:28:42 +0800
committerChe-Liang Chiou <clchiou@google.com>2012-10-23 16:08:58 -0700
commit69998b0c6ff724bf620480140ccce648fec7d6a9 (patch)
treeb6f9c4c00b04a0f140074c4c2dba91ed4f055b11 /manifest_xml.py
parent5c6eeac8f0350fd6b14cf226ffcff655f1dd9582 (diff)
downloadgit-repo-69998b0c6ff724bf620480140ccce648fec7d6a9.tar.gz
Represent git-submodule as nested projects
We need a representation of git-submodule in repo; otherwise repo will not sync submodules, and leave workspace in a broken state. Of course this will not be a problem if all projects are owned by the owner of the manifest file, who may simply choose not to use git-submodule in all projects. However, this is not possible in practice because manifest file owner is unlikely to own all upstream projects. As git submodules are simply git repositories, it is natural to treat them as plain repo projects that live inside a repo project. That is, we could use recursively declared projects to denote the is-submodule relation of git repositories. The behavior of repo remains the same to projects that do not have a sub-project within. As for parent projects, repo fetches them and their sub-projects as normal projects, and then checks out subprojects at the commit specified in parent's commit object. The sub-project is fetched at a path relative to parent project's working directory; so the path specified in manifest file should match that of .gitmodules file. If a submodule is not registered in repo manifest, repo will derive its properties from itself and its parent project, which might not always be correct. In such cases, the subproject is called a derived subproject. To a user, a sub-project is merely a git-submodule; so all tips of working with a git-submodule apply here, too. For example, you should not run `repo sync` in a parent repository if its submodule is dirty. Change-Id: I541e9e2ac1a70304272dbe09724572aa1004eb5c
Diffstat (limited to 'manifest_xml.py')
-rw-r--r--manifest_xml.py108
1 files changed, 82 insertions, 26 deletions
diff --git a/manifest_xml.py b/manifest_xml.py
index 04cabaad..a2a56e92 100644
--- a/manifest_xml.py
+++ b/manifest_xml.py
@@ -180,20 +180,25 @@ class XmlManifest(object):
180 root.appendChild(e) 180 root.appendChild(e)
181 root.appendChild(doc.createTextNode('')) 181 root.appendChild(doc.createTextNode(''))
182 182
183 sort_projects = list(self.projects.keys()) 183 def output_projects(parent, parent_node, projects):
184 sort_projects.sort() 184 for p in projects:
185 185 output_project(parent, parent_node, self.projects[p])
186 for p in sort_projects:
187 p = self.projects[p]
188 186
187 def output_project(parent, parent_node, p):
189 if not p.MatchesGroups(groups): 188 if not p.MatchesGroups(groups):
190 continue 189 return
190
191 name = p.name
192 relpath = p.relpath
193 if parent:
194 name = self._UnjoinName(parent.name, name)
195 relpath = self._UnjoinRelpath(parent.relpath, relpath)
191 196
192 e = doc.createElement('project') 197 e = doc.createElement('project')
193 root.appendChild(e) 198 parent_node.appendChild(e)
194 e.setAttribute('name', p.name) 199 e.setAttribute('name', name)
195 if p.relpath != p.name: 200 if relpath != name:
196 e.setAttribute('path', p.relpath) 201 e.setAttribute('path', relpath)
197 if not d.remote or p.remote.name != d.remote.name: 202 if not d.remote or p.remote.name != d.remote.name:
198 e.setAttribute('remote', p.remote.name) 203 e.setAttribute('remote', p.remote.name)
199 if peg_rev: 204 if peg_rev:
@@ -231,6 +236,16 @@ class XmlManifest(object):
231 if p.sync_c: 236 if p.sync_c:
232 e.setAttribute('sync-c', 'true') 237 e.setAttribute('sync-c', 'true')
233 238
239 if p.subprojects:
240 sort_projects = [subp.name for subp in p.subprojects]
241 sort_projects.sort()
242 output_projects(p, e, sort_projects)
243
244 sort_projects = [key for key in self.projects.keys()
245 if not self.projects[key].parent]
246 sort_projects.sort()
247 output_projects(None, root, sort_projects)
248
234 if self._repo_hooks_project: 249 if self._repo_hooks_project:
235 root.appendChild(doc.createTextNode('')) 250 root.appendChild(doc.createTextNode(''))
236 e = doc.createElement('repo-hooks') 251 e = doc.createElement('repo-hooks')
@@ -383,11 +398,15 @@ class XmlManifest(object):
383 for node in itertools.chain(*node_list): 398 for node in itertools.chain(*node_list):
384 if node.nodeName == 'project': 399 if node.nodeName == 'project':
385 project = self._ParseProject(node) 400 project = self._ParseProject(node)
386 if self._projects.get(project.name): 401 def recursively_add_projects(project):
387 raise ManifestParseError( 402 if self._projects.get(project.name):
388 'duplicate project %s in %s' % 403 raise ManifestParseError(
389 (project.name, self.manifestFile)) 404 'duplicate project %s in %s' %
390 self._projects[project.name] = project 405 (project.name, self.manifestFile))
406 self._projects[project.name] = project
407 for subproject in project.subprojects:
408 recursively_add_projects(subproject)
409 recursively_add_projects(project)
391 if node.nodeName == 'repo-hooks': 410 if node.nodeName == 'repo-hooks':
392 # Get the name of the project and the (space-separated) list of enabled. 411 # Get the name of the project and the (space-separated) list of enabled.
393 repo_hooks_project = self._reqatt(node, 'in-project') 412 repo_hooks_project = self._reqatt(node, 'in-project')
@@ -537,11 +556,19 @@ class XmlManifest(object):
537 556
538 return '\n'.join(cleanLines) 557 return '\n'.join(cleanLines)
539 558
540 def _ParseProject(self, node): 559 def _JoinName(self, parent_name, name):
560 return os.path.join(parent_name, name)
561
562 def _UnjoinName(self, parent_name, name):
563 return os.path.relpath(name, parent_name)
564
565 def _ParseProject(self, node, parent = None):
541 """ 566 """
542 reads a <project> element from the manifest file 567 reads a <project> element from the manifest file
543 """ 568 """
544 name = self._reqatt(node, 'name') 569 name = self._reqatt(node, 'name')
570 if parent:
571 name = self._JoinName(parent.name, name)
545 572
546 remote = self._get_remote(node) 573 remote = self._get_remote(node)
547 if remote is None: 574 if remote is None:
@@ -586,37 +613,66 @@ class XmlManifest(object):
586 groups = node.getAttribute('groups') 613 groups = node.getAttribute('groups')
587 groups = [x for x in re.split('[,\s]+', groups) if x] 614 groups = [x for x in re.split('[,\s]+', groups) if x]
588 615
589 default_groups = ['all', 'name:%s' % name, 'path:%s' % path] 616 if parent is None:
590 groups.extend(set(default_groups).difference(groups)) 617 relpath, worktree, gitdir = self.GetProjectPaths(name, path)
591
592 if self.IsMirror:
593 worktree = None
594 gitdir = os.path.join(self.topdir, '%s.git' % name)
595 else: 618 else:
596 worktree = os.path.join(self.topdir, path).replace('\\', '/') 619 relpath, worktree, gitdir = self.GetSubprojectPaths(parent, path)
597 gitdir = os.path.join(self.repodir, 'projects/%s.git' % path) 620
621 default_groups = ['all', 'name:%s' % name, 'path:%s' % relpath]
622 groups.extend(set(default_groups).difference(groups))
598 623
599 project = Project(manifest = self, 624 project = Project(manifest = self,
600 name = name, 625 name = name,
601 remote = remote.ToRemoteSpec(name), 626 remote = remote.ToRemoteSpec(name),
602 gitdir = gitdir, 627 gitdir = gitdir,
603 worktree = worktree, 628 worktree = worktree,
604 relpath = path, 629 relpath = relpath,
605 revisionExpr = revisionExpr, 630 revisionExpr = revisionExpr,
606 revisionId = None, 631 revisionId = None,
607 rebase = rebase, 632 rebase = rebase,
608 groups = groups, 633 groups = groups,
609 sync_c = sync_c, 634 sync_c = sync_c,
610 upstream = upstream) 635 upstream = upstream,
636 parent = parent)
611 637
612 for n in node.childNodes: 638 for n in node.childNodes:
613 if n.nodeName == 'copyfile': 639 if n.nodeName == 'copyfile':
614 self._ParseCopyFile(project, n) 640 self._ParseCopyFile(project, n)
615 if n.nodeName == 'annotation': 641 if n.nodeName == 'annotation':
616 self._ParseAnnotation(project, n) 642 self._ParseAnnotation(project, n)
643 if n.nodeName == 'project':
644 project.subprojects.append(self._ParseProject(n, parent = project))
617 645
618 return project 646 return project
619 647
648 def GetProjectPaths(self, name, path):
649 relpath = path
650 if self.IsMirror:
651 worktree = None
652 gitdir = os.path.join(self.topdir, '%s.git' % name)
653 else:
654 worktree = os.path.join(self.topdir, path).replace('\\', '/')
655 gitdir = os.path.join(self.repodir, 'projects', '%s.git' % path)
656 return relpath, worktree, gitdir
657
658 def GetSubprojectName(self, parent, submodule_path):
659 return os.path.join(parent.name, submodule_path)
660
661 def _JoinRelpath(self, parent_relpath, relpath):
662 return os.path.join(parent_relpath, relpath)
663
664 def _UnjoinRelpath(self, parent_relpath, relpath):
665 return os.path.relpath(relpath, parent_relpath)
666
667 def GetSubprojectPaths(self, parent, path):
668 relpath = self._JoinRelpath(parent.relpath, path)
669 gitdir = os.path.join(parent.gitdir, 'subprojects', '%s.git' % path)
670 if self.IsMirror:
671 worktree = None
672 else:
673 worktree = os.path.join(parent.worktree, path).replace('\\', '/')
674 return relpath, worktree, gitdir
675
620 def _ParseCopyFile(self, project, node): 676 def _ParseCopyFile(self, project, node):
621 src = self._reqatt(node, 'src') 677 src = self._reqatt(node, 'src')
622 dest = self._reqatt(node, 'dest') 678 dest = self._reqatt(node, 'dest')