##// END OF EJS Templates
transaction: only delete journal on successful abort/commit...
Henrik Stuart -
r8071:9f14b668 default
parent child Browse files
Show More
@@ -1,112 +1,118 b''
1 1 # transaction.py - simple journalling scheme for mercurial
2 2 #
3 3 # This transaction scheme is intended to gracefully handle program
4 4 # errors and interruptions. More serious failures like system crashes
5 5 # can be recovered with an fsck-like tool. As the whole repository is
6 6 # effectively log-structured, this should amount to simply truncating
7 7 # anything that isn't referenced in the changelog.
8 8 #
9 9 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
10 10 #
11 11 # This software may be used and distributed according to the terms
12 12 # of the GNU General Public License, incorporated herein by reference.
13 13
14 14 from i18n import _
15 15 import os, errno
16 16
17 17 class transaction(object):
18 18 def __init__(self, report, opener, journal, after=None, createmode=None):
19 19 self.journal = None
20 20
21 21 self.count = 1
22 22 self.report = report
23 23 self.opener = opener
24 24 self.after = after
25 25 self.entries = []
26 26 self.map = {}
27 27 self.journal = journal
28 28
29 29 self.file = open(self.journal, "w")
30 30 if createmode is not None:
31 31 os.chmod(self.journal, createmode & 0666)
32 32
33 33 def __del__(self):
34 34 if self.journal:
35 35 if self.entries: self.abort()
36 36 self.file.close()
37 try: os.unlink(self.journal)
38 except: pass
39 37
40 38 def add(self, file, offset, data=None):
41 39 if file in self.map: return
42 40 self.entries.append((file, offset, data))
43 41 self.map[file] = len(self.entries) - 1
44 42 # add enough data to the journal to do the truncate
45 43 self.file.write("%s\0%d\n" % (file, offset))
46 44 self.file.flush()
47 45
48 46 def find(self, file):
49 47 if file in self.map:
50 48 return self.entries[self.map[file]]
51 49 return None
52 50
53 51 def replace(self, file, offset, data=None):
54 52 if file not in self.map:
55 53 raise KeyError(file)
56 54 index = self.map[file]
57 55 self.entries[index] = (file, offset, data)
58 56 self.file.write("%s\0%d\n" % (file, offset))
59 57 self.file.flush()
60 58
61 59 def nest(self):
62 60 self.count += 1
63 61 return self
64 62
65 63 def running(self):
66 64 return self.count > 0
67 65
68 66 def close(self):
69 67 self.count -= 1
70 68 if self.count != 0:
71 69 return
72 70 self.file.close()
73 71 self.entries = []
74 72 if self.after:
75 73 self.after()
76 74 else:
77 75 os.unlink(self.journal)
78 76 self.journal = None
79 77
80 78 def abort(self):
81 79 if not self.entries: return
82 80
83 81 self.report(_("transaction abort!\n"))
84 82
83 failed = False
85 84 for f, o, ignore in self.entries:
86 85 try:
87 86 self.opener(f, "a").truncate(o)
88 87 except:
88 failed = True
89 89 self.report(_("failed to truncate %s\n") % f)
90 90
91 91 self.entries = []
92 92
93 self.report(_("rollback completed\n"))
93 if not failed:
94 self.file.close()
95 os.unlink(self.journal)
96 self.journal = None
97 self.report(_("rollback completed\n"))
98 else:
99 self.report(_("rollback failed - please run hg recover\n"))
94 100
95 101 def rollback(opener, file):
96 102 files = {}
97 103 for l in open(file).readlines():
98 104 f, o = l.split('\0')
99 105 files[f] = int(o)
100 106 for f in files:
101 107 o = files[f]
102 108 if o:
103 109 opener(f, "a").truncate(int(o))
104 110 else:
105 111 try:
106 112 fn = opener(f).name
107 113 os.unlink(fn)
108 114 except OSError, inst:
109 115 if inst.errno != errno.ENOENT:
110 116 raise
111 117 os.unlink(file)
112 118
General Comments 0
You need to be logged in to leave comments. Login now