|
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