##// END OF EJS Templates
blackbox: add backup bundle paths to blackbox logs...
Durham Goode -
r18766:64b55625 default
parent child Browse files
Show More
@@ -1,183 +1,184 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 from mercurial import changegroup
9 from mercurial import changegroup
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 import errno
13 import errno
14
14
15 def _bundle(repo, bases, heads, node, suffix, compress=True):
15 def _bundle(repo, bases, heads, node, suffix, compress=True):
16 """create a bundle with the specified revisions as a backup"""
16 """create a bundle with the specified revisions as a backup"""
17 cg = repo.changegroupsubset(bases, heads, 'strip')
17 cg = repo.changegroupsubset(bases, heads, 'strip')
18 backupdir = repo.join("strip-backup")
18 backupdir = repo.join("strip-backup")
19 if not os.path.isdir(backupdir):
19 if not os.path.isdir(backupdir):
20 os.mkdir(backupdir)
20 os.mkdir(backupdir)
21 name = os.path.join(backupdir, "%s-%s.hg" % (short(node), suffix))
21 name = os.path.join(backupdir, "%s-%s.hg" % (short(node), suffix))
22 if compress:
22 if compress:
23 bundletype = "HG10BZ"
23 bundletype = "HG10BZ"
24 else:
24 else:
25 bundletype = "HG10UN"
25 bundletype = "HG10UN"
26 return changegroup.writebundle(cg, name, bundletype)
26 return changegroup.writebundle(cg, name, bundletype)
27
27
28 def _collectfiles(repo, striprev):
28 def _collectfiles(repo, striprev):
29 """find out the filelogs affected by the strip"""
29 """find out the filelogs affected by the strip"""
30 files = set()
30 files = set()
31
31
32 for x in xrange(striprev, len(repo)):
32 for x in xrange(striprev, len(repo)):
33 files.update(repo[x].files())
33 files.update(repo[x].files())
34
34
35 return sorted(files)
35 return sorted(files)
36
36
37 def _collectbrokencsets(repo, files, striprev):
37 def _collectbrokencsets(repo, files, striprev):
38 """return the changesets which will be broken by the truncation"""
38 """return the changesets which will be broken by the truncation"""
39 s = set()
39 s = set()
40 def collectone(revlog):
40 def collectone(revlog):
41 linkgen = (revlog.linkrev(i) for i in revlog)
41 linkgen = (revlog.linkrev(i) for i in revlog)
42 # find the truncation point of the revlog
42 # find the truncation point of the revlog
43 for lrev in linkgen:
43 for lrev in linkgen:
44 if lrev >= striprev:
44 if lrev >= striprev:
45 break
45 break
46 # see if any revision after this point has a linkrev
46 # see if any revision after this point has a linkrev
47 # less than striprev (those will be broken by strip)
47 # less than striprev (those will be broken by strip)
48 for lrev in linkgen:
48 for lrev in linkgen:
49 if lrev < striprev:
49 if lrev < striprev:
50 s.add(lrev)
50 s.add(lrev)
51
51
52 collectone(repo.manifest)
52 collectone(repo.manifest)
53 for fname in files:
53 for fname in files:
54 collectone(repo.file(fname))
54 collectone(repo.file(fname))
55
55
56 return s
56 return s
57
57
58 def strip(ui, repo, nodelist, backup="all", topic='backup'):
58 def strip(ui, repo, nodelist, backup="all", topic='backup'):
59 repo = repo.unfiltered()
59 repo = repo.unfiltered()
60 repo.destroying()
60 repo.destroying()
61
61
62 cl = repo.changelog
62 cl = repo.changelog
63 # TODO handle undo of merge sets
63 # TODO handle undo of merge sets
64 if isinstance(nodelist, str):
64 if isinstance(nodelist, str):
65 nodelist = [nodelist]
65 nodelist = [nodelist]
66 striplist = [cl.rev(node) for node in nodelist]
66 striplist = [cl.rev(node) for node in nodelist]
67 striprev = min(striplist)
67 striprev = min(striplist)
68
68
69 keeppartialbundle = backup == 'strip'
69 keeppartialbundle = backup == 'strip'
70
70
71 # Some revisions with rev > striprev may not be descendants of striprev.
71 # Some revisions with rev > striprev may not be descendants of striprev.
72 # We have to find these revisions and put them in a bundle, so that
72 # We have to find these revisions and put them in a bundle, so that
73 # we can restore them after the truncations.
73 # we can restore them after the truncations.
74 # To create the bundle we use repo.changegroupsubset which requires
74 # To create the bundle we use repo.changegroupsubset which requires
75 # the list of heads and bases of the set of interesting revisions.
75 # the list of heads and bases of the set of interesting revisions.
76 # (head = revision in the set that has no descendant in the set;
76 # (head = revision in the set that has no descendant in the set;
77 # base = revision in the set that has no ancestor in the set)
77 # base = revision in the set that has no ancestor in the set)
78 tostrip = set(striplist)
78 tostrip = set(striplist)
79 for rev in striplist:
79 for rev in striplist:
80 for desc in cl.descendants([rev]):
80 for desc in cl.descendants([rev]):
81 tostrip.add(desc)
81 tostrip.add(desc)
82
82
83 files = _collectfiles(repo, striprev)
83 files = _collectfiles(repo, striprev)
84 saverevs = _collectbrokencsets(repo, files, striprev)
84 saverevs = _collectbrokencsets(repo, files, striprev)
85
85
86 # compute heads
86 # compute heads
87 saveheads = set(saverevs)
87 saveheads = set(saverevs)
88 for r in xrange(striprev + 1, len(cl)):
88 for r in xrange(striprev + 1, len(cl)):
89 if r not in tostrip:
89 if r not in tostrip:
90 saverevs.add(r)
90 saverevs.add(r)
91 saveheads.difference_update(cl.parentrevs(r))
91 saveheads.difference_update(cl.parentrevs(r))
92 saveheads.add(r)
92 saveheads.add(r)
93 saveheads = [cl.node(r) for r in saveheads]
93 saveheads = [cl.node(r) for r in saveheads]
94
94
95 # compute base nodes
95 # compute base nodes
96 if saverevs:
96 if saverevs:
97 descendants = set(cl.descendants(saverevs))
97 descendants = set(cl.descendants(saverevs))
98 saverevs.difference_update(descendants)
98 saverevs.difference_update(descendants)
99 savebases = [cl.node(r) for r in saverevs]
99 savebases = [cl.node(r) for r in saverevs]
100 stripbases = [cl.node(r) for r in tostrip]
100 stripbases = [cl.node(r) for r in tostrip]
101
101
102 # For a set s, max(parents(s) - s) is the same as max(heads(::s - s)), but
102 # For a set s, max(parents(s) - s) is the same as max(heads(::s - s)), but
103 # is much faster
103 # is much faster
104 newbmtarget = repo.revs('max(parents(%ld) - (%ld))', tostrip, tostrip)
104 newbmtarget = repo.revs('max(parents(%ld) - (%ld))', tostrip, tostrip)
105 if newbmtarget:
105 if newbmtarget:
106 newbmtarget = repo[newbmtarget[0]].node()
106 newbmtarget = repo[newbmtarget[0]].node()
107 else:
107 else:
108 newbmtarget = '.'
108 newbmtarget = '.'
109
109
110 bm = repo._bookmarks
110 bm = repo._bookmarks
111 updatebm = []
111 updatebm = []
112 for m in bm:
112 for m in bm:
113 rev = repo[bm[m]].rev()
113 rev = repo[bm[m]].rev()
114 if rev in tostrip:
114 if rev in tostrip:
115 updatebm.append(m)
115 updatebm.append(m)
116
116
117 # create a changegroup for all the branches we need to keep
117 # create a changegroup for all the branches we need to keep
118 backupfile = None
118 backupfile = None
119 if backup == "all":
119 if backup == "all":
120 backupfile = _bundle(repo, stripbases, cl.heads(), node, topic)
120 backupfile = _bundle(repo, stripbases, cl.heads(), node, topic)
121 repo.ui.status(_("saved backup bundle to %s\n") % backupfile)
121 repo.ui.status(_("saved backup bundle to %s\n") % backupfile)
122 repo.ui.log("backupbundle", "saved backup bundle to %s\n", backupfile)
122 if saveheads or savebases:
123 if saveheads or savebases:
123 # do not compress partial bundle if we remove it from disk later
124 # do not compress partial bundle if we remove it from disk later
124 chgrpfile = _bundle(repo, savebases, saveheads, node, 'temp',
125 chgrpfile = _bundle(repo, savebases, saveheads, node, 'temp',
125 compress=keeppartialbundle)
126 compress=keeppartialbundle)
126
127
127 mfst = repo.manifest
128 mfst = repo.manifest
128
129
129 tr = repo.transaction("strip")
130 tr = repo.transaction("strip")
130 offset = len(tr.entries)
131 offset = len(tr.entries)
131
132
132 try:
133 try:
133 tr.startgroup()
134 tr.startgroup()
134 cl.strip(striprev, tr)
135 cl.strip(striprev, tr)
135 mfst.strip(striprev, tr)
136 mfst.strip(striprev, tr)
136 for fn in files:
137 for fn in files:
137 repo.file(fn).strip(striprev, tr)
138 repo.file(fn).strip(striprev, tr)
138 tr.endgroup()
139 tr.endgroup()
139
140
140 try:
141 try:
141 for i in xrange(offset, len(tr.entries)):
142 for i in xrange(offset, len(tr.entries)):
142 file, troffset, ignore = tr.entries[i]
143 file, troffset, ignore = tr.entries[i]
143 repo.sopener(file, 'a').truncate(troffset)
144 repo.sopener(file, 'a').truncate(troffset)
144 tr.close()
145 tr.close()
145 except: # re-raises
146 except: # re-raises
146 tr.abort()
147 tr.abort()
147 raise
148 raise
148
149
149 if saveheads or savebases:
150 if saveheads or savebases:
150 ui.note(_("adding branch\n"))
151 ui.note(_("adding branch\n"))
151 f = open(chgrpfile, "rb")
152 f = open(chgrpfile, "rb")
152 gen = changegroup.readbundle(f, chgrpfile)
153 gen = changegroup.readbundle(f, chgrpfile)
153 if not repo.ui.verbose:
154 if not repo.ui.verbose:
154 # silence internal shuffling chatter
155 # silence internal shuffling chatter
155 repo.ui.pushbuffer()
156 repo.ui.pushbuffer()
156 repo.addchangegroup(gen, 'strip', 'bundle:' + chgrpfile, True)
157 repo.addchangegroup(gen, 'strip', 'bundle:' + chgrpfile, True)
157 if not repo.ui.verbose:
158 if not repo.ui.verbose:
158 repo.ui.popbuffer()
159 repo.ui.popbuffer()
159 f.close()
160 f.close()
160 if not keeppartialbundle:
161 if not keeppartialbundle:
161 os.unlink(chgrpfile)
162 os.unlink(chgrpfile)
162
163
163 # remove undo files
164 # remove undo files
164 for undofile in repo.undofiles():
165 for undofile in repo.undofiles():
165 try:
166 try:
166 os.unlink(undofile)
167 os.unlink(undofile)
167 except OSError, e:
168 except OSError, e:
168 if e.errno != errno.ENOENT:
169 if e.errno != errno.ENOENT:
169 ui.warn(_('error removing %s: %s\n') % (undofile, str(e)))
170 ui.warn(_('error removing %s: %s\n') % (undofile, str(e)))
170
171
171 for m in updatebm:
172 for m in updatebm:
172 bm[m] = repo[newbmtarget].node()
173 bm[m] = repo[newbmtarget].node()
173 bm.write()
174 bm.write()
174 except: # re-raises
175 except: # re-raises
175 if backupfile:
176 if backupfile:
176 ui.warn(_("strip failed, full bundle stored in '%s'\n")
177 ui.warn(_("strip failed, full bundle stored in '%s'\n")
177 % backupfile)
178 % backupfile)
178 elif saveheads:
179 elif saveheads:
179 ui.warn(_("strip failed, partial bundle stored in '%s'\n")
180 ui.warn(_("strip failed, partial bundle stored in '%s'\n")
180 % chgrpfile)
181 % chgrpfile)
181 raise
182 raise
182
183
183 repo.destroyed()
184 repo.destroyed()
@@ -1,79 +1,94 b''
1 setup
1 setup
2 $ cat > mock.py <<EOF
2 $ cat > mock.py <<EOF
3 > from mercurial import util
3 > from mercurial import util
4 > import getpass
4 > import getpass
5 >
5 >
6 > def makedate():
6 > def makedate():
7 > return 0, 0
7 > return 0, 0
8 > def getuser():
8 > def getuser():
9 > return 'bob'
9 > return 'bob'
10 > # mock the date and user apis so the output is always the same
10 > # mock the date and user apis so the output is always the same
11 > def uisetup(ui):
11 > def uisetup(ui):
12 > util.makedate = makedate
12 > util.makedate = makedate
13 > getpass.getuser = getuser
13 > getpass.getuser = getuser
14 > EOF
14 > EOF
15 $ cat >> $HGRCPATH <<EOF
15 $ cat >> $HGRCPATH <<EOF
16 > [extensions]
16 > [extensions]
17 > blackbox=
17 > blackbox=
18 > mock=`pwd`/mock.py
18 > mock=`pwd`/mock.py
19 > mq=
19 > EOF
20 > EOF
20 $ hg init blackboxtest
21 $ hg init blackboxtest
21 $ cd blackboxtest
22 $ cd blackboxtest
22
23
23 command, exit codes, and duration
24 command, exit codes, and duration
24
25
25 $ echo a > a
26 $ echo a > a
26 $ hg add a
27 $ hg add a
27 $ hg blackbox
28 $ hg blackbox
28 1970/01/01 00:00:00 bob> add a
29 1970/01/01 00:00:00 bob> add a
29 1970/01/01 00:00:00 bob> add exited 0 after * seconds (glob)
30 1970/01/01 00:00:00 bob> add exited 0 after * seconds (glob)
30
31
31 incoming change tracking
32 incoming change tracking
32
33
33 create two heads to verify that we only see one change in the log later
34 create two heads to verify that we only see one change in the log later
34 $ hg commit -ma
35 $ hg commit -ma
35 $ hg up null
36 $ hg up null
36 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
37 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
37 $ echo b > b
38 $ echo b > b
38 $ hg commit -Amb
39 $ hg commit -Amb
39 adding b
40 adding b
40 created new head
41 created new head
41
42
42 clone, commit, pull
43 clone, commit, pull
43 $ hg clone . ../blackboxtest2
44 $ hg clone . ../blackboxtest2
44 updating to branch default
45 updating to branch default
45 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
46 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
46 $ echo c > c
47 $ echo c > c
47 $ hg commit -Amc
48 $ hg commit -Amc
48 adding c
49 adding c
49 $ cd ../blackboxtest2
50 $ cd ../blackboxtest2
50 $ hg pull
51 $ hg pull
51 pulling from $TESTTMP/blackboxtest (glob)
52 pulling from $TESTTMP/blackboxtest (glob)
52 searching for changes
53 searching for changes
53 adding changesets
54 adding changesets
54 adding manifests
55 adding manifests
55 adding file changes
56 adding file changes
56 added 1 changesets with 1 changes to 1 files
57 added 1 changesets with 1 changes to 1 files
57 (run 'hg update' to get a working copy)
58 (run 'hg update' to get a working copy)
58 $ hg blackbox -l 3
59 $ hg blackbox -l 3
59 1970/01/01 00:00:00 bob> pull
60 1970/01/01 00:00:00 bob> pull
60 1970/01/01 00:00:00 bob> 1 incoming changes - new heads: d02f48003e62
61 1970/01/01 00:00:00 bob> 1 incoming changes - new heads: d02f48003e62
61 1970/01/01 00:00:00 bob> pull exited None after * seconds (glob)
62 1970/01/01 00:00:00 bob> pull exited None after * seconds (glob)
62
63
64 backup bundles get logged
65
66 $ touch d
67 $ hg commit -Amd
68 adding d
69 created new head
70 $ hg strip tip
71 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
72 saved backup bundle to $TESTTMP/blackboxtest2/.hg/strip-backup/*-backup.hg (glob)
73 $ hg blackbox -l 3
74 1970/01/01 00:00:00 bob> strip tip
75 1970/01/01 00:00:00 bob> saved backup bundle to $TESTTMP/blackboxtest2/.hg/strip-backup/*-backup.hg (glob)
76 1970/01/01 00:00:00 bob> strip exited 0 after * seconds (glob)
77
63 extension and python hooks - use the eol extension for a pythonhook
78 extension and python hooks - use the eol extension for a pythonhook
64
79
65 $ echo '[extensions]' >> .hg/hgrc
80 $ echo '[extensions]' >> .hg/hgrc
66 $ echo 'eol=' >> .hg/hgrc
81 $ echo 'eol=' >> .hg/hgrc
67 $ echo '[hooks]' >> .hg/hgrc
82 $ echo '[hooks]' >> .hg/hgrc
68 $ echo 'update = echo hooked' >> .hg/hgrc
83 $ echo 'update = echo hooked' >> .hg/hgrc
69 $ hg update
84 $ hg update
70 hooked
85 hooked
71 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
86 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
72 $ hg blackbox -l 4
87 $ hg blackbox -l 4
73 1970/01/01 00:00:00 bob> update
88 1970/01/01 00:00:00 bob> update
74 1970/01/01 00:00:00 bob> pythonhook-preupdate: hgext.eol.preupdate finished in * seconds (glob)
89 1970/01/01 00:00:00 bob> pythonhook-preupdate: hgext.eol.preupdate finished in * seconds (glob)
75 1970/01/01 00:00:00 bob> exthook-update: echo hooked finished in * seconds (glob)
90 1970/01/01 00:00:00 bob> exthook-update: echo hooked finished in * seconds (glob)
76 1970/01/01 00:00:00 bob> update exited False after * seconds (glob)
91 1970/01/01 00:00:00 bob> update exited False after * seconds (glob)
77
92
78 cleanup
93 cleanup
79 $ cd ..
94 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now