diff --git a/mercurial/revset.py b/mercurial/revset.py --- a/mercurial/revset.py +++ b/mercurial/revset.py @@ -2202,6 +2202,10 @@ tree = revsetlang.analyze(tree) tree = revsetlang.optimize(tree) posttreebuilthook(tree, repo) + # add commits to pinned revs if hashes of hidden revs is passed and + # accessing hidden commmits is allowed + if repo and repo.filtername in ['visible-hidden', 'visible-warnhidden']: + _updatepinnedrevs(tree, repo) return makematcher(tree) def makematcher(tree): @@ -2230,3 +2234,66 @@ # 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 _updatepinnedrevs(tree, repo): + """ extracts the symbols that looks like hashes and add them to + repo._pinnedrevs for accessing hidden hashes + """ + + hiddenset = set() + cl = repo.unfiltered().changelog + symbols = gethashsymbols(tree, len(cl)) + for revnode in symbols: + try: + revnode = cl._partialmatch(revnode) + except error.LookupError: + revnode = None + if revnode is not None: + rev = cl.rev(revnode) + if rev not in repo.changelog: + hiddenset.add(rev) + + if hiddenset: + if repo.filtername == 'visible-warnhidden': + repo.ui.warn(_("warning: accessing hidden changesets %s " + "for write operation\n") % + (",".join([str(repo.unfiltered()[l]) + for l in hiddenset]))) + + repo._pinnedrevs.update(hiddenset) + repo.invalidatevolatilesets()