Skip to content

Commit 75773b8

Browse files
Peter KjellerstedtLUCI
authored andcommitted
manifest, project: Store project groups as sets
This helps a lot when including common manifests with groups and they use extend-project. Change-Id: Ic574e7d6696139d0eb90d9915e8c7048d5e89c07 Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/525323 Reviewed-by: Gavin Mak <[email protected]> Tested-by: Peter Kjellerstedt <[email protected]> Reviewed-by: Mike Frysinger <[email protected]> Commit-Queue: Peter Kjellerstedt <[email protected]>
1 parent 412367b commit 75773b8

File tree

5 files changed

+62
-60
lines changed

5 files changed

+62
-60
lines changed

docs/manifest-format.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ should be placed. If not supplied, `revision` is used.
287287

288288
`path` may not be an absolute path or use "." or ".." path components.
289289

290-
Attribute `groups`: List of additional groups to which all projects
290+
Attribute `groups`: Set of additional groups to which all projects
291291
in the included submanifest belong. This appends and recurses, meaning
292292
all projects in submanifests carry all parent submanifest groups.
293293
Same syntax as the corresponding element of `project`.
@@ -355,7 +355,7 @@ When using `repo upload`, changes will be submitted for code
355355
review on this branch. If unspecified both here and in the
356356
default element, `revision` is used instead.
357357

358-
Attribute `groups`: List of groups to which this project belongs,
358+
Attribute `groups`: Set of groups to which this project belongs,
359359
whitespace or comma separated. All projects belong to the group
360360
"all", and each project automatically belongs to a group of
361361
its name:`name` and path:`path`. E.g. for
@@ -403,7 +403,7 @@ of the repo client where the Git working directory for this project
403403
should be placed. This is used to move a project in the checkout by
404404
overriding the existing `path` setting.
405405

406-
Attribute `groups`: List of additional groups to which this project
406+
Attribute `groups`: Set of additional groups to which this project
407407
belongs. Same syntax as the corresponding element of `project`.
408408

409409
Attribute `revision`: If specified, overrides the revision of the original
@@ -572,7 +572,7 @@ the manifest repository's root.
572572
"name" may not be an absolute path or use "." or ".." path components.
573573
These restrictions are not enforced for [Local Manifests].
574574

575-
Attribute `groups`: List of additional groups to which all projects
575+
Attribute `groups`: Set of additional groups to which all projects
576576
in the included manifest belong. This appends and recurses, meaning
577577
all projects in included manifests carry all parent include groups.
578578
This also applies to all extend-project elements in the included manifests.

man/repo-manifest.1

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,7 @@ supplied, `revision` is used.
365365
.PP
366366
`path` may not be an absolute path or use "." or ".." path components.
367367
.PP
368-
Attribute `groups`: List of additional groups to which all projects in the
368+
Attribute `groups`: Set of additional groups to which all projects in the
369369
included submanifest belong. This appends and recurses, meaning all projects in
370370
submanifests carry all parent submanifest groups. Same syntax as the
371371
corresponding element of `project`.
@@ -427,7 +427,7 @@ Attribute `dest\-branch`: Name of a Git branch (e.g. `main`). When using `repo
427427
upload`, changes will be submitted for code review on this branch. If
428428
unspecified both here and in the default element, `revision` is used instead.
429429
.PP
430-
Attribute `groups`: List of groups to which this project belongs, whitespace or
430+
Attribute `groups`: Set of groups to which this project belongs, whitespace or
431431
comma separated. All projects belong to the group "all", and each project
432432
automatically belongs to a group of its name:`name` and path:`path`. E.g. for
433433
`<project name="monkeys" path="barrel\-of"/>`, that project definition is
@@ -471,8 +471,8 @@ repo client where the Git working directory for this project should be placed.
471471
This is used to move a project in the checkout by overriding the existing `path`
472472
setting.
473473
.PP
474-
Attribute `groups`: List of additional groups to which this project belongs.
475-
Same syntax as the corresponding element of `project`.
474+
Attribute `groups`: Set of additional groups to which this project belongs. Same
475+
syntax as the corresponding element of `project`.
476476
.PP
477477
Attribute `revision`: If specified, overrides the revision of the original
478478
project. Same syntax as the corresponding element of `project`.
@@ -634,7 +634,7 @@ repository's root.
634634
"name" may not be an absolute path or use "." or ".." path components. These
635635
restrictions are not enforced for [Local Manifests].
636636
.PP
637-
Attribute `groups`: List of additional groups to which all projects in the
637+
Attribute `groups`: Set of additional groups to which all projects in the
638638
included manifest belong. This appends and recurses, meaning all projects in
639639
included manifests carry all parent include groups. This also applies to all
640640
extend\-project elements in the included manifests. Same syntax as the

manifest_xml.py

Lines changed: 46 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@ class _XmlSubmanifest:
255255
project: a string, the name of the manifest project.
256256
revision: a string, the commitish.
257257
manifestName: a string, the submanifest file name.
258-
groups: a list of strings, the groups to add to all projects in the
258+
groups: a set of strings, the groups to add to all projects in the
259259
submanifest.
260260
default_groups: a list of strings, the default groups to sync.
261261
path: a string, the relative path for the submanifest checkout.
@@ -281,7 +281,7 @@ def __init__(
281281
self.project = project
282282
self.revision = revision
283283
self.manifestName = manifestName
284-
self.groups = groups
284+
self.groups = groups or set()
285285
self.default_groups = default_groups
286286
self.path = path
287287
self.parent = parent
@@ -304,7 +304,7 @@ def __init__(
304304
self.repo_client = RepoClient(
305305
parent.repodir,
306306
linkFile,
307-
parent_groups=",".join(groups) or "",
307+
parent_groups=groups,
308308
submanifest_path=os.path.join(parent.path_prefix, self.relpath),
309309
outer_client=outer_client,
310310
default_groups=default_groups,
@@ -345,7 +345,7 @@ def ToSubmanifestSpec(self):
345345
manifestName = self.manifestName or "default.xml"
346346
revision = self.revision or self.name
347347
path = self.path or revision.split("/")[-1]
348-
groups = self.groups or []
348+
groups = self.groups
349349

350350
return SubmanifestSpec(
351351
self.name, manifestUrl, manifestName, revision, path, groups
@@ -359,9 +359,7 @@ def relpath(self):
359359

360360
def GetGroupsStr(self):
361361
"""Returns the `groups` given for this submanifest."""
362-
if self.groups:
363-
return ",".join(self.groups)
364-
return ""
362+
return ",".join(sorted(self.groups))
365363

366364
def GetDefaultGroupsStr(self):
367365
"""Returns the `default-groups` given for this submanifest."""
@@ -381,7 +379,7 @@ def __init__(self, name, manifestUrl, manifestName, revision, path, groups):
381379
self.manifestName = manifestName
382380
self.revision = revision
383381
self.path = path
384-
self.groups = groups or []
382+
self.groups = groups
385383

386384

387385
class XmlManifest:
@@ -393,7 +391,7 @@ def __init__(
393391
manifest_file,
394392
local_manifests=None,
395393
outer_client=None,
396-
parent_groups="",
394+
parent_groups=None,
397395
submanifest_path="",
398396
default_groups=None,
399397
):
@@ -409,7 +407,8 @@ def __init__(
409407
manifests. This will usually be
410408
|repodir|/|LOCAL_MANIFESTS_DIR_NAME|.
411409
outer_client: RepoClient of the outer manifest.
412-
parent_groups: a string, the groups to apply to this projects.
410+
parent_groups: a set of strings, the groups to apply to this
411+
manifest.
413412
submanifest_path: The submanifest root relative to the repo root.
414413
default_groups: a string, the default manifest groups to use.
415414
"""
@@ -432,7 +431,7 @@ def __init__(
432431
self.manifestFileOverrides = {}
433432
self.local_manifests = local_manifests
434433
self._load_local_manifests = True
435-
self.parent_groups = parent_groups
434+
self.parent_groups = parent_groups or set()
436435
self.default_groups = default_groups
437436

438437
if submanifest_path and not outer_client:
@@ -567,6 +566,14 @@ def _ParseList(self, field):
567566
"""
568567
return [x for x in re.split(r"[,\s]+", field) if x]
569568

569+
def _ParseSet(self, field):
570+
"""Parse fields that contain flattened sets.
571+
572+
These are whitespace & comma separated. Empty elements will be
573+
discarded.
574+
"""
575+
return set(self._ParseList(field))
576+
570577
def ToXml(
571578
self,
572579
peg_rev=False,
@@ -725,10 +732,9 @@ def output_project(parent, parent_node, p):
725732
le.setAttribute("dest", lf.dest)
726733
e.appendChild(le)
727734

728-
default_groups = ["all", "name:%s" % p.name, "path:%s" % p.relpath]
729-
egroups = [g for g in p.groups if g not in default_groups]
735+
egroups = p.groups - {"all", f"name:{p.name}", f"path:{p.relpath}"}
730736
if egroups:
731-
e.setAttribute("groups", ",".join(egroups))
737+
e.setAttribute("groups", ",".join(sorted(egroups)))
732738

733739
for a in p.annotations:
734740
if a.keep == "true":
@@ -1171,12 +1177,12 @@ def _Load(self, initial_client=None, submanifest_depth=0):
11711177
b = b[len(R_HEADS) :]
11721178
self.branch = b
11731179

1174-
parent_groups = self.parent_groups
1180+
parent_groups = self.parent_groups.copy()
11751181
if self.path_prefix:
1176-
parent_groups = (
1182+
parent_groups |= {
11771183
f"{SUBMANIFEST_GROUP_PREFIX}:path:"
1178-
f"{self.path_prefix},{parent_groups}"
1179-
)
1184+
f"{self.path_prefix}"
1185+
}
11801186

11811187
# The manifestFile was specified by the user which is why we
11821188
# allow include paths to point anywhere.
@@ -1202,16 +1208,16 @@ def _Load(self, initial_client=None, submanifest_depth=0):
12021208
# Since local manifests are entirely managed by
12031209
# the user, allow them to point anywhere the
12041210
# user wants.
1205-
local_group = (
1211+
local_group = {
12061212
f"{LOCAL_MANIFEST_GROUP_PREFIX}:"
12071213
f"{local_file[:-4]}"
1208-
)
1214+
}
12091215
nodes.append(
12101216
self._ParseManifestXml(
12111217
local,
12121218
self.subdir,
12131219
parent_groups=(
1214-
f"{local_group},{parent_groups}"
1220+
local_group | parent_groups
12151221
),
12161222
restrict_includes=False,
12171223
)
@@ -1262,7 +1268,7 @@ def _ParseManifestXml(
12621268
self,
12631269
path,
12641270
include_root,
1265-
parent_groups="",
1271+
parent_groups=None,
12661272
restrict_includes=True,
12671273
parent_node=None,
12681274
):
@@ -1271,11 +1277,11 @@ def _ParseManifestXml(
12711277
Args:
12721278
path: The XML file to read & parse.
12731279
include_root: The path to interpret include "name"s relative to.
1274-
parent_groups: The groups to apply to this projects.
1280+
parent_groups: The set of groups to apply to this manifest.
12751281
restrict_includes: Whether to constrain the "name" attribute of
12761282
includes.
1277-
parent_node: The parent include node, to apply attribute to this
1278-
projects.
1283+
parent_node: The parent include node, to apply attributes to this
1284+
manifest.
12791285
12801286
Returns:
12811287
List of XML nodes.
@@ -1307,12 +1313,10 @@ def _ParseManifestXml(
13071313
raise ManifestInvalidPathError(
13081314
f'<include> invalid "name": {name}: {msg}'
13091315
)
1310-
include_groups = ""
1311-
if parent_groups:
1312-
include_groups = parent_groups
1316+
include_groups = (parent_groups or set()).copy()
13131317
if node.hasAttribute("groups"):
1314-
include_groups = (
1315-
node.getAttribute("groups") + "," + include_groups
1318+
include_groups |= self._ParseSet(
1319+
node.getAttribute("groups")
13161320
)
13171321
fp = os.path.join(include_root, name)
13181322
if not os.path.isfile(fp):
@@ -1339,12 +1343,12 @@ def _ParseManifestXml(
13391343
"project",
13401344
"extend-project",
13411345
):
1342-
nodeGroups = parent_groups
1346+
nodeGroups = parent_groups.copy()
13431347
if node.hasAttribute("groups"):
1344-
nodeGroups = (
1345-
node.getAttribute("groups") + "," + nodeGroups
1348+
nodeGroups |= self._ParseSet(
1349+
node.getAttribute("groups")
13461350
)
1347-
node.setAttribute("groups", nodeGroups)
1351+
node.setAttribute("groups", ",".join(sorted(nodeGroups)))
13481352
if (
13491353
parent_node
13501354
and node.nodeName == "project"
@@ -1461,7 +1465,7 @@ def recursively_add_projects(project):
14611465
dest_path = node.getAttribute("dest-path")
14621466
groups = node.getAttribute("groups")
14631467
if groups:
1464-
groups = self._ParseList(groups)
1468+
groups = self._ParseSet(groups or "")
14651469
revision = node.getAttribute("revision")
14661470
remote_name = node.getAttribute("remote")
14671471
if not remote_name:
@@ -1482,7 +1486,7 @@ def recursively_add_projects(project):
14821486
if path and p.relpath != path:
14831487
continue
14841488
if groups:
1485-
p.groups.extend(groups)
1489+
p.groups |= groups
14861490
if revision:
14871491
if base_revision:
14881492
if p.revisionExpr != base_revision:
@@ -1813,7 +1817,7 @@ def _ParseSubmanifest(self, node):
18131817
groups = ""
18141818
if node.hasAttribute("groups"):
18151819
groups = node.getAttribute("groups")
1816-
groups = self._ParseList(groups)
1820+
groups = self._ParseSet(groups)
18171821
default_groups = self._ParseList(node.getAttribute("default-groups"))
18181822
path = node.getAttribute("path")
18191823
if path == "":
@@ -1922,11 +1926,6 @@ def _ParseProject(self, node, parent=None, **extra_proj_attrs):
19221926

19231927
upstream = node.getAttribute("upstream") or self._default.upstreamExpr
19241928

1925-
groups = ""
1926-
if node.hasAttribute("groups"):
1927-
groups = node.getAttribute("groups")
1928-
groups = self._ParseList(groups)
1929-
19301929
if parent is None:
19311930
(
19321931
relpath,
@@ -1941,8 +1940,11 @@ def _ParseProject(self, node, parent=None, **extra_proj_attrs):
19411940
parent, name, path
19421941
)
19431942

1944-
default_groups = ["all", "name:%s" % name, "path:%s" % relpath]
1945-
groups.extend(set(default_groups).difference(groups))
1943+
groups = ""
1944+
if node.hasAttribute("groups"):
1945+
groups = node.getAttribute("groups")
1946+
groups = self._ParseSet(groups)
1947+
groups |= {"all", f"name:{name}", f"path:{relpath}"}
19461948

19471949
if self.IsMirror and node.hasAttribute("force-path"):
19481950
if XmlBool(node, "force-path", False):

project.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -554,7 +554,7 @@ def __init__(
554554
revisionExpr,
555555
revisionId,
556556
rebase=True,
557-
groups=None,
557+
groups=set(),
558558
sync_c=False,
559559
sync_s=False,
560560
sync_tags=True,
@@ -839,9 +839,9 @@ def MatchesGroups(self, manifest_groups):
839839
"""
840840
default_groups = self.manifest.default_groups or ["default"]
841841
expanded_manifest_groups = manifest_groups or default_groups
842-
expanded_project_groups = ["all"] + (self.groups or [])
842+
expanded_project_groups = {"all"} | self.groups
843843
if "notdefault" not in expanded_project_groups:
844-
expanded_project_groups += ["default"]
844+
expanded_project_groups |= {"default"}
845845

846846
matched = False
847847
for group in expanded_manifest_groups:

tests/test_manifest_xml.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -605,12 +605,12 @@ def test_group(self):
605605
manifest.projects[0].name: manifest.projects[0].groups,
606606
manifest.projects[1].name: manifest.projects[1].groups,
607607
}
608-
self.assertCountEqual(
609-
result["test-name"], ["name:test-name", "all", "path:test-path"]
608+
self.assertEqual(
609+
result["test-name"], {"name:test-name", "all", "path:test-path"}
610610
)
611-
self.assertCountEqual(
611+
self.assertEqual(
612612
result["extras"],
613-
["g1", "g2", "g1", "name:extras", "all", "path:path"],
613+
{"g1", "g2", "name:extras", "all", "path:path"},
614614
)
615615
groupstr = "default,platform-" + platform.system().lower()
616616
self.assertEqual(groupstr, manifest.GetGroupsStr())

0 commit comments

Comments
 (0)