|
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