From: Junio C Hamano Date: Mon, 16 Jul 2012 04:38:41 +0000 (-0700) Subject: Merge branch 'cw/rebase-i-root' X-Git-Tag: v1.7.12-rc0~32 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/0cd993a778d7c14bed34f624fce46724242557ed?hp=-c Merge branch 'cw/rebase-i-root' "git rebase [-i] --root $tip" can now be used to rewrite all the history down to the root. * cw/rebase-i-root: t3404: make test 57 work with dash and others Add tests for rebase -i --root without --onto rebase -i: support --root without --onto --- 0cd993a778d7c14bed34f624fce46724242557ed diff --combined Documentation/git-rebase.txt index 2d71e4b975,85b5e4425c..b30ed352e5 --- a/Documentation/git-rebase.txt +++ b/Documentation/git-rebase.txt @@@ -8,9 -8,9 +8,9 @@@ git-rebase - Forward-port local commit SYNOPSIS -------- [verse] -'git rebase' [-i | --interactive] [options] [--onto ] +'git rebase' [-i | --interactive] [options] [--exec ] [--onto ] [] [] - 'git rebase' [-i | --interactive] [options] [--exec ] --onto -'git rebase' [-i | --interactive] [options] [--onto ] ++'git rebase' [-i | --interactive] [options] [--exec ] [--onto ] --root [] 'git rebase' --continue | --skip | --abort @@@ -210,7 -210,7 +210,7 @@@ rebase.autosquash: OPTIONS ------- -:: +--onto :: Starting point at which to create the new commits. If the --onto option is not specified, the starting point is . May be any valid commit, and not just an @@@ -344,35 -344,15 +344,36 @@@ This uses the `--interactive` machiner with the `--interactive` option explicitly is generally not a good idea unless you know what you are doing (see BUGS below). +-x :: +--exec :: + Append "exec " after each line creating a commit in the + final history. 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 , instead of limiting them with an . This allows you to rebase - the root commit(s) on a branch. Must be used with --onto, and + the root commit(s) on a branch. When used with --onto, it will skip changes already contained in (instead of - ). When used together with --preserve-merges, 'all' - root commits will be rewritten to have as parent + ) whereas without --onto it will operate on every change. + When used together with both --onto and --preserve-merges, + 'all' root commits will be rewritten to have as parent instead. --autosquash:: @@@ -542,24 -522,6 +543,24 @@@ in `$SHELL`, or the default shell if `$ 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 ----------------- diff --combined git-rebase--interactive.sh index 3a3c382357,fcb5f618da..bef7bc0444 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@@ -9,7 -9,9 +9,7 @@@ # # The original idea comes from Eric W. Biederman, in # http://article.gmane.org/gmane.comp.version-control.git/22407 - -. git-sh-setup - +# # The file containing rebase commands, comments, and empty lines. # This file is created by "git rebase -i" then edited by the user. As # the lines are processed, they are removed from the front of this @@@ -415,6 -417,29 +415,29 @@@ record_in_rewritten() esac } + do_pick () { + if test "$(git rev-parse HEAD)" = "$squash_onto" + then + # Set the correct commit message and author info on the + # sentinel root before cherry-picking the original changes + # without committing (-n). Finally, update the sentinel again + # to include these changes. If the cherry-pick results in a + # conflict, this means our behaviour is similar to a standard + # failed cherry-pick during rebase, with a dirty index to + # resolve before manually running git commit --amend then git + # rebase --continue. + git commit --allow-empty --allow-empty-message --amend \ + --no-post-rewrite -n -q -C $1 && + pick_one -n $1 && + git commit --allow-empty --allow-empty-message \ + --amend --no-post-rewrite -n -q -C $1 || + die_with_patch $1 "Could not apply $1... $2" + else + pick_one $1 || + die_with_patch $1 "Could not apply $1... $2" + fi + } + do_next () { rm -f "$msg" "$author_script" "$amend" || exit read -r command sha1 rest < "$todo" @@@ -426,16 -451,14 +449,14 @@@ comment_for_reflog pick mark_action_done - pick_one $sha1 || - die_with_patch $sha1 "Could not apply $sha1... $rest" + do_pick $sha1 "$rest" record_in_rewritten $sha1 ;; reword|r) comment_for_reflog reword mark_action_done - pick_one $sha1 || - die_with_patch $sha1 "Could not apply $sha1... $rest" + do_pick $sha1 "$rest" git commit --amend --no-post-rewrite || { warn "Could not amend commit after successfully picking $sha1... $rest" warn "This is most likely due to an empty commit message, or the pre-commit hook" @@@ -449,8 -472,7 +470,7 @@@ comment_for_reflog edit mark_action_done - pick_one $sha1 || - die_with_patch $sha1 "Could not apply $sha1... $rest" + do_pick $sha1 "$rest" warn "Stopped at $sha1... $rest" exit_with_patch $sha1 0 ;; @@@ -682,27 -704,6 +702,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? @@@ -876,8 -877,6 +896,8 @@@ f 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 diff --combined git-rebase.sh index 5bddfa9690,bde2be88a0..1cd0633b80 --- a/git-rebase.sh +++ b/git-rebase.sh @@@ -3,8 -3,7 +3,8 @@@ # Copyright (c) 2005 Junio C Hamano. # -USAGE='[--interactive | -i] [-v] [--force-rebase | -f] [--no-ff] [--onto ] [|--root] [] [--quiet | -q]' +USAGE='[--interactive | -i] [--exec | -x ] [-v] [--force-rebase | -f] + [--no-ff] [--onto ] [|--root] [] [--quiet | -q]' LONG_USAGE='git-rebase replaces with a new branch of the same name. When the --onto option is provided the new branch starts out with a HEAD equal to , otherwise it is equal to @@@ -31,8 -30,8 +31,8 @@@ Example: git-rebase master~1 topi SUBDIRECTORY_OK=Yes OPTIONS_KEEPDASHDASH= OPTIONS_SPEC="\ -git rebase [-i] [options] [--onto ] [] [] -git rebase [-i] [options] [--onto ] --root [] +git rebase [-i] [options] [--exec ] [--onto ] [] [] - git rebase [-i] [options] [--exec ] --onto --root [] ++git rebase [-i] [options] [--exec ] [--onto ] --root [] git-rebase [-i] --continue | --abort | --skip -- Available options are @@@ -44,7 -43,6 +44,7 @@@ s,strategy=! use the given merge 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 @@@ -78,7 -76,6 +78,7 @@@ If you would prefer to skip this patch To check out the original branch and stop rebasing run \"git rebase --abort\". " unset onto +cmd= strategy= strategy_opts= do_merge= @@@ -223,11 -220,6 +223,11 @@@ d onto="$2" shift ;; + -x) + test 2 -le "$#" || usage + cmd="${cmd}exec $2${LF}" + shift + ;; -i) interactive_rebase=explicit ;; @@@ -313,12 -305,6 +313,12 @@@ 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?" @@@ -378,6 -364,11 +378,11 @@@ and run me again. I am stopping in cas valuable there.' fi + if test -n "$rebase_root" && test -z "$onto" + then + test -z "$interactive_rebase" && interactive_rebase=implied + fi + if test -n "$interactive_rebase" then type=interactive @@@ -411,10 -402,14 +416,15 @@@ the die "invalid upstream $upstream_name" upstream_arg="$upstream_name" else - test -z "$onto" && die "You must specify --onto when using --root" + if test -z "$onto" + then + empty_tree=`git hash-object -t tree /dev/null` + onto=`git commit-tree $empty_tree 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 ' ' + 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 ' ' + 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_expect_success 'rebase -i --root re-order and drop commits' ' + git checkout E && + FAKE_LINES="3 1 2 5" git rebase -i --root && + test E = $(git cat-file commit HEAD | sed -ne \$p) && + test B = $(git cat-file commit HEAD^ | sed -ne \$p) && + test A = $(git cat-file commit HEAD^^ | sed -ne \$p) && + test C = $(git cat-file commit HEAD^^^ | sed -ne \$p) && + test 0 = $(git cat-file commit HEAD^^^ | grep -c ^parent\ ) + ' + + test_expect_success 'rebase -i --root retain root commit author and message' ' + git checkout A && + echo B >file7 && + git add file7 && + GIT_AUTHOR_NAME="Twerp Snog" git commit -m "different author" && + FAKE_LINES="2" git rebase -i --root && + git cat-file commit HEAD | grep -q "^author Twerp Snog" && + git cat-file commit HEAD | grep -q "^different author$" + ' + + test_expect_success 'rebase -i --root temporary sentinel commit' ' + git checkout B && + ( + FAKE_LINES="2" && + export FAKE_LINES && + test_must_fail git rebase -i --root + ) && + git cat-file commit HEAD | grep "^tree 4b825dc642cb" && + git rebase --abort + ' + test_done diff --combined t/t3412-rebase-root.sh index 1e9d1a737c,e4f9da8536..0b52105728 --- a/t/t3412-rebase-root.sh +++ b/t/t3412-rebase-root.sh @@@ -22,16 -22,6 +22,11 @@@ test_expect_success 'prepare repository test_commit 4 B ' - test_expect_success 'rebase --root expects --onto' ' - git checkout -B fail other && - test_must_fail git rebase --root - ' - +test_expect_success 'rebase --root fails with too many args' ' + git checkout -B fail other && + test_must_fail git rebase --onto master --root fail fail +' + test_expect_success 'setup pre-rebase hook' ' mkdir -p .git/hooks && cat >.git/hooks/pre-rebase < expect <' ' - git checkout -b work && + git checkout -b work other && git rebase --root --onto master && git log --pretty=tformat:"%s" > rebased && test_cmp expect rebased