Check if commits were removed (i.e. a line was deleted) and print warnings or abort git rebase according to the value of the configuration variable rebase.missingCommitsCheckLevel. Add the configuration variable rebase.missingCommitsCheckLevel. - When unset or set to "ignore", no checking is done. - When set to "warn", the commits are checked, warnings are displayed but git rebase still proceeds. - When set to "error", the commits are checked, warnings are displayed and the rebase is aborted. rebase.missingCommitsCheckLevel defaults to "ignore". Signed-off-by: Galan Rémi <remi.galan-alfonso@xxxxxxxxxxxxxxxxxxxxxxx> --- Documentation/config.txt | 7 +++ Documentation/git-rebase.txt | 6 +++ git-rebase--interactive.sh | 105 ++++++++++++++++++++++++++++++++++++++++++ t/t3404-rebase-interactive.sh | 25 ++++++++++ 4 files changed, 143 insertions(+) diff --git a/Documentation/config.txt b/Documentation/config.txt index 4d21ce1..ec9011d 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -2160,6 +2160,13 @@ rebase.autoStash:: successful rebase might result in non-trivial conflicts. Defaults to false. +rebase.missingCommitsCheckLevel:: + If set to "warn", git rebase -i will print a warning if some + commits are removed (i.e. a line was deleted), however the + rebase will still proceed. If set to "error", it will print + the previous warning and abort the rebase. If set to + "ignore", no checking is done. Defaults to "ignore". + receive.advertiseAtomic:: By default, git-receive-pack will advertise the atomic push capability to its clients. If you don't want to this capability diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt index 9cf3760..71029f8 100644 --- a/Documentation/git-rebase.txt +++ b/Documentation/git-rebase.txt @@ -213,6 +213,12 @@ rebase.autoSquash:: rebase.autoStash:: If set to true enable '--autostash' option by default. +rebase.missingCommitsCheckLevel:: + If set to "warn" print warnings about removed commits in + interactive mode. If set to "error" print the warnings and + abort the rebase. If set to "ignore" no checking is + done. "ignore" by default. + OPTIONS ------- --onto <newbase>:: diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index 869cc60..6391423 100644 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -851,6 +851,109 @@ add_exec_commands () { mv "$1.new" "$1" } +# Print the list of the SHA-1 of the commits +# from a todo list in a file. +# $1: todo-file, $2: outfile +todo_list_to_sha_list () { + todo_list=$(git stripspace --strip-comments <"$1") + temp_file=$(mktemp) + echo "$todo_list" >$temp_file + while read -r command sha1 rest + do + case $command in + x|"exec") + ;; + *) + printf "%s\n" "$sha1" + ;; + esac + done <$temp_file >"$2" + rm $temp_file +} + +# Transforms SHA-1 list in argument +# to a list of commits (in place) +# Doesn't check if the SHA-1 are commits. +# $1: file with long SHA-1 list +long_sha_to_commit_list () { + short_missing="" + git_command="git show --oneline" + get_line_command="head -n 1" + temp_file=$(mktemp) + while read -r sha + do + if test -n "$sha" + then + commit=$($git_command $sha | $get_line_command) + if test -n "$commit" + then + printf "%s\n" "$commit" + fi + fi + done <"$1" >$temp_file + mv $temp_file "$1" +} + +# Use warn for each line of a file +# $1: file to warn +warn_file () { + while read -r line + do + warn " - $line" + done <"$1" +} + +# Check if the user dropped some commits by mistake +# Behaviour determined by .gitconfig. +check_commits () { + checkLevel=$(git config --get rebase.missingCommitsCheckLevel) + checkLevel=${checkLevel:-ignore} + # To lowercase + checkLevel=$(echo "$checkLevel" | tr 'A-Z' 'a-z') + + case "$checkLevel" 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 + echo "$(sort -u "$todo".oldsha1)" >"$todo".oldsha1 + echo "$(sort -u "$todo".newsha1)" >"$todo".newsha1 + echo "$(comm -2 -3 "$todo".oldsha1 "$todo".newsha1)" >"$todo".miss + + # Make the list user-friendly + long_sha_to_commit_list "$todo".miss + + # Check missing commits + if test -s "$todo".miss + then + warn "Warning : some commits may have been dropped" \ + "accidentally." + warn "Dropped commits (in no relevent order):" + warn_file "$todo".miss + warn "" + warn "To avoid this message, use \"drop\" to" \ + "explicitly remove a commit." + warn "Use git --config rebase.missingCommitsCheckLevel to change" \ + "the level of warnings (ignore,warn,error)." + warn "" + + if test "$checkLevel" = error + then + die_abort "Rebase aborted due to dropped commits." + fi + fi + ;; + ignore) + ;; + *) + warn "Unrecognized setting $checkLevel for option" \ + "rebase.missingCommitsCheckLevel in git rebase -i." + ;; + esac +} + # 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 @@ -1096,6 +1199,8 @@ has_action "$todo" || expand_todo_ids +check_commits + test -d "$rewritten" || test -n "$force_rebase" || skip_unnecessary_picks GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $onto_name" diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh index 8960083..07a7158 100755 --- a/t/t3404-rebase-interactive.sh +++ b/t/t3404-rebase-interactive.sh @@ -1112,4 +1112,29 @@ test_expect_success 'drop' ' test A = $(git cat-file commit HEAD^^ | sed -ne \$p) ' +test_expect_success 'rebase -i respects rebase.missingCommitsCheckLevel=error' ' + test_config rebase.missingCommitsCheckLevel error && + test_when_finished "git checkout master && + git branch -D tmp2" && + git checkout -b tmp2 master && + set_fake_editor && + test_must_fail env FAKE_LINES="1 2 3 4" \ + git rebase -i --root && + test E = $(git cat-file commit HEAD | sed -ne \$p) +' + +test_expect_success 'rebase -i respects rebase.missingCommitsCheckLevel=warn' ' + test_config rebase.missingCommitsCheckLevel warn && + test_when_finished "git checkout master && + git branch -D tmp2" && + git checkout -b tmp2 master && + set_fake_editor && + FAKE_LINES="1 2 3 4" \ + git rebase -i --root 2>warn.tmp && + test D = $(git cat-file commit HEAD | sed -ne \$p) && + sed -n "1p" warn.tmp >warnl1.tmp && + echo "Warning : some commits may have been dropped accidentally." >l1.tmp && + test_cmp warnl1.tmp l1.tmp +' + test_done -- 2.4.1.411.g9c4ad60 -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html