Hi, thanks to both of you, for your comments. I am a little pressed on time, so I will only be able to fix all this up this evening, but I thought I let you know what the state is. I am not answering point by point, because I agree with all of the nits. The four missing points are: - fix up the commit message so that it is clear from where the list of commits comes, - put a sensible reflog message at the last update, - move the usage section into the man page, and - fix the quoting in patch 1/2. Will resend (after using rebase -i myself, yeah!) tonight. --- Documentation/git-rebase.txt | 12 ++--- git-rebase--interactive.sh | 90 ++++++++++++++++++++++++------------------ 2 files changed, 56 insertions(+), 46 deletions(-) diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt index 2e474e8..e96d72e 100644 --- a/Documentation/git-rebase.txt +++ b/Documentation/git-rebase.txt @@ -235,7 +235,7 @@ Interactive mode ---------------- Rebasing interactively means that you have a chance to edit the commits -which are rebased. You can not only reorder the commits, but also +which are rebased. You can reorder the commits, and you can remove them (weeding out bad or otherwise unwanted patches). The list will look like this: @@ -247,18 +247,16 @@ pick fa1afe1 The oneline of the next commit ------------------------------------------- The oneline descriptions are purely for your pleasure; `git-rebase` will -not look at them but at the commit names, so do not delete or edit the -names. +not look at them but at the commit names ("deadbee" and "fa1afe1" in this +example), so do not delete or edit the names. By replacing the command "pick" with the command "edit", you can tell `git-rebase` to stop after applying that commit, so that you can edit the files and/or the commit message, amend the commit and continue rebasing. -If you want to fold two commits into one, just replace the command "pick" -with "squash" for the second commit. After squashing the commits, -`git-rebase` will start an editor with both commit messages, so you -can compose the commit message for the squashed commit. +If you want to fold two or more commits into one, replace the command +"pick" with "squash" for the second and subsequent commit. A common use case for the interactive mode is when you want to reorder the last 5 commits, such that what was HEAD~4 becomes the new HEAD. To diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index 6a960b4..1d1e927 100755 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -78,30 +78,31 @@ STRATEGY= VERBOSE= warn () { - echo "$@" >&2 + echo "$*" >&2 } require_clean_work_tree () { # test if working tree is dirty git rev-parse --verify HEAD > /dev/null && git update-index --refresh && - test -z "`git diff-files --name-only`" && - test -z "`git diff-index --cached --name-only HEAD`" || + git diff-files --quiet && + git diff-index --cached --quiet HEAD || die "Working tree is dirty" } ORIG_REFLOG_ACTION="$GIT_REFLOG_ACTION" comment_for_reflog () { - if test -z "$ORIG_REFLOG_ACTION"; then - GIT_REFLOG_ACTION="rebase --interactive ($1)" + case "$ORIG_REFLOG_ACTION" in + ''|rebase*) + GIT_REFLOG_ACTION="rebase -i ($1)" export GIT_REFLOG_ACTION - fi + esac } mark_action_done () { - sed -n 1p < "$TODO" >> "$DONE" - sed -n '2,$p' < "$TODO" >> "$TODO".new + sed -e 1q < "$TODO" >> "$DONE" + sed -e 1d < "$TODO" >> "$TODO".new mv -f "$TODO".new "$TODO" } @@ -117,6 +118,7 @@ die_with_patch () { pick_one () { case "$1" in -n) sha1=$2 ;; *) sha1=$1 ;; esac + git rev-parse --verify $sha1 || die "Invalid commit name: $sha1" parent_sha1=$(git rev-parse --verify $sha1^ 2>/dev/null) current_sha1=$(git rev-parse --verify HEAD) if [ $current_sha1 = $parent_sha1 ]; then @@ -131,17 +133,17 @@ pick_one () { do_next () { read command sha1 rest < "$TODO" case "$command" in - \#) + \#|'') mark_action_done continue - ;; + ;; pick) comment_for_reflog pick mark_action_done pick_one $sha1 || die_with_patch $sha1 "Could not apply $sha1... $rest" - ;; + ;; edit) comment_for_reflog edit @@ -155,11 +157,11 @@ do_next () { warn " git commit --amend" warn exit 0 - ;; + ;; squash) comment_for_reflog squash - test -s "$DONE" || + test -z "$(grep -ve '^$' -e '^#' < $DONE)" && die "Cannot 'squash' without a previous commit" mark_action_done @@ -168,20 +170,21 @@ do_next () { MSG="$DOTEST"/message echo "# This is a combination of two commits." > "$MSG" echo "# The first commit's message is:" >> "$MSG" - git cat-file commit HEAD | sed -n '/^$/,$p' >> "$MSG" + echo >> "$MSG" + git cat-file commit HEAD | sed -e '1,/^$/d' >> "$MSG" echo >> "$MSG" echo "# And this is the 2nd commit message:" >> "$MSG" echo >> "$MSG" - git cat-file commit $sha1 | sed -n '/^$/,$p' >> "$MSG" + git cat-file commit $sha1 | sed -e '1,/^$/d' >> "$MSG" git reset --soft HEAD^ author_script=$(get_author_ident_from_commit $sha1) case $failed in f) # This is like --amend, but with a different message - eval $author_script + eval "$author_script" export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE git commit -F "$MSG" -e - ;; + ;; t) cp "$MSG" "$GIT_DIR"/MERGE_MSG warn @@ -192,21 +195,22 @@ do_next () { warn " git commit -F \"$GIT_DIR\"/MERGE_MSG -e" die_with_patch $sha1 "" esac - ;; + ;; *) warn "Unknown command: $command $sha1 $rest" die_with_patch $sha1 "Please fix this in the file $TODO." esac - test -s "$TODO" && continue + test -s "$TODO" && return HEAD=$(git rev-parse HEAD) HEADNAME=$(cat "$DOTEST"/head-name) +# TODO: reflog? git update-ref $HEADNAME $HEAD && git symbolic-ref HEAD $HEADNAME || exit rm -rf "$DOTEST" && warn "Successfully rebased and updated $HEADNAME." - exit $? + exit } do_rest () { @@ -214,12 +218,12 @@ do_rest () { do do_next done - test $? = 0 -a -f "$DOTEST"/verbose && + test -f "$DOTEST"/verbose && git diff --stat $(cat "$DOTEST"/head)..HEAD exit } -while case "$#" in 0) break ;; esac +while case $# in 0) break ;; esac do case "$1" in --continue) @@ -229,7 +233,7 @@ do require_clean_work_tree do_rest - ;; + ;; --abort) comment_for_reflog abort @@ -241,15 +245,16 @@ do git reset --hard $HEAD && rm -rf "$DOTEST" exit - ;; + ;; --skip) comment_for_reflog skip test -d "$DOTEST" || die "No interactive rebase running" git reset --hard && do_rest - ;; + ;; -s|--strategy) + shift case "$#,$1" in *,*=*) STRATEGY="-s `expr "z$1" : 'z-[^=]*=\(.*\)'`" ;; @@ -259,22 +264,22 @@ do STRATEGY="-s $2" shift ;; esac - ;; + ;; --merge) # we use merge anyway - ;; + ;; -C*) die "Interactive rebase uses merge, so $1 does not make sense" - ;; + ;; -v) VERBOSE=t - ;; + ;; -i|--interactive) # yeah, we know - ;; + ;; ''|-h) usage - ;; + ;; *) test -d "$DOTEST" && die "Interactive rebase already started" @@ -295,8 +300,13 @@ do require_clean_work_tree - test -z "$2" || git checkout "$2" || - die "Could not checkout $2" + if [ ! -z "$2"] + then + git show-ref --verify --quiet "refs/heads/$2" || + die "Invalid branchname: $2" + git checkout "$2" || + die "Could not checkout $2" + fi HEAD=$(git rev-parse --verify HEAD) || die "No HEAD?" UPSTREAM=$(git rev-parse --verify "$1") || die "Invalid base" @@ -314,23 +324,25 @@ do test t = "$VERBOSE" && : > "$DOTEST"/verbose cat > "$TODO" << EOF -# Reorder by exchanging lines. Skip by removing lines. If you want to -# edit a commit, replace the "pick" command with "edit". If you want to -# squash the changes of a commit A into a commit B, place A directly -# after B, and replace the "pick" command with "squash". +# Rebasing $UPSTREAM..$HEAD onto $ONTO +# +# Commands: +# pick = use commit +# edit = use commit, but stop for amending +# squash = use commit, but meld into previous commit EOF git rev-list --no-merges --pretty=oneline --abbrev-commit \ --abbrev=7 --reverse $UPSTREAM..$HEAD | \ sed "s/^/pick /" >> "$TODO" - test -s "$TODO" || die "Nothing to do" + test -z "$(grep -ve '^$' -e '^#' < $TODO)" && + die "Nothing to do" cp "$TODO" "$TODO".backup ${VISUAL:-${EDITOR:-vi}} "$TODO" || die "Could not execute editor" git reset --hard $ONTO && do_rest - ;; esac shift done -- 1.5.2.2.279.g9b198-dirty - To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html