mercurial.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. from __future__ import annotations
  2. import configparser
  3. import logging
  4. import os
  5. from pip._internal.exceptions import BadCommand, InstallationError
  6. from pip._internal.utils.misc import HiddenText, display_path
  7. from pip._internal.utils.subprocess import make_command
  8. from pip._internal.utils.urls import path_to_url
  9. from pip._internal.vcs.versioncontrol import (
  10. RevOptions,
  11. VersionControl,
  12. find_path_to_project_root_from_repo_root,
  13. vcs,
  14. )
  15. logger = logging.getLogger(__name__)
  16. class Mercurial(VersionControl):
  17. name = "hg"
  18. dirname = ".hg"
  19. repo_name = "clone"
  20. schemes = (
  21. "hg+file",
  22. "hg+http",
  23. "hg+https",
  24. "hg+ssh",
  25. "hg+static-http",
  26. )
  27. @staticmethod
  28. def get_base_rev_args(rev: str) -> list[str]:
  29. return [f"--rev={rev}"]
  30. def fetch_new(
  31. self, dest: str, url: HiddenText, rev_options: RevOptions, verbosity: int
  32. ) -> None:
  33. rev_display = rev_options.to_display()
  34. logger.info(
  35. "Cloning hg %s%s to %s",
  36. url,
  37. rev_display,
  38. display_path(dest),
  39. )
  40. if verbosity <= 0:
  41. flags: tuple[str, ...] = ("--quiet",)
  42. elif verbosity == 1:
  43. flags = ()
  44. elif verbosity == 2:
  45. flags = ("--verbose",)
  46. else:
  47. flags = ("--verbose", "--debug")
  48. self.run_command(make_command("clone", "--noupdate", *flags, url, dest))
  49. self.run_command(
  50. make_command("update", *flags, rev_options.to_args()),
  51. cwd=dest,
  52. )
  53. def switch(
  54. self,
  55. dest: str,
  56. url: HiddenText,
  57. rev_options: RevOptions,
  58. verbosity: int = 0,
  59. ) -> None:
  60. extra_flags = []
  61. repo_config = os.path.join(dest, self.dirname, "hgrc")
  62. config = configparser.RawConfigParser()
  63. if verbosity <= 0:
  64. extra_flags.append("-q")
  65. try:
  66. config.read(repo_config)
  67. config.set("paths", "default", url.secret)
  68. with open(repo_config, "w") as config_file:
  69. config.write(config_file)
  70. except (OSError, configparser.NoSectionError) as exc:
  71. logger.warning("Could not switch Mercurial repository to %s: %s", url, exc)
  72. else:
  73. cmd_args = make_command("update", *extra_flags, rev_options.to_args())
  74. self.run_command(cmd_args, cwd=dest)
  75. def update(
  76. self,
  77. dest: str,
  78. url: HiddenText,
  79. rev_options: RevOptions,
  80. verbosity: int = 0,
  81. ) -> None:
  82. extra_flags = []
  83. if verbosity <= 0:
  84. extra_flags.append("-q")
  85. self.run_command(["pull", *extra_flags], cwd=dest)
  86. cmd_args = make_command("update", *extra_flags, rev_options.to_args())
  87. self.run_command(cmd_args, cwd=dest)
  88. @classmethod
  89. def get_remote_url(cls, location: str) -> str:
  90. url = cls.run_command(
  91. ["showconfig", "paths.default"],
  92. show_stdout=False,
  93. stdout_only=True,
  94. cwd=location,
  95. ).strip()
  96. if cls._is_local_repository(url):
  97. url = path_to_url(url)
  98. return url.strip()
  99. @classmethod
  100. def get_revision(cls, location: str) -> str:
  101. """
  102. Return the repository-local changeset revision number, as an integer.
  103. """
  104. current_revision = cls.run_command(
  105. ["parents", "--template={rev}"],
  106. show_stdout=False,
  107. stdout_only=True,
  108. cwd=location,
  109. ).strip()
  110. return current_revision
  111. @classmethod
  112. def get_requirement_revision(cls, location: str) -> str:
  113. """
  114. Return the changeset identification hash, as a 40-character
  115. hexadecimal string
  116. """
  117. current_rev_hash = cls.run_command(
  118. ["parents", "--template={node}"],
  119. show_stdout=False,
  120. stdout_only=True,
  121. cwd=location,
  122. ).strip()
  123. return current_rev_hash
  124. @classmethod
  125. def is_commit_id_equal(cls, dest: str, name: str | None) -> bool:
  126. """Always assume the versions don't match"""
  127. return False
  128. @classmethod
  129. def get_subdirectory(cls, location: str) -> str | None:
  130. """
  131. Return the path to Python project root, relative to the repo root.
  132. Return None if the project root is in the repo root.
  133. """
  134. # find the repo root
  135. repo_root = cls.run_command(
  136. ["root"], show_stdout=False, stdout_only=True, cwd=location
  137. ).strip()
  138. if not os.path.isabs(repo_root):
  139. repo_root = os.path.abspath(os.path.join(location, repo_root))
  140. return find_path_to_project_root_from_repo_root(location, repo_root)
  141. @classmethod
  142. def get_repository_root(cls, location: str) -> str | None:
  143. loc = super().get_repository_root(location)
  144. if loc:
  145. return loc
  146. try:
  147. r = cls.run_command(
  148. ["root"],
  149. cwd=location,
  150. show_stdout=False,
  151. stdout_only=True,
  152. on_returncode="raise",
  153. log_failed_cmd=False,
  154. )
  155. except BadCommand:
  156. logger.debug(
  157. "could not determine if %s is under hg control "
  158. "because hg is not available",
  159. location,
  160. )
  161. return None
  162. except InstallationError:
  163. return None
  164. return os.path.normpath(r.rstrip("\r\n"))
  165. vcs.register(Mercurial)