88"""
99
1010from git .utils import LazyMixin , Iterable , IterableList
11- from refs import RemoteReference
11+ from refs import Reference , RemoteReference
12+ import re
13+ import os
1214
1315class _SectionConstraint (object ):
1416 """
@@ -54,29 +56,70 @@ class FetchInfo(object):
5456 Carries information about the results of a fetch operation::
5557
5658 info = remote.fetch()[0]
57- info.local_ref # None, or Reference object to the local head or tag which was moved
5859 info.remote_ref # Symbolic Reference or RemoteReference to the changed remote head or FETCH_HEAD
5960 info.flags # additional flags to be & with enumeration members, i.e. info.flags & info.REJECTED
61+ info.note # additional notes given by git-fetch intended for the user
6062 """
61- __slots__ = tuple ()
62- BRANCH_UPTODATE , REJECTED , FORCED_UPDATED , FAST_FORWARD , NEW_TAG , \
63- TAG_UPDATE , NEW_BRANCH = [ 1 << x for x in range (1 ,8 ) ]
63+ __slots__ = ('remote_ref' , 'flags' , 'note' )
64+ BRANCH_UPTODATE , REJECTED , FORCED_UPDATE , FAST_FORWARD , NEW_TAG , \
65+ TAG_UPDATE , NEW_BRANCH , ERROR = [ 1 << x for x in range (1 ,9 ) ]
66+ # %c %-*s %-*s -> %s (%s)
67+ re_fetch_result = re .compile ("^(.) (\[?[\w\s]+\]?)\s+(.+) -> (.+/.+)( \(.*\)?$)?" )
6468
65- def __init__ (self , local_ref , remote_ref , flags ):
69+ _flag_map = { '!' : ERROR , '+' : FORCED_UPDATE , '-' : TAG_UPDATE , '*' : 0 ,
70+ '=' : BRANCH_UPTODATE , ' ' : FAST_FORWARD }
71+
72+ def __init__ (self , remote_ref , flags , note = '' ):
6673 """
6774 Initialize a new instance
6875 """
69- self .local_ref = local_ref
7076 self .remote_ref = remote_ref
7177 self .flags = flags
78+ self .note = note
7279
7380 @classmethod
74- def _from_line (cls , line ):
81+ def _from_line (cls , repo , line ):
7582 """
7683 Parse information from the given line as returned by git-fetch -v
7784 and return a new FetchInfo object representing this information.
85+
86+ We can handle a line as follows
87+ "%c %-*s %-*s -> %s%s"
88+
89+ Where c is either ' ', !, +, -, *, or =
90+ ! means error
91+ + means success forcing update
92+ - means a tag was updated
93+ * means birth of new branch or tag
94+ = means the head was up to date ( and not moved )
95+ ' ' means a fast-forward
7896 """
79- raise NotImplementedError ("todo" )
97+ line = line .strip ()
98+ match = cls .re_fetch_result .match (line )
99+ if match is None :
100+ raise ValueError ("Failed to parse line: %r" % line )
101+ control_character , operation , local_remote_ref , remote_local_ref , note = match .groups ()
102+
103+ remote_local_ref = Reference .from_path (repo , os .path .join (RemoteReference ._common_path_default , remote_local_ref .strip ()))
104+ note = ( note and note .strip () ) or ''
105+
106+ # parse flags from control_character
107+ flags = 0
108+ try :
109+ flags |= cls ._flag_map [control_character ]
110+ except KeyError :
111+ raise ValueError ("Control character %r unknown as parsed from line %r" % (control_character , line ))
112+ # END control char exception hanlding
113+
114+ # parse operation string for more info
115+ if 'rejected' in operation :
116+ flags |= cls .REJECTED
117+ if 'new tag' in operation :
118+ flags |= cls .NEW_TAG
119+ if 'new branch' in operation :
120+ flags |= cls .NEW_BRANCH
121+
122+ return cls (remote_local_ref , flags , note )
80123
81124 # END FetchInfo definition
82125
@@ -230,6 +273,10 @@ def update(self, **kwargs):
230273 self .repo .git .remote ("update" , self .name )
231274 return self
232275
276+ def _get_fetch_info_from_stderr (self , stderr ):
277+ # skip first line as it is some remote info we are not interested in
278+ return [ self .FetchInfo ._from_line (self .repo , line ) for line in stderr .splitlines ()[1 :] ]
279+
233280 def fetch (self , refspec = None , ** kwargs ):
234281 """
235282 Fetch the latest changes for this remote
@@ -253,8 +300,8 @@ def fetch(self, refspec=None, **kwargs):
253300 list(FetchInfo, ...) list of FetchInfo instances providing detailed
254301 information about the fetch results
255302 """
256- lines = self .repo .git .fetch (self , refspec , v = True , ** kwargs ). splitlines ( )
257- return [ self .FetchInfo . _from_line ( line ) for line in lines ]
303+ status , stdout , stderr = self .repo .git .fetch (self , refspec , with_extended_output = True , v = True , ** kwargs )
304+ return self ._get_fetch_info_from_stderr ( stderr )
258305
259306 def pull (self , refspec = None , ** kwargs ):
260307 """
@@ -270,8 +317,8 @@ def pull(self, refspec=None, **kwargs):
270317 Returns
271318 list(Fetch
272319 """
273- lines = self .repo .git .pull (self , refspec , v = True , ** kwargs ). splitlines ( )
274- return [ self .FetchInfo . _from_line ( line ) for line in lines ]
320+ status , stdout , stderr = self .repo .git .pull (self , refspec , v = True , with_extended_output = True , ** kwargs )
321+ return self ._get_fetch_info_from_stderr ( stderr )
275322
276323 def push (self , refspec = None , ** kwargs ):
277324 """
0 commit comments