@@ -355,22 +355,22 @@ impl Job {
355
355
Self :: checkout_repo_inner (
356
356
repo_dir. path ( ) ,
357
357
& self . response . git_info . repo_url ,
358
- & self . response . git_info . sha ,
359
- self . response . git_info . refspecs . iter ( ) ,
360
- self . response . git_info . depth ,
358
+ Some ( & self . response . git_info . sha ) ,
359
+ & self . response . git_info . refspecs ,
360
+ Some ( self . response . git_info . depth ) ,
361
361
) ?;
362
362
363
363
Ok ( repo_dir. into_path ( ) )
364
364
}
365
365
366
366
/// Fetch and checkout worktree for job
367
367
#[ allow( clippy:: result_large_err) ]
368
- fn checkout_repo_inner (
368
+ pub fn checkout_repo_inner (
369
369
repo_dir : & Path ,
370
370
repo_url : & str ,
371
- sha : & str ,
372
- refspecs : impl Iterator < Item = impl AsRef < str > > ,
373
- depth : u32 ,
371
+ head_ref : Option < & str > ,
372
+ refspecs : impl IntoIterator < Item = impl AsRef < str > > ,
373
+ depth : Option < u32 > ,
374
374
) -> Result < ( ) , GitCheckoutError > {
375
375
let should_interrupt = AtomicBool :: new ( false ) ;
376
376
let mut progress = progress:: Discard ;
@@ -384,48 +384,92 @@ impl Job {
384
384
) ?;
385
385
386
386
// Specify which refspecs are fetched from remote
387
- let refspecs = refspecs
387
+ let mut refspecs = refspecs
388
+ . into_iter ( )
388
389
. map ( |s| {
389
390
refspec:: parse ( s. as_ref ( ) . into ( ) , refspec:: parse:: Operation :: Fetch )
390
391
. map ( |r| r. to_owned ( ) )
391
392
} )
392
393
. collect :: < Result < Vec < _ > , _ > > ( ) ?;
393
394
394
- let mut fetch_opts = remote:: ref_map:: Options :: default ( ) ;
395
- fetch_opts. extra_refspecs . extend ( refspecs) ;
396
- fetch = fetch. with_fetch_options ( fetch_opts) ;
395
+ let mut sha = None ;
396
+
397
+ if let Some ( head_ref) = head_ref {
398
+ if let Ok ( object_id) = gix:: ObjectId :: from_hex ( head_ref. as_bytes ( ) ) {
399
+ // TODO: I need to specify that the sha should be fetched from remote, right?
400
+ // Is there a format for a sha as a refspec?
401
+ if let Ok ( refspec) = refspec:: parse (
402
+ format ! ( "+{head_ref}" ) . as_str ( ) . into ( ) ,
403
+ refspec:: parse:: Operation :: Fetch ,
404
+ ) {
405
+ refspecs. push ( refspec. to_owned ( ) ) ;
406
+ }
407
+ sha = Some ( object_id) ;
408
+ } else {
409
+ // TODO: For some reason this doesn't create refs/heads/{reference}
410
+ if let Ok ( refspec) = refspec:: parse (
411
+ format ! ( "+refs/heads/{head_ref}:refs/remotes/origin/{head_ref}" )
412
+ . as_str ( )
413
+ . into ( ) ,
414
+ refspec:: parse:: Operation :: Fetch ,
415
+ ) {
416
+ refspecs. push ( refspec. to_owned ( ) ) ;
417
+ }
418
+ }
419
+ }
397
420
398
- if depth > 0 {
399
- fetch = fetch. with_shallow ( remote:: fetch:: Shallow :: DepthAtRemote (
400
- NonZeroU32 :: new ( depth) . unwrap ( ) ,
401
- ) )
402
- } ;
421
+ if !refspecs. is_empty ( ) {
422
+ let mut fetch_opts = remote:: ref_map:: Options :: default ( ) ;
423
+ fetch_opts. extra_refspecs . extend ( refspecs) ;
424
+ fetch = fetch. with_fetch_options ( fetch_opts) ;
425
+ }
426
+
427
+ if let Some ( depth) = depth {
428
+ if depth > 0 {
429
+ fetch = fetch. with_shallow ( remote:: fetch:: Shallow :: DepthAtRemote (
430
+ NonZeroU32 :: new ( depth) . unwrap ( ) ,
431
+ ) ) ;
432
+ }
433
+ }
403
434
404
435
let checkout_progress = progress. add_child ( "checkout" . to_string ( ) ) ;
405
436
let ( checkout, _outcome) =
406
437
fetch. fetch_then_checkout ( checkout_progress, & should_interrupt) ?;
407
438
408
439
let repo = checkout. persist ( ) ;
409
440
410
- // Checkout worktree at specific sha
411
- let sha = gix:: ObjectId :: from_hex ( sha. as_bytes ( ) ) ?;
412
- let root_tree_id = repo
413
- . try_find_object ( sha) ?
414
- . ok_or ( GitCheckoutError :: MissingCommit ) ?;
441
+ let root_tree_id = if let Some ( sha) = sha {
442
+ // Checkout worktree at specific sha
443
+ repo. try_find_object ( sha) ?
444
+ . ok_or ( GitCheckoutError :: MissingCommit ) ?
445
+ } else if let Some ( reference) = head_ref {
446
+ repo
447
+ // TODO: This should be refs/heads/{reference} but it doesn't exist
448
+ . try_find_reference ( format ! ( "refs/remotes/origin/{reference}" ) . as_str ( ) ) ?
449
+ . ok_or ( GitCheckoutError :: MissingCommit ) ?
450
+ . peel_to_id_in_place ( ) ?
451
+ . object ( ) ?
452
+ } else {
453
+ // Checkout head
454
+ repo. head ( ) ?
455
+ . try_peel_to_id_in_place ( ) ?
456
+ . ok_or ( GitCheckoutError :: MissingCommit ) ?
457
+ . object ( ) ?
458
+ } ;
415
459
let root_tree = root_tree_id. peel_to_tree ( ) ?. id ;
416
460
417
- let workdir = repo. work_dir ( ) . ok_or_else ( || {
418
- clone:: checkout:: main_worktree:: Error :: BareRepository {
419
- git_dir : repo. git_dir ( ) . to_owned ( ) ,
420
- }
421
- } ) ?;
422
-
423
461
let index = index:: State :: from_tree ( & root_tree, & repo. objects , Default :: default ( ) )
424
462
. map_err ( |err| clone:: checkout:: main_worktree:: Error :: IndexFromTree {
425
463
id : root_tree,
426
464
source : err,
427
465
} ) ?;
428
466
467
+ let workdir = repo. work_dir ( ) . ok_or_else ( || {
468
+ clone:: checkout:: main_worktree:: Error :: BareRepository {
469
+ git_dir : repo. git_dir ( ) . to_owned ( ) ,
470
+ }
471
+ } ) ?;
472
+
429
473
let files_progress = progress. add_child_with_id (
430
474
"files" . to_string ( ) ,
431
475
clone:: checkout:: main_worktree:: ProgressId :: CheckoutFiles . into ( ) ,
0 commit comments