From 608005ac0676c70de9426792ab64baf9bdc9b364 Mon Sep 17 00:00:00 2001 From: Matthew Flatt Date: Sat, 2 Feb 2019 11:09:34 -0700 Subject: [PATCH] git-checkout: faster discovery of commit in common case When the desired reference is not an advertised commit, then try pulling just a few commits --- at depth 8, 16, and 32 -- from the "master" branch to check whether the commit can be found that way. If not, fall back to the exhaustive search that requires a full download. This should help with the common case that a package reference into the Racket repo is a few commits behind the current master branch (because the package server hasn't scanned the repo recently enough). It's much faster to disover that the commit is within the first 32, which is almost always is, than to download the entire repository. --- racket/collects/net/git-checkout.rkt | 59 +++++++++++++++++++--------- 1 file changed, 41 insertions(+), 18 deletions(-) diff --git a/racket/collects/net/git-checkout.rkt b/racket/collects/net/git-checkout.rkt index cb29b16d29..ac1d8dcfe0 100644 --- a/racket/collects/net/git-checkout.rkt +++ b/racket/collects/net/git-checkout.rkt @@ -51,7 +51,7 @@ #:strict-links? [strict-links? #f] #:username [username (current-git-username)] #:password [password (current-git-password)]) - (let retry-loop ([given-depth given-depth]) + (let retry-loop ([given-depth given-depth] [try-limit-depth (and given-depth 8)] [try-only-master? #t]) (define tmp-dir (or given-tmp-dir (make-temporary-file "git~a" 'directory))) (define port (or given-port (case transport @@ -91,7 +91,7 @@ ;; Find the commits needed for `ref`: (define-values (ref-commit ; #f or an ID string want-commits) ; list of ID string - (select-commits ref refs status)) + (select-commits ref refs status try-only-master?)) (unless dest-dir (write-pkt o) ; clean termination @@ -101,13 +101,15 @@ (or ref-commit ref))))) (define depth (and given-depth - ref-commit + (or ref-commit try-limit-depth) (cond - [(member "shallow" server-capabilities) - given-depth] - [else - (status "Server does not support `shallow`") - #f]))) + [(member "shallow" server-capabilities) + (if ref-commit + given-depth + try-limit-depth)] + [else + (status "Server does not support `shallow`") + #f]))) (unless dumb-protocol? ;; Tell the server which commits we need @@ -171,8 +173,7 @@ (lambda () (esc (lambda () (status "Unexpected EOF; retrying without depth") - (retry-loop #f))))))) - + (retry-loop #f #f #f))))))) (maybe-save-objects objs "objs") ;; Convert deltas into full objects withing `tmp`: @@ -182,8 +183,21 @@ (define commit (or ref-commit - (find-commit-as-reference ref obj-ids))) - + (find-commit-as-reference ref obj-ids + (and (or try-only-master? + (and try-limit-depth + (eqv? depth try-limit-depth))) + (lambda () + (esc (lambda () + (cond + [(and depth (eqv? depth try-limit-depth) + (try-limit-depth . < . 32)) + (status "no matching commit found; trying deeper search") + (retry-loop given-depth (* try-limit-depth 2) try-only-master?)] + [else + (status "no matching commit found; trying broader search") + (retry-loop given-depth #f #f)])))))))) + ;; Extract the tree from the packfile objects: (status "Extracting tree to ~a" dest-dir) (extract-commit-tree (hex-string->bytes commit) @@ -341,7 +355,7 @@ ;; initial response. If we can, the list of requested IDs will be ;; just that one. Otherwise, we'll have to return a list of all ;; IDs, and then we'll look for the reference later. -(define (select-commits ref refs status) +(define (select-commits ref refs status try-only-master?) (define ref-looks-like-id? (regexp-match? #rx"^[0-9a-f]+$" ref)) (define ref-rx (byte-regexp (bytes-append @@ -366,9 +380,16 @@ (cond [ref-commit (list ref-commit)] [ref-looks-like-id? - (status "Requested reference looks like commit id; getting all commits") - (for/list ([ref (in-list refs)]) - (cadr ref))] + (cond + [try-only-master? + (status "Requested reference looks like commit id; try within master") + (define-values (master-ref-commit want-commits) + (select-commits "master" refs status #f)) + want-commits] + [else + (status "Requested reference looks like commit id; getting all commits") + (for/list ([ref (in-list refs)]) + (cadr ref))])] [else (raise-git-error 'git "could not find requested reference\n reference: ~a" ref)])) @@ -601,7 +622,7 @@ ;; ---------------------------------------- ;; Finding a commit id -(define (find-commit-as-reference ref obj-ids) +(define (find-commit-as-reference ref obj-ids fail-not-found) (define rx (id-ref->regexp ref)) (define matches (for/list ([(id obj) (in-hash obj-ids)] @@ -611,7 +632,9 @@ (cond [(= 1 (length matches)) (car matches)] [(null? matches) - (raise-git-error 'git-checkout "no commit found matching id: ~a" ref)] + (if fail-not-found + (fail-not-found) + (raise-git-error 'git-checkout "no commit found matching id: ~a" ref))] [else (raise-git-error 'git-checkout "found multiple commits matching id: ~a" ref)]))