1
1
import os
2
- from git .objects import Commit
2
+ from git .objects import Object , Commit
3
3
from git .util import (
4
4
join_path ,
5
5
join_path_native ,
@@ -30,6 +30,7 @@ class SymbolicReference(object):
30
30
A typical example for a symbolic reference is HEAD."""
31
31
__slots__ = ("repo" , "path" )
32
32
_resolve_ref_on_create = False
33
+ _points_to_commits_only = True
33
34
_common_path_default = ""
34
35
_id_attribute_ = "name"
35
36
@@ -107,66 +108,89 @@ def dereference_recursive(cls, repo, ref_path):
107
108
intermediate references as required
108
109
:param repo: the repository containing the reference at ref_path"""
109
110
while True :
110
- ref = cls (repo , ref_path )
111
- hexsha , ref_path = ref ._get_ref_info ()
111
+ hexsha , ref_path = cls ._get_ref_info (repo , ref_path )
112
112
if hexsha is not None :
113
113
return hexsha
114
114
# END recursive dereferencing
115
115
116
- def _get_ref_info (self ):
116
+ @classmethod
117
+ def _get_ref_info (cls , repo , path ):
117
118
"""Return: (sha, target_ref_path) if available, the sha the file at
118
119
rela_path points to, or None. target_ref_path is the reference we
119
120
point to, or None"""
120
121
tokens = None
121
122
try :
122
- fp = open (self . abspath , 'r' )
123
+ fp = open (join ( repo . git_dir , path ) , 'r' )
123
124
value = fp .read ().rstrip ()
124
125
fp .close ()
125
126
tokens = value .split (" " )
126
127
except (OSError ,IOError ):
127
128
# Probably we are just packed, find our entry in the packed refs file
128
129
# NOTE: We are not a symbolic ref if we are in a packed file, as these
129
130
# are excluded explictly
130
- for sha , path in self ._iter_packed_refs (self . repo ):
131
- if path != self . path : continue
131
+ for sha , path in cls ._iter_packed_refs (repo ):
132
+ if path != path : continue
132
133
tokens = (sha , path )
133
134
break
134
135
# END for each packed ref
135
136
# END handle packed refs
136
137
137
138
if tokens is None :
138
- raise ValueError ("Reference at %r does not exist" % self . path )
139
+ raise ValueError ("Reference at %r does not exist" % path )
139
140
140
141
# is it a reference ?
141
142
if tokens [0 ] == 'ref:' :
142
143
return (None , tokens [1 ])
143
144
144
145
# its a commit
145
- if self . repo .re_hexsha_only .match (tokens [0 ]):
146
+ if repo .re_hexsha_only .match (tokens [0 ]):
146
147
return (tokens [0 ], None )
147
148
148
- raise ValueError ("Failed to parse reference information from %r" % self .path )
149
-
149
+ raise ValueError ("Failed to parse reference information from %r" % path )
150
+
151
+ def _get_object (self ):
152
+ """
153
+ :return:
154
+ The object our ref currently refers to. Refs can be cached, they will
155
+ always point to the actual object as it gets re-created on each query"""
156
+ # have to be dynamic here as we may be a tag which can point to anything
157
+ # Our path will be resolved to the hexsha which will be used accordingly
158
+ return Object .new_from_sha (self .repo , hex_to_bin (self .dereference_recursive (self .repo , self .path )))
159
+
150
160
def _get_commit (self ):
151
161
"""
152
162
:return:
153
163
Commit object we point to, works for detached and non-detached
154
- SymbolicReferences"""
155
- # we partially reimplement it to prevent unnecessary file access
156
- hexsha , target_ref_path = self ._get_ref_info ()
157
-
158
- # it is a detached reference
159
- if hexsha :
160
- return Commit (self .repo , hex_to_bin (hexsha ))
161
-
162
- return self .from_path (self .repo , target_ref_path ).commit
164
+ SymbolicReferences. The symbolic reference will be dereferenced recursively."""
165
+ obj = self ._get_object ()
166
+ if obj .type != Commit .type :
167
+ raise TypeError ("Symbolic Reference pointed to object %r, commit was required" % obj )
168
+ #END handle type
169
+ return obj
163
170
164
171
def set_commit (self , commit , msg = None ):
165
- """Set our commit, possibly dereference our symbolic reference first.
172
+ """As set_object, but restricts the type of object to be a Commit
173
+ :note: To save cycles, we do not yet check whether the given Object
174
+ is actually referring to a commit - for now it may be any of our
175
+ Object or Reference types, as well as a refspec"""
176
+ # may have to check the type ... this is costly as we would have to use
177
+ # revparse
178
+ self .set_object (commit , msg )
179
+
180
+
181
+ def set_object (self , object , msg = None ):
182
+ """Set the object we point to, possibly dereference our symbolic reference first.
166
183
If the reference does not exist, it will be created
167
184
185
+ :param object: a refspec, a SymbolicReference or an Object instance. SymbolicReferences
186
+ will be dereferenced beforehand to obtain the object they point to
168
187
:param msg: If not None, the message will be used in the reflog entry to be
169
- written. Otherwise the reflog is not altered"""
188
+ written. Otherwise the reflog is not altered
189
+ :note: plain SymbolicReferences may not actually point to objects by convention"""
190
+ if isinstance (object , SymbolicReference ):
191
+ object = object .object
192
+ #END resolve references
193
+
170
194
is_detached = True
171
195
try :
172
196
is_detached = self .is_detached
@@ -175,56 +199,66 @@ def set_commit(self, commit, msg = None):
175
199
# END handle non-existing ones
176
200
177
201
if is_detached :
178
- return self .set_reference (commit , msg )
202
+ return self .set_reference (object , msg )
179
203
180
204
# set the commit on our reference
181
- self ._get_reference ().set_commit ( commit , msg )
205
+ self ._get_reference ().set_object ( object , msg )
182
206
183
207
commit = property (_get_commit , set_commit , doc = "Query or set commits directly" )
208
+ object = property (_get_object , set_object , doc = "Return the object our ref currently refers to" )
184
209
185
210
def _get_reference (self ):
186
211
""":return: Reference Object we point to
187
212
:raise TypeError: If this symbolic reference is detached, hence it doesn't point
188
213
to a reference, but to a commit"""
189
- sha , target_ref_path = self ._get_ref_info ()
214
+ sha , target_ref_path = self ._get_ref_info (self . repo , self . path )
190
215
if target_ref_path is None :
191
216
raise TypeError ("%s is a detached symbolic reference as it points to %r" % (self , sha ))
192
217
return self .from_path (self .repo , target_ref_path )
193
218
194
219
def set_reference (self , ref , msg = None ):
195
220
"""Set ourselves to the given ref. It will stay a symbol if the ref is a Reference.
196
- Otherwise a commmit , given as Commit object or refspec, is assumed and if valid,
221
+ Otherwise an Object , given as Object instance or refspec, is assumed and if valid,
197
222
will be set which effectively detaches the refererence if it was a purely
198
223
symbolic one.
199
224
200
- :param ref: SymbolicReference instance, Commit instance or refspec string
225
+ :param ref: SymbolicReference instance, Object instance or refspec string
226
+ Only if the ref is a SymbolicRef instance, we will point to it. Everthiny
227
+ else is dereferenced to obtain the actual object.
201
228
:param msg: If set to a string, the message will be used in the reflog.
202
229
Otherwise, a reflog entry is not written for the changed reference.
203
230
The previous commit of the entry will be the commit we point to now.
204
231
205
- See also: log_append()"""
232
+ See also: log_append()
233
+ :note: This symbolic reference will not be dereferenced. For that, see
234
+ ``set_object(...)``"""
206
235
write_value = None
236
+ obj = None
207
237
if isinstance (ref , SymbolicReference ):
208
238
write_value = "ref: %s" % ref .path
209
- elif isinstance (ref , Commit ):
239
+ elif isinstance (ref , Object ):
240
+ obj = ref
210
241
write_value = ref .hexsha
211
- else :
242
+ elif isinstance ( ref , basestring ) :
212
243
try :
213
- write_value = ref .commit .hexsha
214
- except AttributeError :
215
- try :
216
- obj = self .repo .rev_parse (ref + "^{}" ) # optionally deref tags
217
- if obj .type != "commit" :
218
- raise TypeError ("Invalid object type behind sha: %s" % sha )
219
- write_value = obj .hexsha
220
- except Exception :
221
- raise ValueError ("Could not extract object from %s" % ref )
222
- # END end try string
244
+ obj = self .repo .rev_parse (ref + "^{}" ) # optionally deref tags
245
+ write_value = obj .hexsha
246
+ except Exception :
247
+ raise ValueError ("Could not extract object from %s" % ref )
248
+ # END end try string
249
+ else :
250
+ raise ValueError ("Unrecognized Value: %r" % ref )
223
251
# END try commit attribute
252
+
253
+ # typecheck
254
+ if obj is not None and self ._points_to_commits_only and obj .type != Commit .type :
255
+ raise TypeError ("Require commit, got %r" % obj )
256
+ #END verify type
257
+
224
258
oldbinsha = None
225
259
if msg is not None :
226
260
try :
227
- oldhexsha = self .commit .binsha
261
+ oldbinsha = self .commit .binsha
228
262
except ValueError :
229
263
oldbinsha = Commit .NULL_BIN_SHA
230
264
#END handle non-existing
@@ -247,14 +281,14 @@ def set_reference(self, ref, msg = None):
247
281
# aliased reference
248
282
reference = property (_get_reference , set_reference , doc = "Returns the Reference we point to" )
249
283
ref = reference
250
-
284
+
251
285
def is_valid (self ):
252
286
"""
253
287
:return:
254
288
True if the reference is valid, hence it can be read and points to
255
289
a valid object or reference."""
256
290
try :
257
- self .commit
291
+ self .object
258
292
except (OSError , ValueError ):
259
293
return False
260
294
else :
@@ -288,7 +322,7 @@ def log_append(self, oldbinsha, message, newbinsha=None):
288
322
:param newbinsha: The sha the ref points to now. If None, our current commit sha
289
323
will be used
290
324
:return: added RefLogEntry instance"""
291
- return RefLog .append_entry (RefLog .path (self ), oldbinsha ,
325
+ return RefLog .append_entry (self . repo . config_reader (), RefLog .path (self ), oldbinsha ,
292
326
(newbinsha is None and self .commit .binsha ) or newbinsha ,
293
327
message )
294
328
0 commit comments