Show More
@@ -272,7 +272,7 b' def pick(ui, repo, ctx, ha, opts):' | |||||
272 | oldctx = repo[ha] |
|
272 | oldctx = repo[ha] | |
273 | if oldctx.parents()[0] == ctx: |
|
273 | if oldctx.parents()[0] == ctx: | |
274 | ui.debug('node %s unchanged\n' % ha) |
|
274 | ui.debug('node %s unchanged\n' % ha) | |
275 |
return oldctx, [] |
|
275 | return oldctx, [] | |
276 | hg.update(repo, ctx.node()) |
|
276 | hg.update(repo, ctx.node()) | |
277 | stats = applychanges(ui, repo, oldctx, opts) |
|
277 | stats = applychanges(ui, repo, oldctx, opts) | |
278 | if stats and stats[3] > 0: |
|
278 | if stats and stats[3] > 0: | |
@@ -284,8 +284,9 b' def pick(ui, repo, ctx, ha, opts):' | |||||
284 | if n is None: |
|
284 | if n is None: | |
285 | ui.warn(_('%s: empty changeset\n') |
|
285 | ui.warn(_('%s: empty changeset\n') | |
286 | % node.hex(ha)) |
|
286 | % node.hex(ha)) | |
287 |
return ctx, [] |
|
287 | return ctx, [] | |
288 | return repo[n], [n], [oldctx.node()], [] |
|
288 | new = repo[n] | |
|
289 | return new, [(oldctx.node(), (n,))] | |||
289 |
|
290 | |||
290 |
|
291 | |||
291 | def edit(ui, repo, ctx, ha, opts): |
|
292 | def edit(ui, repo, ctx, ha, opts): | |
@@ -308,7 +309,7 b' def fold(ui, repo, ctx, ha, opts):' | |||||
308 | if n is None: |
|
309 | if n is None: | |
309 | ui.warn(_('%s: empty changeset') |
|
310 | ui.warn(_('%s: empty changeset') | |
310 | % node.hex(ha)) |
|
311 | % node.hex(ha)) | |
311 |
return ctx, [] |
|
312 | return ctx, [] | |
312 | return finishfold(ui, repo, ctx, oldctx, n, opts, []) |
|
313 | return finishfold(ui, repo, ctx, oldctx, n, opts, []) | |
313 |
|
314 | |||
314 | def finishfold(ui, repo, ctx, oldctx, newnode, opts, internalchanges): |
|
315 | def finishfold(ui, repo, ctx, oldctx, newnode, opts, internalchanges): | |
@@ -332,12 +333,18 b' def finishfold(ui, repo, ctx, oldctx, ne' | |||||
332 | commitopts['date'] = max(ctx.date(), oldctx.date()) |
|
333 | commitopts['date'] = max(ctx.date(), oldctx.date()) | |
333 | n = collapse(repo, ctx, repo[newnode], commitopts) |
|
334 | n = collapse(repo, ctx, repo[newnode], commitopts) | |
334 | if n is None: |
|
335 | if n is None: | |
335 |
return ctx, [] |
|
336 | return ctx, [] | |
336 | hg.update(repo, n) |
|
337 | hg.update(repo, n) | |
337 | return repo[n], [n], [oldctx.node(), ctx.node()], [newnode] |
|
338 | replacements = [(oldctx.node(), (newnode,)), | |
|
339 | (ctx.node(), (n,)), | |||
|
340 | (newnode, (n,)), | |||
|
341 | ] | |||
|
342 | for ich in internalchanges: | |||
|
343 | replacements.append((ich, (n,))) | |||
|
344 | return repo[n], replacements | |||
338 |
|
345 | |||
339 | def drop(ui, repo, ctx, ha, opts): |
|
346 | def drop(ui, repo, ctx, ha, opts): | |
340 |
return ctx, [ |
|
347 | return ctx, [(repo[ha].node(), ())] | |
341 |
|
348 | |||
342 |
|
349 | |||
343 | def message(ui, repo, ctx, ha, opts): |
|
350 | def message(ui, repo, ctx, ha, opts): | |
@@ -353,9 +360,9 b' def message(ui, repo, ctx, ha, opts):' | |||||
353 | extra=oldctx.extra()) |
|
360 | extra=oldctx.extra()) | |
354 | newctx = repo[new] |
|
361 | newctx = repo[new] | |
355 | if oldctx.node() != newctx.node(): |
|
362 | if oldctx.node() != newctx.node(): | |
356 |
return newctx, [ |
|
363 | return newctx, [(oldctx.node(), (new,))] | |
357 | # We didn't make an edit, so just indicate no replaced nodes |
|
364 | # We didn't make an edit, so just indicate no replaced nodes | |
358 |
return newctx, [ |
|
365 | return newctx, [] | |
359 |
|
366 | |||
360 | actiontable = {'p': pick, |
|
367 | actiontable = {'p': pick, | |
361 | 'pick': pick, |
|
368 | 'pick': pick, | |
@@ -417,22 +424,20 b' def histedit(ui, repo, *parent, **opts):' | |||||
417 | if opts.get('continue', False): |
|
424 | if opts.get('continue', False): | |
418 | if len(parent) != 0: |
|
425 | if len(parent) != 0: | |
419 | raise util.Abort(_('no arguments allowed with --continue')) |
|
426 | raise util.Abort(_('no arguments allowed with --continue')) | |
420 |
(parentctxnode, |
|
427 | (parentctxnode, rules, keep, topmost, replacements) = readstate(repo) | |
421 | existing, rules, keep, topmost, replacemap) = readstate(repo) |
|
428 | currentparent, wantnull = repo.dirstate.parents() | |
422 | parentctx = repo[parentctxnode] |
|
429 | parentctx = repo[parentctxnode] | |
423 | existing = set(existing) |
|
430 | parentctx, repl = bootstrapcontinue(ui, repo, parentctx, rules, opts) | |
424 | parentctx = bootstrapcontinue(ui, repo, parentctx, existing, |
|
431 | replacements.extend(repl) | |
425 | replacemap, rules, tmpnodes, created, |
|
|||
426 | replaced, opts) |
|
|||
427 | elif opts.get('abort', False): |
|
432 | elif opts.get('abort', False): | |
428 | if len(parent) != 0: |
|
433 | if len(parent) != 0: | |
429 | raise util.Abort(_('no arguments allowed with --abort')) |
|
434 | raise util.Abort(_('no arguments allowed with --abort')) | |
430 |
(parentctxnode, |
|
435 | (parentctxnode, rules, keep, topmost, replacements) = readstate(repo) | |
431 | existing, rules, keep, topmost, replacemap) = readstate(repo) |
|
436 | mapping, tmpnodes, leafs, _ntm = processreplacement(repo, replacements) | |
432 | ui.debug('restore wc to old parent %s\n' % node.short(topmost)) |
|
437 | ui.debug('restore wc to old parent %s\n' % node.short(topmost)) | |
433 | hg.clean(repo, topmost) |
|
438 | hg.clean(repo, topmost) | |
434 |
cleanupnode(ui, repo, 'created', |
|
439 | cleanupnode(ui, repo, 'created', tmpnodes) | |
435 |
cleanupnode(ui, repo, 'temp', |
|
440 | cleanupnode(ui, repo, 'temp', leafs) | |
436 | os.unlink(os.path.join(repo.path, 'histedit-state')) |
|
441 | os.unlink(os.path.join(repo.path, 'histedit-state')) | |
437 | return |
|
442 | return | |
438 | else: |
|
443 | else: | |
@@ -443,7 +448,6 b' def histedit(ui, repo, *parent, **opts):' | |||||
443 |
|
448 | |||
444 | topmost, empty = repo.dirstate.parents() |
|
449 | topmost, empty = repo.dirstate.parents() | |
445 |
|
450 | |||
446 |
|
||||
447 | if len(parent) != 1: |
|
451 | if len(parent) != 1: | |
448 | raise util.Abort(_('histedit requires exactly one parent revision')) |
|
452 | raise util.Abort(_('histedit requires exactly one parent revision')) | |
449 | parent = scmutil.revsingle(repo, parent[0]).node() |
|
453 | parent = scmutil.revsingle(repo, parent[0]).node() | |
@@ -452,7 +456,6 b' def histedit(ui, repo, *parent, **opts):' | |||||
452 | revs = between(repo, parent, topmost, keep) |
|
456 | revs = between(repo, parent, topmost, keep) | |
453 |
|
457 | |||
454 | ctxs = [repo[r] for r in revs] |
|
458 | ctxs = [repo[r] for r in revs] | |
455 | existing = [r.node() for r in ctxs] |
|
|||
456 | rules = opts.get('commands', '') |
|
459 | rules = opts.get('commands', '') | |
457 | if not rules: |
|
460 | if not rules: | |
458 | rules = '\n'.join([makedesc(c) for c in ctxs]) |
|
461 | rules = '\n'.join([makedesc(c) for c in ctxs]) | |
@@ -475,72 +478,37 b' def histedit(ui, repo, *parent, **opts):' | |||||
475 |
|
478 | |||
476 | parentctx = repo[parent].parents()[0] |
|
479 | parentctx = repo[parent].parents()[0] | |
477 | keep = opts.get('keep', False) |
|
480 | keep = opts.get('keep', False) | |
478 |
replace |
|
481 | replacements = [] | |
479 | replacemap = {} |
|
|||
480 | tmpnodes = [] |
|
|||
481 | created = [] |
|
|||
482 |
|
482 | |||
483 |
|
483 | |||
484 | while rules: |
|
484 | while rules: | |
485 |
writestate(repo, parentctx.node(), |
|
485 | writestate(repo, parentctx.node(), rules, keep, topmost, replacements) | |
486 | tmpnodes, existing, rules, keep, topmost, replacemap) |
|
|||
487 | action, ha = rules.pop(0) |
|
486 | action, ha = rules.pop(0) | |
488 | ui.debug('histedit: processing %s %s\n' % (action, ha)) |
|
487 | ui.debug('histedit: processing %s %s\n' % (action, ha)) | |
489 |
|
|
488 | actfunc = actiontable[action] | |
490 |
|
|
489 | parentctx, replacement_ = actfunc(ui, repo, parentctx, ha, opts) | |
491 |
|
490 | replacements.extend(replacement_) | ||
492 | if replaced_: |
|
|||
493 | clen, rlen = len(created_), len(replaced_) |
|
|||
494 | if clen == rlen == 1: |
|
|||
495 | ui.debug('histedit: exact replacement of %s with %s\n' % ( |
|
|||
496 | node.short(replaced_[0]), node.short(created_[0]))) |
|
|||
497 |
|
||||
498 | replacemap[replaced_[0]] = created_[0] |
|
|||
499 | elif clen > rlen: |
|
|||
500 | assert rlen == 1, ('unexpected replacement of ' |
|
|||
501 | '%d changes with %d changes' % (rlen, clen)) |
|
|||
502 | # made more changesets than we're replacing |
|
|||
503 | # TODO synthesize patch names for created patches |
|
|||
504 | replacemap[replaced_[0]] = created_[-1] |
|
|||
505 | ui.debug('histedit: created many, assuming %s replaced by %s' % |
|
|||
506 | (node.short(replaced_[0]), node.short(created_[-1]))) |
|
|||
507 | elif rlen > clen: |
|
|||
508 | if not created_: |
|
|||
509 | # This must be a drop. Try and put our metadata on |
|
|||
510 | # the parent change. |
|
|||
511 | assert rlen == 1 |
|
|||
512 | r = replaced_[0] |
|
|||
513 | ui.debug('histedit: %s seems replaced with nothing, ' |
|
|||
514 | 'finding a parent\n' % (node.short(r))) |
|
|||
515 | pctx = repo[r].parents()[0] |
|
|||
516 | if pctx.node() in replacemap: |
|
|||
517 | ui.debug('histedit: parent is already replaced\n') |
|
|||
518 | replacemap[r] = replacemap[pctx.node()] |
|
|||
519 | else: |
|
|||
520 | replacemap[r] = pctx.node() |
|
|||
521 | ui.debug('histedit: %s best replaced by %s\n' % ( |
|
|||
522 | node.short(r), node.short(replacemap[r]))) |
|
|||
523 | else: |
|
|||
524 | assert len(created_) == 1 |
|
|||
525 | for r in replaced_: |
|
|||
526 | ui.debug('histedit: %s replaced by %s\n' % ( |
|
|||
527 | node.short(r), node.short(created_[0]))) |
|
|||
528 | replacemap[r] = created_[0] |
|
|||
529 | else: |
|
|||
530 | assert False, ( |
|
|||
531 | 'Unhandled case in replacement mapping! ' |
|
|||
532 | 'replacing %d changes with %d changes' % (rlen, clen)) |
|
|||
533 | created.extend(created_) |
|
|||
534 | replaced.extend(replaced_) |
|
|||
535 | tmpnodes.extend(tmpnodes_) |
|
|||
536 |
|
491 | |||
537 | hg.update(repo, parentctx.node()) |
|
492 | hg.update(repo, parentctx.node()) | |
538 |
|
493 | |||
|
494 | mapping, tmpnodes, created, ntm = processreplacement(repo, replacements) | |||
|
495 | if mapping: | |||
|
496 | for prec, succs in mapping.iteritems(): | |||
|
497 | if not succs: | |||
|
498 | ui.debug('histedit: %s is dropped\n' % node.short(prec)) | |||
|
499 | else: | |||
|
500 | ui.debug('histedit: %s is replaced by %s\n' % ( | |||
|
501 | node.short(prec), node.short(succs[0]))) | |||
|
502 | if len(succs) > 1: | |||
|
503 | m = 'histedit: %s' | |||
|
504 | for n in succs[1:]: | |||
|
505 | ui.debug(m % node.short(n)) | |||
|
506 | ||||
539 | if not keep: |
|
507 | if not keep: | |
540 |
if |
|
508 | if mapping: | |
541 |
movebookmarks(ui, repo, |
|
509 | movebookmarks(ui, repo, mapping, topmost, ntm) | |
542 | # TODO update mq state |
|
510 | # TODO update mq state | |
543 |
cleanupnode(ui, repo, 'replaced', |
|
511 | cleanupnode(ui, repo, 'replaced', mapping) | |
544 |
|
512 | |||
545 | cleanupnode(ui, repo, 'temp', tmpnodes) |
|
513 | cleanupnode(ui, repo, 'temp', tmpnodes) | |
546 | os.unlink(os.path.join(repo.path, 'histedit-state')) |
|
514 | os.unlink(os.path.join(repo.path, 'histedit-state')) | |
@@ -548,9 +516,9 b' def histedit(ui, repo, *parent, **opts):' | |||||
548 | os.unlink(repo.sjoin('undo')) |
|
516 | os.unlink(repo.sjoin('undo')) | |
549 |
|
517 | |||
550 |
|
518 | |||
551 |
def bootstrapcontinue(ui, repo, parentctx, e |
|
519 | def bootstrapcontinue(ui, repo, parentctx, rules, opts): | |
552 | tmpnodes, created, replaced, opts): |
|
|||
553 | action, currentnode = rules.pop(0) |
|
520 | action, currentnode = rules.pop(0) | |
|
521 | ctx = repo[currentnode] | |||
554 | # is there any new commit between the expected parent and "." |
|
522 | # is there any new commit between the expected parent and "." | |
555 | # |
|
523 | # | |
556 | # note: does not take non linear new change in account (but previous |
|
524 | # note: does not take non linear new change in account (but previous | |
@@ -564,45 +532,46 b' def bootstrapcontinue(ui, repo, parentct' | |||||
564 | '--continue" again') % parentctx |
|
532 | '--continue" again') % parentctx | |
565 | raise util.Abort(msg % parentctx, hint=hint) |
|
533 | raise util.Abort(msg % parentctx, hint=hint) | |
566 | newchildren.pop(0) # remove parentctxnode |
|
534 | newchildren.pop(0) # remove parentctxnode | |
567 | if action in ('f', 'fold'): |
|
535 | # Commit dirty working directory if necessary | |
568 | tmpnodes.extend(newchildren) |
|
536 | new = None | |
569 | else: |
|
|||
570 | created.extend(newchildren) |
|
|||
571 |
|
||||
572 | m, a, r, d = repo.status()[:4] |
|
537 | m, a, r, d = repo.status()[:4] | |
573 | oldctx = repo[currentnode] |
|
|||
574 | message = oldctx.description() + '\n' |
|
|||
575 | if action in ('e', 'edit', 'm', 'mess'): |
|
|||
576 | message = ui.edit(message, ui.username()) |
|
|||
577 | elif action in ('f', 'fold'): |
|
|||
578 | message = 'fold-temp-revision %s' % currentnode |
|
|||
579 | new = None |
|
|||
580 | if m or a or r or d: |
|
538 | if m or a or r or d: | |
581 | new = repo.commit(text=message, user=oldctx.user(), |
|
539 | # prepare the message for the commit to comes | |
582 | date=oldctx.date(), extra=oldctx.extra()) |
|
540 | if action in ('f', 'fold'): | |
|
541 | message = 'fold-temp-revision %s' % currentnode | |||
|
542 | else: | |||
|
543 | message = ctx.description() + '\n' | |||
|
544 | if action in ('e', 'edit', 'm', 'mess'): | |||
|
545 | editor = cmdutil.commitforceeditor | |||
|
546 | else: | |||
|
547 | editor = False | |||
|
548 | new = repo.commit(text=message, user=ctx.user(), | |||
|
549 | date=ctx.date(), extra=ctx.extra(), | |||
|
550 | editor=editor) | |||
|
551 | if new is not None: | |||
|
552 | newchildren.append(new) | |||
583 |
|
553 | |||
584 | # If we're resuming a fold and we have new changes, mark the |
|
554 | replacements = [] | |
585 | # replacements and finish the fold. If not, it's more like a |
|
555 | # track replacements | |
586 | # drop of the changesets that disappeared, and we can skip |
|
556 | if ctx.node() not in newchildren: | |
587 | # this step. |
|
557 | # note: new children may be empty when the changeset is dropped. | |
588 | if action in ('f', 'fold') and (new or newchildren): |
|
558 | # this happen e.g during conflicting pick where we revert content | |
589 | if new: |
|
559 | # to parent. | |
590 | tmpnodes.append(new) |
|
560 | replacements.append((ctx.node(), tuple(newchildren))) | |
591 | else: |
|
561 | ||
|
562 | if action in ('f', 'fold'): | |||
|
563 | # finalize fold operation if applicable | |||
|
564 | if new is None: | |||
592 | new = newchildren[-1] |
|
565 | new = newchildren[-1] | |
593 | (parentctx, created_, replaced_, tmpnodes_) = finishfold( |
|
566 | else: | |
594 | ui, repo, parentctx, oldctx, new, opts, newchildren) |
|
567 | newchildren.pop() # remove new from internal changes | |
595 | replaced.extend(replaced_) |
|
568 | parentctx, repl = finishfold(ui, repo, parentctx, ctx, new, opts, | |
596 | created.extend(created_) |
|
569 | newchildren) | |
597 | tmpnodes.extend(tmpnodes_) |
|
570 | replacements.extend(repl) | |
598 | elif action not in ('d', 'drop'): |
|
571 | elif newchildren: | |
599 | if new != oldctx.node(): |
|
572 | # otherwize update "parentctx" before proceding to further operation | |
600 | replaced.append(oldctx.node()) |
|
573 | parentctx = repo[newchildren[-1]] | |
601 | if new: |
|
574 | return parentctx, replacements | |
602 | if new != oldctx.node(): |
|
|||
603 | created.append(new) |
|
|||
604 | parentctx = repo[new] |
|
|||
605 | return parentctx |
|
|||
606 |
|
575 | |||
607 |
|
576 | |||
608 | def between(repo, old, new, keep): |
|
577 | def between(repo, old, new, keep): | |
@@ -627,17 +596,13 b' def between(repo, old, new, keep):' | |||||
627 | return revs |
|
596 | return revs | |
628 |
|
597 | |||
629 |
|
598 | |||
630 |
def writestate(repo, parent |
|
599 | def writestate(repo, parentnode, rules, keep, topmost, replacements): | |
631 | tmpnodes, existing, rules, keep, topmost, replacemap): |
|
|||
632 | fp = open(os.path.join(repo.path, 'histedit-state'), 'w') |
|
600 | fp = open(os.path.join(repo.path, 'histedit-state'), 'w') | |
633 |
pickle.dump((parent |
|
601 | pickle.dump((parentnode, rules, keep, topmost, replacements), fp) | |
634 | tmpnodes, existing, rules, keep, topmost, replacemap), |
|
|||
635 | fp) |
|
|||
636 | fp.close() |
|
602 | fp.close() | |
637 |
|
603 | |||
638 | def readstate(repo): |
|
604 | def readstate(repo): | |
639 |
"""Returns a tuple of (parentnode, |
|
605 | """Returns a tuple of (parentnode, rules, keep, topmost, replacements). | |
640 | keep, topmost, replacemap ). |
|
|||
641 | """ |
|
606 | """ | |
642 | fp = open(os.path.join(repo.path, 'histedit-state')) |
|
607 | fp = open(os.path.join(repo.path, 'histedit-state')) | |
643 | return pickle.load(fp) |
|
608 | return pickle.load(fp) | |
@@ -684,37 +649,97 b' def verifyrules(rules, repo, ctxs):' | |||||
684 | parsed.append([action, ha]) |
|
649 | parsed.append([action, ha]) | |
685 | return parsed |
|
650 | return parsed | |
686 |
|
651 | |||
687 | def movebookmarks(ui, repo, replacemap, tmpnodes, created): |
|
652 | def processreplacement(repo, replacements): | |
688 | """Move bookmark from old to newly created node""" |
|
653 | """process the list of replacements to return | |
689 | ui.note(_('histedit: Should update metadata for the following ' |
|
654 | ||
690 | 'changes:\n')) |
|
655 | 1) the final mapping between original and created nodes | |
|
656 | 2) the list of temporary node created by histedit | |||
|
657 | 3) the list of new commit created by histedit""" | |||
|
658 | allsuccs = set() | |||
|
659 | replaced = set() | |||
|
660 | fullmapping = {} | |||
|
661 | # initialise basic set | |||
|
662 | # fullmapping record all operation recorded in replacement | |||
|
663 | for rep in replacements: | |||
|
664 | allsuccs.update(rep[1]) | |||
|
665 | replaced.add(rep[0]) | |||
|
666 | fullmapping.setdefault(rep[0], set()).update(rep[1]) | |||
|
667 | new = allsuccs - replaced | |||
|
668 | tmpnodes = allsuccs & replaced | |||
|
669 | # Reduce content fullmapping into direct relation between original nodes | |||
|
670 | # and final node created during history edition | |||
|
671 | # Dropped changeset are replaced by an empty list | |||
|
672 | toproceed = set(fullmapping) | |||
|
673 | final = {} | |||
|
674 | while toproceed: | |||
|
675 | for x in list(toproceed): | |||
|
676 | succs = fullmapping[x] | |||
|
677 | for s in list(succs): | |||
|
678 | if s in toproceed: | |||
|
679 | # non final node with unknown closure | |||
|
680 | # We can't process this now | |||
|
681 | break | |||
|
682 | elif s in final: | |||
|
683 | # non final node, replace with closure | |||
|
684 | succs.remove(s) | |||
|
685 | succs.update(final[s]) | |||
|
686 | else: | |||
|
687 | final[x] = succs | |||
|
688 | toproceed.remove(x) | |||
|
689 | # remove tmpnodes from final mapping | |||
|
690 | for n in tmpnodes: | |||
|
691 | del final[n] | |||
|
692 | # we expect all changes involved in final to exist in the repo | |||
|
693 | # turn `final` into list (topologically sorted) | |||
|
694 | nm = repo.changelog.nodemap | |||
|
695 | for prec, succs in final.items(): | |||
|
696 | final[prec] = sorted(succs, key=nm.get) | |||
691 |
|
697 | |||
692 | def copybms(old, new): |
|
698 | # computed topmost element (necessary for bookmark) | |
693 | if old in tmpnodes or old in created: |
|
699 | if new: | |
694 | # can't have any metadata we'd want to update |
|
700 | newtopmost = max(new, key=repo.changelog.rev) | |
695 | return |
|
701 | elif not final: | |
696 | while new in replacemap: |
|
702 | # Nothing rewritten at all. we won't need `newtopmost` | |
697 | new = replacemap[new] |
|
703 | # It is the same as `oldtopmost` and `processreplacement` know it | |
698 | octx = repo[old] |
|
704 | newtopmost = None | |
699 | marks = octx.bookmarks() |
|
705 | else: | |
700 | if marks: |
|
706 | # every body died. The newtopmost is the parent of the root. | |
701 | for mark in marks: |
|
707 | newtopmost = repo[min(final, key=repo.changelog.rev)].p1().node() | |
702 | ui.note(_('histedit: moving bookmarks %s from %s to %s\n') |
|
708 | ||
703 | % (mark, octx, node.short(new))) |
|
709 | return final, tmpnodes, new, newtopmost | |
704 | repo._bookmarks[mark] = new |
|
|||
705 | bookmarks.write(repo) |
|
|||
706 |
|
710 | |||
707 | # We assume that bookmarks on the tip should remain |
|
711 | def movebookmarks(ui, repo, mapping, oldtopmost, newtopmost): | |
708 | # tipmost, but bookmarks on non-tip changesets should go |
|
712 | """Move bookmark from old to newly created node""" | |
709 | # to their most reasonable successor. As a result, find |
|
713 | if not mapping: | |
710 | # the old tip and new tip and copy those bookmarks first, |
|
714 | # if nothing got rewritten there is not purpose for this function | |
711 | # then do the rest of the bookmark copies. |
|
715 | return | |
712 | oldtip = sorted(replacemap.keys(), key=repo.changelog.rev)[-1] |
|
716 | moves = [] | |
713 | newtip = sorted(replacemap.values(), key=repo.changelog.rev)[-1] |
|
717 | for bk, old in repo._bookmarks.iteritems(): | |
714 | copybms(oldtip, newtip) |
|
718 | if old == oldtopmost: | |
715 |
|
719 | # special case ensure bookmark stay on tip. | ||
716 | for old, new in sorted(replacemap.iteritems()): |
|
720 | # | |
717 | copybms(old, new) |
|
721 | # This is arguably a feature and we may only want that for the | |
|
722 | # active bookmark. But the behavior is kept compatible with the old | |||
|
723 | # version for now. | |||
|
724 | moves.append((bk, newtopmost)) | |||
|
725 | continue | |||
|
726 | base = old | |||
|
727 | new = mapping.get(base, None) | |||
|
728 | if new is None: | |||
|
729 | continue | |||
|
730 | while not new: | |||
|
731 | # base is killed, trying with parent | |||
|
732 | base = repo[base].p1().node() | |||
|
733 | new = mapping.get(base, (base,)) | |||
|
734 | # nothing to move | |||
|
735 | moves.append((bk, new[-1])) | |||
|
736 | if moves: | |||
|
737 | for mark, new in moves: | |||
|
738 | old = repo._bookmarks[mark] | |||
|
739 | ui.note(_('histedit: moving bookmarks %s from %s to %s\n') | |||
|
740 | % (mark, node.short(old), node.short(new))) | |||
|
741 | repo._bookmarks[mark] = new | |||
|
742 | bookmarks.write(repo) | |||
718 |
|
743 | |||
719 | def cleanupnode(ui, repo, name, nodes): |
|
744 | def cleanupnode(ui, repo, name, nodes): | |
720 | """strip a group of nodes from the repository |
|
745 | """strip a group of nodes from the repository | |
@@ -737,4 +762,3 b' def cleanupnode(ui, repo, name, nodes):' | |||||
737 | repair.strip(ui, repo, c) |
|
762 | repair.strip(ui, repo, c) | |
738 | finally: |
|
763 | finally: | |
739 | lockmod.release(lock) |
|
764 | lockmod.release(lock) | |
740 |
|
@@ -84,13 +84,12 b'' | |||||
84 | > pick 652413bf663e 5 f |
|
84 | > pick 652413bf663e 5 f | |
85 | > EOF |
|
85 | > EOF | |
86 | $ hg histedit 1 --commands commands.txt --verbose | grep histedit |
|
86 | $ hg histedit 1 --commands commands.txt --verbose | grep histedit | |
87 | histedit: Should update metadata for the following changes: |
|
87 | histedit: moving bookmarks two from 177f92b77385 to d36c0562f908 | |
88 | histedit: moving bookmarks three from 055a42cdd887 to ae467701c500 |
|
88 | histedit: moving bookmarks three from 055a42cdd887 to ae467701c500 | |
|
89 | histedit: moving bookmarks four from e860deea161a to ae467701c500 | |||
89 | histedit: moving bookmarks also-two from 177f92b77385 to d36c0562f908 |
|
90 | histedit: moving bookmarks also-two from 177f92b77385 to d36c0562f908 | |
90 |
histedit: moving bookmarks |
|
91 | histedit: moving bookmarks will-move-backwards from d2ae7f538514 to cb9a9f314b8b | |
91 | histedit: moving bookmarks five from 652413bf663e to 0efacef7cb48 |
|
92 | histedit: moving bookmarks five from 652413bf663e to 0efacef7cb48 | |
92 | histedit: moving bookmarks will-move-backwards from d2ae7f538514 to cb9a9f314b8b |
|
|||
93 | histedit: moving bookmarks four from e860deea161a to ae467701c500 |
|
|||
94 | saved backup bundle to $TESTTMP/r/.hg/strip-backup/d2ae7f538514-backup.hg (glob) |
|
93 | saved backup bundle to $TESTTMP/r/.hg/strip-backup/d2ae7f538514-backup.hg (glob) | |
95 | saved backup bundle to $TESTTMP/r/.hg/strip-backup/34a9919932c1-backup.hg (glob) |
|
94 | saved backup bundle to $TESTTMP/r/.hg/strip-backup/34a9919932c1-backup.hg (glob) | |
96 | $ hg log --graph |
|
95 | $ hg log --graph | |
@@ -142,10 +141,9 b'' | |||||
142 | > pick ae467701c500 2 d |
|
141 | > pick ae467701c500 2 d | |
143 | > EOF |
|
142 | > EOF | |
144 | $ hg histedit 1 --commands commands.txt --verbose | grep histedit |
|
143 | $ hg histedit 1 --commands commands.txt --verbose | grep histedit | |
145 | histedit: Should update metadata for the following changes: |
|
144 | histedit: moving bookmarks three from ae467701c500 to 1be9c35b4cb2 | |
|
145 | histedit: moving bookmarks four from ae467701c500 to 1be9c35b4cb2 | |||
146 | histedit: moving bookmarks five from 0efacef7cb48 to 1be9c35b4cb2 |
|
146 | histedit: moving bookmarks five from 0efacef7cb48 to 1be9c35b4cb2 | |
147 | histedit: moving bookmarks four from ae467701c500 to 1be9c35b4cb2 |
|
|||
148 | histedit: moving bookmarks three from ae467701c500 to 1be9c35b4cb2 |
|
|||
149 | saved backup bundle to $TESTTMP/r/.hg/strip-backup/ae467701c500-backup.hg (glob) |
|
147 | saved backup bundle to $TESTTMP/r/.hg/strip-backup/ae467701c500-backup.hg (glob) | |
150 |
|
148 | |||
151 | We expect 'five' to stay at tip, since the tipmost bookmark is most |
|
149 | We expect 'five' to stay at tip, since the tipmost bookmark is most |
General Comments 0
You need to be logged in to leave comments.
Login now