66"""
77Module containing all ref based objects
88"""
9+ import os
910from objects .base import Object
1011from objects .utils import get_object_type_by_name
1112from utils import LazyMixin , Iterable
@@ -31,6 +32,9 @@ def __init__(self, repo, path, object = None):
3132 ``object``
3233 Object instance, will be retrieved on demand if None
3334 """
35+ if not path .startswith (self ._common_path_default ):
36+ raise ValueError ("Cannot instantiate %s Reference from path %s" % ( self .__class__ .__name__ , path ))
37+
3438 self .repo = repo
3539 self .path = path
3640 if object is not None :
@@ -75,6 +79,17 @@ def object(self):
7579 # have to be dynamic here as we may be a tag which can point to anything
7680 # Our path will be resolved to the hexsha which will be used accordingly
7781 return Object .new (self .repo , self .path )
82+
83+ @property
84+ def commit (self ):
85+ """
86+ Returns
87+ Commit object the head points to
88+ """
89+ commit = self .object
90+ if commit .type != "commit" :
91+ raise TypeError ("Object of reference %s did not point to a commit" % self )
92+ return commit
7893
7994 @classmethod
8095 def iter_items (cls , repo , common_path = None , ** kwargs ):
@@ -112,6 +127,29 @@ def iter_items(cls, repo, common_path = None, **kwargs):
112127
113128 output = repo .git .for_each_ref (common_path , ** options )
114129 return cls ._iter_from_stream (repo , iter (output .splitlines ()))
130+
131+ @classmethod
132+ def from_path (cls , repo , path ):
133+ """
134+ Return
135+ Instance of type Reference, Head, Tag, SymbolicReference or HEAD
136+ depending on the given path
137+ """
138+ if path == 'HEAD' :
139+ return HEAD (repo , path )
140+
141+ if '/' not in path :
142+ return SymbolicReference (repo , path )
143+
144+ for ref_type in (Head , RemoteReference , TagReference , Reference ):
145+ try :
146+ return ref_type (repo , path )
147+ except ValueError :
148+ pass
149+ # END exception handling
150+ # END for each type to try
151+ raise ValueError ("Could not find reference type suitable to handle path %r" % path )
152+
115153
116154 @classmethod
117155 def _iter_from_stream (cls , repo , stream ):
@@ -145,47 +183,91 @@ def _from_string(cls, repo, line):
145183 # return cls(repo, full_path, obj)
146184
147185
148- class Head ( Reference ):
186+ class SymbolicReference ( object ):
149187 """
150- A Head is a named reference to a Commit. Every Head instance contains a name
151- and a Commit object.
152-
153- Examples::
154-
155- >>> repo = Repo("/path/to/repo")
156- >>> head = repo.heads[0]
157-
158- >>> head.name
159- 'master'
160-
161- >>> head.commit
162- <git.Commit "1c09f116cbc2cb4100fb6935bb162daa4723f455">
163-
164- >>> head.commit.id
165- '1c09f116cbc2cb4100fb6935bb162daa4723f455'
188+ Represents a special case of a reference such that this reference is symbolic.
189+ It does not point to a specific commit, but to another Head, which itself
190+ specifies a commit.
191+
192+ A typical example for a symbolic reference is HEAD.
166193 """
167- _common_path_default = "refs/heads"
194+ __slots__ = ( "repo" , "name" )
168195
196+ def __init__ (self , repo , name ):
197+ if '/' in name :
198+ raise ValueError ("SymbolicReferences are not located within a directory, got %s" % name )
199+ self .repo = repo
200+ self .name = name
201+
202+ def __str__ (self ):
203+ return self .name
204+
205+ def __repr__ (self ):
206+ return '<git.%s "%s">' % (self .__class__ .__name__ , self .name )
207+
208+ def __eq__ (self , other ):
209+ return self .name == other .name
210+
211+ def __ne__ (self , other ):
212+ return not ( self == other )
213+
214+ def __hash__ (self ):
215+ return hash (self .name )
216+
169217 @property
170- def commit (self ):
218+ def reference (self ):
171219 """
172220 Returns
173- Commit object the head points to
221+ Reference Object we point to
174222 """
175- return self .object
223+ fp = open (os .path .join (self .repo .path , self .name ), 'r' )
224+ try :
225+ tokens = fp .readline ().split (' ' )
226+ if tokens [0 ] != 'ref:' :
227+ raise TypeError ("%s is a detached symbolic reference as it points to %r" % tokens [0 ])
228+ return Reference .from_path (self .repo , tokens [1 ])
229+ finally :
230+ fp .close ()
176231
177- @classmethod
178- def reset (cls , repo , commit = 'HEAD' , index = True , working_tree = False ,
232+ # alias
233+ ref = reference
234+
235+ @property
236+ def is_detached (self ):
237+ """
238+ Returns
239+ True if we are a detached reference, hence we point to a specific commit
240+ instead to another reference
241+ """
242+ try :
243+ self .reference
244+ return False
245+ except TypeError :
246+ return True
247+
248+
249+ class HEAD (SymbolicReference ):
250+ """
251+ Special case of a Symbolic Reference as it represents the repository's
252+ HEAD reference.
253+ """
254+ __slots__ = tuple ()
255+
256+ def __init__ (self , repo , name ):
257+ if name != 'HEAD' :
258+ raise ValueError ("HEAD instance must point to 'HEAD', got %s" % name )
259+ super (HEAD , self ).__init__ (repo , name )
260+
261+
262+ def reset (self , commit = 'HEAD' , index = True , working_tree = False ,
179263 paths = None , ** kwargs ):
180264 """
181- Reset the current head to the given commit optionally synchronizing
265+ Reset our HEAD to the given commit optionally synchronizing
182266 the index and working tree.
183267
184- ``repo``
185- Repository containing commit
186-
187268 ``commit``
188- Commit object, Reference Object or string identifying a revision
269+ Commit object, Reference Object or string identifying a revision we
270+ should reset HEAD to.
189271
190272 ``index``
191273 If True, the index will be set to match the given commit. Otherwise
@@ -204,7 +286,7 @@ def reset(cls, repo, commit='HEAD', index=True, working_tree = False,
204286 Additional arguments passed to git-reset.
205287
206288 Returns
207- Head pointing to the specified commit
289+ self
208290 """
209291 mode = "--soft"
210292 if index :
@@ -219,9 +301,32 @@ def reset(cls, repo, commit='HEAD', index=True, working_tree = False,
219301 repo .git .reset (mode , commit , paths , ** kwargs )
220302
221303 # we always point to the active branch as it is the one changing
222- return repo .active_branch
304+ self
305+
306+
307+ class Head (Reference ):
308+ """
309+ A Head is a named reference to a Commit. Every Head instance contains a name
310+ and a Commit object.
311+
312+ Examples::
313+
314+ >>> repo = Repo("/path/to/repo")
315+ >>> head = repo.heads[0]
316+
317+ >>> head.name
318+ 'master'
319+
320+ >>> head.commit
321+ <git.Commit "1c09f116cbc2cb4100fb6935bb162daa4723f455">
322+
323+ >>> head.commit.id
324+ '1c09f116cbc2cb4100fb6935bb162daa4723f455'
325+ """
326+ _common_path_default = "refs/heads"
327+
223328
224- class TagReference (Head ):
329+ class TagReference (Reference ):
225330 """
226331 Class representing a lightweight tag reference which either points to a commit
227332 or to a tag object. In the latter case additional information, like the signature
@@ -230,7 +335,7 @@ class TagReference(Head):
230335 This tag object will always point to a commit object, but may carray additional
231336 information in a tag object::
232337
233- tagref = TagRef .list_items(repo)[0]
338+ tagref = TagReference .list_items(repo)[0]
234339 print tagref.commit.message
235340 if tagref.tag is not None:
236341 print tagref.tag.message
0 commit comments