manifest: enable remove-project using path

A something.xml that gets included by two different
files, that both remove and add same shared project
to two different locations, would not work
prior to this change.

Reason is that remove killed all name keys, even
though reuse of same repo in different locations
is allowed.

Solve by adding optional attrib path to
<remove-project name="foo" path="only_this_path" />
and tweak remove-project.

Behaves as before without path, and deletes
more selectively when remove path is supplied.

As secondary feature, a project can now also be removed
by only using path, assuming a matching project name
can be found.

Change-Id: I502d9f949f5d858ddc1503846b170473f76dc8e2
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/375694
Tested-by: Fredrik de Groot <fredrik.de.groot@aptiv.com>
Reviewed-by: Mike Frysinger <vapier@google.com>
This commit is contained in:
Fredrik de Groot 2023-05-31 16:56:34 +02:00 committed by Mike Frysinger
parent 696e0c48a9
commit be71c2f80f
3 changed files with 88 additions and 15 deletions

View File

@ -109,7 +109,8 @@ following DTD:
<!ATTLIST extend-project upstream CDATA #IMPLIED>
<!ELEMENT remove-project EMPTY>
<!ATTLIST remove-project name CDATA #REQUIRED>
<!ATTLIST remove-project name CDATA #IMPLIED>
<!ATTLIST remove-project path CDATA #IMPLIED>
<!ATTLIST remove-project optional CDATA #IMPLIED>
<!ELEMENT repo-hooks EMPTY>
@ -473,7 +474,7 @@ of the repo client.
### Element remove-project
Deletes the named project from the internal manifest table, possibly
Deletes a project from the internal manifest table, possibly
allowing a subsequent project element in the same manifest file to
replace the project with a different source.
@ -481,6 +482,17 @@ This element is mostly useful in a local manifest file, where
the user can remove a project, and possibly replace it with their
own definition.
The project `name` or project `path` can be used to specify the remove target
meaning one of them is required. If only name is specified, all
projects with that name are removed.
If both name and path are specified, only projects with the same name and
path are removed, meaning projects with the same name but in other
locations are kept.
If only path is specified, a matching project is removed regardless of its
name. Logic otherwise behaves like both are specified.
Attribute `optional`: Set to true to ignore remove-project elements with no
matching `project` element.

View File

@ -1535,22 +1535,45 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md
self._contactinfo = ContactInfo(bugurl)
if node.nodeName == "remove-project":
name = self._reqatt(node, "name")
name = node.getAttribute("name")
path = node.getAttribute("path")
if name in self._projects:
for p in self._projects[name]:
# Name or path needed.
if not name and not path:
raise ManifestParseError(
"remove-project must have name and/or path"
)
removed_project = ""
# Find and remove projects based on name and/or path.
for projname, projects in list(self._projects.items()):
for p in projects:
if name == projname and not path:
del self._paths[p.relpath]
if not removed_project:
del self._projects[name]
removed_project = name
elif path == p.relpath and (
name == projname or not name
):
self._projects[projname].remove(p)
del self._paths[p.relpath]
removed_project = p.name
# If the manifest removes the hooks project, treat it as if
# it deleted
# the repo-hooks element too.
if repo_hooks_project == name:
# it deleted the repo-hooks element too.
if (
removed_project
and removed_project not in self._projects
and repo_hooks_project == removed_project
):
repo_hooks_project = None
elif not XmlBool(node, "optional", False):
if not removed_project and not XmlBool(node, "optional", False):
raise ManifestParseError(
"remove-project element specifies non-existent "
"project: %s" % name
"project: %s" % node.toxml()
)
# Store repo hooks project information.

View File

@ -996,6 +996,44 @@ class RemoveProjectElementTests(ManifestParseTestCase):
)
self.assertEqual(manifest.projects, [])
def test_remove_using_path_attrib(self):
manifest = self.getXmlManifest(
"""
<manifest>
<remote name="default-remote" fetch="http://localhost" />
<default remote="default-remote" revision="refs/heads/main" />
<project name="project1" path="tests/path1" />
<project name="project1" path="tests/path2" />
<project name="project2" />
<project name="project3" />
<project name="project4" path="tests/path3" />
<project name="project4" path="tests/path4" />
<project name="project5" />
<project name="project6" path="tests/path6" />
<remove-project name="project1" path="tests/path2" />
<remove-project name="project3" />
<remove-project name="project4" />
<remove-project path="project5" />
<remove-project path="tests/path6" />
</manifest>
"""
)
found_proj1_path1 = False
found_proj2 = False
for proj in manifest.projects:
if proj.name == "project1":
found_proj1_path1 = True
self.assertEqual(proj.relpath, "tests/path1")
if proj.name == "project2":
found_proj2 = True
self.assertNotEqual(proj.name, "project3")
self.assertNotEqual(proj.name, "project4")
self.assertNotEqual(proj.name, "project5")
self.assertNotEqual(proj.name, "project6")
self.assertTrue(found_proj1_path1)
self.assertTrue(found_proj2)
class ExtendProjectElementTests(ManifestParseTestCase):
"""Tests for <extend-project>."""