##// END OF EJS Templates
repair: bulk update sets...
Martin Geisler -
r8479:3e16c0fc default
parent child Browse files
Show More
@@ -1,149 +1,145
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, incorporated herein by reference.
7 # GNU General Public License version 2, incorporated herein by reference.
8
8
9 import changegroup
9 import changegroup
10 from node import nullrev, short
10 from node import nullrev, short
11 from i18n import _
11 from i18n import _
12 import os
12 import os
13
13
14 def _bundle(repo, bases, heads, node, suffix, extranodes=None):
14 def _bundle(repo, bases, heads, node, suffix, extranodes=None):
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', extranodes)
16 cg = repo.changegroupsubset(bases, heads, 'strip', extranodes)
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" % (short(node), suffix))
20 name = os.path.join(backupdir, "%s-%s" % (short(node), suffix))
21 repo.ui.warn(_("saving bundle to %s\n") % name)
21 repo.ui.warn(_("saving bundle to %s\n") % name)
22 return changegroup.writebundle(cg, name, "HG10BZ")
22 return changegroup.writebundle(cg, name, "HG10BZ")
23
23
24 def _collectfiles(repo, striprev):
24 def _collectfiles(repo, striprev):
25 """find out the filelogs affected by the strip"""
25 """find out the filelogs affected by the strip"""
26 files = set()
26 files = set()
27
27
28 for x in xrange(striprev, len(repo)):
28 for x in xrange(striprev, len(repo)):
29 for name in repo[x].files():
29 files.update(repo[x].files())
30 if name in files:
31 continue
32 files.add(name)
33
30
34 return sorted(files)
31 return sorted(files)
35
32
36 def _collectextranodes(repo, files, link):
33 def _collectextranodes(repo, files, link):
37 """return the nodes that have to be saved before the strip"""
34 """return the nodes that have to be saved before the strip"""
38 def collectone(revlog):
35 def collectone(revlog):
39 extra = []
36 extra = []
40 startrev = count = len(revlog)
37 startrev = count = len(revlog)
41 # find the truncation point of the revlog
38 # find the truncation point of the revlog
42 for i in xrange(0, count):
39 for i in xrange(0, count):
43 lrev = revlog.linkrev(i)
40 lrev = revlog.linkrev(i)
44 if lrev >= link:
41 if lrev >= link:
45 startrev = i + 1
42 startrev = i + 1
46 break
43 break
47
44
48 # see if any revision after that point has a linkrev less than link
45 # see if any revision after that point has a linkrev less than link
49 # (we have to manually save these guys)
46 # (we have to manually save these guys)
50 for i in xrange(startrev, count):
47 for i in xrange(startrev, count):
51 node = revlog.node(i)
48 node = revlog.node(i)
52 lrev = revlog.linkrev(i)
49 lrev = revlog.linkrev(i)
53 if lrev < link:
50 if lrev < link:
54 extra.append((node, cl.node(lrev)))
51 extra.append((node, cl.node(lrev)))
55
52
56 return extra
53 return extra
57
54
58 extranodes = {}
55 extranodes = {}
59 cl = repo.changelog
56 cl = repo.changelog
60 extra = collectone(repo.manifest)
57 extra = collectone(repo.manifest)
61 if extra:
58 if extra:
62 extranodes[1] = extra
59 extranodes[1] = extra
63 for fname in files:
60 for fname in files:
64 f = repo.file(fname)
61 f = repo.file(fname)
65 extra = collectone(f)
62 extra = collectone(f)
66 if extra:
63 if extra:
67 extranodes[fname] = extra
64 extranodes[fname] = extra
68
65
69 return extranodes
66 return extranodes
70
67
71 def strip(ui, repo, node, backup="all"):
68 def strip(ui, repo, node, backup="all"):
72 cl = repo.changelog
69 cl = repo.changelog
73 # TODO delete the undo files, and handle undo of merge sets
70 # TODO delete the undo files, and handle undo of merge sets
74 striprev = cl.rev(node)
71 striprev = cl.rev(node)
75
72
76 # Some revisions with rev > striprev may not be descendants of striprev.
73 # Some revisions with rev > striprev may not be descendants of striprev.
77 # We have to find these revisions and put them in a bundle, so that
74 # We have to find these revisions and put them in a bundle, so that
78 # we can restore them after the truncations.
75 # we can restore them after the truncations.
79 # To create the bundle we use repo.changegroupsubset which requires
76 # To create the bundle we use repo.changegroupsubset which requires
80 # the list of heads and bases of the set of interesting revisions.
77 # the list of heads and bases of the set of interesting revisions.
81 # (head = revision in the set that has no descendant in the set;
78 # (head = revision in the set that has no descendant in the set;
82 # base = revision in the set that has no ancestor in the set)
79 # base = revision in the set that has no ancestor in the set)
83 tostrip = set((striprev,))
80 tostrip = set((striprev,))
84 saveheads = set()
81 saveheads = set()
85 savebases = []
82 savebases = []
86 for r in xrange(striprev + 1, len(cl)):
83 for r in xrange(striprev + 1, len(cl)):
87 parents = cl.parentrevs(r)
84 parents = cl.parentrevs(r)
88 if parents[0] in tostrip or parents[1] in tostrip:
85 if parents[0] in tostrip or parents[1] in tostrip:
89 # r is a descendant of striprev
86 # r is a descendant of striprev
90 tostrip.add(r)
87 tostrip.add(r)
91 # if this is a merge and one of the parents does not descend
88 # if this is a merge and one of the parents does not descend
92 # from striprev, mark that parent as a savehead.
89 # from striprev, mark that parent as a savehead.
93 if parents[1] != nullrev:
90 if parents[1] != nullrev:
94 for p in parents:
91 for p in parents:
95 if p not in tostrip and p > striprev:
92 if p not in tostrip and p > striprev:
96 saveheads.add(p)
93 saveheads.add(p)
97 else:
94 else:
98 # if no parents of this revision will be stripped, mark it as
95 # if no parents of this revision will be stripped, mark it as
99 # a savebase
96 # a savebase
100 if parents[0] < striprev and parents[1] < striprev:
97 if parents[0] < striprev and parents[1] < striprev:
101 savebases.append(cl.node(r))
98 savebases.append(cl.node(r))
102
99
103 for p in parents:
100 saveheads.difference_update(parents)
104 saveheads.discard(p)
105 saveheads.add(r)
101 saveheads.add(r)
106
102
107 saveheads = [cl.node(r) for r in saveheads]
103 saveheads = [cl.node(r) for r in saveheads]
108 files = _collectfiles(repo, striprev)
104 files = _collectfiles(repo, striprev)
109
105
110 extranodes = _collectextranodes(repo, files, striprev)
106 extranodes = _collectextranodes(repo, files, striprev)
111
107
112 # create a changegroup for all the branches we need to keep
108 # create a changegroup for all the branches we need to keep
113 if backup == "all":
109 if backup == "all":
114 _bundle(repo, [node], cl.heads(), node, 'backup')
110 _bundle(repo, [node], cl.heads(), node, 'backup')
115 if saveheads or extranodes:
111 if saveheads or extranodes:
116 chgrpfile = _bundle(repo, savebases, saveheads, node, 'temp',
112 chgrpfile = _bundle(repo, savebases, saveheads, node, 'temp',
117 extranodes)
113 extranodes)
118
114
119 fs = [repo.file(name) for name in files]
115 fs = [repo.file(name) for name in files]
120 mfst = repo.manifest
116 mfst = repo.manifest
121
117
122 tr = repo.transaction()
118 tr = repo.transaction()
123 offset = len(tr.entries)
119 offset = len(tr.entries)
124
120
125 tr.startgroup()
121 tr.startgroup()
126 cl.strip(striprev, tr)
122 cl.strip(striprev, tr)
127 mfst.strip(striprev, tr)
123 mfst.strip(striprev, tr)
128 for f in fs:
124 for f in fs:
129 f.strip(striprev, tr)
125 f.strip(striprev, tr)
130 tr.endgroup()
126 tr.endgroup()
131
127
132 try:
128 try:
133 for i in xrange(offset, len(tr.entries)):
129 for i in xrange(offset, len(tr.entries)):
134 file, troffset, ignore = tr.entries[i]
130 file, troffset, ignore = tr.entries[i]
135 repo.sopener(file, 'a').truncate(troffset)
131 repo.sopener(file, 'a').truncate(troffset)
136 tr.close()
132 tr.close()
137 except:
133 except:
138 tr.abort()
134 tr.abort()
139 raise
135 raise
140
136
141 if saveheads or extranodes:
137 if saveheads or extranodes:
142 ui.status(_("adding branch\n"))
138 ui.status(_("adding branch\n"))
143 f = open(chgrpfile, "rb")
139 f = open(chgrpfile, "rb")
144 gen = changegroup.readbundle(f, chgrpfile)
140 gen = changegroup.readbundle(f, chgrpfile)
145 repo.addchangegroup(gen, 'strip', 'bundle:' + chgrpfile, True)
141 repo.addchangegroup(gen, 'strip', 'bundle:' + chgrpfile, True)
146 f.close()
142 f.close()
147 if backup != "strip":
143 if backup != "strip":
148 os.unlink(chgrpfile)
144 os.unlink(chgrpfile)
149
145
General Comments 0
You need to be logged in to leave comments. Login now