# command is processed, this file is deleted.
AMEND="$DOTEST"/amend
+# For the post-rewrite hook, we make a list of rewritten commits and
+# their new sha1s. The rewritten-pending list keeps the sha1s of
+# commits that have been processed, but not committed yet,
+# e.g. because they are waiting for a 'squash' command.
+REWRITTEN_LIST="$DOTEST"/rewritten-list
+REWRITTEN_PENDING="$DOTEST"/rewritten-pending
+
PRESERVE_MERGES=
STRATEGY=
ONTO=
}
die_with_patch () {
+ echo "$1" > "$DOTEST"/stopped-sha
make_patch "$1"
git rerere
die "$2"
}
pick_one () {
- no_ff=$NEVER_FF
- case "$1" in -n) sha1=$2; no_ff=t ;; *) sha1=$1 ;; esac
+ ff=--ff
+ case "$1" in -n) sha1=$2; ff= ;; *) sha1=$1 ;; esac
+ case "$NEVER_FF" in '') ;; ?*) ff= ;; esac
output git rev-parse --verify $sha1 || die "Invalid commit name: $sha1"
test -d "$REWRITTEN" &&
pick_one_preserving_merges "$@" && return
output git cherry-pick "$@"
return
fi
- parent_sha1=$(git rev-parse --verify $sha1^) ||
- die "Could not get the parent of $sha1"
- current_sha1=$(git rev-parse --verify HEAD)
- if test -z "$no_ff" && test "$current_sha1" = "$parent_sha1"
- then
- output git reset --hard $sha1
- output warn Fast-forward to $(git rev-parse --short $sha1)
- else
- output git cherry-pick "$@"
- fi
+ output git cherry-pick $ff "$@"
}
pick_one_preserving_merges () {
printf "%s\n" "$msg" > "$GIT_DIR"/MERGE_MSG
die_with_patch $sha1 "Error redoing merge $sha1"
fi
+ echo "$sha1 $(git rev-parse HEAD^0)" >> "$REWRITTEN_LIST"
;;
*)
output git cherry-pick "$@" ||
die_with_patch $1 ""
}
+flush_rewritten_pending() {
+ test -s "$REWRITTEN_PENDING" || return
+ newsha1="$(git rev-parse HEAD^0)"
+ sed "s/$/ $newsha1/" < "$REWRITTEN_PENDING" >> "$REWRITTEN_LIST"
+ rm -f "$REWRITTEN_PENDING"
+}
+
+record_in_rewritten() {
+ oldsha1="$(git rev-parse $1)"
+ echo "$oldsha1" >> "$REWRITTEN_PENDING"
+
+ case "$(peek_next_command)" in
+ squash|s|fixup|f)
+ ;;
+ *)
+ flush_rewritten_pending
+ ;;
+ esac
+}
+
do_next () {
rm -f "$MSG" "$AUTHOR_SCRIPT" "$AMEND" || exit
read command sha1 rest < "$TODO"
mark_action_done
pick_one $sha1 ||
die_with_patch $sha1 "Could not apply $sha1... $rest"
+ record_in_rewritten $sha1
;;
reword|r)
comment_for_reflog reword
mark_action_done
pick_one $sha1 ||
die_with_patch $sha1 "Could not apply $sha1... $rest"
- git commit --amend
+ git commit --amend --no-post-rewrite
+ record_in_rewritten $sha1
;;
edit|e)
comment_for_reflog edit
mark_action_done
pick_one $sha1 ||
die_with_patch $sha1 "Could not apply $sha1... $rest"
+ echo "$sha1" > "$DOTEST"/stopped-sha
make_patch $sha1
git rev-parse --verify HEAD > "$AMEND"
warn "Stopped at $sha1... $rest"
rm -f "$SQUASH_MSG" "$FIXUP_MSG"
;;
esac
+ record_in_rewritten $sha1
;;
*)
warn "Unknown command: $command $sha1 $rest"
test ! -f "$DOTEST"/verbose ||
git diff-tree --stat $(cat "$DOTEST"/head)..HEAD
} &&
+ {
+ test -s "$REWRITTEN_LIST" &&
+ git notes copy --for-rewrite=rebase < "$REWRITTEN_LIST" ||
+ true # we don't care if this copying failed
+ } &&
+ if test -x "$GIT_DIR"/hooks/post-rewrite &&
+ test -s "$REWRITTEN_LIST"; then
+ "$GIT_DIR"/hooks/post-rewrite rebase < "$REWRITTEN_LIST"
+ true # we don't care if this hook failed
+ fi &&
rm -rf "$DOTEST" &&
git gc --auto &&
warn "Successfully rebased and updated $HEADNAME."
esac
echo "$command${sha1:+ }$sha1${rest:+ }$rest" >&$fd
done <"$TODO" >"$TODO.new" 3>>"$DONE" &&
- mv -f "$TODO".new "$TODO" ||
+ mv -f "$TODO".new "$TODO" &&
+ case "$(peek_next_command)" in
+ squash|s|fixup|f)
+ record_in_rewritten "$ONTO"
+ ;;
+ esac ||
die "Could not skip unnecessary pick commands"
}
}
fi
+ record_in_rewritten "$(cat "$DOTEST"/stopped-sha)"
+
require_clean_work_tree
do_rest
;;
if test ! -z "$1"
then
- output git show-ref --verify --quiet "refs/heads/$1" ||
- die "Invalid branchname: $1"
output git checkout "$1" ||
die "Could not checkout $1"
fi