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