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 SYNOPSIS
-------- --------
[verse] [verse]
'git rebase' [-i | --interactive] [options] [--onto <newbase>] 'git rebase' [-i | --interactive] [options] [--exec <cmd>] [--onto <newbase>]
[<upstream>] [<branch>] [<upstream>] [<branch>]
'git rebase' [-i | --interactive] [options] --onto <newbase> 'git rebase' [-i | --interactive] [options] [--exec <cmd>] --onto <newbase>
--root [<branch>] --root [<branch>]
'git rebase' --continue | --skip | --abort 'git rebase' --continue | --skip | --abort
@ -210,7 +210,7 @@ rebase.autosquash::
OPTIONS OPTIONS
------- -------
<newbase>:: --onto <newbase>::
Starting point at which to create the new commits. If the Starting point at which to create the new commits. If the
--onto option is not specified, the starting point is --onto option is not specified, the starting point is
<upstream>. May be any valid commit, and not just an <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 with the `--interactive` option explicitly is generally not a good
idea unless you know what you are doing (see BUGS below). 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:: --root::
Rebase all commits reachable from <branch>, instead of 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 use shell features (like "cd", ">", ";" ...). The command is run from
the root of the working tree. 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 SPLITTING COMMITS
----------------- -----------------

View File

@ -684,6 +684,27 @@ rearrange_squash () {
rm -f "$1.sq" "$1.rearranged" 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 case "$action" in
continue) continue)
# do we have anything to commit? # do we have anything to commit?
@ -857,6 +878,8 @@ fi
test -s "$todo" || echo noop >> "$todo" test -s "$todo" || echo noop >> "$todo"
test -n "$autosquash" && rearrange_squash "$todo" test -n "$autosquash" && rearrange_squash "$todo"
test -n "$cmd" && add_exec_commands "$todo"
cat >> "$todo" << EOF cat >> "$todo" << EOF
# Rebase $shortrevisions onto $shortonto # Rebase $shortrevisions onto $shortonto

View File

@ -3,7 +3,8 @@
# Copyright (c) 2005 Junio C Hamano. # 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 LONG_USAGE='git-rebase replaces <branch> with a new branch of the
same name. When the --onto option is provided the new branch starts 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> 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 SUBDIRECTORY_OK=Yes
OPTIONS_KEEPDASHDASH= OPTIONS_KEEPDASHDASH=
OPTIONS_SPEC="\ OPTIONS_SPEC="\
git rebase [-i] [options] [--onto <newbase>] [<upstream>] [<branch>] git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] [<upstream>] [<branch>]
git rebase [-i] [options] --onto <newbase> --root [<branch>] git rebase [-i] [options] [--exec <cmd>] --onto <newbase> --root [<branch>]
git-rebase [-i] --continue | --abort | --skip git-rebase [-i] --continue | --abort | --skip
-- --
Available options are Available options are
@ -43,6 +44,7 @@ s,strategy=! use the given merge strategy
no-ff! cherry-pick all commits, even if unchanged no-ff! cherry-pick all commits, even if unchanged
m,merge! use merging strategies to rebase m,merge! use merging strategies to rebase
i,interactive! let the user edit the list of commits 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 k,keep-empty preserve empty commits during rebase
f,force-rebase! force rebase even if branch is up to date f,force-rebase! force rebase even if branch is up to date
X,strategy-option=! pass the argument through to the merge strategy 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\". To check out the original branch and stop rebasing run \"git rebase --abort\".
" "
unset onto unset onto
cmd=
strategy= strategy=
strategy_opts= strategy_opts=
do_merge= do_merge=
@ -220,6 +223,11 @@ do
onto="$2" onto="$2"
shift shift
;; ;;
-x)
test 2 -le "$#" || usage
cmd="${cmd}exec $2${LF}"
shift
;;
-i) -i)
interactive_rebase=explicit interactive_rebase=explicit
;; ;;
@ -305,6 +313,12 @@ do
done done
test $# -gt 2 && usage 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" if test -n "$action"
then then
test -z "$in_progress" && die "No rebase in progress?" 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_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 test_done