def absorb(ui, repo, stack=None, targetctx=None, pats=None, opts=None): """pick fixup chunks from targetctx, apply them to stack. if targetctx is None, the working copy context will be used. if stack is None, the current draft stack will be used. return fixupstate. """ if stack is None: limit = ui.configint("absorb", "maxstacksize", 50) stack = getdraftstack(repo["."], limit) if limit and len(stack) >= limit: ui.warn( _("absorb: only the recent %d changesets will " "be analysed\n") % limit) if not stack: raise error.Abort(_("no changeset to change")) if targetctx is None: # default to working copy targetctx = repo[None] if pats is None: pats = () if opts is None: opts = {} state = fixupstate(stack, ui=ui, opts=opts) matcher = scmutil.match(targetctx, pats, opts) if opts.get("interactive"): diff = patch.diff(repo, stack[-1], targetctx, matcher) origchunks = patch.parsepatch(diff) chunks = cmdutil.recordfilter(ui, origchunks)[0] targetctx = overlaydiffcontext(stack[-1], chunks) fm = None if not (ui.quiet and opts.get("apply_changes")) and not opts.get("edit_lines"): fm = ui.formatter("absorb", opts) state.diffwith(targetctx, matcher, fm) if fm is not None and state.ctxaffected: fm.startitem() count = len(state.ctxaffected) fm.write( "count", _n("\n%d changeset affected\n", "\n%d changesets affected\n", count), count, ) fm.data(linetype="summary") for ctx in reversed(stack): if ctx not in state.ctxaffected: continue fm.startitem() fm.context(ctx=ctx) fm.data(linetype="changeset") fm.write("node", "%-7.7s ", ctx.hex(), label="absorb.node") descfirstline = ctx.description().splitlines()[0] fm.write("descfirstline", "%s\n", descfirstline, label="absorb.description") fm.end() if not opts.get("edit_lines") and not any( f.fixups for f in state.fixupmap.values()): ui.write(_("nothing to absorb\n")) elif not opts.get("dry_run"): if not opts.get("apply_changes"): if ui.promptchoice("apply changes (yn)? $$ &Yes $$ &No", default=0): raise error.Abort(_("absorb cancelled\n")) state.apply() state.commit() state.printchunkstats() return state
def recordfilter(ui, headers, operation=None): if ui.interface("chunkselector") != "editor": return originalrecordfilter(ui, headers, operation) # construct diff string from headers if len(headers) == 0: return [], {} patch = stringio() patch.write(crecordmod.diffhelptext) specials = {} for header in headers: patch.write("#\n") if header.special(): # this is useful for special changes, we are able to get away with # only including the parts of headers that offer useful info specials[header.filename()] = header for h in header.header: if h.startswith("index "): # starting at 'index', the headers for binary files tend to # stop offering useful info for the viewer patch.write( _("""\ # this modifies a binary file (all or nothing) """)) break if not h.startswith("diff "): # For specials, we only care about the filename header. # The rest can be displayed as comments patch.write("# ") patch.write(h) else: header.write(patch) for hunk in header.hunks: hunk.write(patch) patcheditor = ui.config("ui", "editor.chunkselector") if patcheditor is not None: override = {("ui", "editor"): patcheditor} else: override = {} with ui.configoverride(override): patch = ui.edit(patch.getvalue(), "", action=(operation or "edit")) # remove comments from patch # if there's an empty line, add a space to it patch = [(line if len(line) > 0 else " ") + "\n" for line in patch.splitlines() if not line.startswith("#")] headers = patchmod.parsepatch(patch) applied = {} for h in headers: if h.filename() in specials: h = specials[h.filename()] applied[h.filename()] = [h] + h.hunks return ( sum([i for i in applied.itervalues() if i[0].special() or len(i) > 1], []), {}, )