11#![ allow( dead_code, unused_imports, unused_variables) ]
2- use crate :: git:: run_git;
3- use crate :: state:: State ;
4- use colored:: Colorize ;
2+ use std:: { env, fs:: canonicalize} ;
53
64use anyhow:: { Context , Result , anyhow, bail, ensure} ;
75use clap:: { Parser , Subcommand } ;
6+ use colored:: Colorize ;
87use git:: {
9- DEFAULT_REMOTE , GitBranchStatus , after_text, git_branch_status, git_checkout_main, git_fetch,
10- git_get_upstream, git_remote_main, git_sha, is_ancestor, run_git_status, shas_match,
8+ DEFAULT_REMOTE ,
9+ GitBranchStatus ,
10+ after_text,
11+ git_branch_status,
12+ git_checkout_main,
13+ git_fetch,
14+ git_get_upstream,
15+ git_remote_main,
16+ git_sha,
17+ is_ancestor,
18+ run_git_status,
19+ shas_match,
1120} ;
12- use state:: { Branch , RebaseStep } ;
13- use std:: env;
14- use std:: fs:: canonicalize;
21+ use state:: { Branch , RestackStep , StackMethod } ;
1522use tracing:: level_filters:: LevelFilter ;
16- use tracing_subscriber:: layer:: SubscriberExt as _;
17- use tracing_subscriber:: util:: SubscriberInitExt ; //prelude::*;
23+ use tracing_subscriber:: { layer:: SubscriberExt as _, util:: SubscriberInitExt } ;
24+
25+ use crate :: { git:: run_git, state:: State } ; //prelude::*;
1826
1927mod git;
2028mod state;
@@ -42,6 +50,8 @@ enum Command {
4250 #[ arg( long, short, default_value_t = false ) ]
4351 fetch : bool ,
4452 } ,
53+ /// Open the git-stack state file in an editor for manual editing.
54+ EditState ,
4555 /// Restack your active branch and all branches in its related stack.
4656 Restack {
4757 /// The name of the branch to restack.
@@ -144,6 +154,7 @@ fn inner_main() -> Result<()> {
144154 Some ( Command :: Checkout { branch_name } ) => {
145155 state. checkout ( & repo, current_branch, current_upstream, branch_name)
146156 }
157+ Some ( Command :: EditState ) => state. edit_config ( ) ,
147158 Some ( Command :: Restack {
148159 branch,
149160 fetch,
@@ -261,7 +272,7 @@ fn recur_tree(
261272 }
262273
263274 println ! (
264- "{} ({}) {}{}{}" ,
275+ "{} ({}) {}{}{}{} " ,
265276 match ( is_current_branch, branch_status. is_descendent) {
266277 ( true , true ) => branch. name. truecolor( 142 , 192 , 124 ) . bold( ) ,
267278 ( true , false ) => branch. name. truecolor( 215 , 153 , 33 ) . bold( ) ,
@@ -310,7 +321,11 @@ fn recur_tree(
310321 } else {
311322 String :: new( )
312323 }
313- }
324+ } ,
325+ match branch. stack_method {
326+ StackMethod :: ApplyMerge => " (apply-merge)" . truecolor( 142 , 192 , 124 ) ,
327+ StackMethod :: Merge => " (merge)" . truecolor( 142 , 192 , 124 ) ,
328+ } ,
314329 ) ;
315330 if let Some ( note) = & branch. note {
316331 print ! ( " " ) ;
@@ -353,7 +368,7 @@ fn status(mut state: State, repo: &str, orig_branch: &str, fetch: bool) -> Resul
353368 }
354369 let trunk = state. ensure_trunk ( repo) ?;
355370
356- let Some ( tree) = state. get_tree ( repo) else {
371+ let Some ( tree) = state. get_tree_mut ( repo) else {
357372 eprintln ! (
358373 "No stack tree found for repo {repo}." ,
359374 repo = repo. truecolor( 178 , 178 , 218 )
@@ -392,7 +407,7 @@ fn restack(
392407
393408 tracing:: debug!( ?plan, "Restacking branches with plan..." ) ;
394409 git_checkout_main ( None ) ?;
395- for RebaseStep { parent, branch } in plan {
410+ for RestackStep { parent, branch } in plan {
396411 tracing:: debug!(
397412 "Starting branch: {} [pwd={}]" ,
398413 restack_branch,
@@ -406,74 +421,96 @@ fn restack(
406421 branch. name,
407422 parent
408423 ) ;
409- tracing:: debug!( "Force-pushing '{}' to {DEFAULT_REMOTE}..." , branch. name) ;
410424 if push && !shas_match ( & format ! ( "{DEFAULT_REMOTE}/{}" , branch. name) , & branch. name ) {
411425 run_git ( & [
412426 "push" ,
413- "-fu" ,
427+ match branch. stack_method {
428+ StackMethod :: ApplyMerge => {
429+ tracing:: debug!(
430+ "Force-pushing '{}' to {DEFAULT_REMOTE}..." ,
431+ branch. name
432+ ) ;
433+ "-fu"
434+ }
435+ StackMethod :: Merge => "-u" ,
436+ } ,
414437 DEFAULT_REMOTE ,
415438 & format ! ( "{branch_name}:{branch_name}" , branch_name = branch. name) ,
416439 ] ) ?;
417440 }
418- continue ;
419441 } else {
420442 tracing:: info!( "Branch '{}' is not stacked on '{}'..." , branch. name, parent) ;
421443 make_backup ( & run_version, branch, & source) ?;
422444
423- if let Some ( lkg_parent) = branch. lkg_parent . as_deref ( ) {
424- tracing:: info!( "LKG parent: {}" , lkg_parent) ;
425- if is_ancestor ( lkg_parent, & source) ? {
426- let patch_rev = format ! ( "{}..{}" , & lkg_parent, & branch. name) ;
427- tracing:: info!( "Creating patch {}" , & patch_rev) ;
428- // The branch is still on top of the LKG parent. Let's create a format-patch of the
429- // difference, and apply it on top of the new parent.
430- let format_patch = run_git ( & [ "format-patch" , "--stdout" , & patch_rev] ) ?. output ( ) ;
431- run_git ( & [ "checkout" , "-B" , & branch. name , & parent] ) ?;
432- let Some ( format_patch) = format_patch else {
433- tracing:: debug!( "No diff between LKG and branch?!" ) ;
434- continue ;
435- } ;
436- tracing:: info!( "Applying patch..." ) ;
437- let rebased = run_git_status ( & [ "am" , "--3way" ] , Some ( & format_patch) ) ?. success ( ) ;
438- if !rebased {
439- eprintln ! (
440- "{} did not complete successfully." ,
441- "`git am`" . green( ) . bold( )
442- ) ;
443- eprintln ! ( "Run `git mergetool` to resolve conflicts." ) ;
444- eprintln ! (
445- "Once you have finished with {}, re-run `git stack restack`." ,
446- "`git am --continue`" . green( ) . bold( )
447- ) ;
448- std:: process:: exit ( 1 ) ;
445+ match branch. stack_method {
446+ StackMethod :: ApplyMerge => {
447+ if let Some ( lkg_parent) = branch. lkg_parent . as_deref ( ) {
448+ tracing:: info!( "LKG parent: {}" , lkg_parent) ;
449+ if is_ancestor ( lkg_parent, & source) ? {
450+ let patch_rev = format ! ( "{}..{}" , & lkg_parent, & branch. name) ;
451+ tracing:: info!( "Creating patch {}" , & patch_rev) ;
452+ // The branch is still on top of the LKG parent. Let's create a format-patch of the
453+ // difference, and apply it on top of the new parent.
454+ let format_patch =
455+ run_git ( & [ "format-patch" , "--stdout" , & patch_rev] ) ?. output ( ) ;
456+ run_git ( & [ "checkout" , "-B" , & branch. name , & parent] ) ?;
457+ let Some ( format_patch) = format_patch else {
458+ tracing:: debug!( "No diff between LKG and branch?!" ) ;
459+ continue ;
460+ } ;
461+ tracing:: info!( "Applying patch..." ) ;
462+ let rebased =
463+ run_git_status ( & [ "am" , "--3way" ] , Some ( & format_patch) ) ?. success ( ) ;
464+ if !rebased {
465+ eprintln ! (
466+ "{} did not complete successfully." ,
467+ "`git am`" . green( ) . bold( )
468+ ) ;
469+ eprintln ! ( "Run `git mergetool` to resolve conflicts." ) ;
470+ eprintln ! (
471+ "Once you have finished with {}, re-run `git stack restack`." ,
472+ "`git am --continue`" . green( ) . bold( )
473+ ) ;
474+ std:: process:: exit ( 1 ) ;
475+ }
476+ if push {
477+ git_push ( & branch. name ) ?;
478+ }
479+ continue ;
480+ } else {
481+ tracing:: info!(
482+ "Branch '{}' is not on top of the LKG parent. Using `git rebase`..." ,
483+ branch. name
484+ ) ;
485+ run_git ( & [ "checkout" , & branch. name ] ) ?;
486+ let rebased = run_git_status ( & [ "rebase" , & parent] , None ) ?. success ( ) ;
487+
488+ if !rebased {
489+ eprintln ! (
490+ "{} did not complete automatically." ,
491+ "Rebase" . blue( ) . bold( )
492+ ) ;
493+ eprintln ! ( "Run `git mergetool` to resolve conflicts." ) ;
494+ eprintln ! (
495+ "Once you have finished the {}, re-run this script." ,
496+ "rebase" . blue( ) . bold( )
497+ ) ;
498+ std:: process:: exit ( 1 ) ;
499+ }
500+ if push {
501+ git_push ( & branch. name ) ?;
502+ }
503+ tracing:: info!( "Rebase completed successfully. Continuing..." ) ;
504+ }
449505 }
450- if push {
451- git_push ( & branch. name ) ?;
452- }
453- continue ;
454- } else {
455- tracing:: info!(
456- "Branch '{}' is not on top of the LKG parent. Falling through to `git rebase`..." ,
457- branch. name
458- ) ;
506+ }
507+ StackMethod :: Merge => {
508+ run_git ( & [ "checkout" , & branch. name ] )
509+ . with_context ( || format ! ( "checking out {}" , branch. name) ) ?;
510+ run_git ( & [ "merge" , & parent] )
511+ . with_context ( || format ! ( "merging {parent} into {}" , branch. name) ) ?;
459512 }
460513 }
461- run_git ( & [ "checkout" , & branch. name ] ) ?;
462- let rebased = run_git_status ( & [ "rebase" , & parent] , None ) ?. success ( ) ;
463-
464- if !rebased {
465- eprintln ! ( "{} did not complete automatically." , "Rebase" . blue( ) . bold( ) ) ;
466- eprintln ! ( "Run `git mergetool` to resolve conflicts." ) ;
467- eprintln ! (
468- "Once you have finished the {}, re-run this script." ,
469- "rebase" . blue( ) . bold( )
470- ) ;
471- std:: process:: exit ( 1 ) ;
472- }
473- if push {
474- git_push ( & branch. name ) ?;
475- }
476- tracing:: info!( "Rebase completed successfully. Continuing..." ) ;
477514 }
478515 }
479516 tracing:: info!( "Restoring starting branch '{}'..." , restack_branch) ;
0 commit comments