#!/bin/sh
set -e

# AREAS OF IMPROVEMENT:
# - frequent need to chain "rewind; edit-todo; continue", maybe
#   as "rebase-rewind -i" to be similar to "rebase -i" ?
# - needs to be integrated deeper into git-rebase for more advanced features:
#   - rewind --rebase-merges, --autosquash, --exec
#   - providing "undo to last rewind" on last --continue (similar to "rebase --abort",
#     although more tricky as rewinds can be nested)
#     => needs to record for each rewind the original HEAD and git-rebase-todo
#     Eg. - keep a versionned history of git-rebase-todo (at least on rewind)
#         - record rewinds (eg. in rebase-merge/done with HEAD and git-rebase-todo hashes)
#     Also allow after undo to re-edit git-rebase-todo instead
#   - similarly but finer grained: providing "undo only last --continue"
#   - you name it
#
# LIMITATIONS:
# - needs to know when pick causes conflict, and conflict gets resolved for:
#   - better UI than just "-f"
#   - accurate recording of rewinds in rebase-merge/done

die() {
    echo 2>&1 "ERROR: $@"
    exit 1
}

##

usage() {
    cat <<EOF
Usage: $(basename $0) [-f] <REF>
EOF
}

FORCE=0
case "$1" in
    -h) usage; exit 0 ;;
    -f) FORCE=1; shift ;;
    -*) usage; die "Unknown flag '$1'" ;;
esac

TARGET="${1:-rebase-merge/onto}"
git rev-parse --verify -q "$TARGET^{commit}" > /dev/null || die "'$TARGET' is not a commit"

##

CONFLICTED=
if ! git diff-files --quiet --ignore-submodules; then
    [ $FORCE = 1 ] || die "unstaged changes found in worktree"

    # -f: must undo conflicting pick, unsure heuristic
    CONFLICTED=$(tail -n1 "$(git rev-parse --git-dir)/rebase-merge/done")
fi

# FIXME: assert $TARGET..HEAD can ff
# FIXME: assert $TARGET..HEAD history is linear

# turn commits to be rewinded into a rebase-todo recipe
recipe=$(mktemp --tmpdir git-rebase-rewind.XXXXXX)

# if $TARGET==HEAD, explicitly do nothing (grep -v '^commit' would return non-zero)
if [ $(git rev-parse "$TARGET") != $(git rev-parse HEAD) ]; then
    git rev-list --reverse --pretty=tformat:'pick %h %s' "$TARGET"..HEAD |
        grep -v '^commit' >> "$recipe"
fi

# add the one that caused the conflict if any
[ -z "$CONFLICTED" ] || echo "$CONFLICTED" >> "$recipe"

# include the remaining git-rebase-todo contents, after a break
echo "break" >> "$recipe"
EDITOR="cat >> '$recipe'" git rebase --edit-todo

# inject the new recipe
EDITOR="cat '$recipe' > " git rebase --edit-todo

# rewind HEAD
git reset --hard "$TARGET"
