mirror of
https://github.com/facebook/sapling.git
synced 2024-10-11 09:17:30 +03:00
dabce28285
Summary: Since repoview is removed, those concetps are useless. Therefore remove them. This includes: - repo.unfiltered(), repo.filtered(), repo.filtername - changelog.filteredrevs - error.FilteredIndexError, error.FilteredLookupError, error.FilteredRepoLookupError - repo.unfilteredpropertycache, repo.filteredpropertycache, repo.unfilteredmethod - index.headsrevsfiltered Reviewed By: DurhamG Differential Revision: D22367600 fbshipit-source-id: d133b8aaa136176b4c9f7f4b0c52ee60ac888531
248 lines
7.9 KiB
Python
248 lines
7.9 KiB
Python
# Copyright (c) Facebook, Inc. and its affiliates.
|
|
#
|
|
# This software may be used and distributed according to the terms of the
|
|
# GNU General Public License version 2.
|
|
|
|
# autopull.py - utilities to pull commits automatically.
|
|
|
|
from __future__ import absolute_import
|
|
|
|
import re
|
|
|
|
from . import bookmarks, error, pycompat, registrar, util
|
|
from .i18n import _
|
|
from .node import hex
|
|
|
|
|
|
_commithashre = re.compile(r"\A[0-9a-f]{6,40}\Z")
|
|
_table = {} # {name: (repo, name) -> Optional[pullattempt]}
|
|
|
|
builtinautopullpredicate = registrar.autopullpredicate(_table)
|
|
|
|
|
|
class pullattempt(object):
|
|
"""Describe an auto-pull attempt"""
|
|
|
|
def __init__(
|
|
self,
|
|
friendlyname=None,
|
|
headnodes=None,
|
|
headnames=None,
|
|
bookmarknames=None,
|
|
source=None,
|
|
):
|
|
# Name to display during pull. ex. "commit abcdef", "bookmark foo".
|
|
self.friendlyname = friendlyname
|
|
# Parameters passed to repo.pull.
|
|
self.headnodes = headnodes or []
|
|
self.headnames = headnames or []
|
|
self.bookmarknames = bookmarknames or []
|
|
self.source = source
|
|
|
|
def execute(self, repo):
|
|
"""Execute the pull on a repo."""
|
|
# TODO: Change source to "default" once Mononoke rolls out to
|
|
# everywhere.
|
|
if self.source:
|
|
source = self.source
|
|
elif self.bookmarknames and "infinitepushbookmark" in repo.ui.paths:
|
|
source = "infinitepushbookmark"
|
|
elif "infinitepush" in repo.ui.paths:
|
|
source = "infinitepush"
|
|
elif "default" in repo.ui.paths:
|
|
source = "default"
|
|
else:
|
|
return
|
|
|
|
# Print the "pulling ..." message.
|
|
if self.friendlyname is None:
|
|
name = ", ".join(
|
|
repr(s)
|
|
for s in sorted(
|
|
set(
|
|
self.bookmarknames
|
|
+ self.headnames
|
|
+ [hex(n) for n in self.headnodes]
|
|
)
|
|
)
|
|
)
|
|
else:
|
|
name = self.friendlyname
|
|
path = repo.ui.paths.get(source)
|
|
if path is None:
|
|
return
|
|
url = str(path.url)
|
|
repo.ui.status_err(_("pulling %s from %r\n") % (name, url))
|
|
|
|
# Pull.
|
|
#
|
|
# When pulling a single commit. Also pull selectivepull bookmarks so
|
|
# it does not end up with lagged master issues.
|
|
bookmarknames = list(self.bookmarknames)
|
|
if self.headnodes or self.headnames:
|
|
bookmarknames += bookmarks.selectivepullbookmarknames(repo, source)
|
|
try:
|
|
repo.pull(
|
|
source,
|
|
bookmarknames=bookmarknames,
|
|
headnodes=self.headnodes,
|
|
headnames=self.headnames,
|
|
)
|
|
except Exception as ex:
|
|
repo.ui.status_err(_("pull failed: %s\n") % ex)
|
|
|
|
def trymerge(self, other):
|
|
"""Merge this pullattempt with another pullattempt.
|
|
|
|
If pullattempt cannot be merged, return None.
|
|
"""
|
|
if self.source != other.source:
|
|
return None
|
|
return pullattempt(
|
|
bookmarknames=self.bookmarknames + other.bookmarknames,
|
|
headnodes=self.headnodes + other.headnodes,
|
|
headnames=self.headnames + other.headnames,
|
|
source=self.source,
|
|
)
|
|
|
|
|
|
def _cachedstringmatcher(pattern, _cache={}):
|
|
# _cache is shared across function calls
|
|
result = _cache.get(pattern)
|
|
if result is None:
|
|
result = util.stringmatcher(pattern)[-1]
|
|
_cache[pattern] = result
|
|
return result
|
|
|
|
|
|
def trypull(repo, xs):
|
|
"""Pull the list of given names xs.
|
|
|
|
Return true if pull succeeded for all names. Does not raise.
|
|
"""
|
|
# Do not attempt to pull the same name twice.
|
|
repo._autopulled = getattr(repo, "_autopulled", set())
|
|
xs = [x for x in xs if x not in repo._autopulled]
|
|
if not xs:
|
|
return False
|
|
repo._autopulled.update(xs)
|
|
|
|
# If paths.default is not set. Do not attempt to pull.
|
|
if repo.ui.paths.get("default") is None:
|
|
return False
|
|
|
|
def sortkey(tup):
|
|
name, func = tup
|
|
return (func._priority, name)
|
|
|
|
# Try autopull functions.
|
|
funcs = [
|
|
func
|
|
for _name, func in sorted(_table.items(), key=lambda t: (t[1]._priority, t[0]))
|
|
]
|
|
if not funcs:
|
|
return
|
|
|
|
# Collect all attempts.
|
|
attempts = []
|
|
for x in xs:
|
|
for func in funcs:
|
|
attempt = func(repo, x)
|
|
if attempt:
|
|
assert isinstance(attempt, pullattempt)
|
|
attempts.append(attempt)
|
|
|
|
# Merge all pullattempts and execute it.
|
|
if attempts:
|
|
attempt = attempts[0]
|
|
for other in attempts[1:]:
|
|
merged = attempt.trymerge(other)
|
|
if merged:
|
|
attempt = merged
|
|
else:
|
|
attempt.execute(repo)
|
|
attempt = other
|
|
attempt.execute(repo)
|
|
unfi = repo
|
|
return all(x in unfi for x in xs)
|
|
|
|
return False
|
|
|
|
|
|
def rewritepullrevs(repo, revs):
|
|
"""Rewrite names used by 'pull -r REVS' by applying autopull functions that
|
|
have rewritepullrev set. For example, this can be used to rewrite ["D123"] to
|
|
["COMMIT_HASH"].
|
|
"""
|
|
funcs = [
|
|
func
|
|
for _name, func in sorted(_table.items(), key=lambda t: (t[1]._priority, t[0]))
|
|
if func._rewritepullrev
|
|
]
|
|
newrevs = []
|
|
for rev in revs:
|
|
rewritten = None
|
|
for func in funcs:
|
|
if not rewritten:
|
|
attempt = func(repo, rev, rewritepullrev=True)
|
|
if attempt:
|
|
if attempt.bookmarknames:
|
|
raise error.ProgrammingError(
|
|
"rewriting 'pull -r REV' into bookmark pulls is unsupported"
|
|
)
|
|
rewritten = [hex(n) for n in attempt.headnodes] + attempt.headnames
|
|
if rewritten:
|
|
repo.ui.status_err(
|
|
_("rewriting pull rev %r into %s\n")
|
|
% (rev, ", ".join(repr(r) for r in rewritten))
|
|
)
|
|
newrevs += rewritten
|
|
else:
|
|
newrevs.append(rev)
|
|
return newrevs
|
|
|
|
|
|
@builtinautopullpredicate("remotenames", priority=10)
|
|
def _pullremotebookmarks(repo, x):
|
|
# Pull remote names like "remote/foo" automatically.
|
|
pattern = repo.ui.config("remotenames", "autopullpattern")
|
|
if pattern and "/" in x:
|
|
matchfn = _cachedstringmatcher(pattern)
|
|
if matchfn(x):
|
|
remotename, name = bookmarks.splitremotename(x)
|
|
if remotename in {"default", "remote"}:
|
|
# XXX: remotename should be the source, but is ignored here.
|
|
# This is because "remote/scratch/x" might require special
|
|
# remote name (ex. "infinitepushbookmark", not
|
|
# "remote"/"default") to handle.
|
|
source = None
|
|
else:
|
|
source = remotename
|
|
return pullattempt(bookmarknames=[name], headnames=[name], source=source)
|
|
|
|
|
|
@builtinautopullpredicate("commits", priority=20)
|
|
def _pullcommits(repo, x):
|
|
# Pull commit hashes automatically.
|
|
if repo.ui.configbool("ui", "autopullcommits") and _commithashre.match(x):
|
|
return pullattempt(headnames=[x])
|
|
|
|
|
|
@builtinautopullpredicate("hoistednames", priority=30)
|
|
def _pullhoistnames(repo, x):
|
|
# Pull hoist remote names automatically. For example, "foo" -> "remote/foo".
|
|
hoistpattern = repo.ui.config("remotenames", "autopullhoistpattern")
|
|
if hoistpattern:
|
|
matchfn = _cachedstringmatcher(hoistpattern)
|
|
if matchfn(x):
|
|
# XXX: remotenames.hoist config should be the "source" but is
|
|
# ignored here. See "_pullremotebookmarks" for reasons.
|
|
return pullattempt(bookmarknames=[x])
|
|
|
|
|
|
def loadpredicate(ui, extname, registrarobj):
|
|
for name, func in pycompat.iteritems(registrarobj._table):
|
|
if name in _table:
|
|
raise error.ProgrammingError("namespace '%s' is already registered", name)
|
|
_table[name] = func
|