rebase -i: fix short SHA-1 collision

The 'todo' sheet for interactive rebase shows abbreviated SHA-1's and then performs its operations upon those shortened values. This can lead to an abort if the SHA-1 of a reworded or edited commit is no longer unique within the abbreviated SHA-1 space and a subsequent SHA-1 in the todo list has the same abbreviated value. For example: edit f00dfad first pick badbeef second If, after editing, the new SHA-1 of "first" also has prefix badbeef, then the subsequent 'pick badbeef second' will fail since badbeef is no longer a unique SHA-1 abbreviation: error: short SHA1 badbeef is ambiguous. fatal: Needed a single revision Invalid commit name: badbeef Fix this problem by expanding the SHA-1's in the todo list before performing the operations. [es: also collapse & expand SHA-1's for --edit-todo; respect core.commentchar in transform_todo_ids(); compose commit message] Signed-off-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Junio C Hamano committed Aug 23, 2013 at 20:10 UTC 75c69766554c4b34ede65502d481dd7beb7f3388
2 files changed +31 -1
git-rebase--interactive.sh
+30
@@ -689,6 +689,32 @@ skip_unnecessary_picks () {
689 die "Could not skip unnecessary pick commands"
690 }
691
692 +transform_todo_ids () {
693 + while read -r command rest
694 + do
695 + case "$command" in
696 + "$comment_char"* | exec)
697 + # Be careful for oddball commands like 'exec'
698 + # that do not have a SHA-1 at the beginning of $rest.
699 + ;;
700 + *)
701 + sha1=$(git rev-parse --verify --quiet "$@" ${rest%% *}) &&
702 + rest="$sha1 ${rest#* }"
703 + ;;
704 + esac
705 + printf '%s\n' "$command${rest:+ }$rest"
706 + done <"$todo" >"$todo.new" &&
707 + mv -f "$todo.new" "$todo"
708 +}
709 +
710 +expand_todo_ids() {
711 + transform_todo_ids
712 +}
713 +
714 +collapse_todo_ids() {
715 + transform_todo_ids --short=7
716 +}
717 +
718 # Rearrange the todo list that has both "pick sha1 msg" and
719 # "pick sha1 fixup!/squash! msg" appears in it so that the latter
720 # comes immediately after the former, and change "pick" to
@@ -841,6 +867,7 @@ skip)
867 edit-todo)
868 git stripspace --strip-comments <"$todo" >"$todo".new
869 mv -f "$todo".new "$todo"
870 + collapse_todo_ids
871 append_todo_help
872 git stripspace --comment-lines >>"$todo" <<\EOF
873
@@ -852,6 +879,7 @@ EOF
879
880 git_sequence_editor "$todo" ||
881 die "Could not execute editor"
882 + expand_todo_ids
883
884 exit
885 ;;
@@ -1008,6 +1036,8 @@ git_sequence_editor "$todo" ||
1036 has_action "$todo" ||
1037 die_abort "Nothing to do"
1038
1039 +expand_todo_ids
1040 +
1041 test -d "$rewritten" || test -n "$force_rebase" || skip_unnecessary_picks
1042
1043 GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $onto_name"
t/t3404-rebase-interactive.sh
+1 -1
@@ -1049,7 +1049,7 @@ test_expect_success 'short SHA-1 setup' '
1049 )
1050 '
1051
1052 -test_expect_failure 'short SHA-1 collide' '
1052 +test_expect_success 'short SHA-1 collide' '
1053 test_when_finished "reset_rebase && git checkout master" &&
1054 git checkout collide &&
1055 (