Add 'dest-path' to extend-project to support changing path
This allows us to move the repository to a new location in the source
tree without having to remove-project + add a new project tag.
Change-Id: I4dba6151842e57f6f2b8fe60cda260ecea68b7b4
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/310962
Reviewed-by: Mike Frysinger <vapier@google.com>
Tested-by: Michael Kelly <mkelly@arista.com>
diff --git a/docs/manifest-format.md b/docs/manifest-format.md
index ed297ae..8e0049b 100644
--- a/docs/manifest-format.md
+++ b/docs/manifest-format.md
@@ -90,6 +90,7 @@
<!ELEMENT extend-project EMPTY>
<!ATTLIST extend-project name CDATA #REQUIRED>
<!ATTLIST extend-project path CDATA #IMPLIED>
+ <!ATTLIST extend-project dest-path CDATA #IMPLIED>
<!ATTLIST extend-project groups CDATA #IMPLIED>
<!ATTLIST extend-project revision CDATA #IMPLIED>
<!ATTLIST extend-project remote CDATA #IMPLIED>
@@ -337,6 +338,11 @@
Attribute `path`: If specified, limit the change to projects checked out
at the specified path, rather than all projects with the given name.
+Attribute `dest-path`: If specified, a path relative to the top directory
+of the repo client where the Git working directory for this project
+should be placed. This is used to move a project in the checkout by
+overriding the existing `path` setting.
+
Attribute `groups`: List of additional groups to which this project
belongs. Same syntax as the corresponding element of `project`.
diff --git a/manifest_xml.py b/manifest_xml.py
index 86f2020..3965697 100644
--- a/manifest_xml.py
+++ b/manifest_xml.py
@@ -868,6 +868,7 @@
'project: %s' % name)
path = node.getAttribute('path')
+ dest_path = node.getAttribute('dest-path')
groups = node.getAttribute('groups')
if groups:
groups = self._ParseList(groups)
@@ -876,6 +877,10 @@
if remote:
remote = self._get_remote(node)
+ named_projects = self._projects[name]
+ if dest_path and not path and len(named_projects) > 1:
+ raise ManifestParseError('extend-project cannot use dest-path when '
+ 'matching multiple projects: %s' % name)
for p in self._projects[name]:
if path and p.relpath != path:
continue
@@ -889,6 +894,12 @@
p.revisionId = None
if remote:
p.remote = remote.ToRemoteSpec(name)
+ if dest_path:
+ del self._paths[p.relpath]
+ relpath, worktree, gitdir, objdir, _ = self.GetProjectPaths(name, dest_path)
+ p.UpdatePaths(relpath, worktree, gitdir, objdir)
+ self._paths[p.relpath] = p
+
if node.nodeName == 'repo-hooks':
# Only one project can be the hooks project
if repo_hooks_project is not None:
diff --git a/project.py b/project.py
index 634d88c..9ff9df0 100644
--- a/project.py
+++ b/project.py
@@ -519,13 +519,7 @@
self.client = self.manifest = manifest
self.name = name
self.remote = remote
- self.gitdir = gitdir.replace('\\', '/')
- self.objdir = objdir.replace('\\', '/')
- if worktree:
- self.worktree = os.path.normpath(worktree).replace('\\', '/')
- else:
- self.worktree = None
- self.relpath = relpath
+ self.UpdatePaths(relpath, worktree, gitdir, objdir)
self.revisionExpr = revisionExpr
if revisionId is None \
@@ -556,16 +550,6 @@
self.copyfiles = []
self.linkfiles = []
self.annotations = []
- self.config = GitConfig.ForRepository(gitdir=self.gitdir,
- defaults=self.client.globalConfig)
-
- if self.worktree:
- self.work_git = self._GitGetByExec(self, bare=False, gitdir=gitdir)
- else:
- self.work_git = None
- self.bare_git = self._GitGetByExec(self, bare=True, gitdir=gitdir)
- self.bare_ref = GitRefs(gitdir)
- self.bare_objdir = self._GitGetByExec(self, bare=True, gitdir=objdir)
self.dest_branch = dest_branch
self.old_revision = old_revision
@@ -573,6 +557,27 @@
# project containing repo hooks.
self.enabled_repo_hooks = []
+ def UpdatePaths(self, relpath, worktree, gitdir, objdir):
+ """Update paths used by this project"""
+ self.gitdir = gitdir.replace('\\', '/')
+ self.objdir = objdir.replace('\\', '/')
+ if worktree:
+ self.worktree = os.path.normpath(worktree).replace('\\', '/')
+ else:
+ self.worktree = None
+ self.relpath = relpath
+
+ self.config = GitConfig.ForRepository(gitdir=self.gitdir,
+ defaults=self.manifest.globalConfig)
+
+ if self.worktree:
+ self.work_git = self._GitGetByExec(self, bare=False, gitdir=self.gitdir)
+ else:
+ self.work_git = None
+ self.bare_git = self._GitGetByExec(self, bare=True, gitdir=self.gitdir)
+ self.bare_ref = GitRefs(self.gitdir)
+ self.bare_objdir = self._GitGetByExec(self, bare=True, gitdir=self.objdir)
+
@property
def Derived(self):
return self.is_derived
diff --git a/tests/test_manifest_xml.py b/tests/test_manifest_xml.py
index ce42253..cb3eb85 100644
--- a/tests/test_manifest_xml.py
+++ b/tests/test_manifest_xml.py
@@ -797,3 +797,49 @@
</manifest>
""")
self.assertEqual(manifest.projects, [])
+
+
+class ExtendProjectElementTests(ManifestParseTestCase):
+ """Tests for <extend-project>."""
+
+ def test_extend_project_dest_path_single_match(self):
+ manifest = self.getXmlManifest("""
+<manifest>
+ <remote name="default-remote" fetch="http://localhost" />
+ <default remote="default-remote" revision="refs/heads/main" />
+ <project name="myproject" />
+ <extend-project name="myproject" dest-path="bar" />
+</manifest>
+""")
+ self.assertEqual(len(manifest.projects), 1)
+ self.assertEqual(manifest.projects[0].relpath, 'bar')
+
+ def test_extend_project_dest_path_multi_match(self):
+ with self.assertRaises(manifest_xml.ManifestParseError):
+ manifest = self.getXmlManifest("""
+<manifest>
+ <remote name="default-remote" fetch="http://localhost" />
+ <default remote="default-remote" revision="refs/heads/main" />
+ <project name="myproject" path="x" />
+ <project name="myproject" path="y" />
+ <extend-project name="myproject" dest-path="bar" />
+</manifest>
+""")
+ manifest.projects
+
+ def test_extend_project_dest_path_multi_match_path_specified(self):
+ manifest = self.getXmlManifest("""
+<manifest>
+ <remote name="default-remote" fetch="http://localhost" />
+ <default remote="default-remote" revision="refs/heads/main" />
+ <project name="myproject" path="x" />
+ <project name="myproject" path="y" />
+ <extend-project name="myproject" path="x" dest-path="bar" />
+</manifest>
+""")
+ self.assertEqual(len(manifest.projects), 2)
+ if manifest.projects[0].relpath == 'y':
+ self.assertEqual(manifest.projects[1].relpath, 'bar')
+ else:
+ self.assertEqual(manifest.projects[0].relpath, 'bar')
+ self.assertEqual(manifest.projects[1].relpath, 'y')