def perfpathcopies(ui, repo, rev1, rev2, **opts): timer, fm = gettimer(ui, opts) ctx1 = scmutil.revsingle(repo, rev1, rev1) ctx2 = scmutil.revsingle(repo, rev2, rev2) def d(): copies.pathcopies(ctx1, ctx2) timer(d) fm.end()
def perftestsuitecmd(ui, repo, *revs, **opts): """Runs an in-depth performance suite and logs results to a metrics framework. The rebase distance is configurable:: [perfsuite] rebase.masterdistance = 100 immrebase.masterdistance = 100 The metrics endpoint is configurable:: [ods] endpoint = https://somehost/metrics """ if opts["seed"]: random.seed(opts["seed"]) if opts["rev"]: ui.status(_("updating to %s...\n") % (opts["rev"])) commands.update(ui, repo, scmutil.revsingle(repo, opts["rev"]).hex()) suite = perftestsuite( repo, publish=opts["publish"], profile=opts["use_profile"], printout=opts["print"], ) suite.run()
def perfmergecalculate(ui, repo, rev, **opts): timer, fm = gettimer(ui, opts) wctx = repo[None] rctx = scmutil.revsingle(repo, rev, rev) ancestor = wctx.ancestor(rctx) # we don't want working dir files to be stat'd in the benchmark, so prime # that cache wctx.dirty() def d(): # acceptremote is True because we don't want prompts in the middle of # our benchmark merge.calculateupdates( repo, wctx, rctx, [ancestor], False, False, acceptremote=True, followcopies=True, ) timer(d) fm.end()
def _rebase(orig, ui, repo, *pats, **opts): if not opts.get("date") and not ui.configbool("tweakdefaults", "rebasekeepdate"): opts["date"] = currentdate() if opts.get("continue") or opts.get("abort") or opts.get("restack"): return orig(ui, repo, *pats, **opts) # 'hg rebase' w/o args should do nothing if not opts.get("dest"): raise error.Abort("you must specify a destination (-d) for the rebase") # 'hg rebase' can fast-forward bookmark prev = repo["."] # Only fast-forward the bookmark if no source nodes were explicitly # specified. if not (opts.get("base") or opts.get("source") or opts.get("rev")): dest = scmutil.revsingle(repo, opts.get("dest")) common = dest.ancestor(prev) if prev == common: activebookmark = repo._activebookmark result = hg.updatetotally(ui, repo, dest.node(), activebookmark) if activebookmark: with repo.wlock(): bookmarks.update(repo, [prev.node()], dest.node()) return result return orig(ui, repo, *pats, **opts)
def _filterfetchpaths(repo, paths): """return a subset of paths whose history is long and need to fetch linelog from the server. works with remotefilelog and non-remotefilelog repos. """ threshold = repo.ui.configint("fastannotate", "clientfetchthreshold", 10) if threshold <= 0: return paths master = repo.ui.config("fastannotate", "mainbranch") or "default" if "remotefilelog" in repo.requirements: ctx = scmutil.revsingle(repo, master) f = lambda path: len(ctx[path].ancestormap()) else: f = lambda path: len(repo.file(path)) result = [] for path in paths: try: if f(path) >= threshold: result.append(path) except Exception: # file not found etc. result.append(path) return result
def _fullclean(ui, repo, exclude): start_time = time.perf_counter() ui.status(_("cleaning up uncommitted code\n"), component="snapshot") # The order of operations to cleanup here is very deliberate, to avoid errors. # Most errors happen due to file/dir clashes, see https://fburl.com/jwyhd0fk # Step 1: Forget files that were "hg added" # WARNING: Don't call cmdutil.forget because it might be slow wctx = repo[None] forget = set(wctx.added()) - set(exclude) if forget: wctx.forget(list(forget), "") # Step 2: Remove "untracked changes" (e.g. untracked files) repo.dirstate._fs.purge( scmutil.match(repo[None], opts={"exclude": exclude}), removefiles=True, removedirs=True, removeignored=False, dryrun=False, ) # Step 3: Remove "tracked changes" cmdutil.revert( ui, repo, scmutil.revsingle(repo, None), repo.dirstate.parents(), all=True, no_backup=True, exclude=exclude, ) duration = time.perf_counter() - start_time ui.status( _("cleaned up uncommitted code in {duration:0.5f} seconds\n").format( duration=duration), component="snapshot", )
def gverify(ui, repo, **opts): """verify that a Mercurial rev matches the corresponding Git rev Given a Mercurial revision that has a corresponding Git revision in the map, this attempts to answer whether that revision has the same contents as the corresponding Git revision. """ ctx = scmutil.revsingle(repo, opts.get("rev"), ".") return verify.verify(ui, repo, ctx)
def perfmanifest(ui, repo, rev, **opts): timer, fm = gettimer(ui, opts) ctx = scmutil.revsingle(repo, rev, rev) t = ctx.manifestnode() def d(): repo.manifestlog.clearcaches() repo.manifestlog[t].read() timer(d) fm.end()
def link(ui, repo, *args, **opts): if len(args) != 1: raise error.Abort(_("must specify a pull request")) pr_arg = args[0] pull_request = resolve_pr_arg(pr_arg, ui) if not pull_request: raise error.Abort(_("could not resolve pull request: '%%s'") % pr_arg) ctx = scmutil.revsingle(repo, opts.get("rev"), None) pr_store = PullRequestStore(repo) pr_store.map_commit_to_pull_request(ctx.node(), pull_request)
def debugbuildannotatecache(ui, repo, *pats, **opts): """incrementally build fastannotate cache up to REV for specified files If REV is not specified, use the config 'fastannotate.mainbranch'. If fastannotate.client is True, download the annotate cache from the server. Otherwise, build the annotate cache locally. The annotate cache will be built using the default diff and follow options and lives in '.hg/fastannotate/default'. """ rev = opts.get("REV") or ui.config("fastannotate", "mainbranch") if not rev: raise error.Abort( _("you need to provide a revision"), hint=_("set fastannotate.mainbranch or use --rev"), ) if ui.configbool("fastannotate", "unfilteredrepo", True): repo = repo.unfiltered() ctx = scmutil.revsingle(repo, rev) m = scmutil.match(ctx, pats, opts) paths = list(ctx.walk(m)) if util.safehasattr(repo, "prefetchfastannotate"): # client if opts.get("REV"): raise error.Abort(_("--rev cannot be used for client")) repo.prefetchfastannotate(paths) else: # server, or full repo with progress.bar(ui, _("building"), total=len(paths)) as prog: for i, path in enumerate(paths): prog.value = i with facontext.annotatecontext(repo, path) as actx: try: if actx.isuptodate(rev): continue actx.annotate(rev, rev) except (faerror.CannotReuseError, faerror.CorruptedFileError): # the cache is broken (could happen with renaming so the # file history gets invalidated). rebuild and try again. ui.debug("fastannotate: %s: rebuilding broken cache\n" % path) actx.rebuild() try: actx.annotate(rev, rev) except Exception as ex: # possibly a bug, but should not stop us from # building cache for other files. ui.warn( _("fastannotate: %s: failed to " "build cache: %r\n") % (path, ex) )
def rebaseorfastforward(orig, ui, repo, dest, **args): """Wrapper for rebasemodule.rebase that fast-forwards the working directory and any active bookmark to the rebase destination if there is actually nothing to rebase. """ prev = repo["."] destrev = scmutil.revsingle(repo, dest) common = destrev.ancestor(prev) if prev == common and destrev != prev: result = hg.update(repo, destrev.node()) if repo._activebookmark: with repo.wlock(): bookmarks.update(repo, [prev.node()], destrev.node()) ui.status(_("nothing to rebase - fast-forwarded to %s\n") % dest) return result return orig(ui, repo, dest=dest, **args)
def catnotate(ui, repo, file1, *args, **opts): """output the current or given revision of files annotated with filename and line number. Print the specified files as they were at the given revision. If no revision is given, the parent of the working directory is used. Binary files are skipped unless -a/--text option is provided. """ ctx = scmutil.revsingle(repo, opts.get("rev")) matcher = scmutil.match(ctx, (file1, ) + args, opts) prefix = "" err = 1 # modified and stripped mercurial.cmdutil.cat follows def write(path): fp = cmdutil.makefileobj(repo, opts.get("output"), ctx.node(), pathname=os.path.join(prefix, path)) data = ctx[path].data() if not opts.get("text") and util.binary(data): fp.write(b"%s: binary file\n" % path.encode("utf8")) return for (num, line) in enumerate(data.split(b"\n"), start=1): line = line + b"\n" fp.write(b"%s:%s: %s" % (path.encode("utf8"), str(num).encode("utf8"), line)) fp.close() # Automation often uses hg cat on single files, so special case it # for performance to avoid the cost of parsing the manifest. if len(matcher.files()) == 1 and not matcher.anypats(): file = matcher.files()[0] mfl = repo.manifestlog mfnode = ctx.manifestnode() if mfnode and mfl[mfnode].find(file)[0]: write(file) return 0 for abs in ctx.walk(matcher): write(abs) err = 0 return err
def _matchpaths(repo, rev, pats, opts, aopts=facontext.defaultopts): """generate paths matching given patterns""" perfhack = repo.ui.configbool("fastannotate", "perfhack") # disable perfhack if: # a) any walkopt is used # b) if we treat pats as plain file names, some of them do not have # corresponding linelog files if perfhack: # cwd related to reporoot reporoot = os.path.dirname(repo.path) reldir = os.path.relpath(pycompat.getcwd(), reporoot) if reldir == ".": reldir = "" if any(opts.get(o[1]) for o in commands.walkopts): # a) perfhack = False else: # b) relpats = [ os.path.relpath(p, reporoot) if os.path.isabs(p) else p for p in pats ] # disable perfhack on '..' since it allows escaping from the repo if any( ( ".." in f or not os.path.isfile( facontext.pathhelper(repo, f, aopts).linelogpath ) ) for f in relpats ): perfhack = False # perfhack: emit paths directory without checking with manifest # this can be incorrect if the rev dos not have file. if perfhack: for p in relpats: yield os.path.join(reldir, p) else: def bad(x, y): raise error.Abort("%s: %s" % (x, y)) ctx = scmutil.revsingle(repo, rev) m = scmutil.match(ctx, pats, opts, badfn=bad) for p in ctx.walk(m): yield p
def debugserializecommit(ui, repo, *args, **opts): """serialize commit in format consumable by 'memcommit' command If no revision for serialization is specified, the current commit is serialized. This command is mainly intended for the testing the 'memcommit' command. """ ctx = scmutil.revsingle(repo, opts.get("rev")) changelistbuilder = commitdata.changelistbuilder(ctx.p1().hex()) for path in ctx.files(): if path in ctx: fctx = ctx[path] renamed = fctx.renamed() copysource = renamed[0] if renamed else None info = commitdata.fileinfo(flags=fctx.flags(), content=fctx.data(), copysource=copysource) else: info = commitdata.fileinfo(deleted=True) changelistbuilder.addfile(path, info) changelist = changelistbuilder.build() bookmark = opts.get("dest") to = opts.get("to") pushrebase = opts.get("pushrebase") destination = commitdata.destination(bookmark=bookmark, pushrebase=pushrebase) parents = ([hex(p) for p in repo.nodes(to)] if to else [p.hex() for p in ctx.parents()]) metadata = commitdata.metadata( author=ctx.user(), description=ctx.description(), parents=parents, extra=ctx.extra(), ) params = commitdata.params(changelist, metadata, destination) ui.writebytes(serialization.serialize(params.todict()))
def _mainloop(repo, ui, tr, count, batchsize): i = 0 batchcount = 0 start = time.time() base = scmutil.revsingle(repo, ui.config("repogenerator", "startcommit", "tip")) goalrev = ui.configint("repogenerator", "numcommits", 10000) - 1 ui.write(_("starting commit is: %d (goal is %d)\n") % (base.rev(), goalrev)) generator = editsgenerator.randomeditsgenerator(base) while base.rev() < goalrev: # Make a commit: wctx = context.overlayworkingctx(repo) wctx.setbase(base) generator.makerandomedits(wctx) memctx = wctx.tomemctx("memory commit", parents=(base.rev(), None)) newctx = repo[repo.commitctx(memctx)] # Log production rate: elapsed = time.time() - start if i % 5 == 0: ui.write( _( "created %s, %0.2f sec elapsed " "(%0.2f commits/sec, %s per hour, %s per day)\n" ) % ( i, elapsed, i / elapsed, "{:,}".format(int(i / elapsed * 3600)), "{:,}".format(int(i / elapsed * 86400)), ) ) base = newctx i += 1 batchcount += 1 if batchcount > batchsize: ui.status(_("committing txn...\n")) tr.close() tr = repo.transaction("newtxn_") batchcount = 0 if i >= count: ui.status(_("generated %d commits; quitting\n") % count) return
def _fullclean(ui, repo): ui.status(_("cleaning up uncommitted code\n"), component="snapshot") # Remove "tracked changes" cmdutil.revert( ui, repo, scmutil.revsingle(repo, None), repo.dirstate.parents(), all=True, no_backup=True, ) # Remove "untracked changes" (e.g. untracked files) repo.dirstate._fs.purge( scmutil.match(repo[None]), removefiles=True, removedirs=True, removeignored=False, dryrun=False, )
def _rebase(orig, ui, repo, *pats, **opts): histedit = extensions.find("histedit") contf = opts.get("continue") abortf = opts.get("abort") if ((contf or abortf) and not repo.localvfs.exists("rebasestate") and repo.localvfs.exists("histedit.state")): msg = _("no rebase in progress") hint = _( "If you want to continue or abort an interactive rebase please" ' use "histedit --continue/--abort" instead.') raise error.Abort(msg, hint=hint) if not opts.get("interactive"): return orig(ui, repo, *pats, **opts) # the argument parsing has as lot of copy-paste from rebase.py # Validate input and define rebasing points destf = opts.get("dest", None) srcf = opts.get("source", None) basef = opts.get("base", None) revf = opts.get("rev", []) keepf = opts.get("keep", False) src = None if contf or abortf: raise error.Abort("no interactive rebase in progress") if destf: dest = scmutil.revsingle(repo, destf) else: raise error.Abort("you must specify a destination (-d) for the rebase") if srcf and basef: raise error.Abort(_("cannot specify both a source and a base")) if revf: raise error.Abort("--rev not supported with interactive rebase") elif srcf: src = scmutil.revsingle(repo, srcf) else: base = scmutil.revrange(repo, [basef or "."]) if not base: ui.status( _('empty "base" revision set - ' "can't compute rebase set\n")) return 1 commonanc = repo.revs("ancestor(%ld, %d)", base, dest).first() if commonanc is not None: src = repo.revs("min((%d::(%ld) - %d)::)", commonanc, base, commonanc).first() else: src = None if src is None: raise error.Abort("no revisions to rebase") src = repo[src].node() topmost, empty = repo.dirstate.parents() revs = histedit.between(repo, src, topmost, keepf) if srcf and not revs: raise error.Abort( _("source revision (-s) must be an ancestor of the " "working directory for interactive rebase")) ctxs = [repo[r] for r in revs] state = histedit.histeditstate(repo) rules = [histedit.base(state, repo[dest]) ] + [histedit.pick(state, ctx) for ctx in ctxs] editcomment = """# # Interactive rebase is just a wrapper over histedit (adding the 'base' line as # the first rule). To continue or abort it you should use: # "hg histedit --continue" and "--abort" # """ editcomment += histedit.geteditcomment(ui, node.short(src), node.short(topmost)) histedit.ruleeditor(repo, ui, rules, editcomment=editcomment) return histedit.histedit( ui, repo, node.hex(src), keep=keepf, commands=repo.localvfs.join("histedit-last-edit.txt"), )
def dodiff(ui, repo, cmdline, pats, opts): """Do the actual diff: - copy to a temp structure if diffing 2 internal revisions - copy to a temp structure if diffing working revision with another one and more than 1 file is changed - just invoke the diff for a single file in the working dir """ revs = opts.get("rev") change = opts.get("change") do3way = "$parent2" in cmdline if revs and change: msg = _("cannot specify --rev and --change at the same time") raise error.Abort(msg) elif change: node2 = scmutil.revsingle(repo, change, None).node() node1a, node1b = repo.changelog.parents(node2) else: node1a, node2 = scmutil.revpair(repo, revs) if not revs: node1b = repo.dirstate.p2() else: node1b = nullid # Disable 3-way merge if there is only one parent if do3way: if node1b == nullid: do3way = False matcher = scmutil.match(repo[node2], pats, opts) if opts.get("patch"): if node2 is None: raise error.Abort(_("--patch requires two revisions")) else: mod_a, add_a, rem_a = list(map(set, repo.status(node1a, node2, matcher)[:3])) if do3way: mod_b, add_b, rem_b = list( map(set, repo.status(node1b, node2, matcher)[:3]) ) else: mod_b, add_b, rem_b = set(), set(), set() modadd = mod_a | add_a | mod_b | add_b common = modadd | rem_a | rem_b if not common: return 0 tmproot = tempfile.mkdtemp(prefix="extdiff.") try: if not opts.get("patch"): # Always make a copy of node1a (and node1b, if applicable) dir1a_files = mod_a | rem_a | ((mod_b | add_b) - add_a) dir1a = snapshot(ui, repo, dir1a_files, node1a, tmproot)[0] rev1a = "@%d" % repo[node1a].rev() if do3way: dir1b_files = mod_b | rem_b | ((mod_a | add_a) - add_b) dir1b = snapshot(ui, repo, dir1b_files, node1b, tmproot)[0] rev1b = "@%d" % repo[node1b].rev() else: dir1b = None rev1b = "" fnsandstat = [] # If node2 in not the wc or there is >1 change, copy it dir2root = "" rev2 = "" if node2: dir2 = snapshot(ui, repo, modadd, node2, tmproot)[0] rev2 = "@%d" % repo[node2].rev() elif len(common) > 1: # we only actually need to get the files to copy back to # the working dir in this case (because the other cases # are: diffing 2 revisions or single file -- in which case # the file is already directly passed to the diff tool). dir2, fnsandstat = snapshot(ui, repo, modadd, None, tmproot) else: # This lets the diff tool open the changed file directly dir2 = "" dir2root = repo.root label1a = rev1a label1b = rev1b label2 = rev2 # If only one change, diff the files instead of the directories # Handle bogus modifies correctly by checking if the files exist if len(common) == 1: common_file = util.localpath(common.pop()) dir1a = os.path.join(tmproot, dir1a, common_file) label1a = common_file + rev1a if not os.path.isfile(dir1a): dir1a = os.devnull if do3way: dir1b = os.path.join(tmproot, dir1b, common_file) label1b = common_file + rev1b if not os.path.isfile(dir1b): dir1b = os.devnull dir2 = os.path.join(dir2root, dir2, common_file) label2 = common_file + rev2 else: template = "hg-%h.patch" cmdutil.export( repo, [repo[node1a].rev(), repo[node2].rev()], fntemplate=repo.localvfs.reljoin(tmproot, template), match=matcher, ) label1a = cmdutil.makefilename(repo, template, node1a) label2 = cmdutil.makefilename(repo, template, node2) dir1a = repo.localvfs.reljoin(tmproot, label1a) dir2 = repo.localvfs.reljoin(tmproot, label2) dir1b = None label1b = None fnsandstat = [] # Function to quote file/dir names in the argument string. # When not operating in 3-way mode, an empty string is # returned for parent2 replace = { "parent": dir1a, "parent1": dir1a, "parent2": dir1b, "plabel1": label1a, "plabel2": label1b, "clabel": label2, "child": dir2, "root": repo.root, } def quote(match): pre = match.group(2) key = match.group(3) if not do3way and key == "parent2": return pre return pre + util.shellquote(replace[key]) # Match parent2 first, so 'parent1?' will match both parent1 and parent regex = ( r"""(['"]?)([^\s'"$]*)""" r"\$(parent2|parent1?|child|plabel1|plabel2|clabel|root)\1" ) if not do3way and not re.search(regex, cmdline): cmdline += " $parent1 $child" cmdline = re.sub(regex, quote, cmdline) ui.debug("running %r in %s\n" % (cmdline, tmproot)) ui.system(cmdline, cwd=tmproot, blockedtag="extdiff") for copy_fn, working_fn, st in fnsandstat: cpstat = util.lstat(copy_fn) # Some tools copy the file and attributes, so mtime may not detect # all changes. A size check will detect more cases, but not all. # The only certain way to detect every case is to diff all files, # which could be expensive. # copyfile() carries over the permission, so the mode check could # be in an 'elif' branch, but for the case where the file has # changed without affecting mtime or size. if ( cpstat.st_mtime != st.st_mtime or cpstat.st_size != st.st_size or (cpstat.st_mode & 0o100) != (st.st_mode & 0o100) ): ui.debug( "file changed while diffing. " "Overwriting: %s (src: %s)\n" % (working_fn, copy_fn) ) util.copyfile(copy_fn, working_fn) return 1 finally: ui.note(_("cleaning up temp directory\n")) shutil.rmtree(tmproot)
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 debugremotebookmark(ui, repo, name, rev): """Change a remote bookmark under the 'debugremote' namespace.""" node = scmutil.revsingle(repo, rev).node() setremotebookmark(repo, "debugremote/%s" % name, node)
def _memcommit(repo, params): """create a new commit in the repo based on the params Isolating this method allows easy wrapping by other extensions like hgsql. """ with repo.wlock(), repo.lock(), repo.transaction("memcommit"): def resolvetargetctx(repo, originalparentnode, targetparents): numparents = len(targetparents) if numparents > 1: raise error.Abort(_("merge commits are not supported")) if numparents == 0: raise error.Abort(_("parent commit must be specified")) targetctx = repo[targetparents[0]] targetnode = targetctx.node() if originalparentnode != targetnode: raise error.Abort(_("commit with new parents not supported")) if (not repo.ui.configbool("memcommit", "allowunrelatedroots") and targetnode == nullid): raise error.Abort(_("commit without parents are not allowed")) return targetctx request = pushrequest.frommemcommit(repo, params) originalparentnode = request.stackparentnode targetctx = resolvetargetctx(repo, originalparentnode, params.metadata.parents) targetnode = targetctx.node() destination = params.destination pushrebase = destination.pushrebase ontobookmark = destination.bookmark if ontobookmark: bookmarkctx = scmutil.revsingle(repo, ontobookmark) if not pushrebase and bookmarkctx.node() != targetnode: raise error.Abort( _("destination parent does not match destination bookmark") ) elif pushrebase: raise error.Abort( _("must specify destination bookmark for pushrebase")) if pushrebase: ontonode = bookmarkctx.node() cl = repo.changelog if cl.isancestor(originalparentnode, ontonode): targetctx = bookmarkctx elif cl.isancestor(ontonode, originalparentnode): targetctx = repo[originalparentnode] else: raise error.Abort( _("destination bookmark is not ancestor or descendant of commit parent" )) added, replacements = request.pushonto(targetctx) if len(added) > 1: # We always create a single commit. error.Abort(_("more than one commit was created")) if replacements: # We always create a new commit and therefore, cannot have any # replacements. error.Abort(_("new commit cannot replace any commit")) node = added[0] if ontobookmark: bookmarks.pushbookmark(repo, ontobookmark, bookmarkctx.hex(), node) return node