diff options
-rw-r--r-- | docs/internal-fs-layout.md | 2 | ||||
-rw-r--r-- | git_superproject.py | 154 | ||||
-rwxr-xr-x | repo | 8 | ||||
-rw-r--r-- | subcmds/init.py | 20 | ||||
-rw-r--r-- | subcmds/sync.py | 22 | ||||
-rw-r--r-- | tests/test_git_superproject.py | 115 |
6 files changed, 185 insertions, 136 deletions
diff --git a/docs/internal-fs-layout.md b/docs/internal-fs-layout.md index 4d18bb31..53c42638 100644 --- a/docs/internal-fs-layout.md +++ b/docs/internal-fs-layout.md | |||
@@ -142,11 +142,13 @@ User controlled settings are initialized when running `repo init`. | |||
142 | | repo.partialclone | `--partial-clone` | Create [partial git clones] | | 142 | | repo.partialclone | `--partial-clone` | Create [partial git clones] | |
143 | | repo.reference | `--reference` | Reference repo client checkout | | 143 | | repo.reference | `--reference` | Reference repo client checkout | |
144 | | repo.submodules | `--submodules` | Sync git submodules | | 144 | | repo.submodules | `--submodules` | Sync git submodules | |
145 | | repo.superproject | `--use-superproject` | Sync [superproject] | | ||
145 | | repo.worktree | `--worktree` | Use `git worktree` for checkouts | | 146 | | repo.worktree | `--worktree` | Use `git worktree` for checkouts | |
146 | | user.email | `--config-name` | User's e-mail address; Copied into `.git/config` when checking out a new project | | 147 | | user.email | `--config-name` | User's e-mail address; Copied into `.git/config` when checking out a new project | |
147 | | user.name | `--config-name` | User's name; Copied into `.git/config` when checking out a new project | | 148 | | user.name | `--config-name` | User's name; Copied into `.git/config` when checking out a new project | |
148 | 149 | ||
149 | [partial git clones]: https://git-scm.com/docs/gitrepository-layout#_code_partialclone_code | 150 | [partial git clones]: https://git-scm.com/docs/gitrepository-layout#_code_partialclone_code |
151 | [superproject]: https://en.wikibooks.org/wiki/Git/Submodules_and_Superprojects | ||
150 | 152 | ||
151 | ### Repo hooks settings | 153 | ### Repo hooks settings |
152 | 154 | ||
diff --git a/git_superproject.py b/git_superproject.py index 378ede25..471dadc4 100644 --- a/git_superproject.py +++ b/git_superproject.py | |||
@@ -12,21 +12,22 @@ | |||
12 | # See the License for the specific language governing permissions and | 12 | # See the License for the specific language governing permissions and |
13 | # limitations under the License. | 13 | # limitations under the License. |
14 | 14 | ||
15 | """Provide functionality to get all projects and their SHAs from Superproject. | 15 | """Provide functionality to get all projects and their commit ids from Superproject. |
16 | 16 | ||
17 | For more information on superproject, check out: | 17 | For more information on superproject, check out: |
18 | https://en.wikibooks.org/wiki/Git/Submodules_and_Superprojects | 18 | https://en.wikibooks.org/wiki/Git/Submodules_and_Superprojects |
19 | 19 | ||
20 | Examples: | 20 | Examples: |
21 | superproject = Superproject() | 21 | superproject = Superproject() |
22 | project_shas = superproject.GetAllProjectsSHAs() | 22 | project_commit_ids = superproject.UpdateProjectsRevisionId(projects) |
23 | """ | 23 | """ |
24 | 24 | ||
25 | import os | 25 | import os |
26 | import sys | 26 | import sys |
27 | 27 | ||
28 | from error import BUG_REPORT_URL, GitError | 28 | from error import BUG_REPORT_URL |
29 | from git_command import GitCommand | 29 | from git_command import GitCommand |
30 | from git_refs import R_HEADS | ||
30 | import platform_utils | 31 | import platform_utils |
31 | 32 | ||
32 | _SUPERPROJECT_GIT_NAME = 'superproject.git' | 33 | _SUPERPROJECT_GIT_NAME = 'superproject.git' |
@@ -34,19 +35,24 @@ _SUPERPROJECT_MANIFEST_NAME = 'superproject_override.xml' | |||
34 | 35 | ||
35 | 36 | ||
36 | class Superproject(object): | 37 | class Superproject(object): |
37 | """Get SHAs from superproject. | 38 | """Get commit ids from superproject. |
38 | 39 | ||
39 | It does a 'git clone' of superproject and 'git ls-tree' to get list of SHAs for all projects. | 40 | It does a 'git clone' of superproject and 'git ls-tree' to get list of commit ids |
40 | It contains project_shas which is a dictionary with project/sha entries. | 41 | for all projects. It contains project_commit_ids which is a dictionary with |
42 | project/commit id entries. | ||
41 | """ | 43 | """ |
42 | def __init__(self, repodir, superproject_dir='exp-superproject'): | 44 | def __init__(self, manifest, repodir, superproject_dir='exp-superproject'): |
43 | """Initializes superproject. | 45 | """Initializes superproject. |
44 | 46 | ||
45 | Args: | 47 | Args: |
48 | manifest: A Manifest object that is to be written to a file. | ||
46 | repodir: Path to the .repo/ dir for holding all internal checkout state. | 49 | repodir: Path to the .repo/ dir for holding all internal checkout state. |
50 | It must be in the top directory of the repo client checkout. | ||
47 | superproject_dir: Relative path under |repodir| to checkout superproject. | 51 | superproject_dir: Relative path under |repodir| to checkout superproject. |
48 | """ | 52 | """ |
49 | self._project_shas = None | 53 | self._project_commit_ids = None |
54 | self._manifest = manifest | ||
55 | self._branch = self._GetBranch() | ||
50 | self._repodir = os.path.abspath(repodir) | 56 | self._repodir = os.path.abspath(repodir) |
51 | self._superproject_dir = superproject_dir | 57 | self._superproject_dir = superproject_dir |
52 | self._superproject_path = os.path.join(self._repodir, superproject_dir) | 58 | self._superproject_path = os.path.join(self._repodir, superproject_dir) |
@@ -56,25 +62,35 @@ class Superproject(object): | |||
56 | _SUPERPROJECT_GIT_NAME) | 62 | _SUPERPROJECT_GIT_NAME) |
57 | 63 | ||
58 | @property | 64 | @property |
59 | def project_shas(self): | 65 | def project_commit_ids(self): |
60 | """Returns a dictionary of projects and their SHAs.""" | 66 | """Returns a dictionary of projects and their commit ids.""" |
61 | return self._project_shas | 67 | return self._project_commit_ids |
68 | |||
69 | def _GetBranch(self): | ||
70 | """Returns the branch name for getting the approved manifest.""" | ||
71 | p = self._manifest.manifestProject | ||
72 | b = p.GetBranch(p.CurrentBranch) | ||
73 | if not b: | ||
74 | return None | ||
75 | branch = b.merge | ||
76 | if branch and branch.startswith(R_HEADS): | ||
77 | branch = branch[len(R_HEADS):] | ||
78 | return branch | ||
62 | 79 | ||
63 | def _Clone(self, url, branch=None): | 80 | def _Clone(self, url): |
64 | """Do a 'git clone' for the given url and branch. | 81 | """Do a 'git clone' for the given url. |
65 | 82 | ||
66 | Args: | 83 | Args: |
67 | url: superproject's url to be passed to git clone. | 84 | url: superproject's url to be passed to git clone. |
68 | branch: The branchname to be passed as argument to git clone. | ||
69 | 85 | ||
70 | Returns: | 86 | Returns: |
71 | True if 'git clone <url> <branch>' is successful, or False. | 87 | True if git clone is successful, or False. |
72 | """ | 88 | """ |
73 | if not os.path.exists(self._superproject_path): | 89 | if not os.path.exists(self._superproject_path): |
74 | os.mkdir(self._superproject_path) | 90 | os.mkdir(self._superproject_path) |
75 | cmd = ['clone', url, '--filter', 'blob:none', '--bare'] | 91 | cmd = ['clone', url, '--filter', 'blob:none', '--bare'] |
76 | if branch: | 92 | if self._branch: |
77 | cmd += ['--branch', branch] | 93 | cmd += ['--branch', self._branch] |
78 | p = GitCommand(None, | 94 | p = GitCommand(None, |
79 | cmd, | 95 | cmd, |
80 | cwd=self._superproject_path, | 96 | cwd=self._superproject_path, |
@@ -112,22 +128,20 @@ class Superproject(object): | |||
112 | return False | 128 | return False |
113 | return True | 129 | return True |
114 | 130 | ||
115 | def _LsTree(self, branch='HEAD'): | 131 | def _LsTree(self): |
116 | """Returns the data from 'git ls-tree -r <branch>'. | 132 | """Returns the data from 'git ls-tree ...'. |
117 | 133 | ||
118 | Works only in git repositories. | 134 | Works only in git repositories. |
119 | 135 | ||
120 | Args: | ||
121 | branch: The branchname to be passed as argument to git ls-tree. | ||
122 | |||
123 | Returns: | 136 | Returns: |
124 | data: data returned from 'git ls-tree -r HEAD' instead of None. | 137 | data: data returned from 'git ls-tree ...' instead of None. |
125 | """ | 138 | """ |
126 | if not os.path.exists(self._work_git): | 139 | if not os.path.exists(self._work_git): |
127 | print('git ls-tree missing drectory: %s' % self._work_git, | 140 | print('git ls-tree missing drectory: %s' % self._work_git, |
128 | file=sys.stderr) | 141 | file=sys.stderr) |
129 | return None | 142 | return None |
130 | data = None | 143 | data = None |
144 | branch = 'HEAD' if not self._branch else self._branch | ||
131 | cmd = ['ls-tree', '-z', '-r', branch] | 145 | cmd = ['ls-tree', '-z', '-r', branch] |
132 | 146 | ||
133 | p = GitCommand(None, | 147 | p = GitCommand(None, |
@@ -145,18 +159,25 @@ class Superproject(object): | |||
145 | retval, p.stderr), file=sys.stderr) | 159 | retval, p.stderr), file=sys.stderr) |
146 | return data | 160 | return data |
147 | 161 | ||
148 | def _GetAllProjectsSHAs(self, url, branch=None): | 162 | def Sync(self): |
149 | """Get SHAs for all projects from superproject and save them in _project_shas. | 163 | """Sync superproject either by git clone/fetch. |
150 | |||
151 | Args: | ||
152 | url: superproject's url to be passed to git clone or fetch. | ||
153 | branch: The branchname to be passed as argument to git clone or fetch. | ||
154 | 164 | ||
155 | Returns: | 165 | Returns: |
156 | A dictionary with the projects/SHAs instead of None. | 166 | True if sync of superproject is successful, or False. |
157 | """ | 167 | """ |
168 | print('WARNING: --use-superproject is experimental and not ' | ||
169 | 'for general use', file=sys.stderr) | ||
170 | |||
171 | if not self._manifest.superproject: | ||
172 | print('error: superproject tag is not defined in manifest', | ||
173 | file=sys.stderr) | ||
174 | return False | ||
175 | |||
176 | url = self._manifest.superproject['remote'].url | ||
158 | if not url: | 177 | if not url: |
159 | raise ValueError('url argument is not supplied.') | 178 | print('error: superproject URL is not defined in manifest', |
179 | file=sys.stderr) | ||
180 | return False | ||
160 | 181 | ||
161 | do_clone = True | 182 | do_clone = True |
162 | if os.path.exists(self._superproject_path): | 183 | if os.path.exists(self._superproject_path): |
@@ -166,35 +187,44 @@ class Superproject(object): | |||
166 | else: | 187 | else: |
167 | do_clone = False | 188 | do_clone = False |
168 | if do_clone: | 189 | if do_clone: |
169 | if not self._Clone(url, branch): | 190 | if not self._Clone(url): |
170 | raise GitError('git clone failed for url: %s' % url) | 191 | print('error: git clone failed for url: %s' % url, file=sys.stderr) |
192 | return False | ||
193 | return True | ||
171 | 194 | ||
172 | data = self._LsTree(branch) | 195 | def _GetAllProjectsCommitIds(self): |
196 | """Get commit ids for all projects from superproject and save them in _project_commit_ids. | ||
197 | |||
198 | Returns: | ||
199 | A dictionary with the projects/commit ids on success, otherwise None. | ||
200 | """ | ||
201 | if not self.Sync(): | ||
202 | return None | ||
203 | |||
204 | data = self._LsTree() | ||
173 | if not data: | 205 | if not data: |
174 | raise GitError('git ls-tree failed for url: %s' % url) | 206 | print('error: git ls-tree failed for superproject', file=sys.stderr) |
207 | return None | ||
175 | 208 | ||
176 | # Parse lines like the following to select lines starting with '160000' and | 209 | # Parse lines like the following to select lines starting with '160000' and |
177 | # build a dictionary with project path (last element) and its SHA (3rd element). | 210 | # build a dictionary with project path (last element) and its commit id (3rd element). |
178 | # | 211 | # |
179 | # 160000 commit 2c2724cb36cd5a9cec6c852c681efc3b7c6b86ea\tart\x00 | 212 | # 160000 commit 2c2724cb36cd5a9cec6c852c681efc3b7c6b86ea\tart\x00 |
180 | # 120000 blob acc2cbdf438f9d2141f0ae424cec1d8fc4b5d97f\tbootstrap.bash\x00 | 213 | # 120000 blob acc2cbdf438f9d2141f0ae424cec1d8fc4b5d97f\tbootstrap.bash\x00 |
181 | shas = {} | 214 | commit_ids = {} |
182 | for line in data.split('\x00'): | 215 | for line in data.split('\x00'): |
183 | ls_data = line.split(None, 3) | 216 | ls_data = line.split(None, 3) |
184 | if not ls_data: | 217 | if not ls_data: |
185 | break | 218 | break |
186 | if ls_data[0] == '160000': | 219 | if ls_data[0] == '160000': |
187 | shas[ls_data[3]] = ls_data[2] | 220 | commit_ids[ls_data[3]] = ls_data[2] |
188 | 221 | ||
189 | self._project_shas = shas | 222 | self._project_commit_ids = commit_ids |
190 | return shas | 223 | return commit_ids |
191 | 224 | ||
192 | def _WriteManfiestFile(self, manifest): | 225 | def _WriteManfiestFile(self): |
193 | """Writes manifest to a file. | 226 | """Writes manifest to a file. |
194 | 227 | ||
195 | Args: | ||
196 | manifest: A Manifest object that is to be written to a file. | ||
197 | |||
198 | Returns: | 228 | Returns: |
199 | manifest_path: Path name of the file into which manifest is written instead of None. | 229 | manifest_path: Path name of the file into which manifest is written instead of None. |
200 | """ | 230 | """ |
@@ -203,7 +233,7 @@ class Superproject(object): | |||
203 | self._superproject_path, | 233 | self._superproject_path, |
204 | file=sys.stderr) | 234 | file=sys.stderr) |
205 | return None | 235 | return None |
206 | manifest_str = manifest.ToXml().toxml() | 236 | manifest_str = self._manifest.ToXml().toxml() |
207 | manifest_path = self._manifest_path | 237 | manifest_path = self._manifest_path |
208 | try: | 238 | try: |
209 | with open(manifest_path, 'w', encoding='utf-8') as fp: | 239 | with open(manifest_path, 'w', encoding='utf-8') as fp: |
@@ -215,40 +245,34 @@ class Superproject(object): | |||
215 | return None | 245 | return None |
216 | return manifest_path | 246 | return manifest_path |
217 | 247 | ||
218 | def UpdateProjectsRevisionId(self, manifest, projects, url, branch=None): | 248 | def UpdateProjectsRevisionId(self, projects): |
219 | """Update revisionId of every project in projects with the SHA. | 249 | """Update revisionId of every project in projects with the commit id. |
220 | 250 | ||
221 | Args: | 251 | Args: |
222 | manifest: A Manifest object that is to be written to a file. | ||
223 | projects: List of projects whose revisionId needs to be updated. | 252 | projects: List of projects whose revisionId needs to be updated. |
224 | url: superproject's url to be passed to git clone or fetch. | ||
225 | branch: The branchname to be passed as argument to git clone or fetch. | ||
226 | 253 | ||
227 | Returns: | 254 | Returns: |
228 | manifest_path: Path name of the overriding manfiest file instead of None. | 255 | manifest_path: Path name of the overriding manfiest file instead of None. |
229 | """ | 256 | """ |
230 | try: | 257 | commit_ids = self._GetAllProjectsCommitIds() |
231 | shas = self._GetAllProjectsSHAs(url=url, branch=branch) | 258 | if not commit_ids: |
232 | except Exception as e: | 259 | print('error: Cannot get project commit ids from manifest', file=sys.stderr) |
233 | print('error: Cannot get project SHAs for %s: %s: %s' % | ||
234 | (url, type(e).__name__, str(e)), | ||
235 | file=sys.stderr) | ||
236 | return None | 260 | return None |
237 | 261 | ||
238 | projects_missing_shas = [] | 262 | projects_missing_commit_ids = [] |
239 | for project in projects: | 263 | for project in projects: |
240 | path = project.relpath | 264 | path = project.relpath |
241 | if not path: | 265 | if not path: |
242 | continue | 266 | continue |
243 | sha = shas.get(path) | 267 | commit_id = commit_ids.get(path) |
244 | if sha: | 268 | if commit_id: |
245 | project.SetRevisionId(sha) | 269 | project.SetRevisionId(commit_id) |
246 | else: | 270 | else: |
247 | projects_missing_shas.append(path) | 271 | projects_missing_commit_ids.append(path) |
248 | if projects_missing_shas: | 272 | if projects_missing_commit_ids: |
249 | print('error: please file a bug using %s to report missing shas for: %s' % | 273 | print('error: please file a bug using %s to report missing commit_ids for: %s' % |
250 | (BUG_REPORT_URL, projects_missing_shas), file=sys.stderr) | 274 | (BUG_REPORT_URL, projects_missing_commit_ids), file=sys.stderr) |
251 | return None | 275 | return None |
252 | 276 | ||
253 | manifest_path = self._WriteManfiestFile(manifest) | 277 | manifest_path = self._WriteManfiestFile() |
254 | return manifest_path | 278 | return manifest_path |
@@ -324,6 +324,11 @@ def GetParser(gitc_init=False): | |||
324 | 'each project. See git archive.') | 324 | 'each project. See git archive.') |
325 | group.add_option('--submodules', action='store_true', | 325 | group.add_option('--submodules', action='store_true', |
326 | help='sync any submodules associated with the manifest repo') | 326 | help='sync any submodules associated with the manifest repo') |
327 | group.add_option('--use-superproject', action='store_true', default=None, | ||
328 | help='use the manifest superproject to sync projects') | ||
329 | group.add_option('--no-use-superproject', action='store_false', | ||
330 | dest='use_superproject', | ||
331 | help='disable use of manifest superprojects') | ||
327 | group.add_option('-g', '--groups', default='default', | 332 | group.add_option('-g', '--groups', default='default', |
328 | help='restrict manifest projects to ones with specified ' | 333 | help='restrict manifest projects to ones with specified ' |
329 | 'group(s) [default|all|G1,G2,G3|G4,-G5,-G6]', | 334 | 'group(s) [default|all|G1,G2,G3|G4,-G5,-G6]', |
@@ -333,7 +338,8 @@ def GetParser(gitc_init=False): | |||
333 | 'platform group [auto|all|none|linux|darwin|...]', | 338 | 'platform group [auto|all|none|linux|darwin|...]', |
334 | metavar='PLATFORM') | 339 | metavar='PLATFORM') |
335 | group.add_option('--clone-bundle', action='store_true', | 340 | group.add_option('--clone-bundle', action='store_true', |
336 | help='enable use of /clone.bundle on HTTP/HTTPS (default if not --partial-clone)') | 341 | help='enable use of /clone.bundle on HTTP/HTTPS ' |
342 | '(default if not --partial-clone)') | ||
337 | group.add_option('--no-clone-bundle', | 343 | group.add_option('--no-clone-bundle', |
338 | dest='clone_bundle', action='store_false', | 344 | dest='clone_bundle', action='store_false', |
339 | help='disable use of /clone.bundle on HTTP/HTTPS (default if --partial-clone)') | 345 | help='disable use of /clone.bundle on HTTP/HTTPS (default if --partial-clone)') |
diff --git a/subcmds/init.py b/subcmds/init.py index fe3ebd2c..1d16c856 100644 --- a/subcmds/init.py +++ b/subcmds/init.py | |||
@@ -25,6 +25,7 @@ from error import ManifestParseError | |||
25 | from project import SyncBuffer | 25 | from project import SyncBuffer |
26 | from git_config import GitConfig | 26 | from git_config import GitConfig |
27 | from git_command import git_require, MIN_GIT_VERSION_SOFT, MIN_GIT_VERSION_HARD | 27 | from git_command import git_require, MIN_GIT_VERSION_SOFT, MIN_GIT_VERSION_HARD |
28 | import git_superproject | ||
28 | import platform_utils | 29 | import platform_utils |
29 | from wrapper import Wrapper | 30 | from wrapper import Wrapper |
30 | 31 | ||
@@ -134,6 +135,11 @@ to update the working directory files. | |||
134 | g.add_option('--submodules', | 135 | g.add_option('--submodules', |
135 | dest='submodules', action='store_true', | 136 | dest='submodules', action='store_true', |
136 | help='sync any submodules associated with the manifest repo') | 137 | help='sync any submodules associated with the manifest repo') |
138 | g.add_option('--use-superproject', action='store_true', | ||
139 | help='use the manifest superproject to sync projects') | ||
140 | g.add_option('--no-use-superproject', action='store_false', | ||
141 | dest='use_superproject', | ||
142 | help='disable use of manifest superprojects') | ||
137 | g.add_option('-g', '--groups', | 143 | g.add_option('-g', '--groups', |
138 | dest='groups', default='default', | 144 | dest='groups', default='default', |
139 | help='restrict manifest projects to ones with specified ' | 145 | help='restrict manifest projects to ones with specified ' |
@@ -176,6 +182,14 @@ to update the working directory files. | |||
176 | return {'REPO_MANIFEST_URL': 'manifest_url', | 182 | return {'REPO_MANIFEST_URL': 'manifest_url', |
177 | 'REPO_MIRROR_LOCATION': 'reference'} | 183 | 'REPO_MIRROR_LOCATION': 'reference'} |
178 | 184 | ||
185 | def _CloneSuperproject(self): | ||
186 | """Clone the superproject based on the superproject's url and branch.""" | ||
187 | superproject = git_superproject.Superproject(self.manifest, | ||
188 | self.repodir) | ||
189 | if not superproject.Sync(): | ||
190 | print('error: git update of superproject failed', file=sys.stderr) | ||
191 | sys.exit(1) | ||
192 | |||
179 | def _SyncManifest(self, opt): | 193 | def _SyncManifest(self, opt): |
180 | m = self.manifest.manifestProject | 194 | m = self.manifest.manifestProject |
181 | is_new = not m.Exists | 195 | is_new = not m.Exists |
@@ -305,6 +319,9 @@ to update the working directory files. | |||
305 | if opt.submodules: | 319 | if opt.submodules: |
306 | m.config.SetBoolean('repo.submodules', opt.submodules) | 320 | m.config.SetBoolean('repo.submodules', opt.submodules) |
307 | 321 | ||
322 | if opt.use_superproject is not None: | ||
323 | m.config.SetBoolean('repo.superproject', opt.use_superproject) | ||
324 | |||
308 | if not m.Sync_NetworkHalf(is_new=is_new, quiet=opt.quiet, verbose=opt.verbose, | 325 | if not m.Sync_NetworkHalf(is_new=is_new, quiet=opt.quiet, verbose=opt.verbose, |
309 | clone_bundle=opt.clone_bundle, | 326 | clone_bundle=opt.clone_bundle, |
310 | current_branch_only=opt.current_branch_only, | 327 | current_branch_only=opt.current_branch_only, |
@@ -519,6 +536,9 @@ to update the working directory files. | |||
519 | self._SyncManifest(opt) | 536 | self._SyncManifest(opt) |
520 | self._LinkManifest(opt.manifest_name) | 537 | self._LinkManifest(opt.manifest_name) |
521 | 538 | ||
539 | if self.manifest.manifestProject.config.GetBoolean('repo.superproject'): | ||
540 | self._CloneSuperproject() | ||
541 | |||
522 | if os.isatty(0) and os.isatty(1) and not self.manifest.IsMirror: | 542 | if os.isatty(0) and os.isatty(1) and not self.manifest.IsMirror: |
523 | if opt.config_name or self._ShouldConfigureUser(opt): | 543 | if opt.config_name or self._ShouldConfigureUser(opt): |
524 | self._ConfigureUser(opt) | 544 | self._ConfigureUser(opt) |
diff --git a/subcmds/sync.py b/subcmds/sync.py index 5020ea7a..eda95f96 100644 --- a/subcmds/sync.py +++ b/subcmds/sync.py | |||
@@ -294,28 +294,12 @@ later is required to fix a server side protocol bug. | |||
294 | Returns: | 294 | Returns: |
295 | Returns path to the overriding manifest file. | 295 | Returns path to the overriding manifest file. |
296 | """ | 296 | """ |
297 | if not self.manifest.superproject: | 297 | superproject = git_superproject.Superproject(self.manifest, |
298 | print('error: superproject tag is not defined in manifest.xml', | 298 | self.repodir) |
299 | file=sys.stderr) | ||
300 | sys.exit(1) | ||
301 | print('WARNING: --use-superproject is experimental and not ' | ||
302 | 'for general use', file=sys.stderr) | ||
303 | |||
304 | superproject_url = self.manifest.superproject['remote'].url | ||
305 | if not superproject_url: | ||
306 | print('error: superproject URL is not defined in manifest.xml', | ||
307 | file=sys.stderr) | ||
308 | sys.exit(1) | ||
309 | |||
310 | superproject = git_superproject.Superproject(self.manifest.repodir) | ||
311 | all_projects = self.GetProjects(args, | 299 | all_projects = self.GetProjects(args, |
312 | missing_ok=True, | 300 | missing_ok=True, |
313 | submodules_ok=opt.fetch_submodules) | 301 | submodules_ok=opt.fetch_submodules) |
314 | branch = self._GetBranch() | 302 | manifest_path = superproject.UpdateProjectsRevisionId(all_projects) |
315 | manifest_path = superproject.UpdateProjectsRevisionId(self.manifest, | ||
316 | all_projects, | ||
317 | url=superproject_url, | ||
318 | branch=branch) | ||
319 | if not manifest_path: | 303 | if not manifest_path: |
320 | print('error: Update of revsionId from superproject has failed', | 304 | print('error: Update of revsionId from superproject has failed', |
321 | file=sys.stderr) | 305 | file=sys.stderr) |
diff --git a/tests/test_git_superproject.py b/tests/test_git_superproject.py index fc9101dd..d2ee9f4f 100644 --- a/tests/test_git_superproject.py +++ b/tests/test_git_superproject.py | |||
@@ -19,7 +19,6 @@ import tempfile | |||
19 | import unittest | 19 | import unittest |
20 | from unittest import mock | 20 | from unittest import mock |
21 | 21 | ||
22 | from error import GitError | ||
23 | import git_superproject | 22 | import git_superproject |
24 | import manifest_xml | 23 | import manifest_xml |
25 | import platform_utils | 24 | import platform_utils |
@@ -32,7 +31,6 @@ class SuperprojectTestCase(unittest.TestCase): | |||
32 | """Set up superproject every time.""" | 31 | """Set up superproject every time.""" |
33 | self.tempdir = tempfile.mkdtemp(prefix='repo_tests') | 32 | self.tempdir = tempfile.mkdtemp(prefix='repo_tests') |
34 | self.repodir = os.path.join(self.tempdir, '.repo') | 33 | self.repodir = os.path.join(self.tempdir, '.repo') |
35 | self._superproject = git_superproject.Superproject(self.repodir) | ||
36 | self.manifest_file = os.path.join( | 34 | self.manifest_file = os.path.join( |
37 | self.repodir, manifest_xml.MANIFEST_FILE_NAME) | 35 | self.repodir, manifest_xml.MANIFEST_FILE_NAME) |
38 | os.mkdir(self.repodir) | 36 | os.mkdir(self.repodir) |
@@ -45,6 +43,16 @@ class SuperprojectTestCase(unittest.TestCase): | |||
45 | url = https://localhost:0/manifest | 43 | url = https://localhost:0/manifest |
46 | """) | 44 | """) |
47 | 45 | ||
46 | manifest = self.getXmlManifest(""" | ||
47 | <manifest> | ||
48 | <remote name="default-remote" fetch="http://localhost" /> | ||
49 | <default remote="default-remote" revision="refs/heads/main" /> | ||
50 | <superproject name="superproject"/> | ||
51 | <project path="art" name="platform/art" /> | ||
52 | </manifest> | ||
53 | """) | ||
54 | self._superproject = git_superproject.Superproject(manifest, self.repodir) | ||
55 | |||
48 | def tearDown(self): | 56 | def tearDown(self): |
49 | """Tear down superproject every time.""" | 57 | """Tear down superproject every time.""" |
50 | platform_utils.rmtree(self.tempdir) | 58 | platform_utils.rmtree(self.tempdir) |
@@ -55,37 +63,53 @@ class SuperprojectTestCase(unittest.TestCase): | |||
55 | fp.write(data) | 63 | fp.write(data) |
56 | return manifest_xml.XmlManifest(self.repodir, self.manifest_file) | 64 | return manifest_xml.XmlManifest(self.repodir, self.manifest_file) |
57 | 65 | ||
58 | def test_superproject_get_project_shas_no_url(self): | 66 | def test_superproject_get_superproject_no_superproject(self): |
59 | """Test with no url.""" | 67 | """Test with no url.""" |
60 | with self.assertRaises(ValueError): | 68 | manifest = self.getXmlManifest(""" |
61 | self._superproject._GetAllProjectsSHAs(url=None) | 69 | <manifest> |
70 | </manifest> | ||
71 | """) | ||
72 | superproject = git_superproject.Superproject(manifest, self.repodir) | ||
73 | self.assertFalse(superproject.Sync()) | ||
62 | 74 | ||
63 | def test_superproject_get_project_shas_invalid_url(self): | 75 | def test_superproject_get_superproject_invalid_url(self): |
64 | """Test with an invalid url.""" | 76 | """Test with an invalid url.""" |
65 | with self.assertRaises(GitError): | 77 | manifest = self.getXmlManifest(""" |
66 | self._superproject._GetAllProjectsSHAs(url='localhost') | 78 | <manifest> |
79 | <remote name="test-remote" fetch="localhost" /> | ||
80 | <default remote="test-remote" revision="refs/heads/main" /> | ||
81 | <superproject name="superproject"/> | ||
82 | </manifest> | ||
83 | """) | ||
84 | superproject = git_superproject.Superproject(manifest, self.repodir) | ||
85 | self.assertFalse(superproject.Sync()) | ||
67 | 86 | ||
68 | def test_superproject_get_project_shas_invalid_branch(self): | 87 | def test_superproject_get_superproject_invalid_branch(self): |
69 | """Test with an invalid branch.""" | 88 | """Test with an invalid branch.""" |
70 | with self.assertRaises(GitError): | 89 | manifest = self.getXmlManifest(""" |
71 | self._superproject._GetAllProjectsSHAs( | 90 | <manifest> |
72 | url='sso://android/platform/superproject', | 91 | <remote name="test-remote" fetch="localhost" /> |
73 | branch='junk') | 92 | <default remote="test-remote" revision="refs/heads/main" /> |
93 | <superproject name="superproject"/> | ||
94 | </manifest> | ||
95 | """) | ||
96 | superproject = git_superproject.Superproject(manifest, self.repodir) | ||
97 | with mock.patch.object(self._superproject, '_GetBranch', return_value='junk'): | ||
98 | self.assertFalse(superproject.Sync()) | ||
74 | 99 | ||
75 | def test_superproject_get_project_shas_mock_clone(self): | 100 | def test_superproject_get_superproject_mock_clone(self): |
76 | """Test with _Clone failing.""" | 101 | """Test with _Clone failing.""" |
77 | with self.assertRaises(GitError): | 102 | with mock.patch.object(self._superproject, '_Clone', return_value=False): |
78 | with mock.patch.object(self._superproject, '_Clone', return_value=False): | 103 | self.assertFalse(self._superproject.Sync()) |
79 | self._superproject._GetAllProjectsSHAs(url='localhost') | 104 | |
80 | 105 | def test_superproject_get_superproject_mock_fetch(self): | |
81 | def test_superproject_get_project_shas_mock_fetch(self): | 106 | """Test with _Fetch failing and _clone being called.""" |
82 | """Test with _Fetch failing.""" | 107 | with mock.patch.object(self._superproject, '_Clone', return_value=True): |
83 | with self.assertRaises(GitError): | 108 | os.mkdir(self._superproject._superproject_path) |
84 | with mock.patch.object(self._superproject, '_Clone', return_value=True): | 109 | with mock.patch.object(self._superproject, '_Fetch', return_value=False): |
85 | with mock.patch.object(self._superproject, '_Fetch', return_value=False): | 110 | self.assertTrue(self._superproject.Sync()) |
86 | self._superproject._GetAllProjectsSHAs(url='localhost') | 111 | |
87 | 112 | def test_superproject_get_all_project_commit_ids_mock_ls_tree(self): | |
88 | def test_superproject_get_project_shas_mock_ls_tree(self): | ||
89 | """Test with LsTree being a mock.""" | 113 | """Test with LsTree being a mock.""" |
90 | data = ('120000 blob 158258bdf146f159218e2b90f8b699c4d85b5804\tAndroid.bp\x00' | 114 | data = ('120000 blob 158258bdf146f159218e2b90f8b699c4d85b5804\tAndroid.bp\x00' |
91 | '160000 commit 2c2724cb36cd5a9cec6c852c681efc3b7c6b86ea\tart\x00' | 115 | '160000 commit 2c2724cb36cd5a9cec6c852c681efc3b7c6b86ea\tart\x00' |
@@ -94,8 +118,8 @@ class SuperprojectTestCase(unittest.TestCase): | |||
94 | '160000 commit ade9b7a0d874e25fff4bf2552488825c6f111928\tbuild/bazel\x00') | 118 | '160000 commit ade9b7a0d874e25fff4bf2552488825c6f111928\tbuild/bazel\x00') |
95 | with mock.patch.object(self._superproject, '_Clone', return_value=True): | 119 | with mock.patch.object(self._superproject, '_Clone', return_value=True): |
96 | with mock.patch.object(self._superproject, '_LsTree', return_value=data): | 120 | with mock.patch.object(self._superproject, '_LsTree', return_value=data): |
97 | shas = self._superproject._GetAllProjectsSHAs(url='localhost', branch='junk') | 121 | commit_ids = self._superproject._GetAllProjectsCommitIds() |
98 | self.assertEqual(shas, { | 122 | self.assertEqual(commit_ids, { |
99 | 'art': '2c2724cb36cd5a9cec6c852c681efc3b7c6b86ea', | 123 | 'art': '2c2724cb36cd5a9cec6c852c681efc3b7c6b86ea', |
100 | 'bootable/recovery': 'e9d25da64d8d365dbba7c8ee00fe8c4473fe9a06', | 124 | 'bootable/recovery': 'e9d25da64d8d365dbba7c8ee00fe8c4473fe9a06', |
101 | 'build/bazel': 'ade9b7a0d874e25fff4bf2552488825c6f111928' | 125 | 'build/bazel': 'ade9b7a0d874e25fff4bf2552488825c6f111928' |
@@ -103,19 +127,12 @@ class SuperprojectTestCase(unittest.TestCase): | |||
103 | 127 | ||
104 | def test_superproject_write_manifest_file(self): | 128 | def test_superproject_write_manifest_file(self): |
105 | """Test with writing manifest to a file after setting revisionId.""" | 129 | """Test with writing manifest to a file after setting revisionId.""" |
106 | manifest = self.getXmlManifest(""" | 130 | self.assertEqual(len(self._superproject._manifest.projects), 1) |
107 | <manifest> | 131 | project = self._superproject._manifest.projects[0] |
108 | <remote name="default-remote" fetch="http://localhost" /> | ||
109 | <default remote="default-remote" revision="refs/heads/main" /> | ||
110 | <project name="test-name"/> | ||
111 | </manifest> | ||
112 | """) | ||
113 | self.assertEqual(len(manifest.projects), 1) | ||
114 | project = manifest.projects[0] | ||
115 | project.SetRevisionId('ABCDEF') | 132 | project.SetRevisionId('ABCDEF') |
116 | # Create temporary directory so that it can write the file. | 133 | # Create temporary directory so that it can write the file. |
117 | os.mkdir(self._superproject._superproject_path) | 134 | os.mkdir(self._superproject._superproject_path) |
118 | manifest_path = self._superproject._WriteManfiestFile(manifest) | 135 | manifest_path = self._superproject._WriteManfiestFile() |
119 | self.assertIsNotNone(manifest_path) | 136 | self.assertIsNotNone(manifest_path) |
120 | with open(manifest_path, 'r') as fp: | 137 | with open(manifest_path, 'r') as fp: |
121 | manifest_xml = fp.read() | 138 | manifest_xml = fp.read() |
@@ -124,29 +141,24 @@ class SuperprojectTestCase(unittest.TestCase): | |||
124 | '<?xml version="1.0" ?><manifest>' + | 141 | '<?xml version="1.0" ?><manifest>' + |
125 | '<remote name="default-remote" fetch="http://localhost"/>' + | 142 | '<remote name="default-remote" fetch="http://localhost"/>' + |
126 | '<default remote="default-remote" revision="refs/heads/main"/>' + | 143 | '<default remote="default-remote" revision="refs/heads/main"/>' + |
127 | '<project name="test-name" revision="ABCDEF"/>' + | 144 | '<project name="platform/art" path="art" revision="ABCDEF"/>' + |
145 | '<superproject name="superproject"/>' + | ||
128 | '</manifest>') | 146 | '</manifest>') |
129 | 147 | ||
130 | def test_superproject_update_project_revision_id(self): | 148 | def test_superproject_update_project_revision_id(self): |
131 | """Test with LsTree being a mock.""" | 149 | """Test with LsTree being a mock.""" |
132 | manifest = self.getXmlManifest(""" | 150 | self.assertEqual(len(self._superproject._manifest.projects), 1) |
133 | <manifest> | 151 | projects = self._superproject._manifest.projects |
134 | <remote name="default-remote" fetch="http://localhost" /> | ||
135 | <default remote="default-remote" revision="refs/heads/main" /> | ||
136 | <project path="art" name="platform/art" /> | ||
137 | </manifest> | ||
138 | """) | ||
139 | self.assertEqual(len(manifest.projects), 1) | ||
140 | projects = manifest.projects | ||
141 | data = ('160000 commit 2c2724cb36cd5a9cec6c852c681efc3b7c6b86ea\tart\x00' | 152 | data = ('160000 commit 2c2724cb36cd5a9cec6c852c681efc3b7c6b86ea\tart\x00' |
142 | '160000 commit e9d25da64d8d365dbba7c8ee00fe8c4473fe9a06\tbootable/recovery\x00') | 153 | '160000 commit e9d25da64d8d365dbba7c8ee00fe8c4473fe9a06\tbootable/recovery\x00') |
143 | with mock.patch.object(self._superproject, '_Clone', return_value=True): | 154 | with mock.patch.object(self._superproject, '_Clone', return_value=True): |
144 | with mock.patch.object(self._superproject, '_Fetch', return_value=True): | 155 | with mock.patch.object(self._superproject, '_Fetch', return_value=True): |
145 | with mock.patch.object(self._superproject, '_LsTree', return_value=data): | 156 | with mock.patch.object(self._superproject, |
157 | '_LsTree', | ||
158 | return_value=data): | ||
146 | # Create temporary directory so that it can write the file. | 159 | # Create temporary directory so that it can write the file. |
147 | os.mkdir(self._superproject._superproject_path) | 160 | os.mkdir(self._superproject._superproject_path) |
148 | manifest_path = self._superproject.UpdateProjectsRevisionId( | 161 | manifest_path = self._superproject.UpdateProjectsRevisionId(projects) |
149 | manifest, projects, url='localhost') | ||
150 | self.assertIsNotNone(manifest_path) | 162 | self.assertIsNotNone(manifest_path) |
151 | with open(manifest_path, 'r') as fp: | 163 | with open(manifest_path, 'r') as fp: |
152 | manifest_xml = fp.read() | 164 | manifest_xml = fp.read() |
@@ -157,6 +169,7 @@ class SuperprojectTestCase(unittest.TestCase): | |||
157 | '<default remote="default-remote" revision="refs/heads/main"/>' + | 169 | '<default remote="default-remote" revision="refs/heads/main"/>' + |
158 | '<project name="platform/art" path="art" ' + | 170 | '<project name="platform/art" path="art" ' + |
159 | 'revision="2c2724cb36cd5a9cec6c852c681efc3b7c6b86ea"/>' + | 171 | 'revision="2c2724cb36cd5a9cec6c852c681efc3b7c6b86ea"/>' + |
172 | '<superproject name="superproject"/>' + | ||
160 | '</manifest>') | 173 | '</manifest>') |
161 | 174 | ||
162 | 175 | ||