@@ -20,7 +20,12 @@ import { noop } from "./util/noop";
2020import { uniq } from "./util/uniq" ;
2121import { parseTodoOfStackedRebase } from "./parse-todo-of-stacked-rebase/parseTodoOfStackedRebase" ;
2222import { Termination } from "./util/error" ;
23- import { GoodCommand , namesOfRebaseCommandsThatMakeRebaseExitToPause } from "./parse-todo-of-stacked-rebase/validator" ;
23+ import { assertNever } from "./util/assertNever" ;
24+ import {
25+ GoodCommand , //
26+ namesOfRebaseCommandsThatMakeRebaseExitToPause ,
27+ StackedRebaseCommand ,
28+ } from "./parse-todo-of-stacked-rebase/validator" ;
2429
2530// console.log = () => {};
2631
@@ -466,17 +471,155 @@ export const gitStackedRebase = async (
466471
467472 const goodCommands : GoodCommand [ ] = parseTodoOfStackedRebase ( pathToStackedRebaseTodoFile ) ;
468473
474+ // eslint-disable-next-line no-inner-declarations
475+ async function createBranchForCommand (
476+ cmd : GoodCommand & { commandName : StackedRebaseCommand & "branch-end-new" }
477+ ) : Promise < void > {
478+ const newBranchName : string = cmd . targets ! [ 0 ] ;
479+ const force : number = 0 ;
480+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
481+ const targetCommitSHA : string = cmd . commitSHAThatBranchPointsTo ! ;
482+ const targetCommit : Git . Commit = await Git . Commit . lookup ( repo , targetCommitSHA ) ;
483+ await Git . Branch . create ( repo , newBranchName , targetCommit , force ) ;
484+ }
485+
486+ /**
487+ * TODO should probably go into `validator`
488+ */
489+ const oldLatestBranchCmdIndex : number = goodCommands . findIndex ( ( cmd ) => cmd . commandName === "branch-end-last" ) ;
490+ // if (indexOfLatestBranch === -1) // TODO verify in validator
491+
492+ const isThereANewLatestBranch : boolean = oldLatestBranchCmdIndex !== goodCommands . length - 1 ;
493+
494+ if ( isThereANewLatestBranch ) {
495+ let newLatestBranchCmdIndex : number | null = null ;
496+ for ( let i = goodCommands . length - 1 ; i >= 0 ; i -- ) {
497+ const cmd = goodCommands [ i ] ;
498+ if ( cmd . commandName === "branch-end-new" ) {
499+ newLatestBranchCmdIndex = i ;
500+ break ;
501+ }
502+ }
503+ if ( newLatestBranchCmdIndex === null || newLatestBranchCmdIndex <= oldLatestBranchCmdIndex ) {
504+ // TODO validator
505+ const when =
506+ newLatestBranchCmdIndex === null
507+ ? "at all"
508+ : newLatestBranchCmdIndex <= oldLatestBranchCmdIndex
509+ ? "after the branch-end-latest command"
510+ : "" ; // assertNever(newLatestBranchCmdIndex);
511+
512+ throw new Termination (
513+ "\n" +
514+ `apparently a new latest branch was attempted (by adding commands _after_ the "branch-end-last")` +
515+ `\nbut there was no "branch-end-new" command (${ when } )`
516+ ) ;
517+ }
518+
519+ /**
520+ * strategy:
521+ *
522+ * 1. create the "branch-end-new" at the appropriate position
523+ *
524+ * now, both the new & the old "latest" branches are pointing to the same commit
525+ *
526+ * 2. reset the old "latest" branch to the newly provided, earlier position
527+ *
528+ * 3. update the command names of the 2 branches
529+ * 3.1 in "goodCommands"
530+ * 3.2 in our "git-rebase-todo" file?
531+ *
532+ *
533+ * strategy v2:
534+ * 1. same
535+ * 2.
536+ *
537+ *
538+ * note 1:
539+ * in general, idk if this is the best approach.
540+ * though, it requries the least amount of effort from the user, afaik.
541+ *
542+ * if we instead made the user manually move the "branch-end-latest" to an earlier position (normal / same as here),
543+ * but the also rename it to "branch-end" (extra work),
544+ * and then create a new "branch-end-latest" in the very end (normal / similar to here (just "-latest" instead of "-new")),
545+ *
546+ * it's more steps, and idk if it conveys the picture well,
547+ * because we no longer say "branch-end-new" explicitly,
548+ * nor is it explicit that the "branch-end-last" has been moved.
549+ *
550+ * so then yes, the alternative sucks,
551+ * & this (branch-end-last being not the latest command) is good.
552+ *
553+ * note 2:
554+ * TODO will most likely need to do extra handling for the `rebaseChangedLocalHistory`,
555+ * because even tho we won't change local history _of the commits_,
556+ * the branches do indeed change, and here it's not simply adding a branch in the middle
557+ * (though does that also need extra handling?),
558+ * we're changing the latest branch, so it matters a lot
559+ * and would need to run the `--apply`
560+ * (currently `rebaseChangedLocalHistory` would prevent `--apply` from running).
561+ *
562+ * note 2.1:
563+ * TODO need to support a use-case where the new latest branch
564+ * is not new, i.e. user has had already created it,
565+ * and now has simply moved it after the "branch-end-last".
566+ *
567+ * note 3:
568+ * this logic implies that we should always be doing `--apply`,
569+ * TODO thus consider.
570+ *
571+ */
572+ const oldLatestBranchCmd : GoodCommand = goodCommands [ oldLatestBranchCmdIndex ] ;
573+ const newLatestBranchCmd : GoodCommand = goodCommands [ newLatestBranchCmdIndex ] ;
574+
575+ /**
576+ * create the new "latest branch"
577+ */
578+ await createBranchForCommand ( newLatestBranchCmd as any ) ; // TODO TS
579+
580+ /**
581+ * move the old "latest branch" earlier to it's target
582+ */
583+ await repo . checkoutBranch ( oldLatestBranchCmd . targets ! [ 0 ] ) ;
584+ const commit : Git . Commit = await Git . Commit . lookup ( repo , oldLatestBranchCmd . targets ! [ 0 ] ) ;
585+ await Git . Reset . reset ( repo , commit , Git . Reset . TYPE . HARD , { } ) ;
586+
587+ /**
588+ * go to the new "latest branch".
589+ */
590+ await repo . checkoutBranch ( newLatestBranchCmd . targets ! [ 0 ] ) ;
591+
592+ /**
593+ * TODO update in the actual `git-rebase-todo` file
594+ */
595+
596+ /**
597+ * need to change to "branch-end", instead of "branch-end-new",
598+ * since obviously the branch already exists
599+ */
600+ goodCommands [ oldLatestBranchCmdIndex ] . commandName = "branch-end" ;
601+ goodCommands [ oldLatestBranchCmdIndex ] . commandOrAliasName = "branch-end" ;
602+
603+ goodCommands [ newLatestBranchCmdIndex ] . commandName = "branch-end-last" ;
604+ goodCommands [ newLatestBranchCmdIndex ] . commandOrAliasName = "branch-end-last" ;
605+
606+ /**
607+ * it's fine if the new "latest branch" does not have
608+ * a remote set yet, because `--push` handles that,
609+ * and we regardless wouldn't want to mess anything
610+ * in the remote until `--push` is used.
611+ */
612+ }
613+
469614 for ( const cmd of goodCommands ) {
470615 if ( cmd . rebaseKind === "regular" ) {
471616 regularRebaseTodoLines . push ( cmd . fullLine ) ;
472617 } else if ( cmd . rebaseKind === "stacked" ) {
473618 if ( cmd . commandName === "branch-end-new" ) {
474- const newBranchName : string = cmd . targets ! [ 0 ] ;
475- const force : number = 0 ;
476- const targetCommitSHA : string = cmd . commitSHAThatBranchPointsTo ! ;
477- const targetCommit : Git . Commit = await Git . Commit . lookup ( repo , targetCommitSHA ) ;
478- await Git . Branch . create ( repo , newBranchName , targetCommit , force ) ;
619+ await createBranchForCommand ( cmd as any ) ; // TODO TS
479620 }
621+ } else {
622+ assertNever ( cmd ) ;
480623 }
481624 }
482625
0 commit comments