diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt index b4314568f..3b87f1a1b 100644 --- a/Documentation/git-rebase.txt +++ b/Documentation/git-rebase.txt @@ -466,6 +466,30 @@ sure that the current HEAD is "B", and call $ git rebase -i -p --onto Q O ----------------------------- +Reordering and editing commits usually creates untested intermediate +steps. You may want to check that your history editing did not break +anything by running a test, or at least recompiling at intermediate +points in history by using the "exec" command (shortcut "x"). You may +do so by creating a todo list like this one: + +------------------------------------------- +pick deadbee Implement feature XXX +fixup f1a5c00 Fix to feature XXX +exec make +pick c0ffeee The oneline of the next commit +edit deadbab The oneline of the commit after +exec cd subdir; make test +... +------------------------------------------- + +The interactive rebase will stop when a command fails (i.e. exits with +non-0 status) to give you an opportunity to fix the problem. You can +continue with `git rebase --continue`. + +The "exec" command launches the command in a shell (the one specified +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. SPLITTING COMMITS ----------------- diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index b94c2a038..3419247d0 100755 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -537,6 +537,34 @@ do_next () { esac record_in_rewritten $sha1 ;; + x|"exec") + read -r command rest < "$TODO" + mark_action_done + printf 'Executing: %s\n' "$rest" + # "exec" command doesn't take a sha1 in the todo-list. + # => can't just use $sha1 here. + git rev-parse --verify HEAD > "$DOTEST"/stopped-sha + ${SHELL:-@SHELL_PATH@} -c "$rest" # Actual execution + status=$? + if test "$status" -ne 0 + then + warn "Execution failed: $rest" + warn "You can fix the problem, and then run" + warn + warn " git rebase --continue" + warn + exit "$status" + fi + # Run in subshell because require_clean_work_tree can die. + if ! (require_clean_work_tree) + then + warn "Commit or stash your changes, and then run" + warn + warn " git rebase --continue" + warn + exit 1 + fi + ;; *) warn "Unknown command: $command $sha1 $rest" if git rev-parse --verify -q "$sha1" >/dev/null @@ -591,22 +619,30 @@ do_rest () { # skip picking commits whose parents are unchanged skip_unnecessary_picks () { fd=3 - while read -r command sha1 rest + while read -r command rest do # fd=3 means we skip the command - case "$fd,$command,$(git rev-parse --verify --quiet $sha1^)" in - 3,pick,"$ONTO"*|3,p,"$ONTO"*) + case "$fd,$command" in + 3,pick|3,p) # pick a commit whose parent is current $ONTO -> skip - ONTO=$sha1 + sha1=$(printf '%s' "$rest" | cut -d ' ' -f 1) + case "$(git rev-parse --verify --quiet "$sha1"^)" in + "$ONTO"*) + ONTO=$sha1 + ;; + *) + fd=1 + ;; + esac ;; - 3,#*|3,,*) + 3,#*|3,) # copy comments ;; *) fd=1 ;; esac - printf '%s\n' "$command${sha1:+ }$sha1${rest:+ }$rest" >&$fd + printf '%s\n' "$command${rest:+ }$rest" >&$fd done <"$TODO" >"$TODO.new" 3>>"$DONE" && mv -f "$TODO".new "$TODO" && case "$(peek_next_command)" in @@ -957,6 +993,7 @@ first and then run 'git rebase --continue' again." # e, edit = use commit, but stop for amending # s, squash = use commit, but meld into previous commit # f, fixup = like "squash", but discard this commit's log message +# x , exec = Run a shell command , and stop if it fails # # If you remove a line here THAT COMMIT WILL BE LOST. # However, if you remove everything, the rebase will be aborted. diff --git a/t/README b/t/README index 0d1183c3e..410499a09 100644 --- a/t/README +++ b/t/README @@ -467,6 +467,13 @@ library for your script to use. file. This behaves like "cmp" but produces more helpful output when the test is run with "-v" option. + - test_path_is_file [] + test_path_is_dir [] + test_path_is_missing [] + + Check whether a file/directory exists or doesn't. will + be displayed if the test fails. + - test_when_finished