@@ -25,11 +25,11 @@ use lib::core::repo_ext::RepoExt;
25
25
use lib:: util:: { ExitCode , EyreExitOr } ;
26
26
use tracing:: { instrument, warn} ;
27
27
28
- use git_branchless_opts:: { SwitchOptions , TraverseCommitsOptions } ;
29
- use git_branchless_revset:: resolve_default_smartlog_commits;
28
+ use git_branchless_opts:: { ResolveRevsetOptions , Revset , SwitchOptions , TraverseCommitsOptions } ;
29
+ use git_branchless_revset:: { resolve_commits , resolve_default_smartlog_commits} ;
30
30
use git_branchless_smartlog:: make_smartlog_graph;
31
31
use lib:: core:: config:: get_next_interactive;
32
- use lib:: core:: dag:: { sorted_commit_set, CommitSet , Dag } ;
32
+ use lib:: core:: dag:: { sorted_commit_set, union_all , CommitSet , Dag } ;
33
33
use lib:: core:: effects:: Effects ;
34
34
use lib:: core:: eventlog:: { EventLogDb , EventReplayer } ;
35
35
use lib:: core:: formatting:: Pluralize ;
@@ -515,17 +515,28 @@ pub fn switch(
515
515
/// query in the commit selector.
516
516
Interactive ( String ) ,
517
517
518
+ /// The target expression is probably a git revision or reference and
519
+ /// should be passed directly to git for resolution.
520
+ Passthrough ( String ) ,
521
+
522
+ /// The target expression should be interpreted as a revset.
523
+ Revset ( Revset ) ,
524
+
518
525
/// No target expression was specified.
519
526
None ,
520
527
}
521
528
let initial_query = match ( interactive, target) {
522
- ( true , Some ( target) ) => Target :: Interactive ( target. clone ( ) ) ,
529
+ ( true , Some ( target) ) => Target :: Interactive ( target. to_string ( ) ) ,
523
530
( true , None ) => Target :: Interactive ( String :: new ( ) ) ,
524
- ( false , Some ( _) ) => Target :: None ,
531
+ ( false , Some ( target) ) => match repo. revparse_single_commit ( target. to_string ( ) . as_ref ( ) ) {
532
+ Ok ( Some ( _) ) => Target :: Passthrough ( target. to_string ( ) ) ,
533
+ Ok ( None ) | Err ( _) => Target :: Revset ( target. clone ( ) ) ,
534
+ } ,
525
535
( false , None ) => Target :: None ,
526
536
} ;
527
537
let target: Option < CheckoutTarget > = match initial_query {
528
538
Target :: None => None ,
539
+ Target :: Passthrough ( target) => Some ( CheckoutTarget :: Unknown ( target) ) ,
529
540
Target :: Interactive ( initial_query) => {
530
541
match prompt_select_commit (
531
542
None ,
@@ -548,6 +559,35 @@ pub fn switch(
548
559
None => return Ok ( Err ( ExitCode ( 1 ) ) ) ,
549
560
}
550
561
}
562
+ Target :: Revset ( target) => {
563
+ let commit_sets = resolve_commits (
564
+ effects,
565
+ & repo,
566
+ & mut dag,
567
+ & [ target. clone ( ) ] ,
568
+ & ResolveRevsetOptions :: default ( ) ,
569
+ ) ?;
570
+
571
+ let commit_set = union_all ( & commit_sets) ;
572
+ let commit_set = dag. query_heads ( commit_set) ?;
573
+ let commits = sorted_commit_set ( & repo, & dag, & commit_set) ?;
574
+
575
+ match commits. as_slice ( ) {
576
+ [ commit] => Some ( CheckoutTarget :: Unknown ( commit. get_oid ( ) . to_string ( ) ) ) ,
577
+ [ ] | [ ..] => {
578
+ writeln ! (
579
+ effects. get_error_stream( ) ,
580
+ "Cannot switch to target: expected '{target}' to contain 1 head, but found {}." ,
581
+ commits. len( )
582
+ ) ?;
583
+ writeln ! (
584
+ effects. get_error_stream( ) ,
585
+ "Target should be a commit or a set of commits with exactly 1 head. Aborting."
586
+ ) ?;
587
+ return Ok ( Err ( ExitCode ( 1 ) ) ) ;
588
+ }
589
+ }
590
+ }
551
591
} ;
552
592
553
593
let additional_args = {
0 commit comments