@@ -33,6 +33,8 @@ use std::fmt::Write;
3333use std:: num:: NonZeroU32 ;
3434use std:: path:: { Path , PathBuf } ;
3535use std:: sync:: atomic:: AtomicBool ;
36+ use std:: sync:: atomic:: Ordering ;
37+ use std:: sync:: Arc ;
3638use tokio:: time:: { sleep, Duration } ;
3739use tracing:: warn;
3840use url:: Url ;
@@ -460,27 +462,65 @@ impl Runner {
460462 }
461463}
462464
463- // TODO: Should clone_git_repository be async
464- // gitoxide doesn't currently have async implemented for http backend
465+ // TODO: Should we re-export gix::progress to provide a means to monitor progress
465466// TODO: Is "clippy::result_large_err" the best solution to GitCheckoutError
466467
468+ /// Fetch and checkout a given worktree on a thread
469+ ///
470+ /// See: [clone_git_repository_sync]
471+ pub async fn clone_git_repository (
472+ parent_path : & Path ,
473+ repo_url : & str ,
474+ head_ref : Option < & str > ,
475+ refspecs : impl IntoIterator < Item = impl AsRef < str > > ,
476+ depth : Option < u32 > ,
477+ cancel_token : CancellationToken ,
478+ ) -> Result < PathBuf , GitCheckoutError > {
479+ let parent_path = parent_path. to_owned ( ) ;
480+ let repo_url = repo_url. to_owned ( ) ;
481+ let head_ref = head_ref. map ( |a| a. to_owned ( ) ) ;
482+ let should_interrupt: Arc < AtomicBool > = Default :: default ( ) ;
483+ let should_interrupt_cancel = should_interrupt. clone ( ) ;
484+ let refspecs: Vec < _ > = refspecs
485+ . into_iter ( )
486+ . map ( |s| s. as_ref ( ) . to_owned ( ) )
487+ . collect ( ) ;
488+ // offload the clone operation to one of the tokio runtimes blocking threads
489+ tokio:: select! {
490+ result = tokio:: task:: spawn_blocking( move || {
491+ clone_git_repository_sync(
492+ & parent_path,
493+ & repo_url,
494+ head_ref. as_deref( ) ,
495+ refspecs,
496+ depth,
497+ & should_interrupt,
498+ )
499+ } ) => result?,
500+ _ = cancel_token. cancelled( ) => {
501+ should_interrupt_cancel. store( true , Ordering :: SeqCst ) ;
502+ Err ( GitCheckoutError :: Cancelled )
503+ }
504+ }
505+ }
506+
467507/// Fetch and checkout a given worktree
468508///
469509/// This creates a new path for the repo as gitoxide deletes it on failure.
470510#[ allow( clippy:: result_large_err) ]
471- pub fn clone_git_repository (
511+ pub fn clone_git_repository_sync (
472512 parent_path : & Path ,
473513 repo_url : & str ,
474514 head_ref : Option < & str > ,
475515 refspecs : impl IntoIterator < Item = impl AsRef < str > > ,
476516 depth : Option < u32 > ,
517+ should_interrupt : & AtomicBool ,
477518) -> Result < PathBuf , GitCheckoutError > {
478519 let repo_dir = tempfile:: Builder :: new ( )
479520 . prefix ( "repo_" )
480521 . tempdir_in ( parent_path) ?;
481522
482523 // TODO: Should we expose the ability to interrupt / report progress
483- let should_interrupt = AtomicBool :: new ( false ) ;
484524 let mut progress = progress:: Discard ;
485525
486526 // TODO: Is Options::isolated correct here?
@@ -543,7 +583,7 @@ pub fn clone_git_repository(
543583 }
544584
545585 let checkout_progress = progress. add_child ( "checkout" . to_string ( ) ) ;
546- let ( checkout, _outcome) = fetch. fetch_then_checkout ( checkout_progress, & should_interrupt) ?;
586+ let ( checkout, _outcome) = fetch. fetch_then_checkout ( checkout_progress, should_interrupt) ?;
547587
548588 let repo = checkout. persist ( ) ;
549589
@@ -596,7 +636,7 @@ pub fn clone_git_repository(
596636 repo. objects . clone ( ) . into_arc ( ) ?,
597637 & files_progress,
598638 & bytes_progress,
599- & should_interrupt,
639+ should_interrupt,
600640 Default :: default ( ) ,
601641 ) ?;
602642
0 commit comments