How do I run git rebase --interactive in non-interactive manner?

After some thinking and research, the answer turned out to be trivial: git rebase -i takes the editor name from the well-known EDITOR/VISUAL environment variables, so overriding that to point to a non-interactive script does the job.

However, EDITOR/VISUAL applies indifferently to the list of commits, commit messages when rewording and anything else. So, since http://git.kernel.org/?p=git/git.git;a=commit;h=821881d88d3012a64a52ece9a8c2571ca00c35cd , there's a special environment variable GIT_SEQUENCE_EDITOR which applies only to the commit list.

So, the recipe to re-order or flatten commits is:

Run: GIT_SEQUENCE_EDITOR=<script> git rebase -i <params>. Your <script> should accept a single argument: the path to the file containing the standard rebase commit list. It should rewrite it in-place and exit. Usual rebase processing happens after that.


Adding on to @pfalcon's answer, you can use sed as your GIT_SEQUENCE_EDITOR. For example, I wanted to edit each commit, so I did this:

GIT_SEQUENCE_EDITOR="sed -i -re 's/^pick /e /'" git rebase -i

The variable GIT_SEQUENCE_EDITOR was initially used to change the editor. It is possible to pass a script to this variable to use git rebase -i in a non-interactive manner. So it is possible to use:

GIT_SEQUENCE_EDITOR="sed -i -re 's/^pick 134567/e 1234567/'" git rebase -i 1234567^

This command will run sed on file provided by git rebase -i. It will change the line pick 134567 into e 1234567 (and so, edit the commit 1234567). You can change e with r (rework), f (fixup), s (squash) or d (drop) (the latter is not supported by old versions of git).

Based on that, I wrote a script that automatizes this task:

#!/bin/bash

ACTION=$1
COMMIT=$(git rev-parse --short $2)
[[ "$COMMIT" ]] || exit 1
CORRECT=
for A in p pick r reword e edit s squash f fixup d drop t split; do
     [[ $ACTION == $A ]] && CORRECT=1
done 
[[ "$CORRECT" ]] || exit 1
git merge-base --is-ancestor $COMMIT HEAD || exit 1
if [[ $ACTION == "drop" || $ACTION == "d" ]]; then
    GIT_SEQUENCE_EDITOR="sed -i -e '/^pick $COMMIT/d'" git rebase -i $COMMIT^^
elif [[ $ACTION == "split" || $ACTION == "t" ]]; then
    GIT_SEQUENCE_EDITOR="sed -i -e 's/^pick $COMMIT/edit $COMMIT/'" git rebase -i $COMMIT^^ || exit 1
    git reset --soft HEAD^
    echo "Hints:"
    echo "  Select files to be commited using 'git reset', 'git add' or 'git add -p'"
    echo "  Commit using 'git commit -c $COMMIT'"
    echo "  Finish with 'git rebase --continue'"
else
    GIT_SEQUENCE_EDITOR="sed -i -e 's/^pick $COMMIT/$1 $COMMIT/'" git rebase -i $COMMIT^^
fi

The first argument should be one action. The script uses the same action names than git-rebase. It also add 'split' action (and allow to use drop with old versions of git).

It also checks that commit you ask for is an ancestor of HEAD. It a common (and really annoying) mistake with rebase -i.

The second argument is the commit to want to edit/delete/split/reword.

Then you add an alias to your .gitconfig:

[alias]
  autorebase = ! path_to_your_script