def checkhgserver(ui, repo, opts, path): ui.status( _("Testing connection to Mercurial on the server: querying master bookmark\n" ), component="debugnetwork", ) starttime = util.timer() peer = None try: peer = hg.peer(repo, opts, path) bookmarks = peer.listkeys("bookmarks") master = bookmarks.get("master") except Exception as e: ui.status(_("failed to connect to Mercurial: %s\n") % e, error=_("error")) return False finally: if peer: peer.close() endtime = util.timer() ui.status( _("Connected ok: %s\n") % util.timecount(endtime - starttime), component="debugnetwork", ) if master: ui.status(_("Server master bookmark is %s\n") % master, component="debugnetwork") else: ui.status(_("Server has no master bookmark\n"), component="debugnetwork") return True
def _lookup_node(repo, hexnode, from_scm_type): gitlookupnode = "_gitlookup_%s_%s" % (from_scm_type, hexnode) # ui.expandpath('default') returns 'default' if there is no default # path. This can be the case when command is ran on the server. # In that case let's run lookup() command locally. try: result = repo.lookup(gitlookupnode) except error.RepoLookupError: # Note: RepoLookupError is caught here because repo.lookup() # can throw only this exception. peerpath = repo.ui.expandpath("default") # sshing can cause junk 'remote: ...' output to stdout, so we need to # redirect it temporarily so automation can parse the result easily. oldfout = repo.ui.fout try: repo.baseui.fout = repo.ui.ferr remoterepo = hg.peer(repo, {}, peerpath) result = remoterepo.lookup(gitlookupnode) except error.RepoError: # Note: RepoError can be thrown by hg.peer(), RepoLookupError # can be thrown by remoterepo.lookup(). RepoLookupError is a # subclass of RepoError so catching just error.RepoError is enough. return None finally: repo.baseui.fout = oldfout # Sanity check - result must be 20 chars if len(result) != 20: return None else: return result
def annotatepeer(repo): ui = repo.ui # fileservice belongs to remotefilelog fileservice = getattr(repo, "fileservice", None) sharepeer = ui.configbool("fastannotate", "clientsharepeer", True) if sharepeer and fileservice: ui.debug("fastannotate: using remotefilelog connection pool\n") conn = repo.connectionpool.get(repo.fallbackpath) peer = conn.peer stolen = True else: remotepath = ui.expandpath( ui.config("fastannotate", "remotepath", "default")) peer = hg.peer(ui, {}, remotepath) stolen = False try: # Note: fastannotate requests should never trigger a remotefilelog # "getfiles" request, because "getfiles" puts the stream into a state # that does not exit. See "clientfetch": it does "getannotate" before # any hg stuff that could potentially trigger a "getfiles". yield peer finally: if not stolen: for i in ["close", "cleanup"]: getattr(peer, i, lambda: None)() else: conn.__exit__(None, None, None)
def _getremotepeer(ui, repo, opts): remotepath = opts.get("remote_path") path = ui.paths.getpath(remotepath or None, default="default") destpath = path.pushloc or path.loc other = hg.peer(repo, opts, destpath) return other
def _findbundle(repo, rev): """Returns the backup bundle that contains the given rev. If found, it returns the bundle peer and the full rev hash. If not found, it return None and the given rev value. """ ui = repo.ui backuppath = repo.localvfs.join("strip-backup") backups = filter(os.path.isfile, glob.glob(backuppath + "/*.hg")) backups.sort(key=lambda x: os.path.getmtime(x), reverse=True) for backup in backups: # Much of this is copied from the hg incoming logic source = os.path.relpath(backup, pycompat.getcwd()) source = ui.expandpath(source) source, branches = hg.parseurl(source) other = hg.peer(repo, {}, source) quiet = ui.quiet try: ui.quiet = True ret = bundlerepo.getremotechanges(ui, repo, other, None, None, None) localother, chlist, cleanupfn = ret for node in chlist: if hex(node).startswith(rev): return other, node except error.LookupError: continue finally: ui.quiet = quiet return None, rev
def listserverbookmarks(ui, **opts): """List the bookmarks for a remote server""" path = opts["path"] remote = hg.peer(ui, {}, path) bookmarks = remote.listkeys("bookmarks") for pair in bookmarks.items(): ui.write("%s\1%s\0" % pair) ui.flush()
def openrepo(ui, repopath): repo = None try: repo = hg.peer(ui, {}, repopath) yield repo except error.RepoError: yield None finally: if repo: repo.close()
def _bookmarks(orig, ui, repo, *names, **opts): pattern = opts.get("list_remote") delete = opts.get("delete") remotepath = opts.get("remote_path") path = ui.paths.getpath(remotepath or None, default=(pathname.default, )) if pattern: destpath = path.pushloc or path.loc other = hg.peer(repo, opts, destpath) if not names: raise error.Abort( "--list-remote requires a bookmark pattern", hint='use "hg book" to get a list of your local bookmarks', ) else: # prefix bookmark listing is not yet supported by Edenapi. usehttp = repo.ui.configbool( "infinitepush", "httpbookmarks") and not any(n.endswith("*") for n in names) if usehttp: fetchedbookmarks = _http_bookmark_fetch(repo, names) else: fetchedbookmarks = other.listkeyspatterns("bookmarks", patterns=names) _showbookmarks(ui, fetchedbookmarks, **opts) return elif delete and "remotenames" in extensions._extensions: with repo.wlock(), repo.lock(), repo.transaction("bookmarks"): existing_local_bms = set(repo._bookmarks.keys()) scratch_bms = [] other_bms = [] for name in names: if (repo._scratchbranchmatcher.match(name) and name not in existing_local_bms): scratch_bms.append(name) else: other_bms.append(name) if len(scratch_bms) > 0: if remotepath == "": remotepath = pathname.default bookmarks.deleteremotebookmarks(ui, repo, remotepath, scratch_bms) if len(other_bms) > 0 or len(scratch_bms) == 0: return orig(ui, repo, *other_bms, **opts) else: return orig(ui, repo, *names, **opts)
def getflogheads(ui, repo, path): """ Extension printing a remotefilelog's heads Used for testing purpose """ dest = repo.ui.expandpath("default") peer = hg.peer(repo, {}, dest) flogheads = peer.getflogheads(path) if flogheads: for head in flogheads: ui.write(head + "\n") else: ui.write(_("EMPTY\n"))
def setbranch(self, branch, pbranches): if not self.clonebranches: return setbranch = branch != self.lastbranch self.lastbranch = branch if not branch: branch = "default" pbranches = [(b[0], b[1] and b[1] or "default") for b in pbranches] if pbranches: pbranch = pbranches[0][1] else: pbranch = "default" branchpath = os.path.join(self.path, branch) if setbranch: self.after() try: self.repo = hg.repository(self.ui, branchpath) except Exception: self.repo = hg.repository(self.ui, branchpath, create=True) self.before() # pbranches may bring revisions from other branches (merge parents) # Make sure we have them, or pull them. missings = {} for b in pbranches: try: self.repo.lookup(b[0]) except Exception: missings.setdefault(b[1], []).append(b[0]) if missings: self.after() for pbranch, heads in sorted(pycompat.iteritems(missings)): pbranchpath = os.path.join(self.path, pbranch) prepo = hg.peer(self.ui, {}, pbranchpath) self.ui.note( _("pulling from %s into %s\n") % (pbranch, branch)) exchange.pull(self.repo, prepo, [prepo.lookup(h) for h in heads]) self.before()
def _bookmarks(orig, ui, repo, *names, **opts): pattern = opts.get("list_remote") delete = opts.get("delete") remotepath = opts.get("remote_path") path = ui.paths.getpath(remotepath or None, default=("default")) with repo.wlock(), repo.lock(), repo.transaction("bookmarks"): if pattern: destpath = path.pushloc or path.loc other = hg.peer(repo, opts, destpath) if not names: raise error.Abort( "--list-remote requires a bookmark pattern", hint='use "hg book" to get a list of your local bookmarks', ) else: fetchedbookmarks = other.listkeyspatterns("bookmarks", patterns=names) _showbookmarks(ui, fetchedbookmarks, **opts) return elif delete and "remotenames" in extensions._extensions: existing_local_bms = set(repo._bookmarks.keys()) scratch_bms = [] other_bms = [] for name in names: if ( repo._scratchbranchmatcher.match(name) and name not in existing_local_bms ): scratch_bms.append(name) else: other_bms.append(name) if len(scratch_bms) > 0: if remotepath == "": remotepath = "default" bookmarks.deleteremotebookmarks(ui, repo, remotepath, scratch_bms) if len(other_bms) > 0 or len(scratch_bms) == 0: return orig(ui, repo, *other_bms, **opts) else: return orig(ui, repo, *names, **opts)
def gitgetmeta(ui, repo, source="default"): """get git metadata from a server that supports fb_gitmeta""" source, branch = hg.parseurl(ui.expandpath(source)) other = hg.peer(repo, {}, source) ui.status(_("getting git metadata from %s\n") % util.hidepassword(source)) kwargs = {"bundlecaps": exchange.caps20to10(repo)} capsblob = bundle2.encodecaps(bundle2.getrepocaps(repo)) kwargs["bundlecaps"].add("bundle2=" + util.urlreq.quote(capsblob)) # this would ideally not be in the bundlecaps at all, but adding new kwargs # for wire transmissions is not possible as of Mercurial d19164a018a1 kwargs["bundlecaps"].add("fb_gitmeta") kwargs["heads"] = [nullid] kwargs["cg"] = False kwargs["common"] = _getcommonheads(repo) bundle = other.getbundle("pull", **kwargs) try: op = bundle2.processbundle(repo, bundle) except error.BundleValueError as exc: raise error.Abort("missing support for %s" % exc) writebytes = op.records["fb:gitmeta:writebytes"] ui.status(_("wrote %d files (%d bytes)\n") % (len(writebytes), sum(writebytes)))
def revsetdiff(repo, diffid): """Return a set of revisions corresponding to a given Differential ID """ repo_callsign = repo.ui.config("phrevset", "callsign") if repo_callsign is None: msg = _("phrevset.callsign is not set - doing a linear search\n") hint = _("This will be slow if the diff was not committed recently\n") repo.ui.warn(msg) repo.ui.warn(hint) rev = finddiff(repo, diffid) if rev is None: raise error.Abort("Could not find diff D%s in changelog" % diffid) else: return [rev] revs, resp = forksearch(repo, diffid) if revs is not None: # The log walk found the diff, nothing more to do return revs if resp is None: # The graphql query finished but didn't return anything return [] vcs = resp["source_control_system"] repo.ui.debug("[diffrev] VCS is %s\n" % vcs) if vcs == "git": gitrev = parsedesc(repo, resp, ignoreparsefailure=False) repo.ui.debug("[diffrev] GIT rev is %s\n" % gitrev) peerpath = repo.ui.expandpath("default") remoterepo = hg.peer(repo, {}, peerpath) remoterev = remoterepo.lookup("_gitlookup_git_%s" % gitrev) repo.ui.debug("[diffrev] HG rev is %s\n" % remoterev.encode("hex")) if not remoterev: repo.ui.debug("[diffrev] Falling back to linear search\n") linear_search_result = finddiff(repo, diffid) if linear_search_result is None: # walked the entire repo and couldn't find the diff raise error.Abort("Could not find diff D%s in changelog" % diffid) return [linear_search_result] return [repo[remoterev].rev()] elif vcs == "hg": rev = parsedesc(repo, resp, ignoreparsefailure=True) if rev: # The response from phabricator contains a changeset ID. # Convert it back to a rev number. try: return [repo[rev].rev()] except error.RepoLookupError: # TODO: 's/svnrev/globalrev' after turning off Subversion # servers. We will know about this when we remove the `svnrev` # revset. # # Unfortunately the rev can also be a svnrev/globalrev :(. if rev.isdigit(): try: return [r for r in repo.revs("svnrev(%s)" % rev)] except error.RepoLookupError: pass raise error.Abort( "Landed commit for diff D%s not available " 'in current repository: run "hg pull" ' "to retrieve it" % diffid ) # commit is still local, get its hash props = resp["phabricator_version_properties"]["edges"] commits = [] for prop in props: if prop["node"]["property_name"] == "local:commits": commits = json.loads(prop["node"]["property_value"]) revs = [c["commit"] for c in commits.values()] # verify all revisions exist in the current repo; if not, try to # find their counterpart by parsing the log results = set() for rev in revs: try: unfiltered = repo.unfiltered() node = unfiltered[rev] except error.RepoLookupError: raise error.Abort( "Commit %s corresponding to D%s\n not found in the repo" % (rev, diffid) ) successors = list(repo.revs("last(successors(%n))", node.node())) if len(successors) != 1: results.add(node.rev()) else: results.add(successors[0]) if not results: raise error.Abort("Could not find local commit for D%s" % diffid) return set(results) else: if not vcs: msg = ( "D%s does not have an associated version control system\n" "You can view the diff at https:///our.internmc.facebook.com/intern/diff/D%s\n" ) repo.ui.warn(msg % (diffid, diffid)) return [] else: raise error.Abort( "Conduit returned unknown " 'sourceControlSystem "%s"' % vcs )
def getremote(ui, path): return hg.peer(ui, {}, path)
def _dopull(orig, ui, repo, source="default", **opts): # Copy paste from `pull` command source, branches = hg.parseurl(ui.expandpath(source), opts.get("branch")) scratchbookmarks = {} unfi = repo.unfiltered() unknownnodes = [] pullbookmarks = opts.get("bookmark") or [] for rev in opts.get("rev", []): if repo._scratchbranchmatcher.match(rev): # rev is a scratch bookmark, treat it as a bookmark pullbookmarks.append(rev) elif rev not in unfi: unknownnodes.append(rev) if pullbookmarks: realbookmarks = [] revs = opts.get("rev") or [] for bookmark in pullbookmarks: if repo._scratchbranchmatcher.match(bookmark): # rev is not known yet # it will be fetched with listkeyspatterns next scratchbookmarks[bookmark] = "REVTOFETCH" else: realbookmarks.append(bookmark) if scratchbookmarks: other = hg.peer(repo, opts, source) fetchedbookmarks = other.listkeyspatterns( "bookmarks", patterns=scratchbookmarks) for bookmark in scratchbookmarks: if bookmark not in fetchedbookmarks: raise error.Abort("remote bookmark %s not found!" % bookmark) scratchbookmarks[bookmark] = fetchedbookmarks[bookmark] revs.append(fetchedbookmarks[bookmark]) opts["bookmark"] = realbookmarks opts["rev"] = [rev for rev in revs if rev not in scratchbookmarks] # Pulling revisions that were filtered results in a error. # Let's revive them. unfi = repo.unfiltered() torevive = [] for rev in opts.get("rev", []): try: repo[rev] except error.FilteredRepoLookupError: torevive.append(rev) except error.RepoLookupError: pass if obsolete.isenabled(repo, obsolete.createmarkersopt): obsolete.revive([unfi[r] for r in torevive]) visibility.add(repo, [unfi[r].node() for r in torevive]) if scratchbookmarks or unknownnodes: # Set anyincoming to True extensions.wrapfunction(discovery, "findcommonincoming", _findcommonincoming) try: # Remote scratch bookmarks will be deleted because remotenames doesn't # know about them. Let's save it before pull and restore after remotescratchbookmarks = bookmarks.readremotebookmarks( ui, repo, source) result = orig(ui, repo, source, **opts) # TODO(stash): race condition is possible # if scratch bookmarks was updated right after orig. # But that's unlikely and shouldn't be harmful. with repo.wlock(), repo.lock(), repo.transaction("pull"): if bookmarks.remotebookmarksenabled(ui): remotescratchbookmarks.update(scratchbookmarks) bookmarks.saveremotebookmarks(repo, remotescratchbookmarks, source) else: bookmarks.savelocalbookmarks(repo, scratchbookmarks) return result finally: if scratchbookmarks: extensions.unwrapfunction(discovery, "findcommonincoming")
def _push(orig, ui, repo, dest=None, *args, **opts): bookmark = opts.get("to") or "" create = opts.get("create") or False oldphasemove = None overrides = { ("experimental", "server-bundlestore-bookmark"): bookmark, ("experimental", "server-bundlestore-create"): create, } with ui.configoverride( overrides, "infinitepush"), repo.wlock(), repo.lock(), repo.transaction( "push"): scratchpush = opts.get("bundle_store") if repo._scratchbranchmatcher.match(bookmark): # We are pushing to a scratch bookmark. Check that there is # exactly one revision that is being pushed (this will be the # new bookmarked node). revs = opts.get("rev") if revs: revs = [repo[r] for r in scmutil.revrange(repo, revs)] else: revs = [repo["."]] if len(revs) != 1: msg = _("--to requires exactly one commit to push") hint = _("use --rev HASH or omit --rev for current commit (.)") raise error.Abort(msg, hint=hint) # Put the bookmarked node hash in the bundle to avoid ambiguity. ui.setconfig("experimental", "server-bundlestore-bookmarknode", revs[0].hex()) # If the bookmark destination is a public commit, then there will # be nothing to push. We still need to send a changegroup part # to update the bookmark, so send the null rev instead. if not revs[0].mutable(): opts["rev"] = ["null"] # Hack to fix interaction with remotenames. Remotenames push # '--to' bookmark to the server but we don't want to push scratch # bookmark to the server. Let's delete '--to' and '--create' and # also set allow_anon to True (because if --to is not set # remotenames will think that we are pushing anonymoush head) if "to" in opts: del opts["to"] if "create" in opts: del opts["create"] opts["allow_anon"] = True scratchpush = True # bundle2 can be sent back after push (for example, bundle2 # containing `pushkey` part to update bookmarks) ui.setconfig("experimental", "bundle2.pushback", True) ui.setconfig( "experimental", "non-forward-move", opts.get("non_forward_move"), "--non-forward-move", ) otherpath = None if scratchpush: ui.setconfig("experimental", "infinitepush-scratchpush", True) oldphasemove = extensions.wrapfunction(exchange, "_localphasemove", _phasemove) replicate, path = preparepush(ui, dest) # We'll replicate the push if the user intended their push to go to # the default infinitepush destination. if replicate: try: otherpath = repo.ui.paths.getpath( pathname.infinitepushother) except error.RepoError: pass else: path = ui.paths.getpath(dest, default=(pathname.defaultpush, pathname.default)) # Copy-paste from `push` command if not path: raise error.Abort( _("default repository not configured!"), hint=_("see 'hg help config.paths'"), ) realdest = path.pushloc or path.loc if realdest.startswith("svn+") and scratchpush: raise error.Abort( "infinite push does not work with svn repo", hint="did you forget to `hg push default`?", ) otherdest = otherpath and (otherpath.pushloc or otherpath.loc) if scratchpush: ui.log( "infinitepush_destinations", dest=dest, real_dest=realdest, other_dest=otherdest, bookmark=bookmark, ) # Remote scratch bookmarks will be deleted because remotenames doesn't # know about them. Let's save it before push and restore after remotescratchbookmarks = bookmarks.readremotebookmarks( ui, repo, realdest) result = orig(ui, repo, realdest, *args, **opts) # If an alternate Infinitepush destination is specified, replicate the # push there. This ensures scratch bookmarks (and their commits) can # properly be replicated to Mononoke. if otherdest is not None and otherdest != realdest: m = _( "please wait while we replicate this push to an alternate repository\n" ) ui.warn(m) # NOTE: We ignore the result here (which only represents whether # there were changes to land). orig(ui, repo, otherdest, *args, **opts) if bookmarks.remotebookmarksenabled(ui): if bookmark and scratchpush: other = hg.peer(repo, opts, realdest) fetchedbookmarks = other.listkeyspatterns("bookmarks", patterns=[bookmark]) remotescratchbookmarks.update(fetchedbookmarks) bookmarks.saveremotebookmarks(repo, remotescratchbookmarks, realdest) if oldphasemove: exchange._localphasemove = oldphasemove return result
def expushcmd(orig, ui, repo, dest=None, **opts): # during the upgrade from old to new remotenames, tooling that uses --force # will continue working if remotenames.forcecompat is enabled forcecompat = ui.configbool("remotenames", "forcecompat") # needed for discovery method opargs = { "delete": opts.get("delete"), "to": opts.get("to"), "create": opts.get("create") or (opts.get("force") and forcecompat), "allowanon": opts.get("allow_anon") or repo.ui.configbool("remotenames", "pushanonheads") or (opts.get("force") and forcecompat), "nonforwardmove": opts.get("non_forward_move") or repo.ui.configbool("remotenames", "allownonfastforward") or (opts.get("force") and forcecompat), } if opargs["delete"]: flag = None for f in ("to", "bookmark", "branch", "rev"): if opts.get(f): flag = f break if flag: msg = _("do not specify --delete and " "--%s at the same time") % flag raise error.Abort(msg) # we want to skip pushing any changesets while deleting a remote # bookmark, so we send the null revision opts["rev"] = ["null"] return orig(ui, repo, dest, opargs=opargs, **opts) revs = opts.get("rev") paths = dict((path, url) for path, url in ui.configitems("paths")) # XXX T58629567: The following line triggers an infinite loop in pyre, let's disable it for now. if not typing.TYPE_CHECKING: revrenames = dict( (v, k) for k, v in pycompat.iteritems(_getrenames(ui))) origdest = dest defaultpush = ui.paths.get("default-push") or ui.paths.get("default") if defaultpush: defaultpush = defaultpush.loc if ((not dest or dest == defaultpush) and not opargs["to"] and not revs and _tracking(ui)): current = repo._activebookmark tracking = _readtracking(repo) ui.debug("tracking on %s %s\n" % (current, tracking)) if current and current in tracking: track = tracking[current] path, book = splitremotename(track) # un-rename a path, if needed path = revrenames.get(path, path) if book and path in paths: dest = path opargs["to"] = book # un-rename passed path dest = revrenames.get(dest, dest) # if dest was renamed to default but we aren't specifically requesting # to push to default, change dest to default-push, if available if not origdest and dest == "default" and "default-push" in paths: dest = "default-push" # get the actual path we will push to so we can do some url sniffing for check in [ # dest may be a path name, or an actual url paths.get(dest, dest), paths.get("default-push"), paths.get("default"), ]: if check: # hggit does funky things on push. Just call direct. if check.startswith("git+"): return orig(ui, repo, dest, opargs=opargs, **opts) # Once we have found the path where we are pushing, do not continue # checking for places we are not pushing. break if not opargs["to"]: if ui.configbool("remotenames", "forceto"): msg = _("must specify --to when pushing") hint = _("see configuration option %s") % "remotenames.forceto" raise error.Abort(msg, hint=hint) if not revs: opts["rev"] = _pushrevs(repo, ui, None) return orig(ui, repo, dest, opargs=opargs, **opts) if opts.get("bookmark"): msg = _("do not specify --to/-t and --bookmark/-B at the same time") raise error.Abort(msg) if opts.get("branch"): msg = _("do not specify --to/-t and --branch/-b at the same time") raise error.Abort(msg) # if we are not using the original push command implementation, make sure # pushvars is included in opargs pushvars = opts.get("pushvars") if pushvars: opargs["pushvars"] = pushvars if revs: revs = [repo.lookup(r) for r in repo.anyrevs(revs, user=True)] else: revs = _pushrevs(repo, ui, ".") if len(revs) != 1: msg = _("--to requires exactly one rev to push") hint = _("use --rev BOOKMARK or omit --rev for current commit (.)") raise error.Abort(msg, hint=hint) rev = revs[0] # big can o' copypasta from commands.push dest = ui.expandpath(dest or "default-push", dest or "default") dest, branches = hg.parseurl(dest, opts.get("branch")) try: other = hg.peer(repo, opts, dest) except error.RepoError: if dest == "default-push": hint = _('see the "path" section in "hg help config"') raise error.Abort(_("default repository not configured!"), hint=hint) else: raise # all checks pass, go for it! node = repo.lookup(rev) ui.status_err( _("pushing rev %s to destination %s bookmark %s\n") % (short(node), dest, opargs["to"])) force = opts.get("force") bookmark = opargs["to"] pattern = ui.config("remotenames", "disallowedto") if pattern and re.match(pattern, bookmark): msg = _("this remote bookmark name is not allowed") hint = ui.config("remotenames", "disallowedhint") or _("use another bookmark name") raise error.Abort(msg, hint=hint) # NB: despite the name, 'revs' doesn't work if it's a numeric rev pushop = exchange.push(repo, other, force, revs=[node], bookmarks=(opargs["to"], ), opargs=opargs) result = not pushop.cgresult if pushop.bkresult is not None: if pushop.bkresult == 2: result = 2 elif not result and pushop.bkresult: result = 2 return result
def diffidtonode(repo, diffid): """Return node that matches a given Differential ID or None. The node might exist or not exist in the repo. This function does not raise. """ repo_callsigns = repo.ui.configlist("phrevset", "callsign") if not repo_callsigns: msg = _("phrevset.callsign is not set - doing a linear search\n") hint = _("This will be slow if the diff was not committed recently\n") repo.ui.warn(msg) repo.ui.warn(hint) node = localgetdiff(repo, diffid) if node is None: repo.ui.warn(_("Could not find diff D%s in changelog\n") % diffid) return node node, resp = search(repo, diffid) if node is not None: # The log walk found the diff, nothing more to do return node if resp is None: # The graphql query finished but didn't return anything return None vcs = resp.get("source_control_system") localreponame = repo.ui.config("remotefilelog", "reponame") diffreponame = None repository = resp.get("repository") if repository is not None: diffreponame = repository.get("scm_name") if diffreponame in repo.ui.configlist("phrevset", "aliases"): diffreponame = localreponame if not util.istest() and (diffreponame != localreponame): raise error.Abort( "D%s is for repo '%s', not this repo ('%s')" % (diffid, diffreponame, localreponame) ) repo.ui.debug("[diffrev] VCS is %s\n" % vcs) if vcs == "git": gitrev = parsedesc(repo, resp, ignoreparsefailure=False) repo.ui.debug("[diffrev] GIT rev is %s\n" % gitrev) peerpath = repo.ui.expandpath("default") remoterepo = hg.peer(repo, {}, peerpath) remoterev = remoterepo.lookup("_gitlookup_git_%s" % gitrev) repo.ui.debug("[diffrev] HG rev is %s\n" % hex(remoterev)) if not remoterev: repo.ui.debug("[diffrev] Falling back to linear search\n") node = localgetdiff(repo, diffid) if node is None: repo.ui.warn(_("Could not find diff D%s in changelog\n") % diffid) return node return remoterev elif vcs == "hg": rev = parsedesc(repo, resp, ignoreparsefailure=True) if rev: # The response from phabricator contains a changeset ID. # Convert it back to a node. try: return repo[rev].node() except error.RepoLookupError: # TODO: 's/svnrev/globalrev' after turning off Subversion # servers. We will know about this when we remove the `svnrev` # revset. # # Unfortunately the rev can also be a svnrev/globalrev :(. if rev.isdigit(): try: return list(repo.nodes("svnrev(%s)" % rev))[0] except (IndexError, error.RepoLookupError): pass if len(rev) == len(nullhex): return bin(rev) else: return None # commit is still local, get its hash try: props = resp["phabricator_version_properties"]["edges"] commits = {} for prop in props: if prop["node"]["property_name"] == "local:commits": commits = json.loads(prop["node"]["property_value"]) hexnodes = [c["commit"] for c in commits.values()] except (AttributeError, IndexError, KeyError): hexnodes = [] # find a better alternative of the commit hash specified in # graphql response by looking up successors. for hexnode in hexnodes: if len(hexnode) != len(nullhex): continue node = bin(hexnode) unfi = repo if node in unfi: # Find a successor. successors = list( unfi.nodes("last(successors(%n)-%n-obsolete())", node, node) ) if successors: return successors[0] return node # local:commits is empty return None else: if not vcs: msg = ( "D%s does not have an associated version control system\n" "You can view the diff at https:///our.internmc.facebook.com/intern/diff/D%s\n" ) repo.ui.warn(msg % (diffid, diffid)) return None else: repo.ui.warn( _("Conduit returned unknown sourceControlSystem: '%s'\n") % vcs ) return None
from edenscm.mercurial import commands, fscap, hg, ui as uimod, util TESTDIR = os.environ["TESTDIR"] BUNDLEPATH = os.path.join(TESTDIR, "bundles", "test-no-symlinks.hg") # only makes sense to test on os which supports symlinks if not getattr(os, "symlink", False): sys.exit(80) # SKIPPED_STATUS defined in run-tests.py with open(os.environ["HGRCPATH"], "a+") as f: f.write(b"[extensions]\n") f.write(b"treemanifest=!\n") u = uimod.ui.load() # hide outer repo hg.peer(u, {}, ".", create=True) # clone with symlink support hg.clone(u, {}, BUNDLEPATH, "test0") repo = hg.repository(u, "test0") # wait a bit, or the status call wont update the dirstate time.sleep(1) commands.status(u, repo) # now disable symlink support -- this is what os.symlink would do on a # non-symlink file system def symlink_failure(src, dst): raise OSError(1, "Operation not permitted")