@@ -19,7 +19,7 @@ class Reference(LazyMixin, Iterable):
1919 _common_path_default = "refs"
2020 _id_attribute_ = "name"
2121
22- def __init__ (self , repo , path , object = None ):
22+ def __init__ (self , repo , path ):
2323 """
2424 Initialize this instance
2525 ``repo``
@@ -29,16 +29,12 @@ def __init__(self, repo, path, object = None):
2929 Path relative to the .git/ directory pointing to the ref in question, i.e.
3030 refs/heads/master
3131
32- ``object``
33- Object instance, will be retrieved on demand if None
3432 """
3533 if not path .startswith (self ._common_path_default ):
3634 raise ValueError ("Cannot instantiate %s Reference from path %s" % ( self .__class__ .__name__ , path ))
3735
3836 self .repo = repo
3937 self .path = path
40- if object is not None :
41- self .object = object
4238
4339 def __str__ (self ):
4440 return self .name
@@ -69,8 +65,7 @@ def name(self):
6965
7066 return '/' .join (tokens [2 :])
7167
72- @property
73- def object (self ):
68+ def _get_object (self ):
7469 """
7570 Returns
7671 The object our ref currently refers to. Refs can be cached, they will
@@ -80,17 +75,54 @@ def object(self):
8075 # Our path will be resolved to the hexsha which will be used accordingly
8176 return Object .new (self .repo , self .path )
8277
83- @property
84- def commit (self ):
78+ def _set_object (self , ref , type = None ):
79+ """
80+ Set our reference to point to the given ref. It will be converted
81+ to a specific hexsha.
82+
83+ ``type``
84+ If not None, string type of that the object must have, other we raise
85+ a type error. Only used internally
86+
87+ Returns
88+ Object we have set. This is used internally only to reduce the amount
89+ of calls to the git command
90+ """
91+ obj = Object .new (self .repo , ref )
92+ if type is not None and obj .type != type :
93+ raise TypeError ("Reference %r cannot point to object of type %r" % (self ,obj .type ))
94+
95+ full_ref_path = os .path .join (self .repo .path , self .path )
96+ fp = open (full_ref_path , "w" )
97+ try :
98+ fp .write (str (obj ))
99+ finally :
100+ fp .close ()
101+ return obj
102+
103+ object = property (_get_object , _set_object , doc = "Return the object our ref currently refers to" )
104+
105+ def _set_commit (self , commit ):
106+ """
107+ Set ourselves to point to the given commit.
108+
109+ Raise
110+ ValueError if commit does not actually point to a commit
111+ """
112+ self ._set_object (commit , type = "commit" )
113+
114+ def _get_commit (self ):
85115 """
86116 Returns
87- Commit object the head points to
117+ Commit object the reference points to
88118 """
89119 commit = self .object
90120 if commit .type != "commit" :
91121 raise TypeError ("Object of reference %s did not point to a commit" % self )
92122 return commit
93123
124+ commit = property (_get_commit , _set_commit , doc = "Return Commit object the reference points to" )
125+
94126 @classmethod
95127 def iter_items (cls , repo , common_path = None , ** kwargs ):
96128 """
@@ -182,6 +214,7 @@ def _from_string(cls, repo, line):
182214 # obj.size = object_size
183215 # return cls(repo, full_path, obj)
184216
217+
185218
186219class SymbolicReference (object ):
187220 """
@@ -247,7 +280,7 @@ def _get_reference(self):
247280 try :
248281 tokens = fp .readline ().rstrip ().split (' ' )
249282 if tokens [0 ] != 'ref:' :
250- raise TypeError ("%s is a detached symbolic reference as it points to %r" % tokens [0 ])
283+ raise TypeError ("%s is a detached symbolic reference as it points to %r" % ( self , tokens [0 ]) )
251284 return Reference .from_path (self .repo , tokens [1 ])
252285 finally :
253286 fp .close ()
@@ -384,12 +417,92 @@ class Head(Reference):
384417 """
385418 _common_path_default = "refs/heads"
386419
420+ @classmethod
421+ def create (cls , repo , path , commit = 'HEAD' , force = False , ** kwargs ):
422+ """
423+ Create a new head.
424+ ``repo``
425+ Repository to create the head in
426+
427+ ``path``
428+ The name or path of the head, i.e. 'new_branch' or
429+ feature/feature1. The prefix refs/heads is implied.
430+
431+ ``commit``
432+ Commit to which the new head should point, defaults to the
433+ current HEAD
434+
435+ ``force``
436+ if True, force creation even if branch with that name already exists.
437+
438+ ``**kwargs``
439+ Additional keyword arguments to be passed to git-branch, i.e.
440+ track, no-track, l
441+
442+ Returns
443+ Newly created Head
444+
445+ Note
446+ This does not alter the current HEAD, index or Working Tree
447+ """
448+ if cls is not Head :
449+ raise TypeError ("Only Heads can be created explicitly, not objects of type %s" % cls .__name__ )
450+
451+ args = ( path , commit )
452+ if force :
453+ kwargs ['f' ] = True
454+
455+ repo .git .branch (* args , ** kwargs )
456+ return cls (repo , "%s/%s" % ( cls ._common_path_default , path ))
457+
458+
459+ @classmethod
460+ def delete (cls , repo , * heads , ** kwargs ):
461+ """
462+ Delete the given heads
463+
464+ ``force``
465+ If True, the heads will be deleted even if they are not yet merged into
466+ the main development stream.
467+ Default False
468+ """
469+ force = kwargs .get ("force" , False )
470+ flag = "-d"
471+ if force :
472+ flag = "-D"
473+ repo .git .branch (flag , * heads )
474+
475+
476+ def rename (self , new_path , force = False ):
477+ """
478+ Rename self to a new path
479+
480+ ``new_path``
481+ Either a simple name or a path, i.e. new_name or features/new_name.
482+ The prefix refs/heads is implied
483+
484+ ``force``
485+ If True, the rename will succeed even if a head with the target name
486+ already exists.
487+
488+ Returns
489+ self
490+ """
491+ flag = "-m"
492+ if force :
493+ flag = "-M"
494+
495+ self .repo .git .branch (flag , self , new_path )
496+ self .path = "%s/%s" % (self ._common_path_default , new_path )
497+ return self
498+
499+
387500
388501class TagReference (Reference ):
389502 """
390503 Class representing a lightweight tag reference which either points to a commit
391- or to a tag object. In the latter case additional information, like the signature
392- or the tag-creator, is available.
504+ , a tag object or any other object . In the latter case additional information,
505+ like the signature or the tag-creator, is available.
393506
394507 This tag object will always point to a commit object, but may carray additional
395508 information in a tag object::
@@ -427,6 +540,52 @@ def tag(self):
427540 if self .object .type == "tag" :
428541 return self .object
429542 return None
543+
544+ @classmethod
545+ def create (cls , repo , path , ref = 'HEAD' , message = None , force = False , ** kwargs ):
546+ """
547+ Create a new tag object.
548+
549+ ``path``
550+ The name of the tag, i.e. 1.0 or releases/1.0.
551+ The prefix refs/tags is implied
552+
553+ ``ref``
554+ A reference to the object you want to tag. It can be a commit, tree or
555+ blob.
556+
557+ ``message``
558+ If not None, the message will be used in your tag object. This will also
559+ create an additional tag object that allows to obtain that information, i.e.::
560+ tagref.tag.message
561+
562+ ``force``
563+ If True, to force creation of a tag even though that tag already exists.
564+
565+ ``**kwargs``
566+ Additional keyword arguments to be passed to git-tag
567+
568+ Returns
569+ A new TagReference
570+ """
571+ args = ( path , ref )
572+ if message :
573+ kwargs ['m' ] = message
574+ if force :
575+ kwargs ['f' ] = True
576+
577+ repo .git .tag (* args , ** kwargs )
578+ return TagReference (repo , "%s/%s" % (cls ._common_path_default , path ))
579+
580+ @classmethod
581+ def delete (cls , repo , * tags ):
582+ """
583+ Delete the given existing tag or tags
584+ """
585+ repo .git .tag ("-d" , * tags )
586+
587+
588+
430589
431590
432591# provide an alias
@@ -460,3 +619,14 @@ def remote_branch(self):
460619 """
461620 tokens = self .path .split ('/' )
462621 return '/' .join (tokens [3 :])
622+
623+ @classmethod
624+ def delete (cls , repo , * remotes , ** kwargs ):
625+ """
626+ Delete the given remote references.
627+
628+ Note
629+ kwargs are given for compatability with the base class method as we
630+ should not narrow the signature.
631+ """
632+ repo .git .branch ("-d" , "-r" , * remotes )
0 commit comments