summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFredrik de Groot <fredrik.de.groot@aptiv.com>2023-05-31 16:56:34 +0200
committerMike Frysinger <vapier@google.com>2023-06-21 14:50:16 +0000
commitbe71c2f80fa115e8256211fd0e3116d93cfae93e (patch)
tree39769fb002ec24d520abde60cf6b0614c4c02558
parent696e0c48a9de4d20f3de65bc014ca2991d16f041 (diff)
downloadgit-repo-be71c2f80fa115e8256211fd0e3116d93cfae93e.tar.gz
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>
-rw-r--r--docs/manifest-format.md18
-rw-r--r--manifest_xml.py49
-rw-r--r--tests/test_manifest_xml.py38
3 files changed, 89 insertions, 16 deletions
diff --git a/docs/manifest-format.md b/docs/manifest-format.md
index edcb28cb..36dae6de 100644
--- a/docs/manifest-format.md
+++ b/docs/manifest-format.md
@@ -109,8 +109,9 @@ following DTD:
109 <!ATTLIST extend-project upstream CDATA #IMPLIED> 109 <!ATTLIST extend-project upstream CDATA #IMPLIED>
110 110
111 <!ELEMENT remove-project EMPTY> 111 <!ELEMENT remove-project EMPTY>
112 <!ATTLIST remove-project name CDATA #REQUIRED> 112 <!ATTLIST remove-project name CDATA #IMPLIED>
113 <!ATTLIST remove-project optional CDATA #IMPLIED> 113 <!ATTLIST remove-project path CDATA #IMPLIED>
114 <!ATTLIST remove-project optional CDATA #IMPLIED>
114 115
115 <!ELEMENT repo-hooks EMPTY> 116 <!ELEMENT repo-hooks EMPTY>
116 <!ATTLIST repo-hooks in-project CDATA #REQUIRED> 117 <!ATTLIST repo-hooks in-project CDATA #REQUIRED>
@@ -473,7 +474,7 @@ of the repo client.
473 474
474### Element remove-project 475### Element remove-project
475 476
476Deletes the named project from the internal manifest table, possibly 477Deletes a project from the internal manifest table, possibly
477allowing a subsequent project element in the same manifest file to 478allowing a subsequent project element in the same manifest file to
478replace the project with a different source. 479replace the project with a different source.
479 480
@@ -481,6 +482,17 @@ This element is mostly useful in a local manifest file, where
481the user can remove a project, and possibly replace it with their 482the user can remove a project, and possibly replace it with their
482own definition. 483own definition.
483 484
485The project `name` or project `path` can be used to specify the remove target
486meaning one of them is required. If only name is specified, all
487projects with that name are removed.
488
489If both name and path are specified, only projects with the same name and
490path are removed, meaning projects with the same name but in other
491locations are kept.
492
493If only path is specified, a matching project is removed regardless of its
494name. Logic otherwise behaves like both are specified.
495
484Attribute `optional`: Set to true to ignore remove-project elements with no 496Attribute `optional`: Set to true to ignore remove-project elements with no
485matching `project` element. 497matching `project` element.
486 498
diff --git a/manifest_xml.py b/manifest_xml.py
index 555bf736..73be1b6e 100644
--- a/manifest_xml.py
+++ b/manifest_xml.py
@@ -1535,22 +1535,45 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md
1535 self._contactinfo = ContactInfo(bugurl) 1535 self._contactinfo = ContactInfo(bugurl)
1536 1536
1537 if node.nodeName == "remove-project": 1537 if node.nodeName == "remove-project":
1538 name = self._reqatt(node, "name") 1538 name = node.getAttribute("name")
1539 path = node.getAttribute("path")
1539 1540
1540 if name in self._projects: 1541 # Name or path needed.
1541 for p in self._projects[name]: 1542 if not name and not path:
1542 del self._paths[p.relpath] 1543 raise ManifestParseError(
1543 del self._projects[name] 1544 "remove-project must have name and/or path"
1544 1545 )
1545 # If the manifest removes the hooks project, treat it as if 1546
1546 # it deleted 1547 removed_project = ""
1547 # the repo-hooks element too. 1548
1548 if repo_hooks_project == name: 1549 # Find and remove projects based on name and/or path.
1549 repo_hooks_project = None 1550 for projname, projects in list(self._projects.items()):
1550 elif not XmlBool(node, "optional", False): 1551 for p in projects:
1552 if name == projname and not path:
1553 del self._paths[p.relpath]
1554 if not removed_project:
1555 del self._projects[name]
1556 removed_project = name
1557 elif path == p.relpath and (
1558 name == projname or not name
1559 ):
1560 self._projects[projname].remove(p)
1561 del self._paths[p.relpath]
1562 removed_project = p.name
1563
1564 # If the manifest removes the hooks project, treat it as if
1565 # it deleted the repo-hooks element too.
1566 if (
1567 removed_project
1568 and removed_project not in self._projects
1569 and repo_hooks_project == removed_project
1570 ):
1571 repo_hooks_project = None
1572
1573 if not removed_project and not XmlBool(node, "optional", False):
1551 raise ManifestParseError( 1574 raise ManifestParseError(
1552 "remove-project element specifies non-existent " 1575 "remove-project element specifies non-existent "
1553 "project: %s" % name 1576 "project: %s" % node.toxml()
1554 ) 1577 )
1555 1578
1556 # Store repo hooks project information. 1579 # Store repo hooks project information.
diff --git a/tests/test_manifest_xml.py b/tests/test_manifest_xml.py
index ef511055..1015e114 100644
--- a/tests/test_manifest_xml.py
+++ b/tests/test_manifest_xml.py
@@ -996,6 +996,44 @@ class RemoveProjectElementTests(ManifestParseTestCase):
996 ) 996 )
997 self.assertEqual(manifest.projects, []) 997 self.assertEqual(manifest.projects, [])
998 998
999 def test_remove_using_path_attrib(self):
1000 manifest = self.getXmlManifest(
1001 """
1002<manifest>
1003 <remote name="default-remote" fetch="http://localhost" />
1004 <default remote="default-remote" revision="refs/heads/main" />
1005 <project name="project1" path="tests/path1" />
1006 <project name="project1" path="tests/path2" />
1007 <project name="project2" />
1008 <project name="project3" />
1009 <project name="project4" path="tests/path3" />
1010 <project name="project4" path="tests/path4" />
1011 <project name="project5" />
1012 <project name="project6" path="tests/path6" />
1013
1014 <remove-project name="project1" path="tests/path2" />
1015 <remove-project name="project3" />
1016 <remove-project name="project4" />
1017 <remove-project path="project5" />
1018 <remove-project path="tests/path6" />
1019</manifest>
1020"""
1021 )
1022 found_proj1_path1 = False
1023 found_proj2 = False
1024 for proj in manifest.projects:
1025 if proj.name == "project1":
1026 found_proj1_path1 = True
1027 self.assertEqual(proj.relpath, "tests/path1")
1028 if proj.name == "project2":
1029 found_proj2 = True
1030 self.assertNotEqual(proj.name, "project3")
1031 self.assertNotEqual(proj.name, "project4")
1032 self.assertNotEqual(proj.name, "project5")
1033 self.assertNotEqual(proj.name, "project6")
1034 self.assertTrue(found_proj1_path1)
1035 self.assertTrue(found_proj2)
1036
999 1037
1000class ExtendProjectElementTests(ManifestParseTestCase): 1038class ExtendProjectElementTests(ManifestParseTestCase):
1001 """Tests for <extend-project>.""" 1039 """Tests for <extend-project>."""