How do I run git rebase --interactive in non-interactive manner?
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
.
pfalcon
Updated on June 14, 2022Comments
-
pfalcon about 2 years
Is it possible to do following?
- Make
git rebase --interactive
to just output standard boilerplate to a file, instead to outputting to a file and opening it in editor. - Let the user edit the file.
- Let user re-run
git rebase
with the name of edited file. - Go on with the usual rebase process.
Usecase: scripted rebasing of course. See how to re-order commits in Git non-interactively for example.
- Make
-
c00kiemon5ter almost 12 yearsdid not know of
GIT_SEQUENCE_EDITOR
, seems useful ;) -
pfalcon almost 12 yearsThanks, we probably started to write answers at similar time, I didn't see yours before I posted mine ;-)
-
me_and almost 12 yearsOr, 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 of0
. -
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 over 9 yearsI think it's simpler to re-define EDITOR, either on a per-session or per-command basis.
-
MarcH over 9 yearsHere 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 about 8 yearsI'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, usegit -c var=val
, and in this case settingEDITOR
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 over 7 yearsFor 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 over 7 yearsAlso: make sure
git config --global core.editor
returns empty. -
John Vandenberg almost 7 yearsI found setting
EDITOR
doesnt work any more, and I needed to useGIT_EDITOR
instead. -
John Vandenberg almost 7 years
-
Mike over 6 yearsThis 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>
tomaster
. That causes git to run my tests against each commit in the branch. -
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 almost 5 yearsShould quote $1 and $@
-
Matthew D. Scholefield almost 5 yearsQuoting 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 about 4 yearsBoth
EDITOR
andVISUAL
worked for me.git-2.26.0
. -
Hadrien TOMA about 4 yearsNice answer, thank you! Could you add comments to explain a little bit what each step does?
-
nh2 about 4 yearsYou can use git's native
alias
es by using-c sequence.editor=touch
instead ofGIT_SEQUENCE_EDITOR
. For example, in your.gitconfig
[alias]
section:rs = -c sequence.editor=touch rebase --interactive --autosquash --autostash
.