Sunday, February 7, 2016

how-to: run git interactive rebase non-interactively

TL;DR

git alias to autosquash fixup commits non-interactivly:
git config --global alias.fixup '!GIT_SEQUENCE_EDITOR=true git rebase -i --autosquash'

non-interactive interactive rebase

In the normal workflow, git interactive rebase presents the user with a document to edit interactively to modify and reorder commits. What if you want to run it non-interactively?

Yes, you can do this. And yes I have a use-case!

I'd like to apply/squash all my --fixup commits automatically without spending time in that editor screen. This is an easy use case because I don't change anything in the editor window, that's handled by the --autosquash flag to rebase.

-i
--interactive
Make a list of the commits which are about to be rebased. Let the user edit that list before rebasing. This mode can also be used to split commits (see SPLITTING COMMITS below).

The commit list format can be changed by setting the configuration option rebase.instructionFormat. A customized instruction format will automatically have the long commit hash prepended to the format.
-- git rebase documentation

Git respects the traditional unix environment variables $EDITOR and $VISUAL. Overriding one of those will change the editor that is run during interactive rebase but also changes the editor used while in the rebase to change commit messages and etc.

A third environment variable was added by Peter Oberndorfer: $GIT_SEQUENCE_EDITOR. This editor is only used for the interactive rebase edit. As an aside, this is a wonderful commit message.

"rebase -i": support special-purpose editor to edit insn sheet

The insn sheet used by "rebase -i" is designed to be easily editable by any text editor, but an editor that is specifically meant for it (but is otherwise unsuitable for editing regular text files) could be useful by allowing drag & drop reordering in a GUI environment, for example.

The GIT_SEQUENCE_EDITOR environment variable and/or the sequence.editor configuration variable can be used to specify such an editor, while allowing the usual editor to be used to edit commit log messages. As usual, the environment variable takes precedence over the configuration variable.

It is envisioned that other "sequencer" based tools will use the same mechanism.

Signed-off-by: Peter Oberndorfer
Signed-off-by: Junio C Hamano

-- http://git.kernel.org/cgit/git/git.git/commit/?id=821881d88d3012a64a52ece9a8c2571ca00c35cd

Did I just know all this? No, not really. I hadn't heard of GIT_SEQUENCE_EDITOR until reading the code for the silly little git --blame-someone-else script going around. That gave me the keyword to search to find this excellent Stack Overflow answer.

Autosquash

For my usage, I just need an editor that completes successfully without modifying the input. Luckily I have one of those, a bunch really, but lets go with the simplest:true. Yep, this will run an autosquash interactive rebase without showing me the pick window, where $COMMIT_SHA is the reference for the rebase.
GIT_SEQUENCE_EDITOR=true git rebase -i --autosquash $COMMIT_SHA

By defining the environment variable at the start of the command, it is only stored in the environment for that command.

I've now stored this as a git alias to test out. I'll let you know how it goes.
git config --global alias.fixup '!GIT_SEQUENCE_EDITOR=true git rebase -i --autosquash'

Examples

git fixup master
rebase the current branch to HEAD of master and autosquash the commits.

3 comments:

Anonymous said...

One may want to add --autostash as well. Thank you!

Unknown said...

What's the "!" for in the alias version?

Andrew Grangaard said...

Unknown asked, "What's the '!' for in the alias version?"

Hi Unknown! thank you for the question.

Adding a "!" to the start of a git alias identifies the alias as a full shell command to be executed. Aliases without a "!" are simple aliases to a git command.

This is documented in the "alias" section of git-config documentation.


Quote:

"If the alias expansion is prefixed with an exclamation point, it will be treated as a shell command. For example, defining alias.new = !gitk --all --not ORIG_HEAD, the invocation git new is equivalent to running the shell command gitk --all --not ORIG_HEAD. Note that shell commands will be executed from the top-level directory of a repository, which may not necessarily be the current directory. GIT_PREFIX is set as returned by running git rev-parse --show-prefix from the original current directory. See git-rev-parse[1]."