rebase -i: teach "--exec <cmd>"

During an interactive rebase session, it is sometimes desirable to
run tests on each commit in the resulting history.  This can be done
by adding "exec <test command>" when editing the insn sheet, but the
command used for testing is often the same for all resulting commits.

By passing "--exec <cmd>" from the command line, automatically add
these "exec" lines after each commit in the final history.  To work
well with the --autosquash option, these are added at the end of
each run of "fixup" and "squash".

Helped-by: Johannes Sixt <j6t@kdbg.org>
Signed-off-by: Lucien Kong <Lucien.Kong@ensimag.imag.fr>
Signed-off-by: Valentin Duperray <Valentin.Duperray@ensimag.imag.fr>
Signed-off-by: Franck Jonas <Franck.Jonas@ensimag.imag.fr>
Signed-off-by: Thomas Nguy <Thomas.Nguy@ensimag.imag.fr>
Signed-off-by: Huynh Khoi Nguyen Nguyen <Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr>
Signed-off-by: Matthieu Moy <Matthieu.Moy@grenoble-inp.fr>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Lucien Kong 2012-06-12 10:05:12 +02:00 committed by Junio C Hamano
parent f623ca1cae
commit c214538416
4 changed files with 199 additions and 6 deletions

View file

@ -8,9 +8,9 @@ git-rebase - Forward-port local commits to the updated upstream head
SYNOPSIS
--------
[verse]
'git rebase' [-i | --interactive] [options] [--onto <newbase>]
'git rebase' [-i | --interactive] [options] [--exec <cmd>] [--onto <newbase>]
[<upstream>] [<branch>]
'git rebase' [-i | --interactive] [options] --onto <newbase>
'git rebase' [-i | --interactive] [options] [--exec <cmd>] --onto <newbase>
--root [<branch>]
'git rebase' --continue | --skip | --abort
@ -210,7 +210,7 @@ rebase.autosquash::
OPTIONS
-------
<newbase>::
--onto <newbase>::
Starting point at which to create the new commits. If the
--onto option is not specified, the starting point is
<upstream>. May be any valid commit, and not just an
@ -344,6 +344,27 @@ This uses the `--interactive` machinery internally, but combining it
with the `--interactive` option explicitly is generally not a good
idea unless you know what you are doing (see BUGS below).
-x <cmd>::
--exec <cmd>::
Append "exec <cmd>" after each line creating a commit in the
final history. <cmd> will be interpreted as one or more shell
commands.
+
This option can only be used with the `--interactive` option
(see INTERACTIVE MODE below).
+
You may execute several commands by either using one instance of `--exec`
with several commands:
+
git rebase -i --exec "cmd1 && cmd2 && ..."
+
or by giving more than one `--exec`:
+
git rebase -i --exec "cmd1" --exec "cmd2" --exec ...
+
If `--autosquash` is used, "exec" lines will not be appended for
the intermediate commits, and will only appear at the end of each
squash/fixup series.
--root::
Rebase all commits reachable from <branch>, instead of
@ -521,6 +542,24 @@ in `$SHELL`, or the default shell if `$SHELL` is not set), so you can
use shell features (like "cd", ">", ";" ...). The command is run from
the root of the working tree.
----------------------------------
$ git rebase -i --exec "make test"
----------------------------------
This command lets you check that intermediate commits are compilable.
The todo list becomes like that:
--------------------
pick 5928aea one
exec make test
pick 04d0fda two
exec make test
pick ba46169 three
exec make test
pick f4593f9 four
exec make test
--------------------
SPLITTING COMMITS
-----------------

View file

@ -684,6 +684,27 @@ rearrange_squash () {
rm -f "$1.sq" "$1.rearranged"
}
# Add commands after a pick or after a squash/fixup serie
# in the todo list.
add_exec_commands () {
{
first=t
while read -r insn rest
do
case $insn in
pick)
test -n "$first" ||
printf "%s" "$cmd"
;;
esac
printf "%s %s\n" "$insn" "$rest"
first=
done
printf "%s" "$cmd"
} <"$1" >"$1.new" &&
mv "$1.new" "$1"
}
case "$action" in
continue)
# do we have anything to commit?
@ -857,6 +878,8 @@ fi
test -s "$todo" || echo noop >> "$todo"
test -n "$autosquash" && rearrange_squash "$todo"
test -n "$cmd" && add_exec_commands "$todo"
cat >> "$todo" << EOF
# Rebase $shortrevisions onto $shortonto

View file

@ -3,7 +3,8 @@
# Copyright (c) 2005 Junio C Hamano.
#
USAGE='[--interactive | -i] [-v] [--force-rebase | -f] [--no-ff] [--onto <newbase>] [<upstream>|--root] [<branch>] [--quiet | -q]'
USAGE='[--interactive | -i] [--exec | -x <cmd>] [-v] [--force-rebase | -f]
[--no-ff] [--onto <newbase>] [<upstream>|--root] [<branch>] [--quiet | -q]'
LONG_USAGE='git-rebase replaces <branch> with a new branch of the
same name. When the --onto option is provided the new branch starts
out with a HEAD equal to <newbase>, otherwise it is equal to <upstream>
@ -30,8 +31,8 @@ Example: git-rebase master~1 topic
SUBDIRECTORY_OK=Yes
OPTIONS_KEEPDASHDASH=
OPTIONS_SPEC="\
git rebase [-i] [options] [--onto <newbase>] [<upstream>] [<branch>]
git rebase [-i] [options] --onto <newbase> --root [<branch>]
git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] [<upstream>] [<branch>]
git rebase [-i] [options] [--exec <cmd>] --onto <newbase> --root [<branch>]
git-rebase [-i] --continue | --abort | --skip
--
Available options are
@ -43,6 +44,7 @@ s,strategy=! use the given merge strategy
no-ff! cherry-pick all commits, even if unchanged
m,merge! use merging strategies to rebase
i,interactive! let the user edit the list of commits to rebase
x,exec=! add exec lines after each commit of the editable list
k,keep-empty preserve empty commits during rebase
f,force-rebase! force rebase even if branch is up to date
X,strategy-option=! pass the argument through to the merge strategy
@ -76,6 +78,7 @@ If you would prefer to skip this patch, instead run \"git rebase --skip\".
To check out the original branch and stop rebasing run \"git rebase --abort\".
"
unset onto
cmd=
strategy=
strategy_opts=
do_merge=
@ -220,6 +223,11 @@ do
onto="$2"
shift
;;
-x)
test 2 -le "$#" || usage
cmd="${cmd}exec $2${LF}"
shift
;;
-i)
interactive_rebase=explicit
;;
@ -305,6 +313,12 @@ do
done
test $# -gt 2 && usage
if test -n "$cmd" &&
test "$interactive_rebase" != explicit
then
die "--exec option must be used with --interactive option"
fi
if test -n "$action"
then
test -z "$in_progress" && die "No rebase in progress?"

View file

@ -755,4 +755,121 @@ test_expect_success 'rebase-i history with funny messages' '
test_cmp expect actual
'
test_expect_success 'prepare for rebase -i --exec' '
git checkout master &&
git checkout -b execute &&
test_commit one_exec main.txt one_exec &&
test_commit two_exec main.txt two_exec &&
test_commit three_exec main.txt three_exec
'
test_expect_success 'running "git rebase -i --exec git show HEAD"' '
git rebase -i --exec "git show HEAD" HEAD~2 >actual &&
(
FAKE_LINES="1 exec_git_show_HEAD 2 exec_git_show_HEAD" &&
export FAKE_LINES &&
git rebase -i HEAD~2 >expect
) &&
sed -e "1,9d" expect >expected &&
test_cmp expected actual
'
test_expect_success 'running "git rebase --exec git show HEAD -i"' '
git reset --hard execute &&
git rebase --exec "git show HEAD" -i HEAD~2 >actual &&
(
FAKE_LINES="1 exec_git_show_HEAD 2 exec_git_show_HEAD" &&
export FAKE_LINES &&
git rebase -i HEAD~2 >expect
) &&
sed -e "1,9d" expect >expected &&
test_cmp expected actual
'
test_expect_success 'running "git rebase -ix git show HEAD"' '
git reset --hard execute &&
git rebase -ix "git show HEAD" HEAD~2 >actual &&
(
FAKE_LINES="1 exec_git_show_HEAD 2 exec_git_show_HEAD" &&
export FAKE_LINES &&
git rebase -i HEAD~2 >expect
) &&
sed -e "1,9d" expect >expected &&
test_cmp expected actual
'
test_expect_success 'rebase -ix with several <CMD>' '
git reset --hard execute &&
git rebase -ix "git show HEAD; pwd" HEAD~2 >actual &&
(
FAKE_LINES="1 exec_git_show_HEAD;_pwd 2 exec_git_show_HEAD;_pwd" &&
export FAKE_LINES &&
git rebase -i HEAD~2 >expect
) &&
sed -e "1,9d" expect >expected &&
test_cmp expected actual
'
test_expect_success 'rebase -ix with several instances of --exec' '
git reset --hard execute &&
git rebase -i --exec "git show HEAD" --exec "pwd" HEAD~2 >actual &&
(
FAKE_LINES="1 exec_git_show_HEAD exec_pwd 2
exec_git_show_HEAD exec_pwd" &&
export FAKE_LINES &&
git rebase -i HEAD~2 >expect
) &&
sed -e "1,11d" expect >expected &&
test_cmp expected actual
'
test_expect_success 'rebase -ix with --autosquash' '
git reset --hard execute &&
git checkout -b autosquash &&
echo second >second.txt &&
git add second.txt &&
git commit -m "fixup! two_exec" &&
echo bis >bis.txt &&
git add bis.txt &&
git commit -m "fixup! two_exec" &&
(
git checkout -b autosquash_actual &&
git rebase -i --exec "git show HEAD" --autosquash HEAD~4 >actual
) &&
git checkout autosquash &&
(
git checkout -b autosquash_expected &&
FAKE_LINES="1 fixup 3 fixup 4 exec_git_show_HEAD 2 exec_git_show_HEAD" &&
export FAKE_LINES &&
git rebase -i HEAD~4 >expect
) &&
sed -e "1,13d" expect >expected &&
test_cmp expected actual
'
test_expect_success 'rebase --exec without -i shows error message' '
git reset --hard execute &&
test_must_fail git rebase --exec "git show HEAD" HEAD~2 2>actual &&
echo "--exec option must be used with --interactive option" >expected &&
test_i18ncmp expected actual
'
test_expect_success 'rebase -i --exec without <CMD>' '
git reset --hard execute &&
test_must_fail git rebase -i --exec 2>tmp &&
sed -e "1d" tmp >actual &&
test_must_fail git rebase -h >expected &&
test_cmp expected actual &&
git checkout master
'
test_done