Show More
@@ -1,1009 +1,1014 b'' | |||
|
1 | 1 | # changegroup.py - Mercurial changegroup manipulation functions |
|
2 | 2 | # |
|
3 | 3 | # Copyright 2006 Matt Mackall <mpm@selenic.com> |
|
4 | 4 | # |
|
5 | 5 | # This software may be used and distributed according to the terms of the |
|
6 | 6 | # GNU General Public License version 2 or any later version. |
|
7 | 7 | |
|
8 | 8 | from __future__ import absolute_import |
|
9 | 9 | |
|
10 | 10 | import os |
|
11 | 11 | import struct |
|
12 | 12 | import tempfile |
|
13 | 13 | import weakref |
|
14 | 14 | |
|
15 | 15 | from .i18n import _ |
|
16 | 16 | from .node import ( |
|
17 | 17 | hex, |
|
18 | 18 | nullrev, |
|
19 | 19 | short, |
|
20 | 20 | ) |
|
21 | 21 | |
|
22 | 22 | from . import ( |
|
23 | 23 | dagutil, |
|
24 | 24 | discovery, |
|
25 | 25 | error, |
|
26 | 26 | mdiff, |
|
27 | 27 | phases, |
|
28 | 28 | pycompat, |
|
29 | 29 | util, |
|
30 | 30 | ) |
|
31 | 31 | |
|
32 | 32 | _CHANGEGROUPV1_DELTA_HEADER = "20s20s20s20s" |
|
33 | 33 | _CHANGEGROUPV2_DELTA_HEADER = "20s20s20s20s20s" |
|
34 | 34 | _CHANGEGROUPV3_DELTA_HEADER = ">20s20s20s20s20sH" |
|
35 | 35 | |
|
36 | 36 | def readexactly(stream, n): |
|
37 | 37 | '''read n bytes from stream.read and abort if less was available''' |
|
38 | 38 | s = stream.read(n) |
|
39 | 39 | if len(s) < n: |
|
40 | 40 | raise error.Abort(_("stream ended unexpectedly" |
|
41 | 41 | " (got %d bytes, expected %d)") |
|
42 | 42 | % (len(s), n)) |
|
43 | 43 | return s |
|
44 | 44 | |
|
45 | 45 | def getchunk(stream): |
|
46 | 46 | """return the next chunk from stream as a string""" |
|
47 | 47 | d = readexactly(stream, 4) |
|
48 | 48 | l = struct.unpack(">l", d)[0] |
|
49 | 49 | if l <= 4: |
|
50 | 50 | if l: |
|
51 | 51 | raise error.Abort(_("invalid chunk length %d") % l) |
|
52 | 52 | return "" |
|
53 | 53 | return readexactly(stream, l - 4) |
|
54 | 54 | |
|
55 | 55 | def chunkheader(length): |
|
56 | 56 | """return a changegroup chunk header (string)""" |
|
57 | 57 | return struct.pack(">l", length + 4) |
|
58 | 58 | |
|
59 | 59 | def closechunk(): |
|
60 | 60 | """return a changegroup chunk header (string) for a zero-length chunk""" |
|
61 | 61 | return struct.pack(">l", 0) |
|
62 | 62 | |
|
63 | 63 | def writechunks(ui, chunks, filename, vfs=None): |
|
64 | 64 | """Write chunks to a file and return its filename. |
|
65 | 65 | |
|
66 | 66 | The stream is assumed to be a bundle file. |
|
67 | 67 | Existing files will not be overwritten. |
|
68 | 68 | If no filename is specified, a temporary file is created. |
|
69 | 69 | """ |
|
70 | 70 | fh = None |
|
71 | 71 | cleanup = None |
|
72 | 72 | try: |
|
73 | 73 | if filename: |
|
74 | 74 | if vfs: |
|
75 | 75 | fh = vfs.open(filename, "wb") |
|
76 | 76 | else: |
|
77 | 77 | # Increase default buffer size because default is usually |
|
78 | 78 | # small (4k is common on Linux). |
|
79 | 79 | fh = open(filename, "wb", 131072) |
|
80 | 80 | else: |
|
81 | 81 | fd, filename = tempfile.mkstemp(prefix="hg-bundle-", suffix=".hg") |
|
82 | 82 | fh = os.fdopen(fd, pycompat.sysstr("wb")) |
|
83 | 83 | cleanup = filename |
|
84 | 84 | for c in chunks: |
|
85 | 85 | fh.write(c) |
|
86 | 86 | cleanup = None |
|
87 | 87 | return filename |
|
88 | 88 | finally: |
|
89 | 89 | if fh is not None: |
|
90 | 90 | fh.close() |
|
91 | 91 | if cleanup is not None: |
|
92 | 92 | if filename and vfs: |
|
93 | 93 | vfs.unlink(cleanup) |
|
94 | 94 | else: |
|
95 | 95 | os.unlink(cleanup) |
|
96 | 96 | |
|
97 | 97 | class cg1unpacker(object): |
|
98 | 98 | """Unpacker for cg1 changegroup streams. |
|
99 | 99 | |
|
100 | 100 | A changegroup unpacker handles the framing of the revision data in |
|
101 | 101 | the wire format. Most consumers will want to use the apply() |
|
102 | 102 | method to add the changes from the changegroup to a repository. |
|
103 | 103 | |
|
104 | 104 | If you're forwarding a changegroup unmodified to another consumer, |
|
105 | 105 | use getchunks(), which returns an iterator of changegroup |
|
106 | 106 | chunks. This is mostly useful for cases where you need to know the |
|
107 | 107 | data stream has ended by observing the end of the changegroup. |
|
108 | 108 | |
|
109 | 109 | deltachunk() is useful only if you're applying delta data. Most |
|
110 | 110 | consumers should prefer apply() instead. |
|
111 | 111 | |
|
112 | 112 | A few other public methods exist. Those are used only for |
|
113 | 113 | bundlerepo and some debug commands - their use is discouraged. |
|
114 | 114 | """ |
|
115 | 115 | deltaheader = _CHANGEGROUPV1_DELTA_HEADER |
|
116 | 116 | deltaheadersize = struct.calcsize(deltaheader) |
|
117 | 117 | version = '01' |
|
118 | 118 | _grouplistcount = 1 # One list of files after the manifests |
|
119 | 119 | |
|
120 | 120 | def __init__(self, fh, alg, extras=None): |
|
121 | 121 | if alg is None: |
|
122 | 122 | alg = 'UN' |
|
123 | 123 | if alg not in util.compengines.supportedbundletypes: |
|
124 | 124 | raise error.Abort(_('unknown stream compression type: %s') |
|
125 | 125 | % alg) |
|
126 | 126 | if alg == 'BZ': |
|
127 | 127 | alg = '_truncatedBZ' |
|
128 | 128 | |
|
129 | 129 | compengine = util.compengines.forbundletype(alg) |
|
130 | 130 | self._stream = compengine.decompressorreader(fh) |
|
131 | 131 | self._type = alg |
|
132 | 132 | self.extras = extras or {} |
|
133 | 133 | self.callback = None |
|
134 | 134 | |
|
135 | 135 | # These methods (compressed, read, seek, tell) all appear to only |
|
136 | 136 | # be used by bundlerepo, but it's a little hard to tell. |
|
137 | 137 | def compressed(self): |
|
138 | 138 | return self._type is not None and self._type != 'UN' |
|
139 | 139 | def read(self, l): |
|
140 | 140 | return self._stream.read(l) |
|
141 | 141 | def seek(self, pos): |
|
142 | 142 | return self._stream.seek(pos) |
|
143 | 143 | def tell(self): |
|
144 | 144 | return self._stream.tell() |
|
145 | 145 | def close(self): |
|
146 | 146 | return self._stream.close() |
|
147 | 147 | |
|
148 | 148 | def _chunklength(self): |
|
149 | 149 | d = readexactly(self._stream, 4) |
|
150 | 150 | l = struct.unpack(">l", d)[0] |
|
151 | 151 | if l <= 4: |
|
152 | 152 | if l: |
|
153 | 153 | raise error.Abort(_("invalid chunk length %d") % l) |
|
154 | 154 | return 0 |
|
155 | 155 | if self.callback: |
|
156 | 156 | self.callback() |
|
157 | 157 | return l - 4 |
|
158 | 158 | |
|
159 | 159 | def changelogheader(self): |
|
160 | 160 | """v10 does not have a changelog header chunk""" |
|
161 | 161 | return {} |
|
162 | 162 | |
|
163 | 163 | def manifestheader(self): |
|
164 | 164 | """v10 does not have a manifest header chunk""" |
|
165 | 165 | return {} |
|
166 | 166 | |
|
167 | 167 | def filelogheader(self): |
|
168 | 168 | """return the header of the filelogs chunk, v10 only has the filename""" |
|
169 | 169 | l = self._chunklength() |
|
170 | 170 | if not l: |
|
171 | 171 | return {} |
|
172 | 172 | fname = readexactly(self._stream, l) |
|
173 | 173 | return {'filename': fname} |
|
174 | 174 | |
|
175 | 175 | def _deltaheader(self, headertuple, prevnode): |
|
176 | 176 | node, p1, p2, cs = headertuple |
|
177 | 177 | if prevnode is None: |
|
178 | 178 | deltabase = p1 |
|
179 | 179 | else: |
|
180 | 180 | deltabase = prevnode |
|
181 | 181 | flags = 0 |
|
182 | 182 | return node, p1, p2, deltabase, cs, flags |
|
183 | 183 | |
|
184 | 184 | def deltachunk(self, prevnode): |
|
185 | 185 | l = self._chunklength() |
|
186 | 186 | if not l: |
|
187 | 187 | return {} |
|
188 | 188 | headerdata = readexactly(self._stream, self.deltaheadersize) |
|
189 | 189 | header = struct.unpack(self.deltaheader, headerdata) |
|
190 | 190 | delta = readexactly(self._stream, l - self.deltaheadersize) |
|
191 | 191 | node, p1, p2, deltabase, cs, flags = self._deltaheader(header, prevnode) |
|
192 | 192 | return {'node': node, 'p1': p1, 'p2': p2, 'cs': cs, |
|
193 | 193 | 'deltabase': deltabase, 'delta': delta, 'flags': flags} |
|
194 | 194 | |
|
195 | 195 | def getchunks(self): |
|
196 | 196 | """returns all the chunks contains in the bundle |
|
197 | 197 | |
|
198 | 198 | Used when you need to forward the binary stream to a file or another |
|
199 | 199 | network API. To do so, it parse the changegroup data, otherwise it will |
|
200 | 200 | block in case of sshrepo because it don't know the end of the stream. |
|
201 | 201 | """ |
|
202 | 202 | # an empty chunkgroup is the end of the changegroup |
|
203 | 203 | # a changegroup has at least 2 chunkgroups (changelog and manifest). |
|
204 | 204 | # after that, changegroup versions 1 and 2 have a series of groups |
|
205 | 205 | # with one group per file. changegroup 3 has a series of directory |
|
206 | 206 | # manifests before the files. |
|
207 | 207 | count = 0 |
|
208 | 208 | emptycount = 0 |
|
209 | 209 | while emptycount < self._grouplistcount: |
|
210 | 210 | empty = True |
|
211 | 211 | count += 1 |
|
212 | 212 | while True: |
|
213 | 213 | chunk = getchunk(self) |
|
214 | 214 | if not chunk: |
|
215 | 215 | if empty and count > 2: |
|
216 | 216 | emptycount += 1 |
|
217 | 217 | break |
|
218 | 218 | empty = False |
|
219 | 219 | yield chunkheader(len(chunk)) |
|
220 | 220 | pos = 0 |
|
221 | 221 | while pos < len(chunk): |
|
222 | 222 | next = pos + 2**20 |
|
223 | 223 | yield chunk[pos:next] |
|
224 | 224 | pos = next |
|
225 | 225 | yield closechunk() |
|
226 | 226 | |
|
227 | 227 | def _unpackmanifests(self, repo, revmap, trp, prog, numchanges): |
|
228 | 228 | # We know that we'll never have more manifests than we had |
|
229 | 229 | # changesets. |
|
230 | 230 | self.callback = prog(_('manifests'), numchanges) |
|
231 | 231 | # no need to check for empty manifest group here: |
|
232 | 232 | # if the result of the merge of 1 and 2 is the same in 3 and 4, |
|
233 | 233 | # no new manifest will be created and the manifest group will |
|
234 | 234 | # be empty during the pull |
|
235 | 235 | self.manifestheader() |
|
236 | 236 | repo.manifestlog._revlog.addgroup(self, revmap, trp) |
|
237 | 237 | repo.ui.progress(_('manifests'), None) |
|
238 | 238 | self.callback = None |
|
239 | 239 | |
|
240 | 240 | def apply(self, repo, tr, srctype, url, targetphase=phases.draft, |
|
241 | 241 | expectedtotal=None): |
|
242 | 242 | """Add the changegroup returned by source.read() to this repo. |
|
243 | 243 | srctype is a string like 'push', 'pull', or 'unbundle'. url is |
|
244 | 244 | the URL of the repo where this changegroup is coming from. |
|
245 | 245 | |
|
246 | 246 | Return an integer summarizing the change to this repo: |
|
247 | 247 | - nothing changed or no source: 0 |
|
248 | 248 | - more heads than before: 1+added heads (2..n) |
|
249 | 249 | - fewer heads than before: -1-removed heads (-2..-n) |
|
250 | 250 | - number of heads stays the same: 1 |
|
251 | 251 | """ |
|
252 | 252 | repo = repo.unfiltered() |
|
253 | 253 | def csmap(x): |
|
254 | 254 | repo.ui.debug("add changeset %s\n" % short(x)) |
|
255 | 255 | return len(cl) |
|
256 | 256 | |
|
257 | 257 | def revmap(x): |
|
258 | 258 | return cl.rev(x) |
|
259 | 259 | |
|
260 | 260 | changesets = files = revisions = 0 |
|
261 | 261 | |
|
262 | 262 | try: |
|
263 | 263 | # The transaction may already carry source information. In this |
|
264 | 264 | # case we use the top level data. We overwrite the argument |
|
265 | 265 | # because we need to use the top level value (if they exist) |
|
266 | 266 | # in this function. |
|
267 | 267 | srctype = tr.hookargs.setdefault('source', srctype) |
|
268 | 268 | url = tr.hookargs.setdefault('url', url) |
|
269 | 269 | repo.hook('prechangegroup', throw=True, **tr.hookargs) |
|
270 | 270 | |
|
271 | 271 | # write changelog data to temp files so concurrent readers |
|
272 | 272 | # will not see an inconsistent view |
|
273 | 273 | cl = repo.changelog |
|
274 | 274 | cl.delayupdate(tr) |
|
275 | 275 | oldheads = set(cl.heads()) |
|
276 | 276 | |
|
277 | 277 | trp = weakref.proxy(tr) |
|
278 | 278 | # pull off the changeset group |
|
279 | 279 | repo.ui.status(_("adding changesets\n")) |
|
280 | 280 | clstart = len(cl) |
|
281 | 281 | class prog(object): |
|
282 | 282 | def __init__(self, step, total): |
|
283 | 283 | self._step = step |
|
284 | 284 | self._total = total |
|
285 | 285 | self._count = 1 |
|
286 | 286 | def __call__(self): |
|
287 | 287 | repo.ui.progress(self._step, self._count, unit=_('chunks'), |
|
288 | 288 | total=self._total) |
|
289 | 289 | self._count += 1 |
|
290 | 290 | self.callback = prog(_('changesets'), expectedtotal) |
|
291 | 291 | |
|
292 | 292 | efiles = set() |
|
293 | 293 | def onchangelog(cl, node): |
|
294 | 294 | efiles.update(cl.readfiles(node)) |
|
295 | 295 | |
|
296 | 296 | self.changelogheader() |
|
297 | 297 | cgnodes = cl.addgroup(self, csmap, trp, addrevisioncb=onchangelog) |
|
298 | 298 | efiles = len(efiles) |
|
299 | 299 | |
|
300 | 300 | if not cgnodes: |
|
301 | 301 | repo.ui.develwarn('applied empty changegroup', |
|
302 | 302 | config='empty-changegroup') |
|
303 | 303 | clend = len(cl) |
|
304 | 304 | changesets = clend - clstart |
|
305 | 305 | repo.ui.progress(_('changesets'), None) |
|
306 | 306 | self.callback = None |
|
307 | 307 | |
|
308 | 308 | # pull off the manifest group |
|
309 | 309 | repo.ui.status(_("adding manifests\n")) |
|
310 | 310 | self._unpackmanifests(repo, revmap, trp, prog, changesets) |
|
311 | 311 | |
|
312 | 312 | needfiles = {} |
|
313 | 313 | if repo.ui.configbool('server', 'validate'): |
|
314 | 314 | cl = repo.changelog |
|
315 | 315 | ml = repo.manifestlog |
|
316 | 316 | # validate incoming csets have their manifests |
|
317 | 317 | for cset in xrange(clstart, clend): |
|
318 | 318 | mfnode = cl.changelogrevision(cset).manifest |
|
319 | 319 | mfest = ml[mfnode].readdelta() |
|
320 | 320 | # store file cgnodes we must see |
|
321 | 321 | for f, n in mfest.iteritems(): |
|
322 | 322 | needfiles.setdefault(f, set()).add(n) |
|
323 | 323 | |
|
324 | 324 | # process the files |
|
325 | 325 | repo.ui.status(_("adding file changes\n")) |
|
326 | 326 | newrevs, newfiles = _addchangegroupfiles( |
|
327 | 327 | repo, self, revmap, trp, efiles, needfiles) |
|
328 | 328 | revisions += newrevs |
|
329 | 329 | files += newfiles |
|
330 | 330 | |
|
331 | 331 | deltaheads = 0 |
|
332 | 332 | if oldheads: |
|
333 | 333 | heads = cl.heads() |
|
334 | 334 | deltaheads = len(heads) - len(oldheads) |
|
335 | 335 | for h in heads: |
|
336 | 336 | if h not in oldheads and repo[h].closesbranch(): |
|
337 | 337 | deltaheads -= 1 |
|
338 | 338 | htext = "" |
|
339 | 339 | if deltaheads: |
|
340 | 340 | htext = _(" (%+d heads)") % deltaheads |
|
341 | 341 | |
|
342 | 342 | repo.ui.status(_("added %d changesets" |
|
343 | 343 | " with %d changes to %d files%s\n") |
|
344 | 344 | % (changesets, revisions, files, htext)) |
|
345 | 345 | repo.invalidatevolatilesets() |
|
346 | 346 | |
|
347 | 347 | if changesets > 0: |
|
348 | 348 | if 'node' not in tr.hookargs: |
|
349 | 349 | tr.hookargs['node'] = hex(cl.node(clstart)) |
|
350 | 350 | tr.hookargs['node_last'] = hex(cl.node(clend - 1)) |
|
351 | 351 | hookargs = dict(tr.hookargs) |
|
352 | 352 | else: |
|
353 | 353 | hookargs = dict(tr.hookargs) |
|
354 | 354 | hookargs['node'] = hex(cl.node(clstart)) |
|
355 | 355 | hookargs['node_last'] = hex(cl.node(clend - 1)) |
|
356 | 356 | repo.hook('pretxnchangegroup', throw=True, **hookargs) |
|
357 | 357 | |
|
358 | 358 | added = [cl.node(r) for r in xrange(clstart, clend)] |
|
359 | 359 | phaseall = None |
|
360 | 360 | if srctype in ('push', 'serve'): |
|
361 | 361 | # Old servers can not push the boundary themselves. |
|
362 | 362 | # New servers won't push the boundary if changeset already |
|
363 | 363 | # exists locally as secret |
|
364 | 364 | # |
|
365 | 365 | # We should not use added here but the list of all change in |
|
366 | 366 | # the bundle |
|
367 | 367 | if repo.publishing(): |
|
368 | 368 | targetphase = phaseall = phases.public |
|
369 | 369 | else: |
|
370 | 370 | # closer target phase computation |
|
371 | 371 | |
|
372 | 372 | # Those changesets have been pushed from the |
|
373 | 373 | # outside, their phases are going to be pushed |
|
374 | 374 | # alongside. Therefor `targetphase` is |
|
375 | 375 | # ignored. |
|
376 | 376 | targetphase = phaseall = phases.draft |
|
377 | 377 | if added: |
|
378 | 378 | phases.registernew(repo, tr, targetphase, added) |
|
379 | 379 | if phaseall is not None: |
|
380 | 380 | phases.advanceboundary(repo, tr, phaseall, cgnodes) |
|
381 | 381 | |
|
382 | 382 | if changesets > 0: |
|
383 | 383 | |
|
384 | 384 | def runhooks(): |
|
385 | 385 | # These hooks run when the lock releases, not when the |
|
386 | 386 | # transaction closes. So it's possible for the changelog |
|
387 | 387 | # to have changed since we last saw it. |
|
388 | 388 | if clstart >= len(repo): |
|
389 | 389 | return |
|
390 | 390 | |
|
391 | 391 | repo.hook("changegroup", **hookargs) |
|
392 | 392 | |
|
393 | 393 | for n in added: |
|
394 | 394 | args = hookargs.copy() |
|
395 | 395 | args['node'] = hex(n) |
|
396 | 396 | del args['node_last'] |
|
397 | 397 | repo.hook("incoming", **args) |
|
398 | 398 | |
|
399 | 399 | newheads = [h for h in repo.heads() |
|
400 | 400 | if h not in oldheads] |
|
401 | 401 | repo.ui.log("incoming", |
|
402 | 402 | "%s incoming changes - new heads: %s\n", |
|
403 | 403 | len(added), |
|
404 | 404 | ', '.join([hex(c[:6]) for c in newheads])) |
|
405 | 405 | |
|
406 | 406 | tr.addpostclose('changegroup-runhooks-%020i' % clstart, |
|
407 | 407 | lambda tr: repo._afterlock(runhooks)) |
|
408 | 408 | finally: |
|
409 | 409 | repo.ui.flush() |
|
410 | 410 | # never return 0 here: |
|
411 | 411 | if deltaheads < 0: |
|
412 | 412 | ret = deltaheads - 1 |
|
413 | 413 | else: |
|
414 | 414 | ret = deltaheads + 1 |
|
415 | 415 | return ret |
|
416 | 416 | |
|
417 | 417 | class cg2unpacker(cg1unpacker): |
|
418 | 418 | """Unpacker for cg2 streams. |
|
419 | 419 | |
|
420 | 420 | cg2 streams add support for generaldelta, so the delta header |
|
421 | 421 | format is slightly different. All other features about the data |
|
422 | 422 | remain the same. |
|
423 | 423 | """ |
|
424 | 424 | deltaheader = _CHANGEGROUPV2_DELTA_HEADER |
|
425 | 425 | deltaheadersize = struct.calcsize(deltaheader) |
|
426 | 426 | version = '02' |
|
427 | 427 | |
|
428 | 428 | def _deltaheader(self, headertuple, prevnode): |
|
429 | 429 | node, p1, p2, deltabase, cs = headertuple |
|
430 | 430 | flags = 0 |
|
431 | 431 | return node, p1, p2, deltabase, cs, flags |
|
432 | 432 | |
|
433 | 433 | class cg3unpacker(cg2unpacker): |
|
434 | 434 | """Unpacker for cg3 streams. |
|
435 | 435 | |
|
436 | 436 | cg3 streams add support for exchanging treemanifests and revlog |
|
437 | 437 | flags. It adds the revlog flags to the delta header and an empty chunk |
|
438 | 438 | separating manifests and files. |
|
439 | 439 | """ |
|
440 | 440 | deltaheader = _CHANGEGROUPV3_DELTA_HEADER |
|
441 | 441 | deltaheadersize = struct.calcsize(deltaheader) |
|
442 | 442 | version = '03' |
|
443 | 443 | _grouplistcount = 2 # One list of manifests and one list of files |
|
444 | 444 | |
|
445 | 445 | def _deltaheader(self, headertuple, prevnode): |
|
446 | 446 | node, p1, p2, deltabase, cs, flags = headertuple |
|
447 | 447 | return node, p1, p2, deltabase, cs, flags |
|
448 | 448 | |
|
449 | 449 | def _unpackmanifests(self, repo, revmap, trp, prog, numchanges): |
|
450 | 450 | super(cg3unpacker, self)._unpackmanifests(repo, revmap, trp, prog, |
|
451 | 451 | numchanges) |
|
452 | 452 | for chunkdata in iter(self.filelogheader, {}): |
|
453 | 453 | # If we get here, there are directory manifests in the changegroup |
|
454 | 454 | d = chunkdata["filename"] |
|
455 | 455 | repo.ui.debug("adding %s revisions\n" % d) |
|
456 | 456 | dirlog = repo.manifestlog._revlog.dirlog(d) |
|
457 | 457 | if not dirlog.addgroup(self, revmap, trp): |
|
458 | 458 | raise error.Abort(_("received dir revlog group is empty")) |
|
459 | 459 | |
|
460 | 460 | class headerlessfixup(object): |
|
461 | 461 | def __init__(self, fh, h): |
|
462 | 462 | self._h = h |
|
463 | 463 | self._fh = fh |
|
464 | 464 | def read(self, n): |
|
465 | 465 | if self._h: |
|
466 | 466 | d, self._h = self._h[:n], self._h[n:] |
|
467 | 467 | if len(d) < n: |
|
468 | 468 | d += readexactly(self._fh, n - len(d)) |
|
469 | 469 | return d |
|
470 | 470 | return readexactly(self._fh, n) |
|
471 | 471 | |
|
472 | 472 | class cg1packer(object): |
|
473 | 473 | deltaheader = _CHANGEGROUPV1_DELTA_HEADER |
|
474 | 474 | version = '01' |
|
475 | 475 | def __init__(self, repo, bundlecaps=None): |
|
476 | 476 | """Given a source repo, construct a bundler. |
|
477 | 477 | |
|
478 | 478 | bundlecaps is optional and can be used to specify the set of |
|
479 | 479 | capabilities which can be used to build the bundle. While bundlecaps is |
|
480 | 480 | unused in core Mercurial, extensions rely on this feature to communicate |
|
481 | 481 | capabilities to customize the changegroup packer. |
|
482 | 482 | """ |
|
483 | 483 | # Set of capabilities we can use to build the bundle. |
|
484 | 484 | if bundlecaps is None: |
|
485 | 485 | bundlecaps = set() |
|
486 | 486 | self._bundlecaps = bundlecaps |
|
487 | 487 | # experimental config: bundle.reorder |
|
488 | 488 | reorder = repo.ui.config('bundle', 'reorder') |
|
489 | 489 | if reorder == 'auto': |
|
490 | 490 | reorder = None |
|
491 | 491 | else: |
|
492 | 492 | reorder = util.parsebool(reorder) |
|
493 | 493 | self._repo = repo |
|
494 | 494 | self._reorder = reorder |
|
495 | 495 | self._progress = repo.ui.progress |
|
496 | 496 | if self._repo.ui.verbose and not self._repo.ui.debugflag: |
|
497 | 497 | self._verbosenote = self._repo.ui.note |
|
498 | 498 | else: |
|
499 | 499 | self._verbosenote = lambda s: None |
|
500 | 500 | |
|
501 | 501 | def close(self): |
|
502 | 502 | return closechunk() |
|
503 | 503 | |
|
504 | 504 | def fileheader(self, fname): |
|
505 | 505 | return chunkheader(len(fname)) + fname |
|
506 | 506 | |
|
507 | 507 | # Extracted both for clarity and for overriding in extensions. |
|
508 | 508 | def _sortgroup(self, revlog, nodelist, lookup): |
|
509 | 509 | """Sort nodes for change group and turn them into revnums.""" |
|
510 | 510 | # for generaldelta revlogs, we linearize the revs; this will both be |
|
511 | 511 | # much quicker and generate a much smaller bundle |
|
512 | 512 | if (revlog._generaldelta and self._reorder is None) or self._reorder: |
|
513 | 513 | dag = dagutil.revlogdag(revlog) |
|
514 | 514 | return dag.linearize(set(revlog.rev(n) for n in nodelist)) |
|
515 | 515 | else: |
|
516 | 516 | return sorted([revlog.rev(n) for n in nodelist]) |
|
517 | 517 | |
|
518 | 518 | def group(self, nodelist, revlog, lookup, units=None): |
|
519 | 519 | """Calculate a delta group, yielding a sequence of changegroup chunks |
|
520 | 520 | (strings). |
|
521 | 521 | |
|
522 | 522 | Given a list of changeset revs, return a set of deltas and |
|
523 | 523 | metadata corresponding to nodes. The first delta is |
|
524 | 524 | first parent(nodelist[0]) -> nodelist[0], the receiver is |
|
525 | 525 | guaranteed to have this parent as it has all history before |
|
526 | 526 | these changesets. In the case firstparent is nullrev the |
|
527 | 527 | changegroup starts with a full revision. |
|
528 | 528 | |
|
529 | 529 | If units is not None, progress detail will be generated, units specifies |
|
530 | 530 | the type of revlog that is touched (changelog, manifest, etc.). |
|
531 | 531 | """ |
|
532 | 532 | # if we don't have any revisions touched by these changesets, bail |
|
533 | 533 | if len(nodelist) == 0: |
|
534 | 534 | yield self.close() |
|
535 | 535 | return |
|
536 | 536 | |
|
537 | 537 | revs = self._sortgroup(revlog, nodelist, lookup) |
|
538 | 538 | |
|
539 | 539 | # add the parent of the first rev |
|
540 | 540 | p = revlog.parentrevs(revs[0])[0] |
|
541 | 541 | revs.insert(0, p) |
|
542 | 542 | |
|
543 | 543 | # build deltas |
|
544 | 544 | total = len(revs) - 1 |
|
545 | 545 | msgbundling = _('bundling') |
|
546 | 546 | for r in xrange(len(revs) - 1): |
|
547 | 547 | if units is not None: |
|
548 | 548 | self._progress(msgbundling, r + 1, unit=units, total=total) |
|
549 | 549 | prev, curr = revs[r], revs[r + 1] |
|
550 | 550 | linknode = lookup(revlog.node(curr)) |
|
551 | 551 | for c in self.revchunk(revlog, curr, prev, linknode): |
|
552 | 552 | yield c |
|
553 | 553 | |
|
554 | 554 | if units is not None: |
|
555 | 555 | self._progress(msgbundling, None) |
|
556 | 556 | yield self.close() |
|
557 | 557 | |
|
558 | 558 | # filter any nodes that claim to be part of the known set |
|
559 | 559 | def prune(self, revlog, missing, commonrevs): |
|
560 | 560 | rr, rl = revlog.rev, revlog.linkrev |
|
561 | 561 | return [n for n in missing if rl(rr(n)) not in commonrevs] |
|
562 | 562 | |
|
563 | 563 | def _packmanifests(self, dir, mfnodes, lookuplinknode): |
|
564 | 564 | """Pack flat manifests into a changegroup stream.""" |
|
565 | 565 | assert not dir |
|
566 | 566 | for chunk in self.group(mfnodes, self._repo.manifestlog._revlog, |
|
567 | 567 | lookuplinknode, units=_('manifests')): |
|
568 | 568 | yield chunk |
|
569 | 569 | |
|
570 | 570 | def _manifestsdone(self): |
|
571 | 571 | return '' |
|
572 | 572 | |
|
573 | 573 | def generate(self, commonrevs, clnodes, fastpathlinkrev, source): |
|
574 | 574 | '''yield a sequence of changegroup chunks (strings)''' |
|
575 | 575 | repo = self._repo |
|
576 | 576 | cl = repo.changelog |
|
577 | 577 | |
|
578 | 578 | clrevorder = {} |
|
579 | 579 | mfs = {} # needed manifests |
|
580 | 580 | fnodes = {} # needed file nodes |
|
581 | 581 | changedfiles = set() |
|
582 | 582 | |
|
583 | 583 | # Callback for the changelog, used to collect changed files and manifest |
|
584 | 584 | # nodes. |
|
585 | 585 | # Returns the linkrev node (identity in the changelog case). |
|
586 | 586 | def lookupcl(x): |
|
587 | 587 | c = cl.read(x) |
|
588 | 588 | clrevorder[x] = len(clrevorder) |
|
589 | 589 | n = c[0] |
|
590 | 590 | # record the first changeset introducing this manifest version |
|
591 | 591 | mfs.setdefault(n, x) |
|
592 | 592 | # Record a complete list of potentially-changed files in |
|
593 | 593 | # this manifest. |
|
594 | 594 | changedfiles.update(c[3]) |
|
595 | 595 | return x |
|
596 | 596 | |
|
597 | 597 | self._verbosenote(_('uncompressed size of bundle content:\n')) |
|
598 | 598 | size = 0 |
|
599 | 599 | for chunk in self.group(clnodes, cl, lookupcl, units=_('changesets')): |
|
600 | 600 | size += len(chunk) |
|
601 | 601 | yield chunk |
|
602 | 602 | self._verbosenote(_('%8.i (changelog)\n') % size) |
|
603 | 603 | |
|
604 | 604 | # We need to make sure that the linkrev in the changegroup refers to |
|
605 | 605 | # the first changeset that introduced the manifest or file revision. |
|
606 | 606 | # The fastpath is usually safer than the slowpath, because the filelogs |
|
607 | 607 | # are walked in revlog order. |
|
608 | 608 | # |
|
609 | 609 | # When taking the slowpath with reorder=None and the manifest revlog |
|
610 | 610 | # uses generaldelta, the manifest may be walked in the "wrong" order. |
|
611 | 611 | # Without 'clrevorder', we would get an incorrect linkrev (see fix in |
|
612 | 612 | # cc0ff93d0c0c). |
|
613 | 613 | # |
|
614 | 614 | # When taking the fastpath, we are only vulnerable to reordering |
|
615 | 615 | # of the changelog itself. The changelog never uses generaldelta, so |
|
616 | 616 | # it is only reordered when reorder=True. To handle this case, we |
|
617 | 617 | # simply take the slowpath, which already has the 'clrevorder' logic. |
|
618 | 618 | # This was also fixed in cc0ff93d0c0c. |
|
619 | 619 | fastpathlinkrev = fastpathlinkrev and not self._reorder |
|
620 | 620 | # Treemanifests don't work correctly with fastpathlinkrev |
|
621 | 621 | # either, because we don't discover which directory nodes to |
|
622 | 622 | # send along with files. This could probably be fixed. |
|
623 | 623 | fastpathlinkrev = fastpathlinkrev and ( |
|
624 | 624 | 'treemanifest' not in repo.requirements) |
|
625 | 625 | |
|
626 | 626 | for chunk in self.generatemanifests(commonrevs, clrevorder, |
|
627 | 627 | fastpathlinkrev, mfs, fnodes): |
|
628 | 628 | yield chunk |
|
629 | 629 | mfs.clear() |
|
630 | 630 | clrevs = set(cl.rev(x) for x in clnodes) |
|
631 | 631 | |
|
632 | 632 | if not fastpathlinkrev: |
|
633 | 633 | def linknodes(unused, fname): |
|
634 | 634 | return fnodes.get(fname, {}) |
|
635 | 635 | else: |
|
636 | 636 | cln = cl.node |
|
637 | 637 | def linknodes(filerevlog, fname): |
|
638 | 638 | llr = filerevlog.linkrev |
|
639 | 639 | fln = filerevlog.node |
|
640 | 640 | revs = ((r, llr(r)) for r in filerevlog) |
|
641 | 641 | return dict((fln(r), cln(lr)) for r, lr in revs if lr in clrevs) |
|
642 | 642 | |
|
643 | 643 | for chunk in self.generatefiles(changedfiles, linknodes, commonrevs, |
|
644 | 644 | source): |
|
645 | 645 | yield chunk |
|
646 | 646 | |
|
647 | 647 | yield self.close() |
|
648 | 648 | |
|
649 | 649 | if clnodes: |
|
650 | 650 | repo.hook('outgoing', node=hex(clnodes[0]), source=source) |
|
651 | 651 | |
|
652 | 652 | def generatemanifests(self, commonrevs, clrevorder, fastpathlinkrev, mfs, |
|
653 | 653 | fnodes): |
|
654 | 654 | repo = self._repo |
|
655 | 655 | mfl = repo.manifestlog |
|
656 | 656 | dirlog = mfl._revlog.dirlog |
|
657 | 657 | tmfnodes = {'': mfs} |
|
658 | 658 | |
|
659 | 659 | # Callback for the manifest, used to collect linkrevs for filelog |
|
660 | 660 | # revisions. |
|
661 | 661 | # Returns the linkrev node (collected in lookupcl). |
|
662 | 662 | def makelookupmflinknode(dir): |
|
663 | 663 | if fastpathlinkrev: |
|
664 | 664 | assert not dir |
|
665 | 665 | return mfs.__getitem__ |
|
666 | 666 | |
|
667 | 667 | def lookupmflinknode(x): |
|
668 | 668 | """Callback for looking up the linknode for manifests. |
|
669 | 669 | |
|
670 | 670 | Returns the linkrev node for the specified manifest. |
|
671 | 671 | |
|
672 | 672 | SIDE EFFECT: |
|
673 | 673 | |
|
674 | 674 | 1) fclnodes gets populated with the list of relevant |
|
675 | 675 | file nodes if we're not using fastpathlinkrev |
|
676 | 676 | 2) When treemanifests are in use, collects treemanifest nodes |
|
677 | 677 | to send |
|
678 | 678 | |
|
679 | 679 | Note that this means manifests must be completely sent to |
|
680 | 680 | the client before you can trust the list of files and |
|
681 | 681 | treemanifests to send. |
|
682 | 682 | """ |
|
683 | 683 | clnode = tmfnodes[dir][x] |
|
684 | 684 | mdata = mfl.get(dir, x).readfast(shallow=True) |
|
685 | 685 | for p, n, fl in mdata.iterentries(): |
|
686 | 686 | if fl == 't': # subdirectory manifest |
|
687 | 687 | subdir = dir + p + '/' |
|
688 | 688 | tmfclnodes = tmfnodes.setdefault(subdir, {}) |
|
689 | 689 | tmfclnode = tmfclnodes.setdefault(n, clnode) |
|
690 | 690 | if clrevorder[clnode] < clrevorder[tmfclnode]: |
|
691 | 691 | tmfclnodes[n] = clnode |
|
692 | 692 | else: |
|
693 | 693 | f = dir + p |
|
694 | 694 | fclnodes = fnodes.setdefault(f, {}) |
|
695 | 695 | fclnode = fclnodes.setdefault(n, clnode) |
|
696 | 696 | if clrevorder[clnode] < clrevorder[fclnode]: |
|
697 | 697 | fclnodes[n] = clnode |
|
698 | 698 | return clnode |
|
699 | 699 | return lookupmflinknode |
|
700 | 700 | |
|
701 | 701 | size = 0 |
|
702 | 702 | while tmfnodes: |
|
703 | 703 | dir = min(tmfnodes) |
|
704 | 704 | nodes = tmfnodes[dir] |
|
705 | 705 | prunednodes = self.prune(dirlog(dir), nodes, commonrevs) |
|
706 | 706 | if not dir or prunednodes: |
|
707 | 707 | for x in self._packmanifests(dir, prunednodes, |
|
708 | 708 | makelookupmflinknode(dir)): |
|
709 | 709 | size += len(x) |
|
710 | 710 | yield x |
|
711 | 711 | del tmfnodes[dir] |
|
712 | 712 | self._verbosenote(_('%8.i (manifests)\n') % size) |
|
713 | 713 | yield self._manifestsdone() |
|
714 | 714 | |
|
715 | 715 | # The 'source' parameter is useful for extensions |
|
716 | 716 | def generatefiles(self, changedfiles, linknodes, commonrevs, source): |
|
717 | 717 | repo = self._repo |
|
718 | 718 | progress = self._progress |
|
719 | 719 | msgbundling = _('bundling') |
|
720 | 720 | |
|
721 | 721 | total = len(changedfiles) |
|
722 | 722 | # for progress output |
|
723 | 723 | msgfiles = _('files') |
|
724 | 724 | for i, fname in enumerate(sorted(changedfiles)): |
|
725 | 725 | filerevlog = repo.file(fname) |
|
726 | 726 | if not filerevlog: |
|
727 | 727 | raise error.Abort(_("empty or missing revlog for %s") % fname) |
|
728 | 728 | |
|
729 | 729 | linkrevnodes = linknodes(filerevlog, fname) |
|
730 | 730 | # Lookup for filenodes, we collected the linkrev nodes above in the |
|
731 | 731 | # fastpath case and with lookupmf in the slowpath case. |
|
732 | 732 | def lookupfilelog(x): |
|
733 | 733 | return linkrevnodes[x] |
|
734 | 734 | |
|
735 | 735 | filenodes = self.prune(filerevlog, linkrevnodes, commonrevs) |
|
736 | 736 | if filenodes: |
|
737 | 737 | progress(msgbundling, i + 1, item=fname, unit=msgfiles, |
|
738 | 738 | total=total) |
|
739 | 739 | h = self.fileheader(fname) |
|
740 | 740 | size = len(h) |
|
741 | 741 | yield h |
|
742 | 742 | for chunk in self.group(filenodes, filerevlog, lookupfilelog): |
|
743 | 743 | size += len(chunk) |
|
744 | 744 | yield chunk |
|
745 | 745 | self._verbosenote(_('%8.i %s\n') % (size, fname)) |
|
746 | 746 | progress(msgbundling, None) |
|
747 | 747 | |
|
748 | 748 | def deltaparent(self, revlog, rev, p1, p2, prev): |
|
749 | 749 | return prev |
|
750 | 750 | |
|
751 | 751 | def revchunk(self, revlog, rev, prev, linknode): |
|
752 | 752 | node = revlog.node(rev) |
|
753 | 753 | p1, p2 = revlog.parentrevs(rev) |
|
754 | 754 | base = self.deltaparent(revlog, rev, p1, p2, prev) |
|
755 | 755 | |
|
756 | 756 | prefix = '' |
|
757 | 757 | if revlog.iscensored(base) or revlog.iscensored(rev): |
|
758 | 758 | try: |
|
759 | 759 | delta = revlog.revision(node, raw=True) |
|
760 | 760 | except error.CensoredNodeError as e: |
|
761 | 761 | delta = e.tombstone |
|
762 | 762 | if base == nullrev: |
|
763 | 763 | prefix = mdiff.trivialdiffheader(len(delta)) |
|
764 | 764 | else: |
|
765 | 765 | baselen = revlog.rawsize(base) |
|
766 | 766 | prefix = mdiff.replacediffheader(baselen, len(delta)) |
|
767 | 767 | elif base == nullrev: |
|
768 | 768 | delta = revlog.revision(node, raw=True) |
|
769 | 769 | prefix = mdiff.trivialdiffheader(len(delta)) |
|
770 | 770 | else: |
|
771 | 771 | delta = revlog.revdiff(base, rev) |
|
772 | 772 | p1n, p2n = revlog.parents(node) |
|
773 | 773 | basenode = revlog.node(base) |
|
774 | 774 | flags = revlog.flags(rev) |
|
775 | 775 | meta = self.builddeltaheader(node, p1n, p2n, basenode, linknode, flags) |
|
776 | 776 | meta += prefix |
|
777 | 777 | l = len(meta) + len(delta) |
|
778 | 778 | yield chunkheader(l) |
|
779 | 779 | yield meta |
|
780 | 780 | yield delta |
|
781 | 781 | def builddeltaheader(self, node, p1n, p2n, basenode, linknode, flags): |
|
782 | 782 | # do nothing with basenode, it is implicitly the previous one in HG10 |
|
783 | 783 | # do nothing with flags, it is implicitly 0 for cg1 and cg2 |
|
784 | 784 | return struct.pack(self.deltaheader, node, p1n, p2n, linknode) |
|
785 | 785 | |
|
786 | 786 | class cg2packer(cg1packer): |
|
787 | 787 | version = '02' |
|
788 | 788 | deltaheader = _CHANGEGROUPV2_DELTA_HEADER |
|
789 | 789 | |
|
790 | 790 | def __init__(self, repo, bundlecaps=None): |
|
791 | 791 | super(cg2packer, self).__init__(repo, bundlecaps) |
|
792 | 792 | if self._reorder is None: |
|
793 | 793 | # Since generaldelta is directly supported by cg2, reordering |
|
794 | 794 | # generally doesn't help, so we disable it by default (treating |
|
795 | 795 | # bundle.reorder=auto just like bundle.reorder=False). |
|
796 | 796 | self._reorder = False |
|
797 | 797 | |
|
798 | 798 | def deltaparent(self, revlog, rev, p1, p2, prev): |
|
799 | 799 | dp = revlog.deltaparent(rev) |
|
800 | 800 | if dp == nullrev and revlog.storedeltachains: |
|
801 | 801 | # Avoid sending full revisions when delta parent is null. Pick prev |
|
802 | 802 | # in that case. It's tempting to pick p1 in this case, as p1 will |
|
803 | 803 | # be smaller in the common case. However, computing a delta against |
|
804 | 804 | # p1 may require resolving the raw text of p1, which could be |
|
805 | 805 | # expensive. The revlog caches should have prev cached, meaning |
|
806 | 806 | # less CPU for changegroup generation. There is likely room to add |
|
807 | 807 | # a flag and/or config option to control this behavior. |
|
808 | 808 | return prev |
|
809 | 809 | elif dp == nullrev: |
|
810 | 810 | # revlog is configured to use full snapshot for a reason, |
|
811 | 811 | # stick to full snapshot. |
|
812 | 812 | return nullrev |
|
813 | 813 | elif dp not in (p1, p2, prev): |
|
814 | 814 | # Pick prev when we can't be sure remote has the base revision. |
|
815 | 815 | return prev |
|
816 | 816 | else: |
|
817 | 817 | return dp |
|
818 | 818 | |
|
819 | 819 | def builddeltaheader(self, node, p1n, p2n, basenode, linknode, flags): |
|
820 | 820 | # Do nothing with flags, it is implicitly 0 in cg1 and cg2 |
|
821 | 821 | return struct.pack(self.deltaheader, node, p1n, p2n, basenode, linknode) |
|
822 | 822 | |
|
823 | 823 | class cg3packer(cg2packer): |
|
824 | 824 | version = '03' |
|
825 | 825 | deltaheader = _CHANGEGROUPV3_DELTA_HEADER |
|
826 | 826 | |
|
827 | 827 | def _packmanifests(self, dir, mfnodes, lookuplinknode): |
|
828 | 828 | if dir: |
|
829 | 829 | yield self.fileheader(dir) |
|
830 | 830 | |
|
831 | 831 | dirlog = self._repo.manifestlog._revlog.dirlog(dir) |
|
832 | 832 | for chunk in self.group(mfnodes, dirlog, lookuplinknode, |
|
833 | 833 | units=_('manifests')): |
|
834 | 834 | yield chunk |
|
835 | 835 | |
|
836 | 836 | def _manifestsdone(self): |
|
837 | 837 | return self.close() |
|
838 | 838 | |
|
839 | 839 | def builddeltaheader(self, node, p1n, p2n, basenode, linknode, flags): |
|
840 | 840 | return struct.pack( |
|
841 | 841 | self.deltaheader, node, p1n, p2n, basenode, linknode, flags) |
|
842 | 842 | |
|
843 | 843 | _packermap = {'01': (cg1packer, cg1unpacker), |
|
844 | 844 | # cg2 adds support for exchanging generaldelta |
|
845 | 845 | '02': (cg2packer, cg2unpacker), |
|
846 | 846 | # cg3 adds support for exchanging revlog flags and treemanifests |
|
847 | 847 | '03': (cg3packer, cg3unpacker), |
|
848 | 848 | } |
|
849 | 849 | |
|
850 | 850 | def allsupportedversions(repo): |
|
851 | 851 | versions = set(_packermap.keys()) |
|
852 | 852 | if not (repo.ui.configbool('experimental', 'changegroup3') or |
|
853 | 853 | repo.ui.configbool('experimental', 'treemanifest') or |
|
854 | 854 | 'treemanifest' in repo.requirements): |
|
855 | 855 | versions.discard('03') |
|
856 | 856 | return versions |
|
857 | 857 | |
|
858 | 858 | # Changegroup versions that can be applied to the repo |
|
859 | 859 | def supportedincomingversions(repo): |
|
860 | 860 | return allsupportedversions(repo) |
|
861 | 861 | |
|
862 | 862 | # Changegroup versions that can be created from the repo |
|
863 | 863 | def supportedoutgoingversions(repo): |
|
864 | 864 | versions = allsupportedversions(repo) |
|
865 | 865 | if 'treemanifest' in repo.requirements: |
|
866 | 866 | # Versions 01 and 02 support only flat manifests and it's just too |
|
867 | 867 | # expensive to convert between the flat manifest and tree manifest on |
|
868 | 868 | # the fly. Since tree manifests are hashed differently, all of history |
|
869 | 869 | # would have to be converted. Instead, we simply don't even pretend to |
|
870 | 870 | # support versions 01 and 02. |
|
871 | 871 | versions.discard('01') |
|
872 | 872 | versions.discard('02') |
|
873 | 873 | return versions |
|
874 | 874 | |
|
875 | def localversion(repo): | |
|
876 | # Finds the best version to use for bundles that are meant to be used | |
|
877 | # locally, such as those from strip and shelve, and temporary bundles. | |
|
878 | return max(supportedoutgoingversions(repo)) | |
|
879 | ||
|
875 | 880 | def safeversion(repo): |
|
876 | 881 | # Finds the smallest version that it's safe to assume clients of the repo |
|
877 | 882 | # will support. For example, all hg versions that support generaldelta also |
|
878 | 883 | # support changegroup 02. |
|
879 | 884 | versions = supportedoutgoingversions(repo) |
|
880 | 885 | if 'generaldelta' in repo.requirements: |
|
881 | 886 | versions.discard('01') |
|
882 | 887 | assert versions |
|
883 | 888 | return min(versions) |
|
884 | 889 | |
|
885 | 890 | def getbundler(version, repo, bundlecaps=None): |
|
886 | 891 | assert version in supportedoutgoingversions(repo) |
|
887 | 892 | return _packermap[version][0](repo, bundlecaps) |
|
888 | 893 | |
|
889 | 894 | def getunbundler(version, fh, alg, extras=None): |
|
890 | 895 | return _packermap[version][1](fh, alg, extras=extras) |
|
891 | 896 | |
|
892 | 897 | def _changegroupinfo(repo, nodes, source): |
|
893 | 898 | if repo.ui.verbose or source == 'bundle': |
|
894 | 899 | repo.ui.status(_("%d changesets found\n") % len(nodes)) |
|
895 | 900 | if repo.ui.debugflag: |
|
896 | 901 | repo.ui.debug("list of changesets:\n") |
|
897 | 902 | for node in nodes: |
|
898 | 903 | repo.ui.debug("%s\n" % hex(node)) |
|
899 | 904 | |
|
900 | 905 | def getsubsetraw(repo, outgoing, bundler, source, fastpath=False): |
|
901 | 906 | repo = repo.unfiltered() |
|
902 | 907 | commonrevs = outgoing.common |
|
903 | 908 | csets = outgoing.missing |
|
904 | 909 | heads = outgoing.missingheads |
|
905 | 910 | # We go through the fast path if we get told to, or if all (unfiltered |
|
906 | 911 | # heads have been requested (since we then know there all linkrevs will |
|
907 | 912 | # be pulled by the client). |
|
908 | 913 | heads.sort() |
|
909 | 914 | fastpathlinkrev = fastpath or ( |
|
910 | 915 | repo.filtername is None and heads == sorted(repo.heads())) |
|
911 | 916 | |
|
912 | 917 | repo.hook('preoutgoing', throw=True, source=source) |
|
913 | 918 | _changegroupinfo(repo, csets, source) |
|
914 | 919 | return bundler.generate(commonrevs, csets, fastpathlinkrev, source) |
|
915 | 920 | |
|
916 | 921 | def getsubset(repo, outgoing, bundler, source, fastpath=False): |
|
917 | 922 | gengroup = getsubsetraw(repo, outgoing, bundler, source, fastpath) |
|
918 | 923 | return getunbundler(bundler.version, util.chunkbuffer(gengroup), None, |
|
919 | 924 | {'clcount': len(outgoing.missing)}) |
|
920 | 925 | |
|
921 | 926 | def changegroupsubset(repo, roots, heads, source, version='01'): |
|
922 | 927 | """Compute a changegroup consisting of all the nodes that are |
|
923 | 928 | descendants of any of the roots and ancestors of any of the heads. |
|
924 | 929 | Return a chunkbuffer object whose read() method will return |
|
925 | 930 | successive changegroup chunks. |
|
926 | 931 | |
|
927 | 932 | It is fairly complex as determining which filenodes and which |
|
928 | 933 | manifest nodes need to be included for the changeset to be complete |
|
929 | 934 | is non-trivial. |
|
930 | 935 | |
|
931 | 936 | Another wrinkle is doing the reverse, figuring out which changeset in |
|
932 | 937 | the changegroup a particular filenode or manifestnode belongs to. |
|
933 | 938 | """ |
|
934 | 939 | outgoing = discovery.outgoing(repo, missingroots=roots, missingheads=heads) |
|
935 | 940 | bundler = getbundler(version, repo) |
|
936 | 941 | return getsubset(repo, outgoing, bundler, source) |
|
937 | 942 | |
|
938 | 943 | def getlocalchangegroupraw(repo, source, outgoing, bundlecaps=None, |
|
939 | 944 | version='01'): |
|
940 | 945 | """Like getbundle, but taking a discovery.outgoing as an argument. |
|
941 | 946 | |
|
942 | 947 | This is only implemented for local repos and reuses potentially |
|
943 | 948 | precomputed sets in outgoing. Returns a raw changegroup generator.""" |
|
944 | 949 | if not outgoing.missing: |
|
945 | 950 | return None |
|
946 | 951 | bundler = getbundler(version, repo, bundlecaps) |
|
947 | 952 | return getsubsetraw(repo, outgoing, bundler, source) |
|
948 | 953 | |
|
949 | 954 | def getchangegroup(repo, source, outgoing, bundlecaps=None, |
|
950 | 955 | version='01'): |
|
951 | 956 | """Like getbundle, but taking a discovery.outgoing as an argument. |
|
952 | 957 | |
|
953 | 958 | This is only implemented for local repos and reuses potentially |
|
954 | 959 | precomputed sets in outgoing.""" |
|
955 | 960 | if not outgoing.missing: |
|
956 | 961 | return None |
|
957 | 962 | bundler = getbundler(version, repo, bundlecaps) |
|
958 | 963 | return getsubset(repo, outgoing, bundler, source) |
|
959 | 964 | |
|
960 | 965 | def getlocalchangegroup(repo, *args, **kwargs): |
|
961 | 966 | repo.ui.deprecwarn('getlocalchangegroup is deprecated, use getchangegroup', |
|
962 | 967 | '4.3') |
|
963 | 968 | return getchangegroup(repo, *args, **kwargs) |
|
964 | 969 | |
|
965 | 970 | def changegroup(repo, basenodes, source): |
|
966 | 971 | # to avoid a race we use changegroupsubset() (issue1320) |
|
967 | 972 | return changegroupsubset(repo, basenodes, repo.heads(), source) |
|
968 | 973 | |
|
969 | 974 | def _addchangegroupfiles(repo, source, revmap, trp, expectedfiles, needfiles): |
|
970 | 975 | revisions = 0 |
|
971 | 976 | files = 0 |
|
972 | 977 | for chunkdata in iter(source.filelogheader, {}): |
|
973 | 978 | files += 1 |
|
974 | 979 | f = chunkdata["filename"] |
|
975 | 980 | repo.ui.debug("adding %s revisions\n" % f) |
|
976 | 981 | repo.ui.progress(_('files'), files, unit=_('files'), |
|
977 | 982 | total=expectedfiles) |
|
978 | 983 | fl = repo.file(f) |
|
979 | 984 | o = len(fl) |
|
980 | 985 | try: |
|
981 | 986 | if not fl.addgroup(source, revmap, trp): |
|
982 | 987 | raise error.Abort(_("received file revlog group is empty")) |
|
983 | 988 | except error.CensoredBaseError as e: |
|
984 | 989 | raise error.Abort(_("received delta base is censored: %s") % e) |
|
985 | 990 | revisions += len(fl) - o |
|
986 | 991 | if f in needfiles: |
|
987 | 992 | needs = needfiles[f] |
|
988 | 993 | for new in xrange(o, len(fl)): |
|
989 | 994 | n = fl.node(new) |
|
990 | 995 | if n in needs: |
|
991 | 996 | needs.remove(n) |
|
992 | 997 | else: |
|
993 | 998 | raise error.Abort( |
|
994 | 999 | _("received spurious file revlog entry")) |
|
995 | 1000 | if not needs: |
|
996 | 1001 | del needfiles[f] |
|
997 | 1002 | repo.ui.progress(_('files'), None) |
|
998 | 1003 | |
|
999 | 1004 | for f, needs in needfiles.iteritems(): |
|
1000 | 1005 | fl = repo.file(f) |
|
1001 | 1006 | for n in needs: |
|
1002 | 1007 | try: |
|
1003 | 1008 | fl.rev(n) |
|
1004 | 1009 | except error.LookupError: |
|
1005 | 1010 | raise error.Abort( |
|
1006 | 1011 | _('missing file data for %s:%s - run hg verify') % |
|
1007 | 1012 | (f, hex(n))) |
|
1008 | 1013 | |
|
1009 | 1014 | return revisions, files |
@@ -1,433 +1,433 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 __future__ import absolute_import |
|
10 | 10 | |
|
11 | 11 | import errno |
|
12 | 12 | import hashlib |
|
13 | 13 | |
|
14 | 14 | from .i18n import _ |
|
15 | 15 | from .node import short |
|
16 | 16 | from . import ( |
|
17 | 17 | bundle2, |
|
18 | 18 | changegroup, |
|
19 | 19 | discovery, |
|
20 | 20 | error, |
|
21 | 21 | exchange, |
|
22 | 22 | obsolete, |
|
23 | 23 | obsutil, |
|
24 | 24 | util, |
|
25 | 25 | ) |
|
26 | 26 | |
|
27 | 27 | def _bundle(repo, bases, heads, node, suffix, compress=True, obsolescence=True): |
|
28 | 28 | """create a bundle with the specified revisions as a backup""" |
|
29 | 29 | |
|
30 | 30 | backupdir = "strip-backup" |
|
31 | 31 | vfs = repo.vfs |
|
32 | 32 | if not vfs.isdir(backupdir): |
|
33 | 33 | vfs.mkdir(backupdir) |
|
34 | 34 | |
|
35 | 35 | # Include a hash of all the nodes in the filename for uniqueness |
|
36 | 36 | allcommits = repo.set('%ln::%ln', bases, heads) |
|
37 | 37 | allhashes = sorted(c.hex() for c in allcommits) |
|
38 | 38 | totalhash = hashlib.sha1(''.join(allhashes)).hexdigest() |
|
39 | 39 | name = "%s/%s-%s-%s.hg" % (backupdir, short(node), totalhash[:8], suffix) |
|
40 | 40 | |
|
41 |
cgversion = changegroup. |
|
|
41 | cgversion = changegroup.localversion(repo) | |
|
42 | 42 | comp = None |
|
43 | 43 | if cgversion != '01': |
|
44 | 44 | bundletype = "HG20" |
|
45 | 45 | if compress: |
|
46 | 46 | comp = 'BZ' |
|
47 | 47 | elif compress: |
|
48 | 48 | bundletype = "HG10BZ" |
|
49 | 49 | else: |
|
50 | 50 | bundletype = "HG10UN" |
|
51 | 51 | |
|
52 | 52 | outgoing = discovery.outgoing(repo, missingroots=bases, missingheads=heads) |
|
53 | 53 | contentopts = { |
|
54 | 54 | 'cg.version': cgversion, |
|
55 | 55 | 'obsolescence': obsolescence, |
|
56 | 56 | 'phases': True, |
|
57 | 57 | } |
|
58 | 58 | return bundle2.writenewbundle(repo.ui, repo, 'strip', name, bundletype, |
|
59 | 59 | outgoing, contentopts, vfs, compression=comp) |
|
60 | 60 | |
|
61 | 61 | def _collectfiles(repo, striprev): |
|
62 | 62 | """find out the filelogs affected by the strip""" |
|
63 | 63 | files = set() |
|
64 | 64 | |
|
65 | 65 | for x in xrange(striprev, len(repo)): |
|
66 | 66 | files.update(repo[x].files()) |
|
67 | 67 | |
|
68 | 68 | return sorted(files) |
|
69 | 69 | |
|
70 | 70 | def _collectbrokencsets(repo, files, striprev): |
|
71 | 71 | """return the changesets which will be broken by the truncation""" |
|
72 | 72 | s = set() |
|
73 | 73 | def collectone(revlog): |
|
74 | 74 | _, brokenset = revlog.getstrippoint(striprev) |
|
75 | 75 | s.update([revlog.linkrev(r) for r in brokenset]) |
|
76 | 76 | |
|
77 | 77 | collectone(repo.manifestlog._revlog) |
|
78 | 78 | for fname in files: |
|
79 | 79 | collectone(repo.file(fname)) |
|
80 | 80 | |
|
81 | 81 | return s |
|
82 | 82 | |
|
83 | 83 | def strip(ui, repo, nodelist, backup=True, topic='backup'): |
|
84 | 84 | # This function requires the caller to lock the repo, but it operates |
|
85 | 85 | # within a transaction of its own, and thus requires there to be no current |
|
86 | 86 | # transaction when it is called. |
|
87 | 87 | if repo.currenttransaction() is not None: |
|
88 | 88 | raise error.ProgrammingError('cannot strip from inside a transaction') |
|
89 | 89 | |
|
90 | 90 | # Simple way to maintain backwards compatibility for this |
|
91 | 91 | # argument. |
|
92 | 92 | if backup in ['none', 'strip']: |
|
93 | 93 | backup = False |
|
94 | 94 | |
|
95 | 95 | repo = repo.unfiltered() |
|
96 | 96 | repo.destroying() |
|
97 | 97 | |
|
98 | 98 | cl = repo.changelog |
|
99 | 99 | # TODO handle undo of merge sets |
|
100 | 100 | if isinstance(nodelist, str): |
|
101 | 101 | nodelist = [nodelist] |
|
102 | 102 | striplist = [cl.rev(node) for node in nodelist] |
|
103 | 103 | striprev = min(striplist) |
|
104 | 104 | |
|
105 | 105 | files = _collectfiles(repo, striprev) |
|
106 | 106 | saverevs = _collectbrokencsets(repo, files, striprev) |
|
107 | 107 | |
|
108 | 108 | # Some revisions with rev > striprev may not be descendants of striprev. |
|
109 | 109 | # We have to find these revisions and put them in a bundle, so that |
|
110 | 110 | # we can restore them after the truncations. |
|
111 | 111 | # To create the bundle we use repo.changegroupsubset which requires |
|
112 | 112 | # the list of heads and bases of the set of interesting revisions. |
|
113 | 113 | # (head = revision in the set that has no descendant in the set; |
|
114 | 114 | # base = revision in the set that has no ancestor in the set) |
|
115 | 115 | tostrip = set(striplist) |
|
116 | 116 | saveheads = set(saverevs) |
|
117 | 117 | for r in cl.revs(start=striprev + 1): |
|
118 | 118 | if any(p in tostrip for p in cl.parentrevs(r)): |
|
119 | 119 | tostrip.add(r) |
|
120 | 120 | |
|
121 | 121 | if r not in tostrip: |
|
122 | 122 | saverevs.add(r) |
|
123 | 123 | saveheads.difference_update(cl.parentrevs(r)) |
|
124 | 124 | saveheads.add(r) |
|
125 | 125 | saveheads = [cl.node(r) for r in saveheads] |
|
126 | 126 | |
|
127 | 127 | # compute base nodes |
|
128 | 128 | if saverevs: |
|
129 | 129 | descendants = set(cl.descendants(saverevs)) |
|
130 | 130 | saverevs.difference_update(descendants) |
|
131 | 131 | savebases = [cl.node(r) for r in saverevs] |
|
132 | 132 | stripbases = [cl.node(r) for r in tostrip] |
|
133 | 133 | |
|
134 | 134 | stripobsidx = obsmarkers = () |
|
135 | 135 | if repo.ui.configbool('devel', 'strip-obsmarkers'): |
|
136 | 136 | obsmarkers = obsutil.exclusivemarkers(repo, stripbases) |
|
137 | 137 | if obsmarkers: |
|
138 | 138 | stripobsidx = [i for i, m in enumerate(repo.obsstore) |
|
139 | 139 | if m in obsmarkers] |
|
140 | 140 | |
|
141 | 141 | # For a set s, max(parents(s) - s) is the same as max(heads(::s - s)), but |
|
142 | 142 | # is much faster |
|
143 | 143 | newbmtarget = repo.revs('max(parents(%ld) - (%ld))', tostrip, tostrip) |
|
144 | 144 | if newbmtarget: |
|
145 | 145 | newbmtarget = repo[newbmtarget.first()].node() |
|
146 | 146 | else: |
|
147 | 147 | newbmtarget = '.' |
|
148 | 148 | |
|
149 | 149 | bm = repo._bookmarks |
|
150 | 150 | updatebm = [] |
|
151 | 151 | for m in bm: |
|
152 | 152 | rev = repo[bm[m]].rev() |
|
153 | 153 | if rev in tostrip: |
|
154 | 154 | updatebm.append(m) |
|
155 | 155 | |
|
156 | 156 | # create a changegroup for all the branches we need to keep |
|
157 | 157 | backupfile = None |
|
158 | 158 | vfs = repo.vfs |
|
159 | 159 | node = nodelist[-1] |
|
160 | 160 | if backup: |
|
161 | 161 | backupfile = _bundle(repo, stripbases, cl.heads(), node, topic) |
|
162 | 162 | repo.ui.status(_("saved backup bundle to %s\n") % |
|
163 | 163 | vfs.join(backupfile)) |
|
164 | 164 | repo.ui.log("backupbundle", "saved backup bundle to %s\n", |
|
165 | 165 | vfs.join(backupfile)) |
|
166 | 166 | tmpbundlefile = None |
|
167 | 167 | if saveheads: |
|
168 | 168 | # do not compress temporary bundle if we remove it from disk later |
|
169 | 169 | # |
|
170 | 170 | # We do not include obsolescence, it might re-introduce prune markers |
|
171 | 171 | # we are trying to strip. This is harmless since the stripped markers |
|
172 | 172 | # are already backed up and we did not touched the markers for the |
|
173 | 173 | # saved changesets. |
|
174 | 174 | tmpbundlefile = _bundle(repo, savebases, saveheads, node, 'temp', |
|
175 | 175 | compress=False, obsolescence=False) |
|
176 | 176 | |
|
177 | 177 | mfst = repo.manifestlog._revlog |
|
178 | 178 | |
|
179 | 179 | try: |
|
180 | 180 | with repo.transaction("strip") as tr: |
|
181 | 181 | offset = len(tr.entries) |
|
182 | 182 | |
|
183 | 183 | tr.startgroup() |
|
184 | 184 | cl.strip(striprev, tr) |
|
185 | 185 | mfst.strip(striprev, tr) |
|
186 | 186 | striptrees(repo, tr, striprev, files) |
|
187 | 187 | |
|
188 | 188 | for fn in files: |
|
189 | 189 | repo.file(fn).strip(striprev, tr) |
|
190 | 190 | tr.endgroup() |
|
191 | 191 | |
|
192 | 192 | for i in xrange(offset, len(tr.entries)): |
|
193 | 193 | file, troffset, ignore = tr.entries[i] |
|
194 | 194 | with repo.svfs(file, 'a', checkambig=True) as fp: |
|
195 | 195 | fp.truncate(troffset) |
|
196 | 196 | if troffset == 0: |
|
197 | 197 | repo.store.markremoved(file) |
|
198 | 198 | |
|
199 | 199 | deleteobsmarkers(repo.obsstore, stripobsidx) |
|
200 | 200 | del repo.obsstore |
|
201 | 201 | |
|
202 | 202 | repo._phasecache.filterunknown(repo) |
|
203 | 203 | if tmpbundlefile: |
|
204 | 204 | ui.note(_("adding branch\n")) |
|
205 | 205 | f = vfs.open(tmpbundlefile, "rb") |
|
206 | 206 | gen = exchange.readbundle(ui, f, tmpbundlefile, vfs) |
|
207 | 207 | if not repo.ui.verbose: |
|
208 | 208 | # silence internal shuffling chatter |
|
209 | 209 | repo.ui.pushbuffer() |
|
210 | 210 | tmpbundleurl = 'bundle:' + vfs.join(tmpbundlefile) |
|
211 | 211 | txnname = 'strip' |
|
212 | 212 | if not isinstance(gen, bundle2.unbundle20): |
|
213 | 213 | txnname = "strip\n%s" % util.hidepassword(tmpbundleurl) |
|
214 | 214 | with repo.transaction(txnname) as tr: |
|
215 | 215 | bundle2.applybundle(repo, gen, tr, source='strip', |
|
216 | 216 | url=tmpbundleurl) |
|
217 | 217 | if not repo.ui.verbose: |
|
218 | 218 | repo.ui.popbuffer() |
|
219 | 219 | f.close() |
|
220 | 220 | repo._phasecache.invalidate() |
|
221 | 221 | |
|
222 | 222 | |
|
223 | 223 | with repo.transaction('repair') as tr: |
|
224 | 224 | bmchanges = [(m, repo[newbmtarget].node()) for m in updatebm] |
|
225 | 225 | bm.applychanges(repo, tr, bmchanges) |
|
226 | 226 | |
|
227 | 227 | # remove undo files |
|
228 | 228 | for undovfs, undofile in repo.undofiles(): |
|
229 | 229 | try: |
|
230 | 230 | undovfs.unlink(undofile) |
|
231 | 231 | except OSError as e: |
|
232 | 232 | if e.errno != errno.ENOENT: |
|
233 | 233 | ui.warn(_('error removing %s: %s\n') % |
|
234 | 234 | (undovfs.join(undofile), str(e))) |
|
235 | 235 | |
|
236 | 236 | except: # re-raises |
|
237 | 237 | if backupfile: |
|
238 | 238 | ui.warn(_("strip failed, backup bundle stored in '%s'\n") |
|
239 | 239 | % vfs.join(backupfile)) |
|
240 | 240 | if tmpbundlefile: |
|
241 | 241 | ui.warn(_("strip failed, unrecovered changes stored in '%s'\n") |
|
242 | 242 | % vfs.join(tmpbundlefile)) |
|
243 | 243 | ui.warn(_("(fix the problem, then recover the changesets with " |
|
244 | 244 | "\"hg unbundle '%s'\")\n") % vfs.join(tmpbundlefile)) |
|
245 | 245 | raise |
|
246 | 246 | else: |
|
247 | 247 | if tmpbundlefile: |
|
248 | 248 | # Remove temporary bundle only if there were no exceptions |
|
249 | 249 | vfs.unlink(tmpbundlefile) |
|
250 | 250 | |
|
251 | 251 | repo.destroyed() |
|
252 | 252 | # return the backup file path (or None if 'backup' was False) so |
|
253 | 253 | # extensions can use it |
|
254 | 254 | return backupfile |
|
255 | 255 | |
|
256 | 256 | def safestriproots(ui, repo, nodes): |
|
257 | 257 | """return list of roots of nodes where descendants are covered by nodes""" |
|
258 | 258 | torev = repo.unfiltered().changelog.rev |
|
259 | 259 | revs = set(torev(n) for n in nodes) |
|
260 | 260 | # tostrip = wanted - unsafe = wanted - ancestors(orphaned) |
|
261 | 261 | # orphaned = affected - wanted |
|
262 | 262 | # affected = descendants(roots(wanted)) |
|
263 | 263 | # wanted = revs |
|
264 | 264 | tostrip = set(repo.revs('%ld-(::((roots(%ld)::)-%ld))', revs, revs, revs)) |
|
265 | 265 | notstrip = revs - tostrip |
|
266 | 266 | if notstrip: |
|
267 | 267 | nodestr = ', '.join(sorted(short(repo[n].node()) for n in notstrip)) |
|
268 | 268 | ui.warn(_('warning: orphaned descendants detected, ' |
|
269 | 269 | 'not stripping %s\n') % nodestr) |
|
270 | 270 | return [c.node() for c in repo.set('roots(%ld)', tostrip)] |
|
271 | 271 | |
|
272 | 272 | class stripcallback(object): |
|
273 | 273 | """used as a transaction postclose callback""" |
|
274 | 274 | |
|
275 | 275 | def __init__(self, ui, repo, backup, topic): |
|
276 | 276 | self.ui = ui |
|
277 | 277 | self.repo = repo |
|
278 | 278 | self.backup = backup |
|
279 | 279 | self.topic = topic or 'backup' |
|
280 | 280 | self.nodelist = [] |
|
281 | 281 | |
|
282 | 282 | def addnodes(self, nodes): |
|
283 | 283 | self.nodelist.extend(nodes) |
|
284 | 284 | |
|
285 | 285 | def __call__(self, tr): |
|
286 | 286 | roots = safestriproots(self.ui, self.repo, self.nodelist) |
|
287 | 287 | if roots: |
|
288 | 288 | strip(self.ui, self.repo, roots, self.backup, self.topic) |
|
289 | 289 | |
|
290 | 290 | def delayedstrip(ui, repo, nodelist, topic=None): |
|
291 | 291 | """like strip, but works inside transaction and won't strip irreverent revs |
|
292 | 292 | |
|
293 | 293 | nodelist must explicitly contain all descendants. Otherwise a warning will |
|
294 | 294 | be printed that some nodes are not stripped. |
|
295 | 295 | |
|
296 | 296 | Always do a backup. The last non-None "topic" will be used as the backup |
|
297 | 297 | topic name. The default backup topic name is "backup". |
|
298 | 298 | """ |
|
299 | 299 | tr = repo.currenttransaction() |
|
300 | 300 | if not tr: |
|
301 | 301 | nodes = safestriproots(ui, repo, nodelist) |
|
302 | 302 | return strip(ui, repo, nodes, True, topic) |
|
303 | 303 | # transaction postclose callbacks are called in alphabet order. |
|
304 | 304 | # use '\xff' as prefix so we are likely to be called last. |
|
305 | 305 | callback = tr.getpostclose('\xffstrip') |
|
306 | 306 | if callback is None: |
|
307 | 307 | callback = stripcallback(ui, repo, True, topic) |
|
308 | 308 | tr.addpostclose('\xffstrip', callback) |
|
309 | 309 | if topic: |
|
310 | 310 | callback.topic = topic |
|
311 | 311 | callback.addnodes(nodelist) |
|
312 | 312 | |
|
313 | 313 | def striptrees(repo, tr, striprev, files): |
|
314 | 314 | if 'treemanifest' in repo.requirements: # safe but unnecessary |
|
315 | 315 | # otherwise |
|
316 | 316 | for unencoded, encoded, size in repo.store.datafiles(): |
|
317 | 317 | if (unencoded.startswith('meta/') and |
|
318 | 318 | unencoded.endswith('00manifest.i')): |
|
319 | 319 | dir = unencoded[5:-12] |
|
320 | 320 | repo.manifestlog._revlog.dirlog(dir).strip(striprev, tr) |
|
321 | 321 | |
|
322 | 322 | def rebuildfncache(ui, repo): |
|
323 | 323 | """Rebuilds the fncache file from repo history. |
|
324 | 324 | |
|
325 | 325 | Missing entries will be added. Extra entries will be removed. |
|
326 | 326 | """ |
|
327 | 327 | repo = repo.unfiltered() |
|
328 | 328 | |
|
329 | 329 | if 'fncache' not in repo.requirements: |
|
330 | 330 | ui.warn(_('(not rebuilding fncache because repository does not ' |
|
331 | 331 | 'support fncache)\n')) |
|
332 | 332 | return |
|
333 | 333 | |
|
334 | 334 | with repo.lock(): |
|
335 | 335 | fnc = repo.store.fncache |
|
336 | 336 | # Trigger load of fncache. |
|
337 | 337 | if 'irrelevant' in fnc: |
|
338 | 338 | pass |
|
339 | 339 | |
|
340 | 340 | oldentries = set(fnc.entries) |
|
341 | 341 | newentries = set() |
|
342 | 342 | seenfiles = set() |
|
343 | 343 | |
|
344 | 344 | repolen = len(repo) |
|
345 | 345 | for rev in repo: |
|
346 | 346 | ui.progress(_('rebuilding'), rev, total=repolen, |
|
347 | 347 | unit=_('changesets')) |
|
348 | 348 | |
|
349 | 349 | ctx = repo[rev] |
|
350 | 350 | for f in ctx.files(): |
|
351 | 351 | # This is to minimize I/O. |
|
352 | 352 | if f in seenfiles: |
|
353 | 353 | continue |
|
354 | 354 | seenfiles.add(f) |
|
355 | 355 | |
|
356 | 356 | i = 'data/%s.i' % f |
|
357 | 357 | d = 'data/%s.d' % f |
|
358 | 358 | |
|
359 | 359 | if repo.store._exists(i): |
|
360 | 360 | newentries.add(i) |
|
361 | 361 | if repo.store._exists(d): |
|
362 | 362 | newentries.add(d) |
|
363 | 363 | |
|
364 | 364 | ui.progress(_('rebuilding'), None) |
|
365 | 365 | |
|
366 | 366 | if 'treemanifest' in repo.requirements: # safe but unnecessary otherwise |
|
367 | 367 | for dir in util.dirs(seenfiles): |
|
368 | 368 | i = 'meta/%s/00manifest.i' % dir |
|
369 | 369 | d = 'meta/%s/00manifest.d' % dir |
|
370 | 370 | |
|
371 | 371 | if repo.store._exists(i): |
|
372 | 372 | newentries.add(i) |
|
373 | 373 | if repo.store._exists(d): |
|
374 | 374 | newentries.add(d) |
|
375 | 375 | |
|
376 | 376 | addcount = len(newentries - oldentries) |
|
377 | 377 | removecount = len(oldentries - newentries) |
|
378 | 378 | for p in sorted(oldentries - newentries): |
|
379 | 379 | ui.write(_('removing %s\n') % p) |
|
380 | 380 | for p in sorted(newentries - oldentries): |
|
381 | 381 | ui.write(_('adding %s\n') % p) |
|
382 | 382 | |
|
383 | 383 | if addcount or removecount: |
|
384 | 384 | ui.write(_('%d items added, %d removed from fncache\n') % |
|
385 | 385 | (addcount, removecount)) |
|
386 | 386 | fnc.entries = newentries |
|
387 | 387 | fnc._dirty = True |
|
388 | 388 | |
|
389 | 389 | with repo.transaction('fncache') as tr: |
|
390 | 390 | fnc.write(tr) |
|
391 | 391 | else: |
|
392 | 392 | ui.write(_('fncache already up to date\n')) |
|
393 | 393 | |
|
394 | 394 | def stripbmrevset(repo, mark): |
|
395 | 395 | """ |
|
396 | 396 | The revset to strip when strip is called with -B mark |
|
397 | 397 | |
|
398 | 398 | Needs to live here so extensions can use it and wrap it even when strip is |
|
399 | 399 | not enabled or not present on a box. |
|
400 | 400 | """ |
|
401 | 401 | return repo.revs("ancestors(bookmark(%s)) - " |
|
402 | 402 | "ancestors(head() and not bookmark(%s)) - " |
|
403 | 403 | "ancestors(bookmark() and not bookmark(%s))", |
|
404 | 404 | mark, mark, mark) |
|
405 | 405 | |
|
406 | 406 | def deleteobsmarkers(obsstore, indices): |
|
407 | 407 | """Delete some obsmarkers from obsstore and return how many were deleted |
|
408 | 408 | |
|
409 | 409 | 'indices' is a list of ints which are the indices |
|
410 | 410 | of the markers to be deleted. |
|
411 | 411 | |
|
412 | 412 | Every invocation of this function completely rewrites the obsstore file, |
|
413 | 413 | skipping the markers we want to be removed. The new temporary file is |
|
414 | 414 | created, remaining markers are written there and on .close() this file |
|
415 | 415 | gets atomically renamed to obsstore, thus guaranteeing consistency.""" |
|
416 | 416 | if not indices: |
|
417 | 417 | # we don't want to rewrite the obsstore with the same content |
|
418 | 418 | return |
|
419 | 419 | |
|
420 | 420 | left = [] |
|
421 | 421 | current = obsstore._all |
|
422 | 422 | n = 0 |
|
423 | 423 | for i, m in enumerate(current): |
|
424 | 424 | if i in indices: |
|
425 | 425 | n += 1 |
|
426 | 426 | continue |
|
427 | 427 | left.append(m) |
|
428 | 428 | |
|
429 | 429 | newobsstorefile = obsstore.svfs('obsstore', 'w', atomictemp=True) |
|
430 | 430 | for bytes in obsolete.encodemarkers(left, True, obsstore._version): |
|
431 | 431 | newobsstorefile.write(bytes) |
|
432 | 432 | newobsstorefile.close() |
|
433 | 433 | return n |
@@ -1,983 +1,982 b'' | |||
|
1 | 1 | $ cat >> $HGRCPATH <<EOF |
|
2 | 2 | > [extensions] |
|
3 | 3 | > rebase= |
|
4 | 4 | > drawdag=$TESTDIR/drawdag.py |
|
5 | 5 | > |
|
6 | 6 | > [phases] |
|
7 | 7 | > publish=False |
|
8 | 8 | > |
|
9 | 9 | > [alias] |
|
10 | 10 | > tglog = log -G --template "{rev}: '{desc}' {branches}\n" |
|
11 | 11 | > EOF |
|
12 | 12 | |
|
13 | 13 | |
|
14 | 14 | $ hg init a |
|
15 | 15 | $ cd a |
|
16 | 16 | $ hg unbundle "$TESTDIR/bundles/rebase.hg" |
|
17 | 17 | adding changesets |
|
18 | 18 | adding manifests |
|
19 | 19 | adding file changes |
|
20 | 20 | added 8 changesets with 7 changes to 7 files (+2 heads) |
|
21 | 21 | (run 'hg heads' to see heads, 'hg merge' to merge) |
|
22 | 22 | $ hg up tip |
|
23 | 23 | 3 files updated, 0 files merged, 0 files removed, 0 files unresolved |
|
24 | 24 | $ cd .. |
|
25 | 25 | |
|
26 | 26 | |
|
27 | 27 | Rebasing |
|
28 | 28 | D onto H - simple rebase: |
|
29 | 29 | (this also tests that editor is invoked if '--edit' is specified, and that we |
|
30 | 30 | can abort or warn for colliding untracked files) |
|
31 | 31 | |
|
32 | 32 | $ hg clone -q -u . a a1 |
|
33 | 33 | $ cd a1 |
|
34 | 34 | |
|
35 | 35 | $ hg tglog |
|
36 | 36 | @ 7: 'H' |
|
37 | 37 | | |
|
38 | 38 | | o 6: 'G' |
|
39 | 39 | |/| |
|
40 | 40 | o | 5: 'F' |
|
41 | 41 | | | |
|
42 | 42 | | o 4: 'E' |
|
43 | 43 | |/ |
|
44 | 44 | | o 3: 'D' |
|
45 | 45 | | | |
|
46 | 46 | | o 2: 'C' |
|
47 | 47 | | | |
|
48 | 48 | | o 1: 'B' |
|
49 | 49 | |/ |
|
50 | 50 | o 0: 'A' |
|
51 | 51 | |
|
52 | 52 | |
|
53 | 53 | $ hg status --rev "3^1" --rev 3 |
|
54 | 54 | A D |
|
55 | 55 | $ echo collide > D |
|
56 | 56 | $ HGEDITOR=cat hg rebase -s 3 -d 7 --edit --config merge.checkunknown=warn |
|
57 | 57 | rebasing 3:32af7686d403 "D" |
|
58 | 58 | D: replacing untracked file |
|
59 | 59 | D |
|
60 | 60 | |
|
61 | 61 | |
|
62 | 62 | HG: Enter commit message. Lines beginning with 'HG:' are removed. |
|
63 | 63 | HG: Leave message empty to abort commit. |
|
64 | 64 | HG: -- |
|
65 | 65 | HG: user: Nicolas Dumazet <nicdumz.commits@gmail.com> |
|
66 | 66 | HG: branch 'default' |
|
67 | 67 | HG: added D |
|
68 | 68 | saved backup bundle to $TESTTMP/a1/.hg/strip-backup/32af7686d403-6f7dface-rebase.hg (glob) |
|
69 | 69 | $ cat D.orig |
|
70 | 70 | collide |
|
71 | 71 | $ rm D.orig |
|
72 | 72 | |
|
73 | 73 | $ hg tglog |
|
74 | 74 | o 7: 'D' |
|
75 | 75 | | |
|
76 | 76 | @ 6: 'H' |
|
77 | 77 | | |
|
78 | 78 | | o 5: 'G' |
|
79 | 79 | |/| |
|
80 | 80 | o | 4: 'F' |
|
81 | 81 | | | |
|
82 | 82 | | o 3: 'E' |
|
83 | 83 | |/ |
|
84 | 84 | | o 2: 'C' |
|
85 | 85 | | | |
|
86 | 86 | | o 1: 'B' |
|
87 | 87 | |/ |
|
88 | 88 | o 0: 'A' |
|
89 | 89 | |
|
90 | 90 | $ cd .. |
|
91 | 91 | |
|
92 | 92 | |
|
93 | 93 | D onto F - intermediate point: |
|
94 | 94 | (this also tests that editor is not invoked if '--edit' is not specified, and |
|
95 | 95 | that we can ignore for colliding untracked files) |
|
96 | 96 | |
|
97 | 97 | $ hg clone -q -u . a a2 |
|
98 | 98 | $ cd a2 |
|
99 | 99 | $ echo collide > D |
|
100 | 100 | |
|
101 | 101 | $ HGEDITOR=cat hg rebase -s 3 -d 5 --config merge.checkunknown=ignore |
|
102 | 102 | rebasing 3:32af7686d403 "D" |
|
103 | 103 | saved backup bundle to $TESTTMP/a2/.hg/strip-backup/32af7686d403-6f7dface-rebase.hg (glob) |
|
104 | 104 | $ cat D.orig |
|
105 | 105 | collide |
|
106 | 106 | $ rm D.orig |
|
107 | 107 | |
|
108 | 108 | $ hg tglog |
|
109 | 109 | o 7: 'D' |
|
110 | 110 | | |
|
111 | 111 | | @ 6: 'H' |
|
112 | 112 | |/ |
|
113 | 113 | | o 5: 'G' |
|
114 | 114 | |/| |
|
115 | 115 | o | 4: 'F' |
|
116 | 116 | | | |
|
117 | 117 | | o 3: 'E' |
|
118 | 118 | |/ |
|
119 | 119 | | o 2: 'C' |
|
120 | 120 | | | |
|
121 | 121 | | o 1: 'B' |
|
122 | 122 | |/ |
|
123 | 123 | o 0: 'A' |
|
124 | 124 | |
|
125 | 125 | $ cd .. |
|
126 | 126 | |
|
127 | 127 | |
|
128 | 128 | E onto H - skip of G: |
|
129 | 129 | (this also tests that we can overwrite untracked files and don't create backups |
|
130 | 130 | if they have the same contents) |
|
131 | 131 | |
|
132 | 132 | $ hg clone -q -u . a a3 |
|
133 | 133 | $ cd a3 |
|
134 | 134 | $ hg cat -r 4 E | tee E |
|
135 | 135 | E |
|
136 | 136 | |
|
137 | 137 | $ hg rebase -s 4 -d 7 |
|
138 | 138 | rebasing 4:9520eea781bc "E" |
|
139 | 139 | rebasing 6:eea13746799a "G" |
|
140 | 140 | note: rebase of 6:eea13746799a created no changes to commit |
|
141 | 141 | saved backup bundle to $TESTTMP/a3/.hg/strip-backup/9520eea781bc-fcd8edd4-rebase.hg (glob) |
|
142 | 142 | $ f E.orig |
|
143 | 143 | E.orig: file not found |
|
144 | 144 | |
|
145 | 145 | $ hg tglog |
|
146 | 146 | o 6: 'E' |
|
147 | 147 | | |
|
148 | 148 | @ 5: 'H' |
|
149 | 149 | | |
|
150 | 150 | o 4: 'F' |
|
151 | 151 | | |
|
152 | 152 | | o 3: 'D' |
|
153 | 153 | | | |
|
154 | 154 | | o 2: 'C' |
|
155 | 155 | | | |
|
156 | 156 | | o 1: 'B' |
|
157 | 157 | |/ |
|
158 | 158 | o 0: 'A' |
|
159 | 159 | |
|
160 | 160 | $ cd .. |
|
161 | 161 | |
|
162 | 162 | |
|
163 | 163 | F onto E - rebase of a branching point (skip G): |
|
164 | 164 | |
|
165 | 165 | $ hg clone -q -u . a a4 |
|
166 | 166 | $ cd a4 |
|
167 | 167 | |
|
168 | 168 | $ hg rebase -s 5 -d 4 |
|
169 | 169 | rebasing 5:24b6387c8c8c "F" |
|
170 | 170 | rebasing 6:eea13746799a "G" |
|
171 | 171 | note: rebase of 6:eea13746799a created no changes to commit |
|
172 | 172 | rebasing 7:02de42196ebe "H" (tip) |
|
173 | 173 | saved backup bundle to $TESTTMP/a4/.hg/strip-backup/24b6387c8c8c-c3fe765d-rebase.hg (glob) |
|
174 | 174 | |
|
175 | 175 | $ hg tglog |
|
176 | 176 | @ 6: 'H' |
|
177 | 177 | | |
|
178 | 178 | o 5: 'F' |
|
179 | 179 | | |
|
180 | 180 | o 4: 'E' |
|
181 | 181 | | |
|
182 | 182 | | o 3: 'D' |
|
183 | 183 | | | |
|
184 | 184 | | o 2: 'C' |
|
185 | 185 | | | |
|
186 | 186 | | o 1: 'B' |
|
187 | 187 | |/ |
|
188 | 188 | o 0: 'A' |
|
189 | 189 | |
|
190 | 190 | $ cd .. |
|
191 | 191 | |
|
192 | 192 | |
|
193 | 193 | G onto H - merged revision having a parent in ancestors of target: |
|
194 | 194 | |
|
195 | 195 | $ hg clone -q -u . a a5 |
|
196 | 196 | $ cd a5 |
|
197 | 197 | |
|
198 | 198 | $ hg rebase -s 6 -d 7 |
|
199 | 199 | rebasing 6:eea13746799a "G" |
|
200 | 200 | saved backup bundle to $TESTTMP/a5/.hg/strip-backup/eea13746799a-883828ed-rebase.hg (glob) |
|
201 | 201 | |
|
202 | 202 | $ hg tglog |
|
203 | 203 | o 7: 'G' |
|
204 | 204 | |\ |
|
205 | 205 | | @ 6: 'H' |
|
206 | 206 | | | |
|
207 | 207 | | o 5: 'F' |
|
208 | 208 | | | |
|
209 | 209 | o | 4: 'E' |
|
210 | 210 | |/ |
|
211 | 211 | | o 3: 'D' |
|
212 | 212 | | | |
|
213 | 213 | | o 2: 'C' |
|
214 | 214 | | | |
|
215 | 215 | | o 1: 'B' |
|
216 | 216 | |/ |
|
217 | 217 | o 0: 'A' |
|
218 | 218 | |
|
219 | 219 | $ cd .. |
|
220 | 220 | |
|
221 | 221 | |
|
222 | 222 | F onto B - G maintains E as parent: |
|
223 | 223 | |
|
224 | 224 | $ hg clone -q -u . a a6 |
|
225 | 225 | $ cd a6 |
|
226 | 226 | |
|
227 | 227 | $ hg rebase -s 5 -d 1 |
|
228 | 228 | rebasing 5:24b6387c8c8c "F" |
|
229 | 229 | rebasing 6:eea13746799a "G" |
|
230 | 230 | rebasing 7:02de42196ebe "H" (tip) |
|
231 | 231 | saved backup bundle to $TESTTMP/a6/.hg/strip-backup/24b6387c8c8c-c3fe765d-rebase.hg (glob) |
|
232 | 232 | |
|
233 | 233 | $ hg tglog |
|
234 | 234 | @ 7: 'H' |
|
235 | 235 | | |
|
236 | 236 | | o 6: 'G' |
|
237 | 237 | |/| |
|
238 | 238 | o | 5: 'F' |
|
239 | 239 | | | |
|
240 | 240 | | o 4: 'E' |
|
241 | 241 | | | |
|
242 | 242 | | | o 3: 'D' |
|
243 | 243 | | | | |
|
244 | 244 | +---o 2: 'C' |
|
245 | 245 | | | |
|
246 | 246 | o | 1: 'B' |
|
247 | 247 | |/ |
|
248 | 248 | o 0: 'A' |
|
249 | 249 | |
|
250 | 250 | $ cd .. |
|
251 | 251 | |
|
252 | 252 | |
|
253 | 253 | These will fail (using --source): |
|
254 | 254 | |
|
255 | 255 | G onto F - rebase onto an ancestor: |
|
256 | 256 | |
|
257 | 257 | $ hg clone -q -u . a a7 |
|
258 | 258 | $ cd a7 |
|
259 | 259 | |
|
260 | 260 | $ hg rebase -s 6 -d 5 |
|
261 | 261 | nothing to rebase |
|
262 | 262 | [1] |
|
263 | 263 | |
|
264 | 264 | F onto G - rebase onto a descendant: |
|
265 | 265 | |
|
266 | 266 | $ hg rebase -s 5 -d 6 |
|
267 | 267 | abort: source is ancestor of destination |
|
268 | 268 | [255] |
|
269 | 269 | |
|
270 | 270 | G onto B - merge revision with both parents not in ancestors of target: |
|
271 | 271 | |
|
272 | 272 | $ hg rebase -s 6 -d 1 |
|
273 | 273 | rebasing 6:eea13746799a "G" |
|
274 | 274 | abort: cannot use revision 6 as base, result would have 3 parents |
|
275 | 275 | [255] |
|
276 | 276 | $ hg rebase --abort |
|
277 | 277 | rebase aborted |
|
278 | 278 | |
|
279 | 279 | These will abort gracefully (using --base): |
|
280 | 280 | |
|
281 | 281 | G onto G - rebase onto same changeset: |
|
282 | 282 | |
|
283 | 283 | $ hg rebase -b 6 -d 6 |
|
284 | 284 | nothing to rebase - eea13746799a is both "base" and destination |
|
285 | 285 | [1] |
|
286 | 286 | |
|
287 | 287 | G onto F - rebase onto an ancestor: |
|
288 | 288 | |
|
289 | 289 | $ hg rebase -b 6 -d 5 |
|
290 | 290 | nothing to rebase |
|
291 | 291 | [1] |
|
292 | 292 | |
|
293 | 293 | F onto G - rebase onto a descendant: |
|
294 | 294 | |
|
295 | 295 | $ hg rebase -b 5 -d 6 |
|
296 | 296 | nothing to rebase - "base" 24b6387c8c8c is already an ancestor of destination eea13746799a |
|
297 | 297 | [1] |
|
298 | 298 | |
|
299 | 299 | C onto A - rebase onto an ancestor: |
|
300 | 300 | |
|
301 | 301 | $ hg rebase -d 0 -s 2 |
|
302 | 302 | rebasing 2:5fddd98957c8 "C" |
|
303 | 303 | rebasing 3:32af7686d403 "D" |
|
304 | 304 | saved backup bundle to $TESTTMP/a7/.hg/strip-backup/5fddd98957c8-f9244fa1-rebase.hg (glob) |
|
305 | 305 | $ hg tglog |
|
306 | 306 | o 7: 'D' |
|
307 | 307 | | |
|
308 | 308 | o 6: 'C' |
|
309 | 309 | | |
|
310 | 310 | | @ 5: 'H' |
|
311 | 311 | | | |
|
312 | 312 | | | o 4: 'G' |
|
313 | 313 | | |/| |
|
314 | 314 | | o | 3: 'F' |
|
315 | 315 | |/ / |
|
316 | 316 | | o 2: 'E' |
|
317 | 317 | |/ |
|
318 | 318 | | o 1: 'B' |
|
319 | 319 | |/ |
|
320 | 320 | o 0: 'A' |
|
321 | 321 | |
|
322 | 322 | |
|
323 | 323 | Check rebasing public changeset |
|
324 | 324 | |
|
325 | 325 | $ hg pull --config phases.publish=True -q -r 6 . # update phase of 6 |
|
326 | 326 | $ hg rebase -d 0 -b 6 |
|
327 | 327 | nothing to rebase |
|
328 | 328 | [1] |
|
329 | 329 | $ hg rebase -d 5 -b 6 |
|
330 | 330 | abort: can't rebase public changeset e1c4361dd923 |
|
331 | 331 | (see 'hg help phases' for details) |
|
332 | 332 | [255] |
|
333 | 333 | $ hg rebase -d 5 -r '1 + (6::)' |
|
334 | 334 | abort: can't rebase public changeset e1c4361dd923 |
|
335 | 335 | (see 'hg help phases' for details) |
|
336 | 336 | [255] |
|
337 | 337 | |
|
338 | 338 | $ hg rebase -d 5 -b 6 --keep |
|
339 | 339 | rebasing 6:e1c4361dd923 "C" |
|
340 | 340 | rebasing 7:c9659aac0000 "D" (tip) |
|
341 | 341 | |
|
342 | 342 | Check rebasing mutable changeset |
|
343 | 343 | Source phase greater or equal to destination phase: new changeset get the phase of source: |
|
344 | 344 | $ hg id -n |
|
345 | 345 | 5 |
|
346 | 346 | $ hg rebase -s9 -d0 |
|
347 | 347 | rebasing 9:2b23e52411f4 "D" (tip) |
|
348 | 348 | saved backup bundle to $TESTTMP/a7/.hg/strip-backup/2b23e52411f4-f942decf-rebase.hg (glob) |
|
349 | 349 | $ hg id -n # check we updated back to parent |
|
350 | 350 | 5 |
|
351 | 351 | $ hg log --template "{phase}\n" -r 9 |
|
352 | 352 | draft |
|
353 | 353 | $ hg rebase -s9 -d1 |
|
354 | 354 | rebasing 9:2cb10d0cfc6c "D" (tip) |
|
355 | 355 | saved backup bundle to $TESTTMP/a7/.hg/strip-backup/2cb10d0cfc6c-ddb0f256-rebase.hg (glob) |
|
356 | 356 | $ hg log --template "{phase}\n" -r 9 |
|
357 | 357 | draft |
|
358 | 358 | $ hg phase --force --secret 9 |
|
359 | 359 | $ hg rebase -s9 -d0 |
|
360 | 360 | rebasing 9:c5b12b67163a "D" (tip) |
|
361 | 361 | saved backup bundle to $TESTTMP/a7/.hg/strip-backup/c5b12b67163a-4e372053-rebase.hg (glob) |
|
362 | 362 | $ hg log --template "{phase}\n" -r 9 |
|
363 | 363 | secret |
|
364 | 364 | $ hg rebase -s9 -d1 |
|
365 | 365 | rebasing 9:2a0524f868ac "D" (tip) |
|
366 | 366 | saved backup bundle to $TESTTMP/a7/.hg/strip-backup/2a0524f868ac-cefd8574-rebase.hg (glob) |
|
367 | 367 | $ hg log --template "{phase}\n" -r 9 |
|
368 | 368 | secret |
|
369 | 369 | Source phase lower than destination phase: new changeset get the phase of destination: |
|
370 | 370 | $ hg rebase -s8 -d9 |
|
371 | 371 | rebasing 8:6d4f22462821 "C" |
|
372 | 372 | saved backup bundle to $TESTTMP/a7/.hg/strip-backup/6d4f22462821-3441f70b-rebase.hg (glob) |
|
373 | 373 | $ hg log --template "{phase}\n" -r 'rev(9)' |
|
374 | 374 | secret |
|
375 | 375 | |
|
376 | 376 | $ cd .. |
|
377 | 377 | |
|
378 | 378 | Check that temporary bundle doesn't lose phase when not using generaldelta |
|
379 | 379 | |
|
380 | 380 | $ hg --config format.usegeneraldelta=no init issue5678 |
|
381 | 381 | $ cd issue5678 |
|
382 | 382 | $ grep generaldelta .hg/requires |
|
383 | 383 | [1] |
|
384 | 384 | $ echo a > a |
|
385 | 385 | $ hg ci -Aqm a |
|
386 | 386 | $ echo b > b |
|
387 | 387 | $ hg ci -Aqm b |
|
388 | 388 | $ hg co -q '.^' |
|
389 | 389 | $ echo c > c |
|
390 | 390 | $ hg ci -Aqm c |
|
391 | 391 | $ hg phase --public |
|
392 | 392 | $ hg log -G -T '{rev}:{node|shortest} {phase} {desc}\n' |
|
393 | 393 | @ 2:d36c public c |
|
394 | 394 | | |
|
395 | 395 | | o 1:d2ae draft b |
|
396 | 396 | |/ |
|
397 | 397 | o 0:cb9a public a |
|
398 | 398 | |
|
399 | 399 | $ hg rebase -s 1 -d 2 |
|
400 | 400 | rebasing 1:d2ae7f538514 "b" |
|
401 | 401 | saved backup bundle to $TESTTMP/issue5678/.hg/strip-backup/d2ae7f538514-2953539b-rebase.hg (glob) |
|
402 | BROKEN: d36c should remain public | |
|
403 | 402 | $ hg log -G -T '{rev}:{node|shortest} {phase} {desc}\n' |
|
404 | 403 | o 2:c882 draft b |
|
405 | 404 | | |
|
406 |
@ 1:d36c |
|
|
405 | @ 1:d36c public c | |
|
407 | 406 | | |
|
408 | 407 | o 0:cb9a public a |
|
409 | 408 | |
|
410 | 409 | $ cd .. |
|
411 | 410 | |
|
412 | 411 | Test for revset |
|
413 | 412 | |
|
414 | 413 | We need a bit different graph |
|
415 | 414 | All destination are B |
|
416 | 415 | |
|
417 | 416 | $ hg init ah |
|
418 | 417 | $ cd ah |
|
419 | 418 | $ hg unbundle "$TESTDIR/bundles/rebase-revset.hg" |
|
420 | 419 | adding changesets |
|
421 | 420 | adding manifests |
|
422 | 421 | adding file changes |
|
423 | 422 | added 9 changesets with 9 changes to 9 files (+2 heads) |
|
424 | 423 | (run 'hg heads' to see heads, 'hg merge' to merge) |
|
425 | 424 | $ hg tglog |
|
426 | 425 | o 8: 'I' |
|
427 | 426 | | |
|
428 | 427 | o 7: 'H' |
|
429 | 428 | | |
|
430 | 429 | o 6: 'G' |
|
431 | 430 | | |
|
432 | 431 | | o 5: 'F' |
|
433 | 432 | | | |
|
434 | 433 | | o 4: 'E' |
|
435 | 434 | |/ |
|
436 | 435 | o 3: 'D' |
|
437 | 436 | | |
|
438 | 437 | o 2: 'C' |
|
439 | 438 | | |
|
440 | 439 | | o 1: 'B' |
|
441 | 440 | |/ |
|
442 | 441 | o 0: 'A' |
|
443 | 442 | |
|
444 | 443 | $ cd .. |
|
445 | 444 | |
|
446 | 445 | |
|
447 | 446 | Simple case with keep: |
|
448 | 447 | |
|
449 | 448 | Source on have two descendant heads but ask for one |
|
450 | 449 | |
|
451 | 450 | $ hg clone -q -u . ah ah1 |
|
452 | 451 | $ cd ah1 |
|
453 | 452 | $ hg rebase -r '2::8' -d 1 |
|
454 | 453 | abort: can't remove original changesets with unrebased descendants |
|
455 | 454 | (use --keep to keep original changesets) |
|
456 | 455 | [255] |
|
457 | 456 | $ hg rebase -r '2::8' -d 1 -k |
|
458 | 457 | rebasing 2:c9e50f6cdc55 "C" |
|
459 | 458 | rebasing 3:ffd453c31098 "D" |
|
460 | 459 | rebasing 6:3d8a618087a7 "G" |
|
461 | 460 | rebasing 7:72434a4e60b0 "H" |
|
462 | 461 | rebasing 8:479ddb54a924 "I" (tip) |
|
463 | 462 | $ hg tglog |
|
464 | 463 | o 13: 'I' |
|
465 | 464 | | |
|
466 | 465 | o 12: 'H' |
|
467 | 466 | | |
|
468 | 467 | o 11: 'G' |
|
469 | 468 | | |
|
470 | 469 | o 10: 'D' |
|
471 | 470 | | |
|
472 | 471 | o 9: 'C' |
|
473 | 472 | | |
|
474 | 473 | | o 8: 'I' |
|
475 | 474 | | | |
|
476 | 475 | | o 7: 'H' |
|
477 | 476 | | | |
|
478 | 477 | | o 6: 'G' |
|
479 | 478 | | | |
|
480 | 479 | | | o 5: 'F' |
|
481 | 480 | | | | |
|
482 | 481 | | | o 4: 'E' |
|
483 | 482 | | |/ |
|
484 | 483 | | o 3: 'D' |
|
485 | 484 | | | |
|
486 | 485 | | o 2: 'C' |
|
487 | 486 | | | |
|
488 | 487 | o | 1: 'B' |
|
489 | 488 | |/ |
|
490 | 489 | o 0: 'A' |
|
491 | 490 | |
|
492 | 491 | |
|
493 | 492 | $ cd .. |
|
494 | 493 | |
|
495 | 494 | Base on have one descendant heads we ask for but common ancestor have two |
|
496 | 495 | |
|
497 | 496 | $ hg clone -q -u . ah ah2 |
|
498 | 497 | $ cd ah2 |
|
499 | 498 | $ hg rebase -r '3::8' -d 1 |
|
500 | 499 | abort: can't remove original changesets with unrebased descendants |
|
501 | 500 | (use --keep to keep original changesets) |
|
502 | 501 | [255] |
|
503 | 502 | $ hg rebase -r '3::8' -d 1 --keep |
|
504 | 503 | rebasing 3:ffd453c31098 "D" |
|
505 | 504 | rebasing 6:3d8a618087a7 "G" |
|
506 | 505 | rebasing 7:72434a4e60b0 "H" |
|
507 | 506 | rebasing 8:479ddb54a924 "I" (tip) |
|
508 | 507 | $ hg tglog |
|
509 | 508 | o 12: 'I' |
|
510 | 509 | | |
|
511 | 510 | o 11: 'H' |
|
512 | 511 | | |
|
513 | 512 | o 10: 'G' |
|
514 | 513 | | |
|
515 | 514 | o 9: 'D' |
|
516 | 515 | | |
|
517 | 516 | | o 8: 'I' |
|
518 | 517 | | | |
|
519 | 518 | | o 7: 'H' |
|
520 | 519 | | | |
|
521 | 520 | | o 6: 'G' |
|
522 | 521 | | | |
|
523 | 522 | | | o 5: 'F' |
|
524 | 523 | | | | |
|
525 | 524 | | | o 4: 'E' |
|
526 | 525 | | |/ |
|
527 | 526 | | o 3: 'D' |
|
528 | 527 | | | |
|
529 | 528 | | o 2: 'C' |
|
530 | 529 | | | |
|
531 | 530 | o | 1: 'B' |
|
532 | 531 | |/ |
|
533 | 532 | o 0: 'A' |
|
534 | 533 | |
|
535 | 534 | |
|
536 | 535 | $ cd .. |
|
537 | 536 | |
|
538 | 537 | rebase subset |
|
539 | 538 | |
|
540 | 539 | $ hg clone -q -u . ah ah3 |
|
541 | 540 | $ cd ah3 |
|
542 | 541 | $ hg rebase -r '3::7' -d 1 |
|
543 | 542 | abort: can't remove original changesets with unrebased descendants |
|
544 | 543 | (use --keep to keep original changesets) |
|
545 | 544 | [255] |
|
546 | 545 | $ hg rebase -r '3::7' -d 1 --keep |
|
547 | 546 | rebasing 3:ffd453c31098 "D" |
|
548 | 547 | rebasing 6:3d8a618087a7 "G" |
|
549 | 548 | rebasing 7:72434a4e60b0 "H" |
|
550 | 549 | $ hg tglog |
|
551 | 550 | o 11: 'H' |
|
552 | 551 | | |
|
553 | 552 | o 10: 'G' |
|
554 | 553 | | |
|
555 | 554 | o 9: 'D' |
|
556 | 555 | | |
|
557 | 556 | | o 8: 'I' |
|
558 | 557 | | | |
|
559 | 558 | | o 7: 'H' |
|
560 | 559 | | | |
|
561 | 560 | | o 6: 'G' |
|
562 | 561 | | | |
|
563 | 562 | | | o 5: 'F' |
|
564 | 563 | | | | |
|
565 | 564 | | | o 4: 'E' |
|
566 | 565 | | |/ |
|
567 | 566 | | o 3: 'D' |
|
568 | 567 | | | |
|
569 | 568 | | o 2: 'C' |
|
570 | 569 | | | |
|
571 | 570 | o | 1: 'B' |
|
572 | 571 | |/ |
|
573 | 572 | o 0: 'A' |
|
574 | 573 | |
|
575 | 574 | |
|
576 | 575 | $ cd .. |
|
577 | 576 | |
|
578 | 577 | rebase subset with multiple head |
|
579 | 578 | |
|
580 | 579 | $ hg clone -q -u . ah ah4 |
|
581 | 580 | $ cd ah4 |
|
582 | 581 | $ hg rebase -r '3::(7+5)' -d 1 |
|
583 | 582 | abort: can't remove original changesets with unrebased descendants |
|
584 | 583 | (use --keep to keep original changesets) |
|
585 | 584 | [255] |
|
586 | 585 | $ hg rebase -r '3::(7+5)' -d 1 --keep |
|
587 | 586 | rebasing 3:ffd453c31098 "D" |
|
588 | 587 | rebasing 4:c01897464e7f "E" |
|
589 | 588 | rebasing 5:41bfcc75ed73 "F" |
|
590 | 589 | rebasing 6:3d8a618087a7 "G" |
|
591 | 590 | rebasing 7:72434a4e60b0 "H" |
|
592 | 591 | $ hg tglog |
|
593 | 592 | o 13: 'H' |
|
594 | 593 | | |
|
595 | 594 | o 12: 'G' |
|
596 | 595 | | |
|
597 | 596 | | o 11: 'F' |
|
598 | 597 | | | |
|
599 | 598 | | o 10: 'E' |
|
600 | 599 | |/ |
|
601 | 600 | o 9: 'D' |
|
602 | 601 | | |
|
603 | 602 | | o 8: 'I' |
|
604 | 603 | | | |
|
605 | 604 | | o 7: 'H' |
|
606 | 605 | | | |
|
607 | 606 | | o 6: 'G' |
|
608 | 607 | | | |
|
609 | 608 | | | o 5: 'F' |
|
610 | 609 | | | | |
|
611 | 610 | | | o 4: 'E' |
|
612 | 611 | | |/ |
|
613 | 612 | | o 3: 'D' |
|
614 | 613 | | | |
|
615 | 614 | | o 2: 'C' |
|
616 | 615 | | | |
|
617 | 616 | o | 1: 'B' |
|
618 | 617 | |/ |
|
619 | 618 | o 0: 'A' |
|
620 | 619 | |
|
621 | 620 | |
|
622 | 621 | $ cd .. |
|
623 | 622 | |
|
624 | 623 | More advanced tests |
|
625 | 624 | |
|
626 | 625 | rebase on ancestor with revset |
|
627 | 626 | |
|
628 | 627 | $ hg clone -q -u . ah ah5 |
|
629 | 628 | $ cd ah5 |
|
630 | 629 | $ hg rebase -r '6::' -d 2 |
|
631 | 630 | rebasing 6:3d8a618087a7 "G" |
|
632 | 631 | rebasing 7:72434a4e60b0 "H" |
|
633 | 632 | rebasing 8:479ddb54a924 "I" (tip) |
|
634 | 633 | saved backup bundle to $TESTTMP/ah5/.hg/strip-backup/3d8a618087a7-b4f73f31-rebase.hg (glob) |
|
635 | 634 | $ hg tglog |
|
636 | 635 | o 8: 'I' |
|
637 | 636 | | |
|
638 | 637 | o 7: 'H' |
|
639 | 638 | | |
|
640 | 639 | o 6: 'G' |
|
641 | 640 | | |
|
642 | 641 | | o 5: 'F' |
|
643 | 642 | | | |
|
644 | 643 | | o 4: 'E' |
|
645 | 644 | | | |
|
646 | 645 | | o 3: 'D' |
|
647 | 646 | |/ |
|
648 | 647 | o 2: 'C' |
|
649 | 648 | | |
|
650 | 649 | | o 1: 'B' |
|
651 | 650 | |/ |
|
652 | 651 | o 0: 'A' |
|
653 | 652 | |
|
654 | 653 | $ cd .. |
|
655 | 654 | |
|
656 | 655 | |
|
657 | 656 | rebase with multiple root. |
|
658 | 657 | We rebase E and G on B |
|
659 | 658 | We would expect heads are I, F if it was supported |
|
660 | 659 | |
|
661 | 660 | $ hg clone -q -u . ah ah6 |
|
662 | 661 | $ cd ah6 |
|
663 | 662 | $ hg rebase -r '(4+6)::' -d 1 |
|
664 | 663 | rebasing 4:c01897464e7f "E" |
|
665 | 664 | rebasing 5:41bfcc75ed73 "F" |
|
666 | 665 | rebasing 6:3d8a618087a7 "G" |
|
667 | 666 | rebasing 7:72434a4e60b0 "H" |
|
668 | 667 | rebasing 8:479ddb54a924 "I" (tip) |
|
669 | 668 | saved backup bundle to $TESTTMP/ah6/.hg/strip-backup/3d8a618087a7-aae93a24-rebase.hg (glob) |
|
670 | 669 | $ hg tglog |
|
671 | 670 | o 8: 'I' |
|
672 | 671 | | |
|
673 | 672 | o 7: 'H' |
|
674 | 673 | | |
|
675 | 674 | o 6: 'G' |
|
676 | 675 | | |
|
677 | 676 | | o 5: 'F' |
|
678 | 677 | | | |
|
679 | 678 | | o 4: 'E' |
|
680 | 679 | |/ |
|
681 | 680 | | o 3: 'D' |
|
682 | 681 | | | |
|
683 | 682 | | o 2: 'C' |
|
684 | 683 | | | |
|
685 | 684 | o | 1: 'B' |
|
686 | 685 | |/ |
|
687 | 686 | o 0: 'A' |
|
688 | 687 | |
|
689 | 688 | $ cd .. |
|
690 | 689 | |
|
691 | 690 | More complex rebase with multiple roots |
|
692 | 691 | each root have a different common ancestor with the destination and this is a detach |
|
693 | 692 | |
|
694 | 693 | (setup) |
|
695 | 694 | |
|
696 | 695 | $ hg clone -q -u . a a8 |
|
697 | 696 | $ cd a8 |
|
698 | 697 | $ echo I > I |
|
699 | 698 | $ hg add I |
|
700 | 699 | $ hg commit -m I |
|
701 | 700 | $ hg up 4 |
|
702 | 701 | 1 files updated, 0 files merged, 3 files removed, 0 files unresolved |
|
703 | 702 | $ echo I > J |
|
704 | 703 | $ hg add J |
|
705 | 704 | $ hg commit -m J |
|
706 | 705 | created new head |
|
707 | 706 | $ echo I > K |
|
708 | 707 | $ hg add K |
|
709 | 708 | $ hg commit -m K |
|
710 | 709 | $ hg tglog |
|
711 | 710 | @ 10: 'K' |
|
712 | 711 | | |
|
713 | 712 | o 9: 'J' |
|
714 | 713 | | |
|
715 | 714 | | o 8: 'I' |
|
716 | 715 | | | |
|
717 | 716 | | o 7: 'H' |
|
718 | 717 | | | |
|
719 | 718 | +---o 6: 'G' |
|
720 | 719 | | |/ |
|
721 | 720 | | o 5: 'F' |
|
722 | 721 | | | |
|
723 | 722 | o | 4: 'E' |
|
724 | 723 | |/ |
|
725 | 724 | | o 3: 'D' |
|
726 | 725 | | | |
|
727 | 726 | | o 2: 'C' |
|
728 | 727 | | | |
|
729 | 728 | | o 1: 'B' |
|
730 | 729 | |/ |
|
731 | 730 | o 0: 'A' |
|
732 | 731 | |
|
733 | 732 | (actual test) |
|
734 | 733 | |
|
735 | 734 | $ hg rebase --dest 'desc(G)' --rev 'desc(K) + desc(I)' |
|
736 | 735 | rebasing 8:e7ec4e813ba6 "I" |
|
737 | 736 | rebasing 10:23a4ace37988 "K" (tip) |
|
738 | 737 | saved backup bundle to $TESTTMP/a8/.hg/strip-backup/23a4ace37988-b06984b3-rebase.hg (glob) |
|
739 | 738 | $ hg log --rev 'children(desc(G))' |
|
740 | 739 | changeset: 9:adb617877056 |
|
741 | 740 | parent: 6:eea13746799a |
|
742 | 741 | user: test |
|
743 | 742 | date: Thu Jan 01 00:00:00 1970 +0000 |
|
744 | 743 | summary: I |
|
745 | 744 | |
|
746 | 745 | changeset: 10:882431a34a0e |
|
747 | 746 | tag: tip |
|
748 | 747 | parent: 6:eea13746799a |
|
749 | 748 | user: test |
|
750 | 749 | date: Thu Jan 01 00:00:00 1970 +0000 |
|
751 | 750 | summary: K |
|
752 | 751 | |
|
753 | 752 | $ hg tglog |
|
754 | 753 | @ 10: 'K' |
|
755 | 754 | | |
|
756 | 755 | | o 9: 'I' |
|
757 | 756 | |/ |
|
758 | 757 | | o 8: 'J' |
|
759 | 758 | | | |
|
760 | 759 | | | o 7: 'H' |
|
761 | 760 | | | | |
|
762 | 761 | o---+ 6: 'G' |
|
763 | 762 | |/ / |
|
764 | 763 | | o 5: 'F' |
|
765 | 764 | | | |
|
766 | 765 | o | 4: 'E' |
|
767 | 766 | |/ |
|
768 | 767 | | o 3: 'D' |
|
769 | 768 | | | |
|
770 | 769 | | o 2: 'C' |
|
771 | 770 | | | |
|
772 | 771 | | o 1: 'B' |
|
773 | 772 | |/ |
|
774 | 773 | o 0: 'A' |
|
775 | 774 | |
|
776 | 775 | |
|
777 | 776 | Test that rebase is not confused by $CWD disappearing during rebase (issue4121) |
|
778 | 777 | |
|
779 | 778 | $ cd .. |
|
780 | 779 | $ hg init cwd-vanish |
|
781 | 780 | $ cd cwd-vanish |
|
782 | 781 | $ touch initial-file |
|
783 | 782 | $ hg add initial-file |
|
784 | 783 | $ hg commit -m 'initial commit' |
|
785 | 784 | $ touch dest-file |
|
786 | 785 | $ hg add dest-file |
|
787 | 786 | $ hg commit -m 'dest commit' |
|
788 | 787 | $ hg up 0 |
|
789 | 788 | 0 files updated, 0 files merged, 1 files removed, 0 files unresolved |
|
790 | 789 | $ touch other-file |
|
791 | 790 | $ hg add other-file |
|
792 | 791 | $ hg commit -m 'first source commit' |
|
793 | 792 | created new head |
|
794 | 793 | $ mkdir subdir |
|
795 | 794 | $ cd subdir |
|
796 | 795 | $ touch subfile |
|
797 | 796 | $ hg add subfile |
|
798 | 797 | $ hg commit -m 'second source with subdir' |
|
799 | 798 | |
|
800 | 799 | $ hg rebase -b . -d 1 --traceback |
|
801 | 800 | rebasing 2:779a07b1b7a0 "first source commit" |
|
802 | 801 | current directory was removed (rmcwd !) |
|
803 | 802 | (consider changing to repo root: $TESTTMP/cwd-vanish) (rmcwd !) |
|
804 | 803 | rebasing 3:a7d6f3a00bf3 "second source with subdir" (tip) |
|
805 | 804 | saved backup bundle to $TESTTMP/cwd-vanish/.hg/strip-backup/779a07b1b7a0-853e0073-rebase.hg (glob) |
|
806 | 805 | |
|
807 | 806 | Get back to the root of cwd-vanish. Note that even though `cd ..` |
|
808 | 807 | works on most systems, it does not work on FreeBSD 10, so we use an |
|
809 | 808 | absolute path to get back to the repository. |
|
810 | 809 | $ cd $TESTTMP |
|
811 | 810 | |
|
812 | 811 | Test that rebase is done in topo order (issue5370) |
|
813 | 812 | |
|
814 | 813 | $ hg init order |
|
815 | 814 | $ cd order |
|
816 | 815 | $ touch a && hg add a && hg ci -m A |
|
817 | 816 | $ touch b && hg add b && hg ci -m B |
|
818 | 817 | $ touch c && hg add c && hg ci -m C |
|
819 | 818 | $ hg up 1 |
|
820 | 819 | 0 files updated, 0 files merged, 1 files removed, 0 files unresolved |
|
821 | 820 | $ touch d && hg add d && hg ci -m D |
|
822 | 821 | created new head |
|
823 | 822 | $ hg up 2 |
|
824 | 823 | 1 files updated, 0 files merged, 1 files removed, 0 files unresolved |
|
825 | 824 | $ touch e && hg add e && hg ci -m E |
|
826 | 825 | $ hg up 3 |
|
827 | 826 | 1 files updated, 0 files merged, 2 files removed, 0 files unresolved |
|
828 | 827 | $ touch f && hg add f && hg ci -m F |
|
829 | 828 | $ hg up 0 |
|
830 | 829 | 0 files updated, 0 files merged, 3 files removed, 0 files unresolved |
|
831 | 830 | $ touch g && hg add g && hg ci -m G |
|
832 | 831 | created new head |
|
833 | 832 | |
|
834 | 833 | $ hg tglog |
|
835 | 834 | @ 6: 'G' |
|
836 | 835 | | |
|
837 | 836 | | o 5: 'F' |
|
838 | 837 | | | |
|
839 | 838 | | | o 4: 'E' |
|
840 | 839 | | | | |
|
841 | 840 | | o | 3: 'D' |
|
842 | 841 | | | | |
|
843 | 842 | | | o 2: 'C' |
|
844 | 843 | | |/ |
|
845 | 844 | | o 1: 'B' |
|
846 | 845 | |/ |
|
847 | 846 | o 0: 'A' |
|
848 | 847 | |
|
849 | 848 | |
|
850 | 849 | $ hg rebase -s 1 -d 6 |
|
851 | 850 | rebasing 1:76035bbd54bd "B" |
|
852 | 851 | rebasing 2:d84f5cfaaf14 "C" |
|
853 | 852 | rebasing 4:82ae8dc7a9b7 "E" |
|
854 | 853 | rebasing 3:ab709c9f7171 "D" |
|
855 | 854 | rebasing 5:412b391de760 "F" |
|
856 | 855 | saved backup bundle to $TESTTMP/order/.hg/strip-backup/76035bbd54bd-e341bc99-rebase.hg (glob) |
|
857 | 856 | |
|
858 | 857 | $ hg tglog |
|
859 | 858 | o 6: 'F' |
|
860 | 859 | | |
|
861 | 860 | o 5: 'D' |
|
862 | 861 | | |
|
863 | 862 | | o 4: 'E' |
|
864 | 863 | | | |
|
865 | 864 | | o 3: 'C' |
|
866 | 865 | |/ |
|
867 | 866 | o 2: 'B' |
|
868 | 867 | | |
|
869 | 868 | @ 1: 'G' |
|
870 | 869 | | |
|
871 | 870 | o 0: 'A' |
|
872 | 871 | |
|
873 | 872 | |
|
874 | 873 | Test experimental revset |
|
875 | 874 | ======================== |
|
876 | 875 | |
|
877 | 876 | $ cd ../cwd-vanish |
|
878 | 877 | |
|
879 | 878 | Make the repo a bit more interesting |
|
880 | 879 | |
|
881 | 880 | $ hg up 1 |
|
882 | 881 | 0 files updated, 0 files merged, 2 files removed, 0 files unresolved |
|
883 | 882 | $ echo aaa > aaa |
|
884 | 883 | $ hg add aaa |
|
885 | 884 | $ hg commit -m aaa |
|
886 | 885 | created new head |
|
887 | 886 | $ hg log -G |
|
888 | 887 | @ changeset: 4:5f7bc9025ed2 |
|
889 | 888 | | tag: tip |
|
890 | 889 | | parent: 1:58d79cc1cf43 |
|
891 | 890 | | user: test |
|
892 | 891 | | date: Thu Jan 01 00:00:00 1970 +0000 |
|
893 | 892 | | summary: aaa |
|
894 | 893 | | |
|
895 | 894 | | o changeset: 3:1910d5ff34ea |
|
896 | 895 | | | user: test |
|
897 | 896 | | | date: Thu Jan 01 00:00:00 1970 +0000 |
|
898 | 897 | | | summary: second source with subdir |
|
899 | 898 | | | |
|
900 | 899 | | o changeset: 2:82901330b6ef |
|
901 | 900 | |/ user: test |
|
902 | 901 | | date: Thu Jan 01 00:00:00 1970 +0000 |
|
903 | 902 | | summary: first source commit |
|
904 | 903 | | |
|
905 | 904 | o changeset: 1:58d79cc1cf43 |
|
906 | 905 | | user: test |
|
907 | 906 | | date: Thu Jan 01 00:00:00 1970 +0000 |
|
908 | 907 | | summary: dest commit |
|
909 | 908 | | |
|
910 | 909 | o changeset: 0:e94b687f7da3 |
|
911 | 910 | user: test |
|
912 | 911 | date: Thu Jan 01 00:00:00 1970 +0000 |
|
913 | 912 | summary: initial commit |
|
914 | 913 | |
|
915 | 914 | |
|
916 | 915 | Testing from lower head |
|
917 | 916 | |
|
918 | 917 | $ hg up 3 |
|
919 | 918 | 2 files updated, 0 files merged, 1 files removed, 0 files unresolved |
|
920 | 919 | $ hg log -r '_destrebase()' |
|
921 | 920 | changeset: 4:5f7bc9025ed2 |
|
922 | 921 | tag: tip |
|
923 | 922 | parent: 1:58d79cc1cf43 |
|
924 | 923 | user: test |
|
925 | 924 | date: Thu Jan 01 00:00:00 1970 +0000 |
|
926 | 925 | summary: aaa |
|
927 | 926 | |
|
928 | 927 | |
|
929 | 928 | Testing from upper head |
|
930 | 929 | |
|
931 | 930 | $ hg log -r '_destrebase(4)' |
|
932 | 931 | changeset: 3:1910d5ff34ea |
|
933 | 932 | user: test |
|
934 | 933 | date: Thu Jan 01 00:00:00 1970 +0000 |
|
935 | 934 | summary: second source with subdir |
|
936 | 935 | |
|
937 | 936 | $ hg up 4 |
|
938 | 937 | 1 files updated, 0 files merged, 2 files removed, 0 files unresolved |
|
939 | 938 | $ hg log -r '_destrebase()' |
|
940 | 939 | changeset: 3:1910d5ff34ea |
|
941 | 940 | user: test |
|
942 | 941 | date: Thu Jan 01 00:00:00 1970 +0000 |
|
943 | 942 | summary: second source with subdir |
|
944 | 943 | |
|
945 | 944 | Testing rebase being called inside another transaction |
|
946 | 945 | |
|
947 | 946 | $ cd $TESTTMP |
|
948 | 947 | $ hg init tr-state |
|
949 | 948 | $ cd tr-state |
|
950 | 949 | $ cat > $TESTTMP/wraprebase.py <<EOF |
|
951 | 950 | > from __future__ import absolute_import |
|
952 | 951 | > from mercurial import extensions |
|
953 | 952 | > def _rebase(orig, ui, repo, *args, **kwargs): |
|
954 | 953 | > with repo.wlock(): |
|
955 | 954 | > with repo.lock(): |
|
956 | 955 | > with repo.transaction('wrappedrebase'): |
|
957 | 956 | > return orig(ui, repo, *args, **kwargs) |
|
958 | 957 | > def wraprebase(loaded): |
|
959 | 958 | > assert loaded |
|
960 | 959 | > rebasemod = extensions.find('rebase') |
|
961 | 960 | > extensions.wrapcommand(rebasemod.cmdtable, 'rebase', _rebase) |
|
962 | 961 | > def extsetup(ui): |
|
963 | 962 | > extensions.afterloaded('rebase', wraprebase) |
|
964 | 963 | > EOF |
|
965 | 964 | |
|
966 | 965 | $ cat >> .hg/hgrc <<EOF |
|
967 | 966 | > [extensions] |
|
968 | 967 | > wraprebase=$TESTTMP/wraprebase.py |
|
969 | 968 | > [experimental] |
|
970 | 969 | > evolution=all |
|
971 | 970 | > EOF |
|
972 | 971 | |
|
973 | 972 | $ hg debugdrawdag <<'EOS' |
|
974 | 973 | > B C |
|
975 | 974 | > |/ |
|
976 | 975 | > A |
|
977 | 976 | > EOS |
|
978 | 977 | |
|
979 | 978 | $ hg rebase -s C -d B |
|
980 | 979 | rebasing 2:dc0947a82db8 "C" (C tip) |
|
981 | 980 | |
|
982 | 981 | $ [ -f .hg/rebasestate ] && echo 'WRONG: rebasestate should not exist' |
|
983 | 982 | [1] |
@@ -1,1121 +1,1121 b'' | |||
|
1 | 1 | $ echo "[format]" >> $HGRCPATH |
|
2 | 2 | $ echo "usegeneraldelta=yes" >> $HGRCPATH |
|
3 | 3 | $ echo "[extensions]" >> $HGRCPATH |
|
4 | 4 | $ echo "strip=" >> $HGRCPATH |
|
5 | 5 | $ echo "drawdag=$TESTDIR/drawdag.py" >> $HGRCPATH |
|
6 | 6 | |
|
7 | 7 | $ restore() { |
|
8 | 8 | > hg unbundle -q .hg/strip-backup/* |
|
9 | 9 | > rm .hg/strip-backup/* |
|
10 | 10 | > } |
|
11 | 11 | $ teststrip() { |
|
12 | 12 | > hg up -C $1 |
|
13 | 13 | > echo % before update $1, strip $2 |
|
14 | 14 | > hg parents |
|
15 | 15 | > hg --traceback strip $2 |
|
16 | 16 | > echo % after update $1, strip $2 |
|
17 | 17 | > hg parents |
|
18 | 18 | > restore |
|
19 | 19 | > } |
|
20 | 20 | |
|
21 | 21 | $ hg init test |
|
22 | 22 | $ cd test |
|
23 | 23 | |
|
24 | 24 | $ echo foo > bar |
|
25 | 25 | $ hg ci -Ama |
|
26 | 26 | adding bar |
|
27 | 27 | |
|
28 | 28 | $ echo more >> bar |
|
29 | 29 | $ hg ci -Amb |
|
30 | 30 | |
|
31 | 31 | $ echo blah >> bar |
|
32 | 32 | $ hg ci -Amc |
|
33 | 33 | |
|
34 | 34 | $ hg up 1 |
|
35 | 35 | 1 files updated, 0 files merged, 0 files removed, 0 files unresolved |
|
36 | 36 | $ echo blah >> bar |
|
37 | 37 | $ hg ci -Amd |
|
38 | 38 | created new head |
|
39 | 39 | |
|
40 | 40 | $ echo final >> bar |
|
41 | 41 | $ hg ci -Ame |
|
42 | 42 | |
|
43 | 43 | $ hg log |
|
44 | 44 | changeset: 4:443431ffac4f |
|
45 | 45 | tag: tip |
|
46 | 46 | user: test |
|
47 | 47 | date: Thu Jan 01 00:00:00 1970 +0000 |
|
48 | 48 | summary: e |
|
49 | 49 | |
|
50 | 50 | changeset: 3:65bd5f99a4a3 |
|
51 | 51 | parent: 1:ef3a871183d7 |
|
52 | 52 | user: test |
|
53 | 53 | date: Thu Jan 01 00:00:00 1970 +0000 |
|
54 | 54 | summary: d |
|
55 | 55 | |
|
56 | 56 | changeset: 2:264128213d29 |
|
57 | 57 | user: test |
|
58 | 58 | date: Thu Jan 01 00:00:00 1970 +0000 |
|
59 | 59 | summary: c |
|
60 | 60 | |
|
61 | 61 | changeset: 1:ef3a871183d7 |
|
62 | 62 | user: test |
|
63 | 63 | date: Thu Jan 01 00:00:00 1970 +0000 |
|
64 | 64 | summary: b |
|
65 | 65 | |
|
66 | 66 | changeset: 0:9ab35a2d17cb |
|
67 | 67 | user: test |
|
68 | 68 | date: Thu Jan 01 00:00:00 1970 +0000 |
|
69 | 69 | summary: a |
|
70 | 70 | |
|
71 | 71 | |
|
72 | 72 | $ teststrip 4 4 |
|
73 | 73 | 0 files updated, 0 files merged, 0 files removed, 0 files unresolved |
|
74 | 74 | % before update 4, strip 4 |
|
75 | 75 | changeset: 4:443431ffac4f |
|
76 | 76 | tag: tip |
|
77 | 77 | user: test |
|
78 | 78 | date: Thu Jan 01 00:00:00 1970 +0000 |
|
79 | 79 | summary: e |
|
80 | 80 | |
|
81 | 81 | 1 files updated, 0 files merged, 0 files removed, 0 files unresolved |
|
82 | 82 | saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob) |
|
83 | 83 | % after update 4, strip 4 |
|
84 | 84 | changeset: 3:65bd5f99a4a3 |
|
85 | 85 | tag: tip |
|
86 | 86 | parent: 1:ef3a871183d7 |
|
87 | 87 | user: test |
|
88 | 88 | date: Thu Jan 01 00:00:00 1970 +0000 |
|
89 | 89 | summary: d |
|
90 | 90 | |
|
91 | 91 | $ teststrip 4 3 |
|
92 | 92 | 1 files updated, 0 files merged, 0 files removed, 0 files unresolved |
|
93 | 93 | % before update 4, strip 3 |
|
94 | 94 | changeset: 4:443431ffac4f |
|
95 | 95 | tag: tip |
|
96 | 96 | user: test |
|
97 | 97 | date: Thu Jan 01 00:00:00 1970 +0000 |
|
98 | 98 | summary: e |
|
99 | 99 | |
|
100 | 100 | 1 files updated, 0 files merged, 0 files removed, 0 files unresolved |
|
101 | 101 | saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob) |
|
102 | 102 | % after update 4, strip 3 |
|
103 | 103 | changeset: 1:ef3a871183d7 |
|
104 | 104 | user: test |
|
105 | 105 | date: Thu Jan 01 00:00:00 1970 +0000 |
|
106 | 106 | summary: b |
|
107 | 107 | |
|
108 | 108 | $ teststrip 1 4 |
|
109 | 109 | 0 files updated, 0 files merged, 0 files removed, 0 files unresolved |
|
110 | 110 | % before update 1, strip 4 |
|
111 | 111 | changeset: 1:ef3a871183d7 |
|
112 | 112 | user: test |
|
113 | 113 | date: Thu Jan 01 00:00:00 1970 +0000 |
|
114 | 114 | summary: b |
|
115 | 115 | |
|
116 | 116 | saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob) |
|
117 | 117 | % after update 1, strip 4 |
|
118 | 118 | changeset: 1:ef3a871183d7 |
|
119 | 119 | user: test |
|
120 | 120 | date: Thu Jan 01 00:00:00 1970 +0000 |
|
121 | 121 | summary: b |
|
122 | 122 | |
|
123 | 123 | $ teststrip 4 2 |
|
124 | 124 | 1 files updated, 0 files merged, 0 files removed, 0 files unresolved |
|
125 | 125 | % before update 4, strip 2 |
|
126 | 126 | changeset: 4:443431ffac4f |
|
127 | 127 | tag: tip |
|
128 | 128 | user: test |
|
129 | 129 | date: Thu Jan 01 00:00:00 1970 +0000 |
|
130 | 130 | summary: e |
|
131 | 131 | |
|
132 | 132 | saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob) |
|
133 | 133 | % after update 4, strip 2 |
|
134 | 134 | changeset: 3:443431ffac4f |
|
135 | 135 | tag: tip |
|
136 | 136 | user: test |
|
137 | 137 | date: Thu Jan 01 00:00:00 1970 +0000 |
|
138 | 138 | summary: e |
|
139 | 139 | |
|
140 | 140 | $ teststrip 4 1 |
|
141 | 141 | 1 files updated, 0 files merged, 0 files removed, 0 files unresolved |
|
142 | 142 | % before update 4, strip 1 |
|
143 | 143 | changeset: 4:264128213d29 |
|
144 | 144 | tag: tip |
|
145 | 145 | parent: 1:ef3a871183d7 |
|
146 | 146 | user: test |
|
147 | 147 | date: Thu Jan 01 00:00:00 1970 +0000 |
|
148 | 148 | summary: c |
|
149 | 149 | |
|
150 | 150 | 1 files updated, 0 files merged, 0 files removed, 0 files unresolved |
|
151 | 151 | saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob) |
|
152 | 152 | % after update 4, strip 1 |
|
153 | 153 | changeset: 0:9ab35a2d17cb |
|
154 | 154 | tag: tip |
|
155 | 155 | user: test |
|
156 | 156 | date: Thu Jan 01 00:00:00 1970 +0000 |
|
157 | 157 | summary: a |
|
158 | 158 | |
|
159 | 159 | $ teststrip null 4 |
|
160 | 160 | 0 files updated, 0 files merged, 1 files removed, 0 files unresolved |
|
161 | 161 | % before update null, strip 4 |
|
162 | 162 | saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob) |
|
163 | 163 | % after update null, strip 4 |
|
164 | 164 | |
|
165 | 165 | $ hg log |
|
166 | 166 | changeset: 4:264128213d29 |
|
167 | 167 | tag: tip |
|
168 | 168 | parent: 1:ef3a871183d7 |
|
169 | 169 | user: test |
|
170 | 170 | date: Thu Jan 01 00:00:00 1970 +0000 |
|
171 | 171 | summary: c |
|
172 | 172 | |
|
173 | 173 | changeset: 3:443431ffac4f |
|
174 | 174 | user: test |
|
175 | 175 | date: Thu Jan 01 00:00:00 1970 +0000 |
|
176 | 176 | summary: e |
|
177 | 177 | |
|
178 | 178 | changeset: 2:65bd5f99a4a3 |
|
179 | 179 | user: test |
|
180 | 180 | date: Thu Jan 01 00:00:00 1970 +0000 |
|
181 | 181 | summary: d |
|
182 | 182 | |
|
183 | 183 | changeset: 1:ef3a871183d7 |
|
184 | 184 | user: test |
|
185 | 185 | date: Thu Jan 01 00:00:00 1970 +0000 |
|
186 | 186 | summary: b |
|
187 | 187 | |
|
188 | 188 | changeset: 0:9ab35a2d17cb |
|
189 | 189 | user: test |
|
190 | 190 | date: Thu Jan 01 00:00:00 1970 +0000 |
|
191 | 191 | summary: a |
|
192 | 192 | |
|
193 | 193 | $ hg up -C 4 |
|
194 | 194 | 1 files updated, 0 files merged, 0 files removed, 0 files unresolved |
|
195 | 195 | $ hg parents |
|
196 | 196 | changeset: 4:264128213d29 |
|
197 | 197 | tag: tip |
|
198 | 198 | parent: 1:ef3a871183d7 |
|
199 | 199 | user: test |
|
200 | 200 | date: Thu Jan 01 00:00:00 1970 +0000 |
|
201 | 201 | summary: c |
|
202 | 202 | |
|
203 | 203 | |
|
204 | 204 | $ hg --traceback strip 4 |
|
205 | 205 | 1 files updated, 0 files merged, 0 files removed, 0 files unresolved |
|
206 | 206 | saved backup bundle to $TESTTMP/test/.hg/strip-backup/264128213d29-0b39d6bf-backup.hg (glob) |
|
207 | 207 | $ hg parents |
|
208 | 208 | changeset: 1:ef3a871183d7 |
|
209 | 209 | user: test |
|
210 | 210 | date: Thu Jan 01 00:00:00 1970 +0000 |
|
211 | 211 | summary: b |
|
212 | 212 | |
|
213 | 213 | $ hg debugbundle .hg/strip-backup/* |
|
214 | 214 | Stream params: sortdict([('Compression', 'BZ')]) |
|
215 | 215 | changegroup -- "sortdict([('version', '02'), ('nbchanges', '1')])" |
|
216 | 216 | 264128213d290d868c54642d13aeaa3675551a78 |
|
217 | 217 | phase-heads -- 'sortdict()' |
|
218 | 218 | 264128213d290d868c54642d13aeaa3675551a78 draft |
|
219 | 219 | $ hg pull .hg/strip-backup/* |
|
220 | 220 | pulling from .hg/strip-backup/264128213d29-0b39d6bf-backup.hg |
|
221 | 221 | searching for changes |
|
222 | 222 | adding changesets |
|
223 | 223 | adding manifests |
|
224 | 224 | adding file changes |
|
225 | 225 | added 1 changesets with 0 changes to 0 files (+1 heads) |
|
226 | 226 | (run 'hg heads' to see heads, 'hg merge' to merge) |
|
227 | 227 | $ rm .hg/strip-backup/* |
|
228 | 228 | $ hg log --graph |
|
229 | 229 | o changeset: 4:264128213d29 |
|
230 | 230 | | tag: tip |
|
231 | 231 | | parent: 1:ef3a871183d7 |
|
232 | 232 | | user: test |
|
233 | 233 | | date: Thu Jan 01 00:00:00 1970 +0000 |
|
234 | 234 | | summary: c |
|
235 | 235 | | |
|
236 | 236 | | o changeset: 3:443431ffac4f |
|
237 | 237 | | | user: test |
|
238 | 238 | | | date: Thu Jan 01 00:00:00 1970 +0000 |
|
239 | 239 | | | summary: e |
|
240 | 240 | | | |
|
241 | 241 | | o changeset: 2:65bd5f99a4a3 |
|
242 | 242 | |/ user: test |
|
243 | 243 | | date: Thu Jan 01 00:00:00 1970 +0000 |
|
244 | 244 | | summary: d |
|
245 | 245 | | |
|
246 | 246 | @ changeset: 1:ef3a871183d7 |
|
247 | 247 | | user: test |
|
248 | 248 | | date: Thu Jan 01 00:00:00 1970 +0000 |
|
249 | 249 | | summary: b |
|
250 | 250 | | |
|
251 | 251 | o changeset: 0:9ab35a2d17cb |
|
252 | 252 | user: test |
|
253 | 253 | date: Thu Jan 01 00:00:00 1970 +0000 |
|
254 | 254 | summary: a |
|
255 | 255 | |
|
256 | 256 | $ hg up -C 2 |
|
257 | 257 | 1 files updated, 0 files merged, 0 files removed, 0 files unresolved |
|
258 | 258 | $ hg merge 4 |
|
259 | 259 | 0 files updated, 0 files merged, 0 files removed, 0 files unresolved |
|
260 | 260 | (branch merge, don't forget to commit) |
|
261 | 261 | |
|
262 | 262 | before strip of merge parent |
|
263 | 263 | |
|
264 | 264 | $ hg parents |
|
265 | 265 | changeset: 2:65bd5f99a4a3 |
|
266 | 266 | user: test |
|
267 | 267 | date: Thu Jan 01 00:00:00 1970 +0000 |
|
268 | 268 | summary: d |
|
269 | 269 | |
|
270 | 270 | changeset: 4:264128213d29 |
|
271 | 271 | tag: tip |
|
272 | 272 | parent: 1:ef3a871183d7 |
|
273 | 273 | user: test |
|
274 | 274 | date: Thu Jan 01 00:00:00 1970 +0000 |
|
275 | 275 | summary: c |
|
276 | 276 | |
|
277 | 277 | $ hg strip 4 |
|
278 | 278 | 1 files updated, 0 files merged, 0 files removed, 0 files unresolved |
|
279 | 279 | saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob) |
|
280 | 280 | |
|
281 | 281 | after strip of merge parent |
|
282 | 282 | |
|
283 | 283 | $ hg parents |
|
284 | 284 | changeset: 1:ef3a871183d7 |
|
285 | 285 | user: test |
|
286 | 286 | date: Thu Jan 01 00:00:00 1970 +0000 |
|
287 | 287 | summary: b |
|
288 | 288 | |
|
289 | 289 | $ restore |
|
290 | 290 | |
|
291 | 291 | $ hg up |
|
292 | 292 | 1 files updated, 0 files merged, 0 files removed, 0 files unresolved |
|
293 | 293 | updated to "264128213d29: c" |
|
294 | 294 | 1 other heads for branch "default" |
|
295 | 295 | $ hg log -G |
|
296 | 296 | @ changeset: 4:264128213d29 |
|
297 | 297 | | tag: tip |
|
298 | 298 | | parent: 1:ef3a871183d7 |
|
299 | 299 | | user: test |
|
300 | 300 | | date: Thu Jan 01 00:00:00 1970 +0000 |
|
301 | 301 | | summary: c |
|
302 | 302 | | |
|
303 | 303 | | o changeset: 3:443431ffac4f |
|
304 | 304 | | | user: test |
|
305 | 305 | | | date: Thu Jan 01 00:00:00 1970 +0000 |
|
306 | 306 | | | summary: e |
|
307 | 307 | | | |
|
308 | 308 | | o changeset: 2:65bd5f99a4a3 |
|
309 | 309 | |/ user: test |
|
310 | 310 | | date: Thu Jan 01 00:00:00 1970 +0000 |
|
311 | 311 | | summary: d |
|
312 | 312 | | |
|
313 | 313 | o changeset: 1:ef3a871183d7 |
|
314 | 314 | | user: test |
|
315 | 315 | | date: Thu Jan 01 00:00:00 1970 +0000 |
|
316 | 316 | | summary: b |
|
317 | 317 | | |
|
318 | 318 | o changeset: 0:9ab35a2d17cb |
|
319 | 319 | user: test |
|
320 | 320 | date: Thu Jan 01 00:00:00 1970 +0000 |
|
321 | 321 | summary: a |
|
322 | 322 | |
|
323 | 323 | |
|
324 | 324 | 2 is parent of 3, only one strip should happen |
|
325 | 325 | |
|
326 | 326 | $ hg strip "roots(2)" 3 |
|
327 | 327 | saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob) |
|
328 | 328 | $ hg log -G |
|
329 | 329 | @ changeset: 2:264128213d29 |
|
330 | 330 | | tag: tip |
|
331 | 331 | | user: test |
|
332 | 332 | | date: Thu Jan 01 00:00:00 1970 +0000 |
|
333 | 333 | | summary: c |
|
334 | 334 | | |
|
335 | 335 | o changeset: 1:ef3a871183d7 |
|
336 | 336 | | user: test |
|
337 | 337 | | date: Thu Jan 01 00:00:00 1970 +0000 |
|
338 | 338 | | summary: b |
|
339 | 339 | | |
|
340 | 340 | o changeset: 0:9ab35a2d17cb |
|
341 | 341 | user: test |
|
342 | 342 | date: Thu Jan 01 00:00:00 1970 +0000 |
|
343 | 343 | summary: a |
|
344 | 344 | |
|
345 | 345 | $ restore |
|
346 | 346 | $ hg log -G |
|
347 | 347 | o changeset: 4:443431ffac4f |
|
348 | 348 | | tag: tip |
|
349 | 349 | | user: test |
|
350 | 350 | | date: Thu Jan 01 00:00:00 1970 +0000 |
|
351 | 351 | | summary: e |
|
352 | 352 | | |
|
353 | 353 | o changeset: 3:65bd5f99a4a3 |
|
354 | 354 | | parent: 1:ef3a871183d7 |
|
355 | 355 | | user: test |
|
356 | 356 | | date: Thu Jan 01 00:00:00 1970 +0000 |
|
357 | 357 | | summary: d |
|
358 | 358 | | |
|
359 | 359 | | @ changeset: 2:264128213d29 |
|
360 | 360 | |/ user: test |
|
361 | 361 | | date: Thu Jan 01 00:00:00 1970 +0000 |
|
362 | 362 | | summary: c |
|
363 | 363 | | |
|
364 | 364 | o changeset: 1:ef3a871183d7 |
|
365 | 365 | | user: test |
|
366 | 366 | | date: Thu Jan 01 00:00:00 1970 +0000 |
|
367 | 367 | | summary: b |
|
368 | 368 | | |
|
369 | 369 | o changeset: 0:9ab35a2d17cb |
|
370 | 370 | user: test |
|
371 | 371 | date: Thu Jan 01 00:00:00 1970 +0000 |
|
372 | 372 | summary: a |
|
373 | 373 | |
|
374 | 374 | Failed hook while applying "saveheads" bundle. |
|
375 | 375 | |
|
376 | 376 | $ hg strip 2 --config hooks.pretxnchangegroup.bad=false |
|
377 | 377 | 1 files updated, 0 files merged, 0 files removed, 0 files unresolved |
|
378 | 378 | saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob) |
|
379 | 379 | transaction abort! |
|
380 | 380 | rollback completed |
|
381 | 381 | strip failed, backup bundle stored in '$TESTTMP/test/.hg/strip-backup/*-backup.hg' (glob) |
|
382 | 382 | strip failed, unrecovered changes stored in '$TESTTMP/test/.hg/strip-backup/*-temp.hg' (glob) |
|
383 | 383 | (fix the problem, then recover the changesets with "hg unbundle '$TESTTMP/test/.hg/strip-backup/*-temp.hg'") (glob) |
|
384 | 384 | abort: pretxnchangegroup.bad hook exited with status 1 |
|
385 | 385 | [255] |
|
386 | 386 | $ restore |
|
387 | 387 | $ hg log -G |
|
388 | 388 | o changeset: 4:443431ffac4f |
|
389 | 389 | | tag: tip |
|
390 | 390 | | user: test |
|
391 | 391 | | date: Thu Jan 01 00:00:00 1970 +0000 |
|
392 | 392 | | summary: e |
|
393 | 393 | | |
|
394 | 394 | o changeset: 3:65bd5f99a4a3 |
|
395 | 395 | | parent: 1:ef3a871183d7 |
|
396 | 396 | | user: test |
|
397 | 397 | | date: Thu Jan 01 00:00:00 1970 +0000 |
|
398 | 398 | | summary: d |
|
399 | 399 | | |
|
400 | 400 | | o changeset: 2:264128213d29 |
|
401 | 401 | |/ user: test |
|
402 | 402 | | date: Thu Jan 01 00:00:00 1970 +0000 |
|
403 | 403 | | summary: c |
|
404 | 404 | | |
|
405 | 405 | @ changeset: 1:ef3a871183d7 |
|
406 | 406 | | user: test |
|
407 | 407 | | date: Thu Jan 01 00:00:00 1970 +0000 |
|
408 | 408 | | summary: b |
|
409 | 409 | | |
|
410 | 410 | o changeset: 0:9ab35a2d17cb |
|
411 | 411 | user: test |
|
412 | 412 | date: Thu Jan 01 00:00:00 1970 +0000 |
|
413 | 413 | summary: a |
|
414 | 414 | |
|
415 | 415 | |
|
416 | 416 | 2 different branches: 2 strips |
|
417 | 417 | |
|
418 | 418 | $ hg strip 2 4 |
|
419 | 419 | saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob) |
|
420 | 420 | $ hg log -G |
|
421 | 421 | o changeset: 2:65bd5f99a4a3 |
|
422 | 422 | | tag: tip |
|
423 | 423 | | user: test |
|
424 | 424 | | date: Thu Jan 01 00:00:00 1970 +0000 |
|
425 | 425 | | summary: d |
|
426 | 426 | | |
|
427 | 427 | @ changeset: 1:ef3a871183d7 |
|
428 | 428 | | user: test |
|
429 | 429 | | date: Thu Jan 01 00:00:00 1970 +0000 |
|
430 | 430 | | summary: b |
|
431 | 431 | | |
|
432 | 432 | o changeset: 0:9ab35a2d17cb |
|
433 | 433 | user: test |
|
434 | 434 | date: Thu Jan 01 00:00:00 1970 +0000 |
|
435 | 435 | summary: a |
|
436 | 436 | |
|
437 | 437 | $ restore |
|
438 | 438 | |
|
439 | 439 | 2 different branches and a common ancestor: 1 strip |
|
440 | 440 | |
|
441 | 441 | $ hg strip 1 "2|4" |
|
442 | 442 | 1 files updated, 0 files merged, 0 files removed, 0 files unresolved |
|
443 | 443 | saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob) |
|
444 | 444 | $ restore |
|
445 | 445 | |
|
446 | 446 | verify fncache is kept up-to-date |
|
447 | 447 | |
|
448 | 448 | $ touch a |
|
449 | 449 | $ hg ci -qAm a |
|
450 | 450 | $ cat .hg/store/fncache | sort |
|
451 | 451 | data/a.i |
|
452 | 452 | data/bar.i |
|
453 | 453 | $ hg strip tip |
|
454 | 454 | 0 files updated, 0 files merged, 1 files removed, 0 files unresolved |
|
455 | 455 | saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob) |
|
456 | 456 | $ cat .hg/store/fncache |
|
457 | 457 | data/bar.i |
|
458 | 458 | |
|
459 | 459 | stripping an empty revset |
|
460 | 460 | |
|
461 | 461 | $ hg strip "1 and not 1" |
|
462 | 462 | abort: empty revision set |
|
463 | 463 | [255] |
|
464 | 464 | |
|
465 | 465 | remove branchy history for qimport tests |
|
466 | 466 | |
|
467 | 467 | $ hg strip 3 |
|
468 | 468 | saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob) |
|
469 | 469 | |
|
470 | 470 | |
|
471 | 471 | strip of applied mq should cleanup status file |
|
472 | 472 | |
|
473 | 473 | $ echo "mq=" >> $HGRCPATH |
|
474 | 474 | $ hg up -C 3 |
|
475 | 475 | 1 files updated, 0 files merged, 0 files removed, 0 files unresolved |
|
476 | 476 | $ echo fooagain >> bar |
|
477 | 477 | $ hg ci -mf |
|
478 | 478 | $ hg qimport -r tip:2 |
|
479 | 479 | |
|
480 | 480 | applied patches before strip |
|
481 | 481 | |
|
482 | 482 | $ hg qapplied |
|
483 | 483 | d |
|
484 | 484 | e |
|
485 | 485 | f |
|
486 | 486 | |
|
487 | 487 | stripping revision in queue |
|
488 | 488 | |
|
489 | 489 | $ hg strip 3 |
|
490 | 490 | 1 files updated, 0 files merged, 0 files removed, 0 files unresolved |
|
491 | 491 | saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob) |
|
492 | 492 | |
|
493 | 493 | applied patches after stripping rev in queue |
|
494 | 494 | |
|
495 | 495 | $ hg qapplied |
|
496 | 496 | d |
|
497 | 497 | |
|
498 | 498 | stripping ancestor of queue |
|
499 | 499 | |
|
500 | 500 | $ hg strip 1 |
|
501 | 501 | 1 files updated, 0 files merged, 0 files removed, 0 files unresolved |
|
502 | 502 | saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob) |
|
503 | 503 | |
|
504 | 504 | applied patches after stripping ancestor of queue |
|
505 | 505 | |
|
506 | 506 | $ hg qapplied |
|
507 | 507 | |
|
508 | 508 | Verify strip protects against stripping wc parent when there are uncommitted mods |
|
509 | 509 | |
|
510 | 510 | $ echo b > b |
|
511 | 511 | $ echo bb > bar |
|
512 | 512 | $ hg add b |
|
513 | 513 | $ hg ci -m 'b' |
|
514 | 514 | $ hg log --graph |
|
515 | 515 | @ changeset: 1:76dcf9fab855 |
|
516 | 516 | | tag: tip |
|
517 | 517 | | user: test |
|
518 | 518 | | date: Thu Jan 01 00:00:00 1970 +0000 |
|
519 | 519 | | summary: b |
|
520 | 520 | | |
|
521 | 521 | o changeset: 0:9ab35a2d17cb |
|
522 | 522 | user: test |
|
523 | 523 | date: Thu Jan 01 00:00:00 1970 +0000 |
|
524 | 524 | summary: a |
|
525 | 525 | |
|
526 | 526 | $ hg up 0 |
|
527 | 527 | 1 files updated, 0 files merged, 1 files removed, 0 files unresolved |
|
528 | 528 | $ echo c > bar |
|
529 | 529 | $ hg up -t false |
|
530 | 530 | merging bar |
|
531 | 531 | merging bar failed! |
|
532 | 532 | 1 files updated, 0 files merged, 0 files removed, 1 files unresolved |
|
533 | 533 | use 'hg resolve' to retry unresolved file merges |
|
534 | 534 | [1] |
|
535 | 535 | $ hg sum |
|
536 | 536 | parent: 1:76dcf9fab855 tip |
|
537 | 537 | b |
|
538 | 538 | branch: default |
|
539 | 539 | commit: 1 modified, 1 unknown, 1 unresolved |
|
540 | 540 | update: (current) |
|
541 | 541 | phases: 2 draft |
|
542 | 542 | mq: 3 unapplied |
|
543 | 543 | |
|
544 | 544 | $ echo c > b |
|
545 | 545 | $ hg strip tip |
|
546 | 546 | abort: local changes found |
|
547 | 547 | [255] |
|
548 | 548 | $ hg strip tip --keep |
|
549 | 549 | saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob) |
|
550 | 550 | $ hg log --graph |
|
551 | 551 | @ changeset: 0:9ab35a2d17cb |
|
552 | 552 | tag: tip |
|
553 | 553 | user: test |
|
554 | 554 | date: Thu Jan 01 00:00:00 1970 +0000 |
|
555 | 555 | summary: a |
|
556 | 556 | |
|
557 | 557 | $ hg status |
|
558 | 558 | M bar |
|
559 | 559 | ? b |
|
560 | 560 | ? bar.orig |
|
561 | 561 | |
|
562 | 562 | $ rm bar.orig |
|
563 | 563 | $ hg sum |
|
564 | 564 | parent: 0:9ab35a2d17cb tip |
|
565 | 565 | a |
|
566 | 566 | branch: default |
|
567 | 567 | commit: 1 modified, 1 unknown |
|
568 | 568 | update: (current) |
|
569 | 569 | phases: 1 draft |
|
570 | 570 | mq: 3 unapplied |
|
571 | 571 | |
|
572 | 572 | Strip adds, removes, modifies with --keep |
|
573 | 573 | |
|
574 | 574 | $ touch b |
|
575 | 575 | $ hg add b |
|
576 | 576 | $ hg commit -mb |
|
577 | 577 | $ touch c |
|
578 | 578 | |
|
579 | 579 | ... with a clean working dir |
|
580 | 580 | |
|
581 | 581 | $ hg add c |
|
582 | 582 | $ hg rm bar |
|
583 | 583 | $ hg commit -mc |
|
584 | 584 | $ hg status |
|
585 | 585 | $ hg strip --keep tip |
|
586 | 586 | saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob) |
|
587 | 587 | $ hg status |
|
588 | 588 | ! bar |
|
589 | 589 | ? c |
|
590 | 590 | |
|
591 | 591 | ... with a dirty working dir |
|
592 | 592 | |
|
593 | 593 | $ hg add c |
|
594 | 594 | $ hg rm bar |
|
595 | 595 | $ hg commit -mc |
|
596 | 596 | $ hg status |
|
597 | 597 | $ echo b > b |
|
598 | 598 | $ echo d > d |
|
599 | 599 | $ hg strip --keep tip |
|
600 | 600 | saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob) |
|
601 | 601 | $ hg status |
|
602 | 602 | M b |
|
603 | 603 | ! bar |
|
604 | 604 | ? c |
|
605 | 605 | ? d |
|
606 | 606 | |
|
607 | 607 | ... after updating the dirstate |
|
608 | 608 | $ hg add c |
|
609 | 609 | $ hg commit -mc |
|
610 | 610 | $ hg rm c |
|
611 | 611 | $ hg commit -mc |
|
612 | 612 | $ hg strip --keep '.^' -q |
|
613 | 613 | $ cd .. |
|
614 | 614 | |
|
615 | 615 | stripping many nodes on a complex graph (issue3299) |
|
616 | 616 | |
|
617 | 617 | $ hg init issue3299 |
|
618 | 618 | $ cd issue3299 |
|
619 | 619 | $ hg debugbuilddag '@a.:a@b.:b.:x<a@a.:a<b@b.:b<a@a.:a' |
|
620 | 620 | $ hg strip 'not ancestors(x)' |
|
621 | 621 | saved backup bundle to $TESTTMP/issue3299/.hg/strip-backup/*-backup.hg (glob) |
|
622 | 622 | |
|
623 | 623 | test hg strip -B bookmark |
|
624 | 624 | |
|
625 | 625 | $ cd .. |
|
626 | 626 | $ hg init bookmarks |
|
627 | 627 | $ cd bookmarks |
|
628 | 628 | $ hg debugbuilddag '..<2.*1/2:m<2+3:c<m+3:a<2.:b<m+2:d<2.:e<m+1:f' |
|
629 | 629 | $ hg bookmark -r 'a' 'todelete' |
|
630 | 630 | $ hg bookmark -r 'b' 'B' |
|
631 | 631 | $ hg bookmark -r 'b' 'nostrip' |
|
632 | 632 | $ hg bookmark -r 'c' 'delete' |
|
633 | 633 | $ hg bookmark -r 'd' 'multipledelete1' |
|
634 | 634 | $ hg bookmark -r 'e' 'multipledelete2' |
|
635 | 635 | $ hg bookmark -r 'f' 'singlenode1' |
|
636 | 636 | $ hg bookmark -r 'f' 'singlenode2' |
|
637 | 637 | $ hg up -C todelete |
|
638 | 638 | 0 files updated, 0 files merged, 0 files removed, 0 files unresolved |
|
639 | 639 | (activating bookmark todelete) |
|
640 | 640 | $ hg strip -B nostrip |
|
641 | 641 | bookmark 'nostrip' deleted |
|
642 | 642 | abort: empty revision set |
|
643 | 643 | [255] |
|
644 | 644 | $ hg strip -B todelete |
|
645 | 645 | 0 files updated, 0 files merged, 0 files removed, 0 files unresolved |
|
646 | 646 | saved backup bundle to $TESTTMP/bookmarks/.hg/strip-backup/*-backup.hg (glob) |
|
647 | 647 | bookmark 'todelete' deleted |
|
648 | 648 | $ hg id -ir dcbb326fdec2 |
|
649 | 649 | abort: unknown revision 'dcbb326fdec2'! |
|
650 | 650 | [255] |
|
651 | 651 | $ hg id -ir d62d843c9a01 |
|
652 | 652 | d62d843c9a01 |
|
653 | 653 | $ hg bookmarks |
|
654 | 654 | B 9:ff43616e5d0f |
|
655 | 655 | delete 6:2702dd0c91e7 |
|
656 | 656 | multipledelete1 11:e46a4836065c |
|
657 | 657 | multipledelete2 12:b4594d867745 |
|
658 | 658 | singlenode1 13:43227190fef8 |
|
659 | 659 | singlenode2 13:43227190fef8 |
|
660 | 660 | $ hg strip -B multipledelete1 -B multipledelete2 |
|
661 | 661 | saved backup bundle to $TESTTMP/bookmarks/.hg/strip-backup/e46a4836065c-89ec65c2-backup.hg (glob) |
|
662 | 662 | bookmark 'multipledelete1' deleted |
|
663 | 663 | bookmark 'multipledelete2' deleted |
|
664 | 664 | $ hg id -ir e46a4836065c |
|
665 | 665 | abort: unknown revision 'e46a4836065c'! |
|
666 | 666 | [255] |
|
667 | 667 | $ hg id -ir b4594d867745 |
|
668 | 668 | abort: unknown revision 'b4594d867745'! |
|
669 | 669 | [255] |
|
670 | 670 | $ hg strip -B singlenode1 -B singlenode2 |
|
671 | 671 | saved backup bundle to $TESTTMP/bookmarks/.hg/strip-backup/43227190fef8-8da858f2-backup.hg (glob) |
|
672 | 672 | bookmark 'singlenode1' deleted |
|
673 | 673 | bookmark 'singlenode2' deleted |
|
674 | 674 | $ hg id -ir 43227190fef8 |
|
675 | 675 | abort: unknown revision '43227190fef8'! |
|
676 | 676 | [255] |
|
677 | 677 | $ hg strip -B unknownbookmark |
|
678 | 678 | abort: bookmark 'unknownbookmark' not found |
|
679 | 679 | [255] |
|
680 | 680 | $ hg strip -B unknownbookmark1 -B unknownbookmark2 |
|
681 | 681 | abort: bookmark 'unknownbookmark1,unknownbookmark2' not found |
|
682 | 682 | [255] |
|
683 | 683 | $ hg strip -B delete -B unknownbookmark |
|
684 | 684 | abort: bookmark 'unknownbookmark' not found |
|
685 | 685 | [255] |
|
686 | 686 | $ hg strip -B delete |
|
687 | 687 | saved backup bundle to $TESTTMP/bookmarks/.hg/strip-backup/*-backup.hg (glob) |
|
688 | 688 | bookmark 'delete' deleted |
|
689 | 689 | $ hg id -ir 6:2702dd0c91e7 |
|
690 | 690 | abort: unknown revision '2702dd0c91e7'! |
|
691 | 691 | [255] |
|
692 | 692 | $ hg update B |
|
693 | 693 | 0 files updated, 0 files merged, 0 files removed, 0 files unresolved |
|
694 | 694 | (activating bookmark B) |
|
695 | 695 | $ echo a > a |
|
696 | 696 | $ hg add a |
|
697 | 697 | $ hg strip -B B |
|
698 | 698 | abort: local changes found |
|
699 | 699 | [255] |
|
700 | 700 | $ hg bookmarks |
|
701 | 701 | * B 6:ff43616e5d0f |
|
702 | 702 | |
|
703 | 703 | Make sure no one adds back a -b option: |
|
704 | 704 | |
|
705 | 705 | $ hg strip -b tip |
|
706 | 706 | hg strip: option -b not recognized |
|
707 | 707 | hg strip [-k] [-f] [-B bookmark] [-r] REV... |
|
708 | 708 | |
|
709 | 709 | strip changesets and all their descendants from the repository |
|
710 | 710 | |
|
711 | 711 | (use 'hg help -e strip' to show help for the strip extension) |
|
712 | 712 | |
|
713 | 713 | options ([+] can be repeated): |
|
714 | 714 | |
|
715 | 715 | -r --rev REV [+] strip specified revision (optional, can specify |
|
716 | 716 | revisions without this option) |
|
717 | 717 | -f --force force removal of changesets, discard uncommitted |
|
718 | 718 | changes (no backup) |
|
719 | 719 | --no-backup no backups |
|
720 | 720 | -k --keep do not modify working directory during strip |
|
721 | 721 | -B --bookmark VALUE [+] remove revs only reachable from given bookmark |
|
722 | 722 | --mq operate on patch repository |
|
723 | 723 | |
|
724 | 724 | (use 'hg strip -h' to show more help) |
|
725 | 725 | [255] |
|
726 | 726 | |
|
727 | 727 | $ cd .. |
|
728 | 728 | |
|
729 | 729 | Verify bundles don't get overwritten: |
|
730 | 730 | |
|
731 | 731 | $ hg init doublebundle |
|
732 | 732 | $ cd doublebundle |
|
733 | 733 | $ touch a |
|
734 | 734 | $ hg commit -Aqm a |
|
735 | 735 | $ touch b |
|
736 | 736 | $ hg commit -Aqm b |
|
737 | 737 | $ hg strip -r 0 |
|
738 | 738 | 0 files updated, 0 files merged, 2 files removed, 0 files unresolved |
|
739 | 739 | saved backup bundle to $TESTTMP/doublebundle/.hg/strip-backup/3903775176ed-e68910bd-backup.hg (glob) |
|
740 | 740 | $ ls .hg/strip-backup |
|
741 | 741 | 3903775176ed-e68910bd-backup.hg |
|
742 | 742 | $ hg pull -q -r 3903775176ed .hg/strip-backup/3903775176ed-e68910bd-backup.hg |
|
743 | 743 | $ hg strip -r 0 |
|
744 | 744 | saved backup bundle to $TESTTMP/doublebundle/.hg/strip-backup/3903775176ed-54390173-backup.hg (glob) |
|
745 | 745 | $ ls .hg/strip-backup |
|
746 | 746 | 3903775176ed-54390173-backup.hg |
|
747 | 747 | 3903775176ed-e68910bd-backup.hg |
|
748 | 748 | $ cd .. |
|
749 | 749 | |
|
750 | 750 | Test that we only bundle the stripped changesets (issue4736) |
|
751 | 751 | ------------------------------------------------------------ |
|
752 | 752 | |
|
753 | 753 | initialization (previous repo is empty anyway) |
|
754 | 754 | |
|
755 | 755 | $ hg init issue4736 |
|
756 | 756 | $ cd issue4736 |
|
757 | 757 | $ echo a > a |
|
758 | 758 | $ hg add a |
|
759 | 759 | $ hg commit -m commitA |
|
760 | 760 | $ echo b > b |
|
761 | 761 | $ hg add b |
|
762 | 762 | $ hg commit -m commitB |
|
763 | 763 | $ echo c > c |
|
764 | 764 | $ hg add c |
|
765 | 765 | $ hg commit -m commitC |
|
766 | 766 | $ hg up 'desc(commitB)' |
|
767 | 767 | 0 files updated, 0 files merged, 1 files removed, 0 files unresolved |
|
768 | 768 | $ echo d > d |
|
769 | 769 | $ hg add d |
|
770 | 770 | $ hg commit -m commitD |
|
771 | 771 | created new head |
|
772 | 772 | $ hg up 'desc(commitC)' |
|
773 | 773 | 1 files updated, 0 files merged, 1 files removed, 0 files unresolved |
|
774 | 774 | $ hg merge 'desc(commitD)' |
|
775 | 775 | 1 files updated, 0 files merged, 0 files removed, 0 files unresolved |
|
776 | 776 | (branch merge, don't forget to commit) |
|
777 | 777 | $ hg ci -m 'mergeCD' |
|
778 | 778 | $ hg log -G |
|
779 | 779 | @ changeset: 4:d8db9d137221 |
|
780 | 780 | |\ tag: tip |
|
781 | 781 | | | parent: 2:5c51d8d6557d |
|
782 | 782 | | | parent: 3:6625a5168474 |
|
783 | 783 | | | user: test |
|
784 | 784 | | | date: Thu Jan 01 00:00:00 1970 +0000 |
|
785 | 785 | | | summary: mergeCD |
|
786 | 786 | | | |
|
787 | 787 | | o changeset: 3:6625a5168474 |
|
788 | 788 | | | parent: 1:eca11cf91c71 |
|
789 | 789 | | | user: test |
|
790 | 790 | | | date: Thu Jan 01 00:00:00 1970 +0000 |
|
791 | 791 | | | summary: commitD |
|
792 | 792 | | | |
|
793 | 793 | o | changeset: 2:5c51d8d6557d |
|
794 | 794 | |/ user: test |
|
795 | 795 | | date: Thu Jan 01 00:00:00 1970 +0000 |
|
796 | 796 | | summary: commitC |
|
797 | 797 | | |
|
798 | 798 | o changeset: 1:eca11cf91c71 |
|
799 | 799 | | user: test |
|
800 | 800 | | date: Thu Jan 01 00:00:00 1970 +0000 |
|
801 | 801 | | summary: commitB |
|
802 | 802 | | |
|
803 | 803 | o changeset: 0:105141ef12d0 |
|
804 | 804 | user: test |
|
805 | 805 | date: Thu Jan 01 00:00:00 1970 +0000 |
|
806 | 806 | summary: commitA |
|
807 | 807 | |
|
808 | 808 | |
|
809 | 809 | Check bundle behavior: |
|
810 | 810 | |
|
811 | 811 | $ hg bundle -r 'desc(mergeCD)' --base 'desc(commitC)' ../issue4736.hg |
|
812 | 812 | 2 changesets found |
|
813 | 813 | $ hg log -r 'bundle()' -R ../issue4736.hg |
|
814 | 814 | changeset: 3:6625a5168474 |
|
815 | 815 | parent: 1:eca11cf91c71 |
|
816 | 816 | user: test |
|
817 | 817 | date: Thu Jan 01 00:00:00 1970 +0000 |
|
818 | 818 | summary: commitD |
|
819 | 819 | |
|
820 | 820 | changeset: 4:d8db9d137221 |
|
821 | 821 | tag: tip |
|
822 | 822 | parent: 2:5c51d8d6557d |
|
823 | 823 | parent: 3:6625a5168474 |
|
824 | 824 | user: test |
|
825 | 825 | date: Thu Jan 01 00:00:00 1970 +0000 |
|
826 | 826 | summary: mergeCD |
|
827 | 827 | |
|
828 | 828 | |
|
829 | 829 | check strip behavior |
|
830 | 830 | |
|
831 | 831 | $ hg --config extensions.strip= strip 'desc(commitD)' --debug |
|
832 | 832 | resolving manifests |
|
833 | 833 | branchmerge: False, force: True, partial: False |
|
834 | 834 | ancestor: d8db9d137221+, local: d8db9d137221+, remote: eca11cf91c71 |
|
835 | 835 | c: other deleted -> r |
|
836 | 836 | removing c |
|
837 | 837 | d: other deleted -> r |
|
838 | 838 | removing d |
|
839 | 839 | starting 4 threads for background file closing (?) |
|
840 | 840 | 0 files updated, 0 files merged, 2 files removed, 0 files unresolved |
|
841 | 841 | 2 changesets found |
|
842 | 842 | list of changesets: |
|
843 | 843 | 6625a516847449b6f0fa3737b9ba56e9f0f3032c |
|
844 | 844 | d8db9d1372214336d2b5570f20ee468d2c72fa8b |
|
845 | 845 | bundle2-output-bundle: "HG20", (1 params) 2 parts total |
|
846 | 846 | bundle2-output-part: "changegroup" (params: 1 mandatory 1 advisory) streamed payload |
|
847 | 847 | bundle2-output-part: "phase-heads" 24 bytes payload |
|
848 | 848 | saved backup bundle to $TESTTMP/issue4736/.hg/strip-backup/6625a5168474-345bb43d-backup.hg (glob) |
|
849 | 849 | updating the branch cache |
|
850 | 850 | invalid branchheads cache (served): tip differs |
|
851 | 851 | truncating cache/rbc-revs-v1 to 24 |
|
852 | 852 | $ hg log -G |
|
853 | 853 | o changeset: 2:5c51d8d6557d |
|
854 | 854 | | tag: tip |
|
855 | 855 | | user: test |
|
856 | 856 | | date: Thu Jan 01 00:00:00 1970 +0000 |
|
857 | 857 | | summary: commitC |
|
858 | 858 | | |
|
859 | 859 | @ changeset: 1:eca11cf91c71 |
|
860 | 860 | | user: test |
|
861 | 861 | | date: Thu Jan 01 00:00:00 1970 +0000 |
|
862 | 862 | | summary: commitB |
|
863 | 863 | | |
|
864 | 864 | o changeset: 0:105141ef12d0 |
|
865 | 865 | user: test |
|
866 | 866 | date: Thu Jan 01 00:00:00 1970 +0000 |
|
867 | 867 | summary: commitA |
|
868 | 868 | |
|
869 | 869 | |
|
870 | 870 | strip backup content |
|
871 | 871 | |
|
872 | 872 | $ hg log -r 'bundle()' -R .hg/strip-backup/6625a5168474-*-backup.hg |
|
873 | 873 | changeset: 3:6625a5168474 |
|
874 | 874 | parent: 1:eca11cf91c71 |
|
875 | 875 | user: test |
|
876 | 876 | date: Thu Jan 01 00:00:00 1970 +0000 |
|
877 | 877 | summary: commitD |
|
878 | 878 | |
|
879 | 879 | changeset: 4:d8db9d137221 |
|
880 | 880 | tag: tip |
|
881 | 881 | parent: 2:5c51d8d6557d |
|
882 | 882 | parent: 3:6625a5168474 |
|
883 | 883 | user: test |
|
884 | 884 | date: Thu Jan 01 00:00:00 1970 +0000 |
|
885 | 885 | summary: mergeCD |
|
886 | 886 | |
|
887 | 887 | Check that the phase cache is properly invalidated after a strip with bookmark. |
|
888 | 888 | |
|
889 | 889 | $ cat > ../stripstalephasecache.py << EOF |
|
890 | 890 | > from mercurial import extensions, localrepo |
|
891 | 891 | > def transactioncallback(orig, repo, desc, *args, **kwargs): |
|
892 | 892 | > def test(transaction): |
|
893 | 893 | > # observe cache inconsistency |
|
894 | 894 | > try: |
|
895 | 895 | > [repo.changelog.node(r) for r in repo.revs("not public()")] |
|
896 | 896 | > except IndexError: |
|
897 | 897 | > repo.ui.status("Index error!\n") |
|
898 | 898 | > transaction = orig(repo, desc, *args, **kwargs) |
|
899 | 899 | > # warm up the phase cache |
|
900 | 900 | > list(repo.revs("not public()")) |
|
901 | 901 | > if desc != 'strip': |
|
902 | 902 | > transaction.addpostclose("phase invalidation test", test) |
|
903 | 903 | > return transaction |
|
904 | 904 | > def extsetup(ui): |
|
905 | 905 | > extensions.wrapfunction(localrepo.localrepository, "transaction", |
|
906 | 906 | > transactioncallback) |
|
907 | 907 | > EOF |
|
908 | 908 | $ hg up -C 2 |
|
909 | 909 | 1 files updated, 0 files merged, 0 files removed, 0 files unresolved |
|
910 | 910 | $ echo k > k |
|
911 | 911 | $ hg add k |
|
912 | 912 | $ hg commit -m commitK |
|
913 | 913 | $ echo l > l |
|
914 | 914 | $ hg add l |
|
915 | 915 | $ hg commit -m commitL |
|
916 | 916 | $ hg book -r tip blah |
|
917 | 917 | $ hg strip ".^" --config extensions.crash=$TESTTMP/stripstalephasecache.py |
|
918 | 918 | 0 files updated, 0 files merged, 2 files removed, 0 files unresolved |
|
919 | 919 | saved backup bundle to $TESTTMP/issue4736/.hg/strip-backup/8f0b4384875c-4fa10deb-backup.hg (glob) |
|
920 | 920 | $ hg up -C 1 |
|
921 | 921 | 0 files updated, 0 files merged, 1 files removed, 0 files unresolved |
|
922 | 922 | |
|
923 | 923 | Error during post-close callback of the strip transaction |
|
924 | 924 | (They should be gracefully handled and reported) |
|
925 | 925 | |
|
926 | 926 | $ cat > ../crashstrip.py << EOF |
|
927 | 927 | > from mercurial import error |
|
928 | 928 | > def reposetup(ui, repo): |
|
929 | 929 | > class crashstriprepo(repo.__class__): |
|
930 | 930 | > def transaction(self, desc, *args, **kwargs): |
|
931 | 931 | > tr = super(crashstriprepo, self).transaction(desc, *args, **kwargs) |
|
932 | 932 | > if desc == 'strip': |
|
933 | 933 | > def crash(tra): raise error.Abort('boom') |
|
934 | 934 | > tr.addpostclose('crash', crash) |
|
935 | 935 | > return tr |
|
936 | 936 | > repo.__class__ = crashstriprepo |
|
937 | 937 | > EOF |
|
938 | 938 | $ hg strip tip --config extensions.crash=$TESTTMP/crashstrip.py |
|
939 | 939 | saved backup bundle to $TESTTMP/issue4736/.hg/strip-backup/5c51d8d6557d-70daef06-backup.hg (glob) |
|
940 | 940 | strip failed, backup bundle stored in '$TESTTMP/issue4736/.hg/strip-backup/5c51d8d6557d-70daef06-backup.hg' (glob) |
|
941 | 941 | abort: boom |
|
942 | 942 | [255] |
|
943 | 943 | |
|
944 | 944 | Use delayedstrip to strip inside a transaction |
|
945 | 945 | |
|
946 | 946 | $ cd $TESTTMP |
|
947 | 947 | $ hg init delayedstrip |
|
948 | 948 | $ cd delayedstrip |
|
949 | 949 | $ hg debugdrawdag <<'EOS' |
|
950 | 950 | > D |
|
951 | 951 | > | |
|
952 | 952 | > C F H # Commit on top of "I", |
|
953 | 953 | > | |/| # Strip B+D+I+E+G+H+Z |
|
954 | 954 | > I B E G |
|
955 | 955 | > \|/ |
|
956 | 956 | > A Z |
|
957 | 957 | > EOS |
|
958 | 958 | $ cp -R . ../scmutilcleanup |
|
959 | 959 | |
|
960 | 960 | $ hg up -C I |
|
961 | 961 | 2 files updated, 0 files merged, 0 files removed, 0 files unresolved |
|
962 | 962 | $ echo 3 >> I |
|
963 | 963 | $ cat > $TESTTMP/delayedstrip.py <<EOF |
|
964 | 964 | > from mercurial import repair, commands |
|
965 | 965 | > def reposetup(ui, repo): |
|
966 | 966 | > def getnodes(expr): |
|
967 | 967 | > return [repo.changelog.node(r) for r in repo.revs(expr)] |
|
968 | 968 | > with repo.wlock(): |
|
969 | 969 | > with repo.lock(): |
|
970 | 970 | > with repo.transaction('delayedstrip'): |
|
971 | 971 | > repair.delayedstrip(ui, repo, getnodes('B+I+Z+D+E'), 'J') |
|
972 | 972 | > repair.delayedstrip(ui, repo, getnodes('G+H+Z'), 'I') |
|
973 | 973 | > commands.commit(ui, repo, message='J', date='0 0') |
|
974 | 974 | > EOF |
|
975 | 975 | $ hg log -r . -T '\n' --config extensions.t=$TESTTMP/delayedstrip.py |
|
976 | 976 | warning: orphaned descendants detected, not stripping 08ebfeb61bac, 112478962961, 7fb047a69f22 |
|
977 | 977 | saved backup bundle to $TESTTMP/delayedstrip/.hg/strip-backup/f585351a92f8-17475721-I.hg (glob) |
|
978 | 978 | |
|
979 | 979 | $ hg log -G -T '{rev}:{node|short} {desc}' -r 'sort(all(), topo)' |
|
980 | 980 | @ 6:2f2d51af6205 J |
|
981 | 981 | | |
|
982 | 982 | o 3:08ebfeb61bac I |
|
983 | 983 | | |
|
984 | 984 | | o 5:64a8289d2492 F |
|
985 | 985 | | | |
|
986 | 986 | | o 2:7fb047a69f22 E |
|
987 | 987 | |/ |
|
988 | 988 | | o 4:26805aba1e60 C |
|
989 | 989 | | | |
|
990 | 990 | | o 1:112478962961 B |
|
991 | 991 | |/ |
|
992 | 992 | o 0:426bada5c675 A |
|
993 | 993 | |
|
994 | 994 | Test high-level scmutil.cleanupnodes API |
|
995 | 995 | |
|
996 | 996 | $ cd $TESTTMP/scmutilcleanup |
|
997 | 997 | $ hg debugdrawdag <<'EOS' |
|
998 | 998 | > D2 F2 G2 # D2, F2, G2 are replacements for D, F, G |
|
999 | 999 | > | | | |
|
1000 | 1000 | > C H G |
|
1001 | 1001 | > EOS |
|
1002 | 1002 | $ for i in B C D F G I Z; do |
|
1003 | 1003 | > hg bookmark -i -r $i b-$i |
|
1004 | 1004 | > done |
|
1005 | 1005 | $ hg bookmark -i -r E 'b-F@divergent1' |
|
1006 | 1006 | $ hg bookmark -i -r H 'b-F@divergent2' |
|
1007 | 1007 | $ hg bookmark -i -r G 'b-F@divergent3' |
|
1008 | 1008 | $ cp -R . ../scmutilcleanup.obsstore |
|
1009 | 1009 | |
|
1010 | 1010 | $ cat > $TESTTMP/scmutilcleanup.py <<EOF |
|
1011 | 1011 | > from mercurial import scmutil |
|
1012 | 1012 | > def reposetup(ui, repo): |
|
1013 | 1013 | > def nodes(expr): |
|
1014 | 1014 | > return [repo.changelog.node(r) for r in repo.revs(expr)] |
|
1015 | 1015 | > def node(expr): |
|
1016 | 1016 | > return nodes(expr)[0] |
|
1017 | 1017 | > with repo.wlock(): |
|
1018 | 1018 | > with repo.lock(): |
|
1019 | 1019 | > with repo.transaction('delayedstrip'): |
|
1020 | 1020 | > mapping = {node('F'): [node('F2')], |
|
1021 | 1021 | > node('D'): [node('D2')], |
|
1022 | 1022 | > node('G'): [node('G2')]} |
|
1023 | 1023 | > scmutil.cleanupnodes(repo, mapping, 'replace') |
|
1024 | 1024 | > scmutil.cleanupnodes(repo, nodes('((B::)+I+Z)-D2'), 'replace') |
|
1025 | 1025 | > EOF |
|
1026 | 1026 | $ hg log -r . -T '\n' --config extensions.t=$TESTTMP/scmutilcleanup.py |
|
1027 | 1027 | warning: orphaned descendants detected, not stripping 112478962961, 1fc8102cda62, 26805aba1e60 |
|
1028 | 1028 | saved backup bundle to $TESTTMP/scmutilcleanup/.hg/strip-backup/f585351a92f8-73fb7c03-replace.hg (glob) |
|
1029 | 1029 | |
|
1030 | 1030 | $ hg log -G -T '{rev}:{node|short} {desc} {bookmarks}' -r 'sort(all(), topo)' |
|
1031 | 1031 | o 8:1473d4b996d1 G2 b-F@divergent3 b-G |
|
1032 | 1032 | | |
|
1033 | 1033 | | o 7:d11b3456a873 F2 b-F |
|
1034 | 1034 | | | |
|
1035 | 1035 | | o 5:5cb05ba470a7 H |
|
1036 | 1036 | |/| |
|
1037 | 1037 | | o 3:7fb047a69f22 E b-F@divergent1 |
|
1038 | 1038 | | | |
|
1039 | 1039 | | | o 6:7c78f703e465 D2 b-D |
|
1040 | 1040 | | | | |
|
1041 | 1041 | | | o 4:26805aba1e60 C |
|
1042 | 1042 | | | | |
|
1043 | 1043 | | | o 2:112478962961 B |
|
1044 | 1044 | | |/ |
|
1045 | 1045 | o | 1:1fc8102cda62 G |
|
1046 | 1046 | / |
|
1047 | 1047 | o 0:426bada5c675 A b-B b-C b-I |
|
1048 | 1048 | |
|
1049 | 1049 | $ hg bookmark |
|
1050 | 1050 | b-B 0:426bada5c675 |
|
1051 | 1051 | b-C 0:426bada5c675 |
|
1052 | 1052 | b-D 6:7c78f703e465 |
|
1053 | 1053 | b-F 7:d11b3456a873 |
|
1054 | 1054 | b-F@divergent1 3:7fb047a69f22 |
|
1055 | 1055 | b-F@divergent3 8:1473d4b996d1 |
|
1056 | 1056 | b-G 8:1473d4b996d1 |
|
1057 | 1057 | b-I 0:426bada5c675 |
|
1058 | 1058 | b-Z -1:000000000000 |
|
1059 | 1059 | |
|
1060 | 1060 | Test the above using obsstore "by the way". Not directly related to strip, but |
|
1061 | 1061 | we have reusable code here |
|
1062 | 1062 | |
|
1063 | 1063 | $ cd $TESTTMP/scmutilcleanup.obsstore |
|
1064 | 1064 | $ cat >> .hg/hgrc <<EOF |
|
1065 | 1065 | > [experimental] |
|
1066 | 1066 | > evolution=all |
|
1067 | 1067 | > evolution.track-operation=1 |
|
1068 | 1068 | > EOF |
|
1069 | 1069 | |
|
1070 | 1070 | $ hg log -r . -T '\n' --config extensions.t=$TESTTMP/scmutilcleanup.py |
|
1071 | 1071 | |
|
1072 | 1072 | $ rm .hg/localtags |
|
1073 | 1073 | $ hg log -G -T '{rev}:{node|short} {desc} {bookmarks}' -r 'sort(all(), topo)' |
|
1074 | 1074 | o 12:1473d4b996d1 G2 b-F@divergent3 b-G |
|
1075 | 1075 | | |
|
1076 | 1076 | | o 11:d11b3456a873 F2 b-F |
|
1077 | 1077 | | | |
|
1078 | 1078 | | o 8:5cb05ba470a7 H |
|
1079 | 1079 | |/| |
|
1080 | 1080 | | o 4:7fb047a69f22 E b-F@divergent1 |
|
1081 | 1081 | | | |
|
1082 | 1082 | | | o 10:7c78f703e465 D2 b-D |
|
1083 | 1083 | | | | |
|
1084 | 1084 | | | x 6:26805aba1e60 C |
|
1085 | 1085 | | | | |
|
1086 | 1086 | | | x 3:112478962961 B |
|
1087 | 1087 | | |/ |
|
1088 | 1088 | x | 1:1fc8102cda62 G |
|
1089 | 1089 | / |
|
1090 | 1090 | o 0:426bada5c675 A b-B b-C b-I |
|
1091 | 1091 | |
|
1092 | 1092 | $ hg debugobsolete |
|
1093 | 1093 | 1fc8102cda6204549f031015641606ccf5513ec3 1473d4b996d1d1b121de6b39fab6a04fbf9d873e 0 (Thu Jan 01 00:00:00 1970 +0000) {'operation': 'replace', 'user': 'test'} |
|
1094 | 1094 | 64a8289d249234b9886244d379f15e6b650b28e3 d11b3456a873daec7c7bc53e5622e8df6d741bd2 0 (Thu Jan 01 00:00:00 1970 +0000) {'operation': 'replace', 'user': 'test'} |
|
1095 | 1095 | f585351a92f85104bff7c284233c338b10eb1df7 7c78f703e465d73102cc8780667ce269c5208a40 0 (Thu Jan 01 00:00:00 1970 +0000) {'operation': 'replace', 'user': 'test'} |
|
1096 | 1096 | 48b9aae0607f43ff110d84e6883c151942add5ab 0 {0000000000000000000000000000000000000000} (Thu Jan 01 00:00:00 1970 +0000) {'operation': 'replace', 'user': 'test'} |
|
1097 | 1097 | 112478962961147124edd43549aedd1a335e44bf 0 {426bada5c67598ca65036d57d9e4b64b0c1ce7a0} (Thu Jan 01 00:00:00 1970 +0000) {'operation': 'replace', 'user': 'test'} |
|
1098 | 1098 | 08ebfeb61bac6e3f12079de774d285a0d6689eba 0 {426bada5c67598ca65036d57d9e4b64b0c1ce7a0} (Thu Jan 01 00:00:00 1970 +0000) {'operation': 'replace', 'user': 'test'} |
|
1099 | 1099 | 26805aba1e600a82e93661149f2313866a221a7b 0 {112478962961147124edd43549aedd1a335e44bf} (Thu Jan 01 00:00:00 1970 +0000) {'operation': 'replace', 'user': 'test'} |
|
1100 | 1100 | $ cd .. |
|
1101 | 1101 | |
|
1102 | 1102 | Test that obsmarkers are restored even when not using generaldelta |
|
1103 | 1103 | |
|
1104 | 1104 | $ hg --config format.usegeneraldelta=no init issue5678 |
|
1105 | 1105 | $ cd issue5678 |
|
1106 | 1106 | $ cat >> .hg/hgrc <<EOF |
|
1107 | 1107 | > [experimental] |
|
1108 | 1108 | > evolution=all |
|
1109 | 1109 | > EOF |
|
1110 | 1110 | $ echo a > a |
|
1111 | 1111 | $ hg ci -Aqm a |
|
1112 | 1112 | $ hg ci --amend -m a2 |
|
1113 | 1113 | $ hg debugobsolete |
|
1114 | 1114 | cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b 489bac576828490c0bb8d45eac9e5e172e4ec0a8 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'} |
|
1115 | 1115 | $ hg strip . |
|
1116 | 1116 | 0 files updated, 0 files merged, 1 files removed, 0 files unresolved |
|
1117 | 1117 | saved backup bundle to $TESTTMP/issue5678/.hg/strip-backup/489bac576828-bef27e14-backup.hg (glob) |
|
1118 | 1118 | $ hg unbundle -q .hg/strip-backup/* |
|
1119 | BROKEN: obsmarker got lost | |
|
1120 | 1119 | $ hg debugobsolete |
|
1120 | cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b 489bac576828490c0bb8d45eac9e5e172e4ec0a8 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'} | |
|
1121 | 1121 | $ cd .. |
General Comments 0
You need to be logged in to leave comments.
Login now