##// END OF EJS Templates
repair: use _hexlist() to build revset expression from binary nodes...
Yuya Nishihara -
r25340:28800ab4 default
parent child Browse files
Show More
@@ -1,231 +1,229 b''
1 # repair.py - functions for repository repair for mercurial
1 # repair.py - functions for repository repair for mercurial
2 #
2 #
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
4 # Copyright 2007 Matt Mackall
4 # Copyright 2007 Matt Mackall
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 from mercurial import changegroup, exchange, util, bundle2
9 from mercurial import changegroup, exchange, util, bundle2
10 from mercurial.node import short, hex
10 from mercurial.node import short
11 from mercurial.i18n import _
11 from mercurial.i18n import _
12 import errno
12 import errno
13
13
14 def _bundle(repo, bases, heads, node, suffix, compress=True):
14 def _bundle(repo, bases, heads, node, suffix, compress=True):
15 """create a bundle with the specified revisions as a backup"""
15 """create a bundle with the specified revisions as a backup"""
16 usebundle2 = (repo.ui.config('experimental', 'bundle2-exp') and
16 usebundle2 = (repo.ui.config('experimental', 'bundle2-exp') and
17 repo.ui.config('experimental', 'strip-bundle2-version'))
17 repo.ui.config('experimental', 'strip-bundle2-version'))
18 if usebundle2:
18 if usebundle2:
19 cgversion = repo.ui.config('experimental', 'strip-bundle2-version')
19 cgversion = repo.ui.config('experimental', 'strip-bundle2-version')
20 if cgversion not in changegroup.packermap:
20 if cgversion not in changegroup.packermap:
21 repo.ui.warn(_('unknown strip-bundle2-version value %r; '
21 repo.ui.warn(_('unknown strip-bundle2-version value %r; '
22 'should be one of %r\n') %
22 'should be one of %r\n') %
23 (cgversion, sorted(changegroup.packermap.keys()),))
23 (cgversion, sorted(changegroup.packermap.keys()),))
24 cgversion = '01'
24 cgversion = '01'
25 usebundle2 = False
25 usebundle2 = False
26 else:
26 else:
27 cgversion = '01'
27 cgversion = '01'
28
28
29 cg = changegroup.changegroupsubset(repo, bases, heads, 'strip',
29 cg = changegroup.changegroupsubset(repo, bases, heads, 'strip',
30 version=cgversion)
30 version=cgversion)
31 backupdir = "strip-backup"
31 backupdir = "strip-backup"
32 vfs = repo.vfs
32 vfs = repo.vfs
33 if not vfs.isdir(backupdir):
33 if not vfs.isdir(backupdir):
34 vfs.mkdir(backupdir)
34 vfs.mkdir(backupdir)
35
35
36 # Include a hash of all the nodes in the filename for uniqueness
36 # Include a hash of all the nodes in the filename for uniqueness
37 hexbases = (hex(n) for n in bases)
37 allcommits = repo.set('%ln::%ln', bases, heads)
38 hexheads = (hex(n) for n in heads)
39 allcommits = repo.set('%ls::%ls', hexbases, hexheads)
40 allhashes = sorted(c.hex() for c in allcommits)
38 allhashes = sorted(c.hex() for c in allcommits)
41 totalhash = util.sha1(''.join(allhashes)).hexdigest()
39 totalhash = util.sha1(''.join(allhashes)).hexdigest()
42 name = "%s/%s-%s-%s.hg" % (backupdir, short(node), totalhash[:8], suffix)
40 name = "%s/%s-%s-%s.hg" % (backupdir, short(node), totalhash[:8], suffix)
43
41
44 if usebundle2:
42 if usebundle2:
45 bundletype = "HG20"
43 bundletype = "HG20"
46 elif compress:
44 elif compress:
47 bundletype = "HG10BZ"
45 bundletype = "HG10BZ"
48 else:
46 else:
49 bundletype = "HG10UN"
47 bundletype = "HG10UN"
50 return changegroup.writebundle(repo.ui, cg, name, bundletype, vfs)
48 return changegroup.writebundle(repo.ui, cg, name, bundletype, vfs)
51
49
52 def _collectfiles(repo, striprev):
50 def _collectfiles(repo, striprev):
53 """find out the filelogs affected by the strip"""
51 """find out the filelogs affected by the strip"""
54 files = set()
52 files = set()
55
53
56 for x in xrange(striprev, len(repo)):
54 for x in xrange(striprev, len(repo)):
57 files.update(repo[x].files())
55 files.update(repo[x].files())
58
56
59 return sorted(files)
57 return sorted(files)
60
58
61 def _collectbrokencsets(repo, files, striprev):
59 def _collectbrokencsets(repo, files, striprev):
62 """return the changesets which will be broken by the truncation"""
60 """return the changesets which will be broken by the truncation"""
63 s = set()
61 s = set()
64 def collectone(revlog):
62 def collectone(revlog):
65 _, brokenset = revlog.getstrippoint(striprev)
63 _, brokenset = revlog.getstrippoint(striprev)
66 s.update([revlog.linkrev(r) for r in brokenset])
64 s.update([revlog.linkrev(r) for r in brokenset])
67
65
68 collectone(repo.manifest)
66 collectone(repo.manifest)
69 for fname in files:
67 for fname in files:
70 collectone(repo.file(fname))
68 collectone(repo.file(fname))
71
69
72 return s
70 return s
73
71
74 def strip(ui, repo, nodelist, backup=True, topic='backup'):
72 def strip(ui, repo, nodelist, backup=True, topic='backup'):
75
73
76 # Simple way to maintain backwards compatibility for this
74 # Simple way to maintain backwards compatibility for this
77 # argument.
75 # argument.
78 if backup in ['none', 'strip']:
76 if backup in ['none', 'strip']:
79 backup = False
77 backup = False
80
78
81 repo = repo.unfiltered()
79 repo = repo.unfiltered()
82 repo.destroying()
80 repo.destroying()
83
81
84 cl = repo.changelog
82 cl = repo.changelog
85 # TODO handle undo of merge sets
83 # TODO handle undo of merge sets
86 if isinstance(nodelist, str):
84 if isinstance(nodelist, str):
87 nodelist = [nodelist]
85 nodelist = [nodelist]
88 striplist = [cl.rev(node) for node in nodelist]
86 striplist = [cl.rev(node) for node in nodelist]
89 striprev = min(striplist)
87 striprev = min(striplist)
90
88
91 # Some revisions with rev > striprev may not be descendants of striprev.
89 # Some revisions with rev > striprev may not be descendants of striprev.
92 # We have to find these revisions and put them in a bundle, so that
90 # We have to find these revisions and put them in a bundle, so that
93 # we can restore them after the truncations.
91 # we can restore them after the truncations.
94 # To create the bundle we use repo.changegroupsubset which requires
92 # To create the bundle we use repo.changegroupsubset which requires
95 # the list of heads and bases of the set of interesting revisions.
93 # the list of heads and bases of the set of interesting revisions.
96 # (head = revision in the set that has no descendant in the set;
94 # (head = revision in the set that has no descendant in the set;
97 # base = revision in the set that has no ancestor in the set)
95 # base = revision in the set that has no ancestor in the set)
98 tostrip = set(striplist)
96 tostrip = set(striplist)
99 for rev in striplist:
97 for rev in striplist:
100 for desc in cl.descendants([rev]):
98 for desc in cl.descendants([rev]):
101 tostrip.add(desc)
99 tostrip.add(desc)
102
100
103 files = _collectfiles(repo, striprev)
101 files = _collectfiles(repo, striprev)
104 saverevs = _collectbrokencsets(repo, files, striprev)
102 saverevs = _collectbrokencsets(repo, files, striprev)
105
103
106 # compute heads
104 # compute heads
107 saveheads = set(saverevs)
105 saveheads = set(saverevs)
108 for r in xrange(striprev + 1, len(cl)):
106 for r in xrange(striprev + 1, len(cl)):
109 if r not in tostrip:
107 if r not in tostrip:
110 saverevs.add(r)
108 saverevs.add(r)
111 saveheads.difference_update(cl.parentrevs(r))
109 saveheads.difference_update(cl.parentrevs(r))
112 saveheads.add(r)
110 saveheads.add(r)
113 saveheads = [cl.node(r) for r in saveheads]
111 saveheads = [cl.node(r) for r in saveheads]
114
112
115 # compute base nodes
113 # compute base nodes
116 if saverevs:
114 if saverevs:
117 descendants = set(cl.descendants(saverevs))
115 descendants = set(cl.descendants(saverevs))
118 saverevs.difference_update(descendants)
116 saverevs.difference_update(descendants)
119 savebases = [cl.node(r) for r in saverevs]
117 savebases = [cl.node(r) for r in saverevs]
120 stripbases = [cl.node(r) for r in tostrip]
118 stripbases = [cl.node(r) for r in tostrip]
121
119
122 # For a set s, max(parents(s) - s) is the same as max(heads(::s - s)), but
120 # For a set s, max(parents(s) - s) is the same as max(heads(::s - s)), but
123 # is much faster
121 # is much faster
124 newbmtarget = repo.revs('max(parents(%ld) - (%ld))', tostrip, tostrip)
122 newbmtarget = repo.revs('max(parents(%ld) - (%ld))', tostrip, tostrip)
125 if newbmtarget:
123 if newbmtarget:
126 newbmtarget = repo[newbmtarget.first()].node()
124 newbmtarget = repo[newbmtarget.first()].node()
127 else:
125 else:
128 newbmtarget = '.'
126 newbmtarget = '.'
129
127
130 bm = repo._bookmarks
128 bm = repo._bookmarks
131 updatebm = []
129 updatebm = []
132 for m in bm:
130 for m in bm:
133 rev = repo[bm[m]].rev()
131 rev = repo[bm[m]].rev()
134 if rev in tostrip:
132 if rev in tostrip:
135 updatebm.append(m)
133 updatebm.append(m)
136
134
137 # create a changegroup for all the branches we need to keep
135 # create a changegroup for all the branches we need to keep
138 backupfile = None
136 backupfile = None
139 vfs = repo.vfs
137 vfs = repo.vfs
140 node = nodelist[-1]
138 node = nodelist[-1]
141 if backup:
139 if backup:
142 backupfile = _bundle(repo, stripbases, cl.heads(), node, topic)
140 backupfile = _bundle(repo, stripbases, cl.heads(), node, topic)
143 repo.ui.status(_("saved backup bundle to %s\n") %
141 repo.ui.status(_("saved backup bundle to %s\n") %
144 vfs.join(backupfile))
142 vfs.join(backupfile))
145 repo.ui.log("backupbundle", "saved backup bundle to %s\n",
143 repo.ui.log("backupbundle", "saved backup bundle to %s\n",
146 vfs.join(backupfile))
144 vfs.join(backupfile))
147 if saveheads or savebases:
145 if saveheads or savebases:
148 # do not compress partial bundle if we remove it from disk later
146 # do not compress partial bundle if we remove it from disk later
149 chgrpfile = _bundle(repo, savebases, saveheads, node, 'temp',
147 chgrpfile = _bundle(repo, savebases, saveheads, node, 'temp',
150 compress=False)
148 compress=False)
151
149
152 mfst = repo.manifest
150 mfst = repo.manifest
153
151
154 curtr = repo.currenttransaction()
152 curtr = repo.currenttransaction()
155 if curtr is not None:
153 if curtr is not None:
156 del curtr # avoid carrying reference to transaction for nothing
154 del curtr # avoid carrying reference to transaction for nothing
157 msg = _('programming error: cannot strip from inside a transaction')
155 msg = _('programming error: cannot strip from inside a transaction')
158 raise util.Abort(msg, hint=_('contact your extension maintainer'))
156 raise util.Abort(msg, hint=_('contact your extension maintainer'))
159
157
160 tr = repo.transaction("strip")
158 tr = repo.transaction("strip")
161 offset = len(tr.entries)
159 offset = len(tr.entries)
162
160
163 try:
161 try:
164 tr.startgroup()
162 tr.startgroup()
165 cl.strip(striprev, tr)
163 cl.strip(striprev, tr)
166 mfst.strip(striprev, tr)
164 mfst.strip(striprev, tr)
167 for fn in files:
165 for fn in files:
168 repo.file(fn).strip(striprev, tr)
166 repo.file(fn).strip(striprev, tr)
169 tr.endgroup()
167 tr.endgroup()
170
168
171 try:
169 try:
172 for i in xrange(offset, len(tr.entries)):
170 for i in xrange(offset, len(tr.entries)):
173 file, troffset, ignore = tr.entries[i]
171 file, troffset, ignore = tr.entries[i]
174 repo.svfs(file, 'a').truncate(troffset)
172 repo.svfs(file, 'a').truncate(troffset)
175 if troffset == 0:
173 if troffset == 0:
176 repo.store.markremoved(file)
174 repo.store.markremoved(file)
177 tr.close()
175 tr.close()
178 except: # re-raises
176 except: # re-raises
179 tr.abort()
177 tr.abort()
180 raise
178 raise
181
179
182 if saveheads or savebases:
180 if saveheads or savebases:
183 ui.note(_("adding branch\n"))
181 ui.note(_("adding branch\n"))
184 f = vfs.open(chgrpfile, "rb")
182 f = vfs.open(chgrpfile, "rb")
185 gen = exchange.readbundle(ui, f, chgrpfile, vfs)
183 gen = exchange.readbundle(ui, f, chgrpfile, vfs)
186 if not repo.ui.verbose:
184 if not repo.ui.verbose:
187 # silence internal shuffling chatter
185 # silence internal shuffling chatter
188 repo.ui.pushbuffer()
186 repo.ui.pushbuffer()
189 if isinstance(gen, bundle2.unbundle20):
187 if isinstance(gen, bundle2.unbundle20):
190 tr = repo.transaction('strip')
188 tr = repo.transaction('strip')
191 tr.hookargs = {'source': 'strip',
189 tr.hookargs = {'source': 'strip',
192 'url': 'bundle:' + vfs.join(chgrpfile)}
190 'url': 'bundle:' + vfs.join(chgrpfile)}
193 try:
191 try:
194 bundle2.processbundle(repo, gen, lambda: tr)
192 bundle2.processbundle(repo, gen, lambda: tr)
195 tr.close()
193 tr.close()
196 finally:
194 finally:
197 tr.release()
195 tr.release()
198 else:
196 else:
199 changegroup.addchangegroup(repo, gen, 'strip',
197 changegroup.addchangegroup(repo, gen, 'strip',
200 'bundle:' + vfs.join(chgrpfile),
198 'bundle:' + vfs.join(chgrpfile),
201 True)
199 True)
202 if not repo.ui.verbose:
200 if not repo.ui.verbose:
203 repo.ui.popbuffer()
201 repo.ui.popbuffer()
204 f.close()
202 f.close()
205
203
206 # remove undo files
204 # remove undo files
207 for undovfs, undofile in repo.undofiles():
205 for undovfs, undofile in repo.undofiles():
208 try:
206 try:
209 undovfs.unlink(undofile)
207 undovfs.unlink(undofile)
210 except OSError, e:
208 except OSError, e:
211 if e.errno != errno.ENOENT:
209 if e.errno != errno.ENOENT:
212 ui.warn(_('error removing %s: %s\n') %
210 ui.warn(_('error removing %s: %s\n') %
213 (undovfs.join(undofile), str(e)))
211 (undovfs.join(undofile), str(e)))
214
212
215 for m in updatebm:
213 for m in updatebm:
216 bm[m] = repo[newbmtarget].node()
214 bm[m] = repo[newbmtarget].node()
217 bm.write()
215 bm.write()
218 except: # re-raises
216 except: # re-raises
219 if backupfile:
217 if backupfile:
220 ui.warn(_("strip failed, full bundle stored in '%s'\n")
218 ui.warn(_("strip failed, full bundle stored in '%s'\n")
221 % vfs.join(backupfile))
219 % vfs.join(backupfile))
222 elif saveheads:
220 elif saveheads:
223 ui.warn(_("strip failed, partial bundle stored in '%s'\n")
221 ui.warn(_("strip failed, partial bundle stored in '%s'\n")
224 % vfs.join(chgrpfile))
222 % vfs.join(chgrpfile))
225 raise
223 raise
226 else:
224 else:
227 if saveheads or savebases:
225 if saveheads or savebases:
228 # Remove partial backup only if there were no exceptions
226 # Remove partial backup only if there were no exceptions
229 vfs.unlink(chgrpfile)
227 vfs.unlink(chgrpfile)
230
228
231 repo.destroyed()
229 repo.destroyed()
General Comments 0
You need to be logged in to leave comments. Login now