Kiln » Dependencies » Dulwich Read More
Clone URL:  
Pushed to one repository · View In Graph Contained in master

Fix syntax errors in docstrings.

Changeset 2bcbc61672e5

Parent 9039abf82c4a

by Jelmer Vernooij

Changes to 2 files · Browse files at 2bcbc61672e5 Showing diff from parent 9039abf82c4a Diff from another changeset...

 
228
229
230
231
 
 
232
233
234
 
228
229
230
 
231
232
233
234
235
@@ -228,7 +228,8 @@
  :param tree_id: The SHA of the merge tree.   :param rename_detector: RenameDetector object for detecting renames.   - :yield: Lists of TreeChange objects, one per conflicted path in the merge. + :return: Iterator over lists of TreeChange objects, one per conflicted path + in the merge.     Each list contains one element per parent, with the TreeChange for that   path relative to that parent. An element may be None if it never existed
Change 1 of 1 Show Changes Only dulwich/​walk.py Stacked
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
 
 
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
 
 
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
 # walk.py -- General implementation of walking commits and their contents.  # Copyright (C) 2010 Google, Inc.  #  # This program is free software; you can redistribute it and/or  # modify it under the terms of the GNU General Public License  # as published by the Free Software Foundation; version 2  # or (at your option) any later version of the License.  #  # This program is distributed in the hope that it will be useful,  # but WITHOUT ANY WARRANTY; without even the implied warranty of  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the  # GNU General Public License for more details.  #  # You should have received a copy of the GNU General Public License  # along with this program; if not, write to the Free Software  # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,  # MA 02110-1301, USA.    """General implementation of walking commits and their contents."""      try:   from collections import defaultdict  except ImportError:   from _compat import defaultdict    import collections  import heapq  import itertools    from dulwich._compat import (   all,   )  from dulwich.diff_tree import (   RENAME_CHANGE_TYPES,   tree_changes,   tree_changes_for_merge,   RenameDetector,   )  from dulwich.errors import (   MissingCommitError,   )    ORDER_DATE = 'date'  ORDER_TOPO = 'topo'    ALL_ORDERS = (ORDER_DATE, ORDER_TOPO)    # Maximum number of commits to walk past a commit time boundary.  _MAX_EXTRA_COMMITS = 5      class WalkEntry(object):   """Object encapsulating a single result from a walk."""     def __init__(self, walker, commit):   self.commit = commit   self._store = walker.store   self._changes = None   self._rename_detector = walker.rename_detector     def changes(self):   """Get the tree changes for this entry.     :return: For commits with up to one parent, a list of TreeChange   objects; if the commit has no parents, these will be relative to the   empty tree. For merge commits, a list of lists of TreeChange   objects; see dulwich.diff.tree_changes_for_merge.   """   if self._changes is None:   commit = self.commit   if not commit.parents:   changes_func = tree_changes   parent = None   elif len(commit.parents) == 1:   changes_func = tree_changes   parent = self._store[commit.parents[0]].tree   else:   changes_func = tree_changes_for_merge   parent = [self._store[p].tree for p in commit.parents]   self._changes = list(changes_func(   self._store, parent, commit.tree,   rename_detector=self._rename_detector))   return self._changes     def __repr__(self):   return '<WalkEntry commit=%s, changes=%r>' % (   self.commit.id, self.changes())      class _CommitTimeQueue(object):   """Priority queue of WalkEntry objects by commit time."""     def __init__(self, walker):   self._walker = walker   self._store = walker.store   self._excluded = walker.excluded   self._pq = []   self._pq_set = set()   self._seen = set()   self._done = set()   self._min_time = walker.since   self._last = None   self._extra_commits_left = _MAX_EXTRA_COMMITS   self._is_finished = False     for commit_id in itertools.chain(walker.include, walker.excluded):   self._push(commit_id)     def _push(self, commit_id):   try:   commit = self._store[commit_id]   except KeyError:   raise MissingCommitError(commit_id)   if commit_id not in self._pq_set and commit_id not in self._done:   heapq.heappush(self._pq, (-commit.commit_time, commit))   self._pq_set.add(commit_id)   self._seen.add(commit_id)     def _exclude_parents(self, commit):   excluded = self._excluded   seen = self._seen   todo = [commit]   while todo:   commit = todo.pop()   for parent in commit.parents:   if parent not in excluded and parent in seen:   # TODO: This is inefficient unless the object store does   # some caching (which DiskObjectStore currently does not).   # We could either add caching in this class or pass around   # parsed queue entry objects instead of commits.   todo.append(self._store[parent])   excluded.add(parent)     def next(self):   if self._is_finished:   return None   while self._pq:   _, commit = heapq.heappop(self._pq)   sha = commit.id   self._pq_set.remove(sha)   if sha in self._done:   continue   self._done.add(commit.id)     for parent_id in commit.parents:   self._push(parent_id)     reset_extra_commits = True   is_excluded = sha in self._excluded   if is_excluded:   self._exclude_parents(commit)   if self._pq and all(c.id in self._excluded   for _, c in self._pq):   _, n = self._pq[0]   if self._last and n.commit_time >= self._last.commit_time:   # If the next commit is newer than the last one, we need   # to keep walking in case its parents (which we may not   # have seen yet) are excluded. This gives the excluded   # set a chance to "catch up" while the commit is still   # in the Walker's output queue.   reset_extra_commits = True   else:   reset_extra_commits = False     if (self._min_time is not None and   commit.commit_time < self._min_time):   # We want to stop walking at min_time, but commits at the   # boundary may be out of order with respect to their parents. So   # we walk _MAX_EXTRA_COMMITS more commits once we hit this   # boundary.   reset_extra_commits = False     if reset_extra_commits:   # We're not at a boundary, so reset the counter.   self._extra_commits_left = _MAX_EXTRA_COMMITS   else:   self._extra_commits_left -= 1   if not self._extra_commits_left:   break     if not is_excluded:   self._last = commit   return WalkEntry(self._walker, commit)   self._is_finished = True   return None      class Walker(object):   """Object for performing a walk of commits in a store.     Walker objects are initialized with a store and other options and can then   be treated as iterators of Commit objects.   """     def __init__(self, store, include, exclude=None, order=ORDER_DATE,   reverse=False, max_entries=None, paths=None,   rename_detector=None, follow=False, since=None, until=None,   queue_cls=_CommitTimeQueue):   """Constructor.     :param store: ObjectStore instance for looking up objects.   :param include: Iterable of SHAs of commits to include along with their   ancestors.   :param exclude: Iterable of SHAs of commits to exclude along with their   ancestors, overriding includes.   :param order: ORDER_* constant specifying the order of results. Anything   other than ORDER_DATE may result in O(n) memory usage.   :param reverse: If True, reverse the order of output, requiring O(n)   memory.   :param max_entries: The maximum number of entries to yield, or None for   no limit.   :param paths: Iterable of file or subtree paths to show entries for.   :param rename_detector: diff.RenameDetector object for detecting   renames.   :param follow: If True, follow path across renames/copies. Forces a   default rename_detector.   :param since: Timestamp to list commits after.   :param until: Timestamp to list commits before.   :param queue_cls: A class to use for a queue of commits, supporting the   iterator protocol. The constructor takes a single argument, the   Walker.   """   # Note: when adding arguments to this method, please also update   # dulwich.repo.BaseRepo.get_walker   if order not in ALL_ORDERS:   raise ValueError('Unknown walk order %s' % order)   self.store = store   self.include = include   self.excluded = set(exclude or [])   self.order = order   self.reverse = reverse   self.max_entries = max_entries   self.paths = paths and set(paths) or None   if follow and not rename_detector:   rename_detector = RenameDetector(store)   self.rename_detector = rename_detector   self.follow = follow   self.since = since   self.until = until     self._num_entries = 0   self._queue = queue_cls(self)   self._out_queue = collections.deque()     def _path_matches(self, changed_path):   if changed_path is None:   return False   for followed_path in self.paths:   if changed_path == followed_path:   return True   if (changed_path.startswith(followed_path) and   changed_path[len(followed_path)] == '/'):   return True   return False     def _change_matches(self, change):   if not change:   return False     old_path = change.old.path   new_path = change.new.path   if self._path_matches(new_path):   if self.follow and change.type in RENAME_CHANGE_TYPES:   self.paths.add(old_path)   self.paths.remove(new_path)   return True   elif self._path_matches(old_path):   return True   return False     def _should_return(self, entry):   """Determine if a walk entry should be returned..     :param entry: The WalkEntry to consider.   :return: True if the WalkEntry should be returned by this walk, or False   otherwise (e.g. if it doesn't match any requested paths).   """   commit = entry.commit   if self.since is not None and commit.commit_time < self.since:   return False   if self.until is not None and commit.commit_time > self.until:   return False   if commit.id in self.excluded:   return False     if self.paths is None:   return True     if len(commit.parents) > 1:   for path_changes in entry.changes():   # For merge commits, only include changes with conflicts for   # this path. Since a rename conflict may include different   # old.paths, we have to check all of them.   for change in path_changes:   if self._change_matches(change):   return True   else:   for change in entry.changes():   if self._change_matches(change):   return True   return None     def _next(self):   max_entries = self.max_entries   while max_entries is None or self._num_entries < max_entries:   entry = self._queue.next()   if entry is not None:   self._out_queue.append(entry)   if entry is None or len(self._out_queue) > _MAX_EXTRA_COMMITS:   if not self._out_queue:   return None   entry = self._out_queue.popleft()   if self._should_return(entry):   self._num_entries += 1   return entry   return None     def _reorder(self, results):   """Possibly reorder a results iterator.     :param results: An iterator of WalkEntry objects, in the order returned   from the queue_cls.   :return: An iterator or list of WalkEntry objects, in the order required   by the Walker.   """   if self.order == ORDER_TOPO:   results = _topo_reorder(results)   if self.reverse:   results = reversed(list(results))   return results     def __iter__(self):   return iter(self._reorder(iter(self._next, None)))      def _topo_reorder(entries):   """Reorder an iterable of entries topologically.     This works best assuming the entries are already in almost-topological   order, e.g. in commit time order.     :param entries: An iterable of WalkEntry objects. - :yield: WalkEntry objects from entries in FIFO order, except where a parent - would be yielded before any of its children. + :return: iterator over WalkEntry objects from entries in FIFO order, except + where a parent would be yielded before any of its children.   """   todo = collections.deque()   pending = {}   num_children = defaultdict(int)   for entry in entries:   todo.append(entry)   for p in entry.commit.parents:   num_children[p] += 1     while todo:   entry = todo.popleft()   commit = entry.commit   commit_id = commit.id   if num_children[commit_id]:   pending[commit_id] = entry   continue   for parent_id in commit.parents:   num_children[parent_id] -= 1   if not num_children[parent_id]:   parent_entry = pending.pop(parent_id, None)   if parent_entry:   todo.appendleft(parent_entry)   yield entry