Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/git/refs/reference.py: 79%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

57 statements  

1# This module is part of GitPython and is released under the 

2# 3-Clause BSD License: https://opensource.org/license/bsd-3-clause/ 

3 

4__all__ = ["Reference"] 

5 

6import os 

7from git.util import IterableObj, LazyMixin 

8 

9from .symbolic import SymbolicReference, T_References 

10 

11# typing ------------------------------------------------------------------ 

12 

13from typing import Any, Callable, Iterator, TYPE_CHECKING, Type, Union 

14 

15from git.types import AnyGitObject, PathLike, _T 

16 

17if TYPE_CHECKING: 

18 from git.repo import Repo 

19 

20# ------------------------------------------------------------------------------ 

21 

22# { Utilities 

23 

24 

25def require_remote_ref_path(func: Callable[..., _T]) -> Callable[..., _T]: 

26 """A decorator raising :exc:`ValueError` if we are not a valid remote, based on the 

27 path.""" 

28 

29 def wrapper(self: T_References, *args: Any) -> _T: 

30 if not self.is_remote(): 

31 raise ValueError("ref path does not point to a remote reference: %s" % self.path) 

32 return func(self, *args) 

33 

34 # END wrapper 

35 wrapper.__name__ = func.__name__ 

36 return wrapper 

37 

38 

39# } END utilities 

40 

41 

42class Reference(SymbolicReference, LazyMixin, IterableObj): 

43 """A named reference to any object. 

44 

45 Subclasses may apply restrictions though, e.g., a :class:`~git.refs.head.Head` can 

46 only point to commits. 

47 """ 

48 

49 __slots__ = () 

50 

51 _points_to_commits_only = False 

52 _resolve_ref_on_create = True 

53 _common_path_default = "refs" 

54 

55 def __init__(self, repo: "Repo", path: PathLike, check_path: bool = True) -> None: 

56 """Initialize this instance. 

57 

58 :param repo: 

59 Our parent repository. 

60 

61 :param path: 

62 Path relative to the ``.git/`` directory pointing to the ref in question, 

63 e.g. ``refs/heads/master``. 

64 

65 :param check_path: 

66 If ``False``, you can provide any path. 

67 Otherwise the path must start with the default path prefix of this type. 

68 """ 

69 if check_path and not os.fspath(path).startswith(self._common_path_default + "/"): 

70 raise ValueError(f"Cannot instantiate {self.__class__.__name__!r} from path {path}") 

71 self.path: str # SymbolicReference converts to string at the moment. 

72 super().__init__(repo, path) 

73 

74 def __str__(self) -> str: 

75 return self.name 

76 

77 # { Interface 

78 

79 # @ReservedAssignment 

80 def set_object( 

81 self, 

82 object: Union[AnyGitObject, "SymbolicReference", str], 

83 logmsg: Union[str, None] = None, 

84 ) -> "Reference": 

85 """Special version which checks if the head-log needs an update as well. 

86 

87 :return: 

88 self 

89 """ 

90 oldbinsha = None 

91 if logmsg is not None: 

92 head = self.repo.head 

93 if not head.is_detached and head.ref == self: 

94 oldbinsha = self.commit.binsha 

95 # END handle commit retrieval 

96 # END handle message is set 

97 

98 super().set_object(object, logmsg) 

99 

100 if oldbinsha is not None: 

101 # From refs/files-backend.c in git-source: 

102 # /* 

103 # * Special hack: If a branch is updated directly and HEAD 

104 # * points to it (may happen on the remote side of a push 

105 # * for example) then logically the HEAD reflog should be 

106 # * updated too. 

107 # * A generic solution implies reverse symref information, 

108 # * but finding all symrefs pointing to the given branch 

109 # * would be rather costly for this rare event (the direct 

110 # * update of a branch) to be worth it. So let's cheat and 

111 # * check with HEAD only which should cover 99% of all usage 

112 # * scenarios (even 100% of the default ones). 

113 # */ 

114 self.repo.head.log_append(oldbinsha, logmsg) 

115 # END check if the head 

116 

117 return self 

118 

119 # NOTE: No need to overwrite properties, as the will only work without a the log. 

120 

121 @property 

122 def name(self) -> str: 

123 """ 

124 :return: 

125 (shortest) Name of this reference - it may contain path components 

126 """ 

127 # The first two path tokens can be removed as they are 

128 # refs/heads or refs/tags or refs/remotes. 

129 tokens = self.path.split("/") 

130 if len(tokens) < 3: 

131 return self.path # could be refs/HEAD 

132 return "/".join(tokens[2:]) 

133 

134 @classmethod 

135 def iter_items( 

136 cls: Type[T_References], 

137 repo: "Repo", 

138 common_path: Union[PathLike, None] = None, 

139 *args: Any, 

140 **kwargs: Any, 

141 ) -> Iterator[T_References]: 

142 """Equivalent to 

143 :meth:`SymbolicReference.iter_items <git.refs.symbolic.SymbolicReference.iter_items>`, 

144 but will return non-detached references as well.""" 

145 return cls._iter_items(repo, common_path) 

146 

147 # } END interface 

148 

149 # { Remote Interface 

150 

151 @property 

152 @require_remote_ref_path 

153 def remote_name(self) -> str: 

154 """ 

155 :return: 

156 Name of the remote we are a reference of, such as ``origin`` for a reference 

157 named ``origin/master``. 

158 """ 

159 tokens = self.path.split("/") 

160 # /refs/remotes/<remote name>/<branch_name> 

161 return tokens[2] 

162 

163 @property 

164 @require_remote_ref_path 

165 def remote_head(self) -> str: 

166 """ 

167 :return: 

168 Name of the remote head itself, e.g. ``master``. 

169 

170 :note: 

171 The returned name is usually not qualified enough to uniquely identify a 

172 branch. 

173 """ 

174 tokens = self.path.split("/") 

175 return "/".join(tokens[3:]) 

176 

177 # } END remote interface