diff options
Diffstat (limited to 'git-rebase--interactive.sh')
-rw-r--r-- | git-rebase--interactive.sh | 218 |
1 files changed, 210 insertions, 8 deletions
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index dc3133f681..f01637b1fd 100644 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -152,11 +152,21 @@ Commands: s, squash = use commit, but meld into previous commit f, fixup = like "squash", but discard this commit's log message x, exec = run command (the rest of the line) using shell + d, drop = remove commit These lines can be re-ordered; they are executed from top to bottom. +EOF + if test $(get_missing_commit_check_level) = error + then + git stripspace --comment-lines >>"$todo" <<\EOF +Do not remove any line. Use 'drop' explicitly to remove a commit. +EOF + else + git stripspace --comment-lines >>"$todo" <<\EOF If you remove a line here THAT COMMIT WILL BE LOST. EOF + fi } make_patch () { @@ -505,7 +515,7 @@ do_next () { rm -f "$msg" "$author_script" "$amend" "$state_dir"/stopped-sha || exit read -r command sha1 rest < "$todo" case "$command" in - "$comment_char"*|''|noop) + "$comment_char"*|''|noop|drop|d) mark_action_done ;; pick|p) @@ -740,10 +750,15 @@ collapse_todo_ids() { # "pick sha1 fixup!/squash! msg" appears in it so that the latter # comes immediately after the former, and change "pick" to # "fixup"/"squash". +# +# Note that if the config has specified a custom instruction format +# each log message will be re-retrieved in order to normalize the +# autosquash arrangement rearrange_squash () { # extract fixup!/squash! lines and resolve any referenced sha1's while read -r pick sha1 message do + test -z "${format}" || message=$(git log -n 1 --format="%s" ${sha1}) case "$message" in "squash! "*|"fixup! "*) action="${message%%!*}" @@ -785,6 +800,7 @@ rearrange_squash () { *" $sha1 "*) continue ;; esac printf '%s\n' "$pick $sha1 $message" + test -z "${format}" || message=$(git log -n 1 --format="%s" ${sha1}) used="$used$sha1 " while read -r squash action msg_prefix msg_content do @@ -802,8 +818,13 @@ rearrange_squash () { case "$message" in "$msg_content"*) emit=1;; esac ;; esac if test $emit = 1; then - real_prefix=$(echo "$msg_prefix" | sed "s/,/! /g") - printf '%s\n' "$action $squash ${real_prefix}$msg_content" + if test -n "${format}" + then + msg_content=$(git log -n 1 --format="${format}" ${squash}) + else + msg_content="$(echo "$msg_prefix" | sed "s/,/! /g")$msg_content" + fi + printf '%s\n' "$action $squash $msg_content" used="$used$squash " fi done <"$1.sq" @@ -833,6 +854,180 @@ add_exec_commands () { mv "$1.new" "$1" } +# Check if the SHA-1 passed as an argument is a +# correct one, if not then print $2 in "$todo".badsha +# $1: the SHA-1 to test +# $2: the line to display if incorrect SHA-1 +check_commit_sha () { + badsha=0 + if test -z $1 + then + badsha=1 + else + sha1_verif="$(git rev-parse --verify --quiet $1^{commit})" + if test -z $sha1_verif + then + badsha=1 + fi + fi + + if test $badsha -ne 0 + then + warn "Warning: the SHA-1 is missing or isn't" \ + "a commit in the following line:" + warn " - $2" + warn + fi + + return $badsha +} + +# prints the bad commits and bad commands +# from the todolist in stdin +check_bad_cmd_and_sha () { + retval=0 + git stripspace --strip-comments | + ( + while read -r line + do + IFS=' ' + set -- $line + command=$1 + sha1=$2 + + case $command in + ''|noop|x|"exec") + # Doesn't expect a SHA-1 + ;; + pick|p|drop|d|reword|r|edit|e|squash|s|fixup|f) + if ! check_commit_sha $sha1 "$line" + then + retval=1 + fi + ;; + *) + warn "Warning: the command isn't recognized" \ + "in the following line:" + warn " - $line" + warn + retval=1 + ;; + esac + done + + return $retval + ) +} + +# Print the list of the SHA-1 of the commits +# from stdin to stdout +todo_list_to_sha_list () { + git stripspace --strip-comments | + while read -r command sha1 rest + do + case $command in + "$comment_char"*|''|noop|x|"exec") + ;; + *) + long_sha=$(git rev-list --no-walk "$sha1" 2>/dev/null) + printf "%s\n" "$long_sha" + ;; + esac + done +} + +# Use warn for each line in stdin +warn_lines () { + while read -r line + do + warn " - $line" + done +} + +# Switch to the branch in $into and notify it in the reflog +checkout_onto () { + GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $onto_name" + output git checkout $onto || die_abort "could not detach HEAD" + git update-ref ORIG_HEAD $orig_head +} + +get_missing_commit_check_level () { + check_level=$(git config --get rebase.missingCommitsCheck) + check_level=${check_level:-ignore} + # Don't be case sensitive + printf '%s' "$check_level" | tr 'A-Z' 'a-z' +} + +# Check if the user dropped some commits by mistake +# Behaviour determined by rebase.missingCommitsCheck. +# Check if there is an unrecognized command or a +# bad SHA-1 in a command. +check_todo_list () { + raise_error=f + + check_level=$(get_missing_commit_check_level) + + case "$check_level" in + warn|error) + # Get the SHA-1 of the commits + todo_list_to_sha_list <"$todo".backup >"$todo".oldsha1 + todo_list_to_sha_list <"$todo" >"$todo".newsha1 + + # Sort the SHA-1 and compare them + sort -u "$todo".oldsha1 >"$todo".oldsha1+ + mv "$todo".oldsha1+ "$todo".oldsha1 + sort -u "$todo".newsha1 >"$todo".newsha1+ + mv "$todo".newsha1+ "$todo".newsha1 + comm -2 -3 "$todo".oldsha1 "$todo".newsha1 >"$todo".miss + + # Warn about missing commits + if test -s "$todo".miss + then + test "$check_level" = error && raise_error=t + + warn "Warning: some commits may have been dropped" \ + "accidentally." + warn "Dropped commits (newer to older):" + + # Make the list user-friendly and display + opt="--no-walk=sorted --format=oneline --abbrev-commit --stdin" + git rev-list $opt <"$todo".miss | warn_lines + + warn "To avoid this message, use \"drop\" to" \ + "explicitly remove a commit." + warn + warn "Use 'git config rebase.missingCommitsCheck' to change" \ + "the level of warnings." + warn "The possible behaviours are: ignore, warn, error." + warn + fi + ;; + ignore) + ;; + *) + warn "Unrecognized setting $check_level for option" \ + "rebase.missingCommitsCheck. Ignoring." + ;; + esac + + if ! check_bad_cmd_and_sha <"$todo" + then + raise_error=t + fi + + if test $raise_error = t + then + # Checkout before the first commit of the + # rebase: this way git rebase --continue + # will work correctly as it expects HEAD to be + # placed before the commit of the next action + checkout_onto + + warn "You can fix this with 'git rebase --edit-todo'." + die "Or you can abort the rebase with 'git rebase --abort'." + fi +} + # The whole contents of this file is run by dot-sourcing it from # inside a shell function. It used to be that "return"s we see # below were not inside any function, and expected to return @@ -849,7 +1044,11 @@ continue) # do we have anything to commit? if git diff-index --cached --quiet HEAD -- then - : Nothing to commit -- skip this + # Nothing to commit -- skip this commit + + test ! -f "$GIT_DIR"/CHERRY_PICK_HEAD || + rm "$GIT_DIR"/CHERRY_PICK_HEAD || + die "Could not remove CHERRY_PICK_HEAD" else if ! test -f "$author_script" then @@ -977,7 +1176,10 @@ else revisions=$onto...$orig_head shortrevisions=$shorthead fi -git rev-list $merges_option --pretty=oneline --reverse --left-right --topo-order \ +format=$(git config --get rebase.instructionFormat) +# the 'rev-list .. | sed' requires %m to parse; the instruction requires %H to parse +git rev-list $merges_option --format="%m%H ${format:-%s}" \ + --reverse --left-right --topo-order \ $revisions ${restrict_revision+^$restrict_revision} | \ sed -n "s/^>//p" | while read -r sha1 rest @@ -1076,13 +1278,13 @@ git_sequence_editor "$todo" || has_action "$todo" || return 2 +check_todo_list + expand_todo_ids test -d "$rewritten" || test -n "$force_rebase" || skip_unnecessary_picks -GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $onto_name" -output git checkout $onto || die_abort "could not detach HEAD" -git update-ref ORIG_HEAD $orig_head +checkout_onto do_rest } |