hgext/uncommit.py
changeset 43076 2372284d9457
parent 42974 576fd1c8b20b
child 43077 687b865b95ad
equal deleted inserted replaced
43075:57875cf423c9 43076:2372284d9457
    40 command = registrar.command(cmdtable)
    40 command = registrar.command(cmdtable)
    41 
    41 
    42 configtable = {}
    42 configtable = {}
    43 configitem = registrar.configitem(configtable)
    43 configitem = registrar.configitem(configtable)
    44 
    44 
    45 configitem('experimental', 'uncommitondirtywdir',
    45 configitem(
    46     default=False,
    46     'experimental', 'uncommitondirtywdir', default=False,
    47 )
    47 )
    48 configitem('experimental', 'uncommit.keep',
    48 configitem(
    49     default=False,
    49     'experimental', 'uncommit.keep', default=False,
    50 )
    50 )
    51 
    51 
    52 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
    52 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
    53 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
    53 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
    54 # be specifying the version(s) of Mercurial they are tested with, or
    54 # be specifying the version(s) of Mercurial they are tested with, or
    55 # leave the attribute unspecified.
    55 # leave the attribute unspecified.
    56 testedwith = 'ships-with-hg-core'
    56 testedwith = 'ships-with-hg-core'
    57 
    57 
    58 def _commitfiltered(repo, ctx, match, keepcommit, message=None, user=None,
    58 
    59                     date=None):
    59 def _commitfiltered(
       
    60     repo, ctx, match, keepcommit, message=None, user=None, date=None
       
    61 ):
    60     """Recommit ctx with changed files not in match. Return the new
    62     """Recommit ctx with changed files not in match. Return the new
    61     node identifier, or None if nothing changed.
    63     node identifier, or None if nothing changed.
    62     """
    64     """
    63     base = ctx.p1()
    65     base = ctx.p1()
    64     # ctx
    66     # ctx
    71 
    73 
    72     # return the p1 so that we don't create an obsmarker later
    74     # return the p1 so that we don't create an obsmarker later
    73     if not keepcommit:
    75     if not keepcommit:
    74         return ctx.p1().node()
    76         return ctx.p1().node()
    75 
    77 
    76     files = (initialfiles - exclude)
    78     files = initialfiles - exclude
    77     # Filter copies
    79     # Filter copies
    78     copied = copiesmod.pathcopies(base, ctx)
    80     copied = copiesmod.pathcopies(base, ctx)
    79     copied = dict((dst, src) for dst, src in copied.iteritems()
    81     copied = dict((dst, src) for dst, src in copied.iteritems() if dst in files)
    80                   if dst in files)
    82 
    81     def filectxfn(repo, memctx, path, contentctx=ctx, redirect=()):
    83     def filectxfn(repo, memctx, path, contentctx=ctx, redirect=()):
    82         if path not in contentctx:
    84         if path not in contentctx:
    83             return None
    85             return None
    84         fctx = contentctx[path]
    86         fctx = contentctx[path]
    85         mctx = context.memfilectx(repo, memctx, fctx.path(), fctx.data(),
    87         mctx = context.memfilectx(
    86                                   fctx.islink(),
    88             repo,
    87                                   fctx.isexec(),
    89             memctx,
    88                                   copysource=copied.get(path))
    90             fctx.path(),
       
    91             fctx.data(),
       
    92             fctx.islink(),
       
    93             fctx.isexec(),
       
    94             copysource=copied.get(path),
       
    95         )
    89         return mctx
    96         return mctx
    90 
    97 
    91     if not files:
    98     if not files:
    92         repo.ui.status(_("note: keeping empty commit\n"))
    99         repo.ui.status(_("note: keeping empty commit\n"))
    93 
   100 
    96     if not user:
   103     if not user:
    97         user = ctx.user()
   104         user = ctx.user()
    98     if not date:
   105     if not date:
    99         date = ctx.date()
   106         date = ctx.date()
   100 
   107 
   101     new = context.memctx(repo,
   108     new = context.memctx(
   102                          parents=[base.node(), node.nullid],
   109         repo,
   103                          text=message,
   110         parents=[base.node(), node.nullid],
   104                          files=files,
   111         text=message,
   105                          filectxfn=filectxfn,
   112         files=files,
   106                          user=user,
   113         filectxfn=filectxfn,
   107                          date=date,
   114         user=user,
   108                          extra=ctx.extra())
   115         date=date,
       
   116         extra=ctx.extra(),
       
   117     )
   109     return repo.commitctx(new)
   118     return repo.commitctx(new)
   110 
   119 
   111 @command('uncommit',
   120 
   112     [('', 'keep', None, _('allow an empty commit after uncommitting')),
   121 @command(
   113      ('', 'allow-dirty-working-copy', False,
   122     'uncommit',
   114     _('allow uncommit with outstanding changes')),
   123     [
   115      (b'n', b'note', b'', _(b'store a note on uncommit'), _(b'TEXT'))
   124         ('', 'keep', None, _('allow an empty commit after uncommitting')),
   116     ] + commands.walkopts + commands.commitopts + commands.commitopts2
   125         (
       
   126             '',
       
   127             'allow-dirty-working-copy',
       
   128             False,
       
   129             _('allow uncommit with outstanding changes'),
       
   130         ),
       
   131         (b'n', b'note', b'', _(b'store a note on uncommit'), _(b'TEXT')),
       
   132     ]
       
   133     + commands.walkopts
       
   134     + commands.commitopts
       
   135     + commands.commitopts2
   117     + commands.commitopts3,
   136     + commands.commitopts3,
   118     _('[OPTION]... [FILE]...'),
   137     _('[OPTION]... [FILE]...'),
   119     helpcategory=command.CATEGORY_CHANGE_MANAGEMENT)
   138     helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
       
   139 )
   120 def uncommit(ui, repo, *pats, **opts):
   140 def uncommit(ui, repo, *pats, **opts):
   121     """uncommit part or all of a local changeset
   141     """uncommit part or all of a local changeset
   122 
   142 
   123     This command undoes the effect of a local commit, returning the affected
   143     This command undoes the effect of a local commit, returning the affected
   124     files to their uncommitted state. This means that files modified or
   144     files to their uncommitted state. This means that files modified or
   135 
   155 
   136     with repo.wlock(), repo.lock():
   156     with repo.wlock(), repo.lock():
   137 
   157 
   138         m, a, r, d = repo.status()[:4]
   158         m, a, r, d = repo.status()[:4]
   139         isdirtypath = any(set(m + a + r + d) & set(pats))
   159         isdirtypath = any(set(m + a + r + d) & set(pats))
   140         allowdirtywcopy = (opts['allow_dirty_working_copy'] or
   160         allowdirtywcopy = opts[
   141                     repo.ui.configbool('experimental', 'uncommitondirtywdir'))
   161             'allow_dirty_working_copy'
       
   162         ] or repo.ui.configbool('experimental', 'uncommitondirtywdir')
   142         if not allowdirtywcopy and (not pats or isdirtypath):
   163         if not allowdirtywcopy and (not pats or isdirtypath):
   143             cmdutil.bailifchanged(repo, hint=_('requires '
   164             cmdutil.bailifchanged(
   144                                 '--allow-dirty-working-copy to uncommit'))
   165                 repo,
       
   166                 hint=_('requires ' '--allow-dirty-working-copy to uncommit'),
       
   167             )
   145         old = repo['.']
   168         old = repo['.']
   146         rewriteutil.precheck(repo, [old.rev()], 'uncommit')
   169         rewriteutil.precheck(repo, [old.rev()], 'uncommit')
   147         if len(old.parents()) > 1:
   170         if len(old.parents()) > 1:
   148             raise error.Abort(_("cannot uncommit merge changeset"))
   171             raise error.Abort(_("cannot uncommit merge changeset"))
   149 
   172 
   162             if badfiles:
   185             if badfiles:
   163                 badfiles -= {f for f in util.dirs(eligible)}
   186                 badfiles -= {f for f in util.dirs(eligible)}
   164 
   187 
   165             for f in sorted(badfiles):
   188             for f in sorted(badfiles):
   166                 if f in s.clean:
   189                 if f in s.clean:
   167                     hint = _(b"file was not changed in working directory "
   190                     hint = _(
   168                              b"parent")
   191                         b"file was not changed in working directory " b"parent"
       
   192                     )
   169                 elif repo.wvfs.exists(f):
   193                 elif repo.wvfs.exists(f):
   170                     hint = _(b"file was untracked in working directory parent")
   194                     hint = _(b"file was untracked in working directory parent")
   171                 else:
   195                 else:
   172                     hint = _(b"file does not exist")
   196                     hint = _(b"file does not exist")
   173 
   197 
   174                 raise error.Abort(_(b'cannot uncommit "%s"')
   198                 raise error.Abort(
   175                                   % scmutil.getuipathfn(repo)(f), hint=hint)
   199                     _(b'cannot uncommit "%s"') % scmutil.getuipathfn(repo)(f),
       
   200                     hint=hint,
       
   201                 )
   176 
   202 
   177         with repo.transaction('uncommit'):
   203         with repo.transaction('uncommit'):
   178             if not (opts[b'message'] or opts[b'logfile']):
   204             if not (opts[b'message'] or opts[b'logfile']):
   179                 opts[b'message'] = old.description()
   205                 opts[b'message'] = old.description()
   180             message = cmdutil.logmessage(ui, opts)
   206             message = cmdutil.logmessage(ui, opts)
   183             if not keepcommit:
   209             if not keepcommit:
   184                 if opts.get('keep') is not None:
   210                 if opts.get('keep') is not None:
   185                     keepcommit = opts.get('keep')
   211                     keepcommit = opts.get('keep')
   186                 else:
   212                 else:
   187                     keepcommit = ui.configbool('experimental', 'uncommit.keep')
   213                     keepcommit = ui.configbool('experimental', 'uncommit.keep')
   188             newid = _commitfiltered(repo, old, match, keepcommit,
   214             newid = _commitfiltered(
   189                                     message=message, user=opts.get(b'user'),
   215                 repo,
   190                                     date=opts.get(b'date'))
   216                 old,
       
   217                 match,
       
   218                 keepcommit,
       
   219                 message=message,
       
   220                 user=opts.get(b'user'),
       
   221                 date=opts.get(b'date'),
       
   222             )
   191             if newid is None:
   223             if newid is None:
   192                 ui.status(_("nothing to uncommit\n"))
   224                 ui.status(_("nothing to uncommit\n"))
   193                 return 1
   225                 return 1
   194 
   226 
   195             mapping = {}
   227             mapping = {}
   203             with repo.dirstate.parentchange():
   235             with repo.dirstate.parentchange():
   204                 scmutil.movedirstate(repo, repo[newid], match)
   236                 scmutil.movedirstate(repo, repo[newid], match)
   205 
   237 
   206             scmutil.cleanupnodes(repo, mapping, 'uncommit', fixphase=True)
   238             scmutil.cleanupnodes(repo, mapping, 'uncommit', fixphase=True)
   207 
   239 
       
   240 
   208 def predecessormarkers(ctx):
   241 def predecessormarkers(ctx):
   209     """yields the obsolete markers marking the given changeset as a successor"""
   242     """yields the obsolete markers marking the given changeset as a successor"""
   210     for data in ctx.repo().obsstore.predecessors.get(ctx.node(), ()):
   243     for data in ctx.repo().obsstore.predecessors.get(ctx.node(), ()):
   211         yield obsutil.marker(ctx.repo(), data)
   244         yield obsutil.marker(ctx.repo(), data)
   212 
   245 
   213 @command('unamend', [], helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
   246 
   214          helpbasic=True)
   247 @command(
       
   248     'unamend',
       
   249     [],
       
   250     helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
       
   251     helpbasic=True,
       
   252 )
   215 def unamend(ui, repo, **opts):
   253 def unamend(ui, repo, **opts):
   216     """undo the most recent amend operation on a current changeset
   254     """undo the most recent amend operation on a current changeset
   217 
   255 
   218     This command will roll back to the previous version of a changeset,
   256     This command will roll back to the previous version of a changeset,
   219     leaving working directory in state in which it was before running
   257     leaving working directory in state in which it was before running
   248                 return predctx.filectx(path)
   286                 return predctx.filectx(path)
   249             except KeyError:
   287             except KeyError:
   250                 return None
   288                 return None
   251 
   289 
   252         # Make a new commit same as predctx
   290         # Make a new commit same as predctx
   253         newctx = context.memctx(repo,
   291         newctx = context.memctx(
   254                                 parents=(predctx.p1(), predctx.p2()),
   292             repo,
   255                                 text=predctx.description(),
   293             parents=(predctx.p1(), predctx.p2()),
   256                                 files=predctx.files(),
   294             text=predctx.description(),
   257                                 filectxfn=filectxfn,
   295             files=predctx.files(),
   258                                 user=predctx.user(),
   296             filectxfn=filectxfn,
   259                                 date=predctx.date(),
   297             user=predctx.user(),
   260                                 extra=extras)
   298             date=predctx.date(),
       
   299             extra=extras,
       
   300         )
   261         newprednode = repo.commitctx(newctx)
   301         newprednode = repo.commitctx(newctx)
   262         newpredctx = repo[newprednode]
   302         newpredctx = repo[newprednode]
   263         dirstate = repo.dirstate
   303         dirstate = repo.dirstate
   264 
   304 
   265         with dirstate.parentchange():
   305         with dirstate.parentchange():