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