Skip to content

Commit c4c6a3b

Browse files
committed
implement creating a new "latest branch"
Signed-off-by: Kipras Melnikovas <[email protected]>
1 parent 9f46981 commit c4c6a3b

File tree

3 files changed

+157
-11
lines changed

3 files changed

+157
-11
lines changed

git-stacked-rebase.ts

Lines changed: 149 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,12 @@ import { noop } from "./util/noop";
2020
import { uniq } from "./util/uniq";
2121
import { parseTodoOfStackedRebase } from "./parse-todo-of-stacked-rebase/parseTodoOfStackedRebase";
2222
import { 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

parse-todo-of-stacked-rebase/validator.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -362,11 +362,11 @@ export function validate(
362362
reasonsIfBad.push("initial command must be `branch-end-initial`");
363363
}
364364
}
365-
if (index === linesOfEditedRebaseTodo.length - 1) {
366-
if (commandName !== "branch-end-last") {
367-
reasonsIfBad.push("last command must be `branch-end-last`");
368-
}
369-
}
365+
// if (index === linesOfEditedRebaseTodo.length - 1) {
366+
// if (commandName !== "branch-end-last") {
367+
// reasonsIfBad.push("last command must be `branch-end-last`");
368+
// }
369+
// }
370370
}
371371

372372
if (commandUsedAtLines[commandName].length > allEitherRebaseCommands[commandName].maxUseCount) {

util/assertNever.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export function assertNever(_x: never): never {
2+
throw new Error(`assertNever called (with value ${_x}) - should've been disallowed at compile-time`);
3+
}

0 commit comments

Comments
 (0)