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

15,645

Solution 1

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

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

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

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

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.

Share:
15,645
pfalcon
Author by

pfalcon

Updated on June 14, 2022

Comments

  • pfalcon
    pfalcon about 2 years

    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 how to re-order commits in Git non-interactively for example.

  • c00kiemon5ter
    c00kiemon5ter almost 12 years
    did not know of GIT_SEQUENCE_EDITOR, seems useful ;)
  • pfalcon
    pfalcon almost 12 years
    Thanks, we probably started to write answers at similar time, I didn't see yours before I posted mine ;-)
  • me_and
    me_and almost 12 years
    Or, instead of creating a script for this sole purpose, just use the existing command true, which ignores any arguments and has a fixed return code of 0.
  • Paŭlo Ebermann
    Paŭlo Ebermann almost 10 years
    @me_and this only helps if you want to do a rebase -i without actually reordering the commits.
  • MarcH
    MarcH over 9 years
    I think it's simpler to re-define EDITOR, either on a per-session or per-command basis.
  • MarcH
    MarcH over 9 years
    Here is another example that fixes a typo in the last five commit messages: EDITOR="sed -i -e 's/borken/broken/g'" GIT_SEQUENCE_EDITOR="sed -i -e 's/pick/reword/g'" git rebase -i HEAD~5
  • Matthieu Moy
    Matthieu Moy about 8 years
    I'd strongly advise against doing git config --unset-all or whatever can modify the user's config file in a script. To set a git config variable for one command, use git -c var=val, and in this case setting EDITOR is much simpler. It's an environment variable, so applies only to the current process, it won't disturb other processes or write anything to disk.
  • MarcH
    MarcH over 7 years
    For some unknown reason EDITOR is now ignored by git --version 2.5.5. On the other hand VISUAL still works. So here's another example re-generating the 5 last Change-Id. This assumes the Gerrit commit hook is installed. VISUAL="sed -i -e '/^[[:blank:]]*Change-Id/ d'" GIT_SEQUENCE_EDITOR="sed -i -e 's/pick/reword/g'" git rebase -i HEAD~5 . Tested successfully with git version 2.5.5
  • MarcH
    MarcH over 7 years
    Also: make sure git config --global core.editor returns empty.
  • John Vandenberg
    John Vandenberg almost 7 years
    I found setting EDITOR doesnt work any more, and I needed to use GIT_EDITOR instead.
  • John Vandenberg
    John Vandenberg almost 7 years
    I found VISUAL also didnt work in my attempt at automated git rebase , but GIT_EDITOR worked.
  • Mike
    Mike over 6 years
    This works well with git --interactive --exec <cmd> <branch>. I set <cmd> to a command which runs the tests introduced or modified by my branch, and <branch> to master. That causes git to run my tests against each commit in the branch.
  • pfalcon
    pfalcon over 6 years
    @MarcH: Please mention alternatives in comments to somebody else's answer (yet better in your own answer), not by editing content which has somebody else's signature. Thanks.
  • D. Ben Knoble
    D. Ben Knoble almost 5 years
    Should quote $1 and $@
  • Matthew D. Scholefield
    Matthew D. Scholefield almost 5 years
    Quoting doesn't matter in variable assignment, but I did remove the assigning to variables because it would only pick up the first extra argument unless array assignment was used.
  • x-yuri
    x-yuri about 4 years
    Both EDITOR and VISUAL worked for me. git-2.26.0.
  • Hadrien TOMA
    Hadrien TOMA about 4 years
    Nice answer, thank you! Could you add comments to explain a little bit what each step does?
  • nh2
    nh2 about 4 years
    You can use git's native aliases by using -c sequence.editor=touch instead of GIT_SEQUENCE_EDITOR. For example, in your .gitconfig [alias] section: rs = -c sequence.editor=touch rebase --interactive --autosquash --autostash.