submodule.py 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. # config.py - Reading and writing Git config files
  2. # Copyright (C) 2011-2013 Jelmer Vernooij <jelmer@jelmer.uk>
  3. #
  4. # SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  5. # Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU
  6. # General Public License as published by the Free Software Foundation; version 2.0
  7. # or (at your option) any later version. You can redistribute it and/or
  8. # modify it under the terms of either of these two licenses.
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. #
  16. # You should have received a copy of the licenses; if not, see
  17. # <http://www.gnu.org/licenses/> for a copy of the GNU General Public License
  18. # and <http://www.apache.org/licenses/LICENSE-2.0> for a copy of the Apache
  19. # License, Version 2.0.
  20. #
  21. """Working with Git submodules."""
  22. __all__ = [
  23. "ensure_submodule_placeholder",
  24. "iter_cached_submodules",
  25. ]
  26. import os
  27. from collections.abc import Iterator
  28. from typing import TYPE_CHECKING
  29. from .object_store import iter_tree_contents
  30. from .objects import S_ISGITLINK
  31. if TYPE_CHECKING:
  32. from .objects import ObjectID
  33. from .pack import ObjectContainer
  34. from .repo import Repo
  35. def iter_cached_submodules(
  36. store: "ObjectContainer", root_tree_id: "ObjectID"
  37. ) -> Iterator[tuple[bytes, "ObjectID"]]:
  38. """Iterate over cached submodules.
  39. Args:
  40. store: Object store to iterate
  41. root_tree_id: SHA of root tree
  42. Returns:
  43. Iterator over over (path, sha) tuples
  44. """
  45. for entry in iter_tree_contents(store, root_tree_id):
  46. assert entry.mode is not None
  47. if S_ISGITLINK(entry.mode):
  48. assert entry.path is not None
  49. assert entry.sha is not None
  50. yield entry.path, entry.sha
  51. def ensure_submodule_placeholder(
  52. repo: "Repo",
  53. submodule_path: str | bytes,
  54. ) -> None:
  55. """Create a submodule placeholder directory with .git file.
  56. This creates the minimal structure needed for a submodule:
  57. - The submodule directory
  58. - A .git file pointing to the submodule's git directory
  59. Args:
  60. repo: Parent repository
  61. submodule_path: Path to the submodule relative to repo root
  62. """
  63. # Ensure path is bytes
  64. if isinstance(submodule_path, str):
  65. submodule_path = submodule_path.encode()
  66. # Get repo path as bytes
  67. repo_path = repo.path if isinstance(repo.path, bytes) else repo.path.encode()
  68. # Create full path to submodule
  69. full_path = os.path.join(repo_path, submodule_path)
  70. # Create submodule directory if it doesn't exist
  71. if not os.path.exists(full_path):
  72. os.makedirs(full_path)
  73. # Create .git file pointing to the submodule's git directory
  74. git_filename = b".git" if isinstance(full_path, bytes) else ".git"
  75. git_file_path = os.path.join(full_path, git_filename)
  76. if not os.path.exists(git_file_path):
  77. # Submodule git directories are typically stored in .git/modules/<name>
  78. # The relative path from the submodule to the parent's .git directory
  79. # depends on the submodule's depth
  80. depth = submodule_path.count(b"/") + 1
  81. relative_git_dir = b"../" * depth + b".git/modules/" + submodule_path
  82. with open(git_file_path, "wb") as f:
  83. f.write(b"gitdir: " + relative_git_dir + b"\n")