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``
3233Object 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+
3438self .repo = repo
3539self .path = path
3640if 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
7781return 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
8095def iter_items (cls ,repo ,common_path = None ,** kwargs ):
@@ -112,6 +127,29 @@ def iter_items(cls, repo, common_path = None, **kwargs):
112127
113128output = repo .git .for_each_ref (common_path ,** options )
114129return 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
117155def _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"""
172220Returns
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 ,
179263paths = None ,** kwargs ):
180264"""
181- Resetthe current head to the given commit optionally synchronizing
265+ Resetour HEAD to the given commit optionally synchronizing
182266the 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``
191273If 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,
204286Additional arguments passed to git-reset.
205287
206288Returns
207- Head pointing to the specified commit
289+ self
208290"""
209291mode = "--soft"
210292if index :
@@ -219,9 +301,32 @@ def reset(cls, repo, commit='HEAD', index=True, working_tree = False,
219301repo .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"""
226331Class representing a lightweight tag reference which either points to a commit
227332or to a tag object. In the latter case additional information, like the signature
@@ -230,7 +335,7 @@ class TagReference(Head):
230335This tag object will always point to a commit object, but may carray additional
231336information 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:
236341print tagref.tag.message