All of lore.kernel.org
 help / color / mirror / Atom feed
From: Johannes Schindelin <Johannes.Schindelin@gmx.de>
To: git@vger.kernel.org
Subject: Updated version, was Re: RFC: Patch editing
Date: Fri, 2 Mar 2007 00:30:03 +0100 (CET)	[thread overview]
Message-ID: <Pine.LNX.4.63.0703020020470.22628@wbgn013.biozentrum.uni-wuerzburg.de> (raw)
In-Reply-To: <Pine.LNX.4.63.0702252156190.22628@wbgn013.biozentrum.uni-wuerzburg.de>

Hi,

this is an updated version, which got even less testing than the first 
version :-( However, the changes were small:

- added an attribution to Eric Biederman for the idea,
- if you mark a commit as "edit", it will _not_ be committed, so that
  you do not need --amend _both_ in case of conflicts and no conflicts 
  (noticed by Shawn Pearce),
- a "merge" is refused when it is the _first_ in the list (the first 
  version erroneously checked the opposite...)
- for a failed "merge", like for an "edit", you do not need --amend both 
  in case of conflicts and no conflicts, and
- the wrong usage of the "--reedit-message" flag was replaced by the 
  appropriate "-F .msg -e".

My plans are _not_ to turn this into git-rebase--interactive.sh, as I 
originally planned. Instead, I will try to make git-rebase a builtin, and 
add a "-i" flag which does the equivalent of this script.

BTW would people be mad at me if I did _not_ imitate git-rebase.sh (call 
format-patch and feed that into apply), but rather used cherry-pick 
throughout?

Ciao,
Dscho

-- snipsnap --
#!/bin/sh
#
# Copyright (c) 2006 Johannes E. Schindelin

# SHORT DESCRIPTION
#
# This is a really stupid script to arrange a patch series for submission.
# It does not care for authorship or dates, all it does is make it easy
# to fix up commits in the middle of a series, and rearrange commits.
#
# The original idea comes from Eric W. Biederman, in
# http://article.gmane.org/gmane.comp.version-control.git/22407
#
# MOTIVATION
#
# It is extremely useful for this type of workflow:
#
#	1. have a wonderful idea
#	2. hack on the code
#	3. prepare a series for submission
#	4. submit
#
# where the point (2) consists of several instances of
#
#	A.1. finish something worthy of a commit
#	A.2. commit
#
# and/or
#
#	B.1. realize that something does not work
#	B.2. fix that
#	B.3. commit it
#
# Sometimes the thing fixed in B.2. cannot be amended to the not-quite
# perfect commit it fixes, because that commit is buried deeply in a
# patch series.
#
# Use this script after plenty of "A"s and "B"s, by rearranging, and
# possibly editing and merging commits.
#
# USAGE:
#
# The subcommand "start" will make a list of the commits in the patch
# series <base>..HEAD, where <base> is the commit identiefied by the
# parameter.
#
# It will automatically start an editor, where you can rearrange the
# commits. The list looks more or less like this:
#
#	pick deadbee The oneline of this commit
#	pick fa1afe1 The oneline of the next commit
#	...
#
# By replacing the command "pick" with "edit" you can tell
# this script to stop the loop so you can fix up the commit by editing
# files and/or the commit message.
#
# When replacing the command "pick" with "merge, the script will merge
# this commit's changes into the previous commit, munge the commit
# messages of both, and open the commit message editor.
#
# In both cases, or when a "pick" does not succeed (because of merge
# errors), the loop will stop to let you fix things, and you can continue
# the loop with `git edit-patch-series continue`.

USAGE='(start <basecommit>| continue | reset [--force])'

. git-sh-setup
require_work_tree

SERIESDIR="$(pwd)/.series"
TODO="$SERIESDIR"/todo
DONE="$SERIESDIR"/done

warn () {
	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`" ||
	die "Working tree is dirty"
}

ORIG_REFLOG_ACTION="$GIT_REFLOG_ACTION"

comment_for_reflog () {
	if test -z "$ORIG_REFLOG_ACTION"; then
		GIT_REFLOG_ACTION="edit-patch-series($1)"
		export GIT_REFLOG_ACTION
	fi
}

mark_action_done () {
	sed -n 1p < "$TODO" >> "$DONE"
	sed -n '2,$p' < "$TODO" >> "$TODO".new
	mv -f "$TODO".new "$TODO"
}

do_next () {
	read command sha1 rest < "$TODO"
	case "$command" in
	\#)
		mark_action_done
		continue
	;;
	pick)
		comment_for_reflog pick

		mark_action_done
		git cherry-pick "$sha1" || \
			die "Could not apply $sha1... $rest"
	;;
	edit)
		comment_for_reflog edit

		mark_action_done
		git cherry-pick -n "$sha1" || \
			die "Could not apply $sha1... $rest"
		warn
		warn "After you edited the files, commit the result with"
		warn
		warn "	git commit -F .msg -e"
		warn
		exit 0
	;;
	merge)
		comment_for_reflog merge

		test -s "$DONE" ||
			die "Cannot 'merge' without a previous commit"

		mark_action_done
		failed=f
		git cherry-pick -n "$sha1" || failed=t
		MSG="$SERIESDIR"/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"
		echo "# And this is the 2nd commit message:" >> "$MSG"
		echo >> "$MSG"
		cat .msg >> "$MSG"
		git reset --soft HEAD^
		case $failed in
		f)
			# Usually, I would use --amend here, but -F forbids it
			git commit -F "$MSG" -e
		;;
		t)
			cp "$MSG" .msg
			warn
			warn "Could not apply $sha1... $rest"
			warn "After you fixed that, commit the result with"
			warn
			warn "	git commit -F .msg -e"
			warn
			exit 1
		esac
	;;
	*)
		warn "Unknown command: $command $sha1 $rest"
		warn "Please fix this in the file $TODO."
		exit 1
	esac
	test -s "$TODO" && continue

	HEAD=$(git rev-parse HEAD)
	HEADNAME=$(cat "$SERIESDIR"/head-name)
	git update-ref $HEADNAME $HEAD &&
	git symbolic-ref HEAD $HEADNAME &&
	rm -rf "$SERIESDIR" &&
	warn "Successfully edited patch series and updated $HEADNAME."

	exit $?
}

case "$1" in
start)
	comment_for_reflog start

	HEAD=$(git rev-parse --verify HEAD) || die "No HEAD?"
	BASE=$(git rev-parse --verify "$2") || die "Invalid base"

	# DWIM: use the merge base
	MERGEBASE=$(git merge-base $HEAD $BASE)
	if test "$BASE" != "$MERGEBASE"
	then
		CANDIDATES="$(git merge-base --all $HEAD $BASE)"
		if test "$MERGEBASE" != "$CANDIDATES"
		then
			warn "Error: Multiple merge bases found:"
			warn "$CANDIDATES"
			rm -rf "$SERIESDIR"
			exit 1
		fi
		BASE=$MERGEBASE
	fi

	require_clean_work_tree

	git var GIT_COMMITTER_IDENT >/dev/null || exit

	test -d "$SERIESDIR" &&
		die "Edit-patch-series already started"

	test "z$(git rev-list --parents $BASE..$HEAD | grep " .* ")" != z &&
		die "Cannot edit a series including merges"

	mkdir "$SERIESDIR" || die "Could not create temporary $SERIESDIR"
	git symbolic-ref HEAD > "$SERIESDIR"/head-name || die "Could not get HEAD"

	echo $HEAD > "$SERIESDIR"/head
	echo $BASE > "$SERIESDIR"/base

	cat > "$TODO" << EOF
# Edit the patch series by exchanging lines.  The commits will be
# arranged in the given order.  If you want to edit a commit, replace
# the "pick" command with "edit".  If you want to merge the changes of
# a commit A into a commit B, place A directly after B, and replace the
# "pick" command with "merge".
EOF
	git rev-list --reverse --pretty=oneline --abbrev-commit --abbrev=7 \
		$BASE..$HEAD | sed "s/^/pick /" >> "$TODO"

	cp "$TODO" "$TODO".backup
	${VISUAL:-${EDITOR:-vi}} "$TODO" || warn "Could not execute editor"
	if git diff --no-index "$TODO" "$TODO".backup > /dev/null
	then
		rm -rf "$SERIESDIR"
		warn "No changes"
		exit 0
	fi

	git checkout $BASE || die "Could not checkout $BASE"

	while :
	do
		do_next
	done
;;
continue)
	comment_for_reflog continue

	test -d "$SERIESDIR" || die "No edit-patch-series running"

	require_clean_work_tree

	while :
	do
		do_next
	done
;;
reset)
	comment_for_reflog reset

	test -d "$SERIESDIR" || die "No edit-patch-series running"

	HEADNAME=$(cat "$SERIESDIR"/head-name)
	force=
	case "$2" in
	-f|--force)
		git symbolic-ref HEAD $HEADNAME &&
		git reset --hard
	;;
	*)
		git checkout $HEADNAME
	esac &&
	rm -rf "$SERIESDIR"
;;
*)
	usage
esac

  parent reply	other threads:[~2007-03-01 23:30 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2007-02-25 21:59 RFC: Patch editing Johannes Schindelin
2007-02-26 13:18 ` Peter Baumann
2007-02-26 18:03 ` Shawn O. Pearce
2007-02-26 18:52   ` Johannes Schindelin
2007-02-26 18:56     ` Shawn O. Pearce
2007-02-26 19:51       ` Junio C Hamano
2007-02-27  7:14 ` Daniel Barkalow
2007-02-27 11:54   ` Johannes Schindelin
2007-02-27 17:35     ` Daniel Barkalow
2007-02-27 20:07       ` Johannes Schindelin
2007-02-27 22:07         ` Daniel Barkalow
2007-02-27 22:37           ` Johannes Schindelin
2007-02-28 10:13           ` Karl Hasselström
2007-03-01 23:30 ` Johannes Schindelin [this message]
2007-03-01 23:59   ` Updated version, was " Junio C Hamano

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=Pine.LNX.4.63.0703020020470.22628@wbgn013.biozentrum.uni-wuerzburg.de \
    --to=johannes.schindelin@gmx.de \
    --cc=git@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.