|  | 
| 7 | 7 | # Module implementing a remote object allowing easy access to git remotes | 
| 8 | 8 | 
 | 
| 9 | 9 | from exc import GitCommandError | 
| 10 |  | -from objects import Commit | 
| 11 | 10 | from ConfigParser import NoOptionError | 
| 12 | 11 | from config import SectionConstraint | 
| 13 | 12 | 
 | 
| 14 | 13 | from git.util import ( | 
| 15 | 14 | 						LazyMixin, | 
| 16 | 15 | 						Iterable, | 
| 17 |  | -						IterableList | 
|  | 16 | +						IterableList, | 
|  | 17 | +						RemoteProgress | 
| 18 | 18 | 						) | 
| 19 | 19 | 
 | 
| 20 | 20 | from refs import ( | 
|  | 
| 33 | 33 | 
 | 
| 34 | 34 | __all__ = ('RemoteProgress', 'PushInfo', 'FetchInfo', 'Remote') | 
| 35 | 35 | 
 | 
| 36 |  | -class RemoteProgress(object): | 
| 37 |  | -	""" | 
| 38 |  | -	Handler providing an interface to parse progress information emitted by git-push | 
| 39 |  | -	and git-fetch and to dispatch callbacks allowing subclasses to react to the progress. | 
| 40 |  | -	""" | 
| 41 |  | -	BEGIN, END, COUNTING, COMPRESSING, WRITING =  [ 1 << x for x in range(5) ] | 
| 42 |  | -	STAGE_MASK = BEGIN|END | 
| 43 |  | -	OP_MASK = COUNTING|COMPRESSING|WRITING | 
| 44 |  | -	 | 
| 45 |  | -	__slots__ = ("_cur_line", "_seen_ops") | 
| 46 |  | -	re_op_absolute = re.compile("(remote: )?([\w\s]+):\s+()(\d+)()(.*)") | 
| 47 |  | -	re_op_relative = re.compile("(remote: )?([\w\s]+):\s+(\d+)% \((\d+)/(\d+)\)(.*)") | 
| 48 |  | -	 | 
| 49 |  | -	def __init__(self): | 
| 50 |  | -		self._seen_ops = list() | 
| 51 |  | -	 | 
| 52 |  | -	def _parse_progress_line(self, line): | 
| 53 |  | -		"""Parse progress information from the given line as retrieved by git-push | 
| 54 |  | -		or git-fetch | 
| 55 |  | -		 | 
| 56 |  | -		:return: list(line, ...) list of lines that could not be processed""" | 
| 57 |  | -		# handle | 
| 58 |  | -		# Counting objects: 4, done.  | 
| 59 |  | -		# Compressing objects:	50% (1/2)	\rCompressing objects: 100% (2/2)	\rCompressing objects: 100% (2/2), done. | 
| 60 |  | -		self._cur_line = line | 
| 61 |  | -		sub_lines = line.split('\r') | 
| 62 |  | -		failed_lines = list() | 
| 63 |  | -		for sline in sub_lines: | 
| 64 |  | -			# find esacpe characters and cut them away - regex will not work with  | 
| 65 |  | -			# them as they are non-ascii. As git might expect a tty, it will send them | 
| 66 |  | -			last_valid_index = None | 
| 67 |  | -			for i,c in enumerate(reversed(sline)): | 
| 68 |  | -				if ord(c) < 32: | 
| 69 |  | -					# its a slice index | 
| 70 |  | -					last_valid_index = -i-1  | 
| 71 |  | -				# END character was non-ascii | 
| 72 |  | -			# END for each character in sline | 
| 73 |  | -			if last_valid_index is not None: | 
| 74 |  | -				sline = sline[:last_valid_index] | 
| 75 |  | -			# END cut away invalid part | 
| 76 |  | -			sline = sline.rstrip() | 
| 77 |  | -			 | 
| 78 |  | -			cur_count, max_count = None, None | 
| 79 |  | -			match = self.re_op_relative.match(sline) | 
| 80 |  | -			if match is None: | 
| 81 |  | -				match = self.re_op_absolute.match(sline) | 
| 82 |  | -				 | 
| 83 |  | -			if not match: | 
| 84 |  | -				self.line_dropped(sline) | 
| 85 |  | -				failed_lines.append(sline) | 
| 86 |  | -				continue | 
| 87 |  | -			# END could not get match | 
| 88 |  | -			 | 
| 89 |  | -			op_code = 0 | 
| 90 |  | -			remote, op_name, percent, cur_count, max_count, message = match.groups() | 
| 91 |  | -			 | 
| 92 |  | -			# get operation id | 
| 93 |  | -			if op_name == "Counting objects": | 
| 94 |  | -				op_code |= self.COUNTING | 
| 95 |  | -			elif op_name == "Compressing objects": | 
| 96 |  | -				op_code |= self.COMPRESSING | 
| 97 |  | -			elif op_name == "Writing objects": | 
| 98 |  | -				op_code |= self.WRITING | 
| 99 |  | -			else: | 
| 100 |  | -				raise ValueError("Operation name %r unknown" % op_name) | 
| 101 |  | -			 | 
| 102 |  | -			# figure out stage | 
| 103 |  | -			if op_code not in self._seen_ops: | 
| 104 |  | -				self._seen_ops.append(op_code) | 
| 105 |  | -				op_code |= self.BEGIN | 
| 106 |  | -			# END begin opcode | 
| 107 |  | -			 | 
| 108 |  | -			if message is None: | 
| 109 |  | -				message = '' | 
| 110 |  | -			# END message handling | 
| 111 |  | -			 | 
| 112 |  | -			message = message.strip() | 
| 113 |  | -			done_token = ', done.' | 
| 114 |  | -			if message.endswith(done_token): | 
| 115 |  | -				op_code |= self.END | 
| 116 |  | -				message = message[:-len(done_token)] | 
| 117 |  | -			# END end message handling | 
| 118 |  | -			 | 
| 119 |  | -			self.update(op_code, cur_count, max_count, message) | 
| 120 |  | -		# END for each sub line | 
| 121 |  | -		return failed_lines | 
| 122 |  | -	 | 
| 123 |  | -	def line_dropped(self, line): | 
| 124 |  | -		"""Called whenever a line could not be understood and was therefore dropped.""" | 
| 125 |  | -		pass | 
| 126 |  | -	 | 
| 127 |  | -	def update(self, op_code, cur_count, max_count=None, message=''): | 
| 128 |  | -		"""Called whenever the progress changes | 
| 129 |  | -		 | 
| 130 |  | -		:param op_code: | 
| 131 |  | -			Integer allowing to be compared against Operation IDs and stage IDs. | 
| 132 |  | -			 | 
| 133 |  | -			Stage IDs are BEGIN and END. BEGIN will only be set once for each Operation  | 
| 134 |  | -			ID as well as END. It may be that BEGIN and END are set at once in case only | 
| 135 |  | -			one progress message was emitted due to the speed of the operation. | 
| 136 |  | -			Between BEGIN and END, none of these flags will be set | 
| 137 |  | -			 | 
| 138 |  | -			Operation IDs are all held within the OP_MASK. Only one Operation ID will  | 
| 139 |  | -			be active per call. | 
| 140 |  | -		:param cur_count: Current absolute count of items | 
| 141 |  | -			 | 
| 142 |  | -		:param max_count: | 
| 143 |  | -			The maximum count of items we expect. It may be None in case there is  | 
| 144 |  | -			no maximum number of items or if it is (yet) unknown. | 
| 145 |  | -		 | 
| 146 |  | -		:param message: | 
| 147 |  | -			In case of the 'WRITING' operation, it contains the amount of bytes | 
| 148 |  | -			transferred. It may possibly be used for other purposes as well. | 
| 149 |  | -		 | 
| 150 |  | -		You may read the contents of the current line in self._cur_line""" | 
| 151 |  | -		pass | 
| 152 |  | -		 | 
| 153 | 36 | 
 | 
| 154 | 37 | class PushInfo(object): | 
| 155 | 38 | 	""" | 
|  | 
0 commit comments