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