summaryrefslogtreecommitdiffstats
path: root/command.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 /command.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 'command.py')
-rw-r--r--command.py72
1 files changed, 48 insertions, 24 deletions
diff --git a/command.py b/command.py
index 5a5f468f..51c0cb48 100644
--- a/command.py
+++ b/command.py
@@ -60,6 +60,32 @@ class Command(object):
60 """ 60 """
61 raise NotImplementedError 61 raise NotImplementedError
62 62
63 def _ResetPathToProjectMap(self, projects):
64 self._by_path = dict((p.worktree, p) for p in projects)
65
66 def _UpdatePathToProjectMap(self, project):
67 self._by_path[project.worktree] = project
68
69 def _GetProjectByPath(self, path):
70 project = None
71 if os.path.exists(path):
72 oldpath = None
73 while path \
74 and path != oldpath \
75 and path != self.manifest.topdir:
76 try:
77 project = self._by_path[path]
78 break
79 except KeyError:
80 oldpath = path
81 path = os.path.dirname(path)
82 else:
83 try:
84 project = self._by_path[path]
85 except KeyError:
86 pass
87 return project
88
63 def GetProjects(self, args, missing_ok=False): 89 def GetProjects(self, args, missing_ok=False):
64 """A list of projects that match the arguments. 90 """A list of projects that match the arguments.
65 """ 91 """
@@ -74,40 +100,38 @@ class Command(object):
74 groups = [x for x in re.split('[,\s]+', groups) if x] 100 groups = [x for x in re.split('[,\s]+', groups) if x]
75 101
76 if not args: 102 if not args:
77 for project in all_projects.values(): 103 all_projects_list = all_projects.values()
104 derived_projects = []
105 for project in all_projects_list:
106 if project.Registered:
107 # Do not search registered subproject for derived projects
108 # since its parent has been searched already
109 continue
110 derived_projects.extend(project.GetDerivedSubprojects())
111 all_projects_list.extend(derived_projects)
112 for project in all_projects_list:
78 if ((missing_ok or project.Exists) and 113 if ((missing_ok or project.Exists) and
79 project.MatchesGroups(groups)): 114 project.MatchesGroups(groups)):
80 result.append(project) 115 result.append(project)
81 else: 116 else:
82 by_path = None 117 self._ResetPathToProjectMap(all_projects.values())
83 118
84 for arg in args: 119 for arg in args:
85 project = all_projects.get(arg) 120 project = all_projects.get(arg)
86 121
87 if not project: 122 if not project:
88 path = os.path.abspath(arg).replace('\\', '/') 123 path = os.path.abspath(arg).replace('\\', '/')
89 124 project = self._GetProjectByPath(path)
90 if not by_path: 125
91 by_path = dict() 126 # If it's not a derived project, update path->project mapping and
92 for p in all_projects.values(): 127 # search again, as arg might actually point to a derived subproject.
93 by_path[p.worktree] = p 128 if project and not project.Derived:
94 129 search_again = False
95 if os.path.exists(path): 130 for subproject in project.GetDerivedSubprojects():
96 oldpath = None 131 self._UpdatePathToProjectMap(subproject)
97 while path \ 132 search_again = True
98 and path != oldpath \ 133 if search_again:
99 and path != self.manifest.topdir: 134 project = self._GetProjectByPath(path) or project
100 try:
101 project = by_path[path]
102 break
103 except KeyError:
104 oldpath = path
105 path = os.path.dirname(path)
106 else:
107 try:
108 project = by_path[path]
109 except KeyError:
110 pass
111 135
112 if not project: 136 if not project:
113 raise NoSuchProjectError(arg) 137 raise NoSuchProjectError(arg)