mirror of
https://github.com/git/git.git
synced 2025-03-24 22:12:01 +00:00
Merge branch 'gr/rebase-i-drop-warn'
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
This commit is contained in:
commit
3a760cad79
@ -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
|
||||
|
@ -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.
|
||||
|
||||
@ -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
|
||||
|
@ -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)
|
||||
@ -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
|
||||
@ -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
|
||||
|
||||
}
|
||||
|
@ -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.
|
||||
@ -46,7 +46,7 @@ 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";;
|
||||
@ -54,6 +54,11 @@ set_fake_editor () {
|
||||
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;;
|
||||
|
@ -1123,4 +1123,127 @@ test_expect_success 'rebase --continue removes CHERRY_PICK_HEAD' '
|
||||
test ! -f .git/CHERRY_PICK_HEAD
|
||||
'
|
||||
|
||||
rebase_setup_and_clean () {
|
||||
test_when_finished "
|
||||
git checkout master &&
|
||||
test_might_fail git branch -D $1 &&
|
||||
test_might_fail git rebase --abort
|
||||
" &&
|
||||
git checkout -b $1 master
|
||||
}
|
||||
|
||||
test_expect_success 'drop' '
|
||||
rebase_setup_and_clean drop-test &&
|
||||
set_fake_editor &&
|
||||
FAKE_LINES="1 drop 2 3 drop 4 5" git rebase -i --root &&
|
||||
test E = $(git cat-file commit HEAD | sed -ne \$p) &&
|
||||
test C = $(git cat-file commit HEAD^ | sed -ne \$p) &&
|
||||
test A = $(git cat-file commit HEAD^^ | sed -ne \$p)
|
||||
'
|
||||
|
||||
cat >expect <<EOF
|
||||
Successfully rebased and updated refs/heads/missing-commit.
|
||||
EOF
|
||||
|
||||
test_expect_success 'rebase -i respects rebase.missingCommitsCheck = ignore' '
|
||||
test_config rebase.missingCommitsCheck ignore &&
|
||||
rebase_setup_and_clean missing-commit &&
|
||||
set_fake_editor &&
|
||||
FAKE_LINES="1 2 3 4" \
|
||||
git rebase -i --root 2>actual &&
|
||||
test D = $(git cat-file commit HEAD | sed -ne \$p) &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
cat >expect <<EOF
|
||||
Warning: some commits may have been dropped accidentally.
|
||||
Dropped commits (newer to older):
|
||||
- $(git rev-list --pretty=oneline --abbrev-commit -1 master)
|
||||
To avoid this message, use "drop" to explicitly remove a commit.
|
||||
|
||||
Use 'git config rebase.missingCommitsCheck' to change the level of warnings.
|
||||
The possible behaviours are: ignore, warn, error.
|
||||
|
||||
Successfully rebased and updated refs/heads/missing-commit.
|
||||
EOF
|
||||
|
||||
test_expect_success 'rebase -i respects rebase.missingCommitsCheck = warn' '
|
||||
test_config rebase.missingCommitsCheck warn &&
|
||||
rebase_setup_and_clean missing-commit &&
|
||||
set_fake_editor &&
|
||||
FAKE_LINES="1 2 3 4" \
|
||||
git rebase -i --root 2>actual &&
|
||||
test_cmp expect actual &&
|
||||
test D = $(git cat-file commit HEAD | sed -ne \$p)
|
||||
'
|
||||
|
||||
cat >expect <<EOF
|
||||
Warning: some commits may have been dropped accidentally.
|
||||
Dropped commits (newer to older):
|
||||
- $(git rev-list --pretty=oneline --abbrev-commit -1 master)
|
||||
- $(git rev-list --pretty=oneline --abbrev-commit -1 master~2)
|
||||
To avoid this message, use "drop" to explicitly remove a commit.
|
||||
|
||||
Use 'git config rebase.missingCommitsCheck' to change the level of warnings.
|
||||
The possible behaviours are: ignore, warn, error.
|
||||
|
||||
You can fix this with 'git rebase --edit-todo'.
|
||||
Or you can abort the rebase with 'git rebase --abort'.
|
||||
EOF
|
||||
|
||||
test_expect_success 'rebase -i respects rebase.missingCommitsCheck = error' '
|
||||
test_config rebase.missingCommitsCheck error &&
|
||||
rebase_setup_and_clean missing-commit &&
|
||||
set_fake_editor &&
|
||||
test_must_fail env FAKE_LINES="1 2 4" \
|
||||
git rebase -i --root 2>actual &&
|
||||
test_cmp expect actual &&
|
||||
cp .git/rebase-merge/git-rebase-todo.backup \
|
||||
.git/rebase-merge/git-rebase-todo &&
|
||||
FAKE_LINES="1 2 drop 3 4 drop 5" \
|
||||
git rebase --edit-todo &&
|
||||
git rebase --continue &&
|
||||
test D = $(git cat-file commit HEAD | sed -ne \$p) &&
|
||||
test B = $(git cat-file commit HEAD^ | sed -ne \$p)
|
||||
'
|
||||
|
||||
cat >expect <<EOF
|
||||
Warning: the command isn't recognized in the following line:
|
||||
- badcmd $(git rev-list --oneline -1 master~1)
|
||||
|
||||
You can fix this with 'git rebase --edit-todo'.
|
||||
Or you can abort the rebase with 'git rebase --abort'.
|
||||
EOF
|
||||
|
||||
test_expect_success 'static check of bad command' '
|
||||
rebase_setup_and_clean bad-cmd &&
|
||||
set_fake_editor &&
|
||||
test_must_fail env FAKE_LINES="1 2 3 bad 4 5" \
|
||||
git rebase -i --root 2>actual &&
|
||||
test_cmp expect actual &&
|
||||
FAKE_LINES="1 2 3 drop 4 5" git rebase --edit-todo &&
|
||||
git rebase --continue &&
|
||||
test E = $(git cat-file commit HEAD | sed -ne \$p) &&
|
||||
test C = $(git cat-file commit HEAD^ | sed -ne \$p)
|
||||
'
|
||||
|
||||
cat >expect <<EOF
|
||||
Warning: the SHA-1 is missing or isn't a commit in the following line:
|
||||
- edit XXXXXXX False commit
|
||||
|
||||
You can fix this with 'git rebase --edit-todo'.
|
||||
Or you can abort the rebase with 'git rebase --abort'.
|
||||
EOF
|
||||
|
||||
test_expect_success 'static check of bad SHA-1' '
|
||||
rebase_setup_and_clean bad-sha &&
|
||||
set_fake_editor &&
|
||||
test_must_fail env FAKE_LINES="1 2 edit fakesha 3 4 5 #" \
|
||||
git rebase -i --root 2>actual &&
|
||||
test_cmp expect actual &&
|
||||
FAKE_LINES="1 2 4 5 6" git rebase --edit-todo &&
|
||||
git rebase --continue &&
|
||||
test E = $(git cat-file commit HEAD | sed -ne \$p)
|
||||
'
|
||||
|
||||
test_done
|
||||
|
Loading…
x
Reference in New Issue
Block a user