def _docreatecmd(ui, repo, pats, opts): wctx = repo[None] parents = wctx.parents() if len(parents) > 1: raise error.Abort(_("cannot shelve while merging")) parent = parents[0] origbranch = wctx.branch() if parent.node() != nodemod.nullid: desc = "shelve changes to: %s" % parent.description().split("\n", 1)[0] else: desc = "(changes in empty repository)" if not opts.get("message"): opts["message"] = desc activebookmark = None try: with repo.lock(), repo.transaction("commit", report=None): interactive = opts.get("interactive", False) includeunknown = opts.get( "unknown", False) and not opts.get("addremove", False) name = getshelvename(repo, parent, opts) activebookmark = _backupactivebookmark(repo) extra = {} if includeunknown: _includeunknownfiles(repo, pats, opts, extra) if _iswctxonnewbranch(repo) and not _isbareshelve(pats, opts): # In non-bare shelve we don't store newly created branch # at bundled commit repo.dirstate.setbranch(repo["."].branch()) commitfunc = getcommitfunc(extra, interactive, editor=True) if not interactive: node = cmdutil.commit(ui, repo, commitfunc, pats, opts) else: node = cmdutil.dorecord(ui, repo, commitfunc, None, False, cmdutil.recordfilter, *pats, **opts) if not node: _nothingtoshelvemessaging(ui, repo, pats, opts) return 1 _hidenodes(repo, [node]) except (KeyboardInterrupt, Exception): if activebookmark: bookmarks.activate(repo, activebookmark) raise _shelvecreatedcommit(ui, repo, node, name) if ui.formatted: desc = util.ellipsis(desc, ui.termwidth()) ui.status(_("shelved as %s\n") % name) # current wc parent may be already obsolete because # it might have been created previously and shelve just # reuses it try: hg.update(repo, parent.node()) except (KeyboardInterrupt, Exception): # failed to update to the original revision, which has left us on the # (hidden) shelve commit. Move directly to the original commit by # updating the dirstate parents. repo.setparents(parent.node()) raise finally: if origbranch != repo["."].branch() and not _isbareshelve(pats, opts): repo.dirstate.setbranch(origbranch) if activebookmark: bookmarks.activate(repo, activebookmark)
def split(ui, repo, *revs, **opts): """split a changeset into smaller changesets Prompt for hunks to be selected until exhausted. Each selection of hunks will form a separate changeset, in order from parent to child: the first selection will form the first changeset, the second selection will form the second changeset, and so on. Operates on the current revision by default. Use --rev to split a given changeset instead. """ newcommits = [] revarg = (list(revs) + opts.get("rev")) or ["."] if len(revarg) != 1: msg = _("more than one revset is given") hnt = _( "use either `hg split <rs>` or `hg split --rev <rs>`, not both") raise error.Abort(msg, hint=hnt) rev = scmutil.revsingle(repo, revarg[0]) if opts.get("no_rebase"): torebase = () else: torebase = list( map(hex, repo.nodes("descendants(%d) - (%d)", rev, rev))) with repo.wlock(), repo.lock(): cmdutil.bailifchanged(repo) if torebase: cmdutil.checkunfinished(repo) ctx = repo[rev] r = ctx.hex() allowunstable = visibility.tracking(repo) or obsolete.isenabled( repo, obsolete.allowunstableopt) if not allowunstable: # XXX We should check head revs if repo.revs("(%d::) - %d", rev, rev): raise error.Abort( _("cannot split commit: %s not a head") % ctx) if len(ctx.parents()) > 1: raise error.Abort(_("cannot split merge commits")) prev = ctx.p1() bmupdate = common.bookmarksupdater(repo, ctx.node()) bookactive = repo._activebookmark if bookactive is not None: repo.ui.status(_("(leaving bookmark %s)\n") % repo._activebookmark) bookmarks.deactivate(repo) hg.update(repo, prev) commands.revert(ui, repo, rev=r, all=True) def haschanges(): modified, added, removed, deleted = repo.status()[:4] return modified or added or removed or deleted # We need to detect the case where the user selects all remaining # changes, as that will end the split. That's the commit we want to # mark as the result of the split. To do this, wrap the recordfilter # function and compare the output to see if it contains all the # originalchunks. shouldrecordmutation = [False] def mutinfo(extra): if shouldrecordmutation[0]: return mutation.record( repo, extra, [ctx.node()], "split", splitting=[c.node() for c in newcommits], ) def recordfilter(ui, originalchunks, operation=None): chunks, newopts = cmdutil.recordfilter(ui, originalchunks, operation) if cmdutil.comparechunks(chunks, originalchunks): shouldrecordmutation[0] = True return chunks, newopts msg = ("HG: This is the original pre-split commit message. " "Edit it as appropriate.\n\n") msg += ctx.description() opts["message"] = msg opts["edit"] = True opts["_commitmutinfofunc"] = mutinfo try: while haschanges(): pats = () with repo.transaction("split"): cmdutil.dorecord(ui, repo, commands.commit, "commit", False, recordfilter, *pats, **opts) # TODO: Does no seem like the best way to do this # We should make dorecord return the newly created commit newcommits.append(repo["."]) if haschanges(): if ui.prompt("Done splitting? [yN]", default="n") == "y": shouldrecordmutation[0] = True with repo.transaction("split"): commands.commit(ui, repo, **opts) newcommits.append(repo["."]) break else: ui.status(_("no more change to split\n")) except Exception: # Rollback everything hg.updaterepo(repo, r, True) # overwrite=True if newcommits: visibility.remove(repo, [c.node() for c in newcommits]) if bookactive is not None: bookmarks.activate(repo, bookactive) raise if newcommits: phabdiffs = {} for c in newcommits: phabdiff = diffprops.parserevfromcommitmsg( repo[c].description()) if phabdiff: phabdiffs.setdefault(phabdiff, []).append(c) if any(len(commits) > 1 for commits in phabdiffs.values()): hintutil.trigger("split-phabricator", ui.config("split", "phabricatoradvice")) tip = repo[newcommits[-1]] with repo.transaction("post-split"): bmupdate(tip.node()) if bookactive is not None: bookmarks.activate(repo, bookactive) if obsolete.isenabled(repo, obsolete.createmarkersopt): obsolete.createmarkers(repo, [(repo[r], newcommits)], operation="split") if torebase: rebaseopts = {"dest": "_destrestack(SRC)", "rev": torebase} rebase.rebase(ui, repo, **rebaseopts) unfi = repo with repo.transaction("post-split-hide"): visibility.remove(repo, [unfi[r].node()])
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)