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.checkLevel.

Add the configuration variable rebase.checkLevel.
    - 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.checkLevel defaults to "ignore".

Signed-off-by: Galan Rémi <remi.galan-alfo...@ensimag.grenoble-inp.fr>
---
 Documentation/config.txt      |   9 ++++
 Documentation/git-rebase.txt  |   6 +++
 git-rebase--interactive.sh    | 105 ++++++++++++++++++++++++++++++++++++++++++
 t/t3404-rebase-interactive.sh |  10 +++-
 4 files changed, 129 insertions(+), 1 deletion(-)

diff --git a/Documentation/config.txt b/Documentation/config.txt
index 5f76e8c..e2e5554 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -2160,6 +2160,15 @@ rebase.autoStash::
        successful rebase might result in non-trivial conflicts.
        Defaults to false.
 
+rebase.checkLevel::
+       If set to "warn", git rebase -i will print a warning if some
+       commits are removed (i.e. a line was deleted) or if some
+       commits appear more than one time (e.g. the same commit is
+       picked twice), however the rebase will still proceed. If set
+       to "error", it will print the previous warnings 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..d348ca2 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.checkLevel::
+       If set to "warn" print warnings about removed commits and
+       duplicated 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 2882276..58da6ee 100644
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -837,6 +837,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.checkLevel)
+       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.checkLevel 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.checkLevel 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
@@ -1079,6 +1182,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 1bad068..d3a9ed5 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -1103,7 +1103,6 @@ test_expect_success 'rebase -i commits that overwrite 
untracked files (no ff)' '
 '
 
 test_expect_success 'drop' '
-       git checkout master &&
        test_when_finished "git checkout master" &&
        git checkout -b dropBranchTest master &&
        set_fake_editor &&
@@ -1113,4 +1112,13 @@ test_expect_success 'drop' '
        test A = $(git cat-file commit HEAD^^ | sed -ne \$p)
 '
 
+test_expect_success 'rebase -i respects rebase.checkLevel' '
+       test_config rebase.checkLevel error &&
+       test_when_finished "git checkout master" &&
+       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_done
-- 
2.4.1.363.g9535a9c

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to