hgext/evolve.py
branchstable
changeset 327 c017ad874dfc
parent 326 52c53e2d413b
child 355 72642a6970e0
equal deleted inserted replaced
293:232990fbecb5 327:c017ad874dfc
    25 from mercurial.commands import walkopts, commitopts, commitopts2, logopts
    25 from mercurial.commands import walkopts, commitopts, commitopts2, logopts
    26 from mercurial import hg
    26 from mercurial import hg
    27 
    27 
    28 ### util function
    28 ### util function
    29 #############################
    29 #############################
       
    30 
    30 def noderange(repo, revsets):
    31 def noderange(repo, revsets):
    31     """The same as revrange but return node"""
    32     """The same as revrange but return node"""
    32     return map(repo.changelog.node,
    33     return map(repo.changelog.node,
    33                scmutil.revrange(repo, revsets))
    34                scmutil.revrange(repo, revsets))
    34 
    35 
    35 
    36 def warnobserrors(orig, ui, repo, *args, **kwargs):
    36 
       
    37 def warnunstable(orig, ui, repo, *args, **kwargs):
       
    38     """display warning is the command resulted in more instable changeset"""
    37     """display warning is the command resulted in more instable changeset"""
    39     priorunstables = len(repo.revs('unstable()'))
    38     priorunstables = len(repo.revs('unstable()'))
       
    39     priorlatecomers = len(repo.revs('latecomer()'))
    40     #print orig, priorunstables
    40     #print orig, priorunstables
    41     #print len(repo.revs('secret() - obsolete()'))
    41     #print len(repo.revs('secret() - obsolete()'))
    42     try:
    42     try:
    43         return orig(ui, repo, *args, **kwargs)
    43         return orig(ui, repo, *args, **kwargs)
    44     finally:
    44     finally:
    45         newunstables = len(repo.revs('unstable()')) - priorunstables
    45         newunstables = len(repo.revs('unstable()')) - priorunstables
       
    46         newlatecomers = len(repo.revs('latecomer()')) - priorlatecomers
    46         #print orig, newunstables
    47         #print orig, newunstables
    47         #print len(repo.revs('secret() - obsolete()'))
    48         #print len(repo.revs('secret() - obsolete()'))
    48         if newunstables > 0:
    49         if newunstables > 0:
    49             ui.warn(_('%i new unstables changesets\n') % newunstables)
    50             ui.warn(_('%i new unstables changesets\n') % newunstables)
    50 
    51         if newlatecomers > 0:
    51 
    52             ui.warn(_('%i new latecomers changesets\n') % newlatecomers)
    52 ### extension check
       
    53 #############################
       
    54 
       
    55 
    53 
    56 ### changeset rewriting logic
    54 ### changeset rewriting logic
    57 #############################
    55 #############################
    58 
    56 
    59 def rewrite(repo, old, updates, head, newbases, commitopts):
    57 def rewrite(repo, old, updates, head, newbases, commitopts):
    63     references a changeset existing before the rewrite call.
    61     references a changeset existing before the rewrite call.
    64     """
    62     """
    65     if len(old.parents()) > 1: #XXX remove this unecessary limitation.
    63     if len(old.parents()) > 1: #XXX remove this unecessary limitation.
    66         raise error.Abort(_('cannot amend merge changesets'))
    64         raise error.Abort(_('cannot amend merge changesets'))
    67     base = old.p1()
    65     base = old.p1()
    68     bm = bookmarks.readcurrent(repo)
    66     updatebookmarks = _bookmarksupdater(repo, old.node())
    69 
    67 
    70     wlock = repo.wlock()
    68     wlock = repo.wlock()
    71     try:
    69     try:
    72 
    70 
    73         # commit a new version of the old changeset, including the update
    71         # commit a new version of the old changeset, including the update
   134         revcount = len(repo)
   132         revcount = len(repo)
   135         newid = repo.commitctx(new)
   133         newid = repo.commitctx(new)
   136         new = repo[newid]
   134         new = repo[newid]
   137         created = len(repo) != revcount
   135         created = len(repo) != revcount
   138         if created:
   136         if created:
   139             # update the bookmark
   137             updatebookmarks(newid)
   140             if bm:
       
   141                 repo._bookmarks[bm] = newid
       
   142                 bookmarks.write(repo)
       
   143 
       
   144             # add evolution metadata
   138             # add evolution metadata
   145             repo.addobsolete(new.node(), old.node())
   139             collapsed = set([u.node() for u in updates] + [old.node()])
   146             for u in updates:
   140             repo.addcollapsedobsolete(collapsed, new.node())
   147                 repo.addobsolete(u.node(), old.node())
       
   148                 repo.addobsolete(new.node(), u.node())
       
   149             oldbookmarks = repo.nodebookmarks(old.node())
       
   150             for book in oldbookmarks:
       
   151                 repo._bookmarks[book] = new.node()
       
   152             if oldbookmarks:
       
   153                 bookmarks.write(repo)
       
   154         else:
   141         else:
   155             # newid is an existing revision. It could make sense to
   142             # newid is an existing revision. It could make sense to
   156             # replace revisions with existing ones but probably not by
   143             # replace revisions with existing ones but probably not by
   157             # default.
   144             # default.
   158             pass
   145             pass
   177             rebase.rebasenode(repo, orig.node(), dest.node(),
   164             rebase.rebasenode(repo, orig.node(), dest.node(),
   178                               {node.nullrev: node.nullrev}, False)
   165                               {node.nullrev: node.nullrev}, False)
   179         else:
   166         else:
   180             rebase.rebasenode(repo, orig.node(), dest.node(),
   167             rebase.rebasenode(repo, orig.node(), dest.node(),
   181                               {node.nullrev: node.nullrev})
   168                               {node.nullrev: node.nullrev})
   182         nodenew = rebase.concludenode(repo, orig.node(), dest.node(), node.nullid)
   169         try:
       
   170             nodenew = rebase.concludenode(repo, orig.node(), dest.node(),
       
   171                                           node.nullid)
       
   172         except util.Abort:
       
   173             repo.ui.write_err(_('/!\\ stabilize failed                          /!\\\n'))
       
   174             repo.ui.write_err(_('/!\\ Their is no "hg stabilize --continue"     /!\\\n'))
       
   175             repo.ui.write_err(_('/!\\ use "hg up -C . ; hg stabilize --dry-run" /!\\\n'))
       
   176             raise
   183         oldbookmarks = repo.nodebookmarks(nodesrc)
   177         oldbookmarks = repo.nodebookmarks(nodesrc)
   184         if nodenew is not None:
   178         if nodenew is not None:
   185             phases.retractboundary(repo, destphase, [nodenew])
   179             phases.retractboundary(repo, destphase, [nodenew])
   186             repo.addobsolete(nodenew, nodesrc)
   180             repo.addobsolete(nodenew, nodesrc)
   187             for book in oldbookmarks:
   181             for book in oldbookmarks:
   198     except util.Abort:
   192     except util.Abort:
   199         # Invalidate the previous setparents
   193         # Invalidate the previous setparents
   200         repo.dirstate.invalidate()
   194         repo.dirstate.invalidate()
   201         raise
   195         raise
   202 
   196 
   203 
       
   204 def stabilizableunstable(repo, pctx):
   197 def stabilizableunstable(repo, pctx):
   205     """Return a changectx for an unstable changeset which can be
   198     """Return a changectx for an unstable changeset which can be
   206     stabilized on top of pctx or one of its descendants. None if none
   199     stabilized on top of pctx or one of its descendants. None if none
   207     can be found.
   200     can be found.
   208     """
   201     """
   218                                   ctx.rev()))
   211                                   ctx.rev()))
   219         if unstables:
   212         if unstables:
   220             return unstables[0]
   213             return unstables[0]
   221     return None
   214     return None
   222 
   215 
       
   216 def _bookmarksupdater(repo, oldid):
       
   217     """Return a callable update(newid) updating the current bookmark
       
   218     and bookmarks bound to oldid to newid.
       
   219     """
       
   220     bm = bookmarks.readcurrent(repo)
       
   221     def updatebookmarks(newid):
       
   222         dirty = False
       
   223         if bm:
       
   224             repo._bookmarks[bm] = newid
       
   225             dirty = True
       
   226         oldbookmarks = repo.nodebookmarks(oldid)
       
   227         if oldbookmarks:
       
   228             for b in oldbookmarks:
       
   229                 repo._bookmarks[b] = newid
       
   230             dirty = True
       
   231         if dirty:
       
   232             bookmarks.write(repo)
       
   233     return updatebookmarks
       
   234 
   223 ### new command
   235 ### new command
   224 #############################
   236 #############################
   225 cmdtable = {}
   237 cmdtable = {}
   226 command = cmdutil.command(cmdtable)
   238 command = cmdutil.command(cmdtable)
   227 
   239 
   228 @command('^stabilize|evolve',
   240 @command('^stabilize|evolve',
   229     [
   241     [('n', 'dry-run', False, 'do not perform actions, print what to be done'),
   230      ('n', 'dry-run', False, 'Do nothing but printing what should be done'),
   242     ('A', 'any', False, 'stabilize any unstable changeset'),],
   231      ('A', 'any', False, 'Stabilize unstable change on any topological branch'),
   243     _('[OPTIONS]...'))
   232     ],
       
   233     '')
       
   234 def stabilize(ui, repo, **opts):
   244 def stabilize(ui, repo, **opts):
   235     """rebase an unstable changeset to make it stable again
   245     """rebase an unstable changeset to make it stable again
   236 
   246 
   237     By default, take the first unstable changeset which could be
   247     By default, take the first unstable changeset which could be
   238     rebased as child of the working directory parent revision or one
   248     rebased as child of the working directory parent revision or one
   296             lock.release()
   306             lock.release()
   297 
   307 
   298 shorttemplate = '[{rev}] {desc|firstline}\n'
   308 shorttemplate = '[{rev}] {desc|firstline}\n'
   299 
   309 
   300 @command('^gdown',
   310 @command('^gdown',
   301     [],
   311          [],
   302     'update to working directory parent and display summary lines')
   312          '')
   303 def cmdgdown(ui, repo):
   313 def cmdgdown(ui, repo):
   304     """update to working directory parent an display summary lines"""
   314     """update to parent an display summary lines"""
   305     wkctx = repo[None]
   315     wkctx = repo[None]
   306     wparents = wkctx.parents()
   316     wparents = wkctx.parents()
   307     if len(wparents) != 1:
   317     if len(wparents) != 1:
   308         raise util.Abort('merge in progress')
   318         raise util.Abort('merge in progress')
   309 
   319 
   319             displayer.show(p)
   329             displayer.show(p)
   320         ui.warn(_('multiple parents, explicitly update to one\n'))
   330         ui.warn(_('multiple parents, explicitly update to one\n'))
   321         return 1
   331         return 1
   322 
   332 
   323 @command('^gup',
   333 @command('^gup',
   324     [],
   334          [],
   325     'update to working directory children and display summary lines')
   335          '')
   326 def cmdup(ui, repo):
   336 def cmdup(ui, repo):
   327     """update to working directory children an display summary lines"""
   337     """update to child an display summary lines"""
   328     wkctx = repo[None]
   338     wkctx = repo[None]
   329     wparents = wkctx.parents()
   339     wparents = wkctx.parents()
   330     if len(wparents) != 1:
   340     if len(wparents) != 1:
   331         raise util.Abort('merge in progress')
   341         raise util.Abort('merge in progress')
   332 
   342 
   344         for c in children:
   354         for c in children:
   345             displayer.show(c)
   355             displayer.show(c)
   346         ui.warn(_('Multiple non-obsolete children, explicitly update to one\n'))
   356         ui.warn(_('Multiple non-obsolete children, explicitly update to one\n'))
   347         return 1
   357         return 1
   348 
   358 
   349 
   359 @command('^kill|obsolete|prune',
   350 @command('^kill|obsolete',
   360     [('n', 'new', [], _("successor changeset"))],
   351     [
   361     _('[OPTION] REV...'))
   352     ('n', 'new', [], _("New changeset that justify this one to be killed"))
       
   353     ],
       
   354     '<revs>')
       
   355 def kill(ui, repo, *revs, **opts):
   362 def kill(ui, repo, *revs, **opts):
   356     """mark a changeset as obsolete
   363     """mark a changeset as obsolete
   357 
   364 
   358     This update the parent directory to a not-killed parent if the current
   365     This update the parent directory to a not-killed parent if the current
   359     working directory parent are killed.
   366     working directory parent are killed.
   387         wlock.release()
   394         wlock.release()
   388 
   395 
   389 @command('^amend|refresh',
   396 @command('^amend|refresh',
   390     [('A', 'addremove', None,
   397     [('A', 'addremove', None,
   391      _('mark new/missing files as added/removed before committing')),
   398      _('mark new/missing files as added/removed before committing')),
   392     ('n', 'note', '',
   399     ('n', 'note', '', _('use text as commit message for this update')),
   393      _('use text as commit message for this update')),
   400     ('c', 'change', '', _('specifies the changesets to amend'), _('REV')),
   394     ('c', 'change', '',
   401     ('e', 'edit', False, _('invoke editor on commit messages')),
   395      _('specifies the changeset to amend'), _('REV')),
       
   396     ('e', 'edit', False,
       
   397      _('edit commit message.'), _('')),
       
   398     ] + walkopts + commitopts + commitopts2,
   402     ] + walkopts + commitopts + commitopts2,
   399     _('[OPTION]... [FILE]...'))
   403     _('[OPTION]... [FILE]...'))
   400 
       
   401 def amend(ui, repo, *pats, **opts):
   404 def amend(ui, repo, *pats, **opts):
   402     """combine a changeset with updates and replace it with a new one
   405     """combine a changeset with updates and replace it with a new one
   403 
   406 
   404     Commits a new changeset incorporating both the changes to the given files
   407     Commits a new changeset incorporating both the changes to the given files
   405     and all the changes from the current parent changeset into the repository.
   408     and all the changes from the current parent changeset into the repository.
   406 
   409 
   407     See :hg:`commit` for details about committing changes.
   410     See :hg:`commit` for details about committing changes.
   408 
   411 
   409     If you don't specify -m, the parent's message will be reused.
   412     If you don't specify -m, the parent's message will be reused.
   410 
   413 
   411     If you specify --change, amend additionally considers all changesets between
   414     If you specify --change, amend additionally considers all
   412     the indicated changeset and the working copy parent as updates to be subsumed.
   415     changesets between the indicated changeset and the working copy
   413     This allows you to commit updates manually first. As a special shorthand you
   416     parent as updates to be subsumed.
   414     can say `--amend .` instead of '--amend p1(p1())', which subsumes your latest
       
   415     commit as an update of its parent.
       
   416 
   417 
   417     Behind the scenes, Mercurial first commits the update as a regular child
   418     Behind the scenes, Mercurial first commits the update as a regular child
   418     of the current parent. Then it creates a new commit on the parent's parents
   419     of the current parent. Then it creates a new commit on the parent's parents
   419     with the updated contents. Then it changes the working copy parent to this
   420     with the updated contents. Then it changes the working copy parent to this
   420     new combined changeset. Finally, the old changeset and its update are hidden
   421     new combined changeset. Finally, the old changeset and its update are hidden
   422 
   423 
   423     Returns 0 on success, 1 if nothing changed.
   424     Returns 0 on success, 1 if nothing changed.
   424     """
   425     """
   425 
   426 
   426     # determine updates to subsume
   427     # determine updates to subsume
   427     change = opts.get('change', '.')
   428     old = scmutil.revsingle(repo, opts.get('change') or '.')
   428     if change == '.':
       
   429         change = 'p1(p1())'
       
   430     old = scmutil.revsingle(repo, change)
       
   431 
   429 
   432     lock = repo.lock()
   430     lock = repo.lock()
   433     try:
   431     try:
   434         wlock = repo.wlock()
   432         wlock = repo.wlock()
   435         try:
   433         try:
   436             if not old.phase():
   434             if old.phase() == phases.public:
   437                 raise util.Abort(_("can not rewrite immutable changeset %s") % old)
   435                 raise util.Abort(_("can not rewrite immutable changeset %s")
       
   436                                  % old)
   438             oldphase = old.phase()
   437             oldphase = old.phase()
   439             # commit current changes as update
   438             # commit current changes as update
   440             # code copied from commands.commit to avoid noisy messages
   439             # code copied from commands.commit to avoid noisy messages
   441             ciopts = dict(opts)
   440             ciopts = dict(opts)
   442             ciopts.pop('message', None)
   441             ciopts.pop('message', None)
   443             ciopts.pop('logfile', None)
   442             ciopts.pop('logfile', None)
   444             ciopts['message'] = opts.get('note') or ('amends %s' % old.hex())
   443             ciopts['message'] = opts.get('note') or ('amends %s' % old.hex())
   445             e = cmdutil.commiteditor
   444             e = cmdutil.commiteditor
   446             def commitfunc(ui, repo, message, match, opts):
   445             def commitfunc(ui, repo, message, match, opts):
   447                 return repo.commit(message, opts.get('user'), opts.get('date'), match,
   446                 return repo.commit(message, opts.get('user'), opts.get('date'),
   448                                    editor=e)
   447                                    match, editor=e)
   449             revcount = len(repo)
   448             revcount = len(repo)
   450             tempid = cmdutil.commit(ui, repo, commitfunc, pats, ciopts)
   449             tempid = cmdutil.commit(ui, repo, commitfunc, pats, ciopts)
   451             if len(repo) == revcount:
   450             if len(repo) == revcount:
   452                 # No revision created
   451                 # No revision created
   453                 tempid = None
   452                 tempid = None
   463                     if opts.get(o):
   462                     if opts.get(o):
   464                         break
   463                         break
   465                 else:
   464                 else:
   466                     raise error.Abort(_('no updates found'))
   465                     raise error.Abort(_('no updates found'))
   467             updates = [repo[n] for n in updatenodes]
   466             updates = [repo[n] for n in updatenodes]
   468 
       
   469 
       
   470 
   467 
   471             # perform amend
   468             # perform amend
   472             if opts.get('edit'):
   469             if opts.get('edit'):
   473                 opts['force_editor'] = True
   470                 opts['force_editor'] = True
   474             newid, created = rewrite(repo, old, updates, head,
   471             newid, created = rewrite(repo, old, updates, head,
   488         finally:
   485         finally:
   489             wlock.release()
   486             wlock.release()
   490     finally:
   487     finally:
   491         lock.release()
   488         lock.release()
   492 
   489 
   493 
   490 def _commitfiltered(repo, ctx, match):
   494 
   491     """Recommit ctx with changed files not in match. Return the new
       
   492     node identifier, or None if nothing changed.
       
   493     """
       
   494     base = ctx.p1()
       
   495     m, a, r = repo.status(base, ctx)[:3]
       
   496     allfiles = set(m + a + r)
       
   497     files = set(f for f in allfiles if not match(f))
       
   498     if files == allfiles:
       
   499         return None
       
   500 
       
   501     # Filter copies
       
   502     copied = copies.pathcopies(base, ctx)
       
   503     copied = dict((src, dst) for src, dst in copied.iteritems()
       
   504                   if dst in files)
       
   505     def filectxfn(repo, memctx, path):
       
   506         if path not in ctx:
       
   507             raise IOError()
       
   508         fctx = ctx[path]
       
   509         flags = fctx.flags()
       
   510         mctx = context.memfilectx(fctx.path(), fctx.data(),
       
   511                                   islink='l' in flags,
       
   512                                   isexec='x' in flags,
       
   513                                   copied=copied.get(path))
       
   514         return mctx
       
   515 
       
   516     new = context.memctx(repo,
       
   517                          parents=[base.node(), node.nullid],
       
   518                          text=ctx.description(),
       
   519                          files=files,
       
   520                          filectxfn=filectxfn,
       
   521                          user=ctx.user(),
       
   522                          date=ctx.date(),
       
   523                          extra=ctx.extra())
       
   524     # commitctx always create a new revision, no need to check
       
   525     newid = repo.commitctx(new)
       
   526     return newid
       
   527 
       
   528 def _uncommitdirstate(repo, oldctx, match):
       
   529     """Fix the dirstate after switching the working directory from
       
   530     oldctx to a copy of oldctx not containing changed files matched by
       
   531     match.
       
   532     """
       
   533     ctx = repo['.']
       
   534     ds = repo.dirstate
       
   535     copies = dict(ds.copies())
       
   536     m, a, r = repo.status(oldctx.p1(), oldctx, match=match)[:3]
       
   537     for f in m:
       
   538         if ds[f] == 'r':
       
   539             # modified + removed -> removed
       
   540             continue
       
   541         ds.normallookup(f)
       
   542 
       
   543     for f in a:
       
   544         if ds[f] == 'r':
       
   545             # added + removed -> unknown
       
   546             ds.drop(f)
       
   547         elif ds[f] != 'a':
       
   548             ds.add(f)
       
   549 
       
   550     for f in r:
       
   551         if ds[f] == 'a':
       
   552             # removed + added -> normal
       
   553             ds.normallookup(f)
       
   554         elif ds[f] != 'r':
       
   555             ds.remove(f)
       
   556 
       
   557     # Merge old parent and old working dir copies
       
   558     oldcopies = {}
       
   559     for f in (m + a):
       
   560         src = oldctx[f].renamed()
       
   561         if src:
       
   562             oldcopies[f] = src[0]
       
   563     oldcopies.update(copies)
       
   564     copies = dict((dst, oldcopies.get(src, src))
       
   565                   for dst, src in oldcopies.iteritems())
       
   566     # Adjust the dirstate copies
       
   567     for dst, src in copies.iteritems():
       
   568         if (src not in ctx or dst in ctx or ds[dst] != 'a'):
       
   569             src = None
       
   570         ds.copy(src, dst)
       
   571 
       
   572 @command('^uncommit',
       
   573     [('a', 'all', None, _('uncommit all changes when no arguments given')),
       
   574      ] + commands.walkopts,
       
   575     _('[OPTION]... [NAME]'))
       
   576 def uncommit(ui, repo, *pats, **opts):
       
   577     """move changes from parent revision to working directory
       
   578 
       
   579     Changes to selected files in parent revision appear again as
       
   580     uncommitted changed in the working directory. A new revision
       
   581     without selected changes is created, becomes the new parent and
       
   582     obsoletes the previous one.
       
   583 
       
   584     The --include option specify pattern to uncommit
       
   585     The --exclude option specify pattern to keep in the commit
       
   586 
       
   587     Return 0 if changed files are uncommitted.
       
   588     """
       
   589     lock = repo.lock()
       
   590     try:
       
   591         wlock = repo.wlock()
       
   592         try:
       
   593             wctx = repo[None]
       
   594             if len(wctx.parents()) <= 0:
       
   595                 raise util.Abort(_("cannot uncommit null changeset"))
       
   596             if len(wctx.parents()) > 1:
       
   597                 raise util.Abort(_("cannot uncommit while merging"))
       
   598             old = repo['.']
       
   599             if old.phase() == phases.public:
       
   600                 raise util.Abort(_("cannot rewrite immutable changeset"))
       
   601             if len(old.parents()) > 1:
       
   602                 raise util.Abort(_("cannot uncommit merge changeset"))
       
   603             oldphase = old.phase()
       
   604             updatebookmarks = _bookmarksupdater(repo, old.node())
       
   605             # Recommit the filtered changeset
       
   606             newid = None
       
   607             if (pats or opts.get('include') or opts.get('exclude')
       
   608                 or opts.get('all')):
       
   609                 match = scmutil.match(old, pats, opts)
       
   610                 newid = _commitfiltered(repo, old, match)
       
   611             if newid is None:
       
   612                 raise util.Abort(_('nothing to uncommit'))
       
   613             # Move local changes on filtered changeset
       
   614             repo.addobsolete(newid, old.node())
       
   615             phases.retractboundary(repo, oldphase, [newid])
       
   616             repo.dirstate.setparents(newid, node.nullid)
       
   617             _uncommitdirstate(repo, old, match)
       
   618             updatebookmarks(newid)
       
   619             if not repo[newid].files():
       
   620                 ui.warn(_("new changeset is empty\n"))
       
   621                 ui.status(_('(use "hg kill ." to remove it)\n'))
       
   622         finally:
       
   623             wlock.release()
       
   624     finally:
       
   625         lock.release()
   495 
   626 
   496 def commitwrapper(orig, ui, repo, *arg, **kwargs):
   627 def commitwrapper(orig, ui, repo, *arg, **kwargs):
   497     lock = repo.lock()
   628     lock = repo.lock()
   498     try:
   629     try:
   499         obsoleted = kwargs.get('obsolete', [])
   630         obsoleted = kwargs.get('obsolete', [])
   545     except KeyError:
   676     except KeyError:
   546         rebase = None
   677         rebase = None
   547         raise error.Abort(_('evolution extension require rebase extension.'))
   678         raise error.Abort(_('evolution extension require rebase extension.'))
   548 
   679 
   549     entry = extensions.wrapcommand(commands.table, 'commit', commitwrapper)
   680     entry = extensions.wrapcommand(commands.table, 'commit', commitwrapper)
   550     entry[1].append(('o', 'obsolete', [], _("this commit obsolet this revision")))
   681     entry[1].append(('o', 'obsolete', [],
       
   682                      _("make commit obsolete this revision")))
   551     entry = extensions.wrapcommand(commands.table, 'graft', graftwrapper)
   683     entry = extensions.wrapcommand(commands.table, 'graft', graftwrapper)
   552     entry[1].append(('o', 'obsolete', [], _("this graft obsolet this revision")))
   684     entry[1].append(('o', 'obsolete', [],
   553     entry[1].append(('O', 'old-obsolete', False, _("graft result obsolete graft source")))
   685                      _("make graft obsoletes this revision")))
       
   686     entry[1].append(('O', 'old-obsolete', False,
       
   687                      _("make graft obsoletes its source")))
   554 
   688 
   555     # warning about more obsolete
   689     # warning about more obsolete
   556     for cmd in ['commit', 'push', 'pull', 'graft']:
   690     for cmd in ['commit', 'push', 'pull', 'graft', 'phase', 'unbundle']:
   557         entry = extensions.wrapcommand(commands.table, cmd, warnunstable)
   691         entry = extensions.wrapcommand(commands.table, cmd, warnobserrors)
   558     for cmd in ['kill', 'amend']:
   692     for cmd in ['amend', 'kill', 'uncommit']:
   559         entry = extensions.wrapcommand(cmdtable, cmd, warnunstable)
   693         entry = extensions.wrapcommand(cmdtable, cmd, warnobserrors)
   560 
   694 
   561     if rebase is not None:
   695     if rebase is not None:
   562         entry = extensions.wrapcommand(rebase.cmdtable, 'rebase', warnunstable)
   696         entry = extensions.wrapcommand(rebase.cmdtable, 'rebase', warnobserrors)