diff --git a/remotefilelog/remotefilectx.py b/remotefilelog/remotefilectx.py --- a/remotefilelog/remotefilectx.py +++ b/remotefilelog/remotefilectx.py @@ -8,9 +8,27 @@ import collections from mercurial.i18n import _ from mercurial.node import bin, hex, nullid, nullrev -from mercurial import context, util, error, ancestor, phases +from mercurial import context, util, error, ancestor, phases, extensions propertycache = util.propertycache +conduit = None + +def initializeconduit(ui): + global conduit + try: + conduit = extensions.find("fbconduit") + except KeyError: + try: + from hgext3rd import fbconduit as conduit + except ImportError: + ui.warn(_('unable to find fbconduit extension\n')) + return + if not util.safehasattr(conduit, 'conduit_config'): + ui.warn(_('incompatible conduit module; disabling fastlog\n')) + conduit = None + if not conduit.conduit_config(ui): + ui.warn(_('no conduit host specified in config; disabling fastlog\n')) + conduit = None class remotefilectx(context.filectx): def __init__(self, repo, path, changeid=None, fileid=None, @@ -27,6 +45,13 @@ return self._filelog.size(self._filenode) @propertycache + def _conduit(self): + global conduit + if conduit is None: + initializeconduit(self._repo.ui) + return conduit + + @propertycache def _changeid(self): if '_changeid' in self.__dict__: return self._changeid @@ -252,6 +277,12 @@ # obsolescence markers to find a more-likely-correct linkrev. if not seenpublic and pc.phase(repo, ancrev) == phases.public: seenpublic = True + # Use fastlog (if it's enabled for this repo), as it will give + # us the right linknode directly + if repo.ui.configbool('fastlog', 'enabled'): + linknode = self._linknodeviafastlog(repo, srcrev, path) + if linknode and self._verifylinknode(revs, linknode): + return linknode try: repo.fileservice.prefetch([(path, hex(fnode))], force=True) @@ -272,6 +303,31 @@ return linknode + def _linknodeviafastlog(self, repo, srcrev, path): + reponame = repo.ui.config('fbconduit', 'reponame') + if self._conduit is None: + return None + try: + results = self._conduit.call_conduit( + 'scmquery.log_v2', + repo=reponame, + scm_type='hg', + rev=srcrev, + file_paths=[path], + skip=0, + number=1, + ) + if results is None or len(results) != 1: + repo.ui.warn(_('warning: bad fastlog result for (%s)\n') % path) + return None + hash = results[0]['hash'] + if len(hash) != 40: + repo.ui.warn(_('warning: bad fastlog hash (%s)\n') % hash) + return None + return bin(hash) + except Exception as e: + repo.ui.warn(_('warning: call to fastlog failed (%s)\n') % e) + def _verifylinknode(self, revs, linknode): """ Check if a linknode is correct one for the current history. diff --git a/tests/test-remotefilelog-linknodes.t b/tests/test-remotefilelog-linknodes.t --- a/tests/test-remotefilelog-linknodes.t +++ b/tests/test-remotefilelog-linknodes.t @@ -194,3 +194,113 @@ d4a3ed9310e5 => aee31534993a 000000000000 32e6611f6149 aee31534993a => 1406e7411862 000000000000 0632994590a8 1406e7411862 => 000000000000 000000000000 b292c1e3311f + +Test the same scenario as above but with fastlog enabled + + $ cd .. + $ clearcache + + $ rm -rf master + $ rm -rf shallow + $ hginit master + $ cd master + $ cat >> .hg/hgrc < [remotefilelog] + > server=True + > serverexpiration=-1 + > EOF + $ echo x > x + $ hg commit -qAm x + $ echo x >> x + $ hg commit -Aqm xx + $ cd .. + + $ hgcloneshallow ssh://user@dummy/master shallow -q + 1 files fetched over 1 fetches - (1 misses, 0.00% hit ratio) over * (glob) + $ cd shallow + $ echo x >> x + $ hg commit -Aqm xx2 + $ cd ../master + $ echo y >> y + $ hg commit -Aqm yy2 + $ echo x >> x + $ hg commit -Aqm xx2-fake-rebased + $ echo y >> y + $ hg commit -Aqm yy3 + $ cd ../shallow + $ hg pull -q --config remotefilelog.debug=True + $ hg update tip -q + 1 files fetched over 1 fetches - (1 misses, 0.00% hit ratio) over *s (glob) + $ echo x > x + $ hg commit -qAm xx3 + +Verfiy correct linkrev despite fastlog failures + +Case 1: fastlog service calls fails or times out + + $ cat >> .hg/hgrc < [extensions] + > fbconduit=$TESTTMP/bad_conduit.py + > [fastlog] + > enabled=True + > EOF + $ cat > $TESTTMP/bad_conduit.py < def call_conduit(*args, **kwargs): + > raise Exception('error') + > def conduit_config(*args, **kwargs): + > return True + > EOF + $ hg log -f x -T '{node|short} {desc} {phase} {files}\n' + warning: call to fastlog failed (error) + a5957b6bf0bd xx3 draft x + 32e6611f6149 xx2-fake-rebased public x + 0632994590a8 xx public x + b292c1e3311f x public x + 1 files fetched over 1 fetches - (1 misses, 0.00% hit ratio) over *s (glob) + +Case 2: fastlog returns empty results + + $ cat > $TESTTMP/bad_conduit.py < def call_conduit(*args, **kwargs): + > return [] + > def conduit_config(*args, **kwargs): + > return True + > EOF + $ rm $TESTTMP/bad_conduit.pyc + $ hg log -f x -T '{node|short} {desc} {phase} {files}\n' + warning: bad fastlog result for (x) + a5957b6bf0bd xx3 draft x + 32e6611f6149 xx2-fake-rebased public x + 0632994590a8 xx public x + b292c1e3311f x public x + +Case 3: fastlog returns a bad hash + + $ cat > $TESTTMP/bad_conduit.py < def call_conduit(*args, **kwargs): + > return [{'hash': '123456'}] + > def conduit_config(*args, **kwargs): + > return True + > EOF + $ rm $TESTTMP/bad_conduit.pyc + $ hg log -f x -T '{node|short} {desc} {phase} {files}\n' + warning: bad fastlog hash (123456) + a5957b6bf0bd xx3 draft x + 32e6611f6149 xx2-fake-rebased public x + 0632994590a8 xx public x + b292c1e3311f x public x + +Fastlog succeeds and returns the correct results + + $ cat > $TESTTMP/bad_conduit.py < def call_conduit(*args, **kwargs): + > return [{'hash': '32e6611f6149e85f58def77ee0c22549bb6953a2'}] + > def conduit_config(*args, **kwargs): + > return True + > EOF + $ rm $TESTTMP/bad_conduit.pyc + $ hg log -f x -T '{node|short} {desc} {phase} {files}\n' + a5957b6bf0bd xx3 draft x + 32e6611f6149 xx2-fake-rebased public x + 0632994590a8 xx public x + b292c1e3311f x public x