##// END OF EJS Templates
obsolete: explicitly track folds inside the markers...
Boris Feld -
r40078:208303a8 default
parent child Browse files
Show More
@@ -1,1040 +1,1058 b''
1 1 # obsolete.py - obsolete markers handling
2 2 #
3 3 # Copyright 2012 Pierre-Yves David <pierre-yves.david@ens-lyon.org>
4 4 # Logilab SA <contact@logilab.fr>
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 """Obsolete marker handling
10 10
11 11 An obsolete marker maps an old changeset to a list of new
12 12 changesets. If the list of new changesets is empty, the old changeset
13 13 is said to be "killed". Otherwise, the old changeset is being
14 14 "replaced" by the new changesets.
15 15
16 16 Obsolete markers can be used to record and distribute changeset graph
17 17 transformations performed by history rewrite operations, and help
18 18 building new tools to reconcile conflicting rewrite actions. To
19 19 facilitate conflict resolution, markers include various annotations
20 20 besides old and news changeset identifiers, such as creation date or
21 21 author name.
22 22
23 23 The old obsoleted changeset is called a "predecessor" and possible
24 24 replacements are called "successors". Markers that used changeset X as
25 25 a predecessor are called "successor markers of X" because they hold
26 26 information about the successors of X. Markers that use changeset Y as
27 27 a successors are call "predecessor markers of Y" because they hold
28 28 information about the predecessors of Y.
29 29
30 30 Examples:
31 31
32 32 - When changeset A is replaced by changeset A', one marker is stored:
33 33
34 34 (A, (A',))
35 35
36 36 - When changesets A and B are folded into a new changeset C, two markers are
37 37 stored:
38 38
39 39 (A, (C,)) and (B, (C,))
40 40
41 41 - When changeset A is simply "pruned" from the graph, a marker is created:
42 42
43 43 (A, ())
44 44
45 45 - When changeset A is split into B and C, a single marker is used:
46 46
47 47 (A, (B, C))
48 48
49 49 We use a single marker to distinguish the "split" case from the "divergence"
50 50 case. If two independent operations rewrite the same changeset A in to A' and
51 51 A'', we have an error case: divergent rewriting. We can detect it because
52 52 two markers will be created independently:
53 53
54 54 (A, (B,)) and (A, (C,))
55 55
56 56 Format
57 57 ------
58 58
59 59 Markers are stored in an append-only file stored in
60 60 '.hg/store/obsstore'.
61 61
62 62 The file starts with a version header:
63 63
64 64 - 1 unsigned byte: version number, starting at zero.
65 65
66 66 The header is followed by the markers. Marker format depend of the version. See
67 67 comment associated with each format for details.
68 68
69 69 """
70 70 from __future__ import absolute_import
71 71
72 72 import errno
73 import hashlib
73 74 import struct
74 75
75 76 from .i18n import _
76 77 from . import (
77 78 encoding,
78 79 error,
79 80 node,
80 81 obsutil,
81 82 phases,
82 83 policy,
83 84 pycompat,
84 85 util,
85 86 )
86 87 from .utils import dateutil
87 88
88 89 parsers = policy.importmod(r'parsers')
89 90
90 91 _pack = struct.pack
91 92 _unpack = struct.unpack
92 93 _calcsize = struct.calcsize
93 94 propertycache = util.propertycache
94 95
95 96 # the obsolete feature is not mature enough to be enabled by default.
96 97 # you have to rely on third party extension extension to enable this.
97 98 _enabled = False
98 99
99 100 # Options for obsolescence
100 101 createmarkersopt = 'createmarkers'
101 102 allowunstableopt = 'allowunstable'
102 103 exchangeopt = 'exchange'
103 104
104 105 def _getoptionvalue(repo, option):
105 106 """Returns True if the given repository has the given obsolete option
106 107 enabled.
107 108 """
108 109 configkey = 'evolution.%s' % option
109 110 newconfig = repo.ui.configbool('experimental', configkey)
110 111
111 112 # Return the value only if defined
112 113 if newconfig is not None:
113 114 return newconfig
114 115
115 116 # Fallback on generic option
116 117 try:
117 118 return repo.ui.configbool('experimental', 'evolution')
118 119 except (error.ConfigError, AttributeError):
119 120 # Fallback on old-fashion config
120 121 # inconsistent config: experimental.evolution
121 122 result = set(repo.ui.configlist('experimental', 'evolution'))
122 123
123 124 if 'all' in result:
124 125 return True
125 126
126 127 # For migration purposes, temporarily return true if the config hasn't
127 128 # been set but _enabled is true.
128 129 if len(result) == 0 and _enabled:
129 130 return True
130 131
131 132 # Temporary hack for next check
132 133 newconfig = repo.ui.config('experimental', 'evolution.createmarkers')
133 134 if newconfig:
134 135 result.add('createmarkers')
135 136
136 137 return option in result
137 138
138 139 def getoptions(repo):
139 140 """Returns dicts showing state of obsolescence features."""
140 141
141 142 createmarkersvalue = _getoptionvalue(repo, createmarkersopt)
142 143 unstablevalue = _getoptionvalue(repo, allowunstableopt)
143 144 exchangevalue = _getoptionvalue(repo, exchangeopt)
144 145
145 146 # createmarkers must be enabled if other options are enabled
146 147 if ((unstablevalue or exchangevalue) and not createmarkersvalue):
147 148 raise error.Abort(_("'createmarkers' obsolete option must be enabled "
148 149 "if other obsolete options are enabled"))
149 150
150 151 return {
151 152 createmarkersopt: createmarkersvalue,
152 153 allowunstableopt: unstablevalue,
153 154 exchangeopt: exchangevalue,
154 155 }
155 156
156 157 def isenabled(repo, option):
157 158 """Returns True if the given repository has the given obsolete option
158 159 enabled.
159 160 """
160 161 return getoptions(repo)[option]
161 162
162 163 # Creating aliases for marker flags because evolve extension looks for
163 164 # bumpedfix in obsolete.py
164 165 bumpedfix = obsutil.bumpedfix
165 166 usingsha256 = obsutil.usingsha256
166 167
167 168 ## Parsing and writing of version "0"
168 169 #
169 170 # The header is followed by the markers. Each marker is made of:
170 171 #
171 172 # - 1 uint8 : number of new changesets "N", can be zero.
172 173 #
173 174 # - 1 uint32: metadata size "M" in bytes.
174 175 #
175 176 # - 1 byte: a bit field. It is reserved for flags used in common
176 177 # obsolete marker operations, to avoid repeated decoding of metadata
177 178 # entries.
178 179 #
179 180 # - 20 bytes: obsoleted changeset identifier.
180 181 #
181 182 # - N*20 bytes: new changesets identifiers.
182 183 #
183 184 # - M bytes: metadata as a sequence of nul-terminated strings. Each
184 185 # string contains a key and a value, separated by a colon ':', without
185 186 # additional encoding. Keys cannot contain '\0' or ':' and values
186 187 # cannot contain '\0'.
187 188 _fm0version = 0
188 189 _fm0fixed = '>BIB20s'
189 190 _fm0node = '20s'
190 191 _fm0fsize = _calcsize(_fm0fixed)
191 192 _fm0fnodesize = _calcsize(_fm0node)
192 193
193 194 def _fm0readmarkers(data, off, stop):
194 195 # Loop on markers
195 196 while off < stop:
196 197 # read fixed part
197 198 cur = data[off:off + _fm0fsize]
198 199 off += _fm0fsize
199 200 numsuc, mdsize, flags, pre = _unpack(_fm0fixed, cur)
200 201 # read replacement
201 202 sucs = ()
202 203 if numsuc:
203 204 s = (_fm0fnodesize * numsuc)
204 205 cur = data[off:off + s]
205 206 sucs = _unpack(_fm0node * numsuc, cur)
206 207 off += s
207 208 # read metadata
208 209 # (metadata will be decoded on demand)
209 210 metadata = data[off:off + mdsize]
210 211 if len(metadata) != mdsize:
211 212 raise error.Abort(_('parsing obsolete marker: metadata is too '
212 213 'short, %d bytes expected, got %d')
213 214 % (mdsize, len(metadata)))
214 215 off += mdsize
215 216 metadata = _fm0decodemeta(metadata)
216 217 try:
217 218 when, offset = metadata.pop('date', '0 0').split(' ')
218 219 date = float(when), int(offset)
219 220 except ValueError:
220 221 date = (0., 0)
221 222 parents = None
222 223 if 'p2' in metadata:
223 224 parents = (metadata.pop('p1', None), metadata.pop('p2', None))
224 225 elif 'p1' in metadata:
225 226 parents = (metadata.pop('p1', None),)
226 227 elif 'p0' in metadata:
227 228 parents = ()
228 229 if parents is not None:
229 230 try:
230 231 parents = tuple(node.bin(p) for p in parents)
231 232 # if parent content is not a nodeid, drop the data
232 233 for p in parents:
233 234 if len(p) != 20:
234 235 parents = None
235 236 break
236 237 except TypeError:
237 238 # if content cannot be translated to nodeid drop the data.
238 239 parents = None
239 240
240 241 metadata = tuple(sorted(metadata.iteritems()))
241 242
242 243 yield (pre, sucs, flags, metadata, date, parents)
243 244
244 245 def _fm0encodeonemarker(marker):
245 246 pre, sucs, flags, metadata, date, parents = marker
246 247 if flags & usingsha256:
247 248 raise error.Abort(_('cannot handle sha256 with old obsstore format'))
248 249 metadata = dict(metadata)
249 250 time, tz = date
250 251 metadata['date'] = '%r %i' % (time, tz)
251 252 if parents is not None:
252 253 if not parents:
253 254 # mark that we explicitly recorded no parents
254 255 metadata['p0'] = ''
255 256 for i, p in enumerate(parents, 1):
256 257 metadata['p%i' % i] = node.hex(p)
257 258 metadata = _fm0encodemeta(metadata)
258 259 numsuc = len(sucs)
259 260 format = _fm0fixed + (_fm0node * numsuc)
260 261 data = [numsuc, len(metadata), flags, pre]
261 262 data.extend(sucs)
262 263 return _pack(format, *data) + metadata
263 264
264 265 def _fm0encodemeta(meta):
265 266 """Return encoded metadata string to string mapping.
266 267
267 268 Assume no ':' in key and no '\0' in both key and value."""
268 269 for key, value in meta.iteritems():
269 270 if ':' in key or '\0' in key:
270 271 raise ValueError("':' and '\0' are forbidden in metadata key'")
271 272 if '\0' in value:
272 273 raise ValueError("':' is forbidden in metadata value'")
273 274 return '\0'.join(['%s:%s' % (k, meta[k]) for k in sorted(meta)])
274 275
275 276 def _fm0decodemeta(data):
276 277 """Return string to string dictionary from encoded version."""
277 278 d = {}
278 279 for l in data.split('\0'):
279 280 if l:
280 281 key, value = l.split(':')
281 282 d[key] = value
282 283 return d
283 284
284 285 ## Parsing and writing of version "1"
285 286 #
286 287 # The header is followed by the markers. Each marker is made of:
287 288 #
288 289 # - uint32: total size of the marker (including this field)
289 290 #
290 291 # - float64: date in seconds since epoch
291 292 #
292 293 # - int16: timezone offset in minutes
293 294 #
294 295 # - uint16: a bit field. It is reserved for flags used in common
295 296 # obsolete marker operations, to avoid repeated decoding of metadata
296 297 # entries.
297 298 #
298 299 # - uint8: number of successors "N", can be zero.
299 300 #
300 301 # - uint8: number of parents "P", can be zero.
301 302 #
302 303 # 0: parents data stored but no parent,
303 304 # 1: one parent stored,
304 305 # 2: two parents stored,
305 306 # 3: no parent data stored
306 307 #
307 308 # - uint8: number of metadata entries M
308 309 #
309 310 # - 20 or 32 bytes: predecessor changeset identifier.
310 311 #
311 312 # - N*(20 or 32) bytes: successors changesets identifiers.
312 313 #
313 314 # - P*(20 or 32) bytes: parents of the predecessors changesets.
314 315 #
315 316 # - M*(uint8, uint8): size of all metadata entries (key and value)
316 317 #
317 318 # - remaining bytes: the metadata, each (key, value) pair after the other.
318 319 _fm1version = 1
319 320 _fm1fixed = '>IdhHBBB20s'
320 321 _fm1nodesha1 = '20s'
321 322 _fm1nodesha256 = '32s'
322 323 _fm1nodesha1size = _calcsize(_fm1nodesha1)
323 324 _fm1nodesha256size = _calcsize(_fm1nodesha256)
324 325 _fm1fsize = _calcsize(_fm1fixed)
325 326 _fm1parentnone = 3
326 327 _fm1parentshift = 14
327 328 _fm1parentmask = (_fm1parentnone << _fm1parentshift)
328 329 _fm1metapair = 'BB'
329 330 _fm1metapairsize = _calcsize(_fm1metapair)
330 331
331 332 def _fm1purereadmarkers(data, off, stop):
332 333 # make some global constants local for performance
333 334 noneflag = _fm1parentnone
334 335 sha2flag = usingsha256
335 336 sha1size = _fm1nodesha1size
336 337 sha2size = _fm1nodesha256size
337 338 sha1fmt = _fm1nodesha1
338 339 sha2fmt = _fm1nodesha256
339 340 metasize = _fm1metapairsize
340 341 metafmt = _fm1metapair
341 342 fsize = _fm1fsize
342 343 unpack = _unpack
343 344
344 345 # Loop on markers
345 346 ufixed = struct.Struct(_fm1fixed).unpack
346 347
347 348 while off < stop:
348 349 # read fixed part
349 350 o1 = off + fsize
350 351 t, secs, tz, flags, numsuc, numpar, nummeta, prec = ufixed(data[off:o1])
351 352
352 353 if flags & sha2flag:
353 354 # FIXME: prec was read as a SHA1, needs to be amended
354 355
355 356 # read 0 or more successors
356 357 if numsuc == 1:
357 358 o2 = o1 + sha2size
358 359 sucs = (data[o1:o2],)
359 360 else:
360 361 o2 = o1 + sha2size * numsuc
361 362 sucs = unpack(sha2fmt * numsuc, data[o1:o2])
362 363
363 364 # read parents
364 365 if numpar == noneflag:
365 366 o3 = o2
366 367 parents = None
367 368 elif numpar == 1:
368 369 o3 = o2 + sha2size
369 370 parents = (data[o2:o3],)
370 371 else:
371 372 o3 = o2 + sha2size * numpar
372 373 parents = unpack(sha2fmt * numpar, data[o2:o3])
373 374 else:
374 375 # read 0 or more successors
375 376 if numsuc == 1:
376 377 o2 = o1 + sha1size
377 378 sucs = (data[o1:o2],)
378 379 else:
379 380 o2 = o1 + sha1size * numsuc
380 381 sucs = unpack(sha1fmt * numsuc, data[o1:o2])
381 382
382 383 # read parents
383 384 if numpar == noneflag:
384 385 o3 = o2
385 386 parents = None
386 387 elif numpar == 1:
387 388 o3 = o2 + sha1size
388 389 parents = (data[o2:o3],)
389 390 else:
390 391 o3 = o2 + sha1size * numpar
391 392 parents = unpack(sha1fmt * numpar, data[o2:o3])
392 393
393 394 # read metadata
394 395 off = o3 + metasize * nummeta
395 396 metapairsize = unpack('>' + (metafmt * nummeta), data[o3:off])
396 397 metadata = []
397 398 for idx in pycompat.xrange(0, len(metapairsize), 2):
398 399 o1 = off + metapairsize[idx]
399 400 o2 = o1 + metapairsize[idx + 1]
400 401 metadata.append((data[off:o1], data[o1:o2]))
401 402 off = o2
402 403
403 404 yield (prec, sucs, flags, tuple(metadata), (secs, tz * 60), parents)
404 405
405 406 def _fm1encodeonemarker(marker):
406 407 pre, sucs, flags, metadata, date, parents = marker
407 408 # determine node size
408 409 _fm1node = _fm1nodesha1
409 410 if flags & usingsha256:
410 411 _fm1node = _fm1nodesha256
411 412 numsuc = len(sucs)
412 413 numextranodes = numsuc
413 414 if parents is None:
414 415 numpar = _fm1parentnone
415 416 else:
416 417 numpar = len(parents)
417 418 numextranodes += numpar
418 419 formatnodes = _fm1node * numextranodes
419 420 formatmeta = _fm1metapair * len(metadata)
420 421 format = _fm1fixed + formatnodes + formatmeta
421 422 # tz is stored in minutes so we divide by 60
422 423 tz = date[1]//60
423 424 data = [None, date[0], tz, flags, numsuc, numpar, len(metadata), pre]
424 425 data.extend(sucs)
425 426 if parents is not None:
426 427 data.extend(parents)
427 428 totalsize = _calcsize(format)
428 429 for key, value in metadata:
429 430 lk = len(key)
430 431 lv = len(value)
431 432 if lk > 255:
432 433 msg = ('obsstore metadata key cannot be longer than 255 bytes'
433 434 ' (key "%s" is %u bytes)') % (key, lk)
434 435 raise error.ProgrammingError(msg)
435 436 if lv > 255:
436 437 msg = ('obsstore metadata value cannot be longer than 255 bytes'
437 438 ' (value "%s" for key "%s" is %u bytes)') % (value, key, lv)
438 439 raise error.ProgrammingError(msg)
439 440 data.append(lk)
440 441 data.append(lv)
441 442 totalsize += lk + lv
442 443 data[0] = totalsize
443 444 data = [_pack(format, *data)]
444 445 for key, value in metadata:
445 446 data.append(key)
446 447 data.append(value)
447 448 return ''.join(data)
448 449
449 450 def _fm1readmarkers(data, off, stop):
450 451 native = getattr(parsers, 'fm1readmarkers', None)
451 452 if not native:
452 453 return _fm1purereadmarkers(data, off, stop)
453 454 return native(data, off, stop)
454 455
455 456 # mapping to read/write various marker formats
456 457 # <version> -> (decoder, encoder)
457 458 formats = {_fm0version: (_fm0readmarkers, _fm0encodeonemarker),
458 459 _fm1version: (_fm1readmarkers, _fm1encodeonemarker)}
459 460
460 461 def _readmarkerversion(data):
461 462 return _unpack('>B', data[0:1])[0]
462 463
463 464 @util.nogc
464 465 def _readmarkers(data, off=None, stop=None):
465 466 """Read and enumerate markers from raw data"""
466 467 diskversion = _readmarkerversion(data)
467 468 if not off:
468 469 off = 1 # skip 1 byte version number
469 470 if stop is None:
470 471 stop = len(data)
471 472 if diskversion not in formats:
472 473 msg = _('parsing obsolete marker: unknown version %r') % diskversion
473 474 raise error.UnknownVersion(msg, version=diskversion)
474 475 return diskversion, formats[diskversion][0](data, off, stop)
475 476
476 477 def encodeheader(version=_fm0version):
477 478 return _pack('>B', version)
478 479
479 480 def encodemarkers(markers, addheader=False, version=_fm0version):
480 481 # Kept separate from flushmarkers(), it will be reused for
481 482 # markers exchange.
482 483 encodeone = formats[version][1]
483 484 if addheader:
484 485 yield encodeheader(version)
485 486 for marker in markers:
486 487 yield encodeone(marker)
487 488
488 489 @util.nogc
489 490 def _addsuccessors(successors, markers):
490 491 for mark in markers:
491 492 successors.setdefault(mark[0], set()).add(mark)
492 493
493 494 @util.nogc
494 495 def _addpredecessors(predecessors, markers):
495 496 for mark in markers:
496 497 for suc in mark[1]:
497 498 predecessors.setdefault(suc, set()).add(mark)
498 499
499 500 @util.nogc
500 501 def _addchildren(children, markers):
501 502 for mark in markers:
502 503 parents = mark[5]
503 504 if parents is not None:
504 505 for p in parents:
505 506 children.setdefault(p, set()).add(mark)
506 507
507 508 def _checkinvalidmarkers(markers):
508 509 """search for marker with invalid data and raise error if needed
509 510
510 511 Exist as a separated function to allow the evolve extension for a more
511 512 subtle handling.
512 513 """
513 514 for mark in markers:
514 515 if node.nullid in mark[1]:
515 516 raise error.Abort(_('bad obsolescence marker detected: '
516 517 'invalid successors nullid'))
517 518
518 519 class obsstore(object):
519 520 """Store obsolete markers
520 521
521 522 Markers can be accessed with two mappings:
522 523 - predecessors[x] -> set(markers on predecessors edges of x)
523 524 - successors[x] -> set(markers on successors edges of x)
524 525 - children[x] -> set(markers on predecessors edges of children(x)
525 526 """
526 527
527 528 fields = ('prec', 'succs', 'flag', 'meta', 'date', 'parents')
528 529 # prec: nodeid, predecessors changesets
529 530 # succs: tuple of nodeid, successor changesets (0-N length)
530 531 # flag: integer, flag field carrying modifier for the markers (see doc)
531 532 # meta: binary blob in UTF-8, encoded metadata dictionary
532 533 # date: (float, int) tuple, date of marker creation
533 534 # parents: (tuple of nodeid) or None, parents of predecessors
534 535 # None is used when no data has been recorded
535 536
536 537 def __init__(self, svfs, defaultformat=_fm1version, readonly=False):
537 538 # caches for various obsolescence related cache
538 539 self.caches = {}
539 540 self.svfs = svfs
540 541 self._defaultformat = defaultformat
541 542 self._readonly = readonly
542 543
543 544 def __iter__(self):
544 545 return iter(self._all)
545 546
546 547 def __len__(self):
547 548 return len(self._all)
548 549
549 550 def __nonzero__(self):
550 551 if not self._cached(r'_all'):
551 552 try:
552 553 return self.svfs.stat('obsstore').st_size > 1
553 554 except OSError as inst:
554 555 if inst.errno != errno.ENOENT:
555 556 raise
556 557 # just build an empty _all list if no obsstore exists, which
557 558 # avoids further stat() syscalls
558 559 return bool(self._all)
559 560
560 561 __bool__ = __nonzero__
561 562
562 563 @property
563 564 def readonly(self):
564 565 """True if marker creation is disabled
565 566
566 567 Remove me in the future when obsolete marker is always on."""
567 568 return self._readonly
568 569
569 570 def create(self, transaction, prec, succs=(), flag=0, parents=None,
570 571 date=None, metadata=None, ui=None):
571 572 """obsolete: add a new obsolete marker
572 573
573 574 * ensuring it is hashable
574 575 * check mandatory metadata
575 576 * encode metadata
576 577
577 578 If you are a human writing code creating marker you want to use the
578 579 `createmarkers` function in this module instead.
579 580
580 581 return True if a new marker have been added, False if the markers
581 582 already existed (no op).
582 583 """
583 584 if metadata is None:
584 585 metadata = {}
585 586 if date is None:
586 587 if 'date' in metadata:
587 588 # as a courtesy for out-of-tree extensions
588 589 date = dateutil.parsedate(metadata.pop('date'))
589 590 elif ui is not None:
590 591 date = ui.configdate('devel', 'default-date')
591 592 if date is None:
592 593 date = dateutil.makedate()
593 594 else:
594 595 date = dateutil.makedate()
595 596 if len(prec) != 20:
596 597 raise ValueError(prec)
597 598 for succ in succs:
598 599 if len(succ) != 20:
599 600 raise ValueError(succ)
600 601 if prec in succs:
601 602 raise ValueError(_('in-marker cycle with %s') % node.hex(prec))
602 603
603 604 metadata = tuple(sorted(metadata.iteritems()))
604 605 for k, v in metadata:
605 606 try:
606 607 # might be better to reject non-ASCII keys
607 608 k.decode('utf-8')
608 609 v.decode('utf-8')
609 610 except UnicodeDecodeError:
610 611 raise error.ProgrammingError(
611 612 'obsstore metadata must be valid UTF-8 sequence '
612 613 '(key = %r, value = %r)'
613 614 % (pycompat.bytestr(k), pycompat.bytestr(v)))
614 615
615 616 marker = (bytes(prec), tuple(succs), int(flag), metadata, date, parents)
616 617 return bool(self.add(transaction, [marker]))
617 618
618 619 def add(self, transaction, markers):
619 620 """Add new markers to the store
620 621
621 622 Take care of filtering duplicate.
622 623 Return the number of new marker."""
623 624 if self._readonly:
624 625 raise error.Abort(_('creating obsolete markers is not enabled on '
625 626 'this repo'))
626 627 known = set()
627 628 getsuccessors = self.successors.get
628 629 new = []
629 630 for m in markers:
630 631 if m not in getsuccessors(m[0], ()) and m not in known:
631 632 known.add(m)
632 633 new.append(m)
633 634 if new:
634 635 f = self.svfs('obsstore', 'ab')
635 636 try:
636 637 offset = f.tell()
637 638 transaction.add('obsstore', offset)
638 639 # offset == 0: new file - add the version header
639 640 data = b''.join(encodemarkers(new, offset == 0, self._version))
640 641 f.write(data)
641 642 finally:
642 643 # XXX: f.close() == filecache invalidation == obsstore rebuilt.
643 644 # call 'filecacheentry.refresh()' here
644 645 f.close()
645 646 addedmarkers = transaction.changes.get('obsmarkers')
646 647 if addedmarkers is not None:
647 648 addedmarkers.update(new)
648 649 self._addmarkers(new, data)
649 650 # new marker *may* have changed several set. invalidate the cache.
650 651 self.caches.clear()
651 652 # records the number of new markers for the transaction hooks
652 653 previous = int(transaction.hookargs.get('new_obsmarkers', '0'))
653 654 transaction.hookargs['new_obsmarkers'] = '%d' % (previous + len(new))
654 655 return len(new)
655 656
656 657 def mergemarkers(self, transaction, data):
657 658 """merge a binary stream of markers inside the obsstore
658 659
659 660 Returns the number of new markers added."""
660 661 version, markers = _readmarkers(data)
661 662 return self.add(transaction, markers)
662 663
663 664 @propertycache
664 665 def _data(self):
665 666 return self.svfs.tryread('obsstore')
666 667
667 668 @propertycache
668 669 def _version(self):
669 670 if len(self._data) >= 1:
670 671 return _readmarkerversion(self._data)
671 672 else:
672 673 return self._defaultformat
673 674
674 675 @propertycache
675 676 def _all(self):
676 677 data = self._data
677 678 if not data:
678 679 return []
679 680 self._version, markers = _readmarkers(data)
680 681 markers = list(markers)
681 682 _checkinvalidmarkers(markers)
682 683 return markers
683 684
684 685 @propertycache
685 686 def successors(self):
686 687 successors = {}
687 688 _addsuccessors(successors, self._all)
688 689 return successors
689 690
690 691 @propertycache
691 692 def predecessors(self):
692 693 predecessors = {}
693 694 _addpredecessors(predecessors, self._all)
694 695 return predecessors
695 696
696 697 @propertycache
697 698 def children(self):
698 699 children = {}
699 700 _addchildren(children, self._all)
700 701 return children
701 702
702 703 def _cached(self, attr):
703 704 return attr in self.__dict__
704 705
705 706 def _addmarkers(self, markers, rawdata):
706 707 markers = list(markers) # to allow repeated iteration
707 708 self._data = self._data + rawdata
708 709 self._all.extend(markers)
709 710 if self._cached(r'successors'):
710 711 _addsuccessors(self.successors, markers)
711 712 if self._cached(r'predecessors'):
712 713 _addpredecessors(self.predecessors, markers)
713 714 if self._cached(r'children'):
714 715 _addchildren(self.children, markers)
715 716 _checkinvalidmarkers(markers)
716 717
717 718 def relevantmarkers(self, nodes):
718 719 """return a set of all obsolescence markers relevant to a set of nodes.
719 720
720 721 "relevant" to a set of nodes mean:
721 722
722 723 - marker that use this changeset as successor
723 724 - prune marker of direct children on this changeset
724 725 - recursive application of the two rules on predecessors of these
725 726 markers
726 727
727 728 It is a set so you cannot rely on order."""
728 729
729 730 pendingnodes = set(nodes)
730 731 seenmarkers = set()
731 732 seennodes = set(pendingnodes)
732 733 precursorsmarkers = self.predecessors
733 734 succsmarkers = self.successors
734 735 children = self.children
735 736 while pendingnodes:
736 737 direct = set()
737 738 for current in pendingnodes:
738 739 direct.update(precursorsmarkers.get(current, ()))
739 740 pruned = [m for m in children.get(current, ()) if not m[1]]
740 741 direct.update(pruned)
741 742 pruned = [m for m in succsmarkers.get(current, ()) if not m[1]]
742 743 direct.update(pruned)
743 744 direct -= seenmarkers
744 745 pendingnodes = set([m[0] for m in direct])
745 746 seenmarkers |= direct
746 747 pendingnodes -= seennodes
747 748 seennodes |= pendingnodes
748 749 return seenmarkers
749 750
750 751 def makestore(ui, repo):
751 752 """Create an obsstore instance from a repo."""
752 753 # read default format for new obsstore.
753 754 # developer config: format.obsstore-version
754 755 defaultformat = ui.configint('format', 'obsstore-version')
755 756 # rely on obsstore class default when possible.
756 757 kwargs = {}
757 758 if defaultformat is not None:
758 759 kwargs[r'defaultformat'] = defaultformat
759 760 readonly = not isenabled(repo, createmarkersopt)
760 761 store = obsstore(repo.svfs, readonly=readonly, **kwargs)
761 762 if store and readonly:
762 763 ui.warn(_('obsolete feature not enabled but %i markers found!\n')
763 764 % len(list(store)))
764 765 return store
765 766
766 767 def commonversion(versions):
767 768 """Return the newest version listed in both versions and our local formats.
768 769
769 770 Returns None if no common version exists.
770 771 """
771 772 versions.sort(reverse=True)
772 773 # search for highest version known on both side
773 774 for v in versions:
774 775 if v in formats:
775 776 return v
776 777 return None
777 778
778 779 # arbitrary picked to fit into 8K limit from HTTP server
779 780 # you have to take in account:
780 781 # - the version header
781 782 # - the base85 encoding
782 783 _maxpayload = 5300
783 784
784 785 def _pushkeyescape(markers):
785 786 """encode markers into a dict suitable for pushkey exchange
786 787
787 788 - binary data is base85 encoded
788 789 - split in chunks smaller than 5300 bytes"""
789 790 keys = {}
790 791 parts = []
791 792 currentlen = _maxpayload * 2 # ensure we create a new part
792 793 for marker in markers:
793 794 nextdata = _fm0encodeonemarker(marker)
794 795 if (len(nextdata) + currentlen > _maxpayload):
795 796 currentpart = []
796 797 currentlen = 0
797 798 parts.append(currentpart)
798 799 currentpart.append(nextdata)
799 800 currentlen += len(nextdata)
800 801 for idx, part in enumerate(reversed(parts)):
801 802 data = ''.join([_pack('>B', _fm0version)] + part)
802 803 keys['dump%i' % idx] = util.b85encode(data)
803 804 return keys
804 805
805 806 def listmarkers(repo):
806 807 """List markers over pushkey"""
807 808 if not repo.obsstore:
808 809 return {}
809 810 return _pushkeyescape(sorted(repo.obsstore))
810 811
811 812 def pushmarker(repo, key, old, new):
812 813 """Push markers over pushkey"""
813 814 if not key.startswith('dump'):
814 815 repo.ui.warn(_('unknown key: %r') % key)
815 816 return False
816 817 if old:
817 818 repo.ui.warn(_('unexpected old value for %r') % key)
818 819 return False
819 820 data = util.b85decode(new)
820 821 with repo.lock(), repo.transaction('pushkey: obsolete markers') as tr:
821 822 repo.obsstore.mergemarkers(tr, data)
822 823 repo.invalidatevolatilesets()
823 824 return True
824 825
825 826 # mapping of 'set-name' -> <function to compute this set>
826 827 cachefuncs = {}
827 828 def cachefor(name):
828 829 """Decorator to register a function as computing the cache for a set"""
829 830 def decorator(func):
830 831 if name in cachefuncs:
831 832 msg = "duplicated registration for volatileset '%s' (existing: %r)"
832 833 raise error.ProgrammingError(msg % (name, cachefuncs[name]))
833 834 cachefuncs[name] = func
834 835 return func
835 836 return decorator
836 837
837 838 def getrevs(repo, name):
838 839 """Return the set of revision that belong to the <name> set
839 840
840 841 Such access may compute the set and cache it for future use"""
841 842 repo = repo.unfiltered()
842 843 if not repo.obsstore:
843 844 return frozenset()
844 845 if name not in repo.obsstore.caches:
845 846 repo.obsstore.caches[name] = cachefuncs[name](repo)
846 847 return repo.obsstore.caches[name]
847 848
848 849 # To be simple we need to invalidate obsolescence cache when:
849 850 #
850 851 # - new changeset is added:
851 852 # - public phase is changed
852 853 # - obsolescence marker are added
853 854 # - strip is used a repo
854 855 def clearobscaches(repo):
855 856 """Remove all obsolescence related cache from a repo
856 857
857 858 This remove all cache in obsstore is the obsstore already exist on the
858 859 repo.
859 860
860 861 (We could be smarter here given the exact event that trigger the cache
861 862 clearing)"""
862 863 # only clear cache is there is obsstore data in this repo
863 864 if 'obsstore' in repo._filecache:
864 865 repo.obsstore.caches.clear()
865 866
866 867 def _mutablerevs(repo):
867 868 """the set of mutable revision in the repository"""
868 869 return repo._phasecache.getrevset(repo, phases.mutablephases)
869 870
870 871 @cachefor('obsolete')
871 872 def _computeobsoleteset(repo):
872 873 """the set of obsolete revisions"""
873 874 getnode = repo.changelog.node
874 875 notpublic = _mutablerevs(repo)
875 876 isobs = repo.obsstore.successors.__contains__
876 877 obs = set(r for r in notpublic if isobs(getnode(r)))
877 878 return obs
878 879
879 880 @cachefor('orphan')
880 881 def _computeorphanset(repo):
881 882 """the set of non obsolete revisions with obsolete parents"""
882 883 pfunc = repo.changelog.parentrevs
883 884 mutable = _mutablerevs(repo)
884 885 obsolete = getrevs(repo, 'obsolete')
885 886 others = mutable - obsolete
886 887 unstable = set()
887 888 for r in sorted(others):
888 889 # A rev is unstable if one of its parent is obsolete or unstable
889 890 # this works since we traverse following growing rev order
890 891 for p in pfunc(r):
891 892 if p in obsolete or p in unstable:
892 893 unstable.add(r)
893 894 break
894 895 return unstable
895 896
896 897 @cachefor('suspended')
897 898 def _computesuspendedset(repo):
898 899 """the set of obsolete parents with non obsolete descendants"""
899 900 suspended = repo.changelog.ancestors(getrevs(repo, 'orphan'))
900 901 return set(r for r in getrevs(repo, 'obsolete') if r in suspended)
901 902
902 903 @cachefor('extinct')
903 904 def _computeextinctset(repo):
904 905 """the set of obsolete parents without non obsolete descendants"""
905 906 return getrevs(repo, 'obsolete') - getrevs(repo, 'suspended')
906 907
907 908 @cachefor('phasedivergent')
908 909 def _computephasedivergentset(repo):
909 910 """the set of revs trying to obsolete public revisions"""
910 911 bumped = set()
911 912 # util function (avoid attribute lookup in the loop)
912 913 phase = repo._phasecache.phase # would be faster to grab the full list
913 914 public = phases.public
914 915 cl = repo.changelog
915 916 torev = cl.nodemap.get
916 917 tonode = cl.node
917 918 for rev in repo.revs('(not public()) and (not obsolete())'):
918 919 # We only evaluate mutable, non-obsolete revision
919 920 node = tonode(rev)
920 921 # (future) A cache of predecessors may worth if split is very common
921 922 for pnode in obsutil.allpredecessors(repo.obsstore, [node],
922 923 ignoreflags=bumpedfix):
923 924 prev = torev(pnode) # unfiltered! but so is phasecache
924 925 if (prev is not None) and (phase(repo, prev) <= public):
925 926 # we have a public predecessor
926 927 bumped.add(rev)
927 928 break # Next draft!
928 929 return bumped
929 930
930 931 @cachefor('contentdivergent')
931 932 def _computecontentdivergentset(repo):
932 933 """the set of rev that compete to be the final successors of some revision.
933 934 """
934 935 divergent = set()
935 936 obsstore = repo.obsstore
936 937 newermap = {}
937 938 tonode = repo.changelog.node
938 939 for rev in repo.revs('(not public()) - obsolete()'):
939 940 node = tonode(rev)
940 941 mark = obsstore.predecessors.get(node, ())
941 942 toprocess = set(mark)
942 943 seen = set()
943 944 while toprocess:
944 945 prec = toprocess.pop()[0]
945 946 if prec in seen:
946 947 continue # emergency cycle hanging prevention
947 948 seen.add(prec)
948 949 if prec not in newermap:
949 950 obsutil.successorssets(repo, prec, cache=newermap)
950 951 newer = [n for n in newermap[prec] if n]
951 952 if len(newer) > 1:
952 953 divergent.add(rev)
953 954 break
954 955 toprocess.update(obsstore.predecessors.get(prec, ()))
955 956 return divergent
956 957
958 def makefoldid(relation, user):
959
960 folddigest = hashlib.sha1(user)
961 for p in relation[0] + relation[1]:
962 folddigest.update('%d' % p.rev())
963 folddigest.update(p.node())
964 # Since fold only has to compete against fold for the same successors, it
965 # seems fine to use a small ID. Smaller ID save space.
966 return node.hex(folddigest.digest())[:8]
957 967
958 968 def createmarkers(repo, relations, flag=0, date=None, metadata=None,
959 969 operation=None):
960 970 """Add obsolete markers between changesets in a repo
961 971
962 972 <relations> must be an iterable of ((<old>,...), (<new>, ...)[,{metadata}])
963 973 tuple. `old` and `news` are changectx. metadata is an optional dictionary
964 974 containing metadata for this marker only. It is merged with the global
965 975 metadata specified through the `metadata` argument of this function.
966 976 Any string values in metadata must be UTF-8 bytes.
967 977
968 978 Trying to obsolete a public changeset will raise an exception.
969 979
970 980 Current user and date are used except if specified otherwise in the
971 981 metadata attribute.
972 982
973 983 This function operates within a transaction of its own, but does
974 984 not take any lock on the repo.
975 985 """
976 986 # prepare metadata
977 987 if metadata is None:
978 988 metadata = {}
979 989 if 'user' not in metadata:
980 990 luser = repo.ui.config('devel', 'user.obsmarker') or repo.ui.username()
981 991 metadata['user'] = encoding.fromlocal(luser)
982 992
983 993 # Operation metadata handling
984 994 useoperation = repo.ui.configbool('experimental',
985 995 'evolution.track-operation')
986 996 if useoperation and operation:
987 997 metadata['operation'] = operation
988 998
989 999 # Effect flag metadata handling
990 1000 saveeffectflag = repo.ui.configbool('experimental',
991 1001 'evolution.effect-flags')
992 1002
993 1003 with repo.transaction('add-obsolescence-marker') as tr:
994 1004 markerargs = []
995 1005 for rel in relations:
996 1006 predecessors = rel[0]
997 1007 if not isinstance(predecessors, tuple):
998 1008 # preserve compat with old API until all caller are migrated
999 1009 predecessors = (predecessors,)
1000 1010 if len(predecessors) > 1 and len(rel[1]) != 1:
1001 1011 msg = 'Fold markers can only have 1 successors, not %d'
1002 1012 raise error.ProgrammingError(msg % len(rel[1]))
1003 for prec in predecessors:
1013 foldid = None
1014 foldsize = len(predecessors)
1015 if 1 < foldsize:
1016 foldid = makefoldid(rel, metadata['user'])
1017 for foldidx, prec in enumerate(predecessors, 1):
1004 1018 sucs = rel[1]
1005 1019 localmetadata = metadata.copy()
1006 1020 if len(rel) > 2:
1007 1021 localmetadata.update(rel[2])
1022 if foldid is not None:
1023 localmetadata['fold-id'] = foldid
1024 localmetadata['fold-idx'] = '%d' % foldidx
1025 localmetadata['fold-size'] = '%d' % foldsize
1008 1026
1009 1027 if not prec.mutable():
1010 1028 raise error.Abort(_("cannot obsolete public changeset: %s")
1011 1029 % prec,
1012 1030 hint="see 'hg help phases' for details")
1013 1031 nprec = prec.node()
1014 1032 nsucs = tuple(s.node() for s in sucs)
1015 1033 npare = None
1016 1034 if not nsucs:
1017 1035 npare = tuple(p.node() for p in prec.parents())
1018 1036 if nprec in nsucs:
1019 1037 raise error.Abort(_("changeset %s cannot obsolete itself")
1020 1038 % prec)
1021 1039
1022 1040 # Effect flag can be different by relation
1023 1041 if saveeffectflag:
1024 1042 # The effect flag is saved in a versioned field name for
1025 1043 # future evolution
1026 1044 effectflag = obsutil.geteffectflag(prec, sucs)
1027 1045 localmetadata[obsutil.EFFECTFLAGFIELD] = "%d" % effectflag
1028 1046
1029 1047 # Creating the marker causes the hidden cache to become
1030 1048 # invalid, which causes recomputation when we ask for
1031 1049 # prec.parents() above. Resulting in n^2 behavior. So let's
1032 1050 # prepare all of the args first, then create the markers.
1033 1051 markerargs.append((nprec, nsucs, npare, localmetadata))
1034 1052
1035 1053 for args in markerargs:
1036 1054 nprec, nsucs, npare, localmetadata = args
1037 1055 repo.obsstore.create(tr, nprec, nsucs, flag, parents=npare,
1038 1056 date=date, metadata=localmetadata,
1039 1057 ui=repo.ui)
1040 1058 repo.filteredrevcache.clear()
@@ -1,2137 +1,2137 b''
1 1 ==========================
2 2 Test rebase with obsolete
3 3 ==========================
4 4
5 5 Enable obsolete
6 6
7 7 $ cat >> $HGRCPATH << EOF
8 8 > [ui]
9 9 > logtemplate= {rev}:{node|short} {desc|firstline}{if(obsolete,' ({obsfate})')}
10 10 > [experimental]
11 11 > evolution.createmarkers=True
12 12 > evolution.allowunstable=True
13 13 > [phases]
14 14 > publish=False
15 15 > [extensions]
16 16 > rebase=
17 17 > drawdag=$TESTDIR/drawdag.py
18 18 > strip=
19 19 > EOF
20 20
21 21 Setup rebase canonical repo
22 22
23 23 $ hg init base
24 24 $ cd base
25 25 $ hg unbundle "$TESTDIR/bundles/rebase.hg"
26 26 adding changesets
27 27 adding manifests
28 28 adding file changes
29 29 added 8 changesets with 7 changes to 7 files (+2 heads)
30 30 new changesets cd010b8cd998:02de42196ebe (8 drafts)
31 31 (run 'hg heads' to see heads, 'hg merge' to merge)
32 32 $ hg up tip
33 33 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
34 34 $ hg log -G
35 35 @ 7:02de42196ebe H
36 36 |
37 37 | o 6:eea13746799a G
38 38 |/|
39 39 o | 5:24b6387c8c8c F
40 40 | |
41 41 | o 4:9520eea781bc E
42 42 |/
43 43 | o 3:32af7686d403 D
44 44 | |
45 45 | o 2:5fddd98957c8 C
46 46 | |
47 47 | o 1:42ccdea3bb16 B
48 48 |/
49 49 o 0:cd010b8cd998 A
50 50
51 51 $ cd ..
52 52
53 53 simple rebase
54 54 ---------------------------------
55 55
56 56 $ hg clone base simple
57 57 updating to branch default
58 58 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
59 59 $ cd simple
60 60 $ hg up 32af7686d403
61 61 3 files updated, 0 files merged, 2 files removed, 0 files unresolved
62 62 $ hg rebase -d eea13746799a
63 63 rebasing 1:42ccdea3bb16 "B"
64 64 rebasing 2:5fddd98957c8 "C"
65 65 rebasing 3:32af7686d403 "D"
66 66 $ hg log -G
67 67 @ 10:8eeb3c33ad33 D
68 68 |
69 69 o 9:2327fea05063 C
70 70 |
71 71 o 8:e4e5be0395b2 B
72 72 |
73 73 | o 7:02de42196ebe H
74 74 | |
75 75 o | 6:eea13746799a G
76 76 |\|
77 77 | o 5:24b6387c8c8c F
78 78 | |
79 79 o | 4:9520eea781bc E
80 80 |/
81 81 o 0:cd010b8cd998 A
82 82
83 83 $ hg log --hidden -G
84 84 @ 10:8eeb3c33ad33 D
85 85 |
86 86 o 9:2327fea05063 C
87 87 |
88 88 o 8:e4e5be0395b2 B
89 89 |
90 90 | o 7:02de42196ebe H
91 91 | |
92 92 o | 6:eea13746799a G
93 93 |\|
94 94 | o 5:24b6387c8c8c F
95 95 | |
96 96 o | 4:9520eea781bc E
97 97 |/
98 98 | x 3:32af7686d403 D (rewritten using rebase as 10:8eeb3c33ad33)
99 99 | |
100 100 | x 2:5fddd98957c8 C (rewritten using rebase as 9:2327fea05063)
101 101 | |
102 102 | x 1:42ccdea3bb16 B (rewritten using rebase as 8:e4e5be0395b2)
103 103 |/
104 104 o 0:cd010b8cd998 A
105 105
106 106 $ hg debugobsolete
107 107 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 e4e5be0395b2cbd471ed22a26b1b6a1a0658a794 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'test'}
108 108 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 2327fea05063f39961b14cb69435a9898dc9a245 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'test'}
109 109 32af7686d403cf45b5d95f2d70cebea587ac806a 8eeb3c33ad33d452c89e5dcf611c347f978fb42b 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'test'}
110 110
111 111
112 112 $ cd ..
113 113
114 114 empty changeset
115 115 ---------------------------------
116 116
117 117 $ hg clone base empty
118 118 updating to branch default
119 119 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
120 120 $ cd empty
121 121 $ hg up eea13746799a
122 122 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
123 123
124 124 We make a copy of both the first changeset in the rebased and some other in the
125 125 set.
126 126
127 127 $ hg graft 42ccdea3bb16 32af7686d403
128 128 grafting 1:42ccdea3bb16 "B"
129 129 grafting 3:32af7686d403 "D"
130 130 $ hg rebase -s 42ccdea3bb16 -d .
131 131 rebasing 1:42ccdea3bb16 "B"
132 132 note: rebase of 1:42ccdea3bb16 created no changes to commit
133 133 rebasing 2:5fddd98957c8 "C"
134 134 rebasing 3:32af7686d403 "D"
135 135 note: rebase of 3:32af7686d403 created no changes to commit
136 136 $ hg log -G
137 137 o 10:5ae4c968c6ac C
138 138 |
139 139 @ 9:08483444fef9 D
140 140 |
141 141 o 8:8877864f1edb B
142 142 |
143 143 | o 7:02de42196ebe H
144 144 | |
145 145 o | 6:eea13746799a G
146 146 |\|
147 147 | o 5:24b6387c8c8c F
148 148 | |
149 149 o | 4:9520eea781bc E
150 150 |/
151 151 o 0:cd010b8cd998 A
152 152
153 153 $ hg log --hidden -G
154 154 o 10:5ae4c968c6ac C
155 155 |
156 156 @ 9:08483444fef9 D
157 157 |
158 158 o 8:8877864f1edb B
159 159 |
160 160 | o 7:02de42196ebe H
161 161 | |
162 162 o | 6:eea13746799a G
163 163 |\|
164 164 | o 5:24b6387c8c8c F
165 165 | |
166 166 o | 4:9520eea781bc E
167 167 |/
168 168 | x 3:32af7686d403 D (pruned using rebase)
169 169 | |
170 170 | x 2:5fddd98957c8 C (rewritten using rebase as 10:5ae4c968c6ac)
171 171 | |
172 172 | x 1:42ccdea3bb16 B (pruned using rebase)
173 173 |/
174 174 o 0:cd010b8cd998 A
175 175
176 176 $ hg debugobsolete
177 177 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 {cd010b8cd998f3981a5a8115f94f8da4ab506089} (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '0', 'operation': 'rebase', 'user': 'test'}
178 178 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 5ae4c968c6aca831df823664e706c9d4aa34473d 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'test'}
179 179 32af7686d403cf45b5d95f2d70cebea587ac806a 0 {5fddd98957c8a54a4d436dfe1da9d87f21a1b97b} (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '0', 'operation': 'rebase', 'user': 'test'}
180 180
181 181
182 182 More complex case where part of the rebase set were already rebased
183 183
184 184 $ hg rebase --rev 'desc(D)' --dest 'desc(H)'
185 185 rebasing 9:08483444fef9 "D"
186 186 1 new orphan changesets
187 187 $ hg debugobsolete
188 188 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 {cd010b8cd998f3981a5a8115f94f8da4ab506089} (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '0', 'operation': 'rebase', 'user': 'test'}
189 189 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 5ae4c968c6aca831df823664e706c9d4aa34473d 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'test'}
190 190 32af7686d403cf45b5d95f2d70cebea587ac806a 0 {5fddd98957c8a54a4d436dfe1da9d87f21a1b97b} (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '0', 'operation': 'rebase', 'user': 'test'}
191 191 08483444fef91d6224f6655ee586a65d263ad34c 4596109a6a4328c398bde3a4a3b6737cfade3003 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'test'}
192 192 $ hg log -G
193 193 @ 11:4596109a6a43 D
194 194 |
195 195 | * 10:5ae4c968c6ac C
196 196 | |
197 197 | x 9:08483444fef9 D (rewritten using rebase as 11:4596109a6a43)
198 198 | |
199 199 | o 8:8877864f1edb B
200 200 | |
201 201 o | 7:02de42196ebe H
202 202 | |
203 203 | o 6:eea13746799a G
204 204 |/|
205 205 o | 5:24b6387c8c8c F
206 206 | |
207 207 | o 4:9520eea781bc E
208 208 |/
209 209 o 0:cd010b8cd998 A
210 210
211 211 $ hg rebase --source 'desc(B)' --dest 'tip' --config experimental.rebaseskipobsolete=True
212 212 rebasing 8:8877864f1edb "B"
213 213 note: not rebasing 9:08483444fef9 "D", already in destination as 11:4596109a6a43 "D" (tip)
214 214 rebasing 10:5ae4c968c6ac "C"
215 215 $ hg debugobsolete
216 216 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 {cd010b8cd998f3981a5a8115f94f8da4ab506089} (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '0', 'operation': 'rebase', 'user': 'test'}
217 217 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 5ae4c968c6aca831df823664e706c9d4aa34473d 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'test'}
218 218 32af7686d403cf45b5d95f2d70cebea587ac806a 0 {5fddd98957c8a54a4d436dfe1da9d87f21a1b97b} (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '0', 'operation': 'rebase', 'user': 'test'}
219 219 08483444fef91d6224f6655ee586a65d263ad34c 4596109a6a4328c398bde3a4a3b6737cfade3003 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'test'}
220 220 8877864f1edb05d0e07dc4ba77b67a80a7b86672 462a34d07e599b87ea08676a449373fe4e2e1347 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'test'}
221 221 5ae4c968c6aca831df823664e706c9d4aa34473d 98f6af4ee9539e14da4465128f894c274900b6e5 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'test'}
222 222 $ hg log --rev 'contentdivergent()'
223 223 $ hg log -G
224 224 o 13:98f6af4ee953 C
225 225 |
226 226 o 12:462a34d07e59 B
227 227 |
228 228 @ 11:4596109a6a43 D
229 229 |
230 230 o 7:02de42196ebe H
231 231 |
232 232 | o 6:eea13746799a G
233 233 |/|
234 234 o | 5:24b6387c8c8c F
235 235 | |
236 236 | o 4:9520eea781bc E
237 237 |/
238 238 o 0:cd010b8cd998 A
239 239
240 240 $ hg log --style default --debug -r 4596109a6a4328c398bde3a4a3b6737cfade3003
241 241 changeset: 11:4596109a6a4328c398bde3a4a3b6737cfade3003
242 242 phase: draft
243 243 parent: 7:02de42196ebee42ef284b6780a87cdc96e8eaab6
244 244 parent: -1:0000000000000000000000000000000000000000
245 245 manifest: 11:a91006e3a02f1edf631f7018e6e5684cf27dd905
246 246 user: Nicolas Dumazet <nicdumz.commits@gmail.com>
247 247 date: Sat Apr 30 15:24:48 2011 +0200
248 248 files+: D
249 249 extra: branch=default
250 250 extra: rebase_source=08483444fef91d6224f6655ee586a65d263ad34c
251 251 extra: source=32af7686d403cf45b5d95f2d70cebea587ac806a
252 252 description:
253 253 D
254 254
255 255
256 256 $ hg up -qr 'desc(G)'
257 257 $ hg graft 4596109a6a4328c398bde3a4a3b6737cfade3003
258 258 grafting 11:4596109a6a43 "D"
259 259 $ hg up -qr 'desc(E)'
260 260 $ hg rebase -s tip -d .
261 261 rebasing 14:9e36056a46e3 "D" (tip)
262 262 $ hg log --style default --debug -r tip
263 263 changeset: 15:627d4614809036ba22b9e7cb31638ddc06ab99ab
264 264 tag: tip
265 265 phase: draft
266 266 parent: 4:9520eea781bcca16c1e15acc0ba14335a0e8e5ba
267 267 parent: -1:0000000000000000000000000000000000000000
268 268 manifest: 15:648e8ede73ae3e497d093d3a4c8fcc2daa864f42
269 269 user: Nicolas Dumazet <nicdumz.commits@gmail.com>
270 270 date: Sat Apr 30 15:24:48 2011 +0200
271 271 files+: D
272 272 extra: branch=default
273 273 extra: intermediate-source=4596109a6a4328c398bde3a4a3b6737cfade3003
274 274 extra: rebase_source=9e36056a46e37c9776168c7375734eebc70e294f
275 275 extra: source=32af7686d403cf45b5d95f2d70cebea587ac806a
276 276 description:
277 277 D
278 278
279 279
280 280 Start rebase from a commit that is obsolete but not hidden only because it's
281 281 a working copy parent. We should be moved back to the starting commit as usual
282 282 even though it is hidden (until we're moved there).
283 283
284 284 $ hg --hidden up -qr 'first(hidden())'
285 285 updated to hidden changeset 42ccdea3bb16
286 286 (hidden revision '42ccdea3bb16' is pruned)
287 287 $ hg rebase --rev 13 --dest 15
288 288 rebasing 13:98f6af4ee953 "C"
289 289 $ hg log -G
290 290 o 16:294a2b93eb4d C
291 291 |
292 292 o 15:627d46148090 D
293 293 |
294 294 | o 12:462a34d07e59 B
295 295 | |
296 296 | o 11:4596109a6a43 D
297 297 | |
298 298 | o 7:02de42196ebe H
299 299 | |
300 300 +---o 6:eea13746799a G
301 301 | |/
302 302 | o 5:24b6387c8c8c F
303 303 | |
304 304 o | 4:9520eea781bc E
305 305 |/
306 306 | @ 1:42ccdea3bb16 B (pruned using rebase)
307 307 |/
308 308 o 0:cd010b8cd998 A
309 309
310 310
311 311 $ cd ..
312 312
313 313 collapse rebase
314 314 ---------------------------------
315 315
316 316 $ hg clone base collapse
317 317 updating to branch default
318 318 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
319 319 $ cd collapse
320 320 $ hg rebase -s 42ccdea3bb16 -d eea13746799a --collapse
321 321 rebasing 1:42ccdea3bb16 "B"
322 322 rebasing 2:5fddd98957c8 "C"
323 323 rebasing 3:32af7686d403 "D"
324 324 $ hg log -G
325 325 o 8:4dc2197e807b Collapsed revision
326 326 |
327 327 | @ 7:02de42196ebe H
328 328 | |
329 329 o | 6:eea13746799a G
330 330 |\|
331 331 | o 5:24b6387c8c8c F
332 332 | |
333 333 o | 4:9520eea781bc E
334 334 |/
335 335 o 0:cd010b8cd998 A
336 336
337 337 $ hg log --hidden -G
338 338 o 8:4dc2197e807b Collapsed revision
339 339 |
340 340 | @ 7:02de42196ebe H
341 341 | |
342 342 o | 6:eea13746799a G
343 343 |\|
344 344 | o 5:24b6387c8c8c F
345 345 | |
346 346 o | 4:9520eea781bc E
347 347 |/
348 348 | x 3:32af7686d403 D (rewritten using rebase as 8:4dc2197e807b)
349 349 | |
350 350 | x 2:5fddd98957c8 C (rewritten using rebase as 8:4dc2197e807b)
351 351 | |
352 352 | x 1:42ccdea3bb16 B (rewritten using rebase as 8:4dc2197e807b)
353 353 |/
354 354 o 0:cd010b8cd998 A
355 355
356 356 $ hg id --debug -r tip
357 357 4dc2197e807bae9817f09905b50ab288be2dbbcf tip
358 358 $ hg debugobsolete
359 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 4dc2197e807bae9817f09905b50ab288be2dbbcf 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '13', 'operation': 'rebase', 'user': 'test'}
360 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 4dc2197e807bae9817f09905b50ab288be2dbbcf 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '13', 'operation': 'rebase', 'user': 'test'}
361 32af7686d403cf45b5d95f2d70cebea587ac806a 4dc2197e807bae9817f09905b50ab288be2dbbcf 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '13', 'operation': 'rebase', 'user': 'test'}
359 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 4dc2197e807bae9817f09905b50ab288be2dbbcf 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '13', 'fold-id': '6fb65cdc', 'fold-idx': '1', 'fold-size': '3', 'operation': 'rebase', 'user': 'test'}
360 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 4dc2197e807bae9817f09905b50ab288be2dbbcf 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '13', 'fold-id': '6fb65cdc', 'fold-idx': '2', 'fold-size': '3', 'operation': 'rebase', 'user': 'test'}
361 32af7686d403cf45b5d95f2d70cebea587ac806a 4dc2197e807bae9817f09905b50ab288be2dbbcf 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '13', 'fold-id': '6fb65cdc', 'fold-idx': '3', 'fold-size': '3', 'operation': 'rebase', 'user': 'test'}
362 362
363 363 $ cd ..
364 364
365 365 Rebase set has hidden descendants
366 366 ---------------------------------
367 367
368 368 We rebase a changeset which has hidden descendants. Hidden changesets must not
369 369 be rebased.
370 370
371 371 $ hg clone base hidden
372 372 updating to branch default
373 373 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
374 374 $ cd hidden
375 375 $ hg log -G
376 376 @ 7:02de42196ebe H
377 377 |
378 378 | o 6:eea13746799a G
379 379 |/|
380 380 o | 5:24b6387c8c8c F
381 381 | |
382 382 | o 4:9520eea781bc E
383 383 |/
384 384 | o 3:32af7686d403 D
385 385 | |
386 386 | o 2:5fddd98957c8 C
387 387 | |
388 388 | o 1:42ccdea3bb16 B
389 389 |/
390 390 o 0:cd010b8cd998 A
391 391
392 392 $ hg rebase -s 5fddd98957c8 -d eea13746799a
393 393 rebasing 2:5fddd98957c8 "C"
394 394 rebasing 3:32af7686d403 "D"
395 395 $ hg log -G
396 396 o 9:cf44d2f5a9f4 D
397 397 |
398 398 o 8:e273c5e7d2d2 C
399 399 |
400 400 | @ 7:02de42196ebe H
401 401 | |
402 402 o | 6:eea13746799a G
403 403 |\|
404 404 | o 5:24b6387c8c8c F
405 405 | |
406 406 o | 4:9520eea781bc E
407 407 |/
408 408 | o 1:42ccdea3bb16 B
409 409 |/
410 410 o 0:cd010b8cd998 A
411 411
412 412 $ hg rebase -s 42ccdea3bb16 -d 02de42196ebe
413 413 rebasing 1:42ccdea3bb16 "B"
414 414 $ hg log -G
415 415 o 10:7c6027df6a99 B
416 416 |
417 417 | o 9:cf44d2f5a9f4 D
418 418 | |
419 419 | o 8:e273c5e7d2d2 C
420 420 | |
421 421 @ | 7:02de42196ebe H
422 422 | |
423 423 | o 6:eea13746799a G
424 424 |/|
425 425 o | 5:24b6387c8c8c F
426 426 | |
427 427 | o 4:9520eea781bc E
428 428 |/
429 429 o 0:cd010b8cd998 A
430 430
431 431 $ hg log --hidden -G
432 432 o 10:7c6027df6a99 B
433 433 |
434 434 | o 9:cf44d2f5a9f4 D
435 435 | |
436 436 | o 8:e273c5e7d2d2 C
437 437 | |
438 438 @ | 7:02de42196ebe H
439 439 | |
440 440 | o 6:eea13746799a G
441 441 |/|
442 442 o | 5:24b6387c8c8c F
443 443 | |
444 444 | o 4:9520eea781bc E
445 445 |/
446 446 | x 3:32af7686d403 D (rewritten using rebase as 9:cf44d2f5a9f4)
447 447 | |
448 448 | x 2:5fddd98957c8 C (rewritten using rebase as 8:e273c5e7d2d2)
449 449 | |
450 450 | x 1:42ccdea3bb16 B (rewritten using rebase as 10:7c6027df6a99)
451 451 |/
452 452 o 0:cd010b8cd998 A
453 453
454 454 $ hg debugobsolete
455 455 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b e273c5e7d2d29df783dce9f9eaa3ac4adc69c15d 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'test'}
456 456 32af7686d403cf45b5d95f2d70cebea587ac806a cf44d2f5a9f4297a62be94cbdd3dff7c7dc54258 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'test'}
457 457 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 7c6027df6a99d93f461868e5433f63bde20b6dfb 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'test'}
458 458
459 459 Test that rewriting leaving instability behind is allowed
460 460 ---------------------------------------------------------------------
461 461
462 462 $ hg log -r 'children(8)'
463 463 9:cf44d2f5a9f4 D (no-eol)
464 464 $ hg rebase -r 8
465 465 rebasing 8:e273c5e7d2d2 "C"
466 466 1 new orphan changesets
467 467 $ hg log -G
468 468 o 11:0d8f238b634c C
469 469 |
470 470 o 10:7c6027df6a99 B
471 471 |
472 472 | * 9:cf44d2f5a9f4 D
473 473 | |
474 474 | x 8:e273c5e7d2d2 C (rewritten using rebase as 11:0d8f238b634c)
475 475 | |
476 476 @ | 7:02de42196ebe H
477 477 | |
478 478 | o 6:eea13746799a G
479 479 |/|
480 480 o | 5:24b6387c8c8c F
481 481 | |
482 482 | o 4:9520eea781bc E
483 483 |/
484 484 o 0:cd010b8cd998 A
485 485
486 486 $ cd ..
487 487 $ cp -R hidden stabilize
488 488 $ cd stabilize
489 489 $ hg rebase --auto-orphans '0::' -d 10
490 490 abort: --auto-orphans is incompatible with --dest
491 491 [255]
492 492 $ hg rebase --auto-orphans '0::'
493 493 rebasing 9:cf44d2f5a9f4 "D"
494 494 $ hg log -G
495 495 o 12:7e3935feaa68 D
496 496 |
497 497 o 11:0d8f238b634c C
498 498 |
499 499 o 10:7c6027df6a99 B
500 500 |
501 501 @ 7:02de42196ebe H
502 502 |
503 503 | o 6:eea13746799a G
504 504 |/|
505 505 o | 5:24b6387c8c8c F
506 506 | |
507 507 | o 4:9520eea781bc E
508 508 |/
509 509 o 0:cd010b8cd998 A
510 510
511 511
512 512 $ cd ../hidden
513 513 $ rm -r ../stabilize
514 514
515 515 Test multiple root handling
516 516 ------------------------------------
517 517
518 518 $ hg rebase --dest 4 --rev '7+11+9'
519 519 rebasing 9:cf44d2f5a9f4 "D"
520 520 rebasing 7:02de42196ebe "H"
521 521 rebasing 11:0d8f238b634c "C" (tip)
522 522 $ hg log -G
523 523 o 14:1e8370e38cca C
524 524 |
525 525 @ 13:bfe264faf697 H
526 526 |
527 527 | o 12:102b4c1d889b D
528 528 |/
529 529 | * 10:7c6027df6a99 B
530 530 | |
531 531 | x 7:02de42196ebe H (rewritten using rebase as 13:bfe264faf697)
532 532 | |
533 533 +---o 6:eea13746799a G
534 534 | |/
535 535 | o 5:24b6387c8c8c F
536 536 | |
537 537 o | 4:9520eea781bc E
538 538 |/
539 539 o 0:cd010b8cd998 A
540 540
541 541 $ cd ..
542 542
543 543 Detach both parents
544 544
545 545 $ hg init double-detach
546 546 $ cd double-detach
547 547
548 548 $ hg debugdrawdag <<EOF
549 549 > F
550 550 > /|
551 551 > C E
552 552 > | |
553 553 > B D G
554 554 > \|/
555 555 > A
556 556 > EOF
557 557
558 558 $ hg rebase -d G -r 'B + D + F'
559 559 rebasing 1:112478962961 "B" (B)
560 560 rebasing 2:b18e25de2cf5 "D" (D)
561 561 rebasing 6:f15c3adaf214 "F" (F tip)
562 562 abort: cannot rebase 6:f15c3adaf214 without moving at least one of its parents
563 563 [255]
564 564
565 565 $ cd ..
566 566
567 567 test on rebase dropping a merge
568 568
569 569 (setup)
570 570
571 571 $ hg init dropmerge
572 572 $ cd dropmerge
573 573 $ hg unbundle "$TESTDIR/bundles/rebase.hg"
574 574 adding changesets
575 575 adding manifests
576 576 adding file changes
577 577 added 8 changesets with 7 changes to 7 files (+2 heads)
578 578 new changesets cd010b8cd998:02de42196ebe (8 drafts)
579 579 (run 'hg heads' to see heads, 'hg merge' to merge)
580 580 $ hg up 3
581 581 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
582 582 $ hg merge 7
583 583 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
584 584 (branch merge, don't forget to commit)
585 585 $ hg ci -m 'M'
586 586 $ echo I > I
587 587 $ hg add I
588 588 $ hg ci -m I
589 589 $ hg log -G
590 590 @ 9:4bde274eefcf I
591 591 |
592 592 o 8:53a6a128b2b7 M
593 593 |\
594 594 | o 7:02de42196ebe H
595 595 | |
596 596 | | o 6:eea13746799a G
597 597 | |/|
598 598 | o | 5:24b6387c8c8c F
599 599 | | |
600 600 | | o 4:9520eea781bc E
601 601 | |/
602 602 o | 3:32af7686d403 D
603 603 | |
604 604 o | 2:5fddd98957c8 C
605 605 | |
606 606 o | 1:42ccdea3bb16 B
607 607 |/
608 608 o 0:cd010b8cd998 A
609 609
610 610 (actual test)
611 611
612 612 $ hg rebase --dest 6 --rev '((desc(H) + desc(D))::) - desc(M)'
613 613 rebasing 3:32af7686d403 "D"
614 614 rebasing 7:02de42196ebe "H"
615 615 rebasing 9:4bde274eefcf "I" (tip)
616 616 1 new orphan changesets
617 617 $ hg log -G
618 618 @ 12:acd174b7ab39 I
619 619 |
620 620 o 11:6c11a6218c97 H
621 621 |
622 622 | o 10:b5313c85b22e D
623 623 |/
624 624 | * 8:53a6a128b2b7 M
625 625 | |\
626 626 | | x 7:02de42196ebe H (rewritten using rebase as 11:6c11a6218c97)
627 627 | | |
628 628 o---+ 6:eea13746799a G
629 629 | | |
630 630 | | o 5:24b6387c8c8c F
631 631 | | |
632 632 o---+ 4:9520eea781bc E
633 633 / /
634 634 x | 3:32af7686d403 D (rewritten using rebase as 10:b5313c85b22e)
635 635 | |
636 636 o | 2:5fddd98957c8 C
637 637 | |
638 638 o | 1:42ccdea3bb16 B
639 639 |/
640 640 o 0:cd010b8cd998 A
641 641
642 642
643 643 Test hidden changesets in the rebase set (issue4504)
644 644
645 645 $ hg up --hidden 9
646 646 3 files updated, 0 files merged, 1 files removed, 0 files unresolved
647 647 updated to hidden changeset 4bde274eefcf
648 648 (hidden revision '4bde274eefcf' was rewritten as: acd174b7ab39)
649 649 $ echo J > J
650 650 $ hg add J
651 651 $ hg commit -m J
652 652 1 new orphan changesets
653 653 $ hg debugobsolete `hg log --rev . -T '{node}'`
654 654 obsoleted 1 changesets
655 655
656 656 $ hg rebase --rev .~1::. --dest 'max(desc(D))' --traceback --config experimental.rebaseskipobsolete=off
657 657 rebasing 9:4bde274eefcf "I"
658 658 rebasing 13:06edfc82198f "J" (tip)
659 659 2 new content-divergent changesets
660 660 $ hg log -G
661 661 @ 15:5ae8a643467b J
662 662 |
663 663 * 14:9ad579b4a5de I
664 664 |
665 665 | * 12:acd174b7ab39 I
666 666 | |
667 667 | o 11:6c11a6218c97 H
668 668 | |
669 669 o | 10:b5313c85b22e D
670 670 |/
671 671 | * 8:53a6a128b2b7 M
672 672 | |\
673 673 | | x 7:02de42196ebe H (rewritten using rebase as 11:6c11a6218c97)
674 674 | | |
675 675 o---+ 6:eea13746799a G
676 676 | | |
677 677 | | o 5:24b6387c8c8c F
678 678 | | |
679 679 o---+ 4:9520eea781bc E
680 680 / /
681 681 x | 3:32af7686d403 D (rewritten using rebase as 10:b5313c85b22e)
682 682 | |
683 683 o | 2:5fddd98957c8 C
684 684 | |
685 685 o | 1:42ccdea3bb16 B
686 686 |/
687 687 o 0:cd010b8cd998 A
688 688
689 689 $ hg up 14 -C
690 690 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
691 691 $ echo "K" > K
692 692 $ hg add K
693 693 $ hg commit --amend -m "K"
694 694 1 new orphan changesets
695 695 $ echo "L" > L
696 696 $ hg add L
697 697 $ hg commit -m "L"
698 698 $ hg up '.^'
699 699 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
700 700 $ echo "M" > M
701 701 $ hg add M
702 702 $ hg commit --amend -m "M"
703 703 1 new orphan changesets
704 704 $ hg log -G
705 705 @ 18:bfaedf8eb73b M
706 706 |
707 707 | * 17:97219452e4bd L
708 708 | |
709 709 | x 16:fc37a630c901 K (rewritten using amend as 18:bfaedf8eb73b)
710 710 |/
711 711 | * 15:5ae8a643467b J
712 712 | |
713 713 | x 14:9ad579b4a5de I (rewritten using amend as 16:fc37a630c901)
714 714 |/
715 715 | * 12:acd174b7ab39 I
716 716 | |
717 717 | o 11:6c11a6218c97 H
718 718 | |
719 719 o | 10:b5313c85b22e D
720 720 |/
721 721 | * 8:53a6a128b2b7 M
722 722 | |\
723 723 | | x 7:02de42196ebe H (rewritten using rebase as 11:6c11a6218c97)
724 724 | | |
725 725 o---+ 6:eea13746799a G
726 726 | | |
727 727 | | o 5:24b6387c8c8c F
728 728 | | |
729 729 o---+ 4:9520eea781bc E
730 730 / /
731 731 x | 3:32af7686d403 D (rewritten using rebase as 10:b5313c85b22e)
732 732 | |
733 733 o | 2:5fddd98957c8 C
734 734 | |
735 735 o | 1:42ccdea3bb16 B
736 736 |/
737 737 o 0:cd010b8cd998 A
738 738
739 739 $ hg rebase -s 14 -d 17 --config experimental.rebaseskipobsolete=True
740 740 note: not rebasing 14:9ad579b4a5de "I", already in destination as 16:fc37a630c901 "K"
741 741 rebasing 15:5ae8a643467b "J"
742 742 1 new orphan changesets
743 743
744 744 $ cd ..
745 745
746 746 Skip obsolete changeset even with multiple hops
747 747 -----------------------------------------------
748 748
749 749 setup
750 750
751 751 $ hg init obsskip
752 752 $ cd obsskip
753 753 $ cat << EOF >> .hg/hgrc
754 754 > [experimental]
755 755 > rebaseskipobsolete = True
756 756 > [extensions]
757 757 > strip =
758 758 > EOF
759 759 $ echo A > A
760 760 $ hg add A
761 761 $ hg commit -m A
762 762 $ echo B > B
763 763 $ hg add B
764 764 $ hg commit -m B0
765 765 $ hg commit --amend -m B1
766 766 $ hg commit --amend -m B2
767 767 $ hg up --hidden 'desc(B0)'
768 768 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
769 769 updated to hidden changeset a8b11f55fb19
770 770 (hidden revision 'a8b11f55fb19' was rewritten as: 261e70097290)
771 771 $ echo C > C
772 772 $ hg add C
773 773 $ hg commit -m C
774 774 1 new orphan changesets
775 775 $ hg log -G
776 776 @ 4:212cb178bcbb C
777 777 |
778 778 | o 3:261e70097290 B2
779 779 | |
780 780 x | 1:a8b11f55fb19 B0 (rewritten using amend as 3:261e70097290)
781 781 |/
782 782 o 0:4a2df7238c3b A
783 783
784 784
785 785 Rebase finds its way in a chain of marker
786 786
787 787 $ hg rebase -d 'desc(B2)'
788 788 note: not rebasing 1:a8b11f55fb19 "B0", already in destination as 3:261e70097290 "B2"
789 789 rebasing 4:212cb178bcbb "C" (tip)
790 790
791 791 Even when the chain include missing node
792 792
793 793 $ hg up --hidden 'desc(B0)'
794 794 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
795 795 updated to hidden changeset a8b11f55fb19
796 796 (hidden revision 'a8b11f55fb19' was rewritten as: 261e70097290)
797 797 $ echo D > D
798 798 $ hg add D
799 799 $ hg commit -m D
800 800 1 new orphan changesets
801 801 $ hg --hidden strip -r 'desc(B1)'
802 802 saved backup bundle to $TESTTMP/obsskip/.hg/strip-backup/86f6414ccda7-b1c452ee-backup.hg
803 803 1 new orphan changesets
804 804 $ hg log -G
805 805 @ 5:1a79b7535141 D
806 806 |
807 807 | o 4:ff2c4d47b71d C
808 808 | |
809 809 | o 2:261e70097290 B2
810 810 | |
811 811 x | 1:a8b11f55fb19 B0 (rewritten using amend as 2:261e70097290)
812 812 |/
813 813 o 0:4a2df7238c3b A
814 814
815 815
816 816 $ hg rebase -d 'desc(B2)'
817 817 note: not rebasing 1:a8b11f55fb19 "B0", already in destination as 2:261e70097290 "B2"
818 818 rebasing 5:1a79b7535141 "D" (tip)
819 819 $ hg up 4
820 820 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
821 821 $ echo "O" > O
822 822 $ hg add O
823 823 $ hg commit -m O
824 824 $ echo "P" > P
825 825 $ hg add P
826 826 $ hg commit -m P
827 827 $ hg log -G
828 828 @ 8:8d47583e023f P
829 829 |
830 830 o 7:360bbaa7d3ce O
831 831 |
832 832 | o 6:9c48361117de D
833 833 | |
834 834 o | 4:ff2c4d47b71d C
835 835 |/
836 836 o 2:261e70097290 B2
837 837 |
838 838 o 0:4a2df7238c3b A
839 839
840 840 $ hg debugobsolete `hg log -r 7 -T '{node}\n'` --config experimental.evolution=true
841 841 obsoleted 1 changesets
842 842 1 new orphan changesets
843 843 $ hg rebase -d 6 -r "4::"
844 844 rebasing 4:ff2c4d47b71d "C"
845 845 note: not rebasing 7:360bbaa7d3ce "O", it has no successor
846 846 rebasing 8:8d47583e023f "P" (tip)
847 847
848 848 If all the changeset to be rebased are obsolete and present in the destination, we
849 849 should display a friendly error message
850 850
851 851 $ hg log -G
852 852 @ 10:121d9e3bc4c6 P
853 853 |
854 854 o 9:4be60e099a77 C
855 855 |
856 856 o 6:9c48361117de D
857 857 |
858 858 o 2:261e70097290 B2
859 859 |
860 860 o 0:4a2df7238c3b A
861 861
862 862
863 863 $ hg up 9
864 864 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
865 865 $ echo "non-relevant change" > nonrelevant
866 866 $ hg add nonrelevant
867 867 $ hg commit -m nonrelevant
868 868 created new head
869 869 $ hg debugobsolete `hg log -r 11 -T '{node}\n'` --config experimental.evolution=true
870 870 obsoleted 1 changesets
871 871 $ hg log -G
872 872 @ 11:f44da1f4954c nonrelevant (pruned)
873 873 |
874 874 | o 10:121d9e3bc4c6 P
875 875 |/
876 876 o 9:4be60e099a77 C
877 877 |
878 878 o 6:9c48361117de D
879 879 |
880 880 o 2:261e70097290 B2
881 881 |
882 882 o 0:4a2df7238c3b A
883 883
884 884 $ hg rebase -r . -d 10
885 885 note: not rebasing 11:f44da1f4954c "nonrelevant" (tip), it has no successor
886 886
887 887 If a rebase is going to create divergence, it should abort
888 888
889 889 $ hg log -G
890 890 @ 10:121d9e3bc4c6 P
891 891 |
892 892 o 9:4be60e099a77 C
893 893 |
894 894 o 6:9c48361117de D
895 895 |
896 896 o 2:261e70097290 B2
897 897 |
898 898 o 0:4a2df7238c3b A
899 899
900 900
901 901 $ hg up 9
902 902 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
903 903 $ echo "john" > doe
904 904 $ hg add doe
905 905 $ hg commit -m "john doe"
906 906 created new head
907 907 $ hg up 10
908 908 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
909 909 $ echo "foo" > bar
910 910 $ hg add bar
911 911 $ hg commit --amend -m "10'"
912 912 $ hg up 10 --hidden
913 913 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
914 914 updated to hidden changeset 121d9e3bc4c6
915 915 (hidden revision '121d9e3bc4c6' was rewritten as: 77d874d096a2)
916 916 $ echo "bar" > foo
917 917 $ hg add foo
918 918 $ hg commit -m "bar foo"
919 919 1 new orphan changesets
920 920 $ hg log -G
921 921 @ 14:73568ab6879d bar foo
922 922 |
923 923 | o 13:77d874d096a2 10'
924 924 | |
925 925 | | o 12:3eb461388009 john doe
926 926 | |/
927 927 x | 10:121d9e3bc4c6 P (rewritten using amend as 13:77d874d096a2)
928 928 |/
929 929 o 9:4be60e099a77 C
930 930 |
931 931 o 6:9c48361117de D
932 932 |
933 933 o 2:261e70097290 B2
934 934 |
935 935 o 0:4a2df7238c3b A
936 936
937 937 $ hg summary
938 938 parent: 14:73568ab6879d tip (orphan)
939 939 bar foo
940 940 branch: default
941 941 commit: (clean)
942 942 update: 2 new changesets, 3 branch heads (merge)
943 943 phases: 8 draft
944 944 orphan: 1 changesets
945 945 $ hg rebase -s 10 -d 12
946 946 abort: this rebase will cause divergences from: 121d9e3bc4c6
947 947 (to force the rebase please set experimental.evolution.allowdivergence=True)
948 948 [255]
949 949 $ hg log -G
950 950 @ 14:73568ab6879d bar foo
951 951 |
952 952 | o 13:77d874d096a2 10'
953 953 | |
954 954 | | o 12:3eb461388009 john doe
955 955 | |/
956 956 x | 10:121d9e3bc4c6 P (rewritten using amend as 13:77d874d096a2)
957 957 |/
958 958 o 9:4be60e099a77 C
959 959 |
960 960 o 6:9c48361117de D
961 961 |
962 962 o 2:261e70097290 B2
963 963 |
964 964 o 0:4a2df7238c3b A
965 965
966 966 With experimental.evolution.allowdivergence=True, rebase can create divergence
967 967
968 968 $ hg rebase -s 10 -d 12 --config experimental.evolution.allowdivergence=True
969 969 rebasing 10:121d9e3bc4c6 "P"
970 970 rebasing 14:73568ab6879d "bar foo" (tip)
971 971 2 new content-divergent changesets
972 972 $ hg summary
973 973 parent: 16:61bd55f69bc4 tip
974 974 bar foo
975 975 branch: default
976 976 commit: (clean)
977 977 update: 1 new changesets, 2 branch heads (merge)
978 978 phases: 8 draft
979 979 content-divergent: 2 changesets
980 980
981 981 rebase --continue + skipped rev because their successors are in destination
982 982 we make a change in trunk and work on conflicting changes to make rebase abort.
983 983
984 984 $ hg log -G -r 16::
985 985 @ 16:61bd55f69bc4 bar foo
986 986 |
987 987 ~
988 988
989 989 Create the two changes in trunk
990 990 $ printf "a" > willconflict
991 991 $ hg add willconflict
992 992 $ hg commit -m "willconflict first version"
993 993
994 994 $ printf "dummy" > C
995 995 $ hg commit -m "dummy change successor"
996 996
997 997 Create the changes that we will rebase
998 998 $ hg update -C 16 -q
999 999 $ printf "b" > willconflict
1000 1000 $ hg add willconflict
1001 1001 $ hg commit -m "willconflict second version"
1002 1002 created new head
1003 1003 $ printf "dummy" > K
1004 1004 $ hg add K
1005 1005 $ hg commit -m "dummy change"
1006 1006 $ printf "dummy" > L
1007 1007 $ hg add L
1008 1008 $ hg commit -m "dummy change"
1009 1009 $ hg debugobsolete `hg log -r ".^" -T '{node}'` `hg log -r 18 -T '{node}'` --config experimental.evolution=true
1010 1010 obsoleted 1 changesets
1011 1011 1 new orphan changesets
1012 1012
1013 1013 $ hg log -G -r 16::
1014 1014 @ 21:7bdc8a87673d dummy change
1015 1015 |
1016 1016 x 20:8b31da3c4919 dummy change (rewritten as 18:601db7a18f51)
1017 1017 |
1018 1018 o 19:b82fb57ea638 willconflict second version
1019 1019 |
1020 1020 | o 18:601db7a18f51 dummy change successor
1021 1021 | |
1022 1022 | o 17:357ddf1602d5 willconflict first version
1023 1023 |/
1024 1024 o 16:61bd55f69bc4 bar foo
1025 1025 |
1026 1026 ~
1027 1027 $ hg rebase -r ".^^ + .^ + ." -d 18
1028 1028 rebasing 19:b82fb57ea638 "willconflict second version"
1029 1029 merging willconflict
1030 1030 warning: conflicts while merging willconflict! (edit, then use 'hg resolve --mark')
1031 1031 unresolved conflicts (see hg resolve, then hg rebase --continue)
1032 1032 [1]
1033 1033
1034 1034 $ hg resolve --mark willconflict
1035 1035 (no more unresolved files)
1036 1036 continue: hg rebase --continue
1037 1037 $ hg rebase --continue
1038 1038 rebasing 19:b82fb57ea638 "willconflict second version"
1039 1039 note: not rebasing 20:8b31da3c4919 "dummy change", already in destination as 18:601db7a18f51 "dummy change successor"
1040 1040 rebasing 21:7bdc8a87673d "dummy change" (tip)
1041 1041 $ cd ..
1042 1042
1043 1043 Divergence cases due to obsolete changesets
1044 1044 -------------------------------------------
1045 1045
1046 1046 We should ignore branches with unstable changesets when they are based on an
1047 1047 obsolete changeset which successor is in rebase set.
1048 1048
1049 1049 $ hg init divergence
1050 1050 $ cd divergence
1051 1051 $ cat >> .hg/hgrc << EOF
1052 1052 > [extensions]
1053 1053 > strip =
1054 1054 > [alias]
1055 1055 > strip = strip --no-backup --quiet
1056 1056 > [templates]
1057 1057 > instabilities = '{rev}:{node|short} {desc|firstline}{if(instabilities," ({instabilities})")}\n'
1058 1058 > EOF
1059 1059
1060 1060 $ hg debugdrawdag <<EOF
1061 1061 > e f
1062 1062 > | |
1063 1063 > d' d # replace: d -> d'
1064 1064 > \ /
1065 1065 > c
1066 1066 > |
1067 1067 > x b
1068 1068 > \|
1069 1069 > a
1070 1070 > EOF
1071 1071 1 new orphan changesets
1072 1072 $ hg log -G -r 'a'::
1073 1073 * 7:1143e9adc121 f
1074 1074 |
1075 1075 | o 6:d60ebfa0f1cb e
1076 1076 | |
1077 1077 | o 5:027ad6c5830d d'
1078 1078 | |
1079 1079 x | 4:76be324c128b d (rewritten using replace as 5:027ad6c5830d)
1080 1080 |/
1081 1081 o 3:a82ac2b38757 c
1082 1082 |
1083 1083 | o 2:630d7c95eff7 x
1084 1084 | |
1085 1085 o | 1:488e1b7e7341 b
1086 1086 |/
1087 1087 o 0:b173517d0057 a
1088 1088
1089 1089
1090 1090 Changeset d and its descendants are excluded to avoid divergence of d, which
1091 1091 would occur because the successor of d (d') is also in rebaseset. As a
1092 1092 consequence f (descendant of d) is left behind.
1093 1093
1094 1094 $ hg rebase -b 'e' -d 'x'
1095 1095 rebasing 1:488e1b7e7341 "b" (b)
1096 1096 rebasing 3:a82ac2b38757 "c" (c)
1097 1097 rebasing 5:027ad6c5830d "d'" (d')
1098 1098 rebasing 6:d60ebfa0f1cb "e" (e)
1099 1099 note: not rebasing 4:76be324c128b "d" (d) and its descendants as this would cause divergence
1100 1100 $ hg log -G -r 'a'::
1101 1101 o 11:eb6d63fc4ed5 e
1102 1102 |
1103 1103 o 10:44d8c724a70c d'
1104 1104 |
1105 1105 o 9:d008e6b4d3fd c
1106 1106 |
1107 1107 o 8:67e8f4a16c49 b
1108 1108 |
1109 1109 | * 7:1143e9adc121 f
1110 1110 | |
1111 1111 | | x 6:d60ebfa0f1cb e (rewritten using rebase as 11:eb6d63fc4ed5)
1112 1112 | | |
1113 1113 | | x 5:027ad6c5830d d' (rewritten using rebase as 10:44d8c724a70c)
1114 1114 | | |
1115 1115 | x | 4:76be324c128b d (rewritten using replace as 5:027ad6c5830d)
1116 1116 | |/
1117 1117 | x 3:a82ac2b38757 c (rewritten using rebase as 9:d008e6b4d3fd)
1118 1118 | |
1119 1119 o | 2:630d7c95eff7 x
1120 1120 | |
1121 1121 | x 1:488e1b7e7341 b (rewritten using rebase as 8:67e8f4a16c49)
1122 1122 |/
1123 1123 o 0:b173517d0057 a
1124 1124
1125 1125 $ hg strip -r 8:
1126 1126 $ hg log -G -r 'a'::
1127 1127 * 7:1143e9adc121 f
1128 1128 |
1129 1129 | o 6:d60ebfa0f1cb e
1130 1130 | |
1131 1131 | o 5:027ad6c5830d d'
1132 1132 | |
1133 1133 x | 4:76be324c128b d (rewritten using replace as 5:027ad6c5830d)
1134 1134 |/
1135 1135 o 3:a82ac2b38757 c
1136 1136 |
1137 1137 | o 2:630d7c95eff7 x
1138 1138 | |
1139 1139 o | 1:488e1b7e7341 b
1140 1140 |/
1141 1141 o 0:b173517d0057 a
1142 1142
1143 1143
1144 1144 If the rebase set has an obsolete (d) with a successor (d') outside the rebase
1145 1145 set and none in destination, we still get the divergence warning.
1146 1146 By allowing divergence, we can perform the rebase.
1147 1147
1148 1148 $ hg rebase -r 'c'::'f' -d 'x'
1149 1149 abort: this rebase will cause divergences from: 76be324c128b
1150 1150 (to force the rebase please set experimental.evolution.allowdivergence=True)
1151 1151 [255]
1152 1152 $ hg rebase --config experimental.evolution.allowdivergence=true -r 'c'::'f' -d 'x'
1153 1153 rebasing 3:a82ac2b38757 "c" (c)
1154 1154 rebasing 4:76be324c128b "d" (d)
1155 1155 rebasing 7:1143e9adc121 "f" (f tip)
1156 1156 1 new orphan changesets
1157 1157 2 new content-divergent changesets
1158 1158 $ hg log -G -r 'a':: -T instabilities
1159 1159 o 10:e1744ea07510 f
1160 1160 |
1161 1161 * 9:e2b36ea9a0a0 d (content-divergent)
1162 1162 |
1163 1163 o 8:6a0376de376e c
1164 1164 |
1165 1165 | x 7:1143e9adc121 f
1166 1166 | |
1167 1167 | | * 6:d60ebfa0f1cb e (orphan)
1168 1168 | | |
1169 1169 | | * 5:027ad6c5830d d' (orphan content-divergent)
1170 1170 | | |
1171 1171 | x | 4:76be324c128b d
1172 1172 | |/
1173 1173 | x 3:a82ac2b38757 c
1174 1174 | |
1175 1175 o | 2:630d7c95eff7 x
1176 1176 | |
1177 1177 | o 1:488e1b7e7341 b
1178 1178 |/
1179 1179 o 0:b173517d0057 a
1180 1180
1181 1181 $ hg strip -r 8:
1182 1182
1183 1183 (Not skipping obsoletes means that divergence is allowed.)
1184 1184
1185 1185 $ hg rebase --config experimental.rebaseskipobsolete=false -r 'c'::'f' -d 'x'
1186 1186 rebasing 3:a82ac2b38757 "c" (c)
1187 1187 rebasing 4:76be324c128b "d" (d)
1188 1188 rebasing 7:1143e9adc121 "f" (f tip)
1189 1189 1 new orphan changesets
1190 1190 2 new content-divergent changesets
1191 1191
1192 1192 $ hg strip -r 0:
1193 1193
1194 1194 Similar test on a more complex graph
1195 1195
1196 1196 $ hg debugdrawdag <<EOF
1197 1197 > g
1198 1198 > |
1199 1199 > f e
1200 1200 > | |
1201 1201 > e' d # replace: e -> e'
1202 1202 > \ /
1203 1203 > c
1204 1204 > |
1205 1205 > x b
1206 1206 > \|
1207 1207 > a
1208 1208 > EOF
1209 1209 1 new orphan changesets
1210 1210 $ hg log -G -r 'a':
1211 1211 * 8:2876ce66c6eb g
1212 1212 |
1213 1213 | o 7:3ffec603ab53 f
1214 1214 | |
1215 1215 x | 6:e36fae928aec e (rewritten using replace as 5:63324dc512ea)
1216 1216 | |
1217 1217 | o 5:63324dc512ea e'
1218 1218 | |
1219 1219 o | 4:76be324c128b d
1220 1220 |/
1221 1221 o 3:a82ac2b38757 c
1222 1222 |
1223 1223 | o 2:630d7c95eff7 x
1224 1224 | |
1225 1225 o | 1:488e1b7e7341 b
1226 1226 |/
1227 1227 o 0:b173517d0057 a
1228 1228
1229 1229 $ hg rebase -b 'f' -d 'x'
1230 1230 rebasing 1:488e1b7e7341 "b" (b)
1231 1231 rebasing 3:a82ac2b38757 "c" (c)
1232 1232 rebasing 5:63324dc512ea "e'" (e')
1233 1233 rebasing 7:3ffec603ab53 "f" (f)
1234 1234 rebasing 4:76be324c128b "d" (d)
1235 1235 note: not rebasing 6:e36fae928aec "e" (e) and its descendants as this would cause divergence
1236 1236 $ hg log -G -r 'a':
1237 1237 o 13:a1707a5b7c2c d
1238 1238 |
1239 1239 | o 12:ef6251596616 f
1240 1240 | |
1241 1241 | o 11:b6f172e64af9 e'
1242 1242 |/
1243 1243 o 10:d008e6b4d3fd c
1244 1244 |
1245 1245 o 9:67e8f4a16c49 b
1246 1246 |
1247 1247 | * 8:2876ce66c6eb g
1248 1248 | |
1249 1249 | | x 7:3ffec603ab53 f (rewritten using rebase as 12:ef6251596616)
1250 1250 | | |
1251 1251 | x | 6:e36fae928aec e (rewritten using replace as 5:63324dc512ea)
1252 1252 | | |
1253 1253 | | x 5:63324dc512ea e' (rewritten using rebase as 11:b6f172e64af9)
1254 1254 | | |
1255 1255 | x | 4:76be324c128b d (rewritten using rebase as 13:a1707a5b7c2c)
1256 1256 | |/
1257 1257 | x 3:a82ac2b38757 c (rewritten using rebase as 10:d008e6b4d3fd)
1258 1258 | |
1259 1259 o | 2:630d7c95eff7 x
1260 1260 | |
1261 1261 | x 1:488e1b7e7341 b (rewritten using rebase as 9:67e8f4a16c49)
1262 1262 |/
1263 1263 o 0:b173517d0057 a
1264 1264
1265 1265
1266 1266 issue5782
1267 1267 $ hg strip -r 0:
1268 1268 $ hg debugdrawdag <<EOF
1269 1269 > d
1270 1270 > |
1271 1271 > c1 c # replace: c -> c1
1272 1272 > \ /
1273 1273 > b
1274 1274 > |
1275 1275 > a
1276 1276 > EOF
1277 1277 1 new orphan changesets
1278 1278 $ hg debugobsolete `hg log -T "{node}" --hidden -r 'desc("c1")'`
1279 1279 obsoleted 1 changesets
1280 1280 $ hg log -G -r 'a': --hidden
1281 1281 * 4:76be324c128b d
1282 1282 |
1283 1283 | x 3:ef8a456de8fa c1 (pruned)
1284 1284 | |
1285 1285 x | 2:a82ac2b38757 c (rewritten using replace as 3:ef8a456de8fa)
1286 1286 |/
1287 1287 o 1:488e1b7e7341 b
1288 1288 |
1289 1289 o 0:b173517d0057 a
1290 1290
1291 1291 $ hg rebase -d 0 -r 2
1292 1292 rebasing 2:a82ac2b38757 "c" (c)
1293 1293 $ hg log -G -r 'a': --hidden
1294 1294 o 5:69ad416a4a26 c
1295 1295 |
1296 1296 | * 4:76be324c128b d
1297 1297 | |
1298 1298 | | x 3:ef8a456de8fa c1 (pruned)
1299 1299 | | |
1300 1300 | x | 2:a82ac2b38757 c (rewritten using replace as 3:ef8a456de8fa rewritten using rebase as 5:69ad416a4a26)
1301 1301 | |/
1302 1302 | o 1:488e1b7e7341 b
1303 1303 |/
1304 1304 o 0:b173517d0057 a
1305 1305
1306 1306 $ cd ..
1307 1307
1308 1308 Rebase merge where successor of one parent is equal to destination (issue5198)
1309 1309
1310 1310 $ hg init p1-succ-is-dest
1311 1311 $ cd p1-succ-is-dest
1312 1312
1313 1313 $ hg debugdrawdag <<EOF
1314 1314 > F
1315 1315 > /|
1316 1316 > E D B # replace: D -> B
1317 1317 > \|/
1318 1318 > A
1319 1319 > EOF
1320 1320 1 new orphan changesets
1321 1321
1322 1322 $ hg rebase -d B -s D
1323 1323 note: not rebasing 2:b18e25de2cf5 "D" (D), already in destination as 1:112478962961 "B" (B)
1324 1324 rebasing 4:66f1a38021c9 "F" (F tip)
1325 1325 $ hg log -G
1326 1326 o 5:50e9d60b99c6 F
1327 1327 |\
1328 1328 | | x 4:66f1a38021c9 F (rewritten using rebase as 5:50e9d60b99c6)
1329 1329 | |/|
1330 1330 | o | 3:7fb047a69f22 E
1331 1331 | | |
1332 1332 | | x 2:b18e25de2cf5 D (rewritten using replace as 1:112478962961)
1333 1333 | |/
1334 1334 o | 1:112478962961 B
1335 1335 |/
1336 1336 o 0:426bada5c675 A
1337 1337
1338 1338 $ cd ..
1339 1339
1340 1340 Rebase merge where successor of other parent is equal to destination
1341 1341
1342 1342 $ hg init p2-succ-is-dest
1343 1343 $ cd p2-succ-is-dest
1344 1344
1345 1345 $ hg debugdrawdag <<EOF
1346 1346 > F
1347 1347 > /|
1348 1348 > E D B # replace: E -> B
1349 1349 > \|/
1350 1350 > A
1351 1351 > EOF
1352 1352 1 new orphan changesets
1353 1353
1354 1354 $ hg rebase -d B -s E
1355 1355 note: not rebasing 3:7fb047a69f22 "E" (E), already in destination as 1:112478962961 "B" (B)
1356 1356 rebasing 4:66f1a38021c9 "F" (F tip)
1357 1357 $ hg log -G
1358 1358 o 5:aae1787dacee F
1359 1359 |\
1360 1360 | | x 4:66f1a38021c9 F (rewritten using rebase as 5:aae1787dacee)
1361 1361 | |/|
1362 1362 | | x 3:7fb047a69f22 E (rewritten using replace as 1:112478962961)
1363 1363 | | |
1364 1364 | o | 2:b18e25de2cf5 D
1365 1365 | |/
1366 1366 o / 1:112478962961 B
1367 1367 |/
1368 1368 o 0:426bada5c675 A
1369 1369
1370 1370 $ cd ..
1371 1371
1372 1372 Rebase merge where successor of one parent is ancestor of destination
1373 1373
1374 1374 $ hg init p1-succ-in-dest
1375 1375 $ cd p1-succ-in-dest
1376 1376
1377 1377 $ hg debugdrawdag <<EOF
1378 1378 > F C
1379 1379 > /| |
1380 1380 > E D B # replace: D -> B
1381 1381 > \|/
1382 1382 > A
1383 1383 > EOF
1384 1384 1 new orphan changesets
1385 1385
1386 1386 $ hg rebase -d C -s D
1387 1387 note: not rebasing 2:b18e25de2cf5 "D" (D), already in destination as 1:112478962961 "B" (B)
1388 1388 rebasing 5:66f1a38021c9 "F" (F tip)
1389 1389
1390 1390 $ hg log -G
1391 1391 o 6:0913febf6439 F
1392 1392 |\
1393 1393 +---x 5:66f1a38021c9 F (rewritten using rebase as 6:0913febf6439)
1394 1394 | | |
1395 1395 | o | 4:26805aba1e60 C
1396 1396 | | |
1397 1397 o | | 3:7fb047a69f22 E
1398 1398 | | |
1399 1399 +---x 2:b18e25de2cf5 D (rewritten using replace as 1:112478962961)
1400 1400 | |
1401 1401 | o 1:112478962961 B
1402 1402 |/
1403 1403 o 0:426bada5c675 A
1404 1404
1405 1405 $ cd ..
1406 1406
1407 1407 Rebase merge where successor of other parent is ancestor of destination
1408 1408
1409 1409 $ hg init p2-succ-in-dest
1410 1410 $ cd p2-succ-in-dest
1411 1411
1412 1412 $ hg debugdrawdag <<EOF
1413 1413 > F C
1414 1414 > /| |
1415 1415 > E D B # replace: E -> B
1416 1416 > \|/
1417 1417 > A
1418 1418 > EOF
1419 1419 1 new orphan changesets
1420 1420
1421 1421 $ hg rebase -d C -s E
1422 1422 note: not rebasing 3:7fb047a69f22 "E" (E), already in destination as 1:112478962961 "B" (B)
1423 1423 rebasing 5:66f1a38021c9 "F" (F tip)
1424 1424 $ hg log -G
1425 1425 o 6:c6ab0cc6d220 F
1426 1426 |\
1427 1427 +---x 5:66f1a38021c9 F (rewritten using rebase as 6:c6ab0cc6d220)
1428 1428 | | |
1429 1429 | o | 4:26805aba1e60 C
1430 1430 | | |
1431 1431 | | x 3:7fb047a69f22 E (rewritten using replace as 1:112478962961)
1432 1432 | | |
1433 1433 o---+ 2:b18e25de2cf5 D
1434 1434 / /
1435 1435 o / 1:112478962961 B
1436 1436 |/
1437 1437 o 0:426bada5c675 A
1438 1438
1439 1439 $ cd ..
1440 1440
1441 1441 Rebase merge where successor of one parent is ancestor of destination
1442 1442
1443 1443 $ hg init p1-succ-in-dest-b
1444 1444 $ cd p1-succ-in-dest-b
1445 1445
1446 1446 $ hg debugdrawdag <<EOF
1447 1447 > F C
1448 1448 > /| |
1449 1449 > E D B # replace: E -> B
1450 1450 > \|/
1451 1451 > A
1452 1452 > EOF
1453 1453 1 new orphan changesets
1454 1454
1455 1455 $ hg rebase -d C -b F
1456 1456 rebasing 2:b18e25de2cf5 "D" (D)
1457 1457 note: not rebasing 3:7fb047a69f22 "E" (E), already in destination as 1:112478962961 "B" (B)
1458 1458 rebasing 5:66f1a38021c9 "F" (F tip)
1459 1459 note: rebase of 5:66f1a38021c9 created no changes to commit
1460 1460 $ hg log -G
1461 1461 o 6:8f47515dda15 D
1462 1462 |
1463 1463 | x 5:66f1a38021c9 F (pruned using rebase)
1464 1464 | |\
1465 1465 o | | 4:26805aba1e60 C
1466 1466 | | |
1467 1467 | | x 3:7fb047a69f22 E (rewritten using replace as 1:112478962961)
1468 1468 | | |
1469 1469 | x | 2:b18e25de2cf5 D (rewritten using rebase as 6:8f47515dda15)
1470 1470 | |/
1471 1471 o / 1:112478962961 B
1472 1472 |/
1473 1473 o 0:426bada5c675 A
1474 1474
1475 1475 $ cd ..
1476 1476
1477 1477 Rebase merge where successor of other parent is ancestor of destination
1478 1478
1479 1479 $ hg init p2-succ-in-dest-b
1480 1480 $ cd p2-succ-in-dest-b
1481 1481
1482 1482 $ hg debugdrawdag <<EOF
1483 1483 > F C
1484 1484 > /| |
1485 1485 > E D B # replace: D -> B
1486 1486 > \|/
1487 1487 > A
1488 1488 > EOF
1489 1489 1 new orphan changesets
1490 1490
1491 1491 $ hg rebase -d C -b F
1492 1492 note: not rebasing 2:b18e25de2cf5 "D" (D), already in destination as 1:112478962961 "B" (B)
1493 1493 rebasing 3:7fb047a69f22 "E" (E)
1494 1494 rebasing 5:66f1a38021c9 "F" (F tip)
1495 1495 note: rebase of 5:66f1a38021c9 created no changes to commit
1496 1496
1497 1497 $ hg log -G
1498 1498 o 6:533690786a86 E
1499 1499 |
1500 1500 | x 5:66f1a38021c9 F (pruned using rebase)
1501 1501 | |\
1502 1502 o | | 4:26805aba1e60 C
1503 1503 | | |
1504 1504 | | x 3:7fb047a69f22 E (rewritten using rebase as 6:533690786a86)
1505 1505 | | |
1506 1506 | x | 2:b18e25de2cf5 D (rewritten using replace as 1:112478962961)
1507 1507 | |/
1508 1508 o / 1:112478962961 B
1509 1509 |/
1510 1510 o 0:426bada5c675 A
1511 1511
1512 1512 $ cd ..
1513 1513
1514 1514 Rebase merge where extinct node has successor that is not an ancestor of
1515 1515 destination
1516 1516
1517 1517 $ hg init extinct-with-succ-not-in-dest
1518 1518 $ cd extinct-with-succ-not-in-dest
1519 1519
1520 1520 $ hg debugdrawdag <<EOF
1521 1521 > E C # replace: C -> E
1522 1522 > | |
1523 1523 > D B
1524 1524 > |/
1525 1525 > A
1526 1526 > EOF
1527 1527
1528 1528 $ hg rebase -d D -s B
1529 1529 rebasing 1:112478962961 "B" (B)
1530 1530 note: not rebasing 3:26805aba1e60 "C" (C) and its descendants as this would cause divergence
1531 1531
1532 1532 $ cd ..
1533 1533
1534 1534 $ hg init p2-succ-in-dest-c
1535 1535 $ cd p2-succ-in-dest-c
1536 1536
1537 1537 The scenario here was that B::D were developed on default. B was queued on
1538 1538 stable, but amended before being push to hg-committed. C was queued on default,
1539 1539 along with unrelated J.
1540 1540
1541 1541 $ hg debugdrawdag <<EOF
1542 1542 > J
1543 1543 > |
1544 1544 > F
1545 1545 > |
1546 1546 > E
1547 1547 > | D
1548 1548 > | |
1549 1549 > | C # replace: C -> F
1550 1550 > | | H I # replace: B -> H -> I
1551 1551 > | B |/
1552 1552 > |/ G
1553 1553 > A
1554 1554 > EOF
1555 1555 1 new orphan changesets
1556 1556
1557 1557 This strip seems to be the key to avoid an early divergence warning.
1558 1558 $ hg --config extensions.strip= --hidden strip -qr H
1559 1559 1 new orphan changesets
1560 1560
1561 1561 $ hg rebase -b 'desc("D")' -d 'desc("J")'
1562 1562 abort: this rebase will cause divergences from: 112478962961
1563 1563 (to force the rebase please set experimental.evolution.allowdivergence=True)
1564 1564 [255]
1565 1565
1566 1566 Rebase merge where both parents have successors in destination
1567 1567
1568 1568 $ hg init p12-succ-in-dest
1569 1569 $ cd p12-succ-in-dest
1570 1570 $ hg debugdrawdag <<'EOS'
1571 1571 > E F
1572 1572 > /| /| # replace: A -> C
1573 1573 > A B C D # replace: B -> D
1574 1574 > | |
1575 1575 > X Y
1576 1576 > EOS
1577 1577 1 new orphan changesets
1578 1578 $ hg rebase -r A+B+E -d F
1579 1579 note: not rebasing 4:a3d17304151f "A" (A), already in destination as 0:96cc3511f894 "C" (C)
1580 1580 note: not rebasing 5:b23a2cc00842 "B" (B), already in destination as 1:058c1e1fb10a "D" (D)
1581 1581 rebasing 7:dac5d11c5a7d "E" (E tip)
1582 1582 abort: rebasing 7:dac5d11c5a7d will include unwanted changes from 3:59c792af609c, 5:b23a2cc00842 or 2:ba2b7fa7166d, 4:a3d17304151f
1583 1583 [255]
1584 1584 $ cd ..
1585 1585
1586 1586 Rebase a non-clean merge. One parent has successor in destination, the other
1587 1587 parent moves as requested.
1588 1588
1589 1589 $ hg init p1-succ-p2-move
1590 1590 $ cd p1-succ-p2-move
1591 1591 $ hg debugdrawdag <<'EOS'
1592 1592 > D Z
1593 1593 > /| | # replace: A -> C
1594 1594 > A B C # D/D = D
1595 1595 > EOS
1596 1596 1 new orphan changesets
1597 1597 $ hg rebase -r A+B+D -d Z
1598 1598 note: not rebasing 0:426bada5c675 "A" (A), already in destination as 2:96cc3511f894 "C" (C)
1599 1599 rebasing 1:fc2b737bb2e5 "B" (B)
1600 1600 rebasing 3:b8ed089c80ad "D" (D)
1601 1601
1602 1602 $ rm .hg/localtags
1603 1603 $ hg log -G
1604 1604 o 6:e4f78693cc88 D
1605 1605 |
1606 1606 o 5:76840d832e98 B
1607 1607 |
1608 1608 o 4:50e41c1f3950 Z
1609 1609 |
1610 1610 o 2:96cc3511f894 C
1611 1611
1612 1612 $ hg files -r tip
1613 1613 B
1614 1614 C
1615 1615 D
1616 1616 Z
1617 1617
1618 1618 $ cd ..
1619 1619
1620 1620 $ hg init p1-move-p2-succ
1621 1621 $ cd p1-move-p2-succ
1622 1622 $ hg debugdrawdag <<'EOS'
1623 1623 > D Z
1624 1624 > /| | # replace: B -> C
1625 1625 > A B C # D/D = D
1626 1626 > EOS
1627 1627 1 new orphan changesets
1628 1628 $ hg rebase -r B+A+D -d Z
1629 1629 rebasing 0:426bada5c675 "A" (A)
1630 1630 note: not rebasing 1:fc2b737bb2e5 "B" (B), already in destination as 2:96cc3511f894 "C" (C)
1631 1631 rebasing 3:b8ed089c80ad "D" (D)
1632 1632
1633 1633 $ rm .hg/localtags
1634 1634 $ hg log -G
1635 1635 o 6:1b355ed94d82 D
1636 1636 |
1637 1637 o 5:a81a74d764a6 A
1638 1638 |
1639 1639 o 4:50e41c1f3950 Z
1640 1640 |
1641 1641 o 2:96cc3511f894 C
1642 1642
1643 1643 $ hg files -r tip
1644 1644 A
1645 1645 C
1646 1646 D
1647 1647 Z
1648 1648
1649 1649 $ cd ..
1650 1650
1651 1651 Test that bookmark is moved and working dir is updated when all changesets have
1652 1652 equivalents in destination
1653 1653 $ hg init rbsrepo && cd rbsrepo
1654 1654 $ echo "[experimental]" > .hg/hgrc
1655 1655 $ echo "evolution=true" >> .hg/hgrc
1656 1656 $ echo "rebaseskipobsolete=on" >> .hg/hgrc
1657 1657 $ echo root > root && hg ci -Am root
1658 1658 adding root
1659 1659 $ echo a > a && hg ci -Am a
1660 1660 adding a
1661 1661 $ hg up 0
1662 1662 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1663 1663 $ echo b > b && hg ci -Am b
1664 1664 adding b
1665 1665 created new head
1666 1666 $ hg rebase -r 2 -d 1
1667 1667 rebasing 2:1e9a3c00cbe9 "b" (tip)
1668 1668 $ hg log -r . # working dir is at rev 3 (successor of 2)
1669 1669 3:be1832deae9a b (no-eol)
1670 1670 $ hg book -r 2 mybook --hidden # rev 2 has a bookmark on it now
1671 1671 bookmarking hidden changeset 1e9a3c00cbe9
1672 1672 (hidden revision '1e9a3c00cbe9' was rewritten as: be1832deae9a)
1673 1673 $ hg up 2 && hg log -r . # working dir is at rev 2 again
1674 1674 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1675 1675 2:1e9a3c00cbe9 b (rewritten using rebase as 3:be1832deae9a) (no-eol)
1676 1676 $ hg rebase -r 2 -d 3 --config experimental.evolution.track-operation=1
1677 1677 note: not rebasing 2:1e9a3c00cbe9 "b" (mybook), already in destination as 3:be1832deae9a "b" (tip)
1678 1678 Check that working directory and bookmark was updated to rev 3 although rev 2
1679 1679 was skipped
1680 1680 $ hg log -r .
1681 1681 3:be1832deae9a b (no-eol)
1682 1682 $ hg bookmarks
1683 1683 mybook 3:be1832deae9a
1684 1684 $ hg debugobsolete --rev tip
1685 1685 1e9a3c00cbe90d236ac05ef61efcc5e40b7412bc be1832deae9ac531caa7438b8dcf6055a122cd8e 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'test'}
1686 1686
1687 1687 Obsoleted working parent and bookmark could be moved if an ancestor of working
1688 1688 parent gets moved:
1689 1689
1690 1690 $ hg init $TESTTMP/ancestor-wd-move
1691 1691 $ cd $TESTTMP/ancestor-wd-move
1692 1692 $ hg debugdrawdag <<'EOS'
1693 1693 > E D1 # rebase: D1 -> D2
1694 1694 > | |
1695 1695 > | C
1696 1696 > D2 |
1697 1697 > | B
1698 1698 > |/
1699 1699 > A
1700 1700 > EOS
1701 1701 $ hg update D1 -q
1702 1702 $ hg bookmark book -i
1703 1703 $ hg rebase -r B+D1 -d E
1704 1704 rebasing 1:112478962961 "B" (B)
1705 1705 note: not rebasing 5:15ecf15e0114 "D1" (book D1 tip), already in destination as 2:0807738e0be9 "D2" (D2)
1706 1706 1 new orphan changesets
1707 1707 $ hg log -G -T '{desc} {bookmarks}'
1708 1708 @ B book
1709 1709 |
1710 1710 | x D1
1711 1711 | |
1712 1712 o | E
1713 1713 | |
1714 1714 | * C
1715 1715 | |
1716 1716 o | D2
1717 1717 | |
1718 1718 | x B
1719 1719 |/
1720 1720 o A
1721 1721
1722 1722 Rebasing a merge with one of its parent having a hidden successor
1723 1723
1724 1724 $ hg init $TESTTMP/merge-p1-hidden-successor
1725 1725 $ cd $TESTTMP/merge-p1-hidden-successor
1726 1726
1727 1727 $ hg debugdrawdag <<'EOS'
1728 1728 > E
1729 1729 > |
1730 1730 > B3 B2 # amend: B1 -> B2 -> B3
1731 1731 > |/ # B2 is hidden
1732 1732 > | D
1733 1733 > | |\
1734 1734 > | B1 C
1735 1735 > |/
1736 1736 > A
1737 1737 > EOS
1738 1738 1 new orphan changesets
1739 1739
1740 1740 $ eval `hg tags -T '{tag}={node}\n'`
1741 1741 $ rm .hg/localtags
1742 1742
1743 1743 $ hg rebase -r $D -d $E
1744 1744 rebasing 5:9e62094e4d94 "D"
1745 1745
1746 1746 $ hg log -G
1747 1747 o 7:a699d059adcf D
1748 1748 |\
1749 1749 | o 6:ecc93090a95c E
1750 1750 | |
1751 1751 | o 4:0dc878468a23 B3
1752 1752 | |
1753 1753 o | 1:96cc3511f894 C
1754 1754 /
1755 1755 o 0:426bada5c675 A
1756 1756
1757 1757 For some reasons (--hidden, rebaseskipobsolete=0, directaccess, etc.),
1758 1758 rebasestate may contain hidden hashes. "rebase --abort" should work regardless.
1759 1759
1760 1760 $ hg init $TESTTMP/hidden-state1
1761 1761 $ cd $TESTTMP/hidden-state1
1762 1762 $ cat >> .hg/hgrc <<EOF
1763 1763 > [experimental]
1764 1764 > rebaseskipobsolete=0
1765 1765 > EOF
1766 1766
1767 1767 $ hg debugdrawdag <<'EOS'
1768 1768 > C
1769 1769 > |
1770 1770 > D B # prune: B, C
1771 1771 > |/ # B/D=B
1772 1772 > A
1773 1773 > EOS
1774 1774
1775 1775 $ eval `hg tags -T '{tag}={node}\n'`
1776 1776 $ rm .hg/localtags
1777 1777
1778 1778 $ hg update -q $C --hidden
1779 1779 updated to hidden changeset 7829726be4dc
1780 1780 (hidden revision '7829726be4dc' is pruned)
1781 1781 $ hg rebase -s $B -d $D
1782 1782 rebasing 1:2ec65233581b "B"
1783 1783 merging D
1784 1784 warning: conflicts while merging D! (edit, then use 'hg resolve --mark')
1785 1785 unresolved conflicts (see hg resolve, then hg rebase --continue)
1786 1786 [1]
1787 1787
1788 1788 $ cp -R . $TESTTMP/hidden-state2
1789 1789
1790 1790 $ hg log -G
1791 1791 @ 2:b18e25de2cf5 D
1792 1792 |
1793 1793 | @ 1:2ec65233581b B (pruned using prune)
1794 1794 |/
1795 1795 o 0:426bada5c675 A
1796 1796
1797 1797 $ hg summary
1798 1798 parent: 2:b18e25de2cf5 tip
1799 1799 D
1800 1800 parent: 1:2ec65233581b (obsolete)
1801 1801 B
1802 1802 branch: default
1803 1803 commit: 2 modified, 1 unknown, 1 unresolved (merge)
1804 1804 update: (current)
1805 1805 phases: 3 draft
1806 1806 rebase: 0 rebased, 2 remaining (rebase --continue)
1807 1807
1808 1808 $ hg rebase --abort
1809 1809 rebase aborted
1810 1810
1811 1811 Also test --continue for the above case
1812 1812
1813 1813 $ cd $TESTTMP/hidden-state2
1814 1814 $ hg resolve -m
1815 1815 (no more unresolved files)
1816 1816 continue: hg rebase --continue
1817 1817 $ hg rebase --continue
1818 1818 rebasing 1:2ec65233581b "B"
1819 1819 rebasing 3:7829726be4dc "C" (tip)
1820 1820 $ hg log -G
1821 1821 @ 5:1964d5d5b547 C
1822 1822 |
1823 1823 o 4:68deb90c12a2 B
1824 1824 |
1825 1825 o 2:b18e25de2cf5 D
1826 1826 |
1827 1827 o 0:426bada5c675 A
1828 1828
1829 1829 ====================
1830 1830 Test --stop option |
1831 1831 ====================
1832 1832 $ cd ..
1833 1833 $ hg init rbstop
1834 1834 $ cd rbstop
1835 1835 $ echo a>a
1836 1836 $ hg ci -Aqma
1837 1837 $ echo b>b
1838 1838 $ hg ci -Aqmb
1839 1839 $ echo c>c
1840 1840 $ hg ci -Aqmc
1841 1841 $ echo d>d
1842 1842 $ hg ci -Aqmd
1843 1843 $ hg up 0 -q
1844 1844 $ echo f>f
1845 1845 $ hg ci -Aqmf
1846 1846 $ echo D>d
1847 1847 $ hg ci -Aqm "conflict with d"
1848 1848 $ hg up 3 -q
1849 1849 $ hg log -G --template "{rev}:{short(node)} {person(author)}\n{firstline(desc)} {topic}\n\n"
1850 1850 o 5:00bfc9898aeb test
1851 1851 | conflict with d
1852 1852 |
1853 1853 o 4:dafd40200f93 test
1854 1854 | f
1855 1855 |
1856 1856 | @ 3:055a42cdd887 test
1857 1857 | | d
1858 1858 | |
1859 1859 | o 2:177f92b77385 test
1860 1860 | | c
1861 1861 | |
1862 1862 | o 1:d2ae7f538514 test
1863 1863 |/ b
1864 1864 |
1865 1865 o 0:cb9a9f314b8b test
1866 1866 a
1867 1867
1868 1868 $ hg rebase -s 1 -d 5
1869 1869 rebasing 1:d2ae7f538514 "b"
1870 1870 rebasing 2:177f92b77385 "c"
1871 1871 rebasing 3:055a42cdd887 "d"
1872 1872 merging d
1873 1873 warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
1874 1874 unresolved conflicts (see hg resolve, then hg rebase --continue)
1875 1875 [1]
1876 1876 $ hg rebase --stop
1877 1877 1 new orphan changesets
1878 1878 $ hg log -G --template "{rev}:{short(node)} {person(author)}\n{firstline(desc)} {topic}\n\n"
1879 1879 o 7:7fffad344617 test
1880 1880 | c
1881 1881 |
1882 1882 o 6:b15528633407 test
1883 1883 | b
1884 1884 |
1885 1885 o 5:00bfc9898aeb test
1886 1886 | conflict with d
1887 1887 |
1888 1888 o 4:dafd40200f93 test
1889 1889 | f
1890 1890 |
1891 1891 | @ 3:055a42cdd887 test
1892 1892 | | d
1893 1893 | |
1894 1894 | x 2:177f92b77385 test
1895 1895 | | c
1896 1896 | |
1897 1897 | x 1:d2ae7f538514 test
1898 1898 |/ b
1899 1899 |
1900 1900 o 0:cb9a9f314b8b test
1901 1901 a
1902 1902
1903 1903 Test it aborts if unstable csets is not allowed:
1904 1904 ===============================================
1905 1905 $ cat >> $HGRCPATH << EOF
1906 1906 > [experimental]
1907 1907 > evolution.allowunstable=False
1908 1908 > EOF
1909 1909
1910 1910 $ hg strip 6 --no-backup -q
1911 1911 $ hg log -G --template "{rev}:{short(node)} {person(author)}\n{firstline(desc)} {topic}\n\n"
1912 1912 o 5:00bfc9898aeb test
1913 1913 | conflict with d
1914 1914 |
1915 1915 o 4:dafd40200f93 test
1916 1916 | f
1917 1917 |
1918 1918 | @ 3:055a42cdd887 test
1919 1919 | | d
1920 1920 | |
1921 1921 | o 2:177f92b77385 test
1922 1922 | | c
1923 1923 | |
1924 1924 | o 1:d2ae7f538514 test
1925 1925 |/ b
1926 1926 |
1927 1927 o 0:cb9a9f314b8b test
1928 1928 a
1929 1929
1930 1930 $ hg rebase -s 1 -d 5
1931 1931 rebasing 1:d2ae7f538514 "b"
1932 1932 rebasing 2:177f92b77385 "c"
1933 1933 rebasing 3:055a42cdd887 "d"
1934 1934 merging d
1935 1935 warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
1936 1936 unresolved conflicts (see hg resolve, then hg rebase --continue)
1937 1937 [1]
1938 1938 $ hg rebase --stop
1939 1939 abort: cannot remove original changesets with unrebased descendants
1940 1940 (either enable obsmarkers to allow unstable revisions or use --keep to keep original changesets)
1941 1941 [255]
1942 1942 $ hg rebase --abort
1943 1943 saved backup bundle to $TESTTMP/rbstop/.hg/strip-backup/b15528633407-6eb72b6f-backup.hg
1944 1944 rebase aborted
1945 1945
1946 1946 Test --stop when --keep is passed:
1947 1947 ==================================
1948 1948 $ hg rebase -s 1 -d 5 --keep
1949 1949 rebasing 1:d2ae7f538514 "b"
1950 1950 rebasing 2:177f92b77385 "c"
1951 1951 rebasing 3:055a42cdd887 "d"
1952 1952 merging d
1953 1953 warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
1954 1954 unresolved conflicts (see hg resolve, then hg rebase --continue)
1955 1955 [1]
1956 1956 $ hg rebase --stop
1957 1957 $ hg log -G --template "{rev}:{short(node)} {person(author)}\n{firstline(desc)} {topic}\n\n"
1958 1958 o 7:7fffad344617 test
1959 1959 | c
1960 1960 |
1961 1961 o 6:b15528633407 test
1962 1962 | b
1963 1963 |
1964 1964 o 5:00bfc9898aeb test
1965 1965 | conflict with d
1966 1966 |
1967 1967 o 4:dafd40200f93 test
1968 1968 | f
1969 1969 |
1970 1970 | @ 3:055a42cdd887 test
1971 1971 | | d
1972 1972 | |
1973 1973 | o 2:177f92b77385 test
1974 1974 | | c
1975 1975 | |
1976 1976 | o 1:d2ae7f538514 test
1977 1977 |/ b
1978 1978 |
1979 1979 o 0:cb9a9f314b8b test
1980 1980 a
1981 1981
1982 1982 Test --stop aborts when --collapse was passed:
1983 1983 =============================================
1984 1984 $ cat >> $HGRCPATH << EOF
1985 1985 > [experimental]
1986 1986 > evolution.allowunstable=True
1987 1987 > EOF
1988 1988
1989 1989 $ hg strip 6
1990 1990 saved backup bundle to $TESTTMP/rbstop/.hg/strip-backup/b15528633407-6eb72b6f-backup.hg
1991 1991 $ hg log -G --template "{rev}:{short(node)} {person(author)}\n{firstline(desc)} {topic}\n\n"
1992 1992 o 5:00bfc9898aeb test
1993 1993 | conflict with d
1994 1994 |
1995 1995 o 4:dafd40200f93 test
1996 1996 | f
1997 1997 |
1998 1998 | @ 3:055a42cdd887 test
1999 1999 | | d
2000 2000 | |
2001 2001 | o 2:177f92b77385 test
2002 2002 | | c
2003 2003 | |
2004 2004 | o 1:d2ae7f538514 test
2005 2005 |/ b
2006 2006 |
2007 2007 o 0:cb9a9f314b8b test
2008 2008 a
2009 2009
2010 2010 $ hg rebase -s 1 -d 5 --collapse -m "collapsed b c d"
2011 2011 rebasing 1:d2ae7f538514 "b"
2012 2012 rebasing 2:177f92b77385 "c"
2013 2013 rebasing 3:055a42cdd887 "d"
2014 2014 merging d
2015 2015 warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
2016 2016 unresolved conflicts (see hg resolve, then hg rebase --continue)
2017 2017 [1]
2018 2018 $ hg rebase --stop
2019 2019 abort: cannot stop in --collapse session
2020 2020 [255]
2021 2021 $ hg rebase --abort
2022 2022 rebase aborted
2023 2023 $ hg diff
2024 2024 $ hg log -G --template "{rev}:{short(node)} {person(author)}\n{firstline(desc)} {topic}\n\n"
2025 2025 o 5:00bfc9898aeb test
2026 2026 | conflict with d
2027 2027 |
2028 2028 o 4:dafd40200f93 test
2029 2029 | f
2030 2030 |
2031 2031 | @ 3:055a42cdd887 test
2032 2032 | | d
2033 2033 | |
2034 2034 | o 2:177f92b77385 test
2035 2035 | | c
2036 2036 | |
2037 2037 | o 1:d2ae7f538514 test
2038 2038 |/ b
2039 2039 |
2040 2040 o 0:cb9a9f314b8b test
2041 2041 a
2042 2042
2043 2043 Test --stop raise errors with conflicting options:
2044 2044 =================================================
2045 2045 $ hg rebase -s 3 -d 5
2046 2046 rebasing 3:055a42cdd887 "d"
2047 2047 merging d
2048 2048 warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
2049 2049 unresolved conflicts (see hg resolve, then hg rebase --continue)
2050 2050 [1]
2051 2051 $ hg rebase --stop --dry-run
2052 2052 abort: cannot specify both --dry-run and --stop
2053 2053 [255]
2054 2054
2055 2055 $ hg rebase -s 3 -d 5
2056 2056 abort: rebase in progress
2057 2057 (use 'hg rebase --continue' or 'hg rebase --abort')
2058 2058 [255]
2059 2059 $ hg rebase --stop --continue
2060 2060 abort: cannot use --stop with --continue
2061 2061 [255]
2062 2062
2063 2063 Test --stop moves bookmarks of original revisions to new rebased nodes:
2064 2064 ======================================================================
2065 2065 $ cd ..
2066 2066 $ hg init repo
2067 2067 $ cd repo
2068 2068
2069 2069 $ echo a > a
2070 2070 $ hg ci -Am A
2071 2071 adding a
2072 2072
2073 2073 $ echo b > b
2074 2074 $ hg ci -Am B
2075 2075 adding b
2076 2076 $ hg book X
2077 2077 $ hg book Y
2078 2078
2079 2079 $ echo c > c
2080 2080 $ hg ci -Am C
2081 2081 adding c
2082 2082 $ hg book Z
2083 2083
2084 2084 $ echo d > d
2085 2085 $ hg ci -Am D
2086 2086 adding d
2087 2087
2088 2088 $ hg up 0 -q
2089 2089 $ echo e > e
2090 2090 $ hg ci -Am E
2091 2091 adding e
2092 2092 created new head
2093 2093
2094 2094 $ echo doubt > d
2095 2095 $ hg ci -Am "conflict with d"
2096 2096 adding d
2097 2097
2098 2098 $ hg log -GT "{rev}: {node|short} '{desc}' bookmarks: {bookmarks}\n"
2099 2099 @ 5: 39adf30bc1be 'conflict with d' bookmarks:
2100 2100 |
2101 2101 o 4: 9c1e55f411b6 'E' bookmarks:
2102 2102 |
2103 2103 | o 3: 67a385d4e6f2 'D' bookmarks: Z
2104 2104 | |
2105 2105 | o 2: 49cb3485fa0c 'C' bookmarks: Y
2106 2106 | |
2107 2107 | o 1: 6c81ed0049f8 'B' bookmarks: X
2108 2108 |/
2109 2109 o 0: 1994f17a630e 'A' bookmarks:
2110 2110
2111 2111 $ hg rebase -s 1 -d 5
2112 2112 rebasing 1:6c81ed0049f8 "B" (X)
2113 2113 rebasing 2:49cb3485fa0c "C" (Y)
2114 2114 rebasing 3:67a385d4e6f2 "D" (Z)
2115 2115 merging d
2116 2116 warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
2117 2117 unresolved conflicts (see hg resolve, then hg rebase --continue)
2118 2118 [1]
2119 2119 $ hg rebase --stop
2120 2120 1 new orphan changesets
2121 2121 $ hg log -GT "{rev}: {node|short} '{desc}' bookmarks: {bookmarks}\n"
2122 2122 o 7: 9c86c650b686 'C' bookmarks: Y
2123 2123 |
2124 2124 o 6: 9b87b54e5fd8 'B' bookmarks: X
2125 2125 |
2126 2126 @ 5: 39adf30bc1be 'conflict with d' bookmarks:
2127 2127 |
2128 2128 o 4: 9c1e55f411b6 'E' bookmarks:
2129 2129 |
2130 2130 | * 3: 67a385d4e6f2 'D' bookmarks: Z
2131 2131 | |
2132 2132 | x 2: 49cb3485fa0c 'C' bookmarks:
2133 2133 | |
2134 2134 | x 1: 6c81ed0049f8 'B' bookmarks:
2135 2135 |/
2136 2136 o 0: 1994f17a630e 'A' bookmarks:
2137 2137
General Comments 0
You need to be logged in to leave comments. Login now