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