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-alfo...@ensimag.grenoble-inp.fr>
---
 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 majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to