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

GitScriptingGit RebaseNon Interactive

Git Problem Overview


Is it possible to do following?

  1. Make git rebase --interactive to just output standard boilerplate to a file, instead to outputting to a file and opening it in editor.
  2. Let the user edit the file.
  3. Let user re-run git rebase with the name of edited file.
  4. Go on with the usual rebase process.

Usecase: scripted rebasing of course. See https://stackoverflow.com/questions/4981061/how-to-re-order-commits-in-git-non-interactively for example.

Git Solutions


Solution 1 - Git

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.

Solution 2 - Git

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

Solution 3 - Git

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

Solution 4 - Git

Expanding on pfalcon's answer:

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

If you have an environment variable that contains the contents you want:

GIT_SEQUENCE_EDITOR='echo "$REBASE_DATA" >' git rebase -i [<additional params>]

Catting a file would work too:

GIT_SEQUENCE_EDITOR='cat rebase_data_file >' git rebase -i [<additional params>]

Solution 5 - Git

You can use touch as the editor which will touch the file so it will appear modified. For example

GIT_SEQUENCE_EDITOR=touch git rebase -i [commit]

To alias it, given baseline as a tag I want to rebase against

git config alias.baseline '!GIT_SEQUENCE_EDITOR=touch git rebase -i baseline'

The alias works under Windows because the shell it is executing is bash not cmd.

Solution 6 - Git

interactive modes brings up the set editor to work with.
the editor in use can be retrieved with:

git config --get core.editor

So, if you set a non-interactive editor - that is an editor that accepts commands on stdin, you can work with --interactive in a non-interactive way :)
I know for sure vim accepts commands, and so does the standard editor ed, ofcourse.

so, hold the interactive editor (if wanted)

$ ied="$(git config --get core.editor)"

set the non-interactive editor

$ git config --unset-all core.editor
$ git config --add core.editor ed

and do work with it..

$ printf '%s\n' "some-ed-cmd" "another-ed-cmd" "wq" | git rebase -i HEAD~5

and restore the editor (if wanted)

$ git config --unset-all core.editor
$ git config --add core.editor "$ied"

Solution 7 - Git

I found the solution. You can use:

$ GIT_SEQUENCE_EDITOR=true git rebase -i --autosquash $COMMIT_HASH~1

Solution 8 - Git

To do exactly what the original question asks, use sed -i '1s/^/b\n/' as your editor

git -c sequence.editor="sed -i '1s/^/b\n/'" rebase --interactive
Explanation
  • git -c specifies to run the following git command with a specified setting
  • sequence.editor the setting that determines the "editor" which will edit the redbase-todo file
  • sed -i run sed command in place
    • 1s on the first line
    • ^ match the beginning
    • b\n insert b and a newline \n character. b or break is the rebase command to halt the interactive rebase.
  • rebase --interactive run rebase command

This stops the interactive rebase immediately. Now you can edit the .git/rebase-merge/git-rebase-todo and run git rebase --continue.

Solution 9 - Git

As others have mentioned, you need to provide a custom GIT_SEQUENCE_EDITOR value that will modify the interactive rebase file. If you just want to perform an action on a single commit, you can do it as follows:

# Runs "edit" on HEAD~3
GIT_SEQUENCE_EDITOR="sed -i -ze 's/^pick/edit/'" git rebase -i HEAD~3

Alternatively, here's a function to generalize this:

# Usage: git-rebase-custom edit HEAD~3
git-rebase-custom() {
    local action=$1
    shift

    GIT_SEQUENCE_EDITOR="sed -i -ze 's/^pick/$action/'" git rebase -i "$@"
}

Solution 10 - Git

Based on Jezz's answer, I made a shell-agnostic script (GitReb) which works with multiple-argument revisions, :/<text> syntax, root commit and also does some sanity checks.

I also made it simpler and removed the t/split action and delete->drop conversion which IMO are out of this script's scope.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionpfalconView Question on Stackoverflow
Solution 1 - GitpfalconView Answer on Stackoverflow
Solution 2 - GitLeif WicklandView Answer on Stackoverflow
Solution 3 - GitJérôme PouillerView Answer on Stackoverflow
Solution 4 - GitJames FoucarView Answer on Stackoverflow
Solution 5 - GitArchimedes TrajanoView Answer on Stackoverflow
Solution 6 - Gitc00kiemon5terView Answer on Stackoverflow
Solution 7 - GitWooseong KimView Answer on Stackoverflow
Solution 8 - GitCervEdView Answer on Stackoverflow
Solution 9 - GitMatthew D. ScholefieldView Answer on Stackoverflow
Solution 10 - GitJohn GliksbergView Answer on Stackoverflow