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>

Lucien Kong committed Jun 12, 2012 at 10:05 UTC c214538416ee55b951b77b0628b61370bc4d5de4
4 files changed +199 -6
Documentation/git-rebase.txt
+42 -3
@@ -8,9 +8,9 @@ git-rebase - Forward-port local commits to the updated upstream head
8 SYNOPSIS
9 --------
10 [verse]
11 -'git rebase' [-i | --interactive] [options] [--onto <newbase>]
11 +'git rebase' [-i | --interactive] [options] [--exec <cmd>] [--onto <newbase>]
12 [<upstream>] [<branch>]
13 -'git rebase' [-i | --interactive] [options] --onto <newbase>
13 +'git rebase' [-i | --interactive] [options] [--exec <cmd>] --onto <newbase>
14 --root [<branch>]
15 'git rebase' --continue | --skip | --abort
16
@@ -210,7 +210,7 @@ rebase.autosquash::
210
211 OPTIONS
212 -------
213 -<newbase>::
213 +--onto <newbase>::
214 Starting point at which to create the new commits. If the
215 --onto option is not specified, the starting point is
216 <upstream>. May be any valid commit, and not just an
@@ -344,6 +344,27 @@ This uses the `--interactive` machinery internally, but combining it
344 with the `--interactive` option explicitly is generally not a good
345 idea unless you know what you are doing (see BUGS below).
346
347 +-x <cmd>::
348 +--exec <cmd>::
349 + Append "exec <cmd>" after each line creating a commit in the
350 + final history. <cmd> will be interpreted as one or more shell
351 + commands.
352 ++
353 +This option can only be used with the `--interactive` option
354 +(see INTERACTIVE MODE below).
355 ++
356 +You may execute several commands by either using one instance of `--exec`
357 +with several commands:
358 ++
359 + git rebase -i --exec "cmd1 && cmd2 && ..."
360 ++
361 +or by giving more than one `--exec`:
362 ++
363 + git rebase -i --exec "cmd1" --exec "cmd2" --exec ...
364 ++
365 +If `--autosquash` is used, "exec" lines will not be appended for
366 +the intermediate commits, and will only appear at the end of each
367 +squash/fixup series.
368
369 --root::
370 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
542 use shell features (like "cd", ">", ";" ...). The command is run from
543 the root of the working tree.
544
545 +----------------------------------
546 +$ git rebase -i --exec "make test"
547 +----------------------------------
548 +
549 +This command lets you check that intermediate commits are compilable.
550 +The todo list becomes like that:
551 +
552 +--------------------
553 +pick 5928aea one
554 +exec make test
555 +pick 04d0fda two
556 +exec make test
557 +pick ba46169 three
558 +exec make test
559 +pick f4593f9 four
560 +exec make test
561 +--------------------
562 +
563 SPLITTING COMMITS
564 -----------------
565
git-rebase--interactive.sh
+23
@@ -684,6 +684,27 @@ rearrange_squash () {
684 rm -f "$1.sq" "$1.rearranged"
685 }
686
687 +# Add commands after a pick or after a squash/fixup serie
688 +# in the todo list.
689 +add_exec_commands () {
690 + {
691 + first=t
692 + while read -r insn rest
693 + do
694 + case $insn in
695 + pick)
696 + test -n "$first" ||
697 + printf "%s" "$cmd"
698 + ;;
699 + esac
700 + printf "%s %s\n" "$insn" "$rest"
701 + first=
702 + done
703 + printf "%s" "$cmd"
704 + } <"$1" >"$1.new" &&
705 + mv "$1.new" "$1"
706 +}
707 +
708 case "$action" in
709 continue)
710 # do we have anything to commit?
@@ -857,6 +878,8 @@ fi
878
879 test -s "$todo" || echo noop >> "$todo"
880 test -n "$autosquash" && rearrange_squash "$todo"
881 +test -n "$cmd" && add_exec_commands "$todo"
882 +
883 cat >> "$todo" << EOF
884
885 # Rebase $shortrevisions onto $shortonto
git-rebase.sh
+17 -3
@@ -3,7 +3,8 @@
3 # Copyright (c) 2005 Junio C Hamano.
4 #
5
6 -USAGE='[--interactive | -i] [-v] [--force-rebase | -f] [--no-ff] [--onto <newbase>] [<upstream>|--root] [<branch>] [--quiet | -q]'
6 +USAGE='[--interactive | -i] [--exec | -x <cmd>] [-v] [--force-rebase | -f]
7 + [--no-ff] [--onto <newbase>] [<upstream>|--root] [<branch>] [--quiet | -q]'
8 LONG_USAGE='git-rebase replaces <branch> with a new branch of the
9 same name. When the --onto option is provided the new branch starts
10 out with a HEAD equal to <newbase>, otherwise it is equal to <upstream>
@@ -30,8 +31,8 @@ Example: git-rebase master~1 topic
31 SUBDIRECTORY_OK=Yes
32 OPTIONS_KEEPDASHDASH=
33 OPTIONS_SPEC="\
33 -git rebase [-i] [options] [--onto <newbase>] [<upstream>] [<branch>]
34 -git rebase [-i] [options] --onto <newbase> --root [<branch>]
34 +git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] [<upstream>] [<branch>]
35 +git rebase [-i] [options] [--exec <cmd>] --onto <newbase> --root [<branch>]
36 git-rebase [-i] --continue | --abort | --skip
37 --
38 Available options are
@@ -43,6 +44,7 @@ s,strategy=! use the given merge strategy
44 no-ff! cherry-pick all commits, even if unchanged
45 m,merge! use merging strategies to rebase
46 i,interactive! let the user edit the list of commits to rebase
47 +x,exec=! add exec lines after each commit of the editable list
48 k,keep-empty preserve empty commits during rebase
49 f,force-rebase! force rebase even if branch is up to date
50 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\".
78 To check out the original branch and stop rebasing run \"git rebase --abort\".
79 "
80 unset onto
81 +cmd=
82 strategy=
83 strategy_opts=
84 do_merge=
@@ -220,6 +223,11 @@ do
223 onto="$2"
224 shift
225 ;;
226 + -x)
227 + test 2 -le "$#" || usage
228 + cmd="${cmd}exec $2${LF}"
229 + shift
230 + ;;
231 -i)
232 interactive_rebase=explicit
233 ;;
@@ -305,6 +313,12 @@ do
313 done
314 test $# -gt 2 && usage
315
316 +if test -n "$cmd" &&
317 + test "$interactive_rebase" != explicit
318 +then
319 + die "--exec option must be used with --interactive option"
320 +fi
321 +
322 if test -n "$action"
323 then
324 test -z "$in_progress" && die "No rebase in progress?"
t/t3404-rebase-interactive.sh
+117
@@ -755,4 +755,121 @@ test_expect_success 'rebase-i history with funny messages' '
755 test_cmp expect actual
756 '
757
758 +
759 +test_expect_success 'prepare for rebase -i --exec' '
760 + git checkout master &&
761 + git checkout -b execute &&
762 + test_commit one_exec main.txt one_exec &&
763 + test_commit two_exec main.txt two_exec &&
764 + test_commit three_exec main.txt three_exec
765 +'
766 +
767 +
768 +test_expect_success 'running "git rebase -i --exec git show HEAD"' '
769 + git rebase -i --exec "git show HEAD" HEAD~2 >actual &&
770 + (
771 + FAKE_LINES="1 exec_git_show_HEAD 2 exec_git_show_HEAD" &&
772 + export FAKE_LINES &&
773 + git rebase -i HEAD~2 >expect
774 + ) &&
775 + sed -e "1,9d" expect >expected &&
776 + test_cmp expected actual
777 +'
778 +
779 +
780 +test_expect_success 'running "git rebase --exec git show HEAD -i"' '
781 + git reset --hard execute &&
782 + git rebase --exec "git show HEAD" -i HEAD~2 >actual &&
783 + (
784 + FAKE_LINES="1 exec_git_show_HEAD 2 exec_git_show_HEAD" &&
785 + export FAKE_LINES &&
786 + git rebase -i HEAD~2 >expect
787 + ) &&
788 + sed -e "1,9d" expect >expected &&
789 + test_cmp expected actual
790 +'
791 +
792 +
793 +test_expect_success 'running "git rebase -ix git show HEAD"' '
794 + git reset --hard execute &&
795 + git rebase -ix "git show HEAD" HEAD~2 >actual &&
796 + (
797 + FAKE_LINES="1 exec_git_show_HEAD 2 exec_git_show_HEAD" &&
798 + export FAKE_LINES &&
799 + git rebase -i HEAD~2 >expect
800 + ) &&
801 + sed -e "1,9d" expect >expected &&
802 + test_cmp expected actual
803 +'
804 +
805 +
806 +test_expect_success 'rebase -ix with several <CMD>' '
807 + git reset --hard execute &&
808 + git rebase -ix "git show HEAD; pwd" HEAD~2 >actual &&
809 + (
810 + FAKE_LINES="1 exec_git_show_HEAD;_pwd 2 exec_git_show_HEAD;_pwd" &&
811 + export FAKE_LINES &&
812 + git rebase -i HEAD~2 >expect
813 + ) &&
814 + sed -e "1,9d" expect >expected &&
815 + test_cmp expected actual
816 +'
817 +
818 +
819 +test_expect_success 'rebase -ix with several instances of --exec' '
820 + git reset --hard execute &&
821 + git rebase -i --exec "git show HEAD" --exec "pwd" HEAD~2 >actual &&
822 + (
823 + FAKE_LINES="1 exec_git_show_HEAD exec_pwd 2
824 + exec_git_show_HEAD exec_pwd" &&
825 + export FAKE_LINES &&
826 + git rebase -i HEAD~2 >expect
827 + ) &&
828 + sed -e "1,11d" expect >expected &&
829 + test_cmp expected actual
830 +'
831 +
832 +
833 +test_expect_success 'rebase -ix with --autosquash' '
834 + git reset --hard execute &&
835 + git checkout -b autosquash &&
836 + echo second >second.txt &&
837 + git add second.txt &&
838 + git commit -m "fixup! two_exec" &&
839 + echo bis >bis.txt &&
840 + git add bis.txt &&
841 + git commit -m "fixup! two_exec" &&
842 + (
843 + git checkout -b autosquash_actual &&
844 + git rebase -i --exec "git show HEAD" --autosquash HEAD~4 >actual
845 + ) &&
846 + git checkout autosquash &&
847 + (
848 + git checkout -b autosquash_expected &&
849 + FAKE_LINES="1 fixup 3 fixup 4 exec_git_show_HEAD 2 exec_git_show_HEAD" &&
850 + export FAKE_LINES &&
851 + git rebase -i HEAD~4 >expect
852 + ) &&
853 + sed -e "1,13d" expect >expected &&
854 + test_cmp expected actual
855 +'
856 +
857 +
858 +test_expect_success 'rebase --exec without -i shows error message' '
859 + git reset --hard execute &&
860 + test_must_fail git rebase --exec "git show HEAD" HEAD~2 2>actual &&
861 + echo "--exec option must be used with --interactive option" >expected &&
862 + test_i18ncmp expected actual
863 +'
864 +
865 +
866 +test_expect_success 'rebase -i --exec without <CMD>' '
867 + git reset --hard execute &&
868 + test_must_fail git rebase -i --exec 2>tmp &&
869 + sed -e "1d" tmp >actual &&
870 + test_must_fail git rebase -h >expected &&
871 + test_cmp expected actual &&
872 + git checkout master
873 +'
874 +
875 test_done