803 """Return (nodeid, created) where nodeid is the identifier of the |
804 """Return (nodeid, created) where nodeid is the identifier of the |
804 changeset generated by the rewrite process, and created is True if |
805 changeset generated by the rewrite process, and created is True if |
805 nodeid was actually created. If created is False, nodeid |
806 nodeid was actually created. If created is False, nodeid |
806 references a changeset existing before the rewrite call. |
807 references a changeset existing before the rewrite call. |
807 """ |
808 """ |
808 if len(old.parents()) > 1: #XXX remove this unecessary limitation. |
809 wlock = lock = tr = None |
809 raise error.Abort(_('cannot amend merge changesets')) |
810 try: |
810 base = old.p1() |
811 wlock = repo.wlock() |
811 updatebookmarks = _bookmarksupdater(repo, old.node()) |
812 lock = repo.lock() |
812 |
813 tr = repo.transaction('rewrite') |
813 # commit a new version of the old changeset, including the update |
814 if len(old.parents()) > 1: #XXX remove this unecessary limitation. |
814 # collect all files which might be affected |
815 raise error.Abort(_('cannot amend merge changesets')) |
815 files = set(old.files()) |
816 base = old.p1() |
816 for u in updates: |
817 updatebookmarks = _bookmarksupdater(repo, old.node(), tr) |
817 files.update(u.files()) |
818 |
818 |
819 # commit a new version of the old changeset, including the update |
819 # Recompute copies (avoid recording a -> b -> a) |
820 # collect all files which might be affected |
820 copied = copies.pathcopies(base, head) |
821 files = set(old.files()) |
821 |
822 for u in updates: |
822 |
823 files.update(u.files()) |
823 # prune files which were reverted by the updates |
824 |
824 def samefile(f): |
825 # Recompute copies (avoid recording a -> b -> a) |
825 if f in head.manifest(): |
826 copied = copies.pathcopies(base, head) |
826 a = head.filectx(f) |
827 |
827 if f in base.manifest(): |
828 |
828 b = base.filectx(f) |
829 # prune files which were reverted by the updates |
829 return (a.data() == b.data() |
830 def samefile(f): |
830 and a.flags() == b.flags()) |
831 if f in head.manifest(): |
|
832 a = head.filectx(f) |
|
833 if f in base.manifest(): |
|
834 b = base.filectx(f) |
|
835 return (a.data() == b.data() |
|
836 and a.flags() == b.flags()) |
|
837 else: |
|
838 return False |
831 else: |
839 else: |
832 return False |
840 return f not in base.manifest() |
833 else: |
841 files = [f for f in files if not samefile(f)] |
834 return f not in base.manifest() |
842 # commit version of these files as defined by head |
835 files = [f for f in files if not samefile(f)] |
843 headmf = head.manifest() |
836 # commit version of these files as defined by head |
844 def filectxfn(repo, ctx, path): |
837 headmf = head.manifest() |
845 if path in headmf: |
838 def filectxfn(repo, ctx, path): |
846 fctx = head[path] |
839 if path in headmf: |
847 flags = fctx.flags() |
840 fctx = head[path] |
848 mctx = memfilectx(repo, fctx.path(), fctx.data(), |
841 flags = fctx.flags() |
849 islink='l' in flags, |
842 mctx = memfilectx(repo, fctx.path(), fctx.data(), |
850 isexec='x' in flags, |
843 islink='l' in flags, |
851 copied=copied.get(path)) |
844 isexec='x' in flags, |
852 return mctx |
845 copied=copied.get(path)) |
853 return None |
846 return mctx |
854 |
847 return None |
855 message = cmdutil.logmessage(repo.ui, commitopts) |
848 |
856 if not message: |
849 message = cmdutil.logmessage(repo.ui, commitopts) |
857 message = old.description() |
850 if not message: |
858 |
851 message = old.description() |
859 user = commitopts.get('user') or old.user() |
852 |
860 date = commitopts.get('date') or None # old.date() |
853 user = commitopts.get('user') or old.user() |
861 extra = dict(commitopts.get('extra', old.extra())) |
854 date = commitopts.get('date') or None # old.date() |
862 extra['branch'] = head.branch() |
855 extra = dict(commitopts.get('extra', {})) |
863 |
856 extra['branch'] = head.branch() |
864 new = context.memctx(repo, |
857 |
865 parents=newbases, |
858 new = context.memctx(repo, |
866 text=message, |
859 parents=newbases, |
867 files=files, |
860 text=message, |
868 filectxfn=filectxfn, |
861 files=files, |
869 user=user, |
862 filectxfn=filectxfn, |
870 date=date, |
863 user=user, |
871 extra=extra) |
864 date=date, |
872 |
865 extra=extra) |
873 if commitopts.get('edit'): |
866 |
874 new._text = cmdutil.commitforceeditor(repo, new, []) |
867 if commitopts.get('edit'): |
875 revcount = len(repo) |
868 new._text = cmdutil.commitforceeditor(repo, new, []) |
876 newid = repo.commitctx(new) |
869 revcount = len(repo) |
877 new = repo[newid] |
870 newid = repo.commitctx(new) |
878 created = len(repo) != revcount |
871 new = repo[newid] |
879 updatebookmarks(newid) |
872 created = len(repo) != revcount |
880 |
873 updatebookmarks(newid) |
881 tr.close() |
874 |
882 return newid, created |
875 return newid, created |
883 finally: |
|
884 lockmod.release(lock, wlock, tr) |
876 |
885 |
877 class MergeFailure(util.Abort): |
886 class MergeFailure(util.Abort): |
878 pass |
887 pass |
879 |
888 |
880 def relocate(repo, orig, dest, keepbranch=False): |
889 def relocate(repo, orig, dest, keepbranch=False): |
933 if r[-1]: #some conflict |
942 if r[-1]: #some conflict |
934 raise util.Abort( |
943 raise util.Abort( |
935 'unresolved merge conflicts (see hg help resolve)') |
944 'unresolved merge conflicts (see hg help resolve)') |
936 if commitmsg is None: |
945 if commitmsg is None: |
937 commitmsg = orig.description() |
946 commitmsg = orig.description() |
938 extra = {'rebase_source': orig.hex()} |
947 extra = dict(orig.extra()) |
|
948 if 'branch' in extra: |
|
949 del extra['branch'] |
|
950 extra['rebase_source'] = orig.hex() |
939 |
951 |
940 backup = repo.ui.backupconfig('phases', 'new-commit') |
952 backup = repo.ui.backupconfig('phases', 'new-commit') |
941 try: |
953 try: |
942 targetphase = max(orig.phase(), phases.draft) |
954 targetphase = max(orig.phase(), phases.draft) |
943 repo.ui.setconfig('phases', 'new-commit', targetphase, 'rebase') |
955 repo.ui.setconfig('phases', 'new-commit', targetphase, 'rebase') |
947 finally: |
959 finally: |
948 repo.ui.restoreconfig(backup) |
960 repo.ui.restoreconfig(backup) |
949 except util.Abort, exc: |
961 except util.Abort, exc: |
950 repo.dirstate.beginparentchange() |
962 repo.dirstate.beginparentchange() |
951 repo.setparents(repo['.'].node(), nullid) |
963 repo.setparents(repo['.'].node(), nullid) |
952 repo.dirstate.write() |
964 writedirstate(repo.dirstate, tr) |
953 # fix up dirstate for copies and renames |
965 # fix up dirstate for copies and renames |
954 copies.duplicatecopies(repo, dest.rev(), orig.p1().rev()) |
966 copies.duplicatecopies(repo, dest.rev(), orig.p1().rev()) |
955 repo.dirstate.endparentchange() |
967 repo.dirstate.endparentchange() |
956 class LocalMergeFailure(MergeFailure, exc.__class__): |
968 class LocalMergeFailure(MergeFailure, exc.__class__): |
957 pass |
969 pass |
969 for book in oldbookmarks: |
981 for book in oldbookmarks: |
970 repo._bookmarks[book] = dest.node() |
982 repo._bookmarks[book] = dest.node() |
971 for book in destbookmarks: # restore bookmark that rebase move |
983 for book in destbookmarks: # restore bookmark that rebase move |
972 repo._bookmarks[book] = dest.node() |
984 repo._bookmarks[book] = dest.node() |
973 if oldbookmarks or destbookmarks: |
985 if oldbookmarks or destbookmarks: |
974 repo._bookmarks.write() |
986 repo._bookmarks.recordchange(tr) |
975 tr.close() |
987 tr.close() |
976 finally: |
988 finally: |
977 tr.release() |
989 tr.release() |
978 return nodenew |
990 return nodenew |
979 |
991 |
980 def _bookmarksupdater(repo, oldid): |
992 def _bookmarksupdater(repo, oldid, tr): |
981 """Return a callable update(newid) updating the current bookmark |
993 """Return a callable update(newid) updating the current bookmark |
982 and bookmarks bound to oldid to newid. |
994 and bookmarks bound to oldid to newid. |
983 """ |
995 """ |
984 def updatebookmarks(newid): |
996 def updatebookmarks(newid): |
985 dirty = False |
997 dirty = False |
1498 ordering.extend(sorted(dependencies)) |
1519 ordering.extend(sorted(dependencies)) |
1499 return ordering |
1520 return ordering |
1500 |
1521 |
1501 @command('^evolve|stabilize|solve', |
1522 @command('^evolve|stabilize|solve', |
1502 [('n', 'dry-run', False, |
1523 [('n', 'dry-run', False, |
1503 'do not perform actions, just print what would be done'), |
1524 _('do not perform actions, just print what would be done')), |
1504 ('', 'confirm', False, |
1525 ('', 'confirm', False, |
1505 'ask for confirmation before performing the action'), |
1526 _('ask for confirmation before performing the action')), |
1506 ('A', 'any', False, 'also consider troubled changesets unrelated to current working directory'), |
1527 ('A', 'any', False, _('also consider troubled changesets unrelated to current working directory')), |
1507 ('r', 'rev', [], 'solves troubles of these revisions'), |
1528 ('r', 'rev', [], _('solves troubles of these revisions')), |
1508 ('', 'bumped', False, 'solves only bumped changesets'), |
1529 ('', 'bumped', False, _('solves only bumped changesets')), |
1509 ('', 'divergent', False, 'solves only divergent changesets'), |
1530 ('', 'divergent', False, _('solves only divergent changesets')), |
1510 ('', 'unstable', False, 'solves only unstable changesets (default)'), |
1531 ('', 'unstable', False, _('solves only unstable changesets (default)')), |
1511 ('a', 'all', False, 'evolve all troubled changesets related to the current ' |
1532 ('a', 'all', False, _('evolve all troubled changesets related to the current ' |
1512 'working directory and its descendants'), |
1533 'working directory and its descendants')), |
1513 ('c', 'continue', False, 'continue an interrupted evolution'), |
1534 ('c', 'continue', False, _('continue an interrupted evolution')), |
1514 ] + mergetoolopts, |
1535 ] + mergetoolopts, |
1515 _('[OPTIONS]...')) |
1536 _('[OPTIONS]...')) |
1516 def evolve(ui, repo, **opts): |
1537 def evolve(ui, repo, **opts): |
1517 """solve troubles in your repository |
1538 """solve troubles in your repository |
1518 |
1539 |
1704 " trying to stabilize on its parent\n" % |
1725 " trying to stabilize on its parent\n" % |
1705 obs) |
1726 obs) |
1706 obs = obs.parents()[0] |
1727 obs = obs.parents()[0] |
1707 newer = obsolete.successorssets(repo, obs.node()) |
1728 newer = obsolete.successorssets(repo, obs.node()) |
1708 if len(newer) > 1: |
1729 if len(newer) > 1: |
1709 msg = _("skipping %s: divergent rewriting. can't choose destination\n" % obs) |
1730 msg = _("skipping %s: divergent rewriting. can't choose destination\n") % obs |
1710 ui.write_err(msg) |
1731 ui.write_err(msg) |
1711 return 2 |
1732 return 2 |
1712 targets = newer[0] |
1733 targets = newer[0] |
1713 assert targets |
1734 assert targets |
1714 if len(targets) > 1: |
1735 if len(targets) > 1: |
1715 msg = _("does not handle split parents yet\n") |
1736 # split target, figure out which one to pick, are they all in line? |
1716 ui.write_err(msg) |
1737 targetrevs = [repo[r].rev() for r in targets] |
1717 return 2 |
1738 roots = repo.revs('roots(%ld)', targetrevs) |
1718 target = targets[0] |
1739 heads = repo.revs('heads(%ld)', targetrevs) |
|
1740 if len(roots) > 1 or len(heads) > 1: |
|
1741 msg = "cannot solve split accross two branches\n" |
|
1742 ui.write_err(msg) |
|
1743 return 2 |
|
1744 target = repo[heads.first()] |
|
1745 else: |
|
1746 target = targets[0] |
1719 displayer = cmdutil.show_changeset(ui, repo, {'template': shorttemplate}) |
1747 displayer = cmdutil.show_changeset(ui, repo, {'template': shorttemplate}) |
1720 target = repo[target] |
1748 target = repo[target] |
1721 if not ui.quiet or confirm: |
1749 if not ui.quiet or confirm: |
1722 repo.ui.write(_('move:')) |
1750 repo.ui.write(_('move:')) |
1723 displayer.show(orig) |
1751 displayer.show(orig) |
1748 """Stabilize a bumped changeset""" |
1776 """Stabilize a bumped changeset""" |
1749 repo = repo.unfiltered() |
1777 repo = repo.unfiltered() |
1750 bumped = repo[bumped.rev()] |
1778 bumped = repo[bumped.rev()] |
1751 # For now we deny bumped merge |
1779 # For now we deny bumped merge |
1752 if len(bumped.parents()) > 1: |
1780 if len(bumped.parents()) > 1: |
1753 msg = _('skipping %s : we do not handle merge yet\n' % bumped) |
1781 msg = _('skipping %s : we do not handle merge yet\n') % bumped |
1754 ui.write_err(msg) |
1782 ui.write_err(msg) |
1755 return 2 |
1783 return 2 |
1756 prec = repo.set('last(allprecursors(%d) and public())', bumped).next() |
1784 prec = repo.set('last(allprecursors(%d) and public())', bumped).next() |
1757 # For now we deny target merge |
1785 # For now we deny target merge |
1758 if len(prec.parents()) > 1: |
1786 if len(prec.parents()) > 1: |
1759 msg = _('skipping: %s: public version is a merge, this not handled yet\n' % prec) |
1787 msg = _('skipping: %s: public version is a merge, this not handled yet\n') % prec |
1760 ui.write_err(msg) |
1788 ui.write_err(msg) |
1761 return 2 |
1789 return 2 |
1762 |
1790 |
1763 displayer = cmdutil.show_changeset(ui, repo, {'template': shorttemplate}) |
1791 displayer = cmdutil.show_changeset(ui, repo, {'template': shorttemplate}) |
1764 if not ui.quiet or confirm: |
1792 if not ui.quiet or confirm: |
1776 repo.ui.write('hg commit --msg "bumped update to %s"') |
1804 repo.ui.write('hg commit --msg "bumped update to %s"') |
1777 return 0 |
1805 return 0 |
1778 if progresscb: progresscb() |
1806 if progresscb: progresscb() |
1779 newid = tmpctx = None |
1807 newid = tmpctx = None |
1780 tmpctx = bumped |
1808 tmpctx = bumped |
1781 bmupdate = _bookmarksupdater(repo, bumped.node()) |
|
1782 # Basic check for common parent. Far too complicated and fragile |
1809 # Basic check for common parent. Far too complicated and fragile |
1783 tr = repo.transaction('bumped-stabilize') |
1810 tr = repo.transaction('bumped-stabilize') |
|
1811 bmupdate = _bookmarksupdater(repo, bumped.node(), tr) |
1784 try: |
1812 try: |
1785 if not list(repo.set('parents(%d) and parents(%d)', bumped, prec)): |
1813 if not list(repo.set('parents(%d) and parents(%d)', bumped, prec)): |
1786 # Need to rebase the changeset at the right place |
1814 # Need to rebase the changeset at the right place |
1787 repo.ui.status( |
1815 repo.ui.status( |
1788 _('rebasing to destination parent: %s\n') % prec.p1()) |
1816 _('rebasing to destination parent: %s\n') % prec.p1()) |
1902 displayer.show(divergent) |
1930 displayer.show(divergent) |
1903 ui.write(_('with: ')) |
1931 ui.write(_('with: ')) |
1904 displayer.show(other) |
1932 displayer.show(other) |
1905 ui.write(_('base: ')) |
1933 ui.write(_('base: ')) |
1906 displayer.show(base) |
1934 displayer.show(base) |
1907 if confirm and ui.prompt('perform evolve? [Ny]', 'n') != 'y': |
1935 if confirm and ui.prompt(_('perform evolve? [Ny]'), 'n') != 'y': |
1908 raise util.Abort(_('evolve aborted by user')) |
1936 raise util.Abort(_('evolve aborted by user')) |
1909 if dryrun: |
1937 if dryrun: |
1910 ui.write('hg update -c %s &&\n' % divergent) |
1938 ui.write('hg update -c %s &&\n' % divergent) |
1911 ui.write('hg merge %s &&\n' % other) |
1939 ui.write('hg merge %s &&\n' % other) |
1912 ui.write('hg commit -m "auto merge resolving conflict between ' |
1940 ui.write('hg commit -m "auto merge resolving conflict between ' |
1982 |
2010 |
1983 shorttemplate = '[{rev}] {desc|firstline}\n' |
2011 shorttemplate = '[{rev}] {desc|firstline}\n' |
1984 |
2012 |
1985 @command('^previous', |
2013 @command('^previous', |
1986 [('B', 'move-bookmark', False, |
2014 [('B', 'move-bookmark', False, |
1987 _('Move active bookmark after update')), |
2015 _('move active bookmark after update')), |
1988 ('', 'merge', False, _('bring uncommited change along'))], |
2016 ('', 'merge', False, _('bring uncommitted change along')), |
1989 '[-B]') |
2017 ('n', 'dry-run', False, _('do not perform actions, just print what would be done'))], |
|
2018 '[OPTION]...') |
1990 def cmdprevious(ui, repo, **opts): |
2019 def cmdprevious(ui, repo, **opts): |
1991 """update to parent and display summary lines""" |
2020 """update to parent and display summary lines""" |
1992 wkctx = repo[None] |
2021 wkctx = repo[None] |
1993 wparents = wkctx.parents() |
2022 wparents = wkctx.parents() |
|
2023 dryrunopt = opts['dry_run'] |
1994 if len(wparents) != 1: |
2024 if len(wparents) != 1: |
1995 raise util.Abort('merge in progress') |
2025 raise util.Abort('merge in progress') |
1996 if not opts['merge']: |
2026 if not opts['merge']: |
1997 try: |
2027 try: |
1998 cmdutil.bailifchanged(repo) |
2028 cmdutil.bailifchanged(repo) |
2004 displayer = cmdutil.show_changeset(ui, repo, {'template': shorttemplate}) |
2034 displayer = cmdutil.show_changeset(ui, repo, {'template': shorttemplate}) |
2005 if len(parents) == 1: |
2035 if len(parents) == 1: |
2006 p = parents[0] |
2036 p = parents[0] |
2007 bm = bmactive(repo) |
2037 bm = bmactive(repo) |
2008 shouldmove = opts.get('move_bookmark') and bm is not None |
2038 shouldmove = opts.get('move_bookmark') and bm is not None |
2009 ret = hg.update(repo, p.rev()) |
2039 if dryrunopt: |
2010 if not ret: |
2040 ui.write('hg update %s;\n' % p.rev()) |
2011 wlock = repo.wlock() |
2041 if shouldmove: |
2012 try: |
2042 ui.write('hg bookmark %s -r %s;\n' % (bm, p.rev())) |
2013 if shouldmove: |
2043 else: |
2014 repo._bookmarks[bm] = p.node() |
2044 ret = hg.update(repo, p.rev()) |
2015 repo._bookmarks.write() |
2045 if not ret: |
2016 else: |
2046 wlock = repo.wlock() |
2017 bmdeactivate(repo) |
2047 try: |
2018 finally: |
2048 if shouldmove: |
2019 wlock.release() |
2049 repo._bookmarks[bm] = p.node() |
|
2050 repo._bookmarks.write() |
|
2051 else: |
|
2052 bmdeactivate(repo) |
|
2053 finally: |
|
2054 wlock.release() |
2020 displayer.show(p) |
2055 displayer.show(p) |
2021 return 0 |
2056 return 0 |
2022 else: |
2057 else: |
2023 for p in parents: |
2058 for p in parents: |
2024 displayer.show(p) |
2059 displayer.show(p) |
2025 ui.warn(_('multiple parents, explicitly update to one\n')) |
2060 ui.warn(_('multiple parents, explicitly update to one\n')) |
2026 return 1 |
2061 return 1 |
2027 |
2062 |
2028 @command('^next', |
2063 @command('^next', |
2029 [('B', 'move-bookmark', False, |
2064 [('B', 'move-bookmark', False, |
2030 _('Move active bookmark after update')), |
2065 _('move active bookmark after update')), |
2031 ('', 'merge', False, _('bring uncommited change along')), |
2066 ('', 'merge', False, _('bring uncommitted change along')), |
2032 ('', 'evolve', False, _('evolve the next changeset if necessary'))], |
2067 ('', 'evolve', False, _('evolve the next changeset if necessary')), |
2033 '[-B]') |
2068 ('n', 'dry-run', False, _('do not perform actions, just print what would be done'))], |
|
2069 '[OPTION]...') |
2034 def cmdnext(ui, repo, **opts): |
2070 def cmdnext(ui, repo, **opts): |
2035 """update to next child |
2071 """update to next child |
2036 |
2072 |
2037 You can use the --evolve flag to get unstable children evolved on demand. |
2073 You can use the --evolve flag to get unstable children evolved on demand. |
2038 |
2074 |
2039 The summary line of the destination is displayed for clarity""" |
2075 The summary line of the destination is displayed for clarity""" |
2040 wkctx = repo[None] |
2076 wkctx = repo[None] |
2041 wparents = wkctx.parents() |
2077 wparents = wkctx.parents() |
|
2078 dryrunopt = opts['dry_run'] |
2042 if len(wparents) != 1: |
2079 if len(wparents) != 1: |
2043 raise util.Abort('merge in progress') |
2080 raise util.Abort('merge in progress') |
2044 if not opts['merge']: |
2081 if not opts['merge']: |
2045 try: |
2082 try: |
2046 cmdutil.bailifchanged(repo) |
2083 cmdutil.bailifchanged(repo) |
2081 msg = _('(%i unstable changesets to be evolved here, ' |
2123 msg = _('(%i unstable changesets to be evolved here, ' |
2082 'do you want --evolve?)\n') |
2124 'do you want --evolve?)\n') |
2083 ui.warn(msg % len(aspchildren)) |
2125 ui.warn(msg % len(aspchildren)) |
2084 result = 1 |
2126 result = 1 |
2085 elif 1 < len(aspchildren): |
2127 elif 1 < len(aspchildren): |
2086 ui.warn("ambigious next (unstable) changeset:\n") |
2128 ui.warn(_("ambigious next (unstable) changeset:\n")) |
2087 for c in aspchildren: |
2129 for c in aspchildren: |
2088 displayer.show(repo[c]) |
2130 displayer.show(repo[c]) |
2089 ui.warn(_('(run "hg evolve --rev REV" on one of them)\n')) |
2131 ui.warn(_('(run "hg evolve --rev REV" on one of them)\n')) |
2090 return 1 |
2132 return 1 |
2091 else: |
2133 else: |
2092 cmdutil.bailifchanged(repo) |
2134 cmdutil.bailifchanged(repo) |
2093 result = _solveone(ui, repo, repo[aspchildren[0]], False, |
2135 result = _solveone(ui, repo, repo[aspchildren[0]], dryrunopt, |
2094 False, lambda:None, category='unstable') |
2136 False, lambda:None, category='unstable') |
2095 if not result: |
2137 if not result: |
2096 ui.status(_('working directory now at %s\n') % repo['.']) |
2138 ui.status(_('working directory now at %s\n') % repo['.']) |
2097 return result |
2139 return result |
2098 return 1 |
2140 return 1 |
2155 [('n', 'new', [], _("successor changeset (DEPRECATED)")), |
2200 [('n', 'new', [], _("successor changeset (DEPRECATED)")), |
2156 ('s', 'succ', [], _("successor changeset")), |
2201 ('s', 'succ', [], _("successor changeset")), |
2157 ('r', 'rev', [], _("revisions to prune")), |
2202 ('r', 'rev', [], _("revisions to prune")), |
2158 ('k', 'keep', None, _("does not modify working copy during prune")), |
2203 ('k', 'keep', None, _("does not modify working copy during prune")), |
2159 ('', 'biject', False, _("do a 1-1 map between rev and successor ranges")), |
2204 ('', 'biject', False, _("do a 1-1 map between rev and successor ranges")), |
|
2205 ('', 'fold', False, _("record a fold (multiple precursors, one successors)")), |
|
2206 ('', 'split', False, _("record a split (on precursor, multiple successors)")), |
2160 ('B', 'bookmark', '', _("remove revs only reachable from given" |
2207 ('B', 'bookmark', '', _("remove revs only reachable from given" |
2161 " bookmark"))] + metadataopts, |
2208 " bookmark"))] + metadataopts, |
2162 _('[OPTION] [-r] REV...')) |
2209 _('[OPTION] [-r] REV...')) |
2163 # -U --noupdate option to prevent wc update and or bookmarks update ? |
2210 # -U --noupdate option to prevent wc update and or bookmarks update ? |
2164 def cmdprune(ui, repo, *revs, **opts): |
2211 def cmdprune(ui, repo, *revs, **opts): |
2177 |
2224 |
2178 You can use the ``--biject`` option to specify a 1-1 (bijection) between |
2225 You can use the ``--biject`` option to specify a 1-1 (bijection) between |
2179 revisions to prune and successor changesets. This option may be removed in |
2226 revisions to prune and successor changesets. This option may be removed in |
2180 a future release (with the functionality absorbed automatically). |
2227 a future release (with the functionality absorbed automatically). |
2181 |
2228 |
|
2229 If you specify multiple revisions in --succ, you are recording a "split" |
|
2230 and have to acknowledge it by usng --split. The same logic apply when you |
|
2231 prune multiple changesets with a single successors, this will record a |
|
2232 "fold" requires a --fold flag. |
2182 """ |
2233 """ |
2183 revs = scmutil.revrange(repo, list(revs) + opts.get('rev')) |
2234 revs = scmutil.revrange(repo, list(revs) + opts.get('rev')) |
2184 succs = opts['new'] + opts['succ'] |
2235 succs = opts['new'] + opts['succ'] |
2185 bookmark = opts.get('bookmark') |
2236 bookmark = opts.get('bookmark') |
2186 metadata = _getmetadata(**opts) |
2237 metadata = _getmetadata(**opts) |
2187 biject = opts.get('biject') |
2238 biject = opts.get('biject') |
|
2239 fold = opts.get('fold') |
|
2240 split = opts.get('split') |
|
2241 |
|
2242 options = [o for o in ('biject', 'fold', 'split') if opts.get(o)] |
|
2243 if 1 < len(options): |
|
2244 raise util.Abort(_("can only specify one of %s") % ', '.join(options)) |
2188 |
2245 |
2189 if bookmark: |
2246 if bookmark: |
2190 marks,revs = _reachablefrombookmark(repo, revs, bookmark) |
2247 marks,revs = _reachablefrombookmark(repo, revs, bookmark) |
2191 if not revs: |
2248 if not revs: |
2192 # no revisions to prune - delete bookmark immediately |
2249 # no revisions to prune - delete bookmark immediately |
2222 sucs.sort() |
2279 sucs.sort() |
2223 sucs = tuple(repo[n] for n in sucs) |
2280 sucs = tuple(repo[n] for n in sucs) |
2224 if not biject and len(sucs) > 1 and len(precs) > 1: |
2281 if not biject and len(sucs) > 1 and len(precs) > 1: |
2225 msg = "Can't use multiple successors for multiple precursors" |
2282 msg = "Can't use multiple successors for multiple precursors" |
2226 raise util.Abort(msg) |
2283 raise util.Abort(msg) |
2227 |
2284 elif biject and len(sucs) != len(precs): |
2228 if biject and len(sucs) != len(precs): |
|
2229 msg = "Can't use %d successors for %d precursors" \ |
2285 msg = "Can't use %d successors for %d precursors" \ |
2230 % (len(sucs), len(precs)) |
2286 % (len(sucs), len(precs)) |
2231 raise util.Abort(msg) |
2287 raise util.Abort(msg) |
2232 |
2288 elif (len(precs) == 1 and len(sucs) > 1) and not split: |
2233 relations = [(p, sucs) for p in precs] |
2289 msg = "please add --split if you want to do a split" |
2234 if biject: |
2290 raise util.Abort(msg) |
|
2291 elif len(sucs) == 1 and len(precs) > 1 and not fold: |
|
2292 msg = "please add --fold if you want to do a fold" |
|
2293 raise util.Abort(msg) |
|
2294 elif biject: |
2235 relations = [(p, (s,)) for p, s in zip(precs, sucs)] |
2295 relations = [(p, (s,)) for p, s in zip(precs, sucs)] |
|
2296 else: |
|
2297 relations = [(p, sucs) for p in precs] |
2236 |
2298 |
2237 wdp = repo['.'] |
2299 wdp = repo['.'] |
2238 |
2300 |
2239 if len(sucs) == 1 and len(precs) == 1 and wdp in precs: |
2301 if len(sucs) == 1 and len(precs) == 1 and wdp in precs: |
2240 # '.' killed, so update to the successor |
2302 # '.' killed, so update to the successor |
2264 # reset files that only changed in the dirstate too |
2326 # reset files that only changed in the dirstate too |
2265 dirstate = repo.dirstate |
2327 dirstate = repo.dirstate |
2266 dirchanges = [f for f in dirstate if dirstate[f] != 'n'] |
2328 dirchanges = [f for f in dirstate if dirstate[f] != 'n'] |
2267 changedfiles.extend(dirchanges) |
2329 changedfiles.extend(dirchanges) |
2268 repo.dirstate.rebuild(newnode.node(), newnode.manifest(), changedfiles) |
2330 repo.dirstate.rebuild(newnode.node(), newnode.manifest(), changedfiles) |
2269 repo.dirstate.write() |
2331 writedirstate(dirstate, tr) |
2270 else: |
2332 else: |
2271 bookactive = bmactive(repo) |
2333 bookactive = bmactive(repo) |
2272 # Active bookmark that we don't want to delete (with -B option) |
2334 # Active bookmark that we don't want to delete (with -B option) |
2273 # we deactivate and move it before the update and reactivate it |
2335 # we deactivate and move it before the update and reactivate it |
2274 # after |
2336 # after |
2503 if disallowunstable and not onahead: |
2564 if disallowunstable and not onahead: |
2504 raise util.Abort(_("cannot uncommit in the middle of a stack")) |
2565 raise util.Abort(_("cannot uncommit in the middle of a stack")) |
2505 |
2566 |
2506 # Recommit the filtered changeset |
2567 # Recommit the filtered changeset |
2507 tr = repo.transaction('uncommit') |
2568 tr = repo.transaction('uncommit') |
|
2569 updatebookmarks = _bookmarksupdater(repo, old.node(), tr) |
2508 newid = None |
2570 newid = None |
2509 includeorexclude = opts.get('include') or opts.get('exclude') |
2571 includeorexclude = opts.get('include') or opts.get('exclude') |
2510 if (pats or includeorexclude or opts.get('all')): |
2572 if (pats or includeorexclude or opts.get('all')): |
2511 match = scmutil.match(old, pats, opts) |
2573 match = scmutil.match(old, pats, opts) |
2512 newid = _commitfiltered(repo, old, match, target=rev) |
2574 newid = _commitfiltered(repo, old, match, target=rev) |
2554 if oldbookmarks: |
2616 if oldbookmarks: |
2555 repo._bookmarks.write() |
2617 repo._bookmarks.write() |
2556 return result |
2618 return result |
2557 finally: |
2619 finally: |
2558 lockmod.release(lock, wlock) |
2620 lockmod.release(lock, wlock) |
|
2621 |
|
2622 @command('^split', |
|
2623 [('r', 'rev', [], _("revision to fold")), |
|
2624 ] + commitopts + commitopts2, |
|
2625 _('hg split [OPTION]... [-r] REV')) |
|
2626 def cmdsplit(ui, repo, *revs, **opts): |
|
2627 """Split the current commit using interactive selection (EXPERIMENTAL) |
|
2628 |
|
2629 By default, split the current revision by prompting for all its hunk to be |
|
2630 redistributed into new changesets. |
|
2631 |
|
2632 Use --rev for splitting a given changeset instead. |
|
2633 """ |
|
2634 tr = wlock = lock = None |
|
2635 newcommits = [] |
|
2636 |
|
2637 revopt = opts.get('rev') |
|
2638 if revopt: |
|
2639 revs = scmutil.revrange(repo, revopt) |
|
2640 if len(revs) != 1: |
|
2641 raise util.Abort(_("you can only specify one revision to split")) |
|
2642 else: |
|
2643 rev = list(revs)[0] |
|
2644 else: |
|
2645 rev = '.' |
|
2646 |
|
2647 try: |
|
2648 wlock = repo.wlock() |
|
2649 lock = repo.lock() |
|
2650 cmdutil.bailifchanged(repo) |
|
2651 tr = repo.transaction('split') |
|
2652 ctx = repo[rev] |
|
2653 r = ctx.rev() |
|
2654 disallowunstable = not obsolete.isenabled(repo, |
|
2655 obsolete.allowunstableopt) |
|
2656 if disallowunstable: |
|
2657 # XXX We should check head revs |
|
2658 if repo.revs("(%d::) - %d", rev, rev): |
|
2659 raise util.Abort(_("cannot split commit: %s not a head") % ctx) |
|
2660 |
|
2661 if len(ctx.parents()) > 1: |
|
2662 raise util.Abort(_("cannot split merge commits")) |
|
2663 prev = ctx.p1() |
|
2664 bmupdate = _bookmarksupdater(repo, ctx.node(), tr) |
|
2665 bookactive = bmactive(repo) |
|
2666 if bookactive is not None: |
|
2667 repo.ui.status(_("(leaving bookmark %s)\n") % bmactive(repo)) |
|
2668 bmdeactivate(repo) |
|
2669 hg.update(repo, prev) |
|
2670 |
|
2671 commands.revert(ui, repo, rev=r, all=True) |
|
2672 def haschanges(): |
|
2673 modified, added, removed, deleted = repo.status()[:4] |
|
2674 return modified or added or removed or deleted |
|
2675 while haschanges(): |
|
2676 pats = () |
|
2677 cmdutil.dorecord(ui, repo, commands.commit, 'commit', False, |
|
2678 cmdutil.recordfilter, *pats, **opts) |
|
2679 # TODO: Does no seem like the best way to do this |
|
2680 # We should make dorecord return the newly created commit |
|
2681 newcommits.append(repo['.']) |
|
2682 if haschanges(): |
|
2683 if ui.prompt('Done splitting? [yN]', default='n') == 'y': |
|
2684 commands.commit(ui, repo, **opts) |
|
2685 newcommits.append(repo['.']) |
|
2686 break |
|
2687 else: |
|
2688 ui.status("no more change to split\n") |
|
2689 |
|
2690 tip = repo[newcommits[-1]] |
|
2691 bmupdate(tip.node()) |
|
2692 if bookactive is not None: |
|
2693 bmactivate(repo, bookactive) |
|
2694 obsolete.createmarkers(repo, [(repo[r], newcommits)]) |
|
2695 tr.close() |
|
2696 finally: |
|
2697 lockmod.release(tr, lock, wlock) |
|
2698 |
2559 |
2699 |
2560 @eh.wrapcommand('strip', extension='strip', opts=[ |
2700 @eh.wrapcommand('strip', extension='strip', opts=[ |
2561 ('', 'bundle', None, _("delete the commit entirely and move it to a " |
2701 ('', 'bundle', None, _("delete the commit entirely and move it to a " |
2562 "backup bundle")), |
2702 "backup bundle")), |
2563 ]) |
2703 ]) |
2955 sample = set(undecided) |
3097 sample = set(undecided) |
2956 else: |
3098 else: |
2957 sample = _takefullsample(dag, undecided, size=fullsamplesize) |
3099 sample = _takefullsample(dag, undecided, size=fullsamplesize) |
2958 |
3100 |
2959 roundtrips += 1 |
3101 roundtrips += 1 |
|
3102 ui.progress("comparing with other", totalnb - len(undecided), |
|
3103 total=totalnb) |
2960 ui.debug("query %i; still undecided: %i, sample size is: %i\n" |
3104 ui.debug("query %i; still undecided: %i, sample size is: %i\n" |
2961 % (roundtrips, len(undecided), len(sample))) |
3105 % (roundtrips, len(undecided), len(sample))) |
2962 # indices between sample and externalized version must match |
3106 # indices between sample and externalized version must match |
2963 sample = list(sample) |
3107 sample = list(sample) |
2964 remotehash = getremotehash(dag.externalizeall(sample)) |
3108 remotehash = getremotehash(dag.externalizeall(sample)) |
3321 sha.update(m) |
3467 sha.update(m) |
3322 if entry: |
3468 if entry: |
3323 cache.append((ctx.node(), sha.digest())) |
3469 cache.append((ctx.node(), sha.digest())) |
3324 else: |
3470 else: |
3325 cache.append((ctx.node(), nullid)) |
3471 cache.append((ctx.node(), nullid)) |
|
3472 repo.ui.progress("preparing locally", i, total=len(unfi)) |
|
3473 repo.ui.progress("preparing locally", None) |
3326 return cache |
3474 return cache |
3327 |
3475 |
3328 @command('debugobsrelsethashtree', |
3476 @command('debugobsrelsethashtree', |
3329 [('', 'v0', None, 'hash on marker format "0"'), |
3477 [('', 'v0', None, 'hash on marker format "0"'), |
3330 ('', 'v1', None, 'hash on marker format "1" (default)') |
3478 ('', 'v1', None, 'hash on marker format "1" (default)') |
3366 @command( |
3514 @command( |
3367 'debugobsconvert', |
3515 'debugobsconvert', |
3368 [('', 'new-format', _bestformat, _('Destination format for markers.'))], |
3516 [('', 'new-format', _bestformat, _('Destination format for markers.'))], |
3369 '') |
3517 '') |
3370 def debugobsconvert(ui, repo, new_format): |
3518 def debugobsconvert(ui, repo, new_format): |
|
3519 origmarkers = repo.obsstore._all # settle version |
3371 if new_format == repo.obsstore._version: |
3520 if new_format == repo.obsstore._version: |
3372 msg = _('New format is the same as the old format, not upgrading!') |
3521 msg = _('New format is the same as the old format, not upgrading!') |
3373 raise util.Abort(msg) |
3522 raise util.Abort(msg) |
3374 f = repo.svfs('obsstore', 'wb', atomictemp=True) |
3523 f = repo.svfs('obsstore', 'wb', atomictemp=True) |
3375 origmarkers = repo.obsstore._all |
|
3376 known = set() |
3524 known = set() |
3377 markers = [] |
3525 markers = [] |
3378 for m in origmarkers: |
3526 for m in origmarkers: |
3379 # filter out invalid markers |
3527 # filter out invalid markers |
3380 if nullid in m[1]: |
3528 if nullid in m[1]: |