diff --git a/mercurial/revset.py b/mercurial/revset.py --- a/mercurial/revset.py +++ b/mercurial/revset.py @@ -22,6 +22,7 @@ obsutil, pathutil, phases, + pycompat, registrar, repoview, revsetlang, @@ -2195,6 +2196,11 @@ tree = revsetlang.analyze(tree) tree = revsetlang.optimize(tree) posttreebuilthook(tree, repo) + # add revs to visibility exceptions if hashes of hidden revs is passed and + # accessing hidden commmits is allowed + if repo and repo.filtername in ('visible-allowhidden', + 'visible-warnhidden'): + _updateexceptions(tree, repo) return makematcher(tree) def makematcher(tree): @@ -2223,3 +2229,71 @@ # tell hggettext to extract docstrings from these functions: i18nfunctions = symbols.values() + +hashre = util.re.compile('[0-9a-fA-F]{1,40}') +_listtuple = ('symbol', '_list') + +def _ishashsymbol(symbol, maxrev): + """ returns true if symbol looks like a hash """ + + try: + n = int(symbol) + if n <= maxrev: + # It's a rev number + return False + except ValueError: + pass + return hashre.match(symbol) + +def gethashsymbols(tree, maxrev): + """ + returns the list of symbols of the tree that look like hashes + for example for the revset 3::abe3ff it will return ('abe3ff') + """ + + if not tree: + return [] + + results = [] + if len(tree) in (2, 3) and tree[0] == "symbol": + results.append(tree[1]) + elif tree[0] == "func" and tree[1] == _listtuple: + # the optimiser will group sequence of hash request + results += tree[2][1].split('\0') + elif len(tree) >= 2: + for subtree in tree[1:]: + results += gethashsymbols(subtree, maxrev) + # return directly, we don't need to filter symbols again + return results + return [s for s in results if _ishashsymbol(s, maxrev)] + +def _updateexceptions(tree, repo): + """ + extracts the symbols that looks like hashes and add them to + visibility exceptions set for accessing hidden hashes + """ + + hiddenset = set() + unfi = repo.unfiltered() + unficl = unfi.changelog + cl = repo.changelog + symbols = gethashsymbols(tree, len(unficl)) + pmatch = unficl._partialmatch + for revnode in symbols: + try: + revnode = pmatch(revnode) + except error.LookupError: + revnode = None + if revnode is not None: + rev = unficl.rev(revnode) + if rev not in cl: + hiddenset.add(rev) + + if hiddenset: + if repo.filtername == 'visible-warnhidden': + hsets = _(",").join([pycompat.bytestr(unfi[l]) for l in hiddenset]) + repo.ui.warn(_("warning: accessing hidden changesets for write " + "operation: %s\n") % hsets) + + repo.addvisibilityexceptions(hiddenset) + repo.invalidatevolatilesets()