diff --git a/hgext3rd/lfs/wrapper.py b/hgext3rd/lfs/wrapper.py --- a/hgext3rd/lfs/wrapper.py +++ b/hgext3rd/lfs/wrapper.py @@ -12,6 +12,7 @@ from mercurial import ( error, filelog, + node, revlog, util, ) @@ -180,6 +181,10 @@ # if remotestore is a null store, upload is a no-op and can be skipped return isinstance(repo.svfs.lfsremoteblobstore, blobstore._nullremote) +def candownload(repo): + # if remotestore is a null store, downloads will lead to nothing + return not isinstance(repo.svfs.lfsremoteblobstore, blobstore._nullremote) + def uploadblobsfromrevs(repo, revs): '''upload lfs blobs introduced by revs diff --git a/remotefilelog/__init__.py b/remotefilelog/__init__.py --- a/remotefilelog/__init__.py +++ b/remotefilelog/__init__.py @@ -124,6 +124,7 @@ pass if lfsmod: lfsmod.wrapfilelog(remotefilelog.remotefilelog) + fileserverclient._lfsmod = lfsmod extensions.afterloaded('lfs', _lfsloaded) # debugdata needs remotefilelog.len to work diff --git a/remotefilelog/fileserverclient.py b/remotefilelog/fileserverclient.py --- a/remotefilelog/fileserverclient.py +++ b/remotefilelog/fileserverclient.py @@ -15,9 +15,9 @@ from mercurial import ( error, httppeer, + revlog, sshpeer, util, - util, wireproto, ) @@ -37,6 +37,7 @@ fetched = 0 fetchmisses = 0 +_lfsmod = None _downloading = _('downloading') def getcachekey(reponame, file, id): @@ -586,6 +587,27 @@ raise error.Abort(_("unable to download %d files") % len(missingids)) fetchcost += time.time() - start + self._lfsprefetch(fileids) + + def _lfsprefetch(self, fileids): + if not _lfsmod or not hasattr(self.repo.svfs, 'lfslocalblobstore'): + return + if not _lfsmod.wrapper.candownload(self.repo): + return + pointers = [] + store = self.repo.svfs.lfslocalblobstore + for file, id in fileids: + nodehash = bin(id) + rlog = self.repo.file(file) + if rlog.flags(nodehash) & revlog.REVIDX_EXTSTORED: + text = rlog.revision(nodehash, raw=True) + p = _lfsmod.pointer.deserialize(text) + oid = p.oid() + if not store.has(oid): + pointers.append(p) + if len(pointers) > 0: + self.repo.svfs.lfsremoteblobstore.readbatch(pointers, store) + assert all(store.has(p.oid()) for p in pointers) def logstacktrace(self): import traceback diff --git a/tests/library.sh b/tests/library.sh --- a/tests/library.sh +++ b/tests/library.sh @@ -38,6 +38,30 @@ EOF } +hgcloneshallowlfs() { + local name + local dest + local lfsdir + orig=$1 + shift + dest=$1 + shift + lfsdir=$1 + shift + hg clone --shallow --config "extensions.lfs=" --config "lfs.url=$lfsdir" --config remotefilelog.reponame=master $orig $dest $@ + cat >> $dest/.hg/hgrc <> $HGRCPATH < [extensions] + > lfs=$TESTDIR/../hgext3rd/lfs + > [lfs] + > url=file://$LFSPATH + > EOF + $ cat >> .hg/hgrc < [remotefilelog] + > server=True + > EOF + $ echo x > x + $ echo z > z + $ hg commit -qAm x + $ echo x2 > x + $ echo y > y + $ hg commit -qAm y + $ echo large > large + $ hg --config 'lfs.threshold=1' commit -qAm y + $ hg bookmark foo + $ hg debuglfsupload -r tip + + $ cd .. + +# prefetch a revision + + $ hgcloneshallowlfs ssh://user@dummy/master shallow file://$LFSPATH --noupdate + streaming all changes + 2 files to transfer, 774 bytes of data + transferred 774 bytes in * seconds (*/sec) (glob) + searching for changes + no changes found + $ cd shallow + + $ hg prefetch -r 0 + 2 files fetched over 1 fetches - (2 misses, 0.00% hit ratio) over *s (glob) + + $ hg cat -r 0 x + x + +# prefetch a range of revisions + + $ clearcache + $ hg prefetch -r 0::1 + 4 files fetched over 1 fetches - (4 misses, 0.00% hit ratio) over *s (glob) + + $ hg cat -r 0 x + x + $ hg cat -r 1 x + x2 + +# prefetch certain files + + $ clearcache + $ hg prefetch -r 1 x + 1 files fetched over 1 fetches - (1 misses, 0.00% hit ratio) over *s (glob) + + $ hg cat -r 1 x + x2 + + $ hg cat -r 1 y + y + 1 files fetched over 1 fetches - (1 misses, 0.00% hit ratio) over *s (glob) + +# prefetch large file + + $ hg prefetch -r 2 + 2 files fetched over 1 fetches - (2 misses, 0.00% hit ratio) over *s (glob) + +# prefetch on pull when configured + + $ printf "[remotefilelog]\npullprefetch=bookmark()\n" >> .hg/hgrc + $ hg strip tip + saved backup bundle to $TESTTMP/shallow/.hg/strip-backup/730e2b7b175c-acada81e-backup.hg (glob) + + $ clearcache + $ hg pull + pulling from ssh://user@dummy/master + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 0 changes to 0 files + updating bookmark foo + (run 'hg update' to get a working copy) + prefetching file contents + 4 files fetched over 1 fetches - (4 misses, 0.00% hit ratio) over *s (glob) + + $ hg up tip + 4 files updated, 0 files merged, 0 files removed, 0 files unresolved