From ee78907854d33b4beef6da10433cc5ab2715c44a Mon Sep 17 00:00:00 2001 From: Michael Lohmann Date: Sun, 7 Jan 2024 11:27:39 +0100 Subject: [PATCH] add chapter interactive rebase --- create_challenge.sh | 23 +++++++++++-- lib.sh | 2 +- .../interactive-rebase-continued.md | 32 +++++++++++++++++++ src/hooks/pre-rebase | 3 ++ src/nuggits | 1 + test.sh | 10 ++++-- .../interactive-rebase-sequence-editor.sh | 6 ++++ todo.md | 2 -- 8 files changed, 72 insertions(+), 7 deletions(-) create mode 100644 src/07_rebase_merge/interactive-rebase-continued.md create mode 100755 src/hooks/pre-rebase create mode 100755 test_helpers/interactive-rebase-sequence-editor.sh diff --git a/create_challenge.sh b/create_challenge.sh index 91f1224..33be280 100755 --- a/create_challenge.sh +++ b/create_challenge.sh @@ -79,10 +79,29 @@ git add commit.md commit -m "Add description on commit" CHAPTER_COMMIT_COMMIT="$(git rev-parse --short @)" +create_chapter interactive rebase +git switch --detach main +printf "%s" '# Interactive rebase + +An interactive rebase lets you (as the name suggests) interact with it instead of just step by step applying each patch. This can be incredibly useful after you created a lot of `git commit -m "WIP: Coffee break"`' > interactive-rebase.md +git add interactive-rebase.md +commit -m "WIP: Coffee break" +echo " commits, but after you are done with your feature, you don't want keep them forever and you don't want to bother the reviewer (and you yourself when you will eventually try to find a bug in your code and look at the log) with them." >> interactive-rebase.md +git add interactive-rebase.md +commit -m "WIP: finish sentence on interactive rebases" +INTERACTIVE_REBASE_EXAMPLE_PICKS="$(git log --oneline main..@ | sed 's/^/pick /' | sed 's/$/\\/g') +[...]" +# FIXME +CHAPTER_AMEND_COMMIT=CHAPTER_AMEND_COMMIT +replace_placeholders "$DOCDIR/07_rebase_merge/interactive-rebase-continued.md" >> interactive-rebase.md +git add interactive-rebase.md +commit -m "Finish describing interactive rebases + +TODO: squash commits..." +INTERACTIVE_REBASE_COMMIT="$(git rev-parse --short @)" + create_chapter rebase/merge git switch --detach main -# TODO: create interactive rebase commit -INTERACTIVE_REBASE_COMMIT="INTERACTIVE_REBASE_COMMIT" replace_placeholders "$DOCDIR/07_rebase_merge/combine_history.md" > combine_history.md git add combine_history.md commit -m "Add description on how to combine branches" diff --git a/lib.sh b/lib.sh index 93ff8f1..092d64e 100644 --- a/lib.sh +++ b/lib.sh @@ -79,7 +79,7 @@ add_player_config() { } replace_placeholders() { - sed -e "s/INTERACTIVE_REBASE_BASE_COMMIT/$INTERACTIVE_REBASE_BASE_COMMIT/" \ + sed -e "s/CHAPTER_AMEND_COMMIT/$CHAPTER_AMEND_COMMIT/" \ -e "s/BRANCH_COMMIT/$BRANCH_COMMIT/" \ -e "s/NUMBER_OF_NUGGITS/$NUMBER_OF_NUGGITS"/ \ -e "s/ALMOST_CREDITS_HASH/$ALMOST_CREDITS_HASH"/ \ diff --git a/src/07_rebase_merge/interactive-rebase-continued.md b/src/07_rebase_merge/interactive-rebase-continued.md new file mode 100644 index 0000000..9c2b1d9 --- /dev/null +++ b/src/07_rebase_merge/interactive-rebase-continued.md @@ -0,0 +1,32 @@ +When you run +```sh +git rebase -i CHAPTER_AMEND_COMMIT +``` +(or `--interactive`), an editor will open, showing a list of all the commits that would be applied. + +CAREFUL: in contrast to the output of `git log` where the newest commit is at the top, in this list the last commit is at the bottom! + +The output starts with a block of lines that look like this: +``` +INTERACTIVE_REBASE_EXAMPLE_PICKS +``` +But what does this mean? Fortunately at the bottom of the file it shows a short summary on all the possible commands. + +In short: when you just save and quit, it will be exactly as a normal rebase. But with this version you can do more! None-exhaustive, but very useful: +- reorder commits by just swapping the lines +- "reword" a commit message: keep all the changes in code, but change the commit message (an editor will open with the old message, as soon as this line of the rebase todo is reached) +- "squash" or combine two (or more) commits into a single one: + - put the lines with relevant commits directly below each other + - keep the first commit "pick" (at least for the simple version - go wild if you know what you are doing 😉) + - change the "pick" of the lines below of the commits you want to combine into the previous one to "squash" + - When saving/quitting and so the interactive rebase starts it will open a text editor again, this time with all the commit messages one below the other. Feel free to combine them into a single meaningful one +- if you want the same as squashing, but you don't care about the commit message of this commit (e.g. you just fixed a bug that you want to squash into an older commit and you (read "I") are lazy and just type `git commit -am "fixi"`, because this commit is just a short-lived tool that enables the interactive rebase squashing), you can replace the "squash" with "fixup" and it won't leave you to remove the "fixi" from the combined commit message. +- "drop" commits: make them (and their changes!) disappear, e.g. if I have a branch with multiple unrelated changes and want to hand them in meaningful chunks to review, I tend to: + - first create a new branch with all the changed + - do an interactive rebase and kick out all the not needed commits + CAREFUL: an alternative to replacing the "pick" with a "drop" is just to delete the line! This can accidentally happen (or intentionally because in my editor it is faster than replacing the "pick" 😅) +- "edit": Allows you to amend to a previous commit TODO: explain amend + +git actually only cares about the action (e.g. "pick") and the following commit hash. The commit message in the end is just for decoration. Changing it in the "todo file" does nothing to the commit - use "reword" instead. + +Also note: there is a shorthand notation for all (documented) actions, so e.g. "f" for "fixup" diff --git a/src/hooks/pre-rebase b/src/hooks/pre-rebase new file mode 100755 index 0000000..3aabfae --- /dev/null +++ b/src/hooks/pre-rebase @@ -0,0 +1,3 @@ +#!/bin/sh + +echo 'nuggit: ItsAllAboutTheRebase' diff --git a/src/nuggits b/src/nuggits index 04e1258..790d9b2 100644 --- a/src/nuggits +++ b/src/nuggits @@ -9,3 +9,4 @@ MyFirstBranch LogCat AnnotateMeIfYouCan CuriosityKilledTheCat +ItsAllAboutTheRebase diff --git a/test.sh b/test.sh index 4b4e262..5da3ed4 100755 --- a/test.sh +++ b/test.sh @@ -105,8 +105,14 @@ redeem_nuggit AnnotateMeIfYouCan expect "git switch --detach -q the-first-tag" to succeed ' -xit 'TODO: find title for combine_history testcase' <&1' to contain "nuggit: ItsAllAboutTheRebase" +redeem_nuggit ItsAllAboutTheRebase +EOF + +xit 'interactive rebase succeeds' </dev/null EOF it 'An invalid nuggit should show an error' ' diff --git a/test_helpers/interactive-rebase-sequence-editor.sh b/test_helpers/interactive-rebase-sequence-editor.sh new file mode 100755 index 0000000..83a2865 --- /dev/null +++ b/test_helpers/interactive-rebase-sequence-editor.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +# Mac sed has a different format for -i, so manually replace that file +# hack: just replace "pick" with "fixup" until line 1000 (which does not exist), but I can't be bothered to find a nicer way not to change the first line... +sed '2,1000s/^pick/fixup/' < "$1" > "$1.bak" +mv "$1.bak" "$1" diff --git a/todo.md b/todo.md index fdb169f..4f7607e 100644 --- a/todo.md +++ b/todo.md @@ -22,7 +22,6 @@ - hooks (e.g. automatically add nuggit to commit message) - post-merge - post-rewrite e.g. for commit --amend - - pre-rebase - `--word-diff --word-diff-regex=.` (nuggit hidden "inbetween" two commits) - submodule - `blame` first column @@ -68,7 +67,6 @@ See documentation in the git repository under Documentation/githooks.txt - pre-merge-commit - commit-msg - post-commit -- pre-rebase - post-merge - pre-push - pre-receive