Skip to content

Commit 56367cd

Browse files
committed
implement creating a new "latest branch"
Signed-off-by: Kipras Melnikovas <kipras@kipras.org>
1 parent 7a5ce01 commit 56367cd

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

@@ -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

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)