33from git .util import (
44join_path ,
55join_path_native ,
6- to_native_path_linux
6+ to_native_path_linux ,
7+ assure_directory_exists
78)
89
910from gitdb .util import (
@@ -28,6 +29,7 @@ class SymbolicReference(object):
2829
2930A typical example for a symbolic reference is HEAD."""
3031__slots__ = ("repo" ,"path" )
32+ _resolve_ref_on_create = False
3133_common_path_default = ""
3234_id_attribute_ = "name"
3335
@@ -58,7 +60,8 @@ def name(self):
5860is the path itself."""
5961return self .path
6062
61- def _abs_path (self ):
63+ @property
64+ def abspath (self ):
6265return join_path_native (self .repo .git_dir ,self .path )
6366
6467@classmethod
@@ -116,7 +119,7 @@ def _get_ref_info(self):
116119point to, or None"""
117120tokens = None
118121try :
119- fp = open (self ._abs_path () ,'r' )
122+ fp = open (self .abspath ,'r' )
120123value = fp .read ().rstrip ()
121124fp .close ()
122125tokens = value .split (" " )
@@ -158,37 +161,48 @@ def _get_commit(self):
158161
159162return self .from_path (self .repo ,target_ref_path ).commit
160163
161- def _set_commit (self ,commit ):
164+ def set_commit (self ,commit , msg = None ):
162165"""Set our commit, possibly dereference our symbolic reference first.
163- If the reference does not exist, it will be created"""
166+ If the reference does not exist, it will be created
167+
168+ :param msg: If not None, the message will be used in the reflog entry to be
169+ written. Otherwise the reflog is not altered"""
164170is_detached = True
165171try :
166172is_detached = self .is_detached
167173except ValueError :
168174pass
169175# END handle non-existing ones
176+
170177if is_detached :
171- return self ._set_reference (commit )
178+ return self .set_reference (commit , msg )
172179
173180# set the commit on our reference
174- self ._get_reference ().commit = commit
181+ self ._get_reference ().set_commit ( commit , msg )
175182
176- commit = property (_get_commit ,_set_commit ,doc = "Query or set commits directly" )
183+ commit = property (_get_commit ,set_commit ,doc = "Query or set commits directly" )
177184
178185def _get_reference (self ):
179- """:return: Reference Object we point to"""
186+ """:return: Reference Object we point to
187+ :raise TypeError: If this symbolic reference is detached, hence it doesn't point
188+ to a reference, but to a commit"""
180189sha ,target_ref_path = self ._get_ref_info ()
181190if target_ref_path is None :
182191raise TypeError ("%s is a detached symbolic reference as it points to %r" % (self ,sha ))
183192return self .from_path (self .repo ,target_ref_path )
184193
185- def _set_reference (self ,ref ,msg = None ):
194+ def set_reference (self ,ref ,msg = None ):
186195"""Set ourselves to the given ref. It will stay a symbol if the ref is a Reference.
187- Otherwise we try to get a commit from it using our interface.
196+ Otherwise a commmit, given as Commit object or refspec, is assumed and if valid,
197+ will be set which effectively detaches the refererence if it was a purely
198+ symbolic one.
188199
189- Strings are allowed but will be checked to be sure we have a commit
200+ :param ref: SymbolicReference instance, Commit instance or refspec string
190201:param msg: If set to a string, the message will be used in the reflog.
191- Otherwise, a reflog entry is not written for the changed reference"""
202+ Otherwise, a reflog entry is not written for the changed reference.
203+ The previous commit of the entry will be the commit we point to now.
204+
205+ See also: log_append()"""
192206write_value = None
193207if isinstance (ref ,SymbolicReference ):
194208write_value = "ref: %s" % ref .path
@@ -207,33 +221,31 @@ def _set_reference(self, ref, msg = None):
207221raise ValueError ("Could not extract object from %s" % ref )
208222# END end try string
209223# END try commit attribute
224+ oldbinsha = None
225+ if msg is not None :
226+ try :
227+ oldhexsha = self .commit .binsha
228+ except ValueError :
229+ oldbinsha = Commit .NULL_BIN_SHA
230+ #END handle non-existing
231+ #END retrieve old hexsha
232+
233+ fpath = self .abspath
234+ assure_directory_exists (fpath ,is_file = True )
235+
236+ lfd = LockedFD (fpath )
237+ fd = lfd .open (write = True ,stream = True )
238+ fd .write (write_value )
239+ lfd .commit ()
240+
241+ # Adjust the reflog
242+ if msg is not None :
243+ self .log_append (oldbinsha ,msg )
244+ #END handle reflog
210245
211- # if we are writing a ref, use symbolic ref to get the reflog and more
212- # checking
213- # Otherwise we detach it and have to do it manually. Besides, this works
214- # recursively automaitcally, but should be replaced with a python implementation
215- # soon
216- if write_value .startswith ('ref:' ):
217- self .repo .git .symbolic_ref (self .path ,write_value [5 :])
218- return
219- # END non-detached handling
220-
221- path = self ._abs_path ()
222- directory = dirname (path )
223- if not isdir (directory ):
224- os .makedirs (directory )
225-
226- # TODO: Write using LockedFD
227- fp = open (path ,"wb" )
228- try :
229- fp .write (write_value )
230- finally :
231- fp .close ()
232- # END writing
233-
234246
235247# aliased reference
236- reference = property (_get_reference ,_set_reference ,doc = "Returns the Reference we point to" )
248+ reference = property (_get_reference ,set_reference ,doc = "Returns the Reference we point to" )
237249ref = reference
238250
239251def is_valid (self ):
@@ -255,7 +267,7 @@ def is_detached(self):
255267True if we are a detached reference, hence we point to a specific commit
256268instead to another reference"""
257269try :
258- self .reference
270+ self .ref
259271return False
260272except TypeError :
261273return True
@@ -343,11 +355,18 @@ def delete(cls, repo, path):
343355open (pack_file_path ,'w' ).writelines (new_lines )
344356# END open exception handling
345357# END handle deletion
358+
359+ # delete the reflog
360+ reflog_path = RefLog .path (cls (repo ,full_ref_path ))
361+ if os .path .isfile (reflog_path ):
362+ os .remove (reflog_path )
363+ #END remove reflog
364+
346365
347366@classmethod
348- def _create (cls ,repo ,path ,resolve ,reference ,force ):
367+ def _create (cls ,repo ,path ,resolve ,reference ,force , msg = None ):
349368"""internal method used to create a new symbolic reference.
350- If resolve is False,, the reference will be taken as is, creating
369+ If resolve is False, the reference will be taken as is, creating
351370a proper symbolic reference. Otherwise it will be resolved to the
352371corresponding object and a detached symbolic reference will be created
353372instead"""
@@ -365,16 +384,17 @@ def _create(cls, repo, path, resolve, reference, force):
365384target_data = target .path
366385if not resolve :
367386target_data = "ref: " + target_data
368- if open (abs_ref_path ,'rb' ).read ().strip ()!= target_data :
369- raise OSError ("Reference at %s does already exist" % full_ref_path )
387+ existing_data = open (abs_ref_path ,'rb' ).read ().strip ()
388+ if existing_data != target_data :
389+ raise OSError ("Reference at %r does already exist, pointing to %r, requested was %r" % (full_ref_path ,existing_data ,target_data ))
370390# END no force handling
371391
372392ref = cls (repo ,full_ref_path )
373- ref .reference = target
393+ ref .set_reference ( target , msg )
374394return ref
375395
376396@classmethod
377- def create (cls ,repo ,path ,reference = 'HEAD' ,force = False ):
397+ def create (cls ,repo ,path ,reference = 'HEAD' ,force = False , msg = None ):
378398"""Create a new symbolic reference, hence a reference pointing to another reference.
379399
380400:param repo:
@@ -391,14 +411,18 @@ def create(cls, repo, path, reference='HEAD', force=False ):
391411if True, force creation even if a symbolic reference with that name already exists.
392412Raise OSError otherwise
393413
414+ :param msg:
415+ If not None, the message to append to the reflog. Otherwise no reflog
416+ entry is written.
417+
394418:return: Newly created symbolic Reference
395419
396420:raise OSError:
397421If a (Symbolic)Reference with the same name but different contents
398422already exists.
399423
400424:note: This does not alter the current HEAD, index or Working Tree"""
401- return cls ._create (repo ,path ,False ,reference ,force )
425+ return cls ._create (repo ,path ,cls . _resolve_ref_on_create ,reference ,force , msg )
402426
403427def rename (self ,new_path ,force = False ):
404428"""Rename self to a new path