Skip to content

Commit

Permalink
Merge branch 'gr/rebase-i-drop-warn'
Browse files Browse the repository at this point in the history
Add "drop commit-object-name subject" command as another way to
skip replaying of a commit in "rebase -i", and then punish those
who do not use it (and instead just remove the lines) by throwing
a warning.

* gr/rebase-i-drop-warn:
  git rebase -i: add static check for commands and SHA-1
  git rebase -i: warn about removed commits
  git-rebase -i: add command "drop" to remove a commit
  • Loading branch information
Junio C Hamano committed Aug 3, 2015
2 parents 720e20e + 804098b commit 3a760ca
Show file tree
Hide file tree
Showing 5 changed files with 338 additions and 6 deletions.
11 changes: 11 additions & 0 deletions Documentation/config.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2180,6 +2180,17 @@ rebase.autoStash::
successful rebase might result in non-trivial conflicts.
Defaults to false.

rebase.missingCommitsCheck::
If set to "warn", git rebase -i will print a warning if some
commits are removed (e.g. a line was deleted), however the
rebase will still proceed. If set to "error", it will print
the previous warning and stop the rebase, 'git rebase
--edit-todo' can then be used to correct the error. If set to
"ignore", no checking is done.
To drop a commit without warning or error, use the `drop`
command in the todo-list.
Defaults to "ignore".

rebase.instructionFormat
A format string, as specified in linkgit:git-log[1], to be used for
the instruction list during an interactive rebase. The format will automatically
Expand Down
9 changes: 9 additions & 0 deletions Documentation/git-rebase.txt
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,12 @@ rebase.autoSquash::
rebase.autoStash::
If set to true enable '--autostash' option by default.

rebase.missingCommitsCheck::
If set to "warn", print warnings about removed commits in
interactive mode. If set to "error", print the warnings and
stop the rebase. If set to "ignore", no checking is
done. "ignore" by default.

rebase.instructionFormat::
Custom commit list format to use during an '--interactive' rebase.

Expand Down Expand Up @@ -521,6 +527,9 @@ rebasing.
If you just want to edit the commit message for a commit, replace the
command "pick" with the command "reword".

To drop a commit, replace the command "pick" with "drop", or just
delete the matching line.

If you want to fold two or more commits into one, replace the command
"pick" for the second and subsequent commits with "squash" or "fixup".
If the commits had different authors, the folded commit will be
Expand Down
192 changes: 188 additions & 4 deletions git-rebase--interactive.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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 () {
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -844,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
Expand Down Expand Up @@ -1094,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

}
Expand Down
9 changes: 7 additions & 2 deletions t/lib-rebase.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
# specified line.
#
# "<cmd> <lineno>" -- add a line with the specified command
# ("squash", "fixup", "edit", or "reword") and the SHA1 taken
# ("squash", "fixup", "edit", "reword" or "drop") and the SHA1 taken
# from the specified line.
#
# "exec_cmd_with_args" -- add an "exec cmd with args" line.
Expand Down Expand Up @@ -46,14 +46,19 @@ set_fake_editor () {
action=pick
for line in $FAKE_LINES; do
case $line in
squash|fixup|edit|reword)
squash|fixup|edit|reword|drop)
action="$line";;
exec*)
echo "$line" | sed 's/_/ /g' >> "$1";;
"#")
echo '# comment' >> "$1";;
">")
echo >> "$1";;
bad)
action="badcmd";;
fakesha)
echo "$action XXXXXXX False commit" >> "$1"
action=pick;;
*)
sed -n "${line}s/^pick/$action/p" < "$1".tmp >> "$1"
action=pick;;
Expand Down
Loading

0 comments on commit 3a760ca

Please sign in to comment.