def metarewrite(repo, old, newbases, commitopts, copypreds=None): """Return (nodeid, created) where nodeid is the identifier of the changeset generated by the rewrite process, and created is True if nodeid was actually created. If created is False, nodeid references a changeset existing before the rewrite call. """ wlock = lock = tr = None try: wlock = repo.wlock() lock = repo.lock() tr = repo.transaction("rewrite") updatebookmarks = bookmarksupdater(repo, old.node()) message = cmdutil.logmessage(repo, commitopts) if not message: message = old.description() user = commitopts.get("user") or old.user() date = commitopts.get("date") or None # old.date() extra = dict(commitopts.get("extra", old.extra())) extra["branch"] = old.branch() preds = [old.node()] mutop = "metaedit" if copypreds: preds.extend(copypreds) mutop = "metaedit-copy" mutinfo = mutation.record(repo, extra, preds, mutop) loginfo = {"predecessors": old.hex(), "mutation": mutop} new = context.metadataonlyctx( repo, old, parents=newbases, text=message, user=user, date=date, extra=extra, loginfo=loginfo, mutinfo=mutinfo, ) if commitopts.get("edit"): new._text = cmdutil.commitforceeditor(repo, new, []) revcount = len(repo) newid = repo.commitctx(new) new = repo[newid] created = len(repo) != revcount updatebookmarks(newid) tr.close() return newid, created finally: lockmod.release(tr, lock, wlock)
def commitextra(ui, repo, *pats, **opts): """make a commit with extra fields""" fields = opts.get("field") extras = {} for field in fields: k, v = field.split("=", 1) extras[k] = v message = cmdutil.logmessage(repo, opts) repo.commit( message, opts.get("user"), opts.get("date"), match=scmutil.match(repo[None], pats, opts), extra=extras, ) return 0
def rewrite(repo, old, updates, head, newbases, commitopts, mutop=None): """Return (nodeid, created) where nodeid is the identifier of the changeset generated by the rewrite process, and created is True if nodeid was actually created. If created is False, nodeid references a changeset existing before the rewrite call. """ wlock = lock = tr = None try: wlock = repo.wlock() lock = repo.lock() tr = repo.transaction("rewrite") if len(old.parents()) > 1: # XXX remove this unnecessary limitation. raise error.Abort(_("cannot amend merge changesets")) base = old.p1() updatebookmarks = bookmarksupdater(repo, [old.node()] + [u.node() for u in updates]) # commit a new version of the old changeset, including the update # collect all files which might be affected files = set(old.files()) for u in updates: files.update(u.files()) # Recompute copies (avoid recording a -> b -> a) copied = copies.pathcopies(base, head) # prune files which were reverted by the updates def samefile(f): if f in head.manifest(): a = head.filectx(f) if f in base.manifest(): b = base.filectx(f) return a.data() == b.data() and a.flags() == b.flags() else: return False else: return f not in base.manifest() files = [f for f in files if not samefile(f)] # commit version of these files as defined by head headmf = head.manifest() def filectxfn(repo, ctx, path): if path in headmf: fctx = head[path] flags = fctx.flags() mctx = context.memfilectx( repo, ctx, fctx.path(), fctx.data(), islink="l" in flags, isexec="x" in flags, copied=copied.get(path), ) return mctx return None message = cmdutil.logmessage(repo, commitopts) if not message: message = old.description() user = commitopts.get("user") or old.user() # TODO: In case not date is given, we should take the old commit date # if we are working one one changeset or mimic the fold behavior about # date date = commitopts.get("date") or None extra = dict(commitopts.get("extra", old.extra())) extra["branch"] = head.branch() mutation.record(repo, extra, [c.node() for c in updates], mutop) loginfo = { "predecessors": " ".join(c.hex() for c in updates), "mutation": mutop, } new = context.memctx( repo, parents=newbases, text=message, files=files, filectxfn=filectxfn, user=user, date=date, extra=extra, loginfo=loginfo, ) if commitopts.get("edit"): new._text = cmdutil.commitforceeditor(repo, new, []) revcount = len(repo) newid = repo.commitctx(new) new = repo[newid] created = len(repo) != revcount updatebookmarks(newid) tr.close() return newid, created finally: lockmod.release(tr, lock, wlock)
def amend(ui, repo, *pats, **opts): """save pending changes to the current commit Replaces your current commit with a new commit that contains the contents of the original commit, plus any pending changes. By default, all pending changes (in other words, those reported by 'hg status') are committed. To commit only some of your changes, you can: - Specify an exact list of files for which you want changes committed. - Use the -I or -X flags to pattern match file names to exclude or include by using a fileset. See 'hg help filesets' for more information. - Specify the --interactive flag to open a UI that will enable you to select individual insertions or deletions. By default, hg amend reuses your existing commit message and does not prompt you for changes. To change your commit message, you can: - Specify --edit / -e to open your configured editor to update the existing commit message. - Specify --message / -m to replace the entire commit message, including any commit template fields, with a string that you specify. .. note:: Specifying -m overwrites all information in the commit message, including information specified as part of a pre-loaded commit template. For example, any information associating this commit with a code review system will be lost and might result in breakages. When you amend a commit that has descendants, those descendants are rebased on top of the amended version of the commit, unless doing so would result in merge conflicts. If this happens, run 'hg restack' to manually trigger the rebase so that you can go through the merge conflict resolution process. """ # 'rebase' is a tristate option: None=auto, True=force, False=disable rebase = opts.get("rebase") to = opts.get("to") if rebase and _histediting(repo): # if a histedit is in flight, it's dangerous to remove old commits hint = _("during histedit, use amend without --rebase") raise error.Abort("histedit in progress", hint=hint) badflags = [flag for flag in ["rebase", "fixup"] if opts.get(flag, None)] if opts.get("interactive") and badflags: raise error.Abort( _("--interactive and --%s are mutually exclusive") % badflags[0]) fixup = opts.get("fixup") badtoflags = [ "rebase", "fixup", "addremove", "edit", "interactive", "include", "exclude", "message", "logfile", "date", "user", "no-move-detection", "stack", "template", ] if to and any(opts.get(flag, None) for flag in badtoflags): raise error.Abort(_("--to cannot be used with any other options")) if fixup: ui.warn( _("warning: --fixup is deprecated and WILL BE REMOVED. use 'hg restack' instead.\n" )) fixupamend(ui, repo) return if to: amendtocommit(ui, repo, to) return old = repo["."] if old.phase() == phases.public: raise error.Abort(_("cannot amend public changesets")) if len(repo[None].parents()) > 1: raise error.Abort(_("cannot amend while merging")) haschildren = len(old.children()) > 0 opts["message"] = cmdutil.logmessage(repo, opts) # Avoid further processing of any logfile. If such a file existed, its # contents have been copied into opts['message'] by logmessage opts["logfile"] = "" if not opts.get("noeditmessage") and not opts.get("message"): opts["message"] = old.description() commitdate = opts.get("date") if not commitdate: if ui.config("amend", "date") == "implicitupdate": commitdate = "now" else: commitdate = old.date() oldbookmarks = old.bookmarks() tr = None wlock = None lock = None try: wlock = repo.wlock() lock = repo.lock() if opts.get("interactive"): # Strip the interactive flag to avoid infinite recursive loop opts.pop("interactive") cmdutil.dorecord(ui, repo, amend, None, False, cmdutil.recordfilter, *pats, **opts) return else: node = cmdutil.amend(ui, repo, old, {}, pats, opts) if node == old.node(): ui.status(_("nothing changed\n")) return 1 conf = ui.config("amend", "autorestack", RESTACK_DEFAULT) noconflict = None # RESTACK_NO_CONFLICT requires IMM. if conf == RESTACK_NO_CONFLICT and not ui.config( "rebase", "experimental.inmemory", False): conf = RESTACK_DEFAULT # If they explicitly disabled the old behavior, disable the new behavior # too, for now. # internal config: commands.amend.autorebase if ui.configbool("commands", "amend.autorebase") is False: # In the future we'll add a nag message here. conf = RESTACK_NEVER if conf not in RESTACK_VALUES: ui.warn( _('invalid amend.autorestack config of "%s"; falling back to %s\n' ) % (conf, RESTACK_DEFAULT)) conf = RESTACK_DEFAULT if haschildren and rebase is None and not _histediting(repo): if conf == RESTACK_ALWAYS: rebase = True elif conf == RESTACK_NO_CONFLICT: if repo[None].dirty(): # For now, only restack if the WC is clean (t31742174). ui.status( _("not restacking because working copy is dirty\n")) rebase = False else: # internal config: amend.autorestackmsg msg = ui.config( "amend", "autorestackmsg", _("restacking children automatically (unless they conflict)" ), ) if msg: ui.status("%s\n" % msg) rebase = True noconflict = True elif conf == RESTACK_ONLY_TRIVIAL: newcommit = repo[node] # If the rebase did not change the manifest and the # working copy is clean, force the children to be # restacked. rebase = (old.manifestnode() == newcommit.manifestnode() and not repo[None].dirty()) if rebase: hintutil.trigger("amend-autorebase") else: rebase = False if haschildren and not rebase and not _histediting(repo): hintutil.trigger("amend-restack", old.node()) changes = [] # move old bookmarks to new node for bm in oldbookmarks: changes.append((bm, node)) tr = repo.transaction("fixupamend") repo._bookmarks.applychanges(repo, tr, changes) tr.close() if rebase and haschildren: noconflictmsg = _( "restacking would create conflicts (%s in %s), so you must run it manually\n(run `hg restack` manually to restack this commit's children)" ) revs = [ c.hex() for c in repo.set("(%n::)-%n", old.node(), old.node()) ] with ui.configoverride({ ("rebase", "noconflictmsg"): noconflictmsg }): # Note: this has effects on linearizing (old:: - old). That can # fail. If that fails, it might make sense to try a plain # rebase -s (old:: - old) -d new. restack.restack(ui, repo, rev=revs, noconflict=noconflict) showtemplate(ui, repo, repo[node], **opts) finally: lockmod.release(wlock, lock, tr)