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