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