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