##// END OF EJS Templates
repair: make "strip()" treat bundle files via vfs...
FUJIWARA Katsunori -
r20979:ad5b6137 default
parent child Browse files
Show More
@@ -1,181 +1,183
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
9 from mercurial import changegroup
10 from mercurial.node import short
10 from mercurial.node import short
11 from mercurial.i18n import _
11 from mercurial.i18n import _
12 import os
13 import errno
12 import errno
14
13
15 def _bundle(repo, bases, heads, node, suffix, compress=True):
14 def _bundle(repo, bases, heads, node, suffix, compress=True):
16 """create a bundle with the specified revisions as a backup"""
15 """create a bundle with the specified revisions as a backup"""
17 cg = changegroup.changegroupsubset(repo, bases, heads, 'strip')
16 cg = changegroup.changegroupsubset(repo, bases, heads, 'strip')
18 backupdir = "strip-backup"
17 backupdir = "strip-backup"
19 vfs = repo.vfs
18 vfs = repo.vfs
20 if not vfs.isdir(backupdir):
19 if not vfs.isdir(backupdir):
21 vfs.mkdir(backupdir)
20 vfs.mkdir(backupdir)
22 name = "%s/%s-%s.hg" % (backupdir, short(node), suffix)
21 name = "%s/%s-%s.hg" % (backupdir, short(node), suffix)
23 if compress:
22 if compress:
24 bundletype = "HG10BZ"
23 bundletype = "HG10BZ"
25 else:
24 else:
26 bundletype = "HG10UN"
25 bundletype = "HG10UN"
27 return vfs.join(changegroup.writebundle(cg, name, bundletype, vfs))
26 return changegroup.writebundle(cg, name, bundletype, vfs)
28
27
29 def _collectfiles(repo, striprev):
28 def _collectfiles(repo, striprev):
30 """find out the filelogs affected by the strip"""
29 """find out the filelogs affected by the strip"""
31 files = set()
30 files = set()
32
31
33 for x in xrange(striprev, len(repo)):
32 for x in xrange(striprev, len(repo)):
34 files.update(repo[x].files())
33 files.update(repo[x].files())
35
34
36 return sorted(files)
35 return sorted(files)
37
36
38 def _collectbrokencsets(repo, files, striprev):
37 def _collectbrokencsets(repo, files, striprev):
39 """return the changesets which will be broken by the truncation"""
38 """return the changesets which will be broken by the truncation"""
40 s = set()
39 s = set()
41 def collectone(revlog):
40 def collectone(revlog):
42 _, brokenset = revlog.getstrippoint(striprev)
41 _, brokenset = revlog.getstrippoint(striprev)
43 s.update([revlog.linkrev(r) for r in brokenset])
42 s.update([revlog.linkrev(r) for r in brokenset])
44
43
45 collectone(repo.manifest)
44 collectone(repo.manifest)
46 for fname in files:
45 for fname in files:
47 collectone(repo.file(fname))
46 collectone(repo.file(fname))
48
47
49 return s
48 return s
50
49
51 def strip(ui, repo, nodelist, backup="all", topic='backup'):
50 def strip(ui, repo, nodelist, backup="all", topic='backup'):
52 repo = repo.unfiltered()
51 repo = repo.unfiltered()
53 repo.destroying()
52 repo.destroying()
54
53
55 cl = repo.changelog
54 cl = repo.changelog
56 # TODO handle undo of merge sets
55 # TODO handle undo of merge sets
57 if isinstance(nodelist, str):
56 if isinstance(nodelist, str):
58 nodelist = [nodelist]
57 nodelist = [nodelist]
59 striplist = [cl.rev(node) for node in nodelist]
58 striplist = [cl.rev(node) for node in nodelist]
60 striprev = min(striplist)
59 striprev = min(striplist)
61
60
62 keeppartialbundle = backup == 'strip'
61 keeppartialbundle = backup == 'strip'
63
62
64 # Some revisions with rev > striprev may not be descendants of striprev.
63 # Some revisions with rev > striprev may not be descendants of striprev.
65 # We have to find these revisions and put them in a bundle, so that
64 # We have to find these revisions and put them in a bundle, so that
66 # we can restore them after the truncations.
65 # we can restore them after the truncations.
67 # To create the bundle we use repo.changegroupsubset which requires
66 # To create the bundle we use repo.changegroupsubset which requires
68 # the list of heads and bases of the set of interesting revisions.
67 # the list of heads and bases of the set of interesting revisions.
69 # (head = revision in the set that has no descendant in the set;
68 # (head = revision in the set that has no descendant in the set;
70 # base = revision in the set that has no ancestor in the set)
69 # base = revision in the set that has no ancestor in the set)
71 tostrip = set(striplist)
70 tostrip = set(striplist)
72 for rev in striplist:
71 for rev in striplist:
73 for desc in cl.descendants([rev]):
72 for desc in cl.descendants([rev]):
74 tostrip.add(desc)
73 tostrip.add(desc)
75
74
76 files = _collectfiles(repo, striprev)
75 files = _collectfiles(repo, striprev)
77 saverevs = _collectbrokencsets(repo, files, striprev)
76 saverevs = _collectbrokencsets(repo, files, striprev)
78
77
79 # compute heads
78 # compute heads
80 saveheads = set(saverevs)
79 saveheads = set(saverevs)
81 for r in xrange(striprev + 1, len(cl)):
80 for r in xrange(striprev + 1, len(cl)):
82 if r not in tostrip:
81 if r not in tostrip:
83 saverevs.add(r)
82 saverevs.add(r)
84 saveheads.difference_update(cl.parentrevs(r))
83 saveheads.difference_update(cl.parentrevs(r))
85 saveheads.add(r)
84 saveheads.add(r)
86 saveheads = [cl.node(r) for r in saveheads]
85 saveheads = [cl.node(r) for r in saveheads]
87
86
88 # compute base nodes
87 # compute base nodes
89 if saverevs:
88 if saverevs:
90 descendants = set(cl.descendants(saverevs))
89 descendants = set(cl.descendants(saverevs))
91 saverevs.difference_update(descendants)
90 saverevs.difference_update(descendants)
92 savebases = [cl.node(r) for r in saverevs]
91 savebases = [cl.node(r) for r in saverevs]
93 stripbases = [cl.node(r) for r in tostrip]
92 stripbases = [cl.node(r) for r in tostrip]
94
93
95 # For a set s, max(parents(s) - s) is the same as max(heads(::s - s)), but
94 # For a set s, max(parents(s) - s) is the same as max(heads(::s - s)), but
96 # is much faster
95 # is much faster
97 newbmtarget = repo.revs('max(parents(%ld) - (%ld))', tostrip, tostrip)
96 newbmtarget = repo.revs('max(parents(%ld) - (%ld))', tostrip, tostrip)
98 if newbmtarget:
97 if newbmtarget:
99 newbmtarget = repo[newbmtarget[0]].node()
98 newbmtarget = repo[newbmtarget[0]].node()
100 else:
99 else:
101 newbmtarget = '.'
100 newbmtarget = '.'
102
101
103 bm = repo._bookmarks
102 bm = repo._bookmarks
104 updatebm = []
103 updatebm = []
105 for m in bm:
104 for m in bm:
106 rev = repo[bm[m]].rev()
105 rev = repo[bm[m]].rev()
107 if rev in tostrip:
106 if rev in tostrip:
108 updatebm.append(m)
107 updatebm.append(m)
109
108
110 # create a changegroup for all the branches we need to keep
109 # create a changegroup for all the branches we need to keep
111 backupfile = None
110 backupfile = None
111 vfs = repo.vfs
112 if backup == "all":
112 if backup == "all":
113 backupfile = _bundle(repo, stripbases, cl.heads(), node, topic)
113 backupfile = _bundle(repo, stripbases, cl.heads(), node, topic)
114 repo.ui.status(_("saved backup bundle to %s\n") % backupfile)
114 repo.ui.status(_("saved backup bundle to %s\n") %
115 repo.ui.log("backupbundle", "saved backup bundle to %s\n", backupfile)
115 vfs.join(backupfile))
116 repo.ui.log("backupbundle", "saved backup bundle to %s\n",
117 vfs.join(backupfile))
116 if saveheads or savebases:
118 if saveheads or savebases:
117 # do not compress partial bundle if we remove it from disk later
119 # do not compress partial bundle if we remove it from disk later
118 chgrpfile = _bundle(repo, savebases, saveheads, node, 'temp',
120 chgrpfile = _bundle(repo, savebases, saveheads, node, 'temp',
119 compress=keeppartialbundle)
121 compress=keeppartialbundle)
120
122
121 mfst = repo.manifest
123 mfst = repo.manifest
122
124
123 tr = repo.transaction("strip")
125 tr = repo.transaction("strip")
124 offset = len(tr.entries)
126 offset = len(tr.entries)
125
127
126 try:
128 try:
127 tr.startgroup()
129 tr.startgroup()
128 cl.strip(striprev, tr)
130 cl.strip(striprev, tr)
129 mfst.strip(striprev, tr)
131 mfst.strip(striprev, tr)
130 for fn in files:
132 for fn in files:
131 repo.file(fn).strip(striprev, tr)
133 repo.file(fn).strip(striprev, tr)
132 tr.endgroup()
134 tr.endgroup()
133
135
134 try:
136 try:
135 for i in xrange(offset, len(tr.entries)):
137 for i in xrange(offset, len(tr.entries)):
136 file, troffset, ignore = tr.entries[i]
138 file, troffset, ignore = tr.entries[i]
137 repo.sopener(file, 'a').truncate(troffset)
139 repo.sopener(file, 'a').truncate(troffset)
138 if troffset == 0:
140 if troffset == 0:
139 repo.store.markremoved(file)
141 repo.store.markremoved(file)
140 tr.close()
142 tr.close()
141 except: # re-raises
143 except: # re-raises
142 tr.abort()
144 tr.abort()
143 raise
145 raise
144
146
145 if saveheads or savebases:
147 if saveheads or savebases:
146 ui.note(_("adding branch\n"))
148 ui.note(_("adding branch\n"))
147 f = open(chgrpfile, "rb")
149 f = vfs.open(chgrpfile, "rb")
148 gen = changegroup.readbundle(f, chgrpfile)
150 gen = changegroup.readbundle(f, chgrpfile, vfs)
149 if not repo.ui.verbose:
151 if not repo.ui.verbose:
150 # silence internal shuffling chatter
152 # silence internal shuffling chatter
151 repo.ui.pushbuffer()
153 repo.ui.pushbuffer()
152 changegroup.addchangegroup(repo, gen, 'strip',
154 changegroup.addchangegroup(repo, gen, 'strip',
153 'bundle:' + chgrpfile, True)
155 'bundle:' + vfs.join(chgrpfile), True)
154 if not repo.ui.verbose:
156 if not repo.ui.verbose:
155 repo.ui.popbuffer()
157 repo.ui.popbuffer()
156 f.close()
158 f.close()
157 if not keeppartialbundle:
159 if not keeppartialbundle:
158 os.unlink(chgrpfile)
160 vfs.unlink(chgrpfile)
159
161
160 # remove undo files
162 # remove undo files
161 for undovfs, undofile in repo.undofiles():
163 for undovfs, undofile in repo.undofiles():
162 try:
164 try:
163 undovfs.unlink(undofile)
165 undovfs.unlink(undofile)
164 except OSError, e:
166 except OSError, e:
165 if e.errno != errno.ENOENT:
167 if e.errno != errno.ENOENT:
166 ui.warn(_('error removing %s: %s\n') %
168 ui.warn(_('error removing %s: %s\n') %
167 (undovfs.join(undofile), str(e)))
169 (undovfs.join(undofile), str(e)))
168
170
169 for m in updatebm:
171 for m in updatebm:
170 bm[m] = repo[newbmtarget].node()
172 bm[m] = repo[newbmtarget].node()
171 bm.write()
173 bm.write()
172 except: # re-raises
174 except: # re-raises
173 if backupfile:
175 if backupfile:
174 ui.warn(_("strip failed, full bundle stored in '%s'\n")
176 ui.warn(_("strip failed, full bundle stored in '%s'\n")
175 % backupfile)
177 % vfs.join(backupfile))
176 elif saveheads:
178 elif saveheads:
177 ui.warn(_("strip failed, partial bundle stored in '%s'\n")
179 ui.warn(_("strip failed, partial bundle stored in '%s'\n")
178 % chgrpfile)
180 % vfs.join(chgrpfile))
179 raise
181 raise
180
182
181 repo.destroyed()
183 repo.destroyed()
General Comments 0
You need to be logged in to leave comments. Login now