hgext/evolve.py
branchstable
changeset 1745 6c922448ccec
parent 1743 299cdaa24fa5
child 1752 19e32420e150
equal deleted inserted replaced
1742:970a4c13ebc3 1745:6c922448ccec
    19     - improves some aspect of the early implementation in Mercurial core
    19     - improves some aspect of the early implementation in Mercurial core
    20 '''
    20 '''
    21 
    21 
    22 __version__ = '5.4.1'
    22 __version__ = '5.4.1'
    23 testedwith = '3.4.3 3.5.2 3.6.2 3.7.3 3.8.1 3.9'
    23 testedwith = '3.4.3 3.5.2 3.6.2 3.7.3 3.8.1 3.9'
    24 buglink = 'http://bz.selenic.com/'
    24 buglink = 'https://bz.mercurial-scm.org/'
    25 
    25 
    26 
    26 
    27 evolutionhelptext = """
    27 evolutionhelptext = """
    28 Obsolescence markers make it possible to mark changesets that have been
    28 Obsolescence markers make it possible to mark changesets that have been
    29 deleted or superset in a new version of the changeset.
    29 deleted or superset in a new version of the changeset.
   663 ### template keywords
   663 ### template keywords
   664 # XXX it does not handle troubles well :-/
   664 # XXX it does not handle troubles well :-/
   665 
   665 
   666 @eh.templatekw('obsolete')
   666 @eh.templatekw('obsolete')
   667 def obsoletekw(repo, ctx, templ, **args):
   667 def obsoletekw(repo, ctx, templ, **args):
   668     """:obsolete: String. The obsolescence level of the node, could be
   668     """:obsolete: String. Whether the changeset is ``obsolete``.
   669     ``stable``, ``unstable``, ``suspended`` or ``extinct``.
       
   670     """
   669     """
   671     if ctx.obsolete():
   670     if ctx.obsolete():
   672         if ctx.extinct():
   671         return 'obsolete'
   673             return 'extinct'
   672     return ''
   674         else:
       
   675             return 'suspended'
       
   676     elif ctx.unstable():
       
   677         return 'unstable'
       
   678     return 'stable'
       
   679 
   673 
   680 @eh.templatekw('troubles')
   674 @eh.templatekw('troubles')
   681 def showtroubles(repo, ctx, **args):
   675 def showtroubles(repo, ctx, **args):
   682     """:troubles: List of strings. Evolution troubles affecting the changeset
   676     """:troubles: List of strings. Evolution troubles affecting the changeset
   683     (zero or more of "unstable", "divergent" or "bumped")."""
   677     (zero or more of "unstable", "divergent" or "bumped")."""
   962                            'not be updated\n') % sha1)
   956                            'not be updated\n') % sha1)
   963 
   957 
   964     tr = repo.currenttransaction()
   958     tr = repo.currenttransaction()
   965     assert tr is not None
   959     assert tr is not None
   966     try:
   960     try:
   967         try:
   961         r = _evolvemerge(repo, orig, dest, pctx, keepbranch)
   968             r = _evolvemerge(repo, orig, dest, pctx, keepbranch)
   962         if r[-1]:  #some conflict
   969             if r[-1]:  #some conflict
   963             raise error.Abort(
   970                 raise error.Abort(
   964                     'unresolved merge conflicts (see hg help resolve)')
   971                         'unresolved merge conflicts (see hg help resolve)')
   965         nodenew = _relocatecommit(repo, orig, commitmsg)
   972             nodenew = _relocatecommit(repo, orig, commitmsg)
   966     except error.Abort as exc:
   973         except error.Abort as exc:
   967         repo.dirstate.beginparentchange()
   974             repo.dirstate.beginparentchange()
   968         repo.setparents(repo['.'].node(), nullid)
   975             repo.setparents(repo['.'].node(), nullid)
   969         writedirstate(repo.dirstate, tr)
   976             writedirstate(repo.dirstate, tr)
   970         # fix up dirstate for copies and renames
   977             # fix up dirstate for copies and renames
   971         copies.duplicatecopies(repo, dest.rev(), orig.p1().rev())
   978             copies.duplicatecopies(repo, dest.rev(), orig.p1().rev())
   972         repo.dirstate.endparentchange()
   979             repo.dirstate.endparentchange()
   973         class LocalMergeFailure(MergeFailure, exc.__class__):
   980             class LocalMergeFailure(MergeFailure, exc.__class__):
   974             pass
   981                 pass
   975         exc.__class__ = LocalMergeFailure
   982             exc.__class__ = LocalMergeFailure
   976         tr.close() # to keep changes in this transaction (e.g. dirstate)
   983             tr.close() # to keep changes in this transaction (e.g. dirstate)
   977         raise
   984             raise
   978     oldbookmarks = repo.nodebookmarks(nodesrc)
   985         oldbookmarks = repo.nodebookmarks(nodesrc)
   979     _finalizerelocate(repo, orig, dest, nodenew, tr)
   986         _finalizerelocate(repo, orig, dest, nodenew, tr)
       
   987     finally:
       
   988         pass # TODO: remove this redundant try/finally block
       
   989     return nodenew
   980     return nodenew
   990 
   981 
   991 def _bookmarksupdater(repo, oldid, tr):
   982 def _bookmarksupdater(repo, oldid, tr):
   992     """Return a callable update(newid) updating the current bookmark
   983     """Return a callable update(newid) updating the current bookmark
   993     and bookmarks bound to oldid to newid.
   984     and bookmarks bound to oldid to newid.
  1022         return repo._bookmarkcurrent
  1013         return repo._bookmarkcurrent
  1023 
  1014 
  1024 ### dirstate compatibility layer < hg 3.6
  1015 ### dirstate compatibility layer < hg 3.6
  1025 
  1016 
  1026 def writedirstate(dirstate, tr):
  1017 def writedirstate(dirstate, tr):
  1027     if dirstate.write.func_defaults is not None: # mercurial 3.6 and above
  1018     if dirstate.write.func_code.co_argcount != 1: # mercurial 3.6 and above
  1028         return dirstate.write(tr)
  1019         return dirstate.write(tr)
  1029     return dirstate.write()
  1020     return dirstate.write()
  1030 
  1021 
  1031 
  1022 
  1032 
  1023 
  1792         state = _evolvestateread(repo)
  1783         state = _evolvestateread(repo)
  1793         if state is None:
  1784         if state is None:
  1794             raise error.Abort('no evolve to continue')
  1785             raise error.Abort('no evolve to continue')
  1795         orig = repo[state['current']]
  1786         orig = repo[state['current']]
  1796         # XXX This is a terrible terrible hack, please get rid of it.
  1787         # XXX This is a terrible terrible hack, please get rid of it.
  1797         repo.opener.write('graftstate', orig.hex() + '\n')
  1788         lock = repo.wlock()
  1798         try:
  1789         try:
  1799             graftcmd = commands.table['graft'][0]
  1790             repo.opener.write('graftstate', orig.hex() + '\n')
  1800             ret = graftcmd(ui, repo, old_obsolete=True, **{'continue': True})
  1791             try:
  1801             _evolvestatedelete(repo)
  1792                 graftcmd = commands.table['graft'][0]
  1802             return ret
  1793                 ret = graftcmd(ui, repo, old_obsolete=True, **{'continue': True})
       
  1794                 _evolvestatedelete(repo)
       
  1795                 return ret
       
  1796             finally:
       
  1797                 util.unlinkpath(repo.join('graftstate'), ignoremissing=True)
  1803         finally:
  1798         finally:
  1804             util.unlinkpath(repo.join('graftstate'), ignoremissing=True)
  1799             lock.release()
  1805     cmdutil.bailifchanged(repo)
  1800     cmdutil.bailifchanged(repo)
  1806 
  1801 
  1807 
  1802 
  1808     if revopt and allopt:
  1803     if revopt and allopt:
  1809         raise error.Abort('cannot specify both "--rev" and "--all"')
  1804         raise error.Abort('cannot specify both "--rev" and "--all"')
  1992     tmpctx = bumped
  1987     tmpctx = bumped
  1993     # Basic check for common parent. Far too complicated and fragile
  1988     # Basic check for common parent. Far too complicated and fragile
  1994     tr = repo.currenttransaction()
  1989     tr = repo.currenttransaction()
  1995     assert tr is not None
  1990     assert tr is not None
  1996     bmupdate = _bookmarksupdater(repo, bumped.node(), tr)
  1991     bmupdate = _bookmarksupdater(repo, bumped.node(), tr)
  1997     try:
  1992     if not list(repo.set('parents(%d) and parents(%d)', bumped, prec)):
  1998         if not list(repo.set('parents(%d) and parents(%d)', bumped, prec)):
  1993         # Need to rebase the changeset at the right place
  1999             # Need to rebase the changeset at the right place
  1994         repo.ui.status(
  2000             repo.ui.status(
  1995             _('rebasing to destination parent: %s\n') % prec.p1())
  2001                 _('rebasing to destination parent: %s\n') % prec.p1())
  1996         try:
  2002             try:
  1997             tmpid = relocate(repo, bumped, prec.p1())
  2003                 tmpid = relocate(repo, bumped, prec.p1())
  1998             if tmpid is not None:
  2004                 if tmpid is not None:
  1999                 tmpctx = repo[tmpid]
  2005                     tmpctx = repo[tmpid]
  2000                 obsolete.createmarkers(repo, [(bumped, (tmpctx,))])
  2006                     obsolete.createmarkers(repo, [(bumped, (tmpctx,))])
  2001         except MergeFailure:
  2007             except MergeFailure:
  2002             repo.opener.write('graftstate', bumped.hex() + '\n')
  2008                 repo.opener.write('graftstate', bumped.hex() + '\n')
  2003             repo.ui.write_err(_('evolution failed!\n'))
  2009                 repo.ui.write_err(_('evolution failed!\n'))
  2004             repo.ui.write_err(
  2010                 repo.ui.write_err(
  2005                 _('fix conflict and run "hg evolve --continue"\n'))
  2011                     _('fix conflict and run "hg evolve --continue"\n'))
  2006             raise
  2012                 raise
  2007     # Create the new commit context
  2013         # Create the new commit context
  2008     repo.ui.status(_('computing new diff\n'))
  2014         repo.ui.status(_('computing new diff\n'))
  2009     files = set()
  2015         files = set()
  2010     copied = copies.pathcopies(prec, bumped)
  2016         copied = copies.pathcopies(prec, bumped)
  2011     precmanifest = prec.manifest().copy()
  2017         precmanifest = prec.manifest().copy()
  2012     # 3.3.2 needs a list.
  2018         # 3.3.2 needs a list.
  2013     # future 3.4 don't detect the size change during iteration
  2019         # future 3.4 don't detect the size change during iteration
  2014     # this is fishy
  2020         # this is fishy
  2015     for key, val in list(bumped.manifest().iteritems()):
  2021         for key, val in list(bumped.manifest().iteritems()):
  2016         precvalue = precmanifest.get(key, None)
  2022             precvalue = precmanifest.get(key, None)
  2017         if precvalue is not None:
  2023             if precvalue is not None:
  2018             del precmanifest[key]
  2024                 del precmanifest[key]
  2019         if precvalue != val:
  2025             if precvalue != val:
  2020             files.add(key)
  2026                 files.add(key)
  2021     files.update(precmanifest)  # add missing files
  2027         files.update(precmanifest)  # add missing files
  2022     # commit it
  2028         # commit it
  2023     if files: # something to commit!
  2029         if files: # something to commit!
  2024         def filectxfn(repo, ctx, path):
  2030             def filectxfn(repo, ctx, path):
  2025             if path in bumped:
  2031                 if path in bumped:
  2026                 fctx = bumped[path]
  2032                     fctx = bumped[path]
  2027                 flags = fctx.flags()
  2033                     flags = fctx.flags()
  2028                 mctx = memfilectx(repo, fctx.path(), fctx.data(),
  2034                     mctx = memfilectx(repo, fctx.path(), fctx.data(),
  2029                                   islink='l' in flags,
  2035                                       islink='l' in flags,
  2030                                   isexec='x' in flags,
  2036                                       isexec='x' in flags,
  2031                                   copied=copied.get(path))
  2037                                       copied=copied.get(path))
  2032                 return mctx
  2038                     return mctx
  2033             return None
  2039                 return None
  2034         text = 'bumped update to %s:\n\n' % prec
  2040             text = 'bumped update to %s:\n\n' % prec
  2035         text += bumped.description()
  2041             text += bumped.description()
  2036 
  2042 
  2037         new = context.memctx(repo,
  2043             new = context.memctx(repo,
  2038                              parents=[prec.node(), node.nullid],
  2044                                  parents=[prec.node(), node.nullid],
  2039                              text=text,
  2045                                  text=text,
  2040                              files=files,
  2046                                  files=files,
  2041                              filectxfn=filectxfn,
  2047                                  filectxfn=filectxfn,
  2042                              user=bumped.user(),
  2048                                  user=bumped.user(),
  2043                              date=bumped.date(),
  2049                                  date=bumped.date(),
  2044                              extra=bumped.extra())
  2050                                  extra=bumped.extra())
  2045 
  2051 
  2046         newid = repo.commitctx(new)
  2052             newid = repo.commitctx(new)
  2047     if newid is None:
  2053         if newid is None:
  2048         obsolete.createmarkers(repo, [(tmpctx, ())])
  2054             obsolete.createmarkers(repo, [(tmpctx, ())])
  2049         newid = prec.node()
  2055             newid = prec.node()
  2050     else:
  2056         else:
  2051         phases.retractboundary(repo, tr, bumped.phase(), [newid])
  2057             phases.retractboundary(repo, tr, bumped.phase(), [newid])
  2052         obsolete.createmarkers(repo, [(tmpctx, (repo[newid],))],
  2058             obsolete.createmarkers(repo, [(tmpctx, (repo[newid],))],
  2053                                flag=obsolete.bumpedfix)
  2059                                    flag=obsolete.bumpedfix)
  2054     bmupdate(newid)
  2060         bmupdate(newid)
  2055     repo.ui.status(_('committed as %s\n') % node.short(newid))
  2061         repo.ui.status(_('committed as %s\n') % node.short(newid))
       
  2062     finally:
       
  2063         pass # TODO: remove this redundant try/finally block
       
  2064     # reroute the working copy parent to the new changeset
  2056     # reroute the working copy parent to the new changeset
  2065     repo.dirstate.beginparentchange()
  2057     repo.dirstate.beginparentchange()
  2066     repo.dirstate.setparents(newid, node.nullid)
  2058     repo.dirstate.setparents(newid, node.nullid)
  2067     repo.dirstate.endparentchange()
  2059     repo.dirstate.endparentchange()
  2068 
  2060