157 vfsmap = vfsmap.copy() |
157 vfsmap = vfsmap.copy() |
158 vfsmap[b''] = opener # set default value |
158 vfsmap[b''] = opener # set default value |
159 self._vfsmap = vfsmap |
159 self._vfsmap = vfsmap |
160 self._after = after |
160 self._after = after |
161 self._offsetmap = {} |
161 self._offsetmap = {} |
|
162 self._newfiles = set() |
162 self._journal = journalname |
163 self._journal = journalname |
163 self._undoname = undoname |
164 self._undoname = undoname |
164 self._queue = [] |
165 self._queue = [] |
165 # A callback to do something just after releasing transaction. |
166 # A callback to do something just after releasing transaction. |
166 if releasefn is None: |
167 if releasefn is None: |
246 self._addentry(f, o) |
247 self._addentry(f, o) |
247 |
248 |
248 @active |
249 @active |
249 def add(self, file, offset): |
250 def add(self, file, offset): |
250 """record the state of an append-only file before update""" |
251 """record the state of an append-only file before update""" |
251 if file in self._offsetmap or file in self._backupmap: |
252 if ( |
|
253 file in self._newfiles |
|
254 or file in self._offsetmap |
|
255 or file in self._backupmap |
|
256 ): |
252 return |
257 return |
253 if self._queue: |
258 if self._queue: |
254 self._queue[-1].append((file, offset)) |
259 self._queue[-1].append((file, offset)) |
255 return |
260 return |
256 |
261 |
257 self._addentry(file, offset) |
262 self._addentry(file, offset) |
258 |
263 |
259 def _addentry(self, file, offset): |
264 def _addentry(self, file, offset): |
260 """add a append-only entry to memory and on-disk state""" |
265 """add a append-only entry to memory and on-disk state""" |
261 if file in self._offsetmap or file in self._backupmap: |
266 if ( |
|
267 file in self._newfiles |
|
268 or file in self._offsetmap |
|
269 or file in self._backupmap |
|
270 ): |
262 return |
271 return |
263 self._offsetmap[file] = offset |
272 if offset: |
|
273 self._offsetmap[file] = offset |
|
274 else: |
|
275 self._newfiles.add(file) |
264 # add enough data to the journal to do the truncate |
276 # add enough data to the journal to do the truncate |
265 self._file.write(b"%s\0%d\n" % (file, offset)) |
277 self._file.write(b"%s\0%d\n" % (file, offset)) |
266 self._file.flush() |
278 self._file.flush() |
267 |
279 |
268 @active |
280 @active |
278 """ |
290 """ |
279 if self._queue: |
291 if self._queue: |
280 msg = b'cannot use transaction.addbackup inside "group"' |
292 msg = b'cannot use transaction.addbackup inside "group"' |
281 raise error.ProgrammingError(msg) |
293 raise error.ProgrammingError(msg) |
282 |
294 |
283 if file in self._offsetmap or file in self._backupmap: |
295 if ( |
|
296 file in self._newfiles |
|
297 or file in self._offsetmap |
|
298 or file in self._backupmap |
|
299 ): |
284 return |
300 return |
285 vfs = self._vfsmap[location] |
301 vfs = self._vfsmap[location] |
286 dirname, filename = vfs.split(file) |
302 dirname, filename = vfs.split(file) |
287 backupfilename = b"%s.backup.%s" % (self._journal, filename) |
303 backupfilename = b"%s.backup.%s" % (self._journal, filename) |
288 backupfile = vfs.reljoin(dirname, backupfilename) |
304 backupfile = vfs.reljoin(dirname, backupfilename) |
409 def replace(self, file, offset): |
427 def replace(self, file, offset): |
410 ''' |
428 ''' |
411 replace can only replace already committed entries |
429 replace can only replace already committed entries |
412 that are not pending in the queue |
430 that are not pending in the queue |
413 ''' |
431 ''' |
414 |
432 if file in self._newfiles: |
415 if file not in self._offsetmap: |
433 if not offset: |
|
434 return |
|
435 self._newfiles.remove(file) |
|
436 self._offsetmap[file] = offset |
|
437 elif file in self._offsetmap: |
|
438 if not offset: |
|
439 del self._offsetmap[file] |
|
440 self._newfiles.add(file) |
|
441 else: |
|
442 self._offsetmap[file] = offset |
|
443 else: |
416 raise KeyError(file) |
444 raise KeyError(file) |
417 self._offsetmap[file] = offset |
|
418 self._file.write(b"%s\0%d\n" % (file, offset)) |
445 self._file.write(b"%s\0%d\n" % (file, offset)) |
419 self._file.flush() |
446 self._file.flush() |
420 |
447 |
421 @active |
448 @active |
422 def nest(self, name='<unnamed>'): |
449 def nest(self, name='<unnamed>'): |
553 # Abort may be raise by read only opener |
580 # Abort may be raise by read only opener |
554 self._report( |
581 self._report( |
555 b"couldn't remove %s: %s\n" % (vfs.join(b), inst) |
582 b"couldn't remove %s: %s\n" % (vfs.join(b), inst) |
556 ) |
583 ) |
557 self._offsetmap = {} |
584 self._offsetmap = {} |
|
585 self._newfiles = set() |
558 self._writeundo() |
586 self._writeundo() |
559 if self._after: |
587 if self._after: |
560 self._after() |
588 self._after() |
561 self._after = None # Help prevent cycles. |
589 self._after = None # Help prevent cycles. |
562 if self._opener.isfile(self._backupjournal): |
590 if self._opener.isfile(self._backupjournal): |
636 self._usages = 0 |
664 self._usages = 0 |
637 self._file.close() |
665 self._file.close() |
638 self._backupsfile.close() |
666 self._backupsfile.close() |
639 |
667 |
640 try: |
668 try: |
641 if not self._offsetmap and not self._backupentries: |
669 if not entries and not self._backupentries: |
642 if self._backupjournal: |
670 if self._backupjournal: |
643 self._opener.unlink(self._backupjournal) |
671 self._opener.unlink(self._backupjournal) |
644 if self._journal: |
672 if self._journal: |
645 self._opener.unlink(self._journal) |
673 self._opener.unlink(self._journal) |
646 return |
674 return |