hgext/absorb.py
changeset 50869 e0cae2b44191
parent 49960 7a8bfc05b691
child 50928 d718eddf01d9
equal deleted inserted replaced
50867:788113f056d4 50869:e0cae2b44191
   297         2. (optionally), present self.fixups to the user, or change it
   297         2. (optionally), present self.fixups to the user, or change it
   298         3. call apply, to apply changes
   298         3. call apply, to apply changes
   299         4. read results from "finalcontents", or call getfinalcontent
   299         4. read results from "finalcontents", or call getfinalcontent
   300     """
   300     """
   301 
   301 
   302     def __init__(self, fctxs, path, ui=None, opts=None):
   302     def __init__(self, fctxs, path, ui=None, **opts):
   303         """([fctx], ui or None) -> None
   303         """([fctx], ui or None) -> None
   304 
   304 
   305         fctxs should be linear, and sorted by topo order - oldest first.
   305         fctxs should be linear, and sorted by topo order - oldest first.
   306         fctxs[0] will be considered as "immutable" and will not be changed.
   306         fctxs[0] will be considered as "immutable" and will not be changed.
   307         """
   307         """
   308         self.fctxs = fctxs
   308         self.fctxs = fctxs
   309         self.path = path
   309         self.path = path
   310         self.ui = ui or nullui()
   310         self.ui = ui or nullui()
   311         self.opts = opts or {}
   311         self.opts = opts
   312 
   312 
   313         # following fields are built from fctxs. they exist for perf reason
   313         # following fields are built from fctxs. they exist for perf reason
   314         self.contents = [f.data() for f in fctxs]
   314         self.contents = [f.data() for f in fctxs]
   315         self.contentlines = pycompat.maplist(mdiff.splitnewlines, self.contents)
   315         self.contentlines = pycompat.maplist(mdiff.splitnewlines, self.contents)
   316         self.linelog = self._buildlinelog()
   316         self.linelog = self._buildlinelog()
   373                 self.ui.write(
   373                 self.ui.write(
   374                     _(b'%s: chunk %d:%d -> %d lines\n')
   374                     _(b'%s: chunk %d:%d -> %d lines\n')
   375                     % (short(self.fctxs[idx].node()), a1, a2, len(blines))
   375                     % (short(self.fctxs[idx].node()), a1, a2, len(blines))
   376                 )
   376                 )
   377             self.linelog.replacelines(rev, a1, a2, b1, b2)
   377             self.linelog.replacelines(rev, a1, a2, b1, b2)
   378         if self.opts.get(b'edit_lines', False):
   378         if self.opts.get('edit_lines', False):
   379             self.finalcontents = self._checkoutlinelogwithedits()
   379             self.finalcontents = self._checkoutlinelogwithedits()
   380         else:
   380         else:
   381             self.finalcontents = self._checkoutlinelog()
   381             self.finalcontents = self._checkoutlinelog()
   382 
   382 
   383     def getfinalcontent(self, fctx):
   383     def getfinalcontent(self, fctx):
   666         2. (optionally), present fixups to the user, or edit fixups
   666         2. (optionally), present fixups to the user, or edit fixups
   667         3. call apply, to apply changes to memory
   667         3. call apply, to apply changes to memory
   668         4. call commit, to commit changes to hg database
   668         4. call commit, to commit changes to hg database
   669     """
   669     """
   670 
   670 
   671     def __init__(self, stack, ui=None, opts=None):
   671     def __init__(self, stack, ui=None, **opts):
   672         """([ctx], ui or None) -> None
   672         """([ctx], ui or None) -> None
   673 
   673 
   674         stack: should be linear, and sorted by topo order - oldest first.
   674         stack: should be linear, and sorted by topo order - oldest first.
   675         all commits in stack are considered mutable.
   675         all commits in stack are considered mutable.
   676         """
   676         """
   677         assert stack
   677         assert stack
   678         self.ui = ui or nullui()
   678         self.ui = ui or nullui()
   679         self.opts = opts or {}
   679         self.opts = opts
   680         self.stack = stack
   680         self.stack = stack
   681         self.repo = stack[-1].repo().unfiltered()
   681         self.repo = stack[-1].repo().unfiltered()
   682 
   682 
   683         # following fields will be filled later
   683         # following fields will be filled later
   684         self.paths = []  # [str]
   684         self.paths = []  # [str]
   694         # only care about modified files
   694         # only care about modified files
   695         self.status = self.stack[-1].status(targetctx, match)
   695         self.status = self.stack[-1].status(targetctx, match)
   696         self.paths = []
   696         self.paths = []
   697         # but if --edit-lines is used, the user may want to edit files
   697         # but if --edit-lines is used, the user may want to edit files
   698         # even if they are not modified
   698         # even if they are not modified
   699         editopt = self.opts.get(b'edit_lines')
   699         editopt = self.opts.get('edit_lines')
   700         if not self.status.modified and editopt and match:
   700         if not self.status.modified and editopt and match:
   701             interestingpaths = match.files()
   701             interestingpaths = match.files()
   702         else:
   702         else:
   703             interestingpaths = self.status.modified
   703             interestingpaths = self.status.modified
   704         # prepare the filefixupstate
   704         # prepare the filefixupstate
   718                 continue
   718                 continue
   719             if targetfctx.data() == fctxs[-1].data() and not editopt:
   719             if targetfctx.data() == fctxs[-1].data() and not editopt:
   720                 continue
   720                 continue
   721             seenfctxs.update(fctxs[1:])
   721             seenfctxs.update(fctxs[1:])
   722             self.fctxmap[path] = ctx2fctx
   722             self.fctxmap[path] = ctx2fctx
   723             fstate = filefixupstate(fctxs, path, ui=self.ui, opts=self.opts)
   723             fstate = filefixupstate(fctxs, path, ui=self.ui, **self.opts)
   724             if fm is not None:
   724             if fm is not None:
   725                 fm.startitem()
   725                 fm.startitem()
   726                 fm.plain(b'showing changes for ')
   726                 fm.plain(b'showing changes for ')
   727                 fm.write(b'path', b'%s\n', path, label=b'absorb.path')
   727                 fm.write(b'path', b'%s\n', path, label=b'absorb.path')
   728                 fm.data(linetype=b'path')
   728                 fm.data(linetype=b'path')
  1007             lines[a1:a2] = blines
  1007             lines[a1:a2] = blines
  1008         memworkingcopy[path] = b''.join(lines)
  1008         memworkingcopy[path] = b''.join(lines)
  1009     return overlaycontext(memworkingcopy, ctx)
  1009     return overlaycontext(memworkingcopy, ctx)
  1010 
  1010 
  1011 
  1011 
  1012 def absorb(ui, repo, stack=None, targetctx=None, pats=None, opts=None):
  1012 def absorb(ui, repo, stack=None, targetctx=None, pats=None, **opts):
  1013     """pick fixup chunks from targetctx, apply them to stack.
  1013     """pick fixup chunks from targetctx, apply them to stack.
  1014 
  1014 
  1015     if targetctx is None, the working copy context will be used.
  1015     if targetctx is None, the working copy context will be used.
  1016     if stack is None, the current draft stack will be used.
  1016     if stack is None, the current draft stack will be used.
  1017     return fixupstate.
  1017     return fixupstate.
  1034         raise error.InputError(_(b'no mutable changeset to change'))
  1034         raise error.InputError(_(b'no mutable changeset to change'))
  1035     if targetctx is None:  # default to working copy
  1035     if targetctx is None:  # default to working copy
  1036         targetctx = repo[None]
  1036         targetctx = repo[None]
  1037     if pats is None:
  1037     if pats is None:
  1038         pats = ()
  1038         pats = ()
  1039     if opts is None:
  1039 
  1040         opts = {}
  1040     state = fixupstate(stack, ui=ui, **opts)
  1041     state = fixupstate(stack, ui=ui, opts=opts)
  1041     matcher = scmutil.match(targetctx, pats, pycompat.byteskwargs(opts))
  1042     matcher = scmutil.match(targetctx, pats, opts)
  1042     if opts.get('interactive'):
  1043     if opts.get(b'interactive'):
       
  1044         diff = patch.diff(repo, stack[-1].node(), targetctx.node(), matcher)
  1043         diff = patch.diff(repo, stack[-1].node(), targetctx.node(), matcher)
  1045         origchunks = patch.parsepatch(diff)
  1044         origchunks = patch.parsepatch(diff)
  1046         chunks = cmdutil.recordfilter(ui, origchunks, matcher)[0]
  1045         chunks = cmdutil.recordfilter(ui, origchunks, matcher)[0]
  1047         targetctx = overlaydiffcontext(stack[-1], chunks)
  1046         targetctx = overlaydiffcontext(stack[-1], chunks)
  1048     if opts.get(b'edit_lines'):
  1047     if opts.get('edit_lines'):
  1049         # If we're going to open the editor, don't ask the user to confirm
  1048         # If we're going to open the editor, don't ask the user to confirm
  1050         # first
  1049         # first
  1051         opts[b'apply_changes'] = True
  1050         opts['apply_changes'] = True
  1052     fm = None
  1051     fm = None
  1053     if opts.get(b'print_changes') or not opts.get(b'apply_changes'):
  1052     if opts.get('print_changes') or not opts.get('apply_changes'):
  1054         fm = ui.formatter(b'absorb', opts)
  1053         fm = ui.formatter(b'absorb', pycompat.byteskwargs(opts))
  1055     state.diffwith(targetctx, matcher, fm)
  1054     state.diffwith(targetctx, matcher, fm)
  1056     if fm is not None:
  1055     if fm is not None:
  1057         fm.startitem()
  1056         fm.startitem()
  1058         fm.write(
  1057         fm.write(
  1059             b"count", b"\n%d changesets affected\n", len(state.ctxaffected)
  1058             b"count", b"\n%d changesets affected\n", len(state.ctxaffected)
  1072                 b'%s\n',
  1071                 b'%s\n',
  1073                 descfirstline,
  1072                 descfirstline,
  1074                 label=b'absorb.description',
  1073                 label=b'absorb.description',
  1075             )
  1074             )
  1076         fm.end()
  1075         fm.end()
  1077     if not opts.get(b'dry_run'):
  1076     if not opts.get('dry_run'):
  1078         if (
  1077         if (
  1079             not opts.get(b'apply_changes')
  1078             not opts.get('apply_changes')
  1080             and state.ctxaffected
  1079             and state.ctxaffected
  1081             and ui.promptchoice(
  1080             and ui.promptchoice(
  1082                 b"apply changes (y/N)? $$ &Yes $$ &No", default=1
  1081                 b"apply changes (y/N)? $$ &Yes $$ &No", default=1
  1083             )
  1082             )
  1084         ):
  1083         ):
  1152     to the correct place, run :hg:`absorb -a` to apply the changes
  1151     to the correct place, run :hg:`absorb -a` to apply the changes
  1153     immediately.
  1152     immediately.
  1154 
  1153 
  1155     Returns 0 on success, 1 if all chunks were ignored and nothing amended.
  1154     Returns 0 on success, 1 if all chunks were ignored and nothing amended.
  1156     """
  1155     """
  1157     opts = pycompat.byteskwargs(opts)
       
  1158 
       
  1159     with repo.wlock(), repo.lock():
  1156     with repo.wlock(), repo.lock():
  1160         if not opts[b'dry_run']:
  1157         if not opts['dry_run']:
  1161             cmdutil.checkunfinished(repo)
  1158             cmdutil.checkunfinished(repo)
  1162 
  1159 
  1163         state = absorb(ui, repo, pats=pats, opts=opts)
  1160         state = absorb(ui, repo, pats=pats, **opts)
  1164         if sum(s[0] for s in state.chunkstats.values()) == 0:
  1161         if sum(s[0] for s in state.chunkstats.values()) == 0:
  1165             return 1
  1162             return 1