git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [PATCH 2/2] Teach rebase an interactive mode

From
Johannes Schindelin <johannes.schindelin@gmx.de>
Date
Jun 24, 2007, 11:09 UTC
Message-ID
<Pine.LNX.4.64.0706241204450.4059@racer.site>
In-Reply-To
<7v4pkxydim.fsf@assigned-by-dhcp.cox.net>
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
Previous: Junio C HamanoNext: Johannes Schindelin
Message 7 of 10 in “Teach rebase an interactive mode”
  1. 2/2 Teach rebase an interactive modeJohannes Schindelin, Jun 23, 2007
  2. Alex RiesenJun 23, 2007
  3. Johannes SchindelinJun 23, 2007
  4. Brian GernhardtJun 24, 2007
  5. Johannes SchindelinJun 24, 2007
  6. Junio C HamanoJun 24, 2007
  7. Johannes SchindelinJun 24, 2007
  8. 1/2 Move the pick_author code to git-sh-setupJohannes Schindelin, Jun 25, 2007
  9. 2/2 Teach rebase an interactive modeJohannes Schindelin, Jun 25, 2007
  10. Johannes SchindelinJun 24, 2007

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.