90 def stripdesc(desc): |
89 def stripdesc(desc): |
91 """strip trailing whitespace and leading and trailing empty lines""" |
90 """strip trailing whitespace and leading and trailing empty lines""" |
92 return b'\n'.join([l.rstrip() for l in desc.splitlines()]).strip(b'\n') |
91 return b'\n'.join([l.rstrip() for l in desc.splitlines()]).strip(b'\n') |
93 |
92 |
94 |
93 |
95 class _divertopener: |
|
96 def __init__(self, opener, target): |
|
97 self._opener = opener |
|
98 self._target = target |
|
99 |
|
100 def __call__(self, name, mode=b'r', checkambig=False, **kwargs): |
|
101 if name != self._target: |
|
102 return self._opener(name, mode, **kwargs) |
|
103 return self._opener(name + b".a", mode, **kwargs) |
|
104 |
|
105 def __getattr__(self, attr): |
|
106 return getattr(self._opener, attr) |
|
107 |
|
108 |
|
109 class _delayopener: |
|
110 """build an opener that stores chunks in 'buf' instead of 'target'""" |
|
111 |
|
112 def __init__(self, opener, target, buf): |
|
113 self._opener = opener |
|
114 self._target = target |
|
115 self._buf = buf |
|
116 |
|
117 def __call__(self, name, mode=b'r', checkambig=False, **kwargs): |
|
118 if name != self._target: |
|
119 return self._opener(name, mode, **kwargs) |
|
120 assert not kwargs |
|
121 return randomaccessfile.appender(self._opener, name, mode, self._buf) |
|
122 |
|
123 def __getattr__(self, attr): |
|
124 return getattr(self._opener, attr) |
|
125 |
|
126 |
|
127 @attr.s |
94 @attr.s |
128 class _changelogrevision: |
95 class _changelogrevision: |
129 # Extensions might modify _defaultextra, so let the constructor below pass |
96 # Extensions might modify _defaultextra, so let the constructor below pass |
130 # it in |
97 # it in |
131 extra = attr.ib() |
98 extra = attr.ib() |
372 assert isinstance(val, frozenset) |
336 assert isinstance(val, frozenset) |
373 self._filteredrevs = val |
337 self._filteredrevs = val |
374 self._filteredrevs_hashcache = {} |
338 self._filteredrevs_hashcache = {} |
375 |
339 |
376 def _write_docket(self, tr): |
340 def _write_docket(self, tr): |
377 if not self.is_delaying: |
341 if not self._v2_delayed: |
378 super(changelog, self)._write_docket(tr) |
342 super(changelog, self)._write_docket(tr) |
379 |
|
380 @property |
|
381 def is_delaying(self): |
|
382 return self._delayed |
|
383 |
343 |
384 def delayupdate(self, tr): |
344 def delayupdate(self, tr): |
385 """delay visibility of index updates to other readers""" |
345 """delay visibility of index updates to other readers""" |
386 assert not self._inner.is_open |
346 assert not self._inner.is_open |
387 if self._docket is None and not self.is_delaying: |
347 if self._docket is not None: |
388 if len(self) == 0: |
348 self._v2_delayed = True |
389 self._divert = True |
349 else: |
390 if self._realopener.exists(self._indexfile + b'.a'): |
350 new_index = self._inner.delay() |
391 self._realopener.unlink(self._indexfile + b'.a') |
351 if new_index is not None: |
392 self.opener = _divertopener(self._realopener, self._indexfile) |
352 self._indexfile = new_index |
393 else: |
353 tr.registertmp(new_index) |
394 self._delaybuf = [] |
|
395 self.opener = _delayopener( |
|
396 self._realopener, self._indexfile, self._delaybuf |
|
397 ) |
|
398 self._inner.opener = self.opener |
|
399 self._inner._segmentfile.opener = self.opener |
|
400 self._inner._segmentfile_sidedata.opener = self.opener |
|
401 self._delayed = True |
|
402 tr.addpending(b'cl-%i' % id(self), self._writepending) |
354 tr.addpending(b'cl-%i' % id(self), self._writepending) |
403 tr.addfinalize(b'cl-%i' % id(self), self._finalize) |
355 tr.addfinalize(b'cl-%i' % id(self), self._finalize) |
404 |
356 |
405 def _finalize(self, tr): |
357 def _finalize(self, tr): |
406 """finalize index updates""" |
358 """finalize index updates""" |
407 assert not self._inner.is_open |
359 assert not self._inner.is_open |
408 self._delayed = False |
|
409 self.opener = self._realopener |
|
410 self._inner.opener = self.opener |
|
411 self._inner._segmentfile.opener = self.opener |
|
412 self._inner._segmentfile_sidedata.opener = self.opener |
|
413 # move redirected index data back into place |
|
414 if self._docket is not None: |
360 if self._docket is not None: |
415 self._write_docket(tr) |
361 self._docket.write(tr) |
416 elif self._divert: |
362 self._v2_delayed = False |
417 assert not self._delaybuf |
363 else: |
418 tmpname = self._indexfile + b".a" |
364 new_index_file = self._inner.finalize_pending() |
419 nfile = self.opener.open(tmpname) |
365 self._indexfile = new_index_file |
420 nfile.close() |
366 # split when we're done |
421 self.opener.rename(tmpname, self._indexfile, checkambig=True) |
367 self._enforceinlinesize(tr, side_write=False) |
422 elif self._delaybuf: |
|
423 fp = self.opener(self._indexfile, b'a', checkambig=True) |
|
424 fp.write(b"".join(self._delaybuf)) |
|
425 fp.close() |
|
426 self._delaybuf = None |
|
427 self._divert = False |
|
428 # split when we're done |
|
429 self._enforceinlinesize(tr, side_write=False) |
|
430 |
368 |
431 def _writepending(self, tr): |
369 def _writepending(self, tr): |
432 """create a file containing the unfinalized state for |
370 """create a file containing the unfinalized state for |
433 pretxnchangegroup""" |
371 pretxnchangegroup""" |
434 assert not self._inner.is_open |
372 assert not self._inner.is_open |
435 if self._docket: |
373 if self._docket: |
436 return self._docket.write(tr, pending=True) |
374 any_pending = self._docket.write(tr, pending=True) |
437 if self._delaybuf: |
375 self._v2_delayed = False |
438 # make a temporary copy of the index |
376 else: |
439 fp1 = self._realopener(self._indexfile) |
377 new_index, any_pending = self._inner.write_pending() |
440 pendingfilename = self._indexfile + b".a" |
378 if new_index is not None: |
441 # register as a temp file to ensure cleanup on failure |
379 self._indexfile = new_index |
442 tr.registertmp(pendingfilename) |
380 tr.registertmp(new_index) |
443 # write existing data |
381 return any_pending |
444 fp2 = self._realopener(pendingfilename, b"w") |
|
445 fp2.write(fp1.read()) |
|
446 # add pending data |
|
447 fp2.write(b"".join(self._delaybuf)) |
|
448 fp2.close() |
|
449 # switch modes so finalize can simply rename |
|
450 self._delaybuf = None |
|
451 self._divert = True |
|
452 self.opener = _divertopener(self._realopener, self._indexfile) |
|
453 self._inner.opener = self.opener |
|
454 self._inner._segmentfile.opener = self.opener |
|
455 self._inner._segmentfile_sidedata.opener = self.opener |
|
456 |
|
457 if self._divert: |
|
458 return True |
|
459 |
|
460 return False |
|
461 |
382 |
462 def _enforceinlinesize(self, tr, side_write=True): |
383 def _enforceinlinesize(self, tr, side_write=True): |
463 if not self.is_delaying: |
384 if not self.is_delaying: |
464 revlog.revlog._enforceinlinesize(self, tr, side_write=side_write) |
385 revlog.revlog._enforceinlinesize(self, tr, side_write=side_write) |
465 |
386 |