From 66685f07ecf1b8bc228df66ea0d29fd7086e18e7 Mon Sep 17 00:00:00 2001 From: Kaushik Lingarkar Date: Tue, 17 Dec 2024 13:49:19 -0800 Subject: [PATCH] Use 'gitfile' in submodule checkouts This change takes another step towards ensuring Git can understand repo's submodules to some extent. Replace the old '.git' symlink with gitfile[1] pointing to the bare checkout of the submodule. This is required for Git's 'recurse submodules' opts to work with repo's submodules as '.git' is expected to be writable by Git when recursing over submodules. [1] https://git-scm.com/docs/gitrepository-layout#_description Change-Id: I52d15451768ee7bd6db289f4d2b3be5907370d42 Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/446181 Tested-by: Kaushik Lingarkar Reviewed-by: Josip Sokcevic Reviewed-by: Mike Frysinger Reviewed-by: Nasser Grainawi --- project.py | 49 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 42 insertions(+), 7 deletions(-) diff --git a/project.py b/project.py index b58bd53b..37cec98f 100644 --- a/project.py +++ b/project.py @@ -3430,20 +3430,21 @@ class Project: self._InitGitWorktree() self._CopyAndLinkFiles() else: + # Remove old directory symbolic links for submodules. + if self.parent and platform_utils.islink(dotgit): + platform_utils.remove(dotgit) + init_dotgit = True + if not init_dotgit: # See if the project has changed. - if os.path.realpath(self.gitdir) != os.path.realpath(dotgit): - platform_utils.remove(dotgit) + self._removeBadGitDirLink(dotgit) if init_dotgit or not os.path.exists(dotgit): - os.makedirs(self.worktree, exist_ok=True) - platform_utils.symlink( - os.path.relpath(self.gitdir, self.worktree), dotgit - ) + self._createDotGit(dotgit) if init_dotgit: _lwrite( - os.path.join(dotgit, HEAD), "%s\n" % self.GetRevisionId() + os.path.join(self.gitdir, HEAD), f"{self.GetRevisionId()}\n" ) # Finish checking out the worktree. @@ -3465,6 +3466,40 @@ class Project: self._SyncSubmodules(quiet=True) self._CopyAndLinkFiles() + def _createDotGit(self, dotgit): + """Initialize .git path. + + For submodule projects, create a '.git' file using the gitfile + mechanism, and for the rest, create a symbolic link. + """ + os.makedirs(self.worktree, exist_ok=True) + if self.parent: + _lwrite( + dotgit, + f"gitdir: {os.path.relpath(self.gitdir, self.worktree)}\n", + ) + else: + platform_utils.symlink( + os.path.relpath(self.gitdir, self.worktree), dotgit + ) + + def _removeBadGitDirLink(self, dotgit): + """Verify .git is initialized correctly, otherwise delete it.""" + if self.parent and os.path.isfile(dotgit): + with open(dotgit) as fp: + setting = fp.read() + if not setting.startswith("gitdir:"): + raise GitError( + f"'.git' in {self.worktree} must start with 'gitdir:'", + project=self.name, + ) + gitdir = setting.split(":", 1)[1].strip() + dotgit_path = os.path.normpath(os.path.join(self.worktree, gitdir)) + else: + dotgit_path = os.path.realpath(dotgit) + if os.path.realpath(self.gitdir) != dotgit_path: + platform_utils.remove(dotgit) + @classmethod def _MigrateOldWorkTreeGitDir(cls, dotgit, project=None): """Migrate the old worktree .git/ dir style to a symlink.