comparison hgext3rd/pullbundle.py @ 4138:cfdc6f55599b

pullbundle: improve slicing of the lower part of range The previous method could get confuse by merge and overslice. The new method is better at using sticking on power of two boundaries.
author Pierre-Yves David <pierre-yves.david@octobus.net>
date Tue, 25 Sep 2018 12:20:26 +0200
parents be3a94d3105f
children 2bd652bece97
comparison
equal deleted inserted replaced
4137:21a3c051ca6c 4138:cfdc6f55599b
211 nodes.reverse() 211 nodes.reverse()
212 for rangeid in subranges: 212 for rangeid in subranges:
213 size = rangelength(rangeid) 213 size = rangelength(rangeid)
214 slices.append((rangeid, nodes[idx:idx + size])) 214 slices.append((rangeid, nodes[idx:idx + size]))
215 idx += size 215 idx += size
216 ### slow code block to validate ranges content
217 # rev = repo.changelog.nodemap.get
218 # for ri, ns in slices:
219 # a = set(rev(n) for n in ns)
220 # b = set(repo.stablerange.revsfromrange(repo, ri))
221 # l = repo.stablerange.rangelength(repo, ri)
222 # repo.ui.write('range-length: %d-%d %s %s\n' % (ri[0], ri[1], l, len(a)))
223 # if a != b:
224 # d = (ri[0], ri[1], b - a, a - b)
225 # repo.ui.write("mismatching content: %d-%d -%s +%s\n" % d)
216 return slices 226 return slices
217 227
218 def canonicalsubranges(repo, stablerange, rangeid): 228 def canonicalsubranges(repo, stablerange, rangeid):
219 """slice a size of nodes into most reusable subranges 229 """slice a size of nodes into most reusable subranges
220 230
244 break 254 break
245 cursor //= 2 255 cursor //= 2
246 256
247 # 2. optimise, bottom part 257 # 2. optimise, bottom part
248 if skip != cut: 258 if skip != cut:
249 tailranges = [] 259 currentsize = tailsize = cut - skip
250 tailsize = cut - skip
251 assert 0 < tailsize, tailsize 260 assert 0 < tailsize, tailsize
261
262 # we need to take several "standard cut" in the bottom part
263 #
264 # This is similar to what we will do for the top part, we reusing the
265 # existing structure is a bit more complex.
266 allcuts = list(reversed(standardcut(tailsize)))
252 prerange = (headrev, precut) 267 prerange = (headrev, precut)
253 size = stablerange.rangelength(repo, prerange) 268 ### slow code block to check we operate on the right data
254 sub = stablerange.subranges(repo, prerange)[:-1] 269 # rev = repo.changelog.nodemap.get
255 # This power of two check is too simplistic and misbehave when too many 270 # allrevs = [rev(n) for n in nodes]
256 # merge are involved. because de merge, there can be "canonical" range 271 # allrevs.reverse()
257 # with various size. 272 # prerevs = repo.stablerange.revsfromrange(repo, prerange)
258 while not poweroftwo(tailsize): 273 # assert allrevs == prerevs[(len(prerevs) - len(allrevs)):]
259 for prerange in reversed(sub): 274 # end of check
260 if tailsize <= 0: 275 sub = list(stablerange.subranges(repo, prerange)[:-1])
261 break 276
262 277 bottomranges = []
263 assert stablerange.depthrev(repo, prerange[0]) != prerange[1], prerange 278 # XXX we might be able to reuse core stable-range logic instead of
264 tailrev, tailskip = prerange 279 # redoing this manually
265 size = stablerange.rangelength(repo, (tailrev, tailskip)) 280 currentrange = sub.pop()
266 if tailsize < size: 281 currentsize = stablerange.rangelength(repo, currentrange)
267 tailskip += size - tailsize 282 currentcut = None
268 size = tailsize 283 while allcuts or currentcut is not None:
269 tailranges.append((tailrev, tailskip)) 284 # get the next cut if needed
270 tailsize -= size 285 if currentcut is None:
271 else: 286 currentcut = allcuts.pop()
272 # size of the last block 287 # deal attemp a cut
273 tailsize = stablerange.rangelength(repo, tailranges[-1]) 288 if currentsize == currentcut:
274 if poweroftwo(tailsize): 289 bottomranges.append(currentrange)
275 continue # exit the loop 290 currentcut = None
276 prerange = tailranges.pop() 291 elif currentsize < currentcut:
277 sub = stablerange.subranges(repo, prerange) 292 bottomranges.append(currentrange)
278 293 currentcut -= currentsize
279 tailranges.reverse() 294 else: # currentsize > currentcut
280 canonicals.extend(tailranges) 295 newskip = currentrange[1] + (currentsize - currentcut)
296 currentsub = stablerange._slicesrangeat(repo, currentrange, newskip)
297 bottomranges.append(currentsub.pop())
298 sub.extend(currentsub)
299 currentcut = None
300 currentrange = sub.pop()
301 currentsize = stablerange.rangelength(repo, currentrange)
302 bottomranges.reverse()
303 canonicals.extend(bottomranges)
281 304
282 # 3. take recursive subrange until we get to a power of two size? 305 # 3. take recursive subrange until we get to a power of two size?
283 current = (headrev, cut) 306 current = (headrev, cut)
284 while not poweroftwo(stablerange.rangelength(repo, current)): 307 while not poweroftwo(stablerange.rangelength(repo, current)):
285 sub = stablerange.subranges(repo, current) 308 sub = stablerange.subranges(repo, current)
286 canonicals.extend(sub[:-1]) 309 canonicals.extend(sub[:-1])
287 current = sub[-1] 310 current = sub[-1]
288 canonicals.append(current) 311 canonicals.append(current)
289 312
290 return canonicals 313 return canonicals
314
315 def standardcut(size):
316 assert 0 < size
317 # 0. find the first power of 2 higher than this range depth
318 cut = 1
319 while cut <= size:
320 cut *= 2
321
322 allcuts = []
323 # 1. find all standard expected cut
324 while 1 < cut and size:
325 cut //= 2
326 if cut <= size:
327 allcuts.append(cut)
328 size -= cut
329 return allcuts
291 330
292 def poweroftwo(num): 331 def poweroftwo(num):
293 return num and not num & (num - 1) 332 return num and not num & (num - 1)
294 333
295 def outgoingfromnodes(repo, nodes): 334 def outgoingfromnodes(repo, nodes):