|
10 | 10 | from objects.utils import get_object_type_by_name
|
11 | 11 | from utils import LazyMixin, Iterable, join_path, join_path_native, to_native_path_linux
|
12 | 12 |
|
13 |
| -class Reference(LazyMixin, Iterable): |
14 |
| - """ |
15 |
| - Represents a named reference to any object. Subclasses may apply restrictions though, |
16 |
| - i.e. Heads can only point to commits. |
17 |
| - """ |
18 |
| - __slots__ = ("repo", "path") |
19 |
| - _common_path_default = "refs" |
20 |
| - _id_attribute_ = "name" |
21 |
| - |
22 |
| - def __init__(self, repo, path): |
23 |
| - """ |
24 |
| - Initialize this instance |
25 |
| - ``repo`` |
26 |
| - Our parent repository |
27 |
| - |
28 |
| - ``path`` |
29 |
| - Path relative to the .git/ directory pointing to the ref in question, i.e. |
30 |
| - refs/heads/master |
31 |
| - |
32 |
| - """ |
33 |
| - if not path.startswith(self._common_path_default): |
34 |
| - raise ValueError("Cannot instantiate %s from path %s" % ( self.__class__.__name__, path )) |
35 |
| - |
36 |
| - self.repo = repo |
37 |
| - self.path = path |
38 |
| - |
39 |
| - def __str__(self): |
40 |
| - return self.name |
41 |
| - |
42 |
| - def __repr__(self): |
43 |
| - return '<git.%s "%s">' % (self.__class__.__name__, self.path) |
44 |
| - |
45 |
| - def __eq__(self, other): |
46 |
| - return self.path == other.path |
47 |
| - |
48 |
| - def __ne__(self, other): |
49 |
| - return not ( self == other ) |
50 |
| - |
51 |
| - def __hash__(self): |
52 |
| - return hash(self.path) |
53 |
| - |
54 |
| - @property |
55 |
| - def name(self): |
56 |
| - """ |
57 |
| - Returns |
58 |
| - (shortest) Name of this reference - it may contain path components |
59 |
| - """ |
60 |
| - # first two path tokens are can be removed as they are |
61 |
| - # refs/heads or refs/tags or refs/remotes |
62 |
| - tokens = self.path.split('/') |
63 |
| - if len(tokens) < 3: |
64 |
| - return self.path # could be refs/HEAD |
65 |
| - |
66 |
| - return '/'.join(tokens[2:]) |
67 |
| - |
68 |
| - def _get_object(self): |
69 |
| - """ |
70 |
| - Returns |
71 |
| - The object our ref currently refers to. Refs can be cached, they will |
72 |
| - always point to the actual object as it gets re-created on each query |
73 |
| - """ |
74 |
| - # have to be dynamic here as we may be a tag which can point to anything |
75 |
| - # Our path will be resolved to the hexsha which will be used accordingly |
76 |
| - return Object.new(self.repo, self.path) |
77 |
| - |
78 |
| - def _set_object(self, ref): |
79 |
| - """ |
80 |
| - Set our reference to point to the given ref. It will be converted |
81 |
| - to a specific hexsha. |
82 |
| - |
83 |
| - Note: |
84 |
| - TypeChecking is done by the git command |
85 |
| - """ |
86 |
| - # do it safely by specifying the old value |
87 |
| - self.repo.git.update_ref(self.path, ref, self._get_object().sha) |
88 |
| - |
89 |
| - object = property(_get_object, _set_object, doc="Return the object our ref currently refers to") |
90 |
| - |
91 |
| - def _set_commit(self, commit): |
92 |
| - """ |
93 |
| - Set ourselves to point to the given commit. |
94 |
| - |
95 |
| - Raise |
96 |
| - ValueError if commit does not actually point to a commit |
97 |
| - """ |
98 |
| - self._set_object(commit) |
99 |
| - |
100 |
| - def _get_commit(self): |
101 |
| - """ |
102 |
| - Returns |
103 |
| - Commit object the reference points to |
104 |
| - """ |
105 |
| - commit = self.object |
106 |
| - if commit.type != "commit": |
107 |
| - raise TypeError("Object of reference %s did not point to a commit, but to %r" % (self, commit)) |
108 |
| - return commit |
109 |
| - |
110 |
| - commit = property(_get_commit, _set_commit, doc="Return Commit object the reference points to") |
111 |
| - |
112 |
| - @classmethod |
113 |
| - def iter_items(cls, repo, common_path = None, **kwargs): |
114 |
| - """ |
115 |
| - Find all refs in the repository |
116 |
| -
|
117 |
| - ``repo`` |
118 |
| - is the Repo |
119 |
| -
|
120 |
| - ``common_path`` |
121 |
| - Optional keyword argument to the path which is to be shared by all |
122 |
| - returned Ref objects. |
123 |
| - Defaults to class specific portion if None assuring that only |
124 |
| - refs suitable for the actual class are returned. |
125 |
| -
|
126 |
| - Returns |
127 |
| - git.Reference[] |
128 |
| - |
129 |
| - List is lexigraphically sorted |
130 |
| - The returned objects represent actual subclasses, such as Head or TagReference |
131 |
| - """ |
132 |
| - if common_path is None: |
133 |
| - common_path = cls._common_path_default |
134 |
| - |
135 |
| - rela_paths = set() |
136 |
| - |
137 |
| - # walk loose refs |
138 |
| - # Currently we do not follow links |
139 |
| - for root, dirs, files in os.walk(join_path_native(repo.path, common_path)): |
140 |
| - for f in files: |
141 |
| - abs_path = to_native_path_linux(join_path(root, f)) |
142 |
| - rela_paths.add(abs_path.replace(to_native_path_linux(repo.path) + '/', "")) |
143 |
| - # END for each file in root directory |
144 |
| - # END for each directory to walk |
145 |
| - |
146 |
| - # read packed refs |
147 |
| - packed_refs_path = join_path_native(repo.path, 'packed-refs') |
148 |
| - if os.path.isfile(packed_refs_path): |
149 |
| - fp = open(packed_refs_path, 'r') |
150 |
| - try: |
151 |
| - for line in fp.readlines(): |
152 |
| - if line.startswith('#'): |
153 |
| - continue |
154 |
| - # 439689865b9c6e2a0dad61db22a0c9855bacf597 refs/heads/hello |
155 |
| - line = line.rstrip() |
156 |
| - first_space = line.find(' ') |
157 |
| - if first_space == -1: |
158 |
| - continue |
159 |
| - |
160 |
| - rela_path = line[first_space+1:] |
161 |
| - if rela_path.startswith(common_path): |
162 |
| - rela_paths.add(rela_path) |
163 |
| - # END relative path matches common path |
164 |
| - # END for each line in packed-refs |
165 |
| - finally: |
166 |
| - fp.close() |
167 |
| - # END packed refs reading |
168 |
| - |
169 |
| - # return paths in sorted order |
170 |
| - for path in sorted(rela_paths): |
171 |
| - if path.endswith('/HEAD'): |
172 |
| - continue |
173 |
| - # END skip remote heads |
174 |
| - yield cls.from_path(repo, path) |
175 |
| - # END for each sorted relative refpath |
176 |
| - |
177 |
| - |
178 |
| - @classmethod |
179 |
| - def from_path(cls, repo, path): |
180 |
| - """ |
181 |
| - Return |
182 |
| - Instance of type Reference, Head, or Tag |
183 |
| - depending on the given path |
184 |
| - """ |
185 |
| - if not path: |
186 |
| - raise ValueError("Cannot create Reference from %r" % path) |
187 |
| - |
188 |
| - for ref_type in (Head, RemoteReference, TagReference, Reference): |
189 |
| - try: |
190 |
| - return ref_type(repo, path) |
191 |
| - except ValueError: |
192 |
| - pass |
193 |
| - # END exception handling |
194 |
| - # END for each type to try |
195 |
| - raise ValueError("Could not find reference type suitable to handle path %r" % path) |
196 |
| - |
197 | 13 |
|
198 |
| -class SymbolicReference(object): |
| 14 | +class SymbolicReference(object): |
199 | 15 | """
|
200 | 16 | Represents a special case of a reference such that this reference is symbolic.
|
201 | 17 | It does not point to a specific commit, but to another Head, which itself
|
@@ -224,6 +40,15 @@ def __ne__(self, other):
|
224 | 40 | def __hash__(self):
|
225 | 41 | return hash(self.path)
|
226 | 42 |
|
| 43 | + @property |
| 44 | + def name(self): |
| 45 | + """ |
| 46 | + Returns |
| 47 | + In case of symbolic references, the shortest assumable name |
| 48 | + is the path itself. |
| 49 | + """ |
| 50 | + return self.path |
| 51 | + |
227 | 52 | def _get_path(self):
|
228 | 53 | return join_path_native(self.repo.path, self.path)
|
229 | 54 |
|
@@ -356,6 +181,157 @@ def from_path(cls, repo, path):
|
356 | 181 | return SymbolicReference(repo, path)
|
357 | 182 |
|
358 | 183 | raise ValueError("Could not find symbolic reference type suitable to handle path %r" % path)
|
| 184 | + |
| 185 | + |
| 186 | +class Reference(SymbolicReference, LazyMixin, Iterable): |
| 187 | + """ |
| 188 | + Represents a named reference to any object. Subclasses may apply restrictions though, |
| 189 | + i.e. Heads can only point to commits. |
| 190 | + """ |
| 191 | + __slots__ = tuple() |
| 192 | + _common_path_default = "refs" |
| 193 | + _id_attribute_ = "name" |
| 194 | + |
| 195 | + def __init__(self, repo, path): |
| 196 | + """ |
| 197 | + Initialize this instance |
| 198 | + ``repo`` |
| 199 | + Our parent repository |
| 200 | + |
| 201 | + ``path`` |
| 202 | + Path relative to the .git/ directory pointing to the ref in question, i.e. |
| 203 | + refs/heads/master |
| 204 | + |
| 205 | + """ |
| 206 | + if not path.startswith(self._common_path_default): |
| 207 | + raise ValueError("Cannot instantiate %s from path %s" % ( self.__class__.__name__, path )) |
| 208 | + super(Reference, self).__init__(repo, path) |
| 209 | + |
| 210 | + |
| 211 | + def __str__(self): |
| 212 | + return self.name |
| 213 | + |
| 214 | + def _get_object(self): |
| 215 | + """ |
| 216 | + Returns |
| 217 | + The object our ref currently refers to. Refs can be cached, they will |
| 218 | + always point to the actual object as it gets re-created on each query |
| 219 | + """ |
| 220 | + # have to be dynamic here as we may be a tag which can point to anything |
| 221 | + # Our path will be resolved to the hexsha which will be used accordingly |
| 222 | + return Object.new(self.repo, self.path) |
| 223 | + |
| 224 | + def _set_object(self, ref): |
| 225 | + """ |
| 226 | + Set our reference to point to the given ref. It will be converted |
| 227 | + to a specific hexsha. |
| 228 | + |
| 229 | + Note: |
| 230 | + TypeChecking is done by the git command |
| 231 | + """ |
| 232 | + # do it safely by specifying the old value |
| 233 | + self.repo.git.update_ref(self.path, ref, self._get_object().sha) |
| 234 | + |
| 235 | + object = property(_get_object, _set_object, doc="Return the object our ref currently refers to") |
| 236 | + |
| 237 | + @property |
| 238 | + def name(self): |
| 239 | + """ |
| 240 | + Returns |
| 241 | + (shortest) Name of this reference - it may contain path components |
| 242 | + """ |
| 243 | + # first two path tokens are can be removed as they are |
| 244 | + # refs/heads or refs/tags or refs/remotes |
| 245 | + tokens = self.path.split('/') |
| 246 | + if len(tokens) < 3: |
| 247 | + return self.path # could be refs/HEAD |
| 248 | + return '/'.join(tokens[2:]) |
| 249 | + |
| 250 | + @classmethod |
| 251 | + def iter_items(cls, repo, common_path = None, **kwargs): |
| 252 | + """ |
| 253 | + Find all refs in the repository |
| 254 | +
|
| 255 | + ``repo`` |
| 256 | + is the Repo |
| 257 | +
|
| 258 | + ``common_path`` |
| 259 | + Optional keyword argument to the path which is to be shared by all |
| 260 | + returned Ref objects. |
| 261 | + Defaults to class specific portion if None assuring that only |
| 262 | + refs suitable for the actual class are returned. |
| 263 | +
|
| 264 | + Returns |
| 265 | + git.Reference[] |
| 266 | + |
| 267 | + List is lexigraphically sorted |
| 268 | + The returned objects represent actual subclasses, such as Head or TagReference |
| 269 | + """ |
| 270 | + if common_path is None: |
| 271 | + common_path = cls._common_path_default |
| 272 | + |
| 273 | + rela_paths = set() |
| 274 | + |
| 275 | + # walk loose refs |
| 276 | + # Currently we do not follow links |
| 277 | + for root, dirs, files in os.walk(join_path_native(repo.path, common_path)): |
| 278 | + for f in files: |
| 279 | + abs_path = to_native_path_linux(join_path(root, f)) |
| 280 | + rela_paths.add(abs_path.replace(to_native_path_linux(repo.path) + '/', "")) |
| 281 | + # END for each file in root directory |
| 282 | + # END for each directory to walk |
| 283 | + |
| 284 | + # read packed refs |
| 285 | + packed_refs_path = join_path_native(repo.path, 'packed-refs') |
| 286 | + if os.path.isfile(packed_refs_path): |
| 287 | + fp = open(packed_refs_path, 'r') |
| 288 | + try: |
| 289 | + for line in fp.readlines(): |
| 290 | + if line.startswith('#'): |
| 291 | + continue |
| 292 | + # 439689865b9c6e2a0dad61db22a0c9855bacf597 refs/heads/hello |
| 293 | + line = line.rstrip() |
| 294 | + first_space = line.find(' ') |
| 295 | + if first_space == -1: |
| 296 | + continue |
| 297 | + |
| 298 | + rela_path = line[first_space+1:] |
| 299 | + if rela_path.startswith(common_path): |
| 300 | + rela_paths.add(rela_path) |
| 301 | + # END relative path matches common path |
| 302 | + # END for each line in packed-refs |
| 303 | + finally: |
| 304 | + fp.close() |
| 305 | + # END packed refs reading |
| 306 | + |
| 307 | + # return paths in sorted order |
| 308 | + for path in sorted(rela_paths): |
| 309 | + if path.endswith('/HEAD'): |
| 310 | + continue |
| 311 | + # END skip remote heads |
| 312 | + yield cls.from_path(repo, path) |
| 313 | + # END for each sorted relative refpath |
| 314 | + |
| 315 | + |
| 316 | + @classmethod |
| 317 | + def from_path(cls, repo, path): |
| 318 | + """ |
| 319 | + Return |
| 320 | + Instance of type Reference, Head, or Tag |
| 321 | + depending on the given path |
| 322 | + """ |
| 323 | + if not path: |
| 324 | + raise ValueError("Cannot create Reference from %r" % path) |
| 325 | + |
| 326 | + for ref_type in (Head, RemoteReference, TagReference, Reference): |
| 327 | + try: |
| 328 | + return ref_type(repo, path) |
| 329 | + except ValueError: |
| 330 | + pass |
| 331 | + # END exception handling |
| 332 | + # END for each type to try |
| 333 | + raise ValueError("Could not find reference type suitable to handle path %r" % path) |
| 334 | + |
359 | 335 |
|
360 | 336 | class HEAD(SymbolicReference):
|
361 | 337 | """
|
|
0 commit comments