comparison mercurial/transaction.py @ 43077:687b865b95ad

formatting: byteify all mercurial/ and hgext/ string literals Done with python3.7 contrib/byteify-strings.py -i $(hg files 'set:mercurial/**.py - mercurial/thirdparty/** + hgext/**.py - hgext/fsmonitor/pywatchman/** - mercurial/__init__.py') black -l 80 -t py33 -S $(hg files 'set:**.py - mercurial/thirdparty/** - "contrib/python-zstandard/**" - hgext/fsmonitor/pywatchman/**') # skip-blame mass-reformatting only Differential Revision: https://phab.mercurial-scm.org/D6972
author Augie Fackler <augie@google.com>
date Sun, 06 Oct 2019 09:48:39 -0400
parents 2372284d9457
children d783f945a701
comparison
equal deleted inserted replaced
43076:2372284d9457 43077:687b865b95ad
26 version = 2 26 version = 2
27 27
28 # These are the file generators that should only be executed after the 28 # These are the file generators that should only be executed after the
29 # finalizers are done, since they rely on the output of the finalizers (like 29 # finalizers are done, since they rely on the output of the finalizers (like
30 # the changelog having been written). 30 # the changelog having been written).
31 postfinalizegenerators = {'bookmarks', 'dirstate'} 31 postfinalizegenerators = {b'bookmarks', b'dirstate'}
32 32
33 gengroupall = 'all' 33 gengroupall = b'all'
34 gengroupprefinalize = 'prefinalize' 34 gengroupprefinalize = b'prefinalize'
35 gengrouppostfinalize = 'postfinalize' 35 gengrouppostfinalize = b'postfinalize'
36 36
37 37
38 def active(func): 38 def active(func):
39 def _active(self, *args, **kwds): 39 def _active(self, *args, **kwds):
40 if self._count == 0: 40 if self._count == 0:
41 raise error.Abort( 41 raise error.Abort(
42 _('cannot use transaction when it is already committed/aborted') 42 _(
43 b'cannot use transaction when it is already committed/aborted'
44 )
43 ) 45 )
44 return func(self, *args, **kwds) 46 return func(self, *args, **kwds)
45 47
46 return _active 48 return _active
47 49
56 unlink=True, 58 unlink=True,
57 checkambigfiles=None, 59 checkambigfiles=None,
58 ): 60 ):
59 for f, o, _ignore in entries: 61 for f, o, _ignore in entries:
60 if o or not unlink: 62 if o or not unlink:
61 checkambig = checkambigfiles and (f, '') in checkambigfiles 63 checkambig = checkambigfiles and (f, b'') in checkambigfiles
62 try: 64 try:
63 fp = opener(f, 'a', checkambig=checkambig) 65 fp = opener(f, b'a', checkambig=checkambig)
64 if fp.tell() < o: 66 if fp.tell() < o:
65 raise error.Abort( 67 raise error.Abort(
66 _( 68 _(
67 "attempted to truncate %s to %d bytes, but it was " 69 b"attempted to truncate %s to %d bytes, but it was "
68 "already %d bytes\n" 70 b"already %d bytes\n"
69 ) 71 )
70 % (f, o, fp.tell()) 72 % (f, o, fp.tell())
71 ) 73 )
72 fp.truncate(o) 74 fp.truncate(o)
73 fp.close() 75 fp.close()
74 except IOError: 76 except IOError:
75 report(_("failed to truncate %s\n") % f) 77 report(_(b"failed to truncate %s\n") % f)
76 raise 78 raise
77 else: 79 else:
78 try: 80 try:
79 opener.unlink(f) 81 opener.unlink(f)
80 except (IOError, OSError) as inst: 82 except (IOError, OSError) as inst:
82 raise 84 raise
83 85
84 backupfiles = [] 86 backupfiles = []
85 for l, f, b, c in backupentries: 87 for l, f, b, c in backupentries:
86 if l not in vfsmap and c: 88 if l not in vfsmap and c:
87 report("couldn't handle %s: unknown cache location %s\n" % (b, l)) 89 report(b"couldn't handle %s: unknown cache location %s\n" % (b, l))
88 vfs = vfsmap[l] 90 vfs = vfsmap[l]
89 try: 91 try:
90 if f and b: 92 if f and b:
91 filepath = vfs.join(f) 93 filepath = vfs.join(f)
92 backuppath = vfs.join(b) 94 backuppath = vfs.join(b)
93 checkambig = checkambigfiles and (f, l) in checkambigfiles 95 checkambig = checkambigfiles and (f, l) in checkambigfiles
94 try: 96 try:
95 util.copyfile(backuppath, filepath, checkambig=checkambig) 97 util.copyfile(backuppath, filepath, checkambig=checkambig)
96 backupfiles.append(b) 98 backupfiles.append(b)
97 except IOError: 99 except IOError:
98 report(_("failed to recover %s\n") % f) 100 report(_(b"failed to recover %s\n") % f)
99 else: 101 else:
100 target = f or b 102 target = f or b
101 try: 103 try:
102 vfs.unlink(target) 104 vfs.unlink(target)
103 except (IOError, OSError) as inst: 105 except (IOError, OSError) as inst:
105 raise 107 raise
106 except (IOError, OSError, error.Abort): 108 except (IOError, OSError, error.Abort):
107 if not c: 109 if not c:
108 raise 110 raise
109 111
110 backuppath = "%s.backupfiles" % journal 112 backuppath = b"%s.backupfiles" % journal
111 if opener.exists(backuppath): 113 if opener.exists(backuppath):
112 opener.unlink(backuppath) 114 opener.unlink(backuppath)
113 opener.unlink(journal) 115 opener.unlink(journal)
114 try: 116 try:
115 for f in backupfiles: 117 for f in backupfiles:
153 self._report = report 155 self._report = report
154 # a vfs to the store content 156 # a vfs to the store content
155 self._opener = opener 157 self._opener = opener
156 # a map to access file in various {location -> vfs} 158 # a map to access file in various {location -> vfs}
157 vfsmap = vfsmap.copy() 159 vfsmap = vfsmap.copy()
158 vfsmap[''] = opener # set default value 160 vfsmap[b''] = opener # set default value
159 self._vfsmap = vfsmap 161 self._vfsmap = vfsmap
160 self._after = after 162 self._after = after
161 self._entries = [] 163 self._entries = []
162 self._map = {} 164 self._map = {}
163 self._journal = journalname 165 self._journal = journalname
184 # transaction. 186 # transaction.
185 self.changes = {} 187 self.changes = {}
186 188
187 # a dict of arguments to be passed to hooks 189 # a dict of arguments to be passed to hooks
188 self.hookargs = {} 190 self.hookargs = {}
189 self._file = opener.open(self._journal, "w") 191 self._file = opener.open(self._journal, b"w")
190 192
191 # a list of ('location', 'path', 'backuppath', cache) entries. 193 # a list of ('location', 'path', 'backuppath', cache) entries.
192 # - if 'backuppath' is empty, no file existed at backup time 194 # - if 'backuppath' is empty, no file existed at backup time
193 # - if 'path' is empty, this is a temporary transaction file 195 # - if 'path' is empty, this is a temporary transaction file
194 # - if 'location' is not empty, the path is outside main opener reach. 196 # - if 'location' is not empty, the path is outside main opener reach.
195 # use 'location' value as a key in a vfsmap to find the right 'vfs' 197 # use 'location' value as a key in a vfsmap to find the right 'vfs'
196 # (cache is currently unused) 198 # (cache is currently unused)
197 self._backupentries = [] 199 self._backupentries = []
198 self._backupmap = {} 200 self._backupmap = {}
199 self._backupjournal = "%s.backupfiles" % self._journal 201 self._backupjournal = b"%s.backupfiles" % self._journal
200 self._backupsfile = opener.open(self._backupjournal, 'w') 202 self._backupsfile = opener.open(self._backupjournal, b'w')
201 self._backupsfile.write('%d\n' % version) 203 self._backupsfile.write(b'%d\n' % version)
202 204
203 if createmode is not None: 205 if createmode is not None:
204 opener.chmod(self._journal, createmode & 0o666) 206 opener.chmod(self._journal, createmode & 0o666)
205 opener.chmod(self._backupjournal, createmode & 0o666) 207 opener.chmod(self._backupjournal, createmode & 0o666)
206 208
263 if file in self._map or file in self._backupmap: 265 if file in self._map or file in self._backupmap:
264 return 266 return
265 self._entries.append((file, offset, data)) 267 self._entries.append((file, offset, data))
266 self._map[file] = len(self._entries) - 1 268 self._map[file] = len(self._entries) - 1
267 # add enough data to the journal to do the truncate 269 # add enough data to the journal to do the truncate
268 self._file.write("%s\0%d\n" % (file, offset)) 270 self._file.write(b"%s\0%d\n" % (file, offset))
269 self._file.flush() 271 self._file.flush()
270 272
271 @active 273 @active
272 def addbackup(self, file, hardlink=True, location=''): 274 def addbackup(self, file, hardlink=True, location=b''):
273 """Adds a backup of the file to the transaction 275 """Adds a backup of the file to the transaction
274 276
275 Calling addbackup() creates a hardlink backup of the specified file 277 Calling addbackup() creates a hardlink backup of the specified file
276 that is used to recover the file in the event of the transaction 278 that is used to recover the file in the event of the transaction
277 aborting. 279 aborting.
278 280
279 * `file`: the file path, relative to .hg/store 281 * `file`: the file path, relative to .hg/store
280 * `hardlink`: use a hardlink to quickly create the backup 282 * `hardlink`: use a hardlink to quickly create the backup
281 """ 283 """
282 if self._queue: 284 if self._queue:
283 msg = 'cannot use transaction.addbackup inside "group"' 285 msg = b'cannot use transaction.addbackup inside "group"'
284 raise error.ProgrammingError(msg) 286 raise error.ProgrammingError(msg)
285 287
286 if file in self._map or file in self._backupmap: 288 if file in self._map or file in self._backupmap:
287 return 289 return
288 vfs = self._vfsmap[location] 290 vfs = self._vfsmap[location]
289 dirname, filename = vfs.split(file) 291 dirname, filename = vfs.split(file)
290 backupfilename = "%s.backup.%s" % (self._journal, filename) 292 backupfilename = b"%s.backup.%s" % (self._journal, filename)
291 backupfile = vfs.reljoin(dirname, backupfilename) 293 backupfile = vfs.reljoin(dirname, backupfilename)
292 if vfs.exists(file): 294 if vfs.exists(file):
293 filepath = vfs.join(file) 295 filepath = vfs.join(file)
294 backuppath = vfs.join(backupfile) 296 backuppath = vfs.join(backupfile)
295 util.copyfile(filepath, backuppath, hardlink=hardlink) 297 util.copyfile(filepath, backuppath, hardlink=hardlink)
296 else: 298 else:
297 backupfile = '' 299 backupfile = b''
298 300
299 self._addbackupentry((location, file, backupfile, False)) 301 self._addbackupentry((location, file, backupfile, False))
300 302
301 def _addbackupentry(self, entry): 303 def _addbackupentry(self, entry):
302 """register a new backup entry and write it to disk""" 304 """register a new backup entry and write it to disk"""
303 self._backupentries.append(entry) 305 self._backupentries.append(entry)
304 self._backupmap[entry[1]] = len(self._backupentries) - 1 306 self._backupmap[entry[1]] = len(self._backupentries) - 1
305 self._backupsfile.write("%s\0%s\0%s\0%d\n" % entry) 307 self._backupsfile.write(b"%s\0%s\0%s\0%d\n" % entry)
306 self._backupsfile.flush() 308 self._backupsfile.flush()
307 309
308 @active 310 @active
309 def registertmp(self, tmpfile, location=''): 311 def registertmp(self, tmpfile, location=b''):
310 """register a temporary transaction file 312 """register a temporary transaction file
311 313
312 Such files will be deleted when the transaction exits (on both 314 Such files will be deleted when the transaction exits (on both
313 failure and success). 315 failure and success).
314 """ 316 """
315 self._addbackupentry((location, '', tmpfile, False)) 317 self._addbackupentry((location, b'', tmpfile, False))
316 318
317 @active 319 @active
318 def addfilegenerator(self, genid, filenames, genfunc, order=0, location=''): 320 def addfilegenerator(
321 self, genid, filenames, genfunc, order=0, location=b''
322 ):
319 """add a function to generates some files at transaction commit 323 """add a function to generates some files at transaction commit
320 324
321 The `genfunc` argument is a function capable of generating proper 325 The `genfunc` argument is a function capable of generating proper
322 content of each entry in the `filename` tuple. 326 content of each entry in the `filename` tuple.
323 327
346 def removefilegenerator(self, genid): 350 def removefilegenerator(self, genid):
347 """reverse of addfilegenerator, remove a file generator function""" 351 """reverse of addfilegenerator, remove a file generator function"""
348 if genid in self._filegenerators: 352 if genid in self._filegenerators:
349 del self._filegenerators[genid] 353 del self._filegenerators[genid]
350 354
351 def _generatefiles(self, suffix='', group=gengroupall): 355 def _generatefiles(self, suffix=b'', group=gengroupall):
352 # write files registered for generation 356 # write files registered for generation
353 any = False 357 any = False
354 for id, entry in sorted(self._filegenerators.iteritems()): 358 for id, entry in sorted(self._filegenerators.iteritems()):
355 any = True 359 any = True
356 order, filenames, genfunc, location = entry 360 order, filenames, genfunc, location = entry
373 checkambig = False 377 checkambig = False
374 else: 378 else:
375 self.addbackup(name, location=location) 379 self.addbackup(name, location=location)
376 checkambig = (name, location) in self._checkambigfiles 380 checkambig = (name, location) in self._checkambigfiles
377 files.append( 381 files.append(
378 vfs(name, 'w', atomictemp=True, checkambig=checkambig) 382 vfs(name, b'w', atomictemp=True, checkambig=checkambig)
379 ) 383 )
380 genfunc(*files) 384 genfunc(*files)
381 for f in files: 385 for f in files:
382 f.close() 386 f.close()
383 # skip discard() loop since we're sure no open file remains 387 # skip discard() loop since we're sure no open file remains
404 408
405 if file not in self._map: 409 if file not in self._map:
406 raise KeyError(file) 410 raise KeyError(file)
407 index = self._map[file] 411 index = self._map[file]
408 self._entries[index] = (file, offset, data) 412 self._entries[index] = (file, offset, data)
409 self._file.write("%s\0%d\n" % (file, offset)) 413 self._file.write(b"%s\0%d\n" % (file, offset))
410 self._file.flush() 414 self._file.flush()
411 415
412 @active 416 @active
413 def nest(self, name=r'<unnamed>'): 417 def nest(self, name=r'<unnamed>'):
414 self._count += 1 418 self._count += 1
446 categories = sorted(self._pendingcallback) 450 categories = sorted(self._pendingcallback)
447 for cat in categories: 451 for cat in categories:
448 # remove callback since the data will have been flushed 452 # remove callback since the data will have been flushed
449 any = self._pendingcallback.pop(cat)(self) 453 any = self._pendingcallback.pop(cat)(self)
450 self._anypending = self._anypending or any 454 self._anypending = self._anypending or any
451 self._anypending |= self._generatefiles(suffix='.pending') 455 self._anypending |= self._generatefiles(suffix=b'.pending')
452 return self._anypending 456 return self._anypending
453 457
454 @active 458 @active
455 def addfinalize(self, category, callback): 459 def addfinalize(self, category, callback):
456 """add a callback to be called when the transaction is closed 460 """add a callback to be called when the transaction is closed
510 self._backupsfile.close() 514 self._backupsfile.close()
511 # cleanup temporary files 515 # cleanup temporary files
512 for l, f, b, c in self._backupentries: 516 for l, f, b, c in self._backupentries:
513 if l not in self._vfsmap and c: 517 if l not in self._vfsmap and c:
514 self._report( 518 self._report(
515 "couldn't remove %s: unknown cache location %s\n" % (b, l) 519 b"couldn't remove %s: unknown cache location %s\n" % (b, l)
516 ) 520 )
517 continue 521 continue
518 vfs = self._vfsmap[l] 522 vfs = self._vfsmap[l]
519 if not f and b and vfs.exists(b): 523 if not f and b and vfs.exists(b):
520 try: 524 try:
522 except (IOError, OSError, error.Abort) as inst: 526 except (IOError, OSError, error.Abort) as inst:
523 if not c: 527 if not c:
524 raise 528 raise
525 # Abort may be raise by read only opener 529 # Abort may be raise by read only opener
526 self._report( 530 self._report(
527 "couldn't remove %s: %s\n" % (vfs.join(b), inst) 531 b"couldn't remove %s: %s\n" % (vfs.join(b), inst)
528 ) 532 )
529 self._entries = [] 533 self._entries = []
530 self._writeundo() 534 self._writeundo()
531 if self._after: 535 if self._after:
532 self._after() 536 self._after()
536 if self._opener.isfile(self._journal): 540 if self._opener.isfile(self._journal):
537 self._opener.unlink(self._journal) 541 self._opener.unlink(self._journal)
538 for l, _f, b, c in self._backupentries: 542 for l, _f, b, c in self._backupentries:
539 if l not in self._vfsmap and c: 543 if l not in self._vfsmap and c:
540 self._report( 544 self._report(
541 "couldn't remove %s: unknown cache location" "%s\n" % (b, l) 545 b"couldn't remove %s: unknown cache location"
546 b"%s\n" % (b, l)
542 ) 547 )
543 continue 548 continue
544 vfs = self._vfsmap[l] 549 vfs = self._vfsmap[l]
545 if b and vfs.exists(b): 550 if b and vfs.exists(b):
546 try: 551 try:
548 except (IOError, OSError, error.Abort) as inst: 553 except (IOError, OSError, error.Abort) as inst:
549 if not c: 554 if not c:
550 raise 555 raise
551 # Abort may be raise by read only opener 556 # Abort may be raise by read only opener
552 self._report( 557 self._report(
553 "couldn't remove %s: %s\n" % (vfs.join(b), inst) 558 b"couldn't remove %s: %s\n" % (vfs.join(b), inst)
554 ) 559 )
555 self._backupentries = [] 560 self._backupentries = []
556 self._journal = None 561 self._journal = None
557 562
558 self._releasefn(self, True) # notify success of closing transaction 563 self._releasefn(self, True) # notify success of closing transaction
575 def _writeundo(self): 580 def _writeundo(self):
576 """write transaction data for possible future undo call""" 581 """write transaction data for possible future undo call"""
577 if self._undoname is None: 582 if self._undoname is None:
578 return 583 return
579 undobackupfile = self._opener.open( 584 undobackupfile = self._opener.open(
580 "%s.backupfiles" % self._undoname, 'w' 585 b"%s.backupfiles" % self._undoname, b'w'
581 ) 586 )
582 undobackupfile.write('%d\n' % version) 587 undobackupfile.write(b'%d\n' % version)
583 for l, f, b, c in self._backupentries: 588 for l, f, b, c in self._backupentries:
584 if not f: # temporary file 589 if not f: # temporary file
585 continue 590 continue
586 if not b: 591 if not b:
587 u = '' 592 u = b''
588 else: 593 else:
589 if l not in self._vfsmap and c: 594 if l not in self._vfsmap and c:
590 self._report( 595 self._report(
591 "couldn't remove %s: unknown cache location" 596 b"couldn't remove %s: unknown cache location"
592 "%s\n" % (b, l) 597 b"%s\n" % (b, l)
593 ) 598 )
594 continue 599 continue
595 vfs = self._vfsmap[l] 600 vfs = self._vfsmap[l]
596 base, name = vfs.split(b) 601 base, name = vfs.split(b)
597 assert name.startswith(self._journal), name 602 assert name.startswith(self._journal), name
598 uname = name.replace(self._journal, self._undoname, 1) 603 uname = name.replace(self._journal, self._undoname, 1)
599 u = vfs.reljoin(base, uname) 604 u = vfs.reljoin(base, uname)
600 util.copyfile(vfs.join(b), vfs.join(u), hardlink=True) 605 util.copyfile(vfs.join(b), vfs.join(u), hardlink=True)
601 undobackupfile.write("%s\0%s\0%s\0%d\n" % (l, f, u, c)) 606 undobackupfile.write(b"%s\0%s\0%s\0%d\n" % (l, f, u, c))
602 undobackupfile.close() 607 undobackupfile.close()
603 608
604 def _abort(self): 609 def _abort(self):
605 self._count = 0 610 self._count = 0
606 self._usages = 0 611 self._usages = 0
613 self._opener.unlink(self._backupjournal) 618 self._opener.unlink(self._backupjournal)
614 if self._journal: 619 if self._journal:
615 self._opener.unlink(self._journal) 620 self._opener.unlink(self._journal)
616 return 621 return
617 622
618 self._report(_("transaction abort!\n")) 623 self._report(_(b"transaction abort!\n"))
619 624
620 try: 625 try:
621 for cat in sorted(self._abortcallback): 626 for cat in sorted(self._abortcallback):
622 self._abortcallback[cat](self) 627 self._abortcallback[cat](self)
623 # Prevent double usage and help clear cycles. 628 # Prevent double usage and help clear cycles.
630 self._entries, 635 self._entries,
631 self._backupentries, 636 self._backupentries,
632 False, 637 False,
633 checkambigfiles=self._checkambigfiles, 638 checkambigfiles=self._checkambigfiles,
634 ) 639 )
635 self._report(_("rollback completed\n")) 640 self._report(_(b"rollback completed\n"))
636 except BaseException as exc: 641 except BaseException as exc:
637 self._report(_("rollback failed - please run hg recover\n")) 642 self._report(_(b"rollback failed - please run hg recover\n"))
638 self._report( 643 self._report(
639 _("(failure reason: %s)\n") % stringutil.forcebytestr(exc) 644 _(b"(failure reason: %s)\n") % stringutil.forcebytestr(exc)
640 ) 645 )
641 finally: 646 finally:
642 self._journal = None 647 self._journal = None
643 self._releasefn(self, False) # notify failure of transaction 648 self._releasefn(self, False) # notify failure of transaction
644 self._releasefn = None # Help prevent cycles. 649 self._releasefn = None # Help prevent cycles.
666 fp = opener.open(file) 671 fp = opener.open(file)
667 lines = fp.readlines() 672 lines = fp.readlines()
668 fp.close() 673 fp.close()
669 for l in lines: 674 for l in lines:
670 try: 675 try:
671 f, o = l.split('\0') 676 f, o = l.split(b'\0')
672 entries.append((f, int(o), None)) 677 entries.append((f, int(o), None))
673 except ValueError: 678 except ValueError:
674 report(_("couldn't read journal entry %r!\n") % pycompat.bytestr(l)) 679 report(
675 680 _(b"couldn't read journal entry %r!\n") % pycompat.bytestr(l)
676 backupjournal = "%s.backupfiles" % file 681 )
682
683 backupjournal = b"%s.backupfiles" % file
677 if opener.exists(backupjournal): 684 if opener.exists(backupjournal):
678 fp = opener.open(backupjournal) 685 fp = opener.open(backupjournal)
679 lines = fp.readlines() 686 lines = fp.readlines()
680 if lines: 687 if lines:
681 ver = lines[0][:-1] 688 ver = lines[0][:-1]
682 if ver == (b'%d' % version): 689 if ver == (b'%d' % version):
683 for line in lines[1:]: 690 for line in lines[1:]:
684 if line: 691 if line:
685 # Shave off the trailing newline 692 # Shave off the trailing newline
686 line = line[:-1] 693 line = line[:-1]
687 l, f, b, c = line.split('\0') 694 l, f, b, c = line.split(b'\0')
688 backupentries.append((l, f, b, bool(c))) 695 backupentries.append((l, f, b, bool(c)))
689 else: 696 else:
690 report( 697 report(
691 _( 698 _(
692 "journal was created by a different version of " 699 b"journal was created by a different version of "
693 "Mercurial\n" 700 b"Mercurial\n"
694 ) 701 )
695 ) 702 )
696 703
697 _playback( 704 _playback(
698 file, 705 file,