git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* git sequencer prototype
@ 2008-07-01  2:38 Stephan Beyer
  2008-07-01  2:38 ` [RFC/PATCH 1/4] Add git-sequencer shell prototype Stephan Beyer
                   ` (2 more replies)
  0 siblings, 3 replies; 52+ messages in thread
From: Stephan Beyer @ 2008-07-01  2:38 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Johannes Schindelin

Hi,

here is the patchset for the git-sequencer prototype, documentation,
test suite and a first git-am and git-rebase-i migration.
Indeed, monster patches. ;)

I'm using sequencer-based git-am and git-rebase-i and also git-sequencer
itself for around 2-3 weeks now. So, for me, it is reality-proven, but
I'm curious about your opinions/suggestions in usage and source.

The migration patches are a little hard to code-review in the diff-form,
but feel free to apply, test, and then look at the code ;)

Regards,
  Stephan

^ permalink raw reply	[flat|nested] 52+ messages in thread

* [RFC/PATCH 1/4] Add git-sequencer shell prototype
  2008-07-01  2:38 git sequencer prototype Stephan Beyer
@ 2008-07-01  2:38 ` Stephan Beyer
  2008-07-01  2:38   ` [RFC/PATCH 2/4] Add git-sequencer prototype documentation Stephan Beyer
                     ` (3 more replies)
  2008-07-01  8:51 ` git sequencer prototype Junio C Hamano
  2008-07-04 21:00 ` Alex Riesen
  2 siblings, 4 replies; 52+ messages in thread
From: Stephan Beyer @ 2008-07-01  2:38 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Johannes Schindelin, Stephan Beyer

git sequencer is planned as a backend for user scripts
that execute a sequence of git instructions and perhaps
need manual intervention, for example git-rebase or git-am.

Mentored-by: Christian Couder <chriscool@tuxfamily.org>
Mentored-by: Daniel Barkalow <barkalow@iabervon.org>
Signed-off-by: Stephan Beyer <s-beyer@gmx.net>
---
Some comments:
 - It's declared as plumbing, but the prototype can't really be
   named "plumbing", imho, since it uses porcelain like git-commit.
 - I hope it is obvious that parts of it are based on
   git-rebase-i and git-am code, so if you stumble over code
   that looks familiar, it is not necessarily by accident.

 .gitignore       |    1 +
 Makefile         |    1 +
 command-list.txt |    1 +
 git-sequencer.sh | 1902 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 1905 insertions(+), 0 deletions(-)
 create mode 100755 git-sequencer.sh

diff --git a/.gitignore b/.gitignore
index 4ff2fec..65a075b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -111,6 +111,7 @@ git-revert
 git-rm
 git-send-email
 git-send-pack
+git-sequencer
 git-sh-setup
 git-shell
 git-shortlog
diff --git a/Makefile b/Makefile
index bf77292..db93956 100644
--- a/Makefile
+++ b/Makefile
@@ -250,6 +250,7 @@ SCRIPT_SH += git-rebase--interactive.sh
 SCRIPT_SH += git-rebase.sh
 SCRIPT_SH += git-repack.sh
 SCRIPT_SH += git-request-pull.sh
+SCRIPT_SH += git-sequencer.sh
 SCRIPT_SH += git-sh-setup.sh
 SCRIPT_SH += git-stash.sh
 SCRIPT_SH += git-submodule.sh
diff --git a/command-list.txt b/command-list.txt
index 3583a33..44bb5b0 100644
--- a/command-list.txt
+++ b/command-list.txt
@@ -101,6 +101,7 @@ git-rev-parse                           ancillaryinterrogators
 git-rm                                  mainporcelain common
 git-send-email                          foreignscminterface
 git-send-pack                           synchingrepositories
+git-sequencer                           plumbingmanipulators
 git-shell                               synchelpers
 git-shortlog                            mainporcelain
 git-show                                mainporcelain common
diff --git a/git-sequencer.sh b/git-sequencer.sh
new file mode 100755
index 0000000..31b0568
--- /dev/null
+++ b/git-sequencer.sh
@@ -0,0 +1,1902 @@
+#!/bin/sh
+# A git sequencer prototype.
+
+SUBDIRECTORY_OK=Yes
+OPTIONS_KEEPDASHDASH=
+OPTIONS_SPEC="\
+git-sequencer [options] [--] [<file>]
+git-sequencer (--continue | --abort | --skip | --edit | --status)
+--
+ Options to start a sequencing process
+B,batch        run in batch-mode
+onto=          checkout the given commit or branch first
+q,quiet        suppress output
+v,verbose      be more verbose
+ Options to restart/change a sequencing process or show information
+continue       continue paused sequencer process
+abort          restore original branch and abort
+skip           skip current patch and continue
+status         show the status of the sequencing process
+edit           invoke editor to let user edit the remaining insns
+ Options to be used by user scripts
+caller=        provide information string: name|abort|cont|skip
+"
+
+. git-sh-setup
+require_work_tree
+
+git var GIT_COMMITTER_IDENT >/dev/null ||
+	die 'You need to set your committer info first'
+
+SEQ_DIR="$GIT_DIR/sequencer"
+TODO="$SEQ_DIR/todo"
+DONE="$SEQ_DIR/done"
+MSG="$SEQ_DIR/message"
+PATCH="$SEQ_DIR/patch"
+AUTHOR_SCRIPT="$SEQ_DIR/author-script"
+ORIG_AUTHOR_SCRIPT="$SEQ_DIR/author-script.orig"
+CALLER_SCRIPT="$SEQ_DIR/caller-script"
+WHY_FILE="$SEQ_DIR/why"
+MARK_PREFIX='refs/sequencer-marks'
+
+warn () {
+	echo "$*" >&2
+}
+
+cleanup () {
+	for ref in $(git for-each-ref --format='%(refname)' "$MARK_PREFIX")
+	do
+		git update-ref -d "$ref" "$ref" || return
+	done
+	rm -rf "$SEQ_DIR"
+}
+
+# Die if there has been a conflict
+die_to_continue () {
+	warn "$*"
+	test -z "$BATCHMODE" ||
+		die_abort 'Aborting, because of batch mode.'
+	test -z "$WHY" &&
+		echo 'conflict' >"$WHY_FILE" ||
+		echo "$WHY" >"$WHY_FILE"
+	exit 3
+}
+
+die_abort () {
+	restore
+	cleanup
+	die "$1"
+}
+
+quit () {
+	cleanup
+	test -z "$*" ||
+		echo "$*"
+	exit 0
+}
+
+output () {
+	case "$VERBOSE" in
+	0)
+		"$@" >/dev/null
+		;;
+	1)
+		output=$("$@" 2>&1 )
+		status=$?
+		test $status -ne 0 && printf '%s\n' "$output"
+		return $status
+		;;
+	2)
+		"$@"
+		;;
+	esac
+}
+
+require_clean_work_tree () {
+	# test if working tree is dirty
+	git rev-parse --verify HEAD >/dev/null &&
+	git update-index --ignore-submodules --refresh &&
+	git diff-files --quiet --ignore-submodules &&
+	git diff-index --cached --quiet HEAD --ignore-submodules -- ||
+	die 'Working tree is dirty'
+}
+
+ORIG_REFLOG_ACTION="$GIT_REFLOG_ACTION"
+
+comment_for_reflog () {
+	if test -z "$ORIG_REFLOG_ACTION"
+	then
+		GIT_REFLOG_ACTION='sequencer'
+		test -z "$CALLER" ||
+			GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION ($CALLER)"
+		export GIT_REFLOG_ACTION
+	fi
+}
+
+# Get commit message from commit $1
+commit_message () {
+	git cat-file commit "$1" | sed -e '1,/^$/d'
+}
+
+LAST_COUNT=
+mark_action_done () {
+	sed -e 1q <"$TODO" >>"$DONE"
+	sed -e 1d <"$TODO" >"$TODO.new"
+	mv -f "$TODO.new" "$TODO"
+	if test "$VERBOSE" -gt 0
+	then
+		count=$(grep -c '^[^#]' <"$DONE")
+		total=$(expr "$count" + "$(grep -c '^[^#]' <"$TODO")")
+		if test "$LAST_COUNT" != "$count"
+		then
+			LAST_COUNT="$count"
+			test "$VERBOSE" -lt 1 ||
+				printf 'Sequencing (%d/%d)\r' "$count" "$total"
+			test "$VERBOSE" -lt 2 || echo
+		fi
+	fi
+}
+
+# Generate message, patch and author script files
+make_patch () {
+	parent_sha1=$(git rev-parse --verify "$1"^) ||
+		die "Cannot get patch for $1^"
+	git diff-tree -p "$parent_sha1..$1" >"$PATCH"
+	test -f "$MSG" ||
+		commit_message "$1" >"$MSG"
+	test -f "$AUTHOR_SCRIPT" ||
+		get_author_ident_from_commit "$1" >"$AUTHOR_SCRIPT"
+}
+
+# Generate a patch and die with "conflict" status code
+die_with_patch () {
+	make_patch "$1"
+	git rerere
+	die_to_continue "$2"
+}
+
+restore () {
+	git rerere clear
+
+	HEADNAME=$(cat "$SEQ_DIR/head-name")
+	HEAD=$(cat "$SEQ_DIR/head")
+	case $HEADNAME in
+	refs/*)
+		git symbolic-ref HEAD "$HEADNAME"
+		;;
+	esac &&
+	output git reset --hard "$HEAD"
+}
+
+has_action () {
+	grep '^[^#]' "$1" >/dev/null
+}
+
+# Check if text file $1 contains a commit message
+has_message () {
+	test -n "$(sed -n -e '/^Signed-off-by:/d;/^[^#]/p' <"$1")"
+}
+
+# Count parents of commit $1
+count_parents() {
+	git cat-file commit "$1" | sed -n -e '1,/^$/p' | grep -c '^parent'
+}
+
+# Evaluate the author script to get author information to
+# apply it with "with_author git foo" then.
+get_current_author () {
+	. "$AUTHOR_SCRIPT" ||
+		die_abort 'Author script is damaged. This must not happen!'
+}
+
+# Run command with author information
+with_author () {
+	GIT_AUTHOR_NAME="$GIT_AUTHOR_NAME" \
+	GIT_AUTHOR_EMAIL="$GIT_AUTHOR_EMAIL" \
+	GIT_AUTHOR_DATE="$GIT_AUTHOR_DATE" \
+		"$@"
+}
+
+# Usage: pick_one (cherry-pick|revert) [-*|--edit] sha1
+pick_one () {
+	what="$1"
+	# we just assume that this is either cherry-pick or revert
+	shift
+
+	# check for fast-forward if no options are given
+	if expr "x$1" : 'x[^-]' >/dev/null
+	then
+		test "$(git rev-parse --verify "$1^")" = \
+			"$(git rev-parse --verify HEAD)" &&
+			output git reset --hard "$1" &&
+			return
+	fi
+	test "$1" != '--edit' -a "$what" = 'revert' &&
+		what='revert --no-edit'
+	test -n "$SIGNOFF" &&
+		what="$what -s"
+	$use_output git $what "$@"
+}
+
+nth_string () {
+	case "$1" in
+	*1[0-9]|*[04-9])
+		echo "$1th"
+		;;
+	*1)
+		echo "$1st"
+		;;
+	*2)
+		echo "$1nd"
+		;;
+	*3)
+		echo "$1rd"
+		;;
+	esac
+}
+
+make_squash_message () {
+	if test -f "$squash_msg"
+	then
+		count=$(($(sed -n -e 's/^# This is [^0-9]*\([1-9][0-9]*\).*/\1/p' \
+			<"$squash_msg" | sed -n -e '$p')+1))
+		echo "# This is a combination of $count commits."
+		sed -e '1d' -e '2,/^./{
+			/^$/d
+		}' <"$squash_msg"
+	else
+		count=2
+		echo '# This is a combination of 2 commits.'
+		echo '# The first commit message is:'
+		echo
+		commit_message HEAD
+	fi
+	echo
+	echo "# This is the $(nth_string "$count") commit message:"
+	echo
+	commit_message "$1"
+}
+
+make_squash_message_multiple () {
+	echo '# This is a dummy to get the 0.' >"$squash_msg"
+	for cur_sha1 in $(git rev-list --reverse "$sha1..HEAD")
+	do
+		make_squash_message "$cur_sha1" >"$MSG"
+		cp "$MSG" "$squash_msg"
+	done
+}
+
+peek_next_command () {
+	sed -n -e '1s/ .*$//p' <"$TODO"
+}
+
+# If $1 is a mark, make a ref from it; otherwise keep it
+mark_to_ref () {
+	arg="$1"
+	ref=$(expr "x$arg" : 'x:0*\([0-9][0-9]*\)$')
+	test -n "$ref" &&
+		arg="$MARK_PREFIX/$ref"
+	printf '%s\n' "$arg"
+}
+
+mark_to_commit () {
+	git rev-parse --verify "$(mark_to_ref "$1")"
+}
+
+
+cannot_fallback () {
+	echo "$1"
+	die_to_continue 'Cannot fall back to three-way merge. Please hand-edit.'
+}
+
+
+fallback_3way () {
+	O_OBJECT=$(cd "$GIT_OBJECT_DIRECTORY" && pwd)
+
+	rm -fr "$PATCH-merge-"*
+	mkdir "$PATCH-merge-tmp-dir"
+
+	# First see if the patch records the index info that we can use.
+	git apply --build-fake-ancestor "$PATCH-merge-tmp-index" "$PATCH" &&
+	GIT_INDEX_FILE="$PATCH-merge-tmp-index" \
+		git write-tree >"$PATCH-merge-base+" ||
+	cannot_fallback 'Repository lacks necessary blobs to fall back on 3-way merge.'
+
+	echo 'Using index info to reconstruct a base tree...'
+	if GIT_INDEX_FILE="$PATCH-merge-tmp-index" \
+		git apply --cached "$PATCH"
+	then
+		mv "$PATCH-merge-base+" "$PATCH-merge-base"
+		mv "$PATCH-merge-tmp-index" "$PATCH-merge-index"
+	else
+		cannot_fallback "Did you hand edit your patch?
+It does not apply to blobs recorded in its index."
+	fi
+
+	test -f "$PATCH-merge-index" &&
+	his_tree=$(GIT_INDEX_FILE="$PATCH-merge-index" git write-tree) &&
+	orig_tree=$(cat "$PATCH-merge-base") &&
+	rm -fr "$PATCH-merge-"* || exit 1
+
+	echo 'Falling back to patching base and 3-way merge...'
+
+	# This is not so wrong.  Depending on which base we picked,
+	# orig_tree may be wildly different from ours, but his_tree
+	# has the same set of wildly different changes in parts the
+	# patch did not touch, so recursive ends up canceling them,
+	# saying that we reverted all those changes.
+
+	eval GITHEAD_$his_tree='"$firstline"'
+	export GITHEAD_$his_tree
+	git-merge-recursive "$orig_tree" -- HEAD "$his_tree" || {
+		git rerere
+		die 'Failed to merge in the changes.'
+	}
+}
+
+# Run hook "$@" (with arguments) if executable
+run_hook () {
+	test -z "$1" || return
+	hookname="$1"
+	hook="$GIT_DIR/hooks/$hookname"
+	shift
+	if test -x "$hook"
+	then
+		"$hook" "$@" ||
+			die_to_continue "Hook $hookname failed."
+	fi
+}
+
+# Add Signed-off-by: line if general option --signoff is given
+dashdash_signoff () {
+	add_signoff=
+	if test -n "$SIGNOFF"
+	then
+		last_signed_off_by=$(
+			sed -n -e '/^Signed-off-by: /p' <"$MSG" | sed -n -e '$p'
+		)
+		test "$last_signed_off_by" = "$SIGNOFF" ||
+			add_signoff=$(
+				test '' = "$last_signed_off_by" && echo
+				echo "$SIGNOFF"
+			)
+	fi
+	{
+		test -s "$MSG" && cat "$MSG"
+		test -n "$add_signoff" && echo "$add_signoff"
+	} >"$MSG.new"
+	mv "$MSG.new" "$MSG"
+}
+
+
+### --caller-related functions
+
+# Show string for caller invocation for --abort/--continue/--skip
+print_caller_info () {
+	case "$1" in
+	--abort)
+		echo "$CALLER_ABRT"
+		;;
+	--continue)
+		echo "$CALLER_CONT"
+		;;
+	--skip)
+		echo "$CALLER_SKIP"
+		;;
+	*)
+		warn 'Internal error: Unknown print_caller argument!'
+		;;
+	esac
+}
+
+# Print the program to invoke to (--)abort/continue/skip
+print_caller() {
+	caller_info=$(print_caller_info "$1")
+	if test -n "$CALLERCOMPARE" -a -n "$caller_info"
+	then
+		test -n "$CALLER" && printf "$CALLER "
+		echo "$caller_info"
+	else
+		echo "git sequencer $1"
+	fi
+}
+
+# Test if --caller was set correctly
+# $1 must be abort/continue/skip
+test_caller () {
+	caller_info=$(print_caller_info "--$1")
+	test -n "$CALLERCOMPARE" -a \
+		"$CALLERCOMPARE" != "$CALLERSTRING" -a \
+		-n "$caller_info" &&
+		die "You must use '$CALLER $caller_info' to $1!"
+}
+
+# Generate $CALLER_SCRIPT file from "git foo|--abort|--continue|--skip"
+# string $1
+generate_caller_script () {
+	echo "$1" | sed -e 's/^\(.*\)|\(.*\)|\(.*\)|\(.*\)$/\
+CALLERCOMPARE="\0"\
+CALLER="\1"\
+CALLER_ABRT="\2"\
+CALLER_CONT="\3"\
+CALLER_SKIP="\4"/' >"$CALLER_SCRIPT"
+}
+
+# Run caller script and set GIT_CHERRY_PICK_HELP
+assure_caller () {
+	test -r "$CALLER_SCRIPT" &&
+		. "$CALLER_SCRIPT"
+
+	GIT_CHERRY_PICK_HELP="
+After resolving the conflicts, mark the corrected paths with
+	git add <paths>
+or
+	git rm <paths>
+and run
+	$(print_caller --continue)
+Note, that your working tree must match the index."
+	export GIT_CHERRY_PICK_HELP
+}
+
+
+### Helpers for check_* functions:
+
+# Print a warning on todo checking
+todo_warn () {
+	printf 'Warning at line %d, %s: %s\n' "$line" "$command" "$*"
+}
+
+# Raise an error on todo checking
+todo_error () {
+	printf 'Error at line %d, %s: %s\n' "$line" "$command" "$*"
+	retval=1
+}
+
+# Test if $1 is a commit on todo checking
+commit_check () {
+	test "$(git cat-file -t "$1" 2>/dev/null)" = commit ||
+		todo_error "'$1' is not a commit."
+}
+
+# A helper function to check if $1 is a an available mark during check_*
+arg_is_mark_check () {
+	for cur_mark in $available_marks
+	do
+		test "$cur_mark" -eq "${1#:}" && return
+	done
+	todo_error "Mark $1 is not yet defined."
+}
+
+# A helper function for check_* and mark usage:
+# check if "$1" is a commit or a defined mark
+arg_is_mark_or_commit_check () {
+	if expr "x$1" : 'x:[0-9][0-9]*$' >/dev/null
+	then
+		arg_is_mark_check "$1"
+	else
+		commit_check "$1"
+	fi
+}
+
+strategy_check () {
+	case "$1" in
+	resolve|recursive|octopus|ours|subtree|theirs)
+		return
+		;;
+	esac
+	todo_warn "Strategy '$1' not known."
+}
+
+
+### Author script functions
+
+clean_author_script () {
+	cat "$ORIG_AUTHOR_SCRIPT" >"$AUTHOR_SCRIPT"
+}
+
+# Take "Name <e-mail>" in stdin and outputs author script
+make_author_script_from_string () {
+	sed -e 's/^\(.*\) <\(.*\)>.*$/GIT_AUTHOR_NAME="\1"\
+GIT_AUTHOR_EMAIL="\2"\
+GIT_AUTHOR_DATE=/'
+}
+
+
+### General option functions (and options spec)
+
+OPTIONS_GENERAL=' General options:
+author= Override author
+C,reuse-commit= Reuse message and authorship data from commit
+F,file= Take commit message from given file
+m,message= Specify commit message
+M,reuse-message= Reuse message from commit
+signoff Add signoff
+e,edit Invoke editor to edit commit message'
+
+# Check if option is a general option
+check_general_option () {
+	general_shift=1
+	case "$1" in
+	--signoff)
+		return 0
+		;;
+	--author)
+		general_shift=2
+		author_opt="t$author_opt"
+		expr "x$2" : 'x.* <.*>' >/dev/null ||
+			todo_error "Author \"$2\" not in the correct format \"Name <e-mail>\"."
+		;;
+	-m)
+		general_shift=2
+		msg_opt="t$msg_opt"
+		;;
+	-C)
+		general_shift=2
+		msg_opt="t$msg_opt"
+		author_opt="t$author_opt"
+		commit_check "$2"
+		;;
+	-M)
+		general_shift=2
+		msg_opt="t$msg_opt"
+		commit_check "$2"
+		;;
+	-F)
+		general_shift=2
+		msg_opt="t$msg_opt"
+		test -r "$2" ||
+			todo_error "Cannot read file '$2'."
+		;;
+	-e)
+		test -z "$BATCHMODE" ||
+			todo_error '--batch and --edit options do not make sense together.'
+		;;
+	*)
+		return 1
+		;;
+	esac
+}
+
+# Set a general option variable or return 1
+handle_general_option () {
+	general_shift=1
+	case "$1" in
+	--signoff)
+		SIGNOFF=$(git-var GIT_COMMITTER_IDENT | sed -e '
+				s/>.*/>/
+				s/^/Signed-off-by: /')
+		;;
+	--author)
+		general_shift=2
+		AUTHOR=t
+		echo "$2" |
+			make_author_script_from_string >"$AUTHOR_SCRIPT"
+		;;
+	-m)
+		general_shift=2
+		MESSAGE="$2"
+		;;
+	-C)
+		general_shift=2
+		AUTHOR=t
+		get_author_ident_from_commit "$2" >"$AUTHOR_SCRIPT"
+		MESSAGE=$(commit_message "$2")
+		;;
+	-M)
+		general_shift=2
+		MESSAGE=$(commit_message "$2")
+		;;
+	-F)
+		general_shift=2
+		MESSAGE=$(cat "$2")
+		;;
+	-e)
+		EDIT=--edit
+		;;
+	*)
+		return 1
+		;;
+	esac
+}
+
+
+### Functions for checking and realizing TODO instructions
+# Note that options_*, check_* and insn_* function names are reserved.
+
+options_pause="\
+pause
+--
+"
+
+# Check the "pause" instruction
+check_pause () {
+	shift
+	test -z "$BATCHMODE" ||
+		todo_error '"pause" instruction and --batch do not make sense together.'
+	test $# -eq 0 ||
+		todo_error 'The pause instruction takes no arguments.'
+	return 0
+}
+
+# Realize the "pause" instruction
+insn_pause () {
+	mark_action_done
+	make_patch HEAD
+	if test "$VERBOSE" -gt 0
+	then
+		warn
+		warn 'You can now edit files and add them to the index.'
+		warn 'Once you are satisfied with your changes, run'
+		warn
+		warn "	$(print_caller --continue)"
+		warn
+		warn 'If you only want to change the commit message, run'
+		warn 'git commit --amend before.'
+	fi
+	echo 'pause' >"$WHY_FILE"
+	exit 2
+}
+
+
+options_patch="\
+patch [options] <file>
+--
+3,3way Fall back to 3-way merge
+k Pass to git-mailinfo (keep subject)
+n Pass to git-mailinfo (no utf8)
+$OPTIONS_GENERAL
+ Options passed to git-apply:
+R,reverse Reverse changes
+context= Ensure context of ... lines
+p= Remove ... leading slashes
+unidiff-zero Bypass unidiff checks
+exclude= Do not apply changes to given files
+no-add Ignore additions of patch
+whitespace= Set whitespace error behavior
+inaccurate-eof Support inaccurate EOFs
+u no-op (backward compatibility)
+binary no-op (backward compatibility)
+"
+
+# Check the "file" instruction
+check_patch () {
+	while test $# -gt 1
+	do
+		case "$1" in
+		-3|-k|-n|-u|--binary|-R|--reverse|--unidiff-zero|--no-add|--inaccurate-eof)
+			:
+			;;
+		-p|--whitespace|--exclude|--context)
+			shift
+			;;
+		--)
+			shift
+			break
+			;;
+		-*)
+			check_general_option "$@" ||
+				todo_warn "Unknown option $1"
+			;;
+		*)
+			todo_error "Wrong number of arguments. ($# given, 1 wanted)"
+			break
+			;;
+		esac
+		shift $general_shift
+	done
+
+	if test -f "$1" -a -r "$1"
+	then
+		grep -e '^diff' "$1" >/dev/null ||
+			todo_error "File '$1' contains no patch."
+	else
+		todo_error "Cannot open file '$1'."
+	fi
+	return 0
+}
+
+# Realize the "patch" instruction
+insn_patch () {
+	comment_for_reflog patch
+
+	apply_opts=
+	mailinfo_opts=
+	threeway=
+
+	# temporary files
+	infofile="$SEQ_DIR/patch-info"
+	msgfile="$SEQ_DIR/patch-msg"
+
+	while test "$#" -gt 1
+	do
+		case "$1" in
+		-3)
+			threeway=t
+			;;
+		-k|-n)
+			mailinfo_opts="$mailinfo_opts $1"
+			;;
+		-u|--binary)
+			: Do nothing. It is there due to b/c only.
+			;;
+		-R|--reverse|--unidiff-zero|--no-add|--inaccurate-eof)
+			apply_opts="$apply_opts $1"
+			;;
+		-p)
+			shift
+			apply_opts="$apply_opts -p$1"
+			;;
+		--whitespace|--exclude)
+			apply_opts="$apply_opts $1=$2"
+			shift
+			;;
+		--context)
+			shift
+			apply_opts="$apply_opts -C$1"
+			;;
+		--)
+			shift
+			break
+			;;
+		*)
+			handle_general_option "$@"
+			;;
+		esac
+		shift $general_shift
+	done
+
+	filename="$1"
+
+	mark_action_done
+
+	git mailinfo $mailinfo_opts "$msgfile" "$PATCH" \
+		<"$filename" >"$infofile" ||
+		die_abort 'Could not read or parse mail'
+
+	if test -z "$AUTHOR"
+	then
+		sed -n -e '
+			s/^Author: \(.*\)$/GIT_AUTHOR_NAME="\1"/p;
+			s/^Email: \(.*\)$/GIT_AUTHOR_EMAIL="\1"/p;
+			s/^Date: \(.*\)$/GIT_AUTHOR_DATE="\1"/p
+		' <"$infofile" >>"$AUTHOR_SCRIPT"
+		# If sed's result is empty, we keep the original
+		# author script by appending.
+	fi
+
+	# Ignore every mail that's not containing a patch
+	test -s "$PATCH" || {
+		warn 'Does not contain patch!'
+		return 0
+	}
+
+	edit_msg=
+	if grep -e '^Subject:' "$infofile" >/dev/null
+	then
+		# add subject to commit message
+		sed -n -e '/^Subject:/ s/Subject: //p' <"$infofile"
+		echo
+		echo
+		cat "$msgfile"
+	else
+		cat "$msgfile"
+		edit_msg=t
+	fi | git stripspace >"$MSG"
+	rm -f "$infofile" "$msgfile"
+
+	firstline=$(sed -e '1q' <"$MSG")
+
+	get_current_author
+
+	test -n "$MESSAGE" && printf '%s\n' "$MESSAGE" >"$MSG"
+	test -z "$firstline" && firstline=$(sed -e '1q' <"$MSG")
+
+	dashdash_signoff
+
+	with_author run_hook applypatch-msg "$MSG"
+	failed=
+	with_author git apply $apply_opts --index "$PATCH" || failed=t
+
+	if test -n "$failed" -a -n "$threeway" && (with_author fallback_3way)
+	then
+		# Applying the patch to an earlier tree and merging the
+		# result may have produced the same tree as ours.
+		git diff-index --quiet --cached HEAD -- && {
+			echo 'No changes -- Patch already applied.'
+			return 0
+			# XXX: do we want that?
+		}
+		# clear apply_status -- we have successfully merged.
+		failed=
+	fi
+
+	if test -n "$failed"
+	then
+		# XXX: This is just a stupid hack:
+		with_author git apply $apply_opts --reject --index "$PATCH"
+		die_to_continue 'Patch failed. See the .rej files.'
+		# XXX: We actually needed a git-apply flag that creates
+		# conflict markers and sets the DIFF_STATUS_UNMERGED flag.
+	fi
+
+	with_author run_hook pre-applypatch
+
+	test -n "$EDIT" && edit_msg=t
+	if ! has_message "$MSG" || test -n "$edit_msg"
+	then
+		echo "
+# Please enter the commit message for the applied patch.
+# (Comment lines starting with '#' will not be included)" >>"$MSG"
+
+		git_editor "$MSG" ||
+			die_with_patch 'Editor returned error.'
+		has_message "$MSG" ||
+			die_with_patch 'No commit message given.'
+	fi
+
+	tree=$(git write-tree) &&
+	parent=$(git rev-parse --verify HEAD) &&
+	commit=$(git commit-tree "$tree" -p "$parent" <"$MSG") &&
+	git update-ref -m "$GIT_REFLOG_ACTION: $firstline" HEAD "$commit" "$parent" ||
+		die_to_continue 'Could not commit tree.'
+
+	test -x "$GIT_DIR/hooks/post-applypatch" &&
+		with_author "$GIT_DIR/hooks/post-applypatch"
+
+	return 0
+}
+
+
+options_pick="\
+pick [options] <commit>
+--
+R,reverse Revert introduced changes
+mainline= Specify parent number to use for merge commits
+$OPTIONS_GENERAL
+"
+
+# Check the "pick" instruction
+check_pick () {
+	revert=
+	mainline=
+	while test $# -gt 1
+	do
+		case "$1" in
+		-R)
+			revert="$1"
+			;;
+		--mainline)
+			shift
+			mainline="$1"
+			test "$mainline" -gt 0 || {
+				todo_error '--mainline needs an integer beginning from 1.'
+				mainline=
+			}
+			;;
+		--)
+			shift
+			break
+			;;
+		-*)
+			check_general_option "$@" ||
+				todo_warn "Unknown option $1"
+			;;
+		*)
+			todo_error "Wrong number of arguments. ($# given, 1 wanted)"
+			break
+			;;
+		esac
+		shift $general_shift
+	done
+
+	if test -n "$mainline"
+	then
+		test -z "$revert" ||
+			todo_error "Cannot use $revert together with --mainline."
+
+		parents=$(count_parents "$1")
+		test "$parents" -lt "$mainline" &&
+			todo_error "Commit has only $parents (less than $mainline) parents."
+		test "$parents" -eq 1 &&
+			todo_warn 'Commit is not a merge at all.'
+	fi
+
+	commit_check "$1"
+
+	return 0
+}
+
+# Realize the "pick" instruction
+insn_pick () {
+	op=cherry-pick
+	mainline=
+	while test $# -gt 1
+	do
+		case "$1" in
+		-R)
+			op=revert
+			;;
+		--mainline)
+			mainline="$1 $2"
+			shift
+			;;
+		--)
+			shift
+			break
+			;;
+		-*)
+			handle_general_option "$@"
+			;;
+		esac
+		shift $general_shift
+	done
+
+	sha1=$(git rev-parse --verify "$1")
+
+	comment_for_reflog pick
+
+	mark_action_done
+
+	edit_msg="$EDIT"
+
+	# Don't edit on pick, but later, if author or message given.
+	test -n "$AUTHOR" -o -n "$MESSAGE" && edit_msg=
+
+	# Be kind to users and ignore --mainline=1 on non-merge commits
+	test -n "$mainline" -a 2 -gt $(count_parents "$sha1") && mainline=
+
+	use_output=
+	test -n "$edit_msg" ||
+		use_output=output
+
+	pick_one "$op" $edit_msg $mainline $sha1 ||
+		die_with_patch $sha1 "Could not apply $sha1"
+
+	test -n "$EDIT" ||
+		use_output=output
+
+	get_current_author
+	signoff=
+	test -n "$SIGNOFF" && signoff=-s
+	if test -n "$AUTHOR" -a -n "$MESSAGE"
+	then
+		# this is just because we only want to do ONE amending commit
+		$use_output git commit --amend $EDIT $signoff --no-verify \
+			--author "$GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>" \
+			--message="$MESSAGE"
+	else
+		# correct author if AUTHOR is set
+		test -n "$AUTHOR" &&
+			$use_output git commit --amend $EDIT --no-verify -C HEAD \
+				--author "$GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>"
+		# XXX: a git-cherry-pick --author could be nicer here
+		# correct commit message if MESSAGE is set
+		test -n "$MESSAGE" &&
+			$use_output git commit --amend $EDIT $signoff --no-verify \
+				-C HEAD --message="$MESSAGE"
+	fi
+
+	return 0
+}
+
+options_edit="\
+edit <commit>
+--
+"
+
+# Check the "edit" instruction
+check_edit () {
+	shift
+	test -z "$BATCHMODE" ||
+		todo_error '"edit" instruction and --batch do not make sense together.'
+	test $# -eq 1 ||
+		todo_error "Wrong number of arguments. ($# given, 1 wanted)"
+	return 0
+}
+
+# Realize the "edit" instruction
+insn_edit () {
+	shift
+	insn_pick "$1"
+
+	# work around mark_action_done in insn_pause
+	echo '# pausing' >"$TODO.new"
+	cat "$TODO" >>"$TODO.new"
+	mv -f "$TODO.new" "$TODO"
+	insn_pause
+}
+
+
+options_squash="\
+squash <commit>
+squash [options] --from <mark>
+--
+from Squash all commits from <mark>
+collect-signoffs Collect Signed-off-by: lines
+include-merges Do not fail on merge commits
+$OPTIONS_GENERAL
+"
+
+# Check the "squash" instruction
+check_squash () {
+	from=
+	collect=
+	merges=
+	while test $# -gt 1
+	do
+		case "$1" in
+		--from)
+			from=t
+			;;
+		--collect-signoffs)
+			collect=t
+			todo_warn 'Not yet implemented.'
+			;;
+		--include-merges)
+			merges=t
+			;;
+		--)
+			shift
+			break
+			;;
+		*)
+			check_general_option "$@" ||
+				todo_error "Unknown option $1"
+			;;
+		esac
+		shift $general_shift
+	done
+
+	# in --from mode?
+	if test -n "$from"
+	then
+		test -z "$merges" &&
+			cat "$DONE" "$TODO" |
+			sed -n -e '/^[ \t]*mark[ \t]*:\{0,1\}'"${1#:}"'\($\|[^0-9]\)/,'"$line"'p' |
+			grep '^[ \t]*merge' >/dev/null &&
+			todo_error "$1..HEAD contains a merge commit. You may try --include-merges."
+
+		arg_is_mark_check "$1"
+	else
+		test -n "$merges" &&
+			todo_error '--include-merges only makes sense with --from <mark>.'
+		test -n "$collect" &&
+			todo_error '--collect-signoffs only makes sense with --from <mark>.'
+
+		commit_check "$1"
+	fi
+
+	return 0
+}
+
+# Realize the "squash" instruction
+insn_squash () {
+	squash_msg="$MSG-squash"
+	from=
+	while test $# -gt 1
+	do
+		case "$1" in
+		--from)
+			from=t
+			;;
+		--collect-signoffs)
+			warn '--collect-signoffs is not implemented.'
+			# XXX
+			;;
+		--include-merges)
+			: # This has to be done during check_squash
+			;;
+		--)
+			shift
+			break
+			;;
+		*)
+			handle_general_option "$@"
+			;;
+		esac
+		shift $general_shift
+	done
+
+	if test -n "$from"
+	then
+		sha1=$(mark_to_commit ":${1#:}")
+	else
+		sha1=$(git rev-parse --verify "$1")
+	fi
+
+	comment_for_reflog squash
+
+	# Hm, somehow I don't think --skip on a conflicting squash
+	# may be useful, but if someone wants to do it, it should
+	# do the obvious: skip what squash would do.
+	echo "$(git rev-parse HEAD)" >"$SEQ_DIR/skiphead"
+
+	mark_action_done
+
+	if test -n "$MESSAGE"
+	then
+		printf '%s\n' "$MESSAGE" >"$MSG"
+	else
+		if test -n "$from"
+		then
+			make_squash_message_multiple "$sha1"
+		else
+			make_squash_message "$sha1" >"$MSG"
+		fi
+	fi
+
+	case "$(peek_next_command)" in
+	squash)
+		edit_commit=
+		use_output=output
+		cp "$MSG" "$squash_msg"
+		;;
+	*)
+		edit_commit=-e
+		use_output=
+		rm -f "$squash_msg" || exit
+		;;
+	esac
+
+	test -n "$MESSAGE" && edit_commit=
+	test -n "$EDIT" && edit_commit=-e
+
+	# is --author (or equivalent) set?
+	if test -n "$AUTHOR"
+	then
+		# evaluate author script
+		get_current_author
+	else
+		# if --author is not given, we get the authorship
+		# information from the commit before.
+		eval "$(get_author_ident_from_commit HEAD)"
+		# but we do not write an author script
+	fi
+
+	# --from or not
+	failed=
+	if test -n "$from"
+	then
+		output git reset --soft "$sha1"
+	else
+		output git reset --soft HEAD^
+
+		pick_one cherry-pick -n "$sha1" || failed=t
+	fi
+
+	dashdash_signoff
+
+	if test -z "$failed"
+	then
+		# This is like --amend, but with a different message
+		with_author $use_output git commit --no-verify \
+			-F "$MSG" $edit_commit || failed=t
+	else
+		cp "$MSG" "$GIT_DIR/MERGE_MSG"
+		warn
+		warn "Could not apply $sha1..."
+		die_with_patch $sha1 ""
+	fi
+
+	return 0
+}
+
+
+options_mark="\
+mark <mark>
+--
+"
+
+# Check the "mark" instruction
+check_mark () {
+	shift
+	test $# -eq 1 ||
+		todo_error "Wrong number of arguments. ($# given, 1 wanted)"
+	my_mark=$(expr "x${1#:}" : 'x0*\([0-9][0-9]*\)$')
+	test -n "$my_mark" ||
+		todo_error "Mark $1 not an integer."
+	expr "x$available_marks " : " $my_mark " >/dev/null &&
+		todo_error "Mark :$my_mark already defined. Choose another integer."
+	available_marks="$available_marks $my_mark"
+
+	return 0
+}
+
+# Realize the "mark" instruction
+insn_mark () {
+	shift
+	given="$1"
+
+	mark_action_done
+
+	mark=$(mark_to_ref ":${given#:}")
+	git update-ref "$mark" HEAD
+	return 0
+}
+
+
+options_merge="\
+merge [options] <commit-ish> ...
+--
+standard Generate default commit message
+s,strategy= Use merge strategy ...
+$OPTIONS_GENERAL
+"
+
+# Check the "merge" instruction
+check_merge () {
+	while test $# -gt 1
+	do
+		case "$1" in
+		--standard)
+			msg_opt="t$msg_opt"
+			;;
+		-s)
+			shift
+			strategy_check "$1"
+			;;
+		--)
+			shift
+			break
+			;;
+		-*)
+			check_general_option "$@" ||
+				todo_error "Unknown option $1"
+			;;
+		esac
+		shift $general_shift
+	done
+
+	test $# -gt 0 ||
+		todo_error 'What are my parents? Need new parents!'
+
+	while test $# -gt 0
+	do
+		arg_is_mark_or_commit_check "$1"
+		shift
+	done
+	return 0
+}
+
+# Realize the "merge" instruction
+insn_merge () {
+	comment_for_reflog merge
+
+	standard=
+
+	while test $# -gt 1
+	do
+		case "$1" in
+		--standard)
+			standard=t
+			;;
+		-s)
+			shift
+			strategy="-s $1"
+			;;
+		--)
+			shift
+			break
+			;;
+		-*)
+			handle_general_option "$@"
+			;;
+		esac
+		shift $general_shift
+	done
+
+	new_parents=
+	for p in "$@"
+	do
+		new_parents="$new_parents $(mark_to_ref $p)"
+	done
+	new_parents="${new_parents# }"
+
+	get_current_author
+
+	if test -n "$standard"
+	then
+		for cur_parent in $new_parents
+		do
+			printf '%s\t\t%s' \
+				"$(git rev-parse "$cur_parent")" "$cur_parent"
+		done | git fmt-merge-msg >"$MSG"
+	fi
+
+	test -n "$MESSAGE" &&
+		printf '%s\n' "$MESSAGE" >"$MSG"
+
+	dashdash_signoff
+	if ! has_message "$MSG" || test -n "$EDIT"
+	then
+		echo "
+# Please enter the merge commit message.
+# (Comment lines starting with '#' will not be included)" >>"$MSG"
+
+		git_editor "$MSG" ||
+			die_with_patch 'Editor returned error.'
+		has_message "$MSG" ||
+			die_with_patch 'No commit message given.'
+	fi
+
+	mark_action_done
+	if ! with_author output git merge $strategy -m junk $new_parents
+	then
+		git rerere
+		cat "$MSG" >"$GIT_DIR/MERGE_MSG"
+		die_to_continue 'Error merging'
+	fi
+	with_author output git commit --amend -F "$MSG" --no-verify
+	return 0
+}
+
+
+options_reset="\
+reset <commit-ish>
+--
+"
+
+# Check the "reset" instruction
+check_reset () {
+	shift
+	test $# -eq 1 ||
+		todo_error "Wrong number of arguments. ($# given, 1 wanted)"
+	arg_is_mark_or_commit_check "$1"
+
+	return 0
+}
+
+# Realize the "reset" instruction
+insn_reset () {
+	shift
+	comment_for_reflog reset
+
+	mark_action_done
+	output git reset --hard "$(mark_to_commit "$1")"
+}
+
+
+options_ref="\
+ref <ref>
+--
+"
+
+# Check the "ref" instruction
+check_ref () {
+	shift
+	test $# -eq 1 ||
+		todo_error "Wrong number of arguments. ($# given, 1 wanted)"
+	return 0
+}
+
+# Realize the "ref" instruction
+insn_ref () {
+	shift
+	comment_for_reflog ref
+
+	mark_action_done
+	output git update-ref "$1" HEAD
+}
+
+
+### Instruction main loop
+
+# Run check_* or insn_* with massaged options
+# Usage: run_insn (check|do) <insn> <insn options>
+run_insn () {
+	c_or_i="$1"
+	insn="$2"
+	shift
+	shift
+	eval "option_spec=\"\$options_$insn\""
+	eval "$(printf "%s" "$option_spec" |
+		git rev-parse --parseopt -- "$@" ||
+		echo return)"
+	case "$c_or_i" in
+	check)
+		check_$insn "$@"
+		;;
+	do)
+		insn_$insn "$@"
+		;;
+	esac
+}
+
+# Execute the first line of the current TODO file
+execute_next () {
+	rm -f "$MSG"
+	echo 'HEAD' >"$SEQ_DIR/skiphead"
+	read command rol <"$TODO"
+	case "$command" in
+	'#'*|'')
+		mark_action_done
+		;;
+	*)
+		test "$VERBOSE" -gt 1 &&
+			echo "Next line: $command $rol"
+
+		# reset general options
+		clean_author_script
+		general_shift=1
+		AUTHOR=
+		EDIT=
+		MESSAGE=
+		SIGNOFF=
+		# XXX: eval is evil!
+		eval "run_insn do $command $rol" ||
+			die_to_continue 'An unexpected error occured.'
+		;;
+	esac
+}
+
+# Execute the rest of the TODO file and finish
+execute_rest () {
+	while has_action "$TODO"
+	do
+		execute_next
+	done
+
+	comment_for_reflog finish
+	if test -n "$ONTO"
+	then
+		git update-ref -m "$GIT_REFLOG_ACTION: $ONTO" "$ONTO" HEAD &&
+		git symbolic-ref HEAD "$ONTO"
+	fi &&
+	quit
+}
+
+
+# Main loop to check instructions
+todo_check () {
+	todo="$TODO"
+	test -n "$1" && todo="$1"
+
+	available_marks=' '
+	for cur_mark in $(git for-each-ref --format='%(refname)' "$MARK_PREFIX")
+	do
+		available_marks="$available_marks ${cur_mark##*/}"
+	done
+
+	retval=0
+	line=1
+	while read command rol
+	do
+		case "$command" in
+		'#'*|'')
+			;;
+		*)
+			eval 'test -n "$options_'"$command"'"' || {
+				retval=1
+				todo_error "Unknown $command instruction"
+				continue
+			}
+
+			general_shift=1
+			msg_opt=
+			author_opt=
+			eval "run_insn check $command $rol" ||
+				todo_error "Unknown option used"
+			expr "$msg_opt" : 'ttt*' >/dev/null &&
+				todo_error 'You can only provide one commit message option.'
+			expr "$author_opt" : 'ttt*' >/dev/null &&
+				todo_error 'You can only provide one author option.'
+			;;
+		esac
+		line=$(expr "$line" + 1)
+	done <"$todo"
+	return $retval
+}
+
+prepare_editable_todo () {
+	echo '# ALREADY DONE:'
+	sed -e 's/^/#  /' <"$DONE"
+	echo '# '
+	echo "$markline"
+	cat "$TODO"
+}
+
+get_saved_options () {
+	VERBOSE=$(cat "$SEQ_DIR/verbose")
+	ONTO=$(cat "$SEQ_DIR/onto")
+	WHY=$(cat "$WHY_FILE" 2>/dev/null)
+	return 0
+}
+
+# Realize sequencer invocation
+do_startup () {
+	test -d "$SEQ_DIR" &&
+		die 'sequencer already started'
+
+	require_clean_work_tree
+
+	HEAD=$(git rev-parse --verify HEAD) ||
+		die 'No HEAD?'
+
+	mkdir "$SEQ_DIR" ||
+		die "Could not create temporary $SEQ_DIR"
+
+	# save options
+	echo "$VERBOSE" >"$SEQ_DIR/verbose"
+	test -n "$CALLERSTRING" &&
+		generate_caller_script "$CALLERSTRING"
+	# generate empty DONE and "onto" file
+	: >"$DONE"
+	: >"$SEQ_DIR/onto"
+
+	if test -n "$BATCHMODE"
+	then
+		GIT_CHERRY_PICK_HELP='  Aborting (batch mode)'
+		export GIT_CHERRY_PICK_HELP
+	else
+		assure_caller
+	fi
+
+	comment_for_reflog start
+
+	# save old head before checking out the given <branch>
+	git symbolic-ref HEAD >"$SEQ_DIR/head-name" 2>/dev/null ||
+		echo 'detached HEAD' >"$SEQ_DIR/head-name"
+	echo $HEAD >"$SEQ_DIR/head"
+	# do it here so that die_abort can work ;)
+
+	if test -n "$ONTO"
+	then
+		# if ONTO is a branch name, then keep it, otherwise
+		# we don't care anymore and erase ONTO.
+		if git-show-ref --quiet --verify -- "refs/heads/${ONTO##*/}"
+		then
+			ONTO="refs/heads/${ONTO##*/}"
+			echo "$ONTO" >"$SEQ_DIR/onto"
+			output git checkout "$(git rev-parse "$ONTO")" ||
+				die_abort "Could not checkout branch $ONTO"
+		else
+			output git checkout "$ONTO" ||
+				die_abort "Could not checkout commit $ONTO"
+			ONTO=
+		fi
+	fi
+
+	(git var GIT_AUTHOR_IDENT || git var COMMITTER_IDENT) |
+		make_author_script_from_string >"$ORIG_AUTHOR_SCRIPT"
+
+	# read from file or from stdin?
+	if test -z "$1"
+	then
+		: >"$TODO" ||
+			die_abort "Could not generate TODO file $TODO"
+		while read line
+		do
+			printf '%s\n' "$line" >>"$TODO" ||
+				die_abort "Could not append to TODO file $TODO"
+		done
+	else
+		cp "$1" "$TODO" ||
+			die_abort "Could not find TODO file $1."
+	fi
+
+	has_action "$TODO" || die_abort 'Nothing to do'
+	todo_check || WHY=todo die_to_continue "TODO file contains errors.
+Fix with git sequencer --edit or abort with $(print_caller --abort)."
+
+	execute_rest
+	exit
+}
+
+# Realize --continue.
+do_continue () {
+	test -d "$SEQ_DIR" || die 'No sequencer running'
+	test_caller 'continue'
+
+	todo_check || WHY=todo die_to_continue "TODO file contains errors.
+Fix with git sequencer --edit or abort with $(print_caller --abort)."
+
+	comment_for_reflog continue
+
+	# Sanity check
+	git rev-parse --verify HEAD >/dev/null ||
+		die_to_continue 'Cannot read HEAD'
+	git update-index --ignore-submodules --refresh &&
+		git diff-files --quiet --ignore-submodules ||
+		die_to_continue 'Working tree is dirty. (Use git add or git stash first?)'
+
+	get_saved_options
+
+	# do we have anything to commit? (staged changes)
+	if ! git diff-index --cached --quiet --ignore-submodules HEAD --
+	then
+		. "$AUTHOR_SCRIPT" ||
+			die_to_continue 'Error evaluating author script. Must not happen!'
+
+		# After "pause", we should amend (merge-safe!).
+		# On conflict, we do not have a commit to amend, so we
+		# should just commit.
+		case "$WHY" in
+		pause)
+			with_author git commit --amend --no-verify -F "$MSG" -e ||
+				die_to_continue 'Could not commit staged changes.'
+			;;
+		conflict)
+			with_author git commit --no-verify -F "$MSG" -e ||
+				die_to_continue 'Could not commit staged changes.'
+			echo '# resolved CONFLICTS of the last instruction' >>"$DONE"
+			;;
+		*)
+			die_to_continue 'There are staged changes. Do not know what to do with them.'
+		esac
+	fi
+
+	require_clean_work_tree
+	rm -f "$WHY_FILE"
+	execute_rest
+	exit
+}
+
+# Realize --abort.
+do_abort () {
+	test -d "$SEQ_DIR" || die 'No sequencer running'
+	test_caller 'abort'
+
+	comment_for_reflog abort
+	restore
+	quit
+}
+
+# Realize --skip.
+do_skip () {
+	test -d "$SEQ_DIR" || die 'No sequencer running'
+	test_caller 'skip'
+
+	todo_check || WHY=todo die_to_continue "TODO file contains errors.
+Fix with git sequencer --edit or abort with $(print_caller --abort)."
+	get_saved_options
+
+	comment_for_reflog skip
+	git rerere clear
+
+	output git reset --hard "$(cat "$SEQ_DIR/skiphead")" &&
+	rm -f "$WHY_FILE" &&
+	echo '# SKIPPED the last instruction' >>"$DONE" &&
+		execute_rest
+	exit
+}
+
+# Realize --edit.
+do_edit () {
+	test -d "$SEQ_DIR" || die 'No sequencer running'
+
+	markline='### BEGIN EDITING BELOW THIS LINE ###'
+	prepare_editable_todo >"$TODO.new"
+
+	# XXX: does not make sense
+	#      when input does not come from a terminal
+	git_editor "$TODO.new" ||
+		die 'Editor returned an error.'
+
+	if test -t 0 -a -t 1
+	then
+		echo
+		# interactive:
+		until has_action "$TODO.new" && todo_check "$TODO.new"
+		do
+			has_action "$TODO.new" || echo 'TODO file empty.'
+			printf 'What to do with the file? [c]orrect/[e]dit again/[r]ewind/[s]ave/[?] '
+			read reply
+			case "$reply" in
+			[cC]*)
+				git_editor "$TODO.new"
+				;;
+			[eE]*)
+				prepare_editable_todo >"$TODO.new"
+				git_editor "$TODO.new"
+				;;
+			[rRxXqQ]*)
+				rm -f "$TODO.new"
+				exit 0
+				;;
+			[sS]*)
+				test "$WHY" != 'pause' ||
+					echo 'todo' >"$WHY_FILE"
+				break
+				;;
+			[?hH]*)
+				cat <<EOF
+
+Help:
+s - save TODO file and exit
+c - respawn editor to correct TODO file
+e - drop changes and respawn editor on original TODO file
+r - drop changes and exit as if nothing happened
+? - print this help
+EOF
+				;;
+			esac
+			echo
+		done
+	else
+		# defaults:
+		has_action "$TODO.new" || quit "Nothing to do"
+		todo_check || die 'TODO file contains errors. Aborting.'
+	fi
+	cat "$TODO" >"$TODO.old"
+	if grep "^$markline" "$TODO.new" >/dev/null
+	then
+		sed -e "1,/^$markline/d" <"$TODO.new" >"$TODO"
+	else
+		cat "$TODO.new" >"$TODO"
+	fi
+	rm -f "$TODO.new"
+	echo
+	echo 'TODO file contains:'
+	echo
+	cat "$TODO"
+	exit 0
+}
+
+# Realize --status.
+do_status () {
+	test -d "$SEQ_DIR" || die 'No sequencer running.'
+	get_saved_options
+
+	if has_action "$DONE"
+	then
+		echo 'Already done:'
+		sed -e 's/^/  /' <"$DONE"
+		echo
+	fi
+	case "$WHY" in
+	pause)
+		echo 'Intentionally paused.'
+		;;
+	conflict)
+		echo 'Interrupted by conflict at'
+		sed -n -e 's/^/  /;$p' <"$DONE"
+		;;
+	todo)
+		echo 'Interrupted because of errors in the TODO file.'
+		;;
+	*)
+		echo 'Current state is broken.'
+	esac
+	test "$VERBOSE" -gt 1 && echo 'Running verbosely.'
+	test "$VERBOSE" -lt 1 && echo 'Running quietly.'
+	test -n "$ONTO" &&
+		echo "Sequencing on branch ${ONTO##*/}."
+	if has_action "$TODO"
+	then
+		echo
+
+		echo 'Still to do:'
+		sed -e 's/^/  /' <"$TODO"
+	fi
+	if test "$WHY" = todo
+	then
+		echo
+		echo 'But there are errors. To edit, run:'
+		echo '    git sequencer --edit'
+	else
+		echo
+		echo 'To abort & restore, invoke:'
+		echo "    $(print_caller --abort)"
+		echo 'To continue, invoke:'
+		echo "    $(print_caller --continue)"
+		test "$WHY" = 'pause' || {
+			echo 'To skip the current instruction, invoke:'
+			echo "    $(print_caller --skip)"
+		}
+	fi
+	exit 0
+}
+
+is_standalone () {
+	test $# -eq 2 &&
+	test "$2" = '--' &&
+	test -z "$BATCHMODE" &&
+	test -z "$onto" &&
+	test "$VERBOSE" -eq 1
+}
+
+
+### Option handling and startup
+
+onto=
+BATCHMODE=
+CALLERSTRING=
+VERBOSE=1
+CALLER=
+CALLER_ABRT=
+CALLER_CONT=
+CALLER_SKIP=
+while test $# -gt 0
+do
+	case "$1" in
+	--continue)
+		is_standalone "$@" || usage
+		assure_caller
+		do_continue
+		;;
+	--abort)
+		is_standalone "$@" || usage
+		assure_caller
+		do_abort
+		;;
+	--skip)
+		is_standalone "$@" || usage
+		assure_caller
+		do_skip
+		;;
+	--status)
+		is_standalone "$@" || usage
+		assure_caller
+		do_status
+		;;
+	--edit)
+		is_standalone "$@" || usage
+		do_edit
+		;;
+	--caller)
+		###############################################################
+		# This feature is for user scripts only. (Hence undocumented.)
+		# User scripts should pass an argument like:
+		# --caller="git foo|abrt|go|next"
+		# on every git sequencer call. (It is only ignored on
+		# --edit and --status.)
+		# So git sequencer knows that
+		# "git foo abrt" will abort,
+		# "git foo go" will continue and
+		# "git foo next" will skip the sequencing process.
+		# This is useful if your user script does some extra
+		# preparations or cleanup before/after calling
+		#   git sequencer --caller="..." --abort|--continue|--skip
+		#
+		# Running git-sequencer without the same --caller string
+		# fails then, until the sequencing process has finished or
+		# aborted.
+		#
+		# Btw, --caller="my_tiny_script.sh|-a||" will be
+		# interpreted, that users must invoke "my_tiny_script.sh -a"
+		# to abort, but can invoke "git sequencer --continue" and
+		# "git sequencer --skip" to continue or skip.
+		# And it is also possible to provide three different scripts
+		# by --caller="|script 1|tool 2|util 3".
+		#
+		# If your user script does not need any special
+		# abort/continue/skip behavior, then just do NOT pass
+		# the --caller option.
+		###############################################################
+		shift
+		expr "x$1" : 'x.*|.*|.*|.*$' >/dev/null ||
+			die 'Wrong --caller format.'
+		CALLERSTRING="$1"
+		;;
+	-B)
+		BATCHMODE=t
+		# XXX: we still have abort on editor invokations
+		;;
+	--onto)
+		shift
+		ONTO="$1"
+		test $(git cat-file -t "$ONTO") = 'commit' ||
+			die "$ONTO is no commit or branch."
+		;;
+	-q)
+		VERBOSE=0
+		# XXX: If there were no editors,
+		# we could just do exec >/dev/null
+		;;
+	-v)
+		VERBOSE=2
+		# or increment it if we have several verbosity steps
+		;;
+	--)
+		shift
+		break
+		;;
+	*)
+		die "$1 currently not implemented."
+		;;
+	esac
+	shift
+done
+test $# -gt 1 && usage
+
+do_startup "$1"
-- 
1.5.6.1.130.ga8860.dirty

^ permalink raw reply related	[flat|nested] 52+ messages in thread

* [RFC/PATCH 2/4] Add git-sequencer prototype documentation
  2008-07-01  2:38 ` [RFC/PATCH 1/4] Add git-sequencer shell prototype Stephan Beyer
@ 2008-07-01  2:38   ` Stephan Beyer
  2008-07-01  2:38     ` [RFC/PATCH 3/4] Add git-sequencer test suite (t3350) Stephan Beyer
                       ` (2 more replies)
  2008-07-03  1:45   ` [RFC/PATCH 1/4] Add git-sequencer shell prototype Junio C Hamano
                     ` (2 subsequent siblings)
  3 siblings, 3 replies; 52+ messages in thread
From: Stephan Beyer @ 2008-07-01  2:38 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Johannes Schindelin, Stephan Beyer

Mentored-by: Christian Couder <chriscool@tuxfamily.org>
Mentored-by: Daniel Barkalow <barkalow@iabervon.org>
Signed-off-by: Stephan Beyer <s-beyer@gmx.net>
---
 Documentation/git-sequencer.txt |  348 +++++++++++++++++++++++++++++++++++++++
 1 files changed, 348 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/git-sequencer.txt

diff --git a/Documentation/git-sequencer.txt b/Documentation/git-sequencer.txt
new file mode 100644
index 0000000..e0c6410
--- /dev/null
+++ b/Documentation/git-sequencer.txt
@@ -0,0 +1,348 @@
+git-sequencer(1)
+================
+
+NAME
+----
+git-sequencer - Execute a sequence of git instructions
+
+SYNOPSIS
+--------
+[verse]
+'git-sequencer' [--batch] [--onto=<base>] [--verbose|--quiet] [<file>]
+'git-sequencer' --continue | --skip | --abort | --edit | --status
+
+
+DESCRIPTION
+-----------
+Executes a sequence of git instructions to HEAD or `<base>`.
+The sequence is given by `<file>` or standard input.
+Also see 'TODO FILE FORMAT' below.
+
+Before doing anything, the TODO file is checked for correct syntax
+and sanity.
+
+In case of a conflict or request in the TODO file, git-sequencer will
+pause. On conflict you can use git-diff to locate the markers (`<<<<<<<`)
+and make edits to resolve the conflict.
+
+For each file you edit, you need to tell git the changes by doing
+
+    git add <file>
+
+After resolving the conflict manually and updating the index with the
+desired resolution, you can continue the sequencing process with
+
+    git sequencer --continue
+
+Alternatively, you can undo the git-sequencer progress with
+
+    git sequencer --abort
+
+or skip the current instruction with
+
+    git sequencer --skip
+
+or correct the TODO file with
+
+    git sequencer --edit
+
+During pauses or when finished with the sequencing task, the current
+HEAD will always be the result of the last processed instruction.
+
+
+OPTIONS
+-------
+<file>::
+	Filename of the TODO file.  If omitted, standard input is used.
+	See 'TODO FILE FORMAT' below.
+
+-B::
+--batch::
+	Run in batch mode. If unexpected user intervention is needed
+	(e.g. a conflict or the need to run an editor), git-sequencer fails.
++
+Note that the sanity check fails, if you use this option
+and an instruction like `edit` or `pause`.
+
+--onto=<base>::
+	Checkout given commit or branch before sequencing.
+	If you provide a branch, sequencer will make the provided
+	changes on the branch, i.e. the branch will be changed.
+
+--continue::
+	Restart the sequencing process after having resolved a merge conflict.
+
+--abort::
+	Restore the original branch and abort the sequence operation.
+
+--skip::
+	Restart the sequencing process by skipping the current instruction.
+
+--status::
+	Show the current status of git-sequencer and what
+	operations can be done to change that status.
+
+--edit::
+	Invoke editor to edit the undone rest of the TODO file.
++
+The file is syntax- and sanity-checked afterwards, so that you can
+safely run `git sequencer --skip` or `--continue` after editing.
+If you nonetheless noticed that you made a mistake, you can
+overwrite `.git/sequencer/todo` with `.git/sequencer/todo.old` and
+rerun `git sequencer --edit`.
++
+If the check fails you are prompted if you want to correct your
+changes, edit again, cancel editing or really want to save.
+
+-q::
+--quiet::
+	Suppress output.
+	(Not yet implemented.)
+
+-v::
+--verbose::
+	Be more verbose.
+
+
+NOTES
+-----
+
+When sequencing, it is possible, that you are changing the history of
+a branch in a way that can cause problems for anyone who already has
+a copy of the branch in their repository and tries to pull updates from
+you.  You should understand the implications of using git-sequencer on
+a repository that you share.
+
+git-sequencer will usually be called by another git porcelain, like
+linkgit:git-am[1] or linkgit:git-rebase[1].
+
+
+TODO FILE FORMAT
+----------------
+
+The TODO file contains basically one instruction per line.
+
+Blank lines will be ignored.
+All characters after a `#` character will be ignored until the end of a line.
+
+The following instructions can be used:
+
+
+edit <commit>::
+	Picks a commit and pauses the sequencer process to let you
+	make changes.
++
+This is a short form for `pick <commit> and `pause` on separate lines.
+
+
+mark <mark>::
+	Set a symbolic mark for the last commit.
+	`<mark>` is an unsigned integer starting at 1 and
+	prefixed with a colon, e.g. `:1`.
++
+The marks can help if you want to refer to commits that you
+created during the sequencer process, e.g. if you want to
+merge such a commit.
++
+The set marks are removed after the sequencer has completed.
+
+
+merge [options] <commit-ish1> <commit-ish2> ... <commit-ishN>::
+	Merge commits into HEAD.
++
+A commit can also be given by a mark, if prefixed with a colon.
++
+If you do not provide a commit message (using `-F`, `-m`, `-C`, `-M`,
+or `--standard`), an editor will be invoked.
++
+See the following list and 'GENERAL OPTIONS' for values of `options`:
+
+	--standard;;
+		Generates a commit message like 'Merge ... into HEAD'.
+		See also linkgit:git-fmt-merge-msg[1].
+
+	-s <strategy>;;
+	--strategy=<strategy>;;
+		Use the given merge strategy.
+		See also linkgit:git-merge[1].
+
+
+pick [options] <commit>::
+	Pick (see linkgit:git-cherry-pick[1]) a commit.
+	Sequencer will pause on conflicts.
++
+See the following list and 'GENERAL OPTIONS' for values of `options`:
+
+	-R;;
+	--reverse;;
+		Revert the changes introduced by pick <commit>.
+
+	--mainline=<n>;;
+		Allows you to pick merge commits by specifying the
+		parent number (beginning from 1) to let sequencer
+		replay the changes relative to the specified parent.
+		+
+This option does not work together with `-R`.
+
+
+patch [options] <file>::
+	If file `<file>` is a pure (diff) patch, then apply the patch.
+	If no `--message` option is given, an editor will
+	be invoked to enter a commit message.
++
+If `<file>` is a linkgit:git-format-patch[1]-formatted patch,
+then the patch will be commited.
++
+See the following list and 'GENERAL OPTIONS' for values of `options`:
+
+	-3;;
+	--3way;;
+		When the patch does not apply cleanly, fall back on
+		3-way merge, if the patch records the identity of blobs
+		it is supposed to apply to, and we have those blobs
+		available locally.
+
+	-k;;
+		Pass `-k` flag to `git-mailinfo` (see linkgit:git-mailinfo[1]).
+
+	-n;;
+		Pass `-n` flag to `git-mailinfo` (see linkgit:git-mailinfo[1]).
+
+	-*;;
+		Any other dash-prefixed option is passed to
+		linkgit:git-apply[1].
+		This is especially useful for flags like
+		`--reverse`, `-C<n>`, `-p<n>` or `--whitespace=<action>`.
+
+
+pause::
+	Pauses the sequencer process to let you manually make changes.
+	For example, you can re-edit the done commit, fix bugs or typos,
+	or you can make further commits on top of HEAD before continuing.
++
+After you have finished your changes and added them to the index,
+invoke `git-sequencer --continue`.
+If you only want to edit the last commit message with an editor,
+run `git commit --amend` (see linkgit:git-commit[1]) before saying
+`--continue`.
+
+
+ref <ref>::
+	Set ref `<ref>` to the current HEAD, see also
+	linkgit:git-update-ref[1].
+
+
+reset <commit-ish>::
+	Go back (see linkgit:git-reset[1] `--hard`) to commit `<commit-ish>`.
+	`<commit-ish>` can also be given by a mark, if prefixed with a colon.
+
+
+squash [options] <commit>::
+	Add the changes introduced by `<commit>` to the last commit.
++
+See 'GENERAL OPTIONS' for values of `options`.
+
+squash [options] --from <mark>::
+	Squash all commits from the given mark into one commit.
+	There must not be any `merge` instructions between the
+	`mark` instruction and this `squash --from` instruction.
++
+See the following list and 'GENERAL OPTIONS' for values of `options`:
+
+	--collect-signoffs;;
+		Collect the Signed-off-by: lines of each commit and
+		add them to the squashed commit message.
+		(Not yet implemented.)
+
+	--include-merges;;
+		Sanity check does not fail if you have merges
+		between HEAD and <mark>.
+
+
+GENERAL OPTIONS
+---------------
+
+Besides some special options, the instructions
+`patch`, `merge`, `pick`, `squash` take the following general options:
+
+--author=<author>::
+	Override the author name and e-mail address used in the commit.
+	Use `A U Thor <author@example.com>` format.
+
+-C <commit-ish>::
+--reuse-commit=<commit-ish>::
+	Reuse message and authorship data from specified commit.
+
+-M <commit-ish>
+--reuse-message=<commit-ish>::
+	Reuse message from specified commit.
+	Note, that only the commit message is reused
+	and not the authorship information.
+
+-F <file>::
+--file=<file>::
+	Take the commit message from the given file.
+
+-m <msg>::
+--message=<msg>::
+	Use the given `<msg>` as the commit message.
+
+--signoff::
+	Add `Signed-off-by:` line to the commit message (if not yet there),
+	using the committer identity of yourself.
+
+-e::
+--edit::
+	Regardless what commit message options are given,
+	invoke the editor to allow editing of the commit message.
+
+
+RETURN VALUES
+-------------
+
+git-sequencer returns:
+
+* `0`, if git-sequencer successfully completed all the instructions
+       in the TODO file or successfully aborted after
+       `git sequencer --abort`,
+* `2`, on user-requested pausing, e.g.
+       when using the `edit` instruction.
+* `3`, on pauses that are not requested, e.g.
+       when there are conflicts to resolve
+       or errors in the TODO file.
+* any other value on error, e.g.
+  running git-sequencer on a bare repository.
+
+
+EXAMPLES
+--------
+
+TODO [Here the usage of all commands should become clear.]
+
+
+SEE ALSO
+--------
+
+linkgit:git-add[1],
+linkgit:git-am[1],
+linkgit:git-cherry-pick[1],
+linkgit:git-commit[1],
+linkgit:git-fmt-merge-msg[1],
+linkgit:git-format-patch[1],
+linkgit:git-rebase[1],
+linkgit:git-reset[1],
+linkgit:git-update-ref[1]
+
+
+Authors
+-------
+Written by Stephan Beyer <s-beyer@gmx.net>.
+
+
+Documentation
+-------------
+Documentation by Stephan Beyer and the git-list <git@vger.kernel.org>.
+
+GIT
+---
+Part of the linkgit:git[1] suite
-- 
1.5.6.334.gdaf0

^ permalink raw reply related	[flat|nested] 52+ messages in thread

* [RFC/PATCH 3/4] Add git-sequencer test suite (t3350)
  2008-07-01  2:38   ` [RFC/PATCH 2/4] Add git-sequencer prototype documentation Stephan Beyer
@ 2008-07-01  2:38     ` Stephan Beyer
  2008-07-01  2:38       ` [RFC/PATCH 4/4] Migrate git-am to use git-sequencer Stephan Beyer
  2008-07-05 17:31       ` [RFC/PATCH 3/4] Add git-sequencer test suite (t3350) Stephan Beyer
  2008-07-01 13:02     ` [RFC/PATCH 2/4] Add git-sequencer prototype documentation Jakub Narebski
  2008-07-05 17:00     ` [PATCH v2 " Stephan Beyer
  2 siblings, 2 replies; 52+ messages in thread
From: Stephan Beyer @ 2008-07-01  2:38 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Johannes Schindelin, Stephan Beyer

Mentored-by: Christian Couder <chriscool@tuxfamily.org>
Mentored-by: Daniel Barkalow <barkalow@iabervon.org>
Signed-off-by: Stephan Beyer <s-beyer@gmx.net>
---
Note that the --quiet tests are test_expect_failure, because I
have not really cared about the output behavior of sequencer,
and --quiet cannot be a trivial exec >/dev/null, because sometimes
editors are invoked, etc.

Also note that the fake-editor is quite different from the one
in the rebase tests.  According to a "session", the file is
totally rewritten. It's not possible to do something like
FAKE_LINES="3 1 2" so that the third line is the first one, etc.

 t/t3350-sequencer.sh |  810 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 810 insertions(+), 0 deletions(-)
 create mode 100755 t/t3350-sequencer.sh

diff --git a/t/t3350-sequencer.sh b/t/t3350-sequencer.sh
new file mode 100755
index 0000000..33522eb
--- /dev/null
+++ b/t/t3350-sequencer.sh
@@ -0,0 +1,810 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Stephan Beyer
+#
+# `setup' is based on t3404* by Johannes Schindelin.
+
+test_description='git sequencer
+
+These are basic usage tests for git sequencer.
+'
+. ./test-lib.sh
+
+# set up two branches like this:
+#
+# A - B - C - D - E
+#   \
+#     F - G - H
+#       \
+#         I
+#
+# where B, D and G touch increment value in file1.
+# The others generate empty file[23456].
+
+SEQDIR=".git/sequencer"
+SEQMARK="refs/sequencer-marks"
+MARKDIR=".git/$SEQMARK"
+
+test_expect_success 'setup' '
+	: >file1 &&
+	git add file1 &&
+	test_tick &&
+	git commit -m "generate empty file1" &&
+	git tag A &&
+	echo 1 >file1 &&
+	test_tick &&
+	git commit -m "write 1 into file1" file1 &&
+	git tag B &&
+	: >file2 &&
+	git add file2 &&
+	test_tick &&
+	git commit -m "generate empty file2" &&
+	git tag C &&
+	echo 2 >file1 &&
+	test_tick &&
+	git commit -m "write 2 into file1" file1 &&
+	git tag D &&
+	: >file3 &&
+	git add file3 &&
+	test_tick &&
+	git commit -m "generate empty file3" &&
+	git tag E &&
+	git checkout -b branch1 A &&
+	: >file4 &&
+	git add file4 &&
+	test_tick &&
+	git commit -m "generate empty file4" &&
+	git tag F &&
+	echo 3 >file1 &&
+	test_tick &&
+	git commit -m "write 3 into file1" file1 &&
+	git tag G &&
+	: >file5 &&
+	git add file5 &&
+	test_tick &&
+	git commit -m "generate empty file5" &&
+	git tag H &&
+	git checkout -b branch2 F &&
+	: >file6 &&
+	git add file6 &&
+	test_tick &&
+	git commit -m "generate empty file6" &&
+	git tag I &&
+	git diff -p --raw C..D >patchD.raw &&
+	git diff -p --raw A..F >patchF.raw &&
+	git format-patch --stdout A..B >patchB &&
+	git format-patch --stdout B..C >patchC &&
+	git format-patch --stdout C..D >patchD &&
+	git format-patch --stdout A..F >patchF &&
+	git format-patch --stdout F..G >patchG
+'
+
+orig_author="$GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>"
+
+# Functions to verify exit status of sequencer.
+# Do not just use "test_must_fail git sequencer ..."!
+expect_fail () {
+	"$@"
+	test $? -eq 1
+}
+expect_continue () {
+	"$@"
+	test $? -eq 2
+}
+expect_conflict () {
+	"$@"
+	test $? -eq 3
+}
+
+
+# Other test helpers:
+
+# Test if commit $1 has author $2
+expect_author () {
+	test "$2" = "$(git cat-file commit "$1" |
+		sed -n -e "s/^author \(.*\)> .*$/\1>/p")"
+}
+
+# Test if commit $1 has commit message in file $2
+# Side effect: overwrites actual
+expect_msg () {
+	git cat-file commit "$1" | sed -e "1,/^$/d" >actual &&
+	test_cmp "$2" actual
+}
+
+# Test that no marks are set.
+no_marks_set () {
+	if test -e "$MARKDIR"
+	then
+		rmdir "$MARKDIR"
+	fi
+}
+
+test_expect_success 'fail on empty TODO from stdin' '
+	expect_fail git sequencer <file6 &&
+	! test -d "$SEQDIR"
+'
+
+# Generate fake editor
+#
+# Simple and practical concept:
+#  We use only a small string identifier for "editor sessions".
+#  Each sessions knows what to do and perhaps defines
+#  which session to choose next.
+echo "#!$SHELL_PATH" >fake-editor.sh
+cat >>fake-editor.sh <<\EOF
+test -f fake-editor-session || exit 1
+#test -t 1 || exit 1
+# This test could be useful, but as the test-lib is not always
+# verbose, this will fail.
+next=ok
+this=$(cat fake-editor-session)
+case "$this" in
+commitmsg)
+	echo 'echo 2 >file1'
+	;;
+squashCE)
+	echo 'generate file2 and file3'
+	;;
+squashCI)
+	echo 'generate file2 and file6'
+	next=squashDCE
+	;;
+squashDCE)
+	echo 'generate file2 and file3 and write 2 into file1'
+	next=merge1
+	;;
+merge1)
+	echo 'A typed merge message.'
+	;;
+merge2)
+	test "$(sed -n -e 1p "$1")" = 'test merge' &&
+		echo 'cleanup merge' ||
+		echo error
+	sed -e 1d "$1"
+	;;
+editXXXXXXXXX)
+	printf 'last edited'
+	;;
+edit*)
+	printf 'edited: '
+	cat "$1"
+	next="${this}X"
+	;;
+nochange)
+	cat "$1"
+	;;
+ok|fail)
+	echo '-- THIS IS UNEXPECTED --'
+	next=fail
+	;;
+*)
+	echo 'I do not know.'
+	;;
+esac >"$1".tmp
+mv "$1".tmp "$1"
+echo $next >fake-editor-session
+exit 0
+EOF
+chmod a+x fake-editor.sh
+test_set_editor "$(pwd)/fake-editor.sh"
+
+next_session () {
+	echo "$1" >fake-editor-session
+}
+
+# check if fake-editor-session is ok.
+# If "$1" is set to anything, it will set the
+# next session to "ok", which is nice for
+# test_expect_failure.
+session_ok () {
+	test "ok" = $(cat fake-editor-session)
+	ret=$?
+	test -n "$1" && next_session ok
+	return $ret
+}
+
+
+cat >todotest1 <<EOF
+pick C
+squash E
+ref refs/tags/CE
+EOF
+
+test_expect_success '"pick", "squash", "ref" from stdin' '
+	next_session squashCE &&
+	git sequencer <todotest1 &&
+	! test -d "$SEQDIR" &&
+	session_ok &&
+	test $(git rev-parse CE) = $(git rev-parse HEAD) &&
+	test $(git rev-parse I) = $(git rev-parse HEAD^)
+'
+
+cat >todotest2 <<EOF
+# This is a test
+
+reset I  # go back to I
+
+EOF
+
+test_expect_success '"reset" from file with comments and blank lines' '
+	git sequencer todotest2 &&
+	session_ok &&
+	test $(git rev-parse I) = $(git rev-parse HEAD)
+'
+
+cat >todotest1 <<EOF
+pick C
+EOF
+
+test_expect_success '--onto <branch> keeps branch' '
+	git checkout -b test-branch A &&
+	git checkout master &&
+	git sequencer --onto test-branch <todotest1 &&
+	session_ok &&
+	test "$(git symbolic-ref -q HEAD)" = "refs/heads/test-branch" &&
+	test "$(git rev-parse test-branch^)" = "$(git rev-parse A)"
+'
+
+test_expect_success '--onto commit (detached HEAD) works' '
+	git sequencer --onto A <todotest1 &&
+	session_ok &&
+	test_must_fail git symbolic-ref -q HEAD &&
+	test "$(git rev-parse HEAD)" = "$(git rev-parse test-branch)"
+'
+
+echo 'pick -R C' >>todotest1
+
+test_expect_success 'pick -R works' '
+	git checkout A &&
+	git sequencer todotest1 &&
+	session_ok &&
+	! test -f file2
+'
+
+echo thisdoesnotexist >>todotest1
+
+test_expect_success 'junk is conflict' '
+	git checkout A &&
+	expect_conflict git sequencer todotest1 &&
+	test -d "$SEQDIR" &&
+	git sequencer --abort &&
+	! test -d "$SEQDIR" &&
+	session_ok &&
+	test $(git rev-parse A) = $(git rev-parse HEAD)
+'
+
+GIT_AUTHOR_NAME="Another Thor"
+GIT_AUTHOR_EMAIL="a.thor@example.com"
+GIT_COMMITTER_NAME="Co M Miter"
+GIT_COMMITTER_EMAIL="c.miter@example.com"
+export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_COMMITTER_NAME GIT_COMMITTER_EMAIL
+yet_another="Max Min <mm@example.com>"
+
+cat >todotest1 <<EOF
+patch patchB      # write 1 into file1
+patch -k patchC   # generate file2
+patch patchD.raw  # write 2 into file1
+EOF
+
+echo 'write 1 into file1' >expected1
+echo '[PATCH] generate empty file2' >expected2
+echo 'echo 2 >file1' >expected3
+
+test_expect_success '"patch" insn works' '
+	git checkout A &&
+	next_session commitmsg &&
+	git sequencer todotest1 &&
+	! test -d "$SEQDIR" &&
+	session_ok &&
+	test "$(git rev-parse HEAD~3)" = "$(git rev-parse A)" &&
+	test "$(cat file1)" = "2" &&
+	test -z "$(cat file2)" &&
+	expect_author HEAD~2 "$orig_author" &&
+	expect_author HEAD~1 "$orig_author" &&
+	expect_author HEAD~0 "$GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>" &&
+	expect_msg HEAD~2 expected1 &&
+	expect_msg HEAD~1 expected2 &&
+	expect_msg HEAD~0 expected3
+'
+
+cat >todotest1 <<EOF
+pick B	# write 1 into file1
+pause
+pick C	# generate file2
+EOF
+
+echo 'generate empty file2' >expected1
+echo 'write 1 into file1' >expected2
+
+test_expect_success "pick ; pause insns and --continue works" '
+	git checkout A &&
+	expect_continue git sequencer todotest1 &&
+	session_ok &&
+	echo 5 >file1 &&
+	git add file1 &&
+	next_session nochange &&
+	git sequencer --continue &&
+	test "$(cat file1)" = 5 &&
+	test -z "$(cat file2)" &&
+	expect_msg HEAD expected1 &&
+	expect_msg HEAD^ expected2 &&
+	session_ok
+'
+
+cat >todotest1 <<EOF
+edit B	# write 1 into file1
+pick C	# generate file2
+EOF
+
+test_expect_success "edit insn and --continue works" '
+	git checkout A &&
+	expect_continue git sequencer todotest1 &&
+	session_ok &&
+	echo 5 >file1 &&
+	git add file1 &&
+	next_session nochange &&
+	git sequencer --continue &&
+	test "$(cat file1)" = 5 &&
+	test -z "$(cat file2)" &&
+	expect_msg HEAD expected1 &&
+	expect_msg HEAD^ expected2 &&
+	session_ok
+'
+
+cat >todotest1 <<EOF
+patch patchB	# write 1 into file1
+pick H		# generate file5
+mark :1
+patch patchC	# generate file2
+squash I	# generate file6
+patch patchD	# write 2 into file1
+ref refs/tags/CID
+mark :2
+reset :1	# reset to new H
+patch patchD	# write 2 into file1
+squash CE	# generate file2 and file3
+ref refs/tags/DCE
+merge :2	# merge :2 into HEAD
+patch patchF	# generate file4
+EOF
+
+test_expect_success 'all insns work without options' '
+	git checkout A &&
+	next_session squashCI &&
+	no_marks_set &&
+	git sequencer todotest1 &&
+	no_marks_set &&
+	test "$(cat file1)" = "2" &&
+	test -z "$(cat file2)" &&
+	test -z "$(cat file3)" &&
+	test -z "$(cat file4)" &&
+	test -z "$(cat file5)" &&
+	test -z "$(cat file6)" &&
+	echo "$(git rev-parse DCE)" >expected &&
+	echo "$(git rev-parse CID)" >>expected &&
+	git cat-file commit HEAD^ | sed -n -e "s/^parent //p" >actual &&
+	test_cmp expected actual &&
+	session_ok
+'
+
+cat >todotest1 <<EOF
+merge --standard DCE
+EOF
+
+echo "Merge DCE into HEAD" >expected1
+
+test_expect_success 'merge --standard works' '
+	git checkout CID &&
+	git sequencer todotest1 &&
+	expect_msg HEAD expected1 &&
+	session_ok
+'
+
+cat >todotest1 <<EOF
+merge --standard --message="foo" DCE
+EOF
+
+
+test_expect_success 'merge --standard --message="foo" is conflict' '
+	git checkout CID &&
+	expect_conflict git sequencer todotest1 &&
+	git sequencer --abort &&
+	session_ok
+'
+
+for command in 'pick ' 'patch patch' 'squash ' 'merge --standard '
+do
+	cat >todotest1 <<EOF
+patch patchB	# 1 into file1
+${command}G	# 3 into file1
+patch -3 patchF	# empty file4
+EOF
+
+	test_expect_success "conflict test: ${command%% *} and --abort" '
+		git checkout A &&
+		expect_conflict git sequencer todotest1 &&
+		session_ok &&
+		test -d "$SEQDIR" &&
+		git sequencer --abort &&
+		session_ok &&
+		test $(git rev-parse HEAD) = $(git rev-parse A)
+	'
+
+	test_expect_success "conflict test: ${command%% *} and --continue" '
+		git checkout A &&
+		expect_conflict git sequencer todotest1 &&
+		session_ok &&
+		test -d "$SEQDIR" &&
+		## XXX: It would be perfect if we could remove the if
+		{ if test "${command%% *}" != "patch"
+		then grep -q "^<<<<<<<" file1 ; fi } &&
+		echo 3 >file1 &&
+		git add file1 &&
+		next_session nochange &&
+		git sequencer --continue &&
+		session_ok &&
+		! test -d "$SEQDIR" &&
+		test "$(cat file1)" = "3" &&
+		test -f file4
+	'
+
+	test_expect_success "conflict test: ${command%% *} and --skip" '
+		git checkout A &&
+		expect_conflict git sequencer todotest1 &&
+		session_ok &&
+		test -d "$SEQDIR" &&
+		git sequencer --skip &&
+		session_ok &&
+		! test -d "$SEQDIR" &&
+		test "$(cat file1)" = "1" &&
+		test -f file4
+	'
+done
+
+echo 'file5-gen' >commitmsg
+
+cat >todotest1 <<EOF
+patch --signoff patchB
+pause
+pick --author="$yet_another" --file="commitmsg" --signoff H
+mark :1
+patch --message="file2-gen" patchC
+squash --signoff --author="$yet_another" I
+pause
+patch --message="echo 2 >file1" patchD
+mark :2
+reset :1
+patch --author="$yet_another" patchD
+squash --signoff --message="generate file[23]" CE
+merge --signoff --message="test merge" --author="$yet_another" :2
+pause
+ref refs/tags/a_merge
+patch --message="Generate file4 and write 23 into it" patchF.raw
+pause
+pick I
+EOF
+
+cat >expected1 <<EOF
+write 1 into file
+
+Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>
+EOF
+
+test_expect_success 'insns work with options and another author 1' '
+	git checkout A &&
+	no_marks_set &&
+
+	# patch --signoff patchB  # write 1 into file1
+	# pause
+	expect_continue git sequencer todotest1 &&
+	test "Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" = \
+		"$(git cat-file commit HEAD | grep "^Signed-off-by")" &&
+	expect_author HEAD "$orig_author" &&
+	test -d "$SEQDIR" &&
+	session_ok
+'
+
+cat >expected1 <<EOF
+file5-gen
+
+Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>
+EOF
+
+cat >expected2 <<EOF
+file2-gen
+
+generate empty file6
+
+Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>
+EOF
+
+test_expect_success 'insns work with options and another author 2' '
+	: >file7 &&
+	git add file7 &&
+	next_session nochange &&
+	git commit --amend &&
+	session_ok &&
+
+	next_session nochange &&
+	expect_continue git sequencer --continue &&
+	session_ok &&
+
+	# amended commit
+	test "Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" = \
+		"$(git cat-file commit HEAD^^ | grep "^Signed-off-by")" &&
+	expect_author HEAD^^ "$orig_author" &&
+	test -z "$(cat file7)" &&
+
+	# pick --author="$yet_another" --file="commitmsg" --signoff H
+	expect_author HEAD^ "$yet_another" &&
+	expect_msg HEAD^ expected1 &&
+	test -z "$(cat file5)" &&
+
+	# mark :1
+	test "$(git rev-parse "$SEQMARK/1")" = "$(git rev-parse HEAD^)" &&
+
+	# patch --message="file2-gen" patchC
+	# squash --signoff --author="$yet_another" I # generate file6
+	# pause
+	test -z "$(cat file2)" &&
+	test -z "$(cat file6)" &&
+	git ls-files | grep -q "^file2" &&
+	git ls-files | grep -q "^file6" &&
+	expect_msg HEAD expected2 &&
+	expect_author HEAD "$yet_another"
+'
+
+echo 'echo 2 >file1' >expected1
+
+cat >expected2 <<EOF
+generate file[23]
+
+Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>
+EOF
+
+cat >expected3 <<EOF
+test merge
+
+Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>
+EOF
+
+cat >expected4 <<EOF
+file1
+file2
+file3
+file5
+file6
+file7
+EOF
+
+test_expect_success 'insns work with options and another author 3' '
+	# do not change anything
+	expect_continue git sequencer --continue &&
+	session_ok &&
+
+	# patch --message="echo 2 >file1" patchD
+	# mark :2
+	commit="$(git rev-parse --verify "$SEQMARK/2")" &&
+	expect_author "$commit" "$orig_author" &&
+	expect_msg "$commit" expected1 &&
+
+	# reset :1
+	# patch --author="$yet_another" patchD # write 2 into file1
+	# squash --signoff --message="generate file[23]" CE
+	expect_author HEAD^ "$yet_another" &&
+	expect_msg HEAD^ expected2 &&
+
+	# merge --signoff --message="test merge" --author="$yet_another" :2
+	# pause
+	expect_author HEAD "$yet_another" &&
+	expect_msg HEAD expected3 &&
+	git ls-files >actual &&
+	test_cmp expected4 actual
+'
+
+cat >expected_merge <<EOF
+cleanup merge
+
+Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>
+EOF
+
+echo 'Generate file4 and write 23 into it' >expected2
+
+test_expect_success 'insns work with options and another author 4' '
+	git rm file5 file6 file7 &&
+	next_session merge2 &&
+	expect_continue git sequencer --continue &&
+	session_ok &&
+
+	# ref refs/tags/a_merge
+	expect_author a_merge "$yet_another" &&
+	expect_msg a_merge expected_merge &&
+
+	# patch --message="Generate file4 and write 23 into it" patchF.raw
+	# pause
+	git ls-files | grep -q "^file4" &&
+	echo 23 >file4 &&
+	git add file4 &&
+	next_session nochange &&
+	git sequencer --continue &&
+	session_ok &&
+	no_marks_set &&
+	test "$(cat file1)" = "2" &&
+	test "$(cat file4)" = "23" &&
+	expect_msg HEAD^ expected2 &&
+	expect_author HEAD^ "$GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>" &&
+
+	# pick I
+	test -z "$(cat file6)" &&
+	git ls-files | grep -q "^file6" &&
+	session_ok
+'
+
+# almost the same to test --quiet
+cat >todotest1 <<EOF
+patch patchB
+pick H
+mark :1
+patch patchC
+squash --message="a squash" I
+patch patchD
+mark :2
+reset :1
+patch patchD
+squash --message="another squash" CE
+merge --message="test merge" :2
+pause
+patch patchF
+EOF
+
+test_expect_failure '--quiet works' '
+	git checkout A &&
+	expect_continue git sequencer --quiet todotest1 >actual &&
+	session_ok &&
+	! test -s actual
+'
+
+test_expect_failure '--quiet works on continue' '
+	git sequencer --continue >>actual &&
+	session_ok &&
+	! test -s actual
+'
+
+echo 'merge --strategy=ours --reuse-commit=a_merge branch1 branch2 CE CID' >todotest1
+
+test_expect_success 'merge multiple branches and --reuse-commit works' '
+	git checkout -b merge-multiple master &&
+	git sequencer todotest1 &&
+	session_ok &&
+	expect_msg HEAD expected_merge &&
+	git rev-parse HEAD^ >expected &&
+	git rev-parse branch1 >>expected &&
+	git rev-parse branch2 >>expected &&
+	git rev-parse CE >>expected &&
+	git rev-parse CID >>expected &&
+	git cat-file commit HEAD | sed -n -e "s/^parent //p" >actual &&
+	test_cmp expected actual &&
+	! test -f file6
+'
+
+echo 'pick --mainline=5 merge-multiple' >todotest1
+
+test_expect_success 'pick --mainline works' '
+	git checkout -b mainline CID &&
+	git sequencer todotest1 &&
+	session_ok &&
+	expect_msg HEAD expected_merge &&
+	! test -f file6 &&
+	test -f file3 &&
+	test -f file2 &&
+	test "$(cat file1)" = 2
+'
+
+cat >todotest1 <<EOF
+pick C		# file2
+mark :1
+patch patchB	# write 1 into file1
+patch patchD	# write 2 into file1
+pick I		# file6
+squash --message="2 in file1 and file6 exists" --signoff --from :1
+EOF
+
+cat >expected1 <<EOF
+2 in file1 and file6 exists
+
+Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>
+EOF
+
+test_expect_success 'squash --from works' '
+	git checkout A &&
+	git sequencer <todotest1 &&
+	session_ok &&
+	test "$(git rev-parse A)" = "$(git rev-parse HEAD~2)" &&
+	test "$(cat file1)" = "2" &&
+	test -z "$(cat file6)" &&
+	expect_msg HEAD expected1
+'
+
+cat >todotest1 <<EOF
+patch patchB	# write 1 into file1
+pick H		# generate file5
+mark :1
+patch patchC	# generate file2
+squash --message="file5" I	# generate file6
+patch patchD	# write 2 into file1
+mark :2
+reset :1	# reset to new H
+patch patchD	# write 2 into file1
+squash --message="CE" CE	# generate file2 and file3
+merge --standard :2	# merge :2 into HEAD
+patch patchF	# generate file4
+EOF
+cp todotest1 todotest2
+cat todotest1 | sed -e 's/^\(patch\|pick\|squash\|merge\) /&--edit /' >todotest3
+echo 'squash --message="doesnt work either" --from :1' >>todotest1
+echo 'squash --include-merges --message="stupid" --from :1' >>todotest2
+
+test_expect_success 'squash --from conflicts with merge in between' '
+	git checkout A &&
+	expect_conflict git sequencer todotest1 &&
+	git sequencer --abort &&
+	session_ok &&
+	! test -d "$SEQDIR"
+'
+
+test_expect_success 'squash --include-merges --from succeeds with merge in between' '
+	git checkout A &&
+	git sequencer todotest2 &&
+	session_ok &&
+	test "$(git rev-parse HEAD~3)" = "$(git rev-parse A)"
+'
+
+test_expect_success 'patch|pick|squash|merge --edit works' '
+	git checkout A &&
+	next_session editX &&
+	git sequencer todotest3 &&
+	session_ok
+'
+
+cat >todotest1 <<EOF
+patch patchB
+pause
+EOF
+
+test_expect_success 'batch mode fails on pause insn' '
+	git checkout A &&
+	expect_fail git sequencer --batch todotest1 &&
+	session_ok &&
+	! test -d "$SEQDIR"
+'
+
+cat >todotest1 <<EOF
+patch patchB
+pick G
+EOF
+
+test_expect_success 'batch mode fails on conflict' '
+	git checkout A &&
+	expect_fail git sequencer --batch <todotest1 &&
+	session_ok &&
+	! test -d "$SEQDIR" &&
+	test -z $(cat file1)
+'
+
+cat >todotest1 <<EOF
+patch patchB
+pause
+EOF
+
+test_expect_success '--caller works' '
+	git checkout A &&
+	expect_continue git sequencer \
+		--caller="this works|abrt||skip" todotest1 &&
+	expect_fail git sequencer --abort &&
+	expect_fail git sequencer --skip &&
+	git sequencer --continue &&
+	session_ok
+'
+
+test_done
-- 
1.5.6.334.gdaf0

^ permalink raw reply related	[flat|nested] 52+ messages in thread

* [RFC/PATCH 4/4] Migrate git-am to use git-sequencer
  2008-07-01  2:38     ` [RFC/PATCH 3/4] Add git-sequencer test suite (t3350) Stephan Beyer
@ 2008-07-01  2:38       ` Stephan Beyer
  2008-07-01  2:39         ` git-rebase-i migration to sequencer Stephan Beyer
  2008-07-05 17:31       ` [RFC/PATCH 3/4] Add git-sequencer test suite (t3350) Stephan Beyer
  1 sibling, 1 reply; 52+ messages in thread
From: Stephan Beyer @ 2008-07-01  2:38 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Johannes Schindelin, Stephan Beyer

This patch also adds --abort to git-am, which is just a
trivial implication of using git-sequencer.

Signed-off-by: Stephan Beyer <s-beyer@gmx.net>
---
Hm, what to say here?

I have to admit that interactive mode is purely tested.
Well, I've used sequencer-based git-am for a while, but never really
used the interactive mode (except for first manual test cases).

 Documentation/git-am.txt |    5 +-
 git-am.sh                |  552 +++++++++++++---------------------------------
 git-rebase.sh            |    7 +-
 t/t3407-rebase-abort.sh  |   10 +-
 t/t4150-am.sh            |   21 ++-
 5 files changed, 178 insertions(+), 417 deletions(-)

diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index 46544a0..b714106 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -13,7 +13,7 @@ SYNOPSIS
          [--3way] [--interactive] [--binary]
          [--whitespace=<option>] [-C<n>] [-p<n>]
          <mbox>|<Maildir>...
-'git-am' [--skip | --resolved]
+'git-am' (--abort | --skip | --resolved)
 
 DESCRIPTION
 -----------
@@ -79,6 +79,9 @@ default.   You could use `--no-utf8` to override this.
 --interactive::
 	Run interactively.
 
+--abort::
+	Abort applying and rewind applied patches.
+
 --skip::
 	Skip the current patch.  This is only meaningful when
 	restarting an aborted patch.
diff --git a/git-am.sh b/git-am.sh
index 2c517ed..dd19dd7 100755
--- a/git-am.sh
+++ b/git-am.sh
@@ -6,9 +6,11 @@ SUBDIRECTORY_OK=Yes
 OPTIONS_KEEPDASHDASH=
 OPTIONS_SPEC="\
 git-am [options] <mbox>|<Maildir>...
+git-am [options] --abort
 git-am [options] --resolved
 git-am [options] --skip
 --
+abort           abort patching and reset done patches
 d,dotest=       (removed -- do not use)
 i,interactive   run interactively
 b,binary        pass --allow-binary-replacement to git-apply
@@ -30,104 +32,101 @@ set_reflog_action am
 require_work_tree
 cd_to_toplevel
 
-git var GIT_COMMITTER_IDENT >/dev/null || exit
+git var GIT_COMMITTER_IDENT >/dev/null ||
+	die "You need to set your committer info first"
 
-stop_here () {
-    echo "$1" >"$dotest/next"
-    exit 1
+cleanup () {
+	git gc --auto
+	rm -fr "$dotest"
 }
 
-stop_here_user_resolve () {
-    if [ -n "$resolvemsg" ]; then
-	    printf '%s\n' "$resolvemsg"
-	    stop_here $1
-    fi
-    cmdline=$(basename $0)
-    if test '' != "$interactive"
-    then
-        cmdline="$cmdline -i"
-    fi
-    if test '' != "$threeway"
-    then
-        cmdline="$cmdline -3"
-    fi
-    echo "When you have resolved this problem run \"$cmdline --resolved\"."
-    echo "If you would prefer to skip this patch, instead run \"$cmdline --skip\"."
-
-    stop_here $1
+die_abort () {
+	cleanup
+	die "$1"
 }
 
-go_next () {
-	rm -f "$dotest/$msgnum" "$dotest/msg" "$dotest/msg-clean" \
-		"$dotest/patch" "$dotest/info"
-	echo "$next" >"$dotest/next"
-	this=$next
-}
+be_interactive () {
+	msg="$GIT_DIR/sequencer/message"
+	patch="$GIT_DIR/sequencer/patch"
+	author_script="$GIT_DIR/sequencer/author-script"
+	# we rely on sequencer here
+
+	test -t 0 ||
+		die "cannot be interactive without stdin connected to a terminal."
+	action=$(cat "$dotest/interactive")
+	while test "$action" = again
+	do
+		echo "Commit Body is:"
+		echo "--------------------------"
+		cat "$msg"
+		echo "--------------------------"
+		printf "Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all "
 
-cannot_fallback () {
-	echo "$1"
-	echo "Cannot fall back to three-way merge."
-	exit 1
+		read reply
+		case "$reply" in
+		[yY]*)
+			run_sequencer --continue
+			;;
+		[nN]*)
+			git reset --hard HEAD^
+			run_sequencer --continue
+			;;
+		[eE]*)
+			git_editor "$msg"
+			git commit --amend --file="$msg" --no-verify >/dev/null
+			;;
+		[vV]*)
+			LESS=-S ${PAGER:-less} "$patch"
+			;;
+		[aA]*)
+			echo 'accept' >"$dotest/interactive"
+			run_sequencer --continue
+			;;
+		*)
+			:
+			;;
+		esac
+	done
+	test "$action" = accept &&
+		run_sequencer --continue
 }
 
-fall_back_3way () {
-    O_OBJECT=`cd "$GIT_OBJECT_DIRECTORY" && pwd`
-
-    rm -fr "$dotest"/patch-merge-*
-    mkdir "$dotest/patch-merge-tmp-dir"
-
-    # First see if the patch records the index info that we can use.
-    git apply --build-fake-ancestor "$dotest/patch-merge-tmp-index" \
-	"$dotest/patch" &&
-    GIT_INDEX_FILE="$dotest/patch-merge-tmp-index" \
-    git write-tree >"$dotest/patch-merge-base+" ||
-    cannot_fallback "Repository lacks necessary blobs to fall back on 3-way merge."
-
-    echo Using index info to reconstruct a base tree...
-    if GIT_INDEX_FILE="$dotest/patch-merge-tmp-index" \
-	git apply $binary --cached <"$dotest/patch"
-    then
-	mv "$dotest/patch-merge-base+" "$dotest/patch-merge-base"
-	mv "$dotest/patch-merge-tmp-index" "$dotest/patch-merge-index"
-    else
-        cannot_fallback "Did you hand edit your patch?
-It does not apply to blobs recorded in its index."
-    fi
-
-    test -f "$dotest/patch-merge-index" &&
-    his_tree=$(GIT_INDEX_FILE="$dotest/patch-merge-index" git write-tree) &&
-    orig_tree=$(cat "$dotest/patch-merge-base") &&
-    rm -fr "$dotest"/patch-merge-* || exit 1
-
-    echo Falling back to patching base and 3-way merge...
-
-    # This is not so wrong.  Depending on which base we picked,
-    # orig_tree may be wildly different from ours, but his_tree
-    # has the same set of wildly different changes in parts the
-    # patch did not touch, so recursive ends up canceling them,
-    # saying that we reverted all those changes.
 
-    eval GITHEAD_$his_tree='"$FIRSTLINE"'
-    export GITHEAD_$his_tree
-    git-merge-recursive $orig_tree -- HEAD $his_tree || {
-	    git rerere
-	    echo Failed to merge in the changes.
-	    exit 1
-    }
-    unset GITHEAD_$his_tree
+run_sequencer () {
+	git sequencer --caller='git am|--abort|--resolved|--skip' "$@"
+	case "$?" in
+	0)
+		cleanup
+		exit 0
+		;;
+	2)
+		test -n "$interactive" && be_interactive
+		echo 'git-sequencer needs continuation (by edit).'
+		exit 0
+		;;
+	3)
+		die 'git-sequencer needs continuation (by conflict).'
+		;;
+	*)
+		die_abort 'git-sequencer died.'
+		;;
+	esac
 }
 
 prec=4
 dotest=".dotest"
+todofile="$dotest/todo"
 sign= utf8=t keep= skip= interactive= resolved= binary= rebasing=
-resolvemsg= resume=
-git_apply_opt=
+resolvemsg=
+opts=
 
 while test $# != 0
 do
 	case "$1" in
 	-i|--interactive)
-		interactive=t ;;
+		interactive=-q ;;
+	--abort)
+		abort=t ;;
 	-b|--binary)
 		binary=t ;;
 	-3|--3way)
@@ -152,9 +151,9 @@ do
 	--resolvemsg)
 		shift; resolvemsg=$1 ;;
 	--whitespace)
-		git_apply_opt="$git_apply_opt $1=$2"; shift ;;
+		opts="$opts $1=$2"; shift ;;
 	-C|-p)
-		git_apply_opt="$git_apply_opt $1$2"; shift ;;
+		opts="$opts $1$2"; shift ;;
 	--)
 		shift; break ;;
 	*)
@@ -163,347 +162,92 @@ do
 	shift
 done
 
-# If the dotest directory exists, but we have finished applying all the
-# patches in them, clear it out.
-if test -d "$dotest" &&
-   last=$(cat "$dotest/last") &&
-   next=$(cat "$dotest/next") &&
-   test $# != 0 &&
-   test "$next" -gt "$last"
-then
-   rm -fr "$dotest"
-fi
-
 if test -d "$dotest"
 then
-	case "$#,$skip$resolved" in
-	0,*t*)
-		# Explicit resume command and we do not have file, so
-		# we are happy.
-		: ;;
-	0,)
-		# No file input but without resume parameters; catch
-		# user error to feed us a patch from standard input
-		# when there is already $dotest.  This is somewhat
-		# unreliable -- stdin could be /dev/null for example
-		# and the caller did not intend to feed us a patch but
-		# wanted to continue unattended.
-		tty -s
-		;;
-	*)
-		false
-		;;
-	esac ||
-	die "previous dotest directory $dotest still exists but mbox given."
-	resume=yes
-else
-	# Make sure we are not given --skip nor --resolved
-	test ",$skip,$resolved," = ,,, ||
-		die "Resolve operation not in progress, we are not resuming."
+	test "$#" != 0 &&
+		die "previous dotest directory $dotest still exists but mbox given."
+
+	test -f "$dotest/interactive" &&
+		interactive=-q action=$(cat "$dotest/interactive")
 
-	# Start afresh.
-	mkdir -p "$dotest" || exit
+	# No file input but without resume parameters; catch
+	# user error to feed us a patch from standard input
+	# when there is already $dotest.  This is somewhat
+	# unreliable -- stdin could be /dev/null for example
+	# and the caller did not intend to feed us a patch but
+	# wanted to continue unattended.
+	test -z "$abort$resolved$skip" && tty -s
 
-	if test -n "$prefix" && test $# != 0
-	then
-		first=t
-		for arg
-		do
-			test -n "$first" && {
-				set x
-				first=
-			}
-			case "$arg" in
-			/*)
-				set "$@" "$arg" ;;
-			*)
-				set "$@" "$prefix$arg" ;;
-			esac
-		done
-		shift
-	fi
-	git mailsplit -d"$prec" -o"$dotest" -b -- "$@" > "$dotest/last" ||  {
-		rm -fr "$dotest"
-		exit 1
-	}
+	test -n "$abort" && run_sequencer --abort
+	test -n "$resolved" && run_sequencer --continue
+	test -n "$skip" && run_sequencer --skip
 
-	# -b, -s, -u, -k and --whitespace flags are kept for the
-	# resuming session after a patch failure.
-	# -3 and -i can and must be given when resuming.
-	echo "$binary" >"$dotest/binary"
-	echo " $ws" >"$dotest/whitespace"
-	echo "$sign" >"$dotest/sign"
-	echo "$utf8" >"$dotest/utf8"
-	echo "$keep" >"$dotest/keep"
-	echo 1 >"$dotest/next"
-	if test -n "$rebasing"
-	then
-		: >"$dotest/rebasing"
-	else
-		: >"$dotest/applying"
-	fi
+	die "$dotest still exists. Use git am --abort/--skip/--resolved."
 fi
 
-case "$resolved" in
-'')
-	files=$(git diff-index --cached --name-only HEAD --) || exit
-	if [ "$files" ]; then
-	   echo "Dirty index: cannot apply patches (dirty: $files)" >&2
-	   exit 1
-	fi
-esac
+# Make sure we are not given --skip nor --resolved nor --abort
+test -z "$abort$resolved$skip" ||
+	die 'git-am is not in progress. You cannot use --abort/--skip/--resolved then.'
 
-if test "$(cat "$dotest/binary")" = t
-then
-	binary=--allow-binary-replacement
-fi
-if test "$(cat "$dotest/utf8")" = t
-then
-	utf8=-u
-else
-	utf8=-n
-fi
-if test "$(cat "$dotest/keep")" = t
-then
-	keep=-k
-fi
-ws=`cat "$dotest/whitespace"`
-if test "$(cat "$dotest/sign")" = t
-then
-	SIGNOFF=`git-var GIT_COMMITTER_IDENT | sed -e '
-			s/>.*/>/
-			s/^/Signed-off-by: /'
-		`
-else
-	SIGNOFF=
-fi
+# Start afresh.
+mkdir -p "$dotest" ||
+	die "Could not create $dotest directory."
 
-last=`cat "$dotest/last"`
-this=`cat "$dotest/next"`
-if test "$skip" = t
+if test -n "$prefix" && test $# != 0
 then
-	git rerere clear
-	this=`expr "$this" + 1`
-	resume=
+	first=t
+	for arg
+	do
+		test -n "$first" && {
+			set x
+			first=
+		}
+		case "$arg" in
+		/*)
+			set "$@" "$arg" ;;
+		*)
+			set "$@" "$prefix$arg" ;;
+		esac
+	done
+	shift
 fi
+last=$(git mailsplit -d"$prec" -o"$dotest" -b -- "$@") ||  {
+	cleanup
+	exit 1
+}
+this=1
 
-if test "$this" -gt "$last"
-then
-	echo Nothing to do.
-	rm -fr "$dotest"
-	exit
+files=$(git diff-index --cached --name-only HEAD --) || exit
+if [ "$files" ]; then
+	echo "Dirty index: cannot apply patches (dirty: $files)" >&2
+	exit 1
 fi
 
-while test "$this" -le "$last"
-do
-	msgnum=`printf "%0${prec}d" $this`
-	next=`expr "$this" + 1`
-	test -f "$dotest/$msgnum" || {
-		resume=
-		go_next
-		continue
-	}
-
-	# If we are not resuming, parse and extract the patch information
-	# into separate files:
-	#  - info records the authorship and title
-	#  - msg is the rest of commit log message
-	#  - patch is the patch body.
-	#
-	# When we are resuming, these files are either already prepared
-	# by the user, or the user can tell us to do so by --resolved flag.
-	case "$resume" in
-	'')
-		git mailinfo $keep $utf8 "$dotest/msg" "$dotest/patch" \
-			<"$dotest/$msgnum" >"$dotest/info" ||
-			stop_here $this
+test -n "$interactive" && echo 'again' >"$dotest/interactive"
 
-		# skip pine's internal folder data
-		grep '^Author: Mail System Internal Data$' \
-			<"$dotest"/info >/dev/null &&
-			go_next && continue
+# converting our options to git-sequencer file insn options
+test -n "$binary" && opts="$opts --binary"
+test -n "$utf8" || opts="$opts -n"
+test -n "$keep" && opts="$opts -k"
+test -n "$sign" && opts="$opts --signoff"
+test -n "$threeway" && opts="$opts -3"
 
-		test -s $dotest/patch || {
-			echo "Patch is empty.  Was it split wrong?"
-			stop_here $this
-		}
-		if test -f "$dotest/rebasing" &&
-			commit=$(sed -e 's/^From \([0-9a-f]*\) .*/\1/' \
-				-e q "$dotest/$msgnum") &&
-			test "$(git cat-file -t "$commit")" = commit
-		then
-			git cat-file commit "$commit" |
-			sed -e '1,/^$/d' >"$dotest/msg-clean"
-		else
-			SUBJECT="$(sed -n '/^Subject/ s/Subject: //p' "$dotest/info")"
-			case "$keep_subject" in -k)  SUBJECT="[PATCH] $SUBJECT" ;; esac
-
-			(printf '%s\n\n' "$SUBJECT"; cat "$dotest/msg") |
-				git stripspace > "$dotest/msg-clean"
-		fi
-		;;
-	esac
-
-	GIT_AUTHOR_NAME="$(sed -n '/^Author/ s/Author: //p' "$dotest/info")"
-	GIT_AUTHOR_EMAIL="$(sed -n '/^Email/ s/Email: //p' "$dotest/info")"
-	GIT_AUTHOR_DATE="$(sed -n '/^Date/ s/Date: //p' "$dotest/info")"
-
-	if test -z "$GIT_AUTHOR_EMAIL"
-	then
-		echo "Patch does not have a valid e-mail address."
-		stop_here $this
-	fi
-
-	export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE
-
-	case "$resume" in
-	'')
-	    if test '' != "$SIGNOFF"
-	    then
-		LAST_SIGNED_OFF_BY=`
-		    sed -ne '/^Signed-off-by: /p' \
-		    "$dotest/msg-clean" |
-		    sed -ne '$p'
-		`
-		ADD_SIGNOFF=`
-		    test "$LAST_SIGNED_OFF_BY" = "$SIGNOFF" || {
-		    test '' = "$LAST_SIGNED_OFF_BY" && echo
-		    echo "$SIGNOFF"
-		}`
-	    else
-		ADD_SIGNOFF=
-	    fi
-	    {
-		if test -s "$dotest/msg-clean"
-		then
-			cat "$dotest/msg-clean"
-		fi
-		if test '' != "$ADD_SIGNOFF"
-		then
-			echo "$ADD_SIGNOFF"
-		fi
-	    } >"$dotest/final-commit"
-	    ;;
-	*)
-		case "$resolved$interactive" in
-		tt)
-			# This is used only for interactive view option.
-			git diff-index -p --cached HEAD -- >"$dotest/patch"
-			;;
-		esac
-	esac
-
-	resume=
-	if test "$interactive" = t
-	then
-	    test -t 0 ||
-	    die "cannot be interactive without stdin connected to a terminal."
-	    action=again
-	    while test "$action" = again
-	    do
-		echo "Commit Body is:"
-		echo "--------------------------"
-		cat "$dotest/final-commit"
-		echo "--------------------------"
-		printf "Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all "
-		read reply
-		case "$reply" in
-		[yY]*) action=yes ;;
-		[aA]*) action=yes interactive= ;;
-		[nN]*) action=skip ;;
-		[eE]*) git_editor "$dotest/final-commit"
-		       action=again ;;
-		[vV]*) action=again
-		       LESS=-S ${PAGER:-less} "$dotest/patch" ;;
-		*)     action=again ;;
-		esac
-	    done
-	else
-	    action=yes
-	fi
-	FIRSTLINE=$(sed 1q "$dotest/final-commit")
+# create todofile
+: > "$todofile" ||
+	die_abort "Cannot create $todofile"
+while test "$this" -le "$last"
+do
+	msgnum=$(printf "%0${prec}d" $this)
+	this=$(($this+1))
 
-	if test $action = skip
-	then
-		go_next
+	# This ignores every mail that does not contain a patch.
+	grep '^diff' "$dotest/$msgnum" >/dev/null ||
 		continue
-	fi
-
-	if test -x "$GIT_DIR"/hooks/applypatch-msg
-	then
-		"$GIT_DIR"/hooks/applypatch-msg "$dotest/final-commit" ||
-		stop_here $this
-	fi
 
-	printf 'Applying %s\n' "$FIRSTLINE"
-
-	case "$resolved" in
-	'')
-		git apply $git_apply_opt $binary --index "$dotest/patch"
-		apply_status=$?
-		;;
-	t)
-		# Resolved means the user did all the hard work, and
-		# we do not have to do any patch application.  Just
-		# trust what the user has in the index file and the
-		# working tree.
-		resolved=
-		git diff-index --quiet --cached HEAD -- && {
-			echo "No changes - did you forget to use 'git add'?"
-			stop_here_user_resolve $this
-		}
-		unmerged=$(git ls-files -u)
-		if test -n "$unmerged"
-		then
-			echo "You still have unmerged paths in your index"
-			echo "did you forget to use 'git add'?"
-			stop_here_user_resolve $this
-		fi
-		apply_status=0
-		git rerere
-		;;
-	esac
-
-	if test $apply_status = 1 && test "$threeway" = t
-	then
-		if (fall_back_3way)
-		then
-		    # Applying the patch to an earlier tree and merging the
-		    # result may have produced the same tree as ours.
-		    git diff-index --quiet --cached HEAD -- && {
-			echo No changes -- Patch already applied.
-			go_next
-			continue
-		    }
-		    # clear apply_status -- we have successfully merged.
-		    apply_status=0
-		fi
-	fi
-	if test $apply_status != 0
-	then
-		echo Patch failed at $msgnum.
-		stop_here_user_resolve $this
-	fi
-
-	if test -x "$GIT_DIR"/hooks/pre-applypatch
-	then
-		"$GIT_DIR"/hooks/pre-applypatch || stop_here $this
-	fi
-
-	tree=$(git write-tree) &&
-	parent=$(git rev-parse --verify HEAD) &&
-	commit=$(git commit-tree $tree -p $parent <"$dotest/final-commit") &&
-	git update-ref -m "$GIT_REFLOG_ACTION: $FIRSTLINE" HEAD $commit $parent ||
-	stop_here $this
-
-	if test -x "$GIT_DIR"/hooks/post-applypatch
-	then
-		"$GIT_DIR"/hooks/post-applypatch
-	fi
-
-	go_next
+	printf 'patch%s "%s" #%s\n' \
+		"$opts" "$dotest/$msgnum" \
+		"$(sed -n '1,/^Subject:/s/Subject://p' "$dotest/$msgnum")" >> "$todofile"
+	test -n "$interactive" && echo 'pause' >>"$todofile"
 done
 
-git gc --auto
-
-rm -fr "$dotest"
+run_sequencer $interactive "$todofile"
diff --git a/git-rebase.sh b/git-rebase.sh
index e2d85ee..b0d4c7d 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -216,12 +216,11 @@ do
 		if test -d "$dotest"
 		then
 			move_to_original_branch
+			git reset --hard $(cat "$dotest/orig-head")
+			rm -r "$dotest"
 		else
-			dotest=.dotest
-			move_to_original_branch
+			git am --abort
 		fi
-		git reset --hard $(cat "$dotest/orig-head")
-		rm -r "$dotest"
 		exit
 		;;
 	--onto)
diff --git a/t/t3407-rebase-abort.sh b/t/t3407-rebase-abort.sh
index 1777ffe..f6bbd51 100755
--- a/t/t3407-rebase-abort.sh
+++ b/t/t3407-rebase-abort.sh
@@ -39,9 +39,11 @@ testrebase() {
 		git reset --hard pre-rebase &&
 		test_must_fail git rebase$type master &&
 		test -d "$dotest" &&
+		#test -d ".git/sequencer" &&
 		git rebase --abort &&
 		test $(git rev-parse to-rebase) = $(git rev-parse pre-rebase) &&
-		test ! -d "$dotest"
+		test ! -d "$dotest" &&
+		test ! -d ".git/sequencer"
 	'
 
 	test_expect_success "rebase$type --abort after --skip" '
@@ -54,7 +56,8 @@ testrebase() {
 		test $(git rev-parse HEAD) = $(git rev-parse master) &&
 		git-rebase --abort &&
 		test $(git rev-parse to-rebase) = $(git rev-parse pre-rebase) &&
-		test ! -d "$dotest"
+		test ! -d "$dotest" &&
+		test ! -d ".git/sequencer"
 	'
 
 	test_expect_success "rebase$type --abort after --continue" '
@@ -70,7 +73,8 @@ testrebase() {
 		test $(git rev-parse HEAD) != $(git rev-parse master) &&
 		git rebase --abort &&
 		test $(git rev-parse to-rebase) = $(git rev-parse pre-rebase) &&
-		test ! -d "$dotest"
+		test ! -d "$dotest" &&
+		test ! -d ".git/sequencer"
 	'
 }
 
diff --git a/t/t4150-am.sh b/t/t4150-am.sh
index bc98260..a9ee55e 100755
--- a/t/t4150-am.sh
+++ b/t/t4150-am.sh
@@ -83,6 +83,10 @@ test_expect_success setup '
 	git commit -m third &&
 	git format-patch --stdout first >patch2	&&
 	git checkout -b lorem &&
+	echo new >another &&
+	git add another &&
+	test_tick &&
+	git commit -m "added another file" &&
 	sed -n -e "11,\$p" msg >file &&
 	head -n 9 msg >>file &&
 	test_tick &&
@@ -181,7 +185,7 @@ test_expect_success 'am -3 falls back to 3-way merge' '
 '
 
 test_expect_success 'am pauses on conflict' '
-	git checkout lorem2^^ &&
+	git checkout lorem2~3 &&
 	! git am lorem-move.patch &&
 	test -d .dotest
 '
@@ -193,8 +197,17 @@ test_expect_success 'am --skip works' '
 	test goodbye = "$(cat another)"
 '
 
+test_expect_success 'am --abort works' '
+	git checkout lorem2~3 &&
+	! git am lorem-move.patch &&
+	test -d .dotest &&
+	git am --abort &&
+	test "$(git rev-parse HEAD)" = "$(git rev-parse lorem2~3)" &&
+	! test -f another
+'
+
 test_expect_success 'am --resolved works' '
-	git checkout lorem2^^ &&
+	git checkout lorem2~3 &&
 	! git am lorem-move.patch &&
 	test -d .dotest &&
 	echo resolved >>file &&
@@ -212,14 +225,12 @@ test_expect_success 'am takes patches from a Pine mailbox' '
 '
 
 test_expect_success 'am fails on mail without patch' '
-	! git am <failmail &&
-	rm -r .dotest/
+	! git am <failmail
 '
 
 test_expect_success 'am fails on empty patch' '
 	echo "---" >>failmail &&
 	! git am <failmail &&
-	git am --skip &&
 	! test -d .dotest
 '
 
-- 
1.5.6.334.gdaf0

^ permalink raw reply related	[flat|nested] 52+ messages in thread

* git-rebase-i migration to sequencer
  2008-07-01  2:38       ` [RFC/PATCH 4/4] Migrate git-am to use git-sequencer Stephan Beyer
@ 2008-07-01  2:39         ` Stephan Beyer
  2008-07-01  2:39           ` [PATCH 1/2] Make rebase--interactive use OPTIONS_SPEC Stephan Beyer
  0 siblings, 1 reply; 52+ messages in thread
From: Stephan Beyer @ 2008-07-01  2:39 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Johannes Schindelin

Hi,

the patches for rebase-i migration follow.

Why this intermediate mail?
Because the patches will not apply to master/next/pu/whatever.
It needs js/rebase-i-sequencer, that is "dropped for now" from
git.git.  So you have to do some extra work to apply.

Well, I could've just added a "Squashed js/rebase-i-sequencer patch"
or something, but
 1. I didn't write the patches so I don't want to claim I'm the author,
 2. It's an opportunity to try sequencer, as follows:

If you still have the commits (a freshly cloned repo hasn't),
you can do:

git sequencer <<EOF
#pick 367bcc9cbc3d903c076f # optional: "fake-editor: output TODO list if unchanged"
pick 0fa413ebc6e073a52d88
pick -R 88b1f0b8094638b1f953 # just to prevent a silly conflict
pick 966fdf0ab272a64250fb
pick 45d6022d611fd86a13c5
pick 051e0cfb88cd1d2a24bd
# note: the following patches will make t3404 fail until migration to sequencer
pick d9711a80796fd00169ca
pick 10046a6f8adaccac0ea1
pick abe32eafcd8302c387ed
pick d481bcc93f393bbfaaef
pick dbcd70439f697deeb317
pick b1ba88d812434a51889f
pick cc76fdac36ff25eaa77b
pick f8e037d76a224bd87236
pick 777790a3bdfeb91ee4a6
pick 7cdf70106412f44b9116
pick 14a4f1e0a804ccc905d7
EOF

If this results in a lot of "is not a commit" errors, then do
	git sequencer --abort
	(You may also have a look at git sequencer --status, if you like)
and fetch the missing commits:
	git fetch git://repo.or.cz/git/sbeyer.git seq-proto-dev2
and then redo the example above.

When the patches are applied, the following patches will apply, too.

Regards,
  Stephan

PS: Sorry, for the nastiness of the patch application ;-)

^ permalink raw reply	[flat|nested] 52+ messages in thread

* [PATCH 1/2] Make rebase--interactive use OPTIONS_SPEC
  2008-07-01  2:39         ` git-rebase-i migration to sequencer Stephan Beyer
@ 2008-07-01  2:39           ` Stephan Beyer
  2008-07-01  2:39             ` [RFC/PATCH 2/2] Migrate git-rebase--i to use git-sequencer Stephan Beyer
  0 siblings, 1 reply; 52+ messages in thread
From: Stephan Beyer @ 2008-07-01  2:39 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Johannes Schindelin, Stephan Beyer

Also add some checks that --continue/--abort/--skip
actions are used without --onto, -p, -t, etc.

Signed-off-by: Stephan Beyer <s-beyer@gmx.net>
---
 git-rebase--interactive.sh |   72 ++++++++++++++++++++++++++++---------------
 1 files changed, 47 insertions(+), 25 deletions(-)

diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 7e073bb..ad16fa2 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -10,10 +10,27 @@
 # The original idea comes from Eric W. Biederman, in
 # http://article.gmane.org/gmane.comp.version-control.git/22407
 
-USAGE='(--continue | --abort | --skip | [--preserve-merges] [--first-parent]
-	[--preserve-tags] [--verbose] [--onto <branch>] <upstream> [<branch>])'
+OPTIONS_KEEPDASHDASH=
+OPTIONS_SPEC="\
+git-rebase [-i] [options] [--] <upstream> [<branch>]
+git-rebase [-i] (--continue | --abort | --skip)
+--
+ Available options are
+p,preserve-merges  try to recreate merges instead of ignoring them
+t,preserve-tags    update tags to the new commit object
+m,merge            always used (no-op)
+i,interactive      always used (no-op)
+onto=              rebase onto given branch instead of upstream
+v,verbose          display a diffstat of what changed upstream
+ When preserving merges
+f,first-parent     show only commits following the first parent of each commit
+s,strategy=        use the given merge strategy
+ Actions:
+continue           continue rebasing process
+abort              abort rebasing process and restore original branch
+skip               skip current patch and continue rebasing process
+"
 
-OPTIONS_SPEC=
 . git-sh-setup
 require_work_tree
 
@@ -25,6 +42,8 @@ SQUASH_MSG="$DOTEST"/message-squash
 PRESERVE_MERGES=
 STRATEGY=
 VERBOSE=
+ONTO=
+MARK_PREFIX='refs/rebase-marks'
 test -f "$DOTEST"/strategy && STRATEGY="$(cat "$DOTEST"/strategy)"
 test -f "$DOTEST"/verbose && VERBOSE=t
 
@@ -515,10 +534,19 @@ create_extended_todo_list () {
 	done
 }
 
+is_standalone () {
+	test $# -eq 2 -a "$2" = '--' &&
+	test -z "$ONTO" &&
+	test -z "$PRESERVE_TAGS" &&
+	test -z "$PRESERVE_MERGES" &&
+	test -z "$FIRST_PARENT"
+}
+
 while test $# != 0
 do
 	case "$1" in
 	--continue)
+		is_standalone "$@" || usage
 		comment_for_reflog continue
 
 		test -d "$DOTEST" || die "No interactive rebase running"
@@ -551,6 +579,7 @@ do
 		do_rest
 		;;
 	--abort)
+		is_standalone "$@" || usage
 		comment_for_reflog abort
 
 		git rerere clear
@@ -568,6 +597,7 @@ do
 		exit
 		;;
 	--skip)
+		is_standalone "$@" || usage
 		comment_for_reflog skip
 
 		git rerere clear
@@ -575,7 +605,7 @@ do
 
 		output git reset --hard && do_rest
 		;;
-	-s|--strategy)
+	-s)
 		case "$#,$1" in
 		*,*=*)
 			STRATEGY="-s "$(expr "z$1" : 'z-[^=]*=\(.*\)') ;;
@@ -586,32 +616,33 @@ do
 			shift ;;
 		esac
 		;;
-	-m|--merge)
+	-m)
 		# we use merge anyway
 		;;
-	-C*)
-		die "Interactive rebase uses merge, so $1 does not make sense"
-		;;
-	-v|--verbose)
+	-v)
 		VERBOSE=t
 		;;
-	-p|--preserve-merges)
+	-p)
 		PRESERVE_MERGES=t
 		;;
-	-f|--first-parent)
+	-f)
 		FIRST_PARENT=t
 		PRESERVE_MERGES=t
 		;;
-	-t|--preserve-tags)
+	-t)
 		PRESERVE_TAGS=t
 		;;
-	-i|--interactive)
+	-i)
 		# yeah, we know
 		;;
-	''|-h)
-		usage
+	--onto)
+		shift
+		ONTO=$(git rev-parse --verify "$1") ||
+			die "Does not point to a valid commit: $1"
 		;;
-	*)
+	--)
+		shift
+		test $# -eq 1 -o $# -eq 2 || usage
 		test -d "$DOTEST" &&
 			die "Interactive rebase already started"
 
@@ -620,15 +651,6 @@ do
 
 		comment_for_reflog start
 
-		ONTO=
-		case "$1" in
-		--onto)
-			ONTO=$(git rev-parse --verify "$2") ||
-				die "Does not point to a valid commit: $2"
-			shift; shift
-			;;
-		esac
-
 		require_clean_work_tree
 
 		UPSTREAM=$(git rev-parse --verify "$1") || die "Invalid base"
-- 
1.5.6.334.gdaf0

^ permalink raw reply related	[flat|nested] 52+ messages in thread

* [RFC/PATCH 2/2] Migrate git-rebase--i to use git-sequencer
  2008-07-01  2:39           ` [PATCH 1/2] Make rebase--interactive use OPTIONS_SPEC Stephan Beyer
@ 2008-07-01  2:39             ` Stephan Beyer
  0 siblings, 0 replies; 52+ messages in thread
From: Stephan Beyer @ 2008-07-01  2:39 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Johannes Schindelin, Stephan Beyer

Signed-off-by: Stephan Beyer <s-beyer@gmx.net>
---
 git-rebase--interactive.sh    |  423 ++++++-----------------------------------
 t/t3404-rebase-interactive.sh |   29 ++--
 2 files changed, 71 insertions(+), 381 deletions(-)

diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index ad16fa2..60f43b2 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -35,28 +35,13 @@ skip               skip current patch and continue rebasing process
 require_work_tree
 
 DOTEST="$GIT_DIR/.dotest-merge"
-TODO="$DOTEST"/git-rebase-todo
-DONE="$DOTEST"/done
-MSG="$DOTEST"/message
-SQUASH_MSG="$DOTEST"/message-squash
+TODO="$DOTEST/git-rebase-todo"
 PRESERVE_MERGES=
 STRATEGY=
 VERBOSE=
+INTERACTIVE=
 ONTO=
-MARK_PREFIX='refs/rebase-marks'
-test -f "$DOTEST"/strategy && STRATEGY="$(cat "$DOTEST"/strategy)"
-test -f "$DOTEST"/verbose && VERBOSE=t
-
-GIT_CHERRY_PICK_HELP="  After resolving the conflicts,
-mark the corrected paths with 'git add <paths>', and
-run 'git rebase --continue'"
-export GIT_CHERRY_PICK_HELP
-
-mark_prefix=refs/rebase-marks/
-
-warn () {
-	echo "$*" >&2
-}
+test -f "$DOTEST"/verbose && VERBOSE=--verbose
 
 output () {
 	case "$VERBOSE" in
@@ -83,51 +68,11 @@ require_clean_work_tree () {
 
 ORIG_REFLOG_ACTION="$GIT_REFLOG_ACTION"
 
-comment_for_reflog () {
-	case "$ORIG_REFLOG_ACTION" in
-	''|rebase*)
-		GIT_REFLOG_ACTION="rebase -i ($1)"
-		export GIT_REFLOG_ACTION
-		;;
-	esac
-}
-
-last_count=
-mark_action_done () {
-	sed -e 1q < "$TODO" >> "$DONE"
-	sed -e 1d < "$TODO" >> "$TODO".new
-	mv -f "$TODO".new "$TODO"
-	count=$(grep -c '^[^#]' < "$DONE")
-	total=$(($count+$(grep -c '^[^#]' < "$TODO")))
-	if test "$last_count" != "$count"
-	then
-		last_count=$count
-		printf "Rebasing (%d/%d)\r" $count $total
-		test -z "$VERBOSE" || echo
-	fi
-}
-
-make_patch () {
-	parent_sha1=$(git rev-parse --verify "$1"^) ||
-		die "Cannot get patch for $1^"
-	git diff-tree -p "$parent_sha1".."$1" > "$DOTEST"/patch
-	test -f "$DOTEST"/message ||
-		git cat-file commit "$1" | sed "1,/^$/d" > "$DOTEST"/message
-	test -f "$DOTEST"/author-script ||
-		get_author_ident_from_commit "$1" > "$DOTEST"/author-script
-}
-
-die_with_patch () {
-	make_patch "$1"
-	git rerere
-	die "$2"
+warn () {
+	echo "$*" >&2
 }
 
 cleanup_before_quit () {
-	for ref in $(git for-each-ref --format='%(refname)' "${mark_prefix%/}")
-	do
-		git update-ref -d "$ref" "$ref" || return 1
-	done
 	rm -rf "$DOTEST"
 }
 
@@ -136,229 +81,13 @@ die_abort () {
 	die "$1"
 }
 
-has_action () {
-	grep '^[^#]' "$1" >/dev/null
-}
-
-redo_merge () {
-	rm_sha1=$1
-	shift
-
-	eval "$(get_author_ident_from_commit $rm_sha1)"
-	msg="$(git cat-file commit $rm_sha1 | sed -e '1,/^$/d')"
-
-	if ! GIT_AUTHOR_NAME="$GIT_AUTHOR_NAME" \
-		GIT_AUTHOR_EMAIL="$GIT_AUTHOR_EMAIL" \
-		GIT_AUTHOR_DATE="$GIT_AUTHOR_DATE" \
-		output git merge $STRATEGY -m "$msg" "$@"
-	then
-		git rerere
-		printf "%s\n" "$msg" > "$GIT_DIR"/MERGE_MSG
-		die Error redoing merge $rm_sha1
-	fi
-	unset rm_sha1
-}
-
-pick_one () {
-	no_ff=
-	case "$1" in -n) sha1=$2; no_ff=t ;; *) sha1=$1 ;; esac
-	output git rev-parse --verify $sha1 || die "Invalid commit name: $sha1"
-	parent_sha1=$(git rev-parse --verify $sha1^) ||
-		die "Could not get the parent of $sha1"
-	current_sha1=$(git rev-parse --verify HEAD)
-	if test "$no_ff$current_sha1" = "$parent_sha1"; then
-		output git reset --hard $sha1
-		test "a$1" = a-n && output git reset --soft $current_sha1
-		sha1=$(git rev-parse --short $sha1)
-		output warn Fast forward to $sha1
-	else
-		output git cherry-pick "$@"
-	fi
-}
-
-nth_string () {
-	case "$1" in
-	*1[0-9]|*[04-9]) echo "$1"th;;
-	*1) echo "$1"st;;
-	*2) echo "$1"nd;;
-	*3) echo "$1"rd;;
-	esac
-}
-
-make_squash_message () {
-	if test -f "$SQUASH_MSG"; then
-		COUNT=$(($(sed -n "s/^# This is [^0-9]*\([1-9][0-9]*\).*/\1/p" \
-			< "$SQUASH_MSG" | sed -ne '$p')+1))
-		echo "# This is a combination of $COUNT commits."
-		sed -e 1d -e '2,/^./{
-			/^$/d
-		}' <"$SQUASH_MSG"
-	else
-		COUNT=2
-		echo "# This is a combination of two commits."
-		echo "# The first commit's message is:"
-		echo
-		git cat-file commit HEAD | sed -e '1,/^$/d'
-	fi
-	echo
-	echo "# This is the $(nth_string $COUNT) commit message:"
-	echo
-	git cat-file commit $1 | sed -e '1,/^$/d'
-}
-
-peek_next_command () {
-	sed -n "1s/ .*$//p" < "$TODO"
-}
-
-mark_to_ref () {
-	if expr "$1" : "^:[0-9][0-9]*$" >/dev/null
-	then
-		echo "$mark_prefix$(printf %d ${1#:})"
-	else
-		echo "$1"
-	fi
-}
-
-do_next () {
-	rm -f "$DOTEST"/message "$DOTEST"/author-script \
-		"$DOTEST"/amend || exit
-	read command sha1 rest < "$TODO"
-	case "$command" in
-	'#'*|'')
-		mark_action_done
-		;;
-	pick|p)
-		comment_for_reflog pick
-
-		mark_action_done
-		pick_one $sha1 ||
-			die_with_patch $sha1 "Could not apply $sha1... $rest"
-		;;
-	edit|e)
-		comment_for_reflog edit
-
-		mark_action_done
-		pick_one $sha1 ||
-			die_with_patch $sha1 "Could not apply $sha1... $rest"
-		make_patch $sha1
-		: > "$DOTEST"/amend
-		warn
-		warn "You can amend the commit now, with"
-		warn
-		warn "	git commit --amend"
-		warn
-		warn "Once you are satisfied with your changes, run"
-		warn
-		warn "	git rebase --continue"
-		warn
-		exit 0
-		;;
-	squash|s)
-		comment_for_reflog squash
-
-		has_action "$DONE" ||
-			die "Cannot 'squash' without a previous commit"
-
-		mark_action_done
-		make_squash_message $sha1 > "$MSG"
-		case "$(peek_next_command)" in
-		squash|s)
-			EDIT_COMMIT=
-			USE_OUTPUT=output
-			cp "$MSG" "$SQUASH_MSG"
-			;;
-		*)
-			EDIT_COMMIT=-e
-			USE_OUTPUT=
-			rm -f "$SQUASH_MSG" || exit
-			;;
-		esac
-
-		failed=f
-		author_script=$(get_author_ident_from_commit HEAD)
-		output git reset --soft HEAD^
-		pick_one -n $sha1 || failed=t
-		echo "$author_script" > "$DOTEST"/author-script
-		if test $failed = f
-		then
-			# This is like --amend, but with a different message
-			eval "$author_script"
-			GIT_AUTHOR_NAME="$GIT_AUTHOR_NAME" \
-			GIT_AUTHOR_EMAIL="$GIT_AUTHOR_EMAIL" \
-			GIT_AUTHOR_DATE="$GIT_AUTHOR_DATE" \
-			$USE_OUTPUT git commit --no-verify -F "$MSG" $EDIT_COMMIT || failed=t
-		fi
-		if test $failed = t
-		then
-			cp "$MSG" "$GIT_DIR"/MERGE_MSG
-			warn
-			warn "Could not apply $sha1... $rest"
-			die_with_patch $sha1 ""
-		fi
-		;;
-	mark)
-		mark_action_done
-
-		mark=$(mark_to_ref :${sha1#:})
-		test :${sha1#:} = "$mark" && die "Invalid mark '$sha1'"
-
-		git rev-parse --verify "$mark" > /dev/null 2>&1 && \
-			warn "mark $sha1 already exist; overwriting it"
-
-		git update-ref "$mark" HEAD || die "update-ref failed"
-		;;
-	merge|m)
-		comment_for_reflog merge
-
-		if ! git rev-parse --verify $sha1 > /dev/null
-		then
-			die "Invalid reference merge '$sha1' in"
-					"$command $sha1 $rest"
-		fi
-
-		new_parents=
-		for p in $rest
-		do
-			new_parents="$new_parents $(mark_to_ref $p)"
-		done
-		new_parents="${new_parents# }"
-		test -n "$new_parents" || \
-			die "You forgot to give the parents for the" \
-				"merge $sha1. Please fix it in $TODO"
-
-		mark_action_done
-		redo_merge $sha1 $new_parents
-		;;
-	reset|r)
-		comment_for_reflog reset
-
-		tmp=$(git rev-parse --verify "$(mark_to_ref $sha1)") ||
-			die "Invalid parent '$sha1' in $command $sha1 $rest"
-
-		mark_action_done
-		output git reset --hard $tmp
-		;;
-	tag|t)
-		comment_for_reflog tag
-
-		mark_action_done
-		output git tag -f "$sha1"
-		;;
-	*)
-		warn "Unknown command: $command $sha1 $rest"
-		die_with_patch $sha1 "Please fix this in the file $TODO."
-		;;
-	esac
-	test -s "$TODO" && return
-
-	comment_for_reflog finish &&
+update_refs () {
 	HEADNAME=$(cat "$DOTEST"/head-name) &&
 	OLDHEAD=$(cat "$DOTEST"/head) &&
-	SHORTONTO=$(git rev-parse --short $(cat "$DOTEST"/onto)) &&
 	NEWHEAD=$(git rev-parse HEAD) &&
 	case $HEADNAME in
 	refs/*)
-		message="$GIT_REFLOG_ACTION: $HEADNAME onto $SHORTONTO)" &&
+		message="$GIT_REFLOG_ACTION: $HEADNAME)" &&
 		git update-ref -m "$message" $HEADNAME $NEWHEAD $OLDHEAD &&
 		git symbolic-ref HEAD $HEADNAME
 		;;
@@ -369,15 +98,30 @@ do_next () {
 	cleanup_before_quit &&
 	git gc --auto &&
 	warn "Successfully rebased and updated $HEADNAME."
+}
 
-	exit
+run_sequencer () {
+	git sequencer --caller='git rebase -i|--abort|--continue|--skip' "$@"
+	case "$?" in
+	0)
+		update_refs
+		exit 0
+		;;
+	2)
+		warn 'git-sequencer needs continuation (by edit).'
+		exit 0
+		;;
+	3)
+		die 'git-sequencer needs continuation (by conflict).'
+		;;
+	*)
+		die_abort 'git-sequencer died.'
+		;;
+	esac
 }
 
-do_rest () {
-	while :
-	do
-		do_next
-	done
+has_action () {
+	grep '^[^#]' "$1" >/dev/null
 }
 
 get_value_from_list () {
@@ -418,7 +162,6 @@ create_extended_todo_list () {
 			(
 			while read sha1 tag
 			do
-				tag=${tag#refs/tags/}
 				if test ${last_sha1:-0000} = $sha1
 				then
 					saved_tags="$saved_tags:$tag"
@@ -457,7 +200,7 @@ create_extended_todo_list () {
 		then
 			for t in $(echo $tmp | tr : ' ')
 			do
-				echo tag $t
+				echo ref $t
 			done
 		fi
 
@@ -477,18 +220,18 @@ create_extended_todo_list () {
 				fi
 			done
 			unset p
-			echo merge $commit $new_parents
+			echo "merge $STRATEGY --reuse-commit=$commit $new_parents"
 			unset new_parents
 			;;
 		*)
-			echo "pick $commit $subject"
+			echo "pick $commit # $subject"
 			;;
 		esac
 	done
 	test -n "${last_parent:-}" -a "${last_parent:-}" != $SHORTUPSTREAM && \
-		echo reset $last_parent
+		echo "reset $last_parent"
 	) | \
-	perl -e 'print reverse <>' | \
+	@@PERL@@ -e 'print reverse <>' | \
 	while read cmd args
 	do
 		: ${commit_mark_list:=} ${last_commit:=000}
@@ -515,6 +258,7 @@ create_extended_todo_list () {
 			fi
 			;;
 		merge)
+			args="${args#*--reuse-commit=}"
 			new_args=
 			for i in ${args#* }
 			do
@@ -527,7 +271,7 @@ create_extended_todo_list () {
 				fi
 			done
 			last_commit="${args%% *}"
-			args="$last_commit ${new_args# }"
+			args="$STRATEGY --reuse-commit=$last_commit ${new_args# }"
 			;;
 		esac
 		echo "$cmd $args"
@@ -545,65 +289,9 @@ is_standalone () {
 while test $# != 0
 do
 	case "$1" in
-	--continue)
-		is_standalone "$@" || usage
-		comment_for_reflog continue
-
-		test -d "$DOTEST" || die "No interactive rebase running"
-
-		# Sanity check
-		git rev-parse --verify HEAD >/dev/null ||
-			die "Cannot read HEAD"
-		git update-index --ignore-submodules --refresh &&
-			git diff-files --quiet --ignore-submodules ||
-			die "Working tree is dirty"
-
-		# do we have anything to commit?
-		if git diff-index --cached --quiet --ignore-submodules HEAD --
-		then
-			: Nothing to commit -- skip this
-		else
-			. "$DOTEST"/author-script ||
-				die "Cannot find the author identity"
-			if test -f "$DOTEST"/amend
-			then
-				git reset --soft HEAD^ ||
-				die "Cannot rewind the HEAD"
-			fi
-			export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE &&
-			git commit --no-verify -F "$DOTEST"/message -e ||
-			die "Could not commit staged changes."
-		fi
-
-		require_clean_work_tree
-		do_rest
-		;;
-	--abort)
+	--continue|--abort|--skip)
 		is_standalone "$@" || usage
-		comment_for_reflog abort
-
-		git rerere clear
-		test -d "$DOTEST" || die "No interactive rebase running"
-
-		HEADNAME=$(cat "$DOTEST"/head-name)
-		HEAD=$(cat "$DOTEST"/head)
-		case $HEADNAME in
-		refs/*)
-			git symbolic-ref HEAD $HEADNAME
-			;;
-		esac &&
-		output git reset --hard $HEAD &&
-		cleanup_before_quit
-		exit
-		;;
-	--skip)
-		is_standalone "$@" || usage
-		comment_for_reflog skip
-
-		git rerere clear
-		test -d "$DOTEST" || die "No interactive rebase running"
-
-		output git reset --hard && do_rest
+		run_sequencer "$1"
 		;;
 	-s)
 		case "$#,$1" in
@@ -620,7 +308,7 @@ do
 		# we use merge anyway
 		;;
 	-v)
-		VERBOSE=t
+		VERBOSE=--verbose
 		;;
 	-p)
 		PRESERVE_MERGES=t
@@ -633,7 +321,7 @@ do
 		PRESERVE_TAGS=t
 		;;
 	-i)
-		# yeah, we know
+		INTERACTIVE=t
 		;;
 	--onto)
 		shift
@@ -649,14 +337,12 @@ do
 		git var GIT_COMMITTER_IDENT >/dev/null ||
 			die "You need to set your committer info first"
 
-		comment_for_reflog start
-
 		require_clean_work_tree
 
 		UPSTREAM=$(git rev-parse --verify "$1") || die "Invalid base"
 		test -z "$ONTO" && ONTO=$UPSTREAM
 
-		if test ! -z "$2"
+		if test -n "$2"
 		then
 			output git show-ref --verify --quiet "refs/heads/$2" ||
 				die "Invalid branchname: $2"
@@ -674,8 +360,7 @@ do
 		echo $HEAD > "$DOTEST"/head
 		echo $UPSTREAM > "$DOTEST"/upstream
 		echo $ONTO > "$DOTEST"/onto
-		test -z "$STRATEGY" || echo "$STRATEGY" > "$DOTEST"/strategy
-		test t = "$VERBOSE" && : > "$DOTEST"/verbose
+		test -n "$VERBOSE" && : > "$DOTEST"/verbose
 
 		SHORTUPSTREAM=$(git rev-parse --short=7 $UPSTREAM)
 		SHORTHEAD=$(git rev-parse --short=7 $HEAD)
@@ -696,7 +381,8 @@ do
 				create_extended_todo_list
 		else
 			git rev-list --no-merges --reverse --pretty=oneline \
-				 $common_rev_list_opts | sed -n "s/^>/pick /p"
+				 $common_rev_list_opts |
+				 sed -n -e "s/^>\([0-9a-f]\+\)/pick \1\t#/p"
 		fi > "$TODO"
 
 		cat >> "$TODO" << EOF
@@ -713,26 +399,29 @@ do
 #  squash = use commit, but meld into previous commit
 #  mark :mark = mark the current HEAD for later reference
 #  reset commit = reset HEAD to the commit
-#  merge commit-M commit-P ... = redo merge commit-M with the
-#         current HEAD and the parents commit-P
-#  tag = reset tag to the current HEAD
+#  merge [--reuse-commit=commit-M] remote ... =
+#     * merge the remotes into HEAD
+#     * can use the author and commit message from commit-M
+#  ref = update ref to the current HEAD
 #
 # If you remove a line here THAT COMMIT WILL BE LOST.
 # However, if you remove everything, the rebase will be aborted.
 #
 EOF
-
 		has_action "$TODO" ||
 			die_abort "Nothing to do"
 
-		cp "$TODO" "$TODO".backup
-		git_editor "$TODO" ||
-			die "Could not execute editor"
+		if test -n "$INTERACTIVE"
+		then
+			cp "$TODO" "$TODO".backup
+			git_editor "$TODO" ||
+				die_abort "Could not execute editor"
 
-		has_action "$TODO" ||
-			die_abort "Nothing to do"
+			has_action "$TODO" ||
+				die_abort "Nothing to do"
+		fi
 
-		output git checkout $ONTO && do_rest
+		run_sequencer $VERBOSE --onto "$ONTO" "$TODO"
 		;;
 	esac
 	shift
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index 940eb24..8b0781d 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -89,11 +89,11 @@ for line in $FAKE_LINES; do
 		echo "reset ${line#reset}"
 		echo "reset ${line#reset}" >> "$1";;
 	merge*)
-		echo "merge ${line#merge}" | tr / ' '
-		echo "merge ${line#merge}" | tr / ' ' >> "$1";;
+		echo "merge --reuse-commit=${line#merge}" | tr / ' '
+		echo "merge --reuse-commit=${line#merge}" | tr / ' ' >> "$1";;
 	tag*)
-		echo "tag ${line#tag}"
-		echo "tag ${line#tag}" >> "$1";;
+		echo "ref refs/tags/${line#tag}"
+		echo "ref refs/tags/${line#tag}" >> "$1";;
 	*)
 		sed -n "${line}{s/^pick/$action/; p;}" < "$1".tmp
 		sed -n "${line}{s/^pick/$action/; p;}" < "$1".tmp >> "$1"
@@ -170,19 +170,20 @@ test_expect_success 'stop on conflicting pick' '
 	git tag new-branch1 &&
 	test_must_fail git rebase -i master &&
 	test "$(git rev-parse HEAD~3)" = "$(git rev-parse master)" &&
-	test_cmp expect .git/.dotest-merge/patch &&
+	test_cmp expect .git/sequencer/patch &&
 	test_cmp expect2 file1 &&
 	test "$(git-diff --name-status |
 		sed -n -e "/^U/s/^U[^a-z]*//p")" = file1 &&
-	test 4 = $(grep -v "^#" < .git/.dotest-merge/done | wc -l) &&
-	test 0 = $(grep -c "^[^#]" < .git/.dotest-merge/git-rebase-todo)
+	test 4 = $(grep -v "^#" < .git/sequencer/done | wc -l) &&
+	test 0 = $(grep -c "^[^#]" < .git/sequencer/todo) &&
+	test -d .git/.dotest-merge
 '
 
 test_expect_success 'abort' '
 	git rebase --abort &&
 	test $(git rev-parse new-branch1) = $(git rev-parse HEAD) &&
 	test "$(git symbolic-ref -q HEAD)" = "refs/heads/branch1" &&
-	! test -d .git/.dotest-merge
+	! test -d .git/sequencer
 '
 
 test_expect_success 'retain authorship' '
@@ -219,13 +220,13 @@ test_expect_success '-p handles "no changes" gracefully' '
 test_expect_success 'setting marks works' '
 	git checkout master &&
 	FAKE_LINES="mark:0 2 1 mark:42 3 edit 4" git rebase -i HEAD~4 &&
-	marks_dir=.git/refs/rebase-marks &&
+	marks_dir=.git/refs/sequencer-marks &&
 	test -d $marks_dir &&
 	test $(ls $marks_dir | wc -l) -eq 2 &&
 	test "$(git rev-parse HEAD~4)" = \
-		"$(git rev-parse refs/rebase-marks/0)" &&
+		"$(git rev-parse refs/sequencer-marks/0)" &&
 	test "$(git rev-parse HEAD~2)" = \
-		"$(git rev-parse refs/rebase-marks/42)" &&
+		"$(git rev-parse refs/sequencer-marks/42)" &&
 	git rebase --abort &&
 	test 0 = $(ls $marks_dir | wc -l)
 '
@@ -233,8 +234,8 @@ test_expect_success 'setting marks works' '
 test_expect_success 'reset with nonexistent mark fails' '
 	export FAKE_LINES="reset:0 1" &&
 	test_must_fail git rebase -i HEAD~1 &&
-	unset FAKE_LINES &&
-	git rebase --abort
+	git rebase --abort &&
+	unset FAKE_LINES
 '
 
 test_expect_success 'reset to HEAD is a nop' '
@@ -325,7 +326,7 @@ test_expect_success 'interactive --first-parent gives a linear list' '
 	test "$head" = "$(git rev-parse HEAD)"
 '
 
-test_expect_success 'tag sets tags' '
+test_expect_success 'ref can set tags' '
 	head=$(git rev-parse HEAD) &&
 	FAKE_LINES="1 2 3 4 5 tagbb-tag1 6 7 8 9 10 11 12 13 14 15 \
 		tagbb-tag2 16 tagbb-tag3a tagbb-tag3b 17 18 19 20 21 22" \
-- 
1.5.6.334.gdaf0

^ permalink raw reply related	[flat|nested] 52+ messages in thread

* Re: git sequencer prototype
  2008-07-01  2:38 git sequencer prototype Stephan Beyer
  2008-07-01  2:38 ` [RFC/PATCH 1/4] Add git-sequencer shell prototype Stephan Beyer
@ 2008-07-01  8:51 ` Junio C Hamano
  2008-07-01 14:53   ` Stephan Beyer
  2008-07-04 21:00 ` Alex Riesen
  2 siblings, 1 reply; 52+ messages in thread
From: Junio C Hamano @ 2008-07-01  8:51 UTC (permalink / raw)
  To: Stephan Beyer; +Cc: git, Johannes Schindelin

Stephan Beyer <s-beyer@gmx.net> writes:

> I'm using sequencer-based git-am and git-rebase-i and also git-sequencer
> itself for around 2-3 weeks now. So, for me, it is reality-proven, but
> I'm curious about your opinions/suggestions in usage and source.

Thanks.

> The migration patches are a little hard to code-review in the diff-form,
> but feel free to apply, test, and then look at the code ;)

Heh, these three typically come in different order.  Look at the log
message and docs to see if the design is sound, then look at the code, and
if things overall look Ok, then finally apply and test.

I unfortunately ran out of time tonight and will look at them probably
Thursday evening.  It seems that another GSoC topic is finally nearing
completion and I can shift my attention to other topics now.

By the way Christian is not CC'ed?

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC/PATCH 2/4] Add git-sequencer prototype documentation
  2008-07-01  2:38   ` [RFC/PATCH 2/4] Add git-sequencer prototype documentation Stephan Beyer
  2008-07-01  2:38     ` [RFC/PATCH 3/4] Add git-sequencer test suite (t3350) Stephan Beyer
@ 2008-07-01 13:02     ` Jakub Narebski
  2008-07-01 16:03       ` Stephan Beyer
  2008-07-05 17:00     ` [PATCH v2 " Stephan Beyer
  2 siblings, 1 reply; 52+ messages in thread
From: Jakub Narebski @ 2008-07-01 13:02 UTC (permalink / raw)
  To: Stephan Beyer; +Cc: git, Junio C Hamano, Johannes Schindelin

Stephan Beyer <s-beyer@gmx.net> writes:

> Mentored-by: Christian Couder <chriscool@tuxfamily.org>
> Mentored-by: Daniel Barkalow <barkalow@iabervon.org>

Cc mentors?

> +git-sequencer will usually be called by another git porcelain, like
> +linkgit:git-am[1] or linkgit:git-rebase[1].

I hope that it could be also used by git-cherry-pick and git-revert,
so it would be possible to pick more than one commit...

> +In case of a conflict or request in the TODO file, git-sequencer will
> +pause. On conflict you can use git-diff to locate the markers (`<<<<<<<`)
> +and make edits to resolve the conflict.
> +
> +For each file you edit, you need to tell git the changes by doing
> +
> +    git add <file>
> +
> +After resolving the conflict manually and updating the index with the
> +desired resolution, you can continue the sequencing process with
> +
> +    git sequencer --continue

Does it run pre-commit hooks, for example checking for merge markers
(but also whitespace errors), and can those checks be disabled?

> +-B::
> +--batch::
> +	Run in batch mode. If unexpected user intervention is needed
> +	(e.g. a conflict or the need to run an editor), git-sequencer fails.
> ++
> +Note that the sanity check fails, if you use this option
> +and an instruction like `edit` or `pause`.

s/.$/ is in the TODO file./

> +--edit::
> +	Invoke editor to edit the undone rest of the TODO file.

Does it mean that editor will be invoked with only _unprocessed_
part of the TODO file?  It looks like it, but it might be not
obvious to some.

> +merge [options] <commit-ish1> <commit-ish2> ... <commit-ishN>::
> +	Merge commits into HEAD.

Nice.

"HEAD" mean last picked up / created commit, isn't it?

> ++
> +A commit can also be given by a mark, if prefixed with a colon.

"You can refer to commit by mark", perhaps?

> +	--standard;;
> +		Generates a commit message like 'Merge ... into HEAD'.
> +		See also linkgit:git-fmt-merge-msg[1].

Is it short for `--standard-message`?

> +pick [options] <commit>::
> +	Pick (see linkgit:git-cherry-pick[1]) a commit.
> +	Sequencer will pause on conflicts.
> ++
> +See the following list and 'GENERAL OPTIONS' for values of `options`:
> +
> +	-R;;
> +	--reverse;;
> +		Revert the changes introduced by pick <commit>.
> +
> +	--mainline=<n>;;
> +		Allows you to pick merge commits by specifying the
> +		parent number (beginning from 1) to let sequencer
> +		replay the changes relative to the specified parent.
> +		+
> +This option does not work together with `-R`.

Why?  I would have thought that it would work...

> +patch [options] <file>::
[...]
> +	-*;;
> +		Any other dash-prefixed option is passed to
> +		linkgit:git-apply[1].
> +		This is especially useful for flags like
> +		`--reverse`, `-C<n>`, `-p<n>` or `--whitespace=<action>`.

Do all options make sense?  What about `--index' and `--cached',
or about different no-apply options?

> +ref <ref>::
> +	Set ref `<ref>` to the current HEAD, see also
> +	linkgit:git-update-ref[1].

So this functions like "git reset --soft <ref>", isn't it?

> +squash [options] --from <mark>::
> +	Squash all commits from the given mark into one commit.
> +	There must not be any `merge` instructions between the
> +	`mark` instruction and this `squash --from` instruction.

Can you use <commit> instead of <mark> here?

> +	--include-merges;;
> +		Sanity check does not fail if you have merges
> +		between HEAD and <mark>.

How do you squash merges?  Creating merge with an union of parents,
excluding commits which got squashed?

It means

  ...a---b---c---d         ...[abcd]
            /        ==>        /
      ...x-/               ..x-/

but

  ...a---b---c---d          ...[abcd]
      \     /        ==>
       \-x-/ 

> +RETURN VALUES
> +-------------
> +* any other value on error, e.g.
> +  running git-sequencer on a bare repository.

Don't you enumerate those return values?

-- 
Jakub Narebski
Poland
ShadeHawk on #git

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: git sequencer prototype
  2008-07-01  8:51 ` git sequencer prototype Junio C Hamano
@ 2008-07-01 14:53   ` Stephan Beyer
  0 siblings, 0 replies; 52+ messages in thread
From: Stephan Beyer @ 2008-07-01 14:53 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Johannes Schindelin

Hi,

> > The migration patches are a little hard to code-review in the diff-form,
> > but feel free to apply, test, and then look at the code ;)
> 
> Heh, these three typically come in different order.  Look at the log
> message and docs to see if the design is sound, then look at the code, and
> if things overall look Ok, then finally apply and test.

Hmm, I usually prefer testing first (on such large patches), because I think
I value things different after I've tested them.

> I unfortunately ran out of time tonight and will look at them probably
> Thursday evening.

Don't hurry.

> It seems that another GSoC topic is finally nearing
> completion and I can shift my attention to other topics now.

Yes, the builtin merge should perhaps get some more priority first.

> By the way Christian is not CC'ed?

Shame on me, I forgot to Cc my mentors ;-)

Regards,
  Stephan

-- 
Stephan Beyer <s-beyer@gmx.net>, PGP 0x6EDDD207FCC5040F

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC/PATCH 2/4] Add git-sequencer prototype documentation
  2008-07-01 13:02     ` [RFC/PATCH 2/4] Add git-sequencer prototype documentation Jakub Narebski
@ 2008-07-01 16:03       ` Stephan Beyer
  2008-07-01 18:04         ` Jakub Narebski
  0 siblings, 1 reply; 52+ messages in thread
From: Stephan Beyer @ 2008-07-01 16:03 UTC (permalink / raw)
  To: Jakub Narebski; +Cc: git, Junio C Hamano, Johannes Schindelin

Hi,

On Tue, Jul 01, 2008 at 06:02:54AM -0700,
Jakub Narebski <jnareb@gmail.com> wrote:
> > +git-sequencer will usually be called by another git porcelain, like
> > +linkgit:git-am[1] or linkgit:git-rebase[1].
> 
> I hope that it could be also used by git-cherry-pick and git-revert,
> so it would be possible to pick more than one commit...

Right, you've already mentioned it several times. ;-)
But I haven't included it currently, because git-sequencer currently *uses*
cherry-pick/revert to pick/revert commits, and I think this can lead to
some confusion.
And, I think there also has to be some discussion, e.g.
if even the single cherry-pick/revert uses sequencer, then the behavior
changes for the user: currently you have to "git commit" after a
conflict on cherry-pick. When it uses sequencer, you have to
git-sequencer --continue (or --skip or --abort) ;)

So I'm only concentrating on rebase and am users until the builtin sequencer
is in good shape.

> > +After resolving the conflict manually and updating the index with the
> > +desired resolution, you can continue the sequencing process with
> > +
> > +    git sequencer --continue
> 
> Does it run pre-commit hooks, for example checking for merge markers
> (but also whitespace errors), and can those checks be disabled?

It only commits (git commit) staged changes (if the working tree is
clean) and the pre-commit hooks are bypassed (as in rebase-i).

> > +-B::
> > +--batch::
> > +	Run in batch mode. If unexpected user intervention is needed
> > +	(e.g. a conflict or the need to run an editor), git-sequencer fails.
> > ++
> > +Note that the sanity check fails, if you use this option
> > +and an instruction like `edit` or `pause`.
> 
> s/.$/ is in the TODO file./

Agreed.

diff --git a/Documentation/git-sequencer.txt b/Documentation/git-sequencer.txt
index e0c6410..1a9eda4 100644
--- a/Documentation/git-sequencer.txt
+++ b/Documentation/git-sequencer.txt
@@ -62,7 +62,7 @@ OPTIONS
 	(e.g. a conflict or the need to run an editor), git-sequencer fails.
 +
 Note that the sanity check fails, if you use this option
-and an instruction like `edit` or `pause`.
+and an instruction like `edit` or `pause` is in the TODO file.
 
 --onto=<base>::
 	Checkout given commit or branch before sequencing.
##

> > +--edit::
> > +	Invoke editor to edit the undone rest of the TODO file.
> 
> Does it mean that editor will be invoked with only _unprocessed_
> part of the TODO file?  It looks like it, but it might be not
> obvious to some.

Thanks, this sounds better.

@@ -83,7 +83,7 @@ and an instruction like `edit` or `pause`.
 	operations can be done to change that status.
 
 --edit::
-	Invoke editor to edit the undone rest of the TODO file.
+	Invoke editor to edit the unprocessed part of the TODO file.
 +
 The file is syntax- and sanity-checked afterwards, so that you can
 safely run `git sequencer --skip` or `--continue` after editing.
##

> > +merge [options] <commit-ish1> <commit-ish2> ... <commit-ishN>::
> > +	Merge commits into HEAD.
> 
> Nice.
> 
> "HEAD" mean last picked up / created commit, isn't it?

Right. This is used throughout the document...
I thought it is clear and better to use than always describing around it
by "last created commit".
But currently I'm unsure about it :)

> > ++
> > +A commit can also be given by a mark, if prefixed with a colon.
> 
> "You can refer to commit by mark", perhaps?

Hm, is "by" the right preposition?

@@ -150,7 +150,7 @@ The set marks are removed after the sequencer has completed.
 merge [options] <commit-ish1> <commit-ish2> ... <commit-ishN>::
 	Merge commits into HEAD.
 +
-A commit can also be given by a mark, if prefixed with a colon.
+You can refer to a commit by a mark.
 +
 If you do not provide a commit message (using `-F`, `-m`, `-C`, `-M`,
 or `--standard`), an editor will be invoked.
##

> > +	--standard;;
> > +		Generates a commit message like 'Merge ... into HEAD'.
> > +		See also linkgit:git-fmt-merge-msg[1].
> 
> Is it short for `--standard-message`?

Do you think it should?
Semantically it is less ambiguous, right, but
as there is not short option, I think we should not make
the long options too long...

Perhaps --std-msg?

> > +pick [options] <commit>::
> > +	Pick (see linkgit:git-cherry-pick[1]) a commit.
> > +	Sequencer will pause on conflicts.
> > ++
> > +See the following list and 'GENERAL OPTIONS' for values of `options`:
> > +
> > +	-R;;
> > +	--reverse;;
> > +		Revert the changes introduced by pick <commit>.
> > +
> > +	--mainline=<n>;;
> > +		Allows you to pick merge commits by specifying the
> > +		parent number (beginning from 1) to let sequencer
> > +		replay the changes relative to the specified parent.
> > +		+
> > +This option does not work together with `-R`.
> 
> Why?  I would have thought that it would work...

git-revert has no --mainline option.
When sequencer won't use cherry-pick/revert anymore, we can add that.

> > +patch [options] <file>::
> [...]
> > +	-*;;
> > +		Any other dash-prefixed option is passed to
> > +		linkgit:git-apply[1].
> > +		This is especially useful for flags like
> > +		`--reverse`, `-C<n>`, `-p<n>` or `--whitespace=<action>`.
> 
> Do all options make sense?  What about `--index' and `--cached',
> or about different no-apply options?

No, not all make sense and in fact the "any other dash-prefixed option"
was true some weeks ago, but now it's a lie. Now it is just a definite
subset and -C was renamed to --context due a conflict with the general
option -C.

This is better:    (Lot of text copy&pasted from git-apply.txt)

@@ -208,11 +208,50 @@ See the following list and 'GENERAL OPTIONS' for values of `options`:
 	-n;;
 		Pass `-n` flag to `git-mailinfo` (see linkgit:git-mailinfo[1]).
 
-	-*;;
-		Any other dash-prefixed option is passed to
-		linkgit:git-apply[1].
-		This is especially useful for flags like
-		`--reverse`, `-C<n>`, `-p<n>` or `--whitespace=<action>`.
+	--exclude=<path-pattern>;;
+		Do not apply changes to files matching the given path pattern.
+		This can be useful when importing patchsets, where you want to
+		exclude certain files or directories.
+
+	-R;;
+	--reverse;;
+		Apply the patch in reverse.
+
+	--no-add;;
+		When applying a patch, ignore additions made by the
+		patch.  This can be used to extract the common part between
+		two files by first running `diff` on them and applying
+		the result with this option, which would apply the
+		deletion part but not addition part.
+
+	--whitespace=<action>;;
+		Specify behavior on whitespace errors.
+		See linkgit:git-apply[1] for a detailed description.
+
+	--context=<n>;;
+		Ensure at least <n> lines of surrounding context match before
+		and after each change.  When fewer lines of surrounding
+		context exist they all must match.  By default no context is
+		ever ignored.
+
+	--inaccurate-eof;;
+		Under certain circumstances, some versions of diff do not
+		correctly detect a missing new-line at the end of the file.
+		As a result, patches created by such diff programs do not
+		record incomplete lines correctly.
+		This option adds support for applying such patches by
+		working around this bug.
+
+	-p<n>;;
+		Remove <n> leading slashes from traditional diff paths.
+		The default is 1.
+
+	--unidiff-zero;;
+		By default, linkgit:git-apply[1] expects that the patch being
+		applied is a unified diff with at least one line of context.
+		This provides good safety measures, but breaks down when
+		applying a diff generated with --unified=0. To bypass these
+		checks use '--unidiff-zero'.
 
 
 pause::
##

> > +ref <ref>::
> > +	Set ref `<ref>` to the current HEAD, see also
> > +	linkgit:git-update-ref[1].
> 
> So this functions like "git reset --soft <ref>", isn't it?

No. Why do you think that? `ref` is set, and not HEAD.
I think the description makes that clear.

> > +squash [options] --from <mark>::
> > +	Squash all commits from the given mark into one commit.
> > +	There must not be any `merge` instructions between the
> > +	`mark` instruction and this `squash --from` instruction.
> 
> Can you use <commit> instead of <mark> here?

I wanted to make it clear that you can only specify a mark:
a commit that has been somehow used in this sequencer session.

Or have you meant that you think it is a bad restriction to only
allow marks and no commits?
I think this is a useful thing, because it's user-safe and I
cannot imagine a case where you want to give a sha1 or a
tag or branch or something.

Here an example why it is useful for user-editing:

(on commit f00babe)
	mark :1
	pick badcebab
	patch foo
	pick dadadada
	squash -m "Foo the cebab" --signoff --from :1

This squashes all between the mark and the squash into one commit,
on top of f00babe.

If we allow commits, the user could think that

	pick badcebab
	patch foo
	pick dadadada
	squash -m "Foo the cebab" --signoff --from badcebab

does the same, but this leads to:
 f00babe, picked-badcebab, squash-of-foo-and-dadadada

So
	squash -m "Foo the cebab" --signoff --from f00babe
would be correct.  But this is unintuitive.
So it's safer if we restrict this to marks.

> > +	--include-merges;;
> > +		Sanity check does not fail if you have merges
> > +		between HEAD and <mark>.
> 
> How do you squash merges?  Creating merge with an union of parents,
> excluding commits which got squashed?

My squashes are realized using git reset --soft ... and then commit.
I think this makes only sense when there are no merges in between,
so I added the check, but if someone wants to squash merges, he should
be able to do it.
To somehow answer your question: I do not care what the result is,
because I do not know what the result "should be".
IIRC the "other" parents of the merge commit are not kept, so it seems
that you have a commit that contains the changes after the merge, but
not the meta information like the right parents.

> It means
> 
>   ...a---b---c---d         ...[abcd]
>             /        ==>        /
>       ...x-/               ..x-/
> 
> but
> 
>   ...a---b---c---d          ...[abcd]
>       \     /        ==>
>        \-x-/ 
> 

I think it will be ...[abcdx].

But as I said, "I don't care". And the help description shows that.

> > +RETURN VALUES
> > +-------------
> > +* any other value on error, e.g.
> > +  running git-sequencer on a bare repository.
> 
> Don't you enumerate those return values?

In one of the very first spec versions, it was value 1, but:

	$ grep -A 3 ^die git-sh-setup
	die() {
		echo >&2 "$@"
		exit 1
	}

	$ grep -m 1 -A 4 die_builtin usage.c
	static NORETURN void die_builtin(const char *err, va_list params)
	{
		report("fatal: ", err, params);
		exit(128);
	}


Regards,
  Stephan

-- 
Stephan Beyer <s-beyer@gmx.net>, PGP 0x6EDDD207FCC5040F

^ permalink raw reply related	[flat|nested] 52+ messages in thread

* Re: [RFC/PATCH 2/4] Add git-sequencer prototype documentation
  2008-07-01 16:03       ` Stephan Beyer
@ 2008-07-01 18:04         ` Jakub Narebski
  2008-07-01 19:50           ` Stephan Beyer
  0 siblings, 1 reply; 52+ messages in thread
From: Jakub Narebski @ 2008-07-01 18:04 UTC (permalink / raw)
  To: Stephan Beyer; +Cc: git, Junio C Hamano, Johannes Schindelin

Hi,

On Tue, 1 Jul 2008, Stephan Beyer wrote:
> On Tue, Jul 01, 2008 at 06:02:54AM -0700,
> Jakub Narebski <jnareb@gmail.com> wrote:
>> Stephan Beyer wrote:
>>>
>>> +git-sequencer will usually be called by another git porcelain, like
>>> +linkgit:git-am[1] or linkgit:git-rebase[1].
>> 
>> I hope that it could be also used by git-cherry-pick and git-revert,
>> so it would be possible to pick more than one commit...
> 
> Right, you've already mentioned it several times. ;-)

:blush: I'm sorry about that...

> But I haven't included it currently, because git-sequencer currently *uses*
> cherry-pick/revert to pick/revert commits, and I think this can lead to
> some confusion.
[...]
> So I'm only concentrating on rebase and am users until the builtin sequencer
> is in good shape.

Ah, I understand.  It _is_ quite reasonable.

>>> +merge [options] <commit-ish1> <commit-ish2> ... <commit-ishN>::
>>> +	Merge commits into HEAD.
>> 
>> Nice.
>> 
>> "HEAD" mean last picked up / created commit, isn't it?
> 
> Right. This is used throughout the document...
> I thought it is clear and better to use than always describing around it
> by "last created commit".

O.K.

>>> +ref <ref>::
>>> +	Set ref `<ref>` to the current HEAD, see also
>>> +	linkgit:git-update-ref[1].
>> 
>> So this functions like "git reset --soft <ref>", isn't it?
> 
> No. Why do you think that? `ref` is set, and not HEAD.
> I think the description makes that clear.

Ah.  I'm sorry.  So it is like "git branch <ref>", isn't it?

What is important is: does it update reflog (correctly)?
 
>>> +squash [options] --from <mark>::
>>> +	Squash all commits from the given mark into one commit.
>>> +	There must not be any `merge` instructions between the
>>> +	`mark` instruction and this `squash --from` instruction.
>> 
>> Can you use <commit> instead of <mark> here?
> 
> I wanted to make it clear that you can only specify a mark:
> a commit that has been somehow used in this sequencer session.
> 
> Or have you meant that you think it is a bad restriction to only
> allow marks and no commits?
> I think this is a useful thing, because it's user-safe and I
> cannot imagine a case where you want to give a sha1 or a
> tag or branch or something.
> 
> Here an example why it is useful for user-editing:
> 
> (on commit f00babe)
> 	mark :1
> 	pick badcebab
> 	patch foo
> 	pick dadadada
> 	squash -m "Foo the cebab" --signoff --from :1
> 
> This squashes all between the mark and the squash into one commit,
> on top of f00babe.
 
Ah, so squash --from <mark> picks up everything since "mark <mark>",
but does not include marked commit!  Clever!  In this case allowing
only <mark> is a good idea, IMVHO.
 
>>> +	--include-merges;;
>>> +		Sanity check does not fail if you have merges
>>> +		between HEAD and <mark>.
>> 
>> How do you squash merges?  Creating merge with an union of parents,
>> excluding commits which got squashed?
> 
> My squashes are realized using git reset --soft ... and then commit.
> I think this makes only sense when there are no merges in between,
> so I added the check, but if someone wants to squash merges, he should
> be able to do it.
>
> To somehow answer your question: I do not care what the result is,
> because I do not know what the result "should be".

O.K.  I guess that is something left for later, especially that
forbidding merges in squashed commit is default (and you can always
do without merges, I think).

>>> +RETURN VALUES
>>> +-------------
>>> +* any other value on error, e.g.
>>> +  running git-sequencer on a bare repository.
>> 
>> Don't you enumerate those return values?
> 
> In one of the very first spec versions, it was value 1, but:
[...]
> 	$ grep -m 1 -A 4 die_builtin usage.c
> 	static NORETURN void die_builtin(const char *err, va_list params)
> 	{
> 		report("fatal: ", err, params);
> 		exit(128);
> 	}

Perhaps stating that it returns 128 on fatal error, or rewording
that any other return value means some fatal error (does it?)
would be better.  But that are details...

-- 
Jakub Narebski
Poland

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC/PATCH 2/4] Add git-sequencer prototype documentation
  2008-07-01 18:04         ` Jakub Narebski
@ 2008-07-01 19:50           ` Stephan Beyer
  2008-07-02  0:39             ` Jakub Narebski
  0 siblings, 1 reply; 52+ messages in thread
From: Stephan Beyer @ 2008-07-01 19:50 UTC (permalink / raw)
  To: Jakub Narebski; +Cc: git, Junio C Hamano, Johannes Schindelin

Hi,

On Tue, Jul 01, 2008 at 08:04:10PM +0200, Jakub Narebski wrote:
> On Tue, 1 Jul 2008, Stephan Beyer wrote:
> > On Tue, Jul 01, 2008 at 06:02:54AM -0700,
> > Jakub Narebski <jnareb@gmail.com> wrote:
> >> Stephan Beyer wrote:
> >>> +merge [options] <commit-ish1> <commit-ish2> ... <commit-ishN>::
> >>> +	Merge commits into HEAD.
> >> 
> >> Nice.
> >> 
> >> "HEAD" mean last picked up / created commit, isn't it?
> > 
> > Right. This is used throughout the document...
> > I thought it is clear and better to use than always describing around it
> > by "last created commit".
> 
> O.K.

I don't know.  If something like "last created commit" is better,
I have no problem to change it :)

> >>> +ref <ref>::
> >>> +	Set ref `<ref>` to the current HEAD, see also
> >>> +	linkgit:git-update-ref[1].
> >> 
> >> So this functions like "git reset --soft <ref>", isn't it?
> > 
> > No. Why do you think that? `ref` is set, and not HEAD.
> > I think the description makes that clear.
> 
> Ah.  I'm sorry.  So it is like "git branch <ref>", isn't it?

No. It is "git-update-ref <ref> HEAD".

> What is important is: does it update reflog (correctly)?

Good question. The reflog is another mistery to me that I haven't really
cared about, because I haven't used it yet myself.
At least the reflog test cases for git rebase -i in the test suite pass.
(Of course, this is only a weak indication that it works as it should.)

> >>> +squash [options] --from <mark>::
[...]
> > Here an example why it is useful for user-editing:
> > 
> > (on commit f00babe)
> > 	mark :1
> > 	pick badcebab
> > 	patch foo
> > 	pick dadadada
> > 	squash -m "Foo the cebab" --signoff --from :1
> > 
> > This squashes all between the mark and the squash into one commit,
> > on top of f00babe.
>  
> Ah, so squash --from <mark> picks up everything since "mark <mark>",
> but does not include marked commit!  Clever!  In this case allowing
> only <mark> is a good idea, IMVHO.

Good, thanks :)

> >>> +	--include-merges;;
> >>> +		Sanity check does not fail if you have merges
> >>> +		between HEAD and <mark>.
> >> 
> >> How do you squash merges?  Creating merge with an union of parents,
> >> excluding commits which got squashed?
> > 
> > My squashes are realized using git reset --soft ... and then commit.
> > I think this makes only sense when there are no merges in between,
> > so I added the check, but if someone wants to squash merges, he should
> > be able to do it.
> >
> > To somehow answer your question: I do not care what the result is,
> > because I do not know what the result "should be".
> 
> O.K.  I guess that is something left for later, especially that
> forbidding merges in squashed commit is default (and you can always
> do without merges, I think).

Forbidding merges is default currently. It's just a sanity check. And
the option bypasses this check.

Regards,
  Stephan

-- 
Stephan Beyer <s-beyer@gmx.net>, PGP 0x6EDDD207FCC5040F

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC/PATCH 2/4] Add git-sequencer prototype documentation
  2008-07-01 19:50           ` Stephan Beyer
@ 2008-07-02  0:39             ` Jakub Narebski
  2008-07-02  1:20               ` Junio C Hamano
  0 siblings, 1 reply; 52+ messages in thread
From: Jakub Narebski @ 2008-07-02  0:39 UTC (permalink / raw)
  To: Stephan Beyer; +Cc: git, Junio C Hamano, Johannes Schindelin

Hi!

On Tue, 1 July 2008, Stephan Beyer wrote:
> On Tue, Jul 01, 2008 at 08:04:10PM +0200, Jakub Narebski wrote:
>> On Tue, 1 Jul 2008, Stephan Beyer wrote:
>>> On Tue, Jul 01, 2008 at 06:02:54AM -0700,
>>> Jakub Narebski <jnareb@gmail.com> wrote:
>>>> Stephan Beyer wrote:
[...]
>>>>> +ref <ref>::
>>>>> +	Set ref `<ref>` to the current HEAD, see also
>>>>> +	linkgit:git-update-ref[1].
>>>> 
>>>> So this functions like "git reset --soft <ref>", isn't it?
>>> 
>>> No. Why do you think that? `ref` is set, and not HEAD.
>>> I think the description makes that clear.
>> 
>> Ah.  I'm sorry.  So it is like "git branch <ref>", isn't it?

Actually I meant "git branch -f <ref>" here.  But I forgot that it
can be used to create lightweight tags, too.

> No. It is "git-update-ref <ref> HEAD".

So what do you envision would this be used for?

>> What is important is: does it update reflog (correctly)?
> 
> Good question. The reflog is another mistery to me that I haven't really
> cared about, because I haven't used it yet myself.
> At least the reflog test cases for git rebase -i in the test suite pass.
> (Of course, this is only a weak indication that it works as it should.)

You have 'branch' (<onto>) reflog, HEAD reflog, and reflog for '<ref>'.
While 'branch' reflog should I think record only start and end of
sequencer, or rather git-am or git-rebase, I'm not sure what to do about
HEAD reflog...  We should fill reflog for <ref>, to be able to revert
easily.

>>>>> +squash [options] --from <mark>::
> [...]
>>> Here an example why it is useful for user-editing:
>>> 
>>> (on commit f00babe)
>>> 	mark :1
>>> 	pick badcebab
>>> 	patch foo
>>> 	pick dadadada
>>> 	squash -m "Foo the cebab" --signoff --from :1
>>> 
>>> This squashes all between the mark and the squash into one commit,
>>> on top of f00babe.
>>  
>> Ah, so squash --from <mark> picks up everything since "mark <mark>",
>> but does not include marked commit!  Clever!  In this case allowing
>> only <mark> is a good idea, IMVHO.
> 
> Good, thanks :)

Although I guess having example would make it clear from the go...

Good work!
-- 
Jakub Narebski
Poland

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC/PATCH 2/4] Add git-sequencer prototype documentation
  2008-07-02  0:39             ` Jakub Narebski
@ 2008-07-02  1:20               ` Junio C Hamano
  2008-07-02  3:01                 ` Stephan Beyer
  0 siblings, 1 reply; 52+ messages in thread
From: Junio C Hamano @ 2008-07-02  1:20 UTC (permalink / raw)
  To: Jakub Narebski; +Cc: Stephan Beyer, git, Johannes Schindelin

Jakub Narebski <jnareb@gmail.com> writes:

>> No. It is "git-update-ref <ref> HEAD".
>
> So what do you envision would this be used for?

A simple answer and a more elaborate one.

 * It is the final step of "git rebase" which detaches HEAD while it
   operates these days.

 * You can drive sequencer backend from a front-end that rewrite history
   while rewriting tags, like filter-branch does.

>>> What is important is: does it update reflog (correctly)?

That is not very important question, as reflog updates would happen as
long as you use update-ref automatically.

Much more important question you did not ask is how it would interact with
"sequencer --abort".  Ideally it should rewind the ref update (and without
relying on the user having reflog on that ref).

I however personally feel that this "ref" thing is being a bit too
ambitious.

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC/PATCH 2/4] Add git-sequencer prototype documentation
  2008-07-02  1:20               ` Junio C Hamano
@ 2008-07-02  3:01                 ` Stephan Beyer
  0 siblings, 0 replies; 52+ messages in thread
From: Stephan Beyer @ 2008-07-02  3:01 UTC (permalink / raw)
  To: Jakub Narebski, Junio C Hamano; +Cc: git, Johannes Schindelin

On Wed, Jul 02, 2008 at 02:39:40AM +0200, Jakub Narebski wrote:
> On Tue, 1 July 2008, Stephan Beyer wrote:
> > On Tue, Jul 01, 2008 at 08:04:10PM +0200, Jakub Narebski wrote:
[...]
> > No. It is "git-update-ref <ref> HEAD".
> 
> So what do you envision would this be used for?

I think if I had not started on top js/rebase-i-sequencer, I would have
not necessarily added "ref" (or "tag", as it was before).
But js/rebase-i-sequencer introduced a nice feature to
git-rebase-i:
 -t,--preserve-tags ... "update tags to the new commit object"

I think this is worth the feature ;-)

On Tue, Jul 01, 2008 at 06:20:15PM -0700, Junio C Hamano wrote:
> Jakub Narebski <jnareb@gmail.com> writes:
> 
> >> No. It is "git-update-ref <ref> HEAD".
> >
> > So what do you envision would this be used for?
> 
> A simple answer and a more elaborate one.
> 
>  * It is the final step of "git rebase" which detaches HEAD while it
>    operates these days.

Ha ;)
That's a quite obvious truth that I have not implemented.
Btw: sequencer does the detaching/attaching when using "--onto <base>"
and <base> is a branch.
IIRC there was a reason that I also kept the detaching/attaching of
git-rebase-i itself but I can't remember. (I think I have to look
over the rebase-i code more carefully later...)

>  * You can drive sequencer backend from a front-end that rewrite history
>    while rewriting tags, like filter-branch does.

Yes.

> >>> What is important is: does it update reflog (correctly)?
> 
> That is not very important question, as reflog updates would happen as
> long as you use update-ref automatically.
> 
> Much more important question you did not ask is how it would interact with
> "sequencer --abort".  Ideally it should rewind the ref update (and without
> relying on the user having reflog on that ref).

Yes, you asked the question in the RFC thread about the spec.

"Rewind the ref update" means:
 - delete new generated refs
 - update overwritten refs to their old commits
So if I keep a list containing <ref> [<oldcommit>], that behavior
is easily reached. But this is not suited for the shell prototype ;)


Btw, also the --skip case can be arguable:

	pick fa1afe1
	pick cedefe1	# conflict, will be "--skip"ped
	ref refs/tags/v1.6.2

Here the question can be, if the "ref" should be skipped, too.
But I think the just-don't-care-strategy is a good one, so
that fa1afe1' will be tagged.

> I however personally feel that this "ref" thing is being a bit too
> ambitious.

I agree that it is no must-have feature but a nice-to-have feature ;)
Especially for the --preserve-tags feature.


Jakub wrote:
> >>>>> +squash [options] --from <mark>::
> > [...]
> >>> Here an example why it is useful for user-editing:
> >>> 
> >>> (on commit f00babe)
> >>> 	mark :1
> >>> 	pick badcebab
> >>> 	patch foo
> >>> 	pick dadadada
> >>> 	squash -m "Foo the cebab" --signoff --from :1
> >>> 
> >>> This squashes all between the mark and the squash into one commit,
> >>> on top of f00babe.
> >>  
> >> Ah, so squash --from <mark> picks up everything since "mark <mark>",
> >> but does not include marked commit!  Clever!  In this case allowing
> >> only <mark> is a good idea, IMVHO.
> > 
> > Good, thanks :)
> 
> Although I guess having example would make it clear from the go...

Yes, I see that the EXAMPLES section is really important and the
squash --from is important therein.

Regards,
  Stephan

-- 
Stephan Beyer <s-beyer@gmx.net>, PGP 0x6EDDD207FCC5040F

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC/PATCH 1/4] Add git-sequencer shell prototype
  2008-07-01  2:38 ` [RFC/PATCH 1/4] Add git-sequencer shell prototype Stephan Beyer
  2008-07-01  2:38   ` [RFC/PATCH 2/4] Add git-sequencer prototype documentation Stephan Beyer
@ 2008-07-03  1:45   ` Junio C Hamano
  2008-07-03 11:03     ` Johannes Schindelin
  2008-07-03 13:10   ` Jakub Narebski
  2008-07-05 16:58   ` [PATCH v2 " Stephan Beyer
  3 siblings, 1 reply; 52+ messages in thread
From: Junio C Hamano @ 2008-07-03  1:45 UTC (permalink / raw)
  To: Stephan Beyer; +Cc: git, Johannes Schindelin, Daniel Barkalow, Christian Couder

Stephan Beyer <s-beyer@gmx.net> writes:

> git sequencer is planned as a backend for user scripts
> that execute a sequence of git instructions and perhaps
> need manual intervention, for example git-rebase or git-am.

...
> +output () {
> +	case "$VERBOSE" in
> +	0)
> +		"$@" >/dev/null
> +		;;
> +	1)
> +		output=$("$@" 2>&1 )
> +		status=$?
> +		test $status -ne 0 && printf '%s\n' "$output"
> +		return $status
> +		;;
> +	2)
> +		"$@"
> +		;;
> +	esac
> +}

Perhaps misnamed?  This feels more like "do" or "perform" or "run".

> +require_clean_work_tree () {
> +	# test if working tree is dirty
> +	git rev-parse --verify HEAD >/dev/null &&
> +	git update-index --ignore-submodules --refresh &&
> +	git diff-files --quiet --ignore-submodules &&
> +	git diff-index --cached --quiet HEAD --ignore-submodules -- ||
> +	die 'Working tree is dirty'
> +}

When is it necessary to ignore submodules and why?  Are there cases where
submodules should not be ignored?

> +LAST_COUNT=
> +mark_action_done () {
> +	sed -e 1q <"$TODO" >>"$DONE"
> +	sed -e 1d <"$TODO" >"$TODO.new"
> +	mv -f "$TODO.new" "$TODO"
> +	if test "$VERBOSE" -gt 0
> +	then
> +		count=$(grep -c '^[^#]' <"$DONE")
> +		total=$(expr "$count" + "$(grep -c '^[^#]' <"$TODO")")

Here we are not counting lines that are comments as insns (I am not
complaining; just making a mental note).

> +		if test "$LAST_COUNT" != "$count"
> +		then
> +			LAST_COUNT="$count"
> +			test "$VERBOSE" -lt 1 ||
> +				printf 'Sequencing (%d/%d)\r' "$count" "$total"
> +			test "$VERBOSE" -lt 2 || echo
> +		fi
> +	fi
> +}
> +
> +# Generate message, patch and author script files
> +make_patch () {
> +	parent_sha1=$(git rev-parse --verify "$1"^) ||
> +		die "Cannot get patch for $1^"
> +	git diff-tree -p "$parent_sha1..$1" >"$PATCH"

Could there be a case where we need/want to deal with a root commit
without parents?

> +	test -f "$MSG" ||
> +		commit_message "$1" >"$MSG"
> +	test -f "$AUTHOR_SCRIPT" ||
> +		get_author_ident_from_commit "$1" >"$AUTHOR_SCRIPT"
> +}
> +
> +# Generate a patch and die with "conflict" status code
> +die_with_patch () {
> +	make_patch "$1"
> +	git rerere
> +	die_to_continue "$2"
> +}
> +
> +restore () {
> +	git rerere clear
> +
> +	HEADNAME=$(cat "$SEQ_DIR/head-name")
> +	HEAD=$(cat "$SEQ_DIR/head")

Perhaps

	read HEADNAME <"$SEQ_DIR/head-name"

provided if these values are $IFS safe?

> +	case $HEADNAME in
> +	refs/*)
> +		git symbolic-ref HEAD "$HEADNAME"
> +		;;
> +	esac &&
> +	output git reset --hard "$HEAD"
> +}
> +
> +has_action () {
> +	grep '^[^#]' "$1" >/dev/null
> +}
> +
> +# Check if text file $1 contains a commit message
> +has_message () {
> +	test -n "$(sed -n -e '/^Signed-off-by:/d;/^[^#]/p' <"$1")"
> +}

Makes one wonder if we would want to special case other kinds like
"Acked-by:" as well...

> +# Usage: pick_one (cherry-pick|revert) [-*|--edit] sha1
> +pick_one () {
> +	what="$1"
> +	# we just assume that this is either cherry-pick or revert
> +	shift
> +
> +	# check for fast-forward if no options are given
> +	if expr "x$1" : 'x[^-]' >/dev/null
> +	then
> +		test "$(git rev-parse --verify "$1^")" = \
> +			"$(git rev-parse --verify HEAD)" &&
> +			output git reset --hard "$1" &&
> +			return
> +	fi
> +	test "$1" != '--edit' -a "$what" = 'revert' &&
> +		what='revert --no-edit'

This looks somewhat wrong.

When the history looks like ---A---B and we are at A, cherry-picking B can
be optimized to just advancing to B, but that optimization has a slight
difference (or two) in the semantics.

 (1) The committer information would not record the user and time of the
     sequencer operation, which actually may be a good thing.

 (2) When $what is revert, this codepath shouldn't be exercised, should it?

 (3) If B is a merge, even if $what is pick, this codepath shouldn't be
     exercised, should it?

As to the syntax I tend to prefer

	case "$1" in
        -*)	... do option thing ... ;;
        *)	... do other thing... ;;
        esac

So how about...

	case "$what,$1" in
        revert,--edit)
        	what='revert --no-edit' ;;
        revert,* | cherry-pick,-* )
        	;;
        *)
		if ! git rev-parse --verify "$1^2" &&
	                test "$(git rev-parse --verify "$1^") = \
                	"$(git rev-parse --verify HEAD)"
		then
                	output git reset --hard "$1"
			return
		fi
		;;
	esac

> +make_squash_message () {
> +	if test -f "$squash_msg"
> +	then
> +		count=$(($(sed -n -e 's/^# This is [^0-9]*\([1-9][0-9]*\).*/\1/p' \
> +			<"$squash_msg" | sed -n -e '$p')+1))
> +		echo "# This is a combination of $count commits."
> +		sed -e '1d' -e '2,/^./{
> +			/^$/d
> +		}' <"$squash_msg"
> +	else
> +		count=2
> +		echo '# This is a combination of 2 commits.'
> +		echo '# The first commit message is:'
> +		echo
> +		commit_message HEAD
> +	fi
> +	echo
> +	echo "# This is the $(nth_string "$count") commit message:"
> +	echo
> +	commit_message "$1"
> +}
> +
> +make_squash_message_multiple () {
> +	echo '# This is a dummy to get the 0.' >"$squash_msg"
> +	for cur_sha1 in $(git rev-list --reverse "$sha1..HEAD")
> +	do
> +		make_squash_message "$cur_sha1" >"$MSG"
> +		cp "$MSG" "$squash_msg"
> +	done
> +}

Hmm, I know this is how rebase-i is written, but we should be able to do
better than writing and flipping temporary times N times, shouldn't we?

> +peek_next_command () {
> +	sed -n -e '1s/ .*$//p' <"$TODO"
> +}

... which could respond "the next command is '#' (comment)", so we are
actively counting a comment as a step here.  Does this contradict with the
mental note we made earlier, and if so, does the discrepancy hurt us
somewhere in this program?

> +# If $1 is a mark, make a ref from it; otherwise keep it
> +mark_to_ref () {
> +	arg="$1"
> +	ref=$(expr "x$arg" : 'x:0*\([0-9][0-9]*\)$')

You might want to leave comments to describe constraints that led to this
slightly awkward regexp:

 * :0 is allowed
 * :01 is the same as :1

> +strategy_check () {
> +	case "$1" in
> +	resolve|recursive|octopus|ours|subtree|theirs)
> +		return
> +		;;
> +	esac
> +	todo_warn "Strategy '$1' not known."
> +}

Hmm.  Do we need to maintain list of available strategies here and then in
git-merge separately?

> +### Author script functions
> +
> +clean_author_script () {
> +	cat "$ORIG_AUTHOR_SCRIPT" >"$AUTHOR_SCRIPT"
> +}
> +
> +# Take "Name <e-mail>" in stdin and outputs author script
> +make_author_script_from_string () {
> +	sed -e 's/^\(.*\) <\(.*\)>.*$/GIT_AUTHOR_NAME="\1"\
> +GIT_AUTHOR_EMAIL="\2"\
> +GIT_AUTHOR_DATE=/'
> +}

If you are going to "."-source or eval the output from this, you would
need to quote the values a lot more robustly, wouldn't you?  Is this safe
against shell metacharacters in names, mails and/or space between unixtime
and the timezone information?

> +	if test -z "$AUTHOR"
> +	then
> +		sed -n -e '
> +			s/^Author: \(.*\)$/GIT_AUTHOR_NAME="\1"/p;
> +			s/^Email: \(.*\)$/GIT_AUTHOR_EMAIL="\1"/p;
> +			s/^Date: \(.*\)$/GIT_AUTHOR_DATE="\1"/p
> +		' <"$infofile" >>"$AUTHOR_SCRIPT"

The same comment on quoting applies here, I think.

> +		# If sed's result is empty, we keep the original
> +		# author script by appending.
> +	fi
> ...
> +	failed=
> +	with_author git apply $apply_opts --index "$PATCH" || failed=t
> +
> +	if test -n "$failed" -a -n "$threeway" && (with_author fallback_3way)
> +	then
> +		# Applying the patch to an earlier tree and merging the
> +		# result may have produced the same tree as ours.
> +		git diff-index --quiet --cached HEAD -- && {
> +			echo 'No changes -- Patch already applied.'
> +			return 0
> +			# XXX: do we want that?
> +		}
> +		# clear apply_status -- we have successfully merged.
> +		failed=
> +	fi
> +
> +	if test -n "$failed"
> +	then
> +		# XXX: This is just a stupid hack:
> +		with_author git apply $apply_opts --reject --index "$PATCH"

Please don't do this without being asked, if you are planning to use this
in "am" when 3-way fallback was not asked.  It _may_ make sense to give an
option to the users to ask for .rej if they prefer to work that way better
than working with 3-way merge fallback, but doing this without being asked
is not acceptable.

> +		die_to_continue 'Patch failed. See the .rej files.'
> +		# XXX: We actually needed a git-apply flag that creates
> +		# conflict markers and sets the DIFF_STATUS_UNMERGED flag.

That is what -3way is all about, and this codepath is when the user did
not ask for it, isn't it?

> +# Check the "pick" instruction
> +check_pick () {
> +	revert=
> +	mainline=
> +	while test $# -gt 1
> +	do
> ...
> +	done
> +
> +	if test -n "$mainline"
> +	then
> +		test -z "$revert" ||
> +			todo_error "Cannot use $revert together with --mainline."

Why not?  If you have this...

	---A---C---D
              /
          ---B

and you are at D, you may want to undo the merge you made at C and go back
to either A or B, which essentially is same as cherry-picking diff between
C and D on top of either A or B.  Both are valid operations aren't they?

The remainder of the review will have to be in a separate message..

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC/PATCH 1/4] Add git-sequencer shell prototype
  2008-07-03  1:45   ` [RFC/PATCH 1/4] Add git-sequencer shell prototype Junio C Hamano
@ 2008-07-03 11:03     ` Johannes Schindelin
  2008-07-03 21:09       ` Stephan Beyer
                         ` (2 more replies)
  0 siblings, 3 replies; 52+ messages in thread
From: Johannes Schindelin @ 2008-07-03 11:03 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Stephan Beyer, git, Daniel Barkalow, Christian Couder

Hi,

On Wed, 2 Jul 2008, Junio C Hamano wrote:

> Stephan Beyer <s-beyer@gmx.net> writes:
> 
> > git sequencer is planned as a backend for user scripts
> > that execute a sequence of git instructions and perhaps
> > need manual intervention, for example git-rebase or git-am.
> 
> ...
> > +output () {
> > +	case "$VERBOSE" in
> > +	0)
> > +		"$@" >/dev/null
> > +		;;
> > +	1)
> > +		output=$("$@" 2>&1 )
> > +		status=$?
> > +		test $status -ne 0 && printf '%s\n' "$output"
> > +		return $status
> > +		;;
> > +	2)
> > +		"$@"
> > +		;;
> > +	esac
> > +}
> 
> Perhaps misnamed?  This feels more like "do" or "perform" or "run".

My fault.  I like "perform".

> > +require_clean_work_tree () {
> > +	# test if working tree is dirty
> > +	git rev-parse --verify HEAD >/dev/null &&
> > +	git update-index --ignore-submodules --refresh &&
> > +	git diff-files --quiet --ignore-submodules &&
> > +	git diff-index --cached --quiet HEAD --ignore-submodules -- ||
> > +	die 'Working tree is dirty'
> > +}
> 
> When is it necessary to ignore submodules and why?

Submodules are not updated by checkout.  Indeed, the _only_ Git command 
that actually changes the state of a submodule is "git submodule update".

Therefore, it is wrong to assume that rebase/am/whatever works with 
submodules as far as the working directory is concerned.  Updating 
submodules with any Git command other than "git submodule update" is a 
_pure_ index operation.

Of course, that means that if you use rebase -i's "edit" command to go 
back to a certain revision, edit that, and want to test, it is _your_ 
responsibility to make sure that the submodules are at their correct 
revision.

> Are there cases where submodules should not be ignored?

With above reasoning, it would always be wrong for sequencer to require 
the submodules to be up-to-date.

> > +LAST_COUNT=
> > +mark_action_done () {
> > +	sed -e 1q <"$TODO" >>"$DONE"
> > +	sed -e 1d <"$TODO" >"$TODO.new"
> > +	mv -f "$TODO.new" "$TODO"
> > +	if test "$VERBOSE" -gt 0
> > +	then
> > +		count=$(grep -c '^[^#]' <"$DONE")
> > +		total=$(expr "$count" + "$(grep -c '^[^#]' <"$TODO")")
> 
> Here we are not counting lines that are comments as insns (I am not
> complaining; just making a mental note).

As "count" and "total" are only used for the progress output, anything 
else would not make sense.

> > +		if test "$LAST_COUNT" != "$count"
> > +		then
> > +			LAST_COUNT="$count"
> > +			test "$VERBOSE" -lt 1 ||
> > +				printf 'Sequencing (%d/%d)\r' "$count" "$total"
> > +			test "$VERBOSE" -lt 2 || echo
> > +		fi
> > +	fi
> > +}
> > +
> > +# Generate message, patch and author script files
> > +make_patch () {
> > +	parent_sha1=$(git rev-parse --verify "$1"^) ||
> > +		die "Cannot get patch for $1^"
> > +	git diff-tree -p "$parent_sha1..$1" >"$PATCH"
> 
> Could there be a case where we need/want to deal with a root commit
> without parents?

Yes.  I had exactly that need a few days ago, where I wanted to import a 
few zips, and rebase them on top of an existing branch (which was 
generated in the same manner).

I worked around that limitation of rebase (actually, I only tried rebase 
-i, come to think of it), by rewriting the first commit object, inserting 
the "parent" line.  "fast-export > a1; vi a1; fast-import < a1" can be so 
much fun!

> > +	test -f "$MSG" ||
> > +		commit_message "$1" >"$MSG"
> > +	test -f "$AUTHOR_SCRIPT" ||
> > +		get_author_ident_from_commit "$1" >"$AUTHOR_SCRIPT"
> > +}
> > +
> > +# Generate a patch and die with "conflict" status code
> > +die_with_patch () {
> > +	make_patch "$1"
> > +	git rerere
> > +	die_to_continue "$2"
> > +}
> > +
> > +restore () {
> > +	git rerere clear
> > +
> > +	HEADNAME=$(cat "$SEQ_DIR/head-name")
> > +	HEAD=$(cat "$SEQ_DIR/head")
> 
> Perhaps
> 
> 	read HEADNAME <"$SEQ_DIR/head-name"
> 
> provided if these values are $IFS safe?

Again, my fault (as this was most likely copied from rebase -i).  All the 
files written to $DOTEST in rebase -i should be $IFS safe.

> > +	case $HEADNAME in
> > +	refs/*)
> > +		git symbolic-ref HEAD "$HEADNAME"
> > +		;;
> > +	esac &&
> > +	output git reset --hard "$HEAD"
> > +}
> > +
> > +has_action () {
> > +	grep '^[^#]' "$1" >/dev/null
> > +}
> > +
> > +# Check if text file $1 contains a commit message
> > +has_message () {
> > +	test -n "$(sed -n -e '/^Signed-off-by:/d;/^[^#]/p' <"$1")"
> > +}
> 
> Makes one wonder if we would want to special case other kinds like
> "Acked-by:" as well...

I think this just mimicks "git commit".

> > +# Usage: pick_one (cherry-pick|revert) [-*|--edit] sha1
> > +pick_one () {
> > +	what="$1"
> > +	# we just assume that this is either cherry-pick or revert
> > +	shift
> > +
> > +	# check for fast-forward if no options are given
> > +	if expr "x$1" : 'x[^-]' >/dev/null
> > +	then
> > +		test "$(git rev-parse --verify "$1^")" = \
> > +			"$(git rev-parse --verify HEAD)" &&
> > +			output git reset --hard "$1" &&
> > +			return
> > +	fi
> > +	test "$1" != '--edit' -a "$what" = 'revert' &&
> > +		what='revert --no-edit'
> 
> This looks somewhat wrong.
> 
> When the history looks like ---A---B and we are at A, cherry-picking B can
> be optimized to just advancing to B, but that optimization has a slight
> difference (or two) in the semantics.
> 
>  (1) The committer information would not record the user and time of the
>      sequencer operation, which actually may be a good thing.

This is debatable.  But I think you are correct, for all the same reasons 
why a merge can result in a fast-forward.

>  (2) When $what is revert, this codepath shouldn't be exercised, should 
>  it?

Yes.

>  (3) If B is a merge, even if $what is pick, this codepath shouldn't be
>      exercised, should it?

I think it should, again for the same reason why a merge can result in a 
fast-forward.

> > +make_squash_message () {
> > +	if test -f "$squash_msg"
> > +	then
> > +		count=$(($(sed -n -e 's/^# This is [^0-9]*\([1-9][0-9]*\).*/\1/p' \
> > +			<"$squash_msg" | sed -n -e '$p')+1))
> > +		echo "# This is a combination of $count commits."
> > +		sed -e '1d' -e '2,/^./{
> > +			/^$/d
> > +		}' <"$squash_msg"
> > +	else
> > +		count=2
> > +		echo '# This is a combination of 2 commits.'
> > +		echo '# The first commit message is:'
> > +		echo
> > +		commit_message HEAD
> > +	fi
> > +	echo
> > +	echo "# This is the $(nth_string "$count") commit message:"
> > +	echo
> > +	commit_message "$1"
> > +}
> > +
> > +make_squash_message_multiple () {
> > +	echo '# This is a dummy to get the 0.' >"$squash_msg"
> > +	for cur_sha1 in $(git rev-list --reverse "$sha1..HEAD")
> > +	do
> > +		make_squash_message "$cur_sha1" >"$MSG"
> > +		cp "$MSG" "$squash_msg"
> > +	done
> > +}
> 
> Hmm, I know this is how rebase-i is written, but we should be able to do
> better than writing and flipping temporary times N times, shouldn't we?

Right, again my fault.

> > +peek_next_command () {
> > +	sed -n -e '1s/ .*$//p' <"$TODO"
> > +}
> 
> ... which could respond "the next command is '#' (comment)", so we are
> actively counting a comment as a step here.  Does this contradict with the
> mental note we made earlier, and if so, does the discrepancy hurt us
> somewhere in this program?

Yes, this is wrong.  it must be

	sed -n -e '/^#/d' -e '1s .*$//p' < "$TODO"

> > +strategy_check () {
> > +	case "$1" in
> > +	resolve|recursive|octopus|ours|subtree|theirs)
> > +		return
> > +		;;
> > +	esac
> > +	todo_warn "Strategy '$1' not known."
> > +}
> 
> Hmm.  Do we need to maintain list of available strategies here and then in
> git-merge separately?

I'd not check in sequencer for the strategy.  Especially given that we 
want to support user-written strategies in the future.

Ciao,
Dscho

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC/PATCH 1/4] Add git-sequencer shell prototype
  2008-07-01  2:38 ` [RFC/PATCH 1/4] Add git-sequencer shell prototype Stephan Beyer
  2008-07-01  2:38   ` [RFC/PATCH 2/4] Add git-sequencer prototype documentation Stephan Beyer
  2008-07-03  1:45   ` [RFC/PATCH 1/4] Add git-sequencer shell prototype Junio C Hamano
@ 2008-07-03 13:10   ` Jakub Narebski
  2008-07-03 21:12     ` Stephan Beyer
  2008-07-05 16:58   ` [PATCH v2 " Stephan Beyer
  3 siblings, 1 reply; 52+ messages in thread
From: Jakub Narebski @ 2008-07-03 13:10 UTC (permalink / raw)
  To: Stephan Beyer; +Cc: git, Junio C Hamano, Johannes Schindelin

Stephan Beyer <s-beyer@gmx.net> writes:

> +# Generate message, patch and author script files
> +make_patch () {
> +	parent_sha1=$(git rev-parse --verify "$1"^) ||
> +		die "Cannot get patch for $1^"
> +	git diff-tree -p "$parent_sha1..$1" >"$PATCH"

First, let's not perpetuate _convenience_ calling convention of "A..B"
of git-diff* family, but use "A B" to set _endpoints_.

Second, with "A B" convention you can fairly easy deal with root
commit, changing the code to the fragment below:

+# Generate message, patch and author script files
+make_patch () {
+	parent_sha1=$(git rev-parse --verify "$1"^) ||
+		echo '--root'
+	git diff-tree -p "$parent_sha1" "$1" >"$PATCH"


BTW. what is best way of checking if given revision is parent-less?
-- 
Jakub Narebski
Poland
ShadeHawk on #git

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC/PATCH 1/4] Add git-sequencer shell prototype
  2008-07-03 11:03     ` Johannes Schindelin
@ 2008-07-03 21:09       ` Stephan Beyer
  2008-07-03 22:11         ` Junio C Hamano
  2008-07-03 23:53         ` Johannes Schindelin
  2008-07-03 22:51       ` Junio C Hamano
  2008-07-03 23:33       ` Stephan Beyer
  2 siblings, 2 replies; 52+ messages in thread
From: Stephan Beyer @ 2008-07-03 21:09 UTC (permalink / raw)
  To: Junio C Hamano, Johannes Schindelin
  Cc: git, Daniel Barkalow, Christian Couder

Hi,

On Thu, Jul 03, 2008 at 12:03:49PM +0100, Johannes Schindelin wrote:
> Hi,
> 
> On Wed, 2 Jul 2008, Junio C Hamano wrote:
> > Stephan Beyer <s-beyer@gmx.net> writes:
> > 
> > > git sequencer is planned as a backend for user scripts
> > > that execute a sequence of git instructions and perhaps
> > > need manual intervention, for example git-rebase or git-am.
> > 
> > ...
> > > +output () {
> > > +	case "$VERBOSE" in
> > > +	0)
> > > +		"$@" >/dev/null
> > > +		;;
> > > +	1)
> > > +		output=$("$@" 2>&1 )
> > > +		status=$?
> > > +		test $status -ne 0 && printf '%s\n' "$output"
> > > +		return $status
> > > +		;;
> > > +	2)
> > > +		"$@"
> > > +		;;
> > > +	esac
> > > +}
> > 
> > Perhaps misnamed?  This feels more like "do" or "perform" or "run".
> 
> My fault.  I like "perform".

Right, the "output" name puts a wrong behavior in mind.
"perform" (or "do" or "run") won't exactly say that this function
"adjusts" the output somehow, but I'm nonetheless fine with taking
"perform".

(I inline-attach patches so that you can incrementally review what I did
with your feedback.)
--- a/git-sequencer.sh
+++ b/git-sequencer.sh
@@ -75,7 +75,7 @@ quit () {
 	exit 0
 }
 
-output () {
+perform () {
 	case "$VERBOSE" in
 	0)
 		"$@" >/dev/null
@@ -165,7 +165,7 @@ restore () {
 		git symbolic-ref HEAD "$HEADNAME"
 		;;
 	esac &&
-	output git reset --hard "$HEAD"
+	perform git reset --hard "$HEAD"
 }
 
 has_action () {
@@ -208,14 +208,14 @@ pick_one () {
 	then
 		test "$(git rev-parse --verify "$1^")" = \
 			"$(git rev-parse --verify HEAD)" &&
-			output git reset --hard "$1" &&
+			perform git reset --hard "$1" &&
 			return
 	fi
 	test "$1" != '--edit' -a "$what" = 'revert' &&
 		what='revert --no-edit'
 	test -n "$SIGNOFF" &&
 		what="$what -s"
-	$use_output git $what "$@"
+	$use_perform git $what "$@"
 }
 
 nth_string () {
@@ -945,15 +945,15 @@ insn_pick () {
 	# Be kind to users and ignore --mainline=1 on non-merge commits
 	test -n "$mainline" -a 2 -gt $(count_parents "$sha1") && mainline=
 
-	use_output=
+	use_perform=
 	test -n "$edit_msg" ||
-		use_output=output
+		use_perform=perform
 
 	pick_one "$op" $edit_msg $mainline $sha1 ||
 		die_with_patch $sha1 "Could not apply $sha1"
 
 	test -n "$EDIT" ||
-		use_output=output
+		use_perform=perform
 
 	get_current_author
 	signoff=
@@ -961,18 +961,18 @@ insn_pick () {
 	if test -n "$AUTHOR" -a -n "$MESSAGE"
 	then
 		# this is just because we only want to do ONE amending commit
-		$use_output git commit --amend $EDIT $signoff --no-verify \
+		$use_perform git commit --amend $EDIT $signoff --no-verify \
 			--author "$GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>" \
 			--message="$MESSAGE"
 	else
 		# correct author if AUTHOR is set
 		test -n "$AUTHOR" &&
-			$use_output git commit --amend $EDIT --no-verify -C HEAD \
+			$use_perform git commit --amend $EDIT --no-verify -C HEAD \
 				--author "$GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>"
 		# XXX: a git-cherry-pick --author could be nicer here
 		# correct commit message if MESSAGE is set
 		test -n "$MESSAGE" &&
-			$use_output git commit --amend $EDIT $signoff --no-verify \
+			$use_perform git commit --amend $EDIT $signoff --no-verify \
 				-C HEAD --message="$MESSAGE"
 	fi
 
@@ -1128,12 +1128,12 @@ insn_squash () {
 	case "$(peek_next_command)" in
 	squash)
 		edit_commit=
-		use_output=output
+		use_perform=perform
 		cp "$MSG" "$squash_msg"
 		;;
 	*)
 		edit_commit=-e
-		use_output=
+		use_perform=
 		rm -f "$squash_msg" || exit
 		;;
 	esac
@@ -1157,9 +1157,9 @@ insn_squash () {
 	failed=
 	if test -n "$from"
 	then
-		output git reset --soft "$sha1"
+		perform git reset --soft "$sha1"
 	else
-		output git reset --soft HEAD^
+		perform git reset --soft HEAD^
 
 		pick_one cherry-pick -n "$sha1" || failed=t
 	fi
@@ -1169,7 +1169,7 @@ insn_squash () {
 	if test -z "$failed"
 	then
 		# This is like --amend, but with a different message
-		with_author $use_output git commit --no-verify \
+		with_author $use_perform git commit --no-verify \
 			-F "$MSG" $edit_commit || failed=t
 	else
 		cp "$MSG" "$GIT_DIR/MERGE_MSG"
@@ -1320,13 +1320,13 @@ insn_merge () {
 	fi
 
 	mark_action_done
-	if ! with_author output git merge $strategy -m junk $new_parents
+	if ! with_author perform git merge $strategy -m junk $new_parents
 	then
 		git rerere
 		cat "$MSG" >"$GIT_DIR/MERGE_MSG"
 		die_to_continue 'Error merging'
 	fi
-	with_author output git commit --amend -F "$MSG" --no-verify
+	with_author perform git commit --amend -F "$MSG" --no-verify
 	return 0
 }
 
@@ -1352,7 +1352,7 @@ insn_reset () {
 	comment_for_reflog reset
 
 	mark_action_done
-	output git reset --hard "$(mark_to_commit "$1")"
+	perform git reset --hard "$(mark_to_commit "$1")"
 }
 
 
@@ -1375,7 +1375,7 @@ insn_ref () {
 	comment_for_reflog ref
 
 	mark_action_done
-	output git update-ref "$1" HEAD
+	perform git update-ref "$1" HEAD
 }
 
 
@@ -1547,10 +1547,10 @@ do_startup () {
 		then
 			ONTO="refs/heads/${ONTO##*/}"
 			echo "$ONTO" >"$SEQ_DIR/onto"
-			output git checkout "$(git rev-parse "$ONTO")" ||
+			perform git checkout "$(git rev-parse "$ONTO")" ||
 				die_abort "Could not checkout branch $ONTO"
 		else
-			output git checkout "$ONTO" ||
+			perform git checkout "$ONTO" ||
 				die_abort "Could not checkout commit $ONTO"
 			ONTO=
 		fi
@@ -1653,7 +1653,7 @@ Fix with git sequencer --edit or abort with $(print_caller --abort)."
 	comment_for_reflog skip
 	git rerere clear
 
-	output git reset --hard "$(cat "$SEQ_DIR/skiphead")" &&
+	perform git reset --hard "$(cat "$SEQ_DIR/skiphead")" &&
 	rm -f "$WHY_FILE" &&
 	echo '# SKIPPED the last instruction' >>"$DONE" &&
 		execute_rest
###

On Wed, Jul 02, 2008 at 06:45:06PM -0700, Junio C Hamano wrote:
> > +LAST_COUNT=
> > +mark_action_done () {
> > +	sed -e 1q <"$TODO" >>"$DONE"
> > +	sed -e 1d <"$TODO" >"$TODO.new"
> > +	mv -f "$TODO.new" "$TODO"
> > +	if test "$VERBOSE" -gt 0
> > +	then
> > +		count=$(grep -c '^[^#]' <"$DONE")
> > +		total=$(expr "$count" + "$(grep -c '^[^#]' <"$TODO")")
> 
> Here we are not counting lines that are comments as insns (I am not
> complaining; just making a mental note).

Add to your note, that empty lines are also skipped, because at least
one character (which is not #) must be there.
And add to your note, that leading whitespace is not ignored on that
checks, which can lead to wrong numbers, but: (quoting Dscho)
> "count" and "total" are only used for the progress output

(The builtin handles that better because it just does proper parsing
and not such hacks.)

Junio C Hamano wrote:
> > +		if test "$LAST_COUNT" != "$count"
> > +		then
> > +			LAST_COUNT="$count"
> > +			test "$VERBOSE" -lt 1 ||
> > +				printf 'Sequencing (%d/%d)\r' "$count" "$total"
> > +			test "$VERBOSE" -lt 2 || echo
> > +		fi
> > +	fi
> > +}
> > +
> > +# Generate message, patch and author script files
> > +make_patch () {
> > +	parent_sha1=$(git rev-parse --verify "$1"^) ||
> > +		die "Cannot get patch for $1^"
> > +	git diff-tree -p "$parent_sha1..$1" >"$PATCH"
> 
> Could there be a case where we need/want to deal with a root commit
> without parents?

Yes, and this is not handled correctly.

I've taken Jakub's idea:
--- a/git-sequencer.sh
+++ b/git-sequencer.sh
@@ -139,9 +139,9 @@ mark_action_done () {
 
 # Generate message, patch and author script files
 make_patch () {
-	parent_sha1=$(git rev-parse --verify "$1"^) ||
-		die "Cannot get patch for $1^"
-	git diff-tree -p "$parent_sha1..$1" >"$PATCH"
+	parent_sha1=$(git rev-parse --verify "$1^" 2>/dev/null ||
+		echo '--root')
+	git diff-tree -p "$parent_sha1" "$1" >"$PATCH"
 	test -f "$MSG" ||
 		commit_message "$1" >"$MSG"
 	test -f "$AUTHOR_SCRIPT" ||
###

Btw, another root commit problem is btw that it's not possible
to cherry-pick root commits.

Junio C Hamano wrote:
> > +	test -f "$MSG" ||
> > +		commit_message "$1" >"$MSG"
> > +	test -f "$AUTHOR_SCRIPT" ||
> > +		get_author_ident_from_commit "$1" >"$AUTHOR_SCRIPT"
> > +}
> > +
> > +# Generate a patch and die with "conflict" status code
> > +die_with_patch () {
> > +	make_patch "$1"
> > +	git rerere
> > +	die_to_continue "$2"
> > +}
> > +
> > +restore () {
> > +	git rerere clear
> > +
> > +	HEADNAME=$(cat "$SEQ_DIR/head-name")
> > +	HEAD=$(cat "$SEQ_DIR/head")
> 
> Perhaps
> 
> 	read HEADNAME <"$SEQ_DIR/head-name"
> 
> provided if these values are $IFS safe?

They are IFS-safe. I don't really have an opinion if
	read FOO <foo
is better than
	FOO=$(cat foo)
or vice versa, so I have no problem to change this.

--- a/git-sequencer.sh
+++ b/git-sequencer.sh
@@ -158,8 +158,8 @@ die_with_patch () {
 restore () {
 	git rerere clear
 
-	HEADNAME=$(cat "$SEQ_DIR/head-name")
-	HEAD=$(cat "$SEQ_DIR/head")
+	read HEADNAME <"$SEQ_DIR/head-name"
+	read HEAD <"$SEQ_DIR/head"
 	case $HEADNAME in
 	refs/*)
 		git symbolic-ref HEAD "$HEADNAME"
@@ -1496,9 +1496,10 @@ prepare_editable_todo () {
 }
 
 get_saved_options () {
-	VERBOSE=$(cat "$SEQ_DIR/verbose")
-	ONTO=$(cat "$SEQ_DIR/onto")
-	WHY=$(cat "$WHY_FILE" 2>/dev/null)
+	read VERBOSE <"$SEQ_DIR/verbose"
+	read ONTO <"$SEQ_DIR/onto"
+	test -f "$WHY_FILE" &&
+		read WHY <"$WHY_FILE"
 	return 0
 }
 
###

Johannes Schindelin wrote:
> > > +# Usage: pick_one (cherry-pick|revert) [-*|--edit] sha1
> > > +pick_one () {
> > > +	what="$1"
> > > +	# we just assume that this is either cherry-pick or revert
> > > +	shift
> > > +
> > > +	# check for fast-forward if no options are given
> > > +	if expr "x$1" : 'x[^-]' >/dev/null
> > > +	then
> > > +		test "$(git rev-parse --verify "$1^")" = \
> > > +			"$(git rev-parse --verify HEAD)" &&
> > > +			output git reset --hard "$1" &&
> > > +			return
> > > +	fi
> > > +	test "$1" != '--edit' -a "$what" = 'revert' &&
> > > +		what='revert --no-edit'
> > 
> > This looks somewhat wrong.
> > 
> > When the history looks like ---A---B and we are at A, cherry-picking B can
> > be optimized to just advancing to B, but that optimization has a slight
> > difference (or two) in the semantics.
> > 
> >  (1) The committer information would not record the user and time of the
> >      sequencer operation, which actually may be a good thing.
> 
> This is debatable.  But I think you are correct, for all the same reasons 
> why a merge can result in a fast-forward.

Dscho, you mean me by referring to 'you' here, right?
Otherwise I'm a bit confused: "For the same reasons why a merge can result
in a fast-forward we should not do fast forward here" ;-)

I think the ff is more useful than real picking and I think the rebase-i
test suite even has a test case for this.
(Checked: "* FAIL 17: merge redoes merges" if it is removed, so it's
implicitly checked for redone merges.)

> >  (2) When $what is revert, this codepath shouldn't be exercised, should 
> >  it?
> 
> Yes.

I haven't done a check intentionally, but there was a stupid thinko.
So you're right.

But: this will only be a bug if the commit that _comes next in the
original history_ is to be reverted.  This is usually user-nonsense
(I can't imagine a useful application for that), but nonetheless a
user can try it for fun and such a case is not worth a sanity check.
So it's perhaps good to add a test here and just do the revert (and no
ff) as the user requests.

> >  (3) If B is a merge, even if $what is pick, this codepath shouldn't be
> >      exercised, should it?
> 
> I think it should, again for the same reason why a merge can result in a 
> fast-forward.

Right.

Junio C Hamano wrote:
> As to the syntax I tend to prefer
> 
> 	case "$1" in
>         -*)	... do option thing ... ;;
>         *)	... do other thing... ;;
>         esac

Christian has mentioned this, but I didn't listen to him ;)
Because I think, if you put the cases, the do_thing and the ;;
on separate lines,
1. it is just consistent compared to the cases where you
   do several things ... you don't do them on one line and
   you put the ;; on an extra line
2. the diffs will look better on extensions

> So how about...
> 
> 	case "$what,$1" in
>         revert,--edit)
>         	what='revert --no-edit' ;;
>         revert,* | cherry-pick,-* )
>         	;;
>         *)
> 		if ! git rev-parse --verify "$1^2" &&
> 	                test "$(git rev-parse --verify "$1^") = \
>                 	"$(git rev-parse --verify HEAD)"
> 		then
>                 	output git reset --hard "$1"
> 			return
> 		fi
> 		;;
> 	esac

I've done this:
--- a/git-sequencer.sh
+++ b/git-sequencer.sh
@@ -200,19 +200,25 @@ with_author () {
 # Usage: pick_one (cherry-pick|revert) [-*|--edit] sha1
 pick_one () {
 	what="$1"
-	# we just assume that this is either cherry-pick or revert
 	shift
 
-	# check for fast-forward if no options are given
-	if expr "x$1" : 'x[^-]' >/dev/null
-	then
-		test "$(git rev-parse --verify "$1^")" = \
-			"$(git rev-parse --verify HEAD)" &&
-			perform git reset --hard "$1" &&
+	case "$what,$1" in
+	revert,*)
+		test "$1" != '--edit' &&
+			what='revert --no-edit'
+		;;
+	cherry-pick,-*)
+		;;
+	cherry-pick,*)
+		# fast forward
+		if test "$(git rev-parse --verify "$1^")" = \
+			"$(git rev-parse --verify HEAD)"
+		then
+			perform git reset --hard "$1"
 			return
-	fi
-	test "$1" != '--edit' -a "$what" = 'revert' &&
-		what='revert --no-edit'
+		fi
+		;;
+	esac
 	test -n "$SIGNOFF" &&
 		what="$what -s"
 	$use_perform git $what "$@"
###

Johannes Schindelin wrote:
> > > +make_squash_message () {
> > > +	if test -f "$squash_msg"
> > > +	then
> > > +		count=$(($(sed -n -e 's/^# This is [^0-9]*\([1-9][0-9]*\).*/\1/p' \
> > > +			<"$squash_msg" | sed -n -e '$p')+1))
> > > +		echo "# This is a combination of $count commits."
> > > +		sed -e '1d' -e '2,/^./{
> > > +			/^$/d
> > > +		}' <"$squash_msg"
> > > +	else
> > > +		count=2
> > > +		echo '# This is a combination of 2 commits.'
> > > +		echo '# The first commit message is:'
> > > +		echo
> > > +		commit_message HEAD
> > > +	fi
> > > +	echo
> > > +	echo "# This is the $(nth_string "$count") commit message:"
> > > +	echo
> > > +	commit_message "$1"
> > > +}
> > > +
> > > +make_squash_message_multiple () {
> > > +	echo '# This is a dummy to get the 0.' >"$squash_msg"
> > > +	for cur_sha1 in $(git rev-list --reverse "$sha1..HEAD")
> > > +	do
> > > +		make_squash_message "$cur_sha1" >"$MSG"
> > > +		cp "$MSG" "$squash_msg"
> > > +	done
> > > +}
> > 
> > Hmm, I know this is how rebase-i is written, but we should be able to do
> > better than writing and flipping temporary times N times, shouldn't we?
> 
> Right, again my fault.

No, not your fault.
I think Junio is referring to make_squash_message_multiple which is for
	squash --from <mark>
and just calls your make_squash_message several times.

When I did this, I knew that this is not most efficient, but I had in
mind: this is just a prototype. The builtin will solve this in a
different way.

Nonetheless, purely tested:
--- a/git-sequencer.sh
+++ b/git-sequencer.sh
@@ -264,11 +264,26 @@ make_squash_message () {
 }
 
 make_squash_message_multiple () {
-	echo '# This is a dummy to get the 0.' >"$squash_msg"
-	for cur_sha1 in $(git rev-list --reverse "$sha1..HEAD")
+	revlist=$(git rev-list --reverse "$sha1..HEAD")
+	count=$(echo "$revlist" | wc -l)
+	squash_i=0
+	echo "# This is a combination of $count commits."
+	for cur_sha1 in $revlist
 	do
-		make_squash_message "$cur_sha1" >"$MSG"
-		cp "$MSG" "$squash_msg"
+		squash_i=$(($squash_i+1))
+		if test -f "$squash_msg"
+		then
+			count=$(($count + $(sed -n -e \
+				's/^# This is [^0-9]*\([1-9][0-9]*\).*/\1/p' \
+				<"$squash_msg" | sed -n -e '$p')+1))
+			sed -e '1d' -e '2,/^./{
+				/^$/d
+			}' <"$squash_msg"
+		fi
+		echo
+		echo "# This is the $(nth_string "$squash_i") commit message:"
+		echo
+		commit_message "$cur_sha1"
 	done
 }
 
@@ -1128,7 +1143,7 @@ insn_squash () {
 	else
 		if test -n "$from"
 		then
-			make_squash_message_multiple "$sha1"
+			make_squash_message_multiple "$sha1" >"$MSG"
 		else
 			make_squash_message "$sha1" >"$MSG"
 		fi
###

Johannes Schindelin wrote:
> > > +peek_next_command () {
> > > +	sed -n -e '1s/ .*$//p' <"$TODO"
> > > +}
> > 
> > ... which could respond "the next command is '#' (comment)", so we are
> > actively counting a comment as a step here.  Does this contradict with the
> > mental note we made earlier, and if so, does the discrepancy hurt us
> > somewhere in this program?

Yes it does hurt ;-)

> Yes, this is wrong.  it must be
> 
> 	sed -n -e '/^#/d' -e '1s .*$//p' < "$TODO"

Thanks ;)

--- a/git-sequencer.sh
+++ b/git-sequencer.sh
@@ -273,7 +273,7 @@ make_squash_message_multiple () {
 }
 
 peek_next_command () {
-	sed -n -e '1s/ .*$//p' <"$TODO"
+	sed -n -e '/^#/d' -e '1s/ .*$//p' <"$TODO"
 }
 
 # If $1 is a mark, make a ref from it; otherwise keep it.
###

Junio C Hamano wrote:
> > +# If $1 is a mark, make a ref from it; otherwise keep it
> > +mark_to_ref () {
> > +	arg="$1"
> > +	ref=$(expr "x$arg" : 'x:0*\([0-9][0-9]*\)$')
> 
> You might want to leave comments to describe constraints that led to this
> slightly awkward regexp:
> 
>  * :0 is allowed
>  * :01 is the same as :1

Right.

--- a/git-sequencer.sh
+++ b/git-sequencer.sh
@@ -270,7 +270,10 @@ peek_next_command () {
 	sed -n -e '1s/ .*$//p' <"$TODO"
 }
 
-# If $1 is a mark, make a ref from it; otherwise keep it
+# If $1 is a mark, make a ref from it; otherwise keep it.
+# Note on marks:
+#  * :0 is allowed
+#  * :01 is the same as :1
 mark_to_ref () {
 	arg="$1"
 	ref=$(expr "x$arg" : 'x:0*\([0-9][0-9]*\)$')
###

Junio C Hamano wrote:
> > +strategy_check () {
> > +	case "$1" in
> > +	resolve|recursive|octopus|ours|subtree|theirs)
> > +		return
> > +		;;
> > +	esac
> > +	todo_warn "Strategy '$1' not known."
> > +}
> 
> Hmm.  Do we need to maintain list of available strategies here and then in
> git-merge separately?

No, this is potentially-error-prone at large.
But I want a check for this, so I'd vote for a
	--list-strategies
feature in the builtin-merge.
(Before writing the quoted code, I was looking for such a feature, but
haven't found. But since builtin-merge is in work I didn't want send
patches on git-merge.sh)

Johannes Schindelin wrote:
> I'd not check in sequencer for the strategy.  Especially given that we 
> want to support user-written strategies in the future.

I don't know how this is planned to look like, but perhaps --list-strategies
may make sense here, too.
Also, the shell completion scripts could use that.

Junio C Hamano wrote:
> > +### Author script functions
> > +
> > +clean_author_script () {
> > +	cat "$ORIG_AUTHOR_SCRIPT" >"$AUTHOR_SCRIPT"
> > +}
> > +
> > +# Take "Name <e-mail>" in stdin and outputs author script
> > +make_author_script_from_string () {
> > +	sed -e 's/^\(.*\) <\(.*\)>.*$/GIT_AUTHOR_NAME="\1"\
> > +GIT_AUTHOR_EMAIL="\2"\
> > +GIT_AUTHOR_DATE=/'
> > +}
> 
> If you are going to "."-source or eval the output from this, you would
> need to quote the values a lot more robustly, wouldn't you?  Is this safe
> against shell metacharacters in names, mails and/or space between unixtime
> and the timezone information?

Time is set empty here.
It is not save according to shell metacharacters in name or e-mail and I
knew that when writing.
Of course, a
	First "nick" Sure
author will get problems here. ;-)
And as long as nobody is named $(rm -rf "$HOME")  [1], I thought this is
sufficient for the prototype.

But...
--- a/git-sequencer.sh
+++ b/git-sequencer.sh
@@ -520,7 +520,7 @@ clean_author_script () {
 
 # Take "Name <e-mail>" in stdin and outputs author script
 make_author_script_from_string () {
-	sed -e 's/^\(.*\) <\(.*\)>.*$/GIT_AUTHOR_NAME="\1"\
+	sed -e 's/[$"\\]/\\&/g' -e 's/^\(.*\) <\(.*\)>.*$/GIT_AUTHOR_NAME="\1"\
 GIT_AUTHOR_EMAIL="\2"\
 GIT_AUTHOR_DATE=/'
 }
@@ -779,7 +779,7 @@ insn_patch () {
 	
 	if test -z "$AUTHOR"
 	then
-		sed -n -e '
+		sed -e 's/[$"\\]/\\&/g' -n -e '
 			s/^Author: \(.*\)$/GIT_AUTHOR_NAME="\1"/p;
 			s/^Email: \(.*\)$/GIT_AUTHOR_EMAIL="\1"/p;
 			s/^Date: \(.*\)$/GIT_AUTHOR_DATE="\1"/p
###

Is escaping $, " and \ enough?

> > +	if test -z "$AUTHOR"
> > +	then
> > +		sed -n -e '
> > +			s/^Author: \(.*\)$/GIT_AUTHOR_NAME="\1"/p;
> > +			s/^Email: \(.*\)$/GIT_AUTHOR_EMAIL="\1"/p;
> > +			s/^Date: \(.*\)$/GIT_AUTHOR_DATE="\1"/p
> > +		' <"$infofile" >>"$AUTHOR_SCRIPT"
> 
> The same comment on quoting applies here, I think.

Right.

> > +	if test -n "$failed"
> > +	then
> > +		# XXX: This is just a stupid hack:
> > +		with_author git apply $apply_opts --reject --index "$PATCH"
> 
> Please don't do this without being asked, if you are planning to use this
> in "am" when 3-way fallback was not asked.  It _may_ make sense to give an
> option to the users to ask for .rej if they prefer to work that way better
> than working with 3-way merge fallback, but doing this without being asked
> is not acceptable.

The --reject was just a mind marker for that what I actually think is
useful and less annoying than the current behavior:

> > +		die_to_continue 'Patch failed. See the .rej files.'
> > +		# XXX: We actually needed a git-apply flag that creates
> > +		# conflict markers and sets the DIFF_STATUS_UNMERGED flag.
> 
> That is what -3way is all about, and this codepath is when the user did
> not ask for it, isn't it?

Now imagine you apply a patch that cannot be applied 100% cleanly and
you don't have the 3-way base in the repo. You know what happens?
Yes, the patch is completly rejected, because apply is atomic.
And I think a git-apply option that results in a non-atomic behavior,
that creates conflict markers (and no .rej files), would be a great
usability feature for the "patch" insn in sequencer.
I'd even suggest to make that the default, but that's debatable.

Btw, I removed the mind marker:
--- a/git-sequencer.sh
+++ b/git-sequencer.sh
@@ -836,9 +836,7 @@ insn_patch () {
 
 	if test -n "$failed"
 	then
-		# XXX: This is just a stupid hack:
-		with_author git apply $apply_opts --reject --index "$PATCH"
-		die_to_continue 'Patch failed. See the .rej files.'
+		die_to_continue 'Patch failed.'
 		# XXX: We actually needed a git-apply flag that creates
 		# conflict markers and sets the DIFF_STATUS_UNMERGED flag.
 	fi
###

> > +# Check the "pick" instruction
> > +check_pick () {
> > +	revert=
> > +	mainline=
> > +	while test $# -gt 1
> > +	do
> > ...
> > +	done
> > +
> > +	if test -n "$mainline"
> > +	then
> > +		test -z "$revert" ||
> > +			todo_error "Cannot use $revert together with --mainline."
> 
> Why not?  If you have this...
> 
> 	---A---C---D
>               /
>           ---B
> 
> and you are at D, you may want to undo the merge you made at C and go back
> to either A or B, which essentially is same as cherry-picking diff between
> C and D on top of either A or B.  Both are valid operations aren't they?

Jakub already asked. I told him, that git-revert doesn't know --mainline,
but it seems that this was wrong.  So:

--- a/git-sequencer.sh
+++ b/git-sequencer.sh
@@ -879,13 +879,11 @@ $OPTIONS_GENERAL
 
 # Check the "pick" instruction
 check_pick () {
-	revert=
 	mainline=
 	while test $# -gt 1
 	do
 		case "$1" in
 		-R)
-			revert="$1"
 			;;
 		--mainline)
 			shift
@@ -913,9 +911,6 @@ check_pick () {
 
 	if test -n "$mainline"
 	then
-		test -z "$revert" ||
-			todo_error "Cannot use $revert together with --mainline."
-
 		parents=$(count_parents "$1")
 		test "$parents" -lt "$mainline" &&
 			todo_error "Commit has only $parents (less than $mainline) parents."
###

Regards and big thanks for the fast reply,
  Stephan

-- 
Stephan Beyer <s-beyer@gmx.net>, PGP 0x6EDDD207FCC5040F

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC/PATCH 1/4] Add git-sequencer shell prototype
  2008-07-03 13:10   ` Jakub Narebski
@ 2008-07-03 21:12     ` Stephan Beyer
  0 siblings, 0 replies; 52+ messages in thread
From: Stephan Beyer @ 2008-07-03 21:12 UTC (permalink / raw)
  To: Jakub Narebski; +Cc: git, Junio C Hamano, Johannes Schindelin

Hi,

first, thanks for the --root idea ;-)

> BTW. what is best way of checking if given revision is parent-less?

I think count the number of parents is quite sane.

In sequencer I have:
	count_parents() {
		git cat-file commit "$1" | sed -n -e '1,/^$/p' | grep -c '^parent'
	}

Regards,
  Stephan

-- 
Stephan Beyer <s-beyer@gmx.net>, PGP 0x6EDDD207FCC5040F

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC/PATCH 1/4] Add git-sequencer shell prototype
  2008-07-03 21:09       ` Stephan Beyer
@ 2008-07-03 22:11         ` Junio C Hamano
  2008-07-03 22:34           ` Stephan Beyer
  2008-07-03 23:53         ` Johannes Schindelin
  1 sibling, 1 reply; 52+ messages in thread
From: Junio C Hamano @ 2008-07-03 22:11 UTC (permalink / raw)
  To: Stephan Beyer; +Cc: Johannes Schindelin, git, Daniel Barkalow, Christian Couder

Stephan Beyer <s-beyer@gmx.net> writes:

> +		sed -e 's/[$"\\]/\\&/g' -n -e '
>  			s/^Author: \(.*\)$/GIT_AUTHOR_NAME="\1"/p;
>  			s/^Email: \(.*\)$/GIT_AUTHOR_EMAIL="\1"/p;
>  			s/^Date: \(.*\)$/GIT_AUTHOR_DATE="\1"/p
> ###
>
> Is escaping $, " and \ enough?

Look at how it is done in git-sh-setup get_author_ident_from_commit.

>> > +	if test -n "$failed"
>> > +	then
>> > +		# XXX: This is just a stupid hack:
>> > +		with_author git apply $apply_opts --reject --index "$PATCH"
>> 
>> Please don't do this without being asked, if you are planning to use this
>> in "am" when 3-way fallback was not asked.  It _may_ make sense to give an
>> option to the users to ask for .rej if they prefer to work that way better
>> than working with 3-way merge fallback, but doing this without being asked
>> is not acceptable.
>
> The --reject was just a mind marker for that what I actually think is
> useful and less annoying than the current behavior:
>
>> > +		die_to_continue 'Patch failed. See the .rej files.'
>> > +		# XXX: We actually needed a git-apply flag that creates
>> > +		# conflict markers and sets the DIFF_STATUS_UNMERGED flag.
>> 
>> That is what -3way is all about, and this codepath is when the user did
>> not ask for it, isn't it?
>
> Now imagine you apply a patch that cannot be applied 100% cleanly and
> you don't have the 3-way base in the repo. You know what happens?

Do you think I don't?  You can check who invented 3way by running "git
log" or "git blame" on git-am.sh ;-)

I think you misread my "That is what -3way is all about".  That remark is
about the comment you have about "creates conflict markers".  The conflict
markers is only possible because we do 3-way merge when you ran "am -3".
If you do not have the base object but only a blob and an unapplicable
patch, you cannot do "here is our change since common ancestor, and here
is their change the patch wants to make" conflict markers, because you do
not have the common ancestor.

> Yes, the patch is completly rejected, because apply is atomic.
> And I think a git-apply option that results in a non-atomic behavior,
> that creates conflict markers (and no .rej files), would be a great
> usability feature for the "patch" insn in sequencer.

Yes, I think I already said in the message you are responding to that it
may make sense to have such an option (but at the same time we should
remember that nobody asked to add --reject to "git am").

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC/PATCH 1/4] Add git-sequencer shell prototype
  2008-07-03 22:11         ` Junio C Hamano
@ 2008-07-03 22:34           ` Stephan Beyer
  0 siblings, 0 replies; 52+ messages in thread
From: Stephan Beyer @ 2008-07-03 22:34 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Johannes Schindelin, git, Daniel Barkalow, Christian Couder

On Thu, Jul 03, 2008 at 03:11:45PM -0700, Junio C Hamano wrote:
> Stephan Beyer <s-beyer@gmx.net> writes:
> >> > +		die_to_continue 'Patch failed. See the .rej files.'
> >> > +		# XXX: We actually needed a git-apply flag that creates
> >> > +		# conflict markers and sets the DIFF_STATUS_UNMERGED flag.
> >> 
> >> That is what -3way is all about, and this codepath is when the user did
> >> not ask for it, isn't it?
> >
> > Now imagine you apply a patch that cannot be applied 100% cleanly and
> > you don't have the 3-way base in the repo. You know what happens?
> 
> Do you think I don't?  You can check who invented 3way by running "git
> log" or "git blame" on git-am.sh ;-)

I know you know.  The question was just rhetorical.

> I think you misread my "That is what -3way is all about".  That remark is
> about the comment you have about "creates conflict markers".  The conflict
> markers is only possible because we do 3-way merge when you ran "am -3".
> If you do not have the base object but only a blob and an unapplicable
> patch, you cannot do "here is our change since common ancestor, and here
> is their change the patch wants to make" conflict markers, because you do
> not have the common ancestor.

I didn't misread it.
But it is a wrong implication that you cannot do this when you do not have
a common ancestor.
For example: Even if no context matches (or no context exists), it is
possible to add a conflict marker at the specified line. It can happen
that this will result in crap, but resolving some parts that just look
wrong is easier than applying a patch 100% manually.

I think I'm going to add such a git-apply option after GSoC, if nobody
does this before.

> > Yes, the patch is completly rejected, because apply is atomic.
> > And I think a git-apply option that results in a non-atomic behavior,
> > that creates conflict markers (and no .rej files), would be a great
> > usability feature for the "patch" insn in sequencer.
> 
> Yes, I think I already said in the message you are responding to that it
> may make sense to have such an option (but at the same time we should
> remember that nobody asked to add --reject to "git am").

Right, so I removed it ;)

Regards,
  Stephan

-- 
Stephan Beyer <s-beyer@gmx.net>, PGP 0x6EDDD207FCC5040F

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC/PATCH 1/4] Add git-sequencer shell prototype
  2008-07-03 11:03     ` Johannes Schindelin
  2008-07-03 21:09       ` Stephan Beyer
@ 2008-07-03 22:51       ` Junio C Hamano
  2008-07-03 23:33       ` Stephan Beyer
  2 siblings, 0 replies; 52+ messages in thread
From: Junio C Hamano @ 2008-07-03 22:51 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: Stephan Beyer, git, Daniel Barkalow, Christian Couder

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:

>> When the history looks like ---A---B and we are at A, cherry-picking B can
>> be optimized to just advancing to B, but that optimization has a slight
>> difference (or two) in the semantics.
>> 
>>  (1) The committer information would not record the user and time of the
>>      sequencer operation, which actually may be a good thing.
>
> This is debatable.  But I think you are correct, for all the same reasons 
> why a merge can result in a fast-forward.
>
>>  (2) When $what is revert, this codepath shouldn't be exercised, should 
>>  it?
>
> Yes.
>
>>  (3) If B is a merge, even if $what is pick, this codepath shouldn't be
>>      exercised, should it?
>
> I think it should, again for the same reason why a merge can result in a 
> fast-forward.

Sorry, I disagree.  "cherry-pick" when this optimization is not applicable
always creates a single parent commit.  If the original history looks like:

	D---A---B---C
               /
              M

and when you are cherry-picking B, the above logic would make the result a
merge if you happen to be at A but if you are working elsewhere (perhaps C
or D, or "rewritten A") the result will become a single-parent commit.  I
do not see the justification behind such an unreliable/unpredictable
result, from the end-user's point of view.

I like the check and avoidance of creating a commit that will anyway have
the same parent and the same tree as an _optimization_.  Making the result
of "cherry-pick B" a merge or a non-merge however is not an optimization;
it is changing semantics.

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC/PATCH 1/4] Add git-sequencer shell prototype
  2008-07-03 11:03     ` Johannes Schindelin
  2008-07-03 21:09       ` Stephan Beyer
  2008-07-03 22:51       ` Junio C Hamano
@ 2008-07-03 23:33       ` Stephan Beyer
  2 siblings, 0 replies; 52+ messages in thread
From: Stephan Beyer @ 2008-07-03 23:33 UTC (permalink / raw)
  To: Junio C Hamano, Johannes Schindelin
  Cc: git, Daniel Barkalow, Christian Couder

Hi again,


On Thu, Jul 03, 2008 at 03:11:45PM -0700, Junio C Hamano wrote:
> Stephan Beyer <s-beyer@gmx.net> writes:
> > +		sed -e 's/[$"\\]/\\&/g' -n -e '
> >  			s/^Author: \(.*\)$/GIT_AUTHOR_NAME="\1"/p;
> >  			s/^Email: \(.*\)$/GIT_AUTHOR_EMAIL="\1"/p;
> >  			s/^Date: \(.*\)$/GIT_AUTHOR_DATE="\1"/p
> > ###
> >
> > Is escaping $, " and \ enough?
> 
> Look at how it is done in git-sh-setup get_author_ident_from_commit.

Yes, single quotes are the other variant and perhaps a little more
robust.

--- a/git-sequencer.sh
+++ b/git-sequencer.sh
@@ -520,8 +520,9 @@ clean_author_script () {
 
 # Take "Name <e-mail>" in stdin and outputs author script
 make_author_script_from_string () {
-	sed -e 's/^\(.*\) <\(.*\)>.*$/GIT_AUTHOR_NAME="\1"\
-GIT_AUTHOR_EMAIL="\2"\
+	sed -e "s/'/'"'\\'"''/g" \
+	    -e 's/^\(.*\) <\(.*\)>.*$/GIT_AUTHOR_NAME='\''\1'\''\
+GIT_AUTHOR_EMAIL='\''\2'\''\
 GIT_AUTHOR_DATE=/'
 }
 
@@ -779,10 +780,10 @@ insn_patch () {
 	
 	if test -z "$AUTHOR"
 	then
-		sed -n -e '
-			s/^Author: \(.*\)$/GIT_AUTHOR_NAME="\1"/p;
-			s/^Email: \(.*\)$/GIT_AUTHOR_EMAIL="\1"/p;
-			s/^Date: \(.*\)$/GIT_AUTHOR_DATE="\1"/p
+		sed -e "s/'/'"'\\'"''/g" -n -e '
+			s/^Author: \(.*\)$/GIT_AUTHOR_NAME='\''\1'\''/p;
+			s/^Email: \(.*\)$/GIT_AUTHOR_EMAIL='\''\1'\''/p;
+			s/^Date: \(.*\)$/GIT_AUTHOR_DATE='\''\1'\''/p
 		' <"$infofile" >>"$AUTHOR_SCRIPT"
 		# If sed's result is empty, we keep the original
 		# author script by appending.
###

On Thu, Jul 03, 2008 at 11:09:50PM +0200, Stephan Beyer wrote:
> And as long as nobody is named $(rm -rf "$HOME")  [1], I thought this is
> sufficient for the prototype.

Btw, the [1] should have been a link to http://xkcd.com/327/
Just if you're wondering ;-)

Regards,
  Stephan

-- 
Stephan Beyer <s-beyer@gmx.net>, PGP 0x6EDDD207FCC5040F

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC/PATCH 1/4] Add git-sequencer shell prototype
  2008-07-03 21:09       ` Stephan Beyer
  2008-07-03 22:11         ` Junio C Hamano
@ 2008-07-03 23:53         ` Johannes Schindelin
  2008-07-04  0:38           ` Stephan Beyer
  2008-07-04  1:06           ` [RFC/PATCH 1/4] Add git-sequencer shell prototype Stephan Beyer
  1 sibling, 2 replies; 52+ messages in thread
From: Johannes Schindelin @ 2008-07-03 23:53 UTC (permalink / raw)
  To: Stephan Beyer; +Cc: Junio C Hamano, git, Daniel Barkalow, Christian Couder

Hi,

On Thu, 3 Jul 2008, Stephan Beyer wrote:

> On Thu, Jul 03, 2008 at 12:03:49PM +0100, Johannes Schindelin wrote:
> 
> > On Wed, 2 Jul 2008, Junio C Hamano wrote:
> > > Stephan Beyer <s-beyer@gmx.net> writes:
> > > 
> > > > git sequencer is planned as a backend for user scripts that 
> > > > execute a sequence of git instructions and perhaps need manual 
> > > > intervention, for example git-rebase or git-am.
> > > 
> > > ...
> > > > +output () {
> > > > +	case "$VERBOSE" in
> > > > +	0)
> > > > +		"$@" >/dev/null
> > > > +		;;
> > > > +	1)
> > > > +		output=$("$@" 2>&1 )
> > > > +		status=$?
> > > > +		test $status -ne 0 && printf '%s\n' "$output"
> > > > +		return $status
> > > > +		;;
> > > > +	2)
> > > > +		"$@"
> > > > +		;;
> > > > +	esac
> > > > +}
> > > 
> > > Perhaps misnamed?  This feels more like "do" or "perform" or "run".
> > 
> > My fault.  I like "perform".
> 
> Right, the "output" name puts a wrong behavior in mind. "perform" (or 
> "do" or "run") won't exactly say that this function "adjusts" the output 
> somehow, but I'm nonetheless fine with taking "perform".

Actually, it does not even "adjust" the output.  It just performs the 
action, and _if_ there was an error and "verbose" was set to 1, it shows 
the output.  If "verbose" was set to 2, it always shows the output.  
Otherwise, the output is suppressed.

So, the use of the function really is in the output, but that is either 
shown or not shown, never modified.

> Btw, another root commit problem is btw that it's not possible to 
> cherry-pick root commits.

That is a problem to be fixed in cherry-pick, not in sequencer.  Care to 
take care of that?

> Johannes Schindelin wrote:
> > > > +# Usage: pick_one (cherry-pick|revert) [-*|--edit] sha1
> > > > +pick_one () {
> > > > +	what="$1"
> > > > +	# we just assume that this is either cherry-pick or revert
> > > > +	shift
> > > > +
> > > > +	# check for fast-forward if no options are given
> > > > +	if expr "x$1" : 'x[^-]' >/dev/null
> > > > +	then
> > > > +		test "$(git rev-parse --verify "$1^")" = \
> > > > +			"$(git rev-parse --verify HEAD)" &&
> > > > +			output git reset --hard "$1" &&
> > > > +			return
> > > > +	fi
> > > > +	test "$1" != '--edit' -a "$what" = 'revert' &&
> > > > +		what='revert --no-edit'
> > > 
> > > This looks somewhat wrong.
> > > 
> > > When the history looks like ---A---B and we are at A, cherry-picking B can
> > > be optimized to just advancing to B, but that optimization has a slight
> > > difference (or two) in the semantics.
> > > 
> > >  (1) The committer information would not record the user and time of the
> > >      sequencer operation, which actually may be a good thing.
> > 
> > This is debatable.  But I think you are correct, for all the same reasons 
> > why a merge can result in a fast-forward.
> 
> Dscho, you mean me by referring to 'you' here, right?

Nope.

> Otherwise I'm a bit confused: "For the same reasons why a merge can 
> result in a fast-forward we should not do fast forward here" ;-)

What I meant: there is no use here to redo it.  It has already be done, 
and redoing just pretends that the girl calling sequencer tried to pretend 
that she did it.

If the merge has been done already, it should not be redone.

Only if the user _explicitely_ specified a merge strategy, there _might_ 
be a reason to redo the merge, but I still doubt it.

> > >  (2) When $what is revert, this codepath shouldn't be exercised, 
> > >  should it?
> > 
> > Yes.
> 
> I haven't done a check intentionally, but there was a stupid thinko.
> So you're right.
> 
> But: this will only be a bug if the commit that _comes next in the
> original history_ is to be reverted.

Does not matter.  It's a bug.

A bug is almost always in the details, a corner-case, but it almost always 
needs fixing nevertheless.

> Nonetheless, purely tested:

"Nevertheless", maybe?  "untested", maybe?

> Johannes Schindelin wrote:
> > I'd not check in sequencer for the strategy.  Especially given that we 
> > want to support user-written strategies in the future.
> 
> I don't know how this is planned to look like, but perhaps 
> --list-strategies may make sense here, too.

No.  You just do not check for strategies.  Period.  git-merge does that, 
and you can easily abort a rebase if you explicitely asked for an invalid 
strategy.

BTW your coalescing multiple replies into one mail was a major pain in the 
donkey.  Should you do that again, I will not even bother reading that 
mail.

Ciao,
Dscho

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC/PATCH 1/4] Add git-sequencer shell prototype
  2008-07-03 23:53         ` Johannes Schindelin
@ 2008-07-04  0:38           ` Stephan Beyer
  2008-07-04  1:03             ` Johannes Schindelin
  2008-07-04  1:06           ` [RFC/PATCH 1/4] Add git-sequencer shell prototype Stephan Beyer
  1 sibling, 1 reply; 52+ messages in thread
From: Stephan Beyer @ 2008-07-04  0:38 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: Junio C Hamano, git, Daniel Barkalow, Christian Couder

Hi,

On Fri, Jul 04, 2008 at 01:53:21AM +0200, Johannes Schindelin wrote:
> On Thu, 3 Jul 2008, Stephan Beyer wrote:
> > Btw, another root commit problem is btw that it's not possible to 
> > cherry-pick root commits.
> 
> That is a problem to be fixed in cherry-pick, not in sequencer.  Care to 
> take care of that?

Not at the moment but that's one of the things I note down for later ;-)

And btw, somehow it is still open for me if builtin sequencer should be
a git-cherry-pick user (for pick) or if git-cherry-pick should be a
sequencer user (which would result in a change of usage on cherry-pick
conflicts).

> > Johannes Schindelin wrote:
> > > > > +# Usage: pick_one (cherry-pick|revert) [-*|--edit] sha1
> > > > > +pick_one () {
> > > > > +	what="$1"
> > > > > +	# we just assume that this is either cherry-pick or revert
> > > > > +	shift
> > > > > +
> > > > > +	# check for fast-forward if no options are given
> > > > > +	if expr "x$1" : 'x[^-]' >/dev/null
> > > > > +	then
> > > > > +		test "$(git rev-parse --verify "$1^")" = \
> > > > > +			"$(git rev-parse --verify HEAD)" &&
> > > > > +			output git reset --hard "$1" &&
> > > > > +			return
> > > > > +	fi
> > > > > +	test "$1" != '--edit' -a "$what" = 'revert' &&
> > > > > +		what='revert --no-edit'
> > > > 
> > > > This looks somewhat wrong.
> > > > 
> > > > When the history looks like ---A---B and we are at A, cherry-picking B can
> > > > be optimized to just advancing to B, but that optimization has a slight
> > > > difference (or two) in the semantics.
> > > > 
> > > >  (1) The committer information would not record the user and time of the
> > > >      sequencer operation, which actually may be a good thing.
> > > 
> > > This is debatable.  But I think you are correct, for all the same reasons 
> > > why a merge can result in a fast-forward.
> > 
> > Dscho, you mean me by referring to 'you' here, right?
> 
> Nope.
> 
> > Otherwise I'm a bit confused: "For the same reasons why a merge can 
> > result in a fast-forward we should not do fast forward here" ;-)
> 
> What I meant: there is no use here to redo it.  It has already be done, 
> and redoing just pretends that the girl calling sequencer tried to pretend 
> that she did it.
> 
> If the merge has been done already, it should not be redone.
> 
> Only if the user _explicitely_ specified a merge strategy, there _might_ 
> be a reason to redo the merge, but I still doubt it.

I don't get the light bulb.  You're talking about "the merge", I am
talking about fast-forward on picks.
Perhaps I got Junio wrong, too.

I try a simple example just to go sure that we're talking about the
same.

We have commits

  A ---- B ---- C ---- D
       HEAD

A is parent of B, B of C, C of D.

Now we do:
	pick C
	pick --signoff D
(Assume that the Signed-off-by: line is missing on D)

Without fast-forward, we get

  A ---- B ---- C ---- D
          \
           `--- C'---- D'
                     HEAD

C' differs in C only in the committer data, perhaps only committer date.

With fast-forward, we get:

  A ---- B ---- C ---- D
                 \
                  `--- D'
                     HEAD

If Junio meant with
>  (1) The committer information would not record the user and time of the
>      sequencer operation, which actually may be a good thing.
that he thinks the first variant is the way to go, I strongly disagree.
But perhaps I'm getting everyone wrong these days ;)


> > > >  (2) When $what is revert, this codepath shouldn't be exercised, 
> > > >  should it?
> > > 
> > > Yes.
> > 
> > I haven't done a check intentionally, but there was a stupid thinko.
> > So you're right.
> > 
> > But: this will only be a bug if the commit that _comes next in the
> > original history_ is to be reverted.
> 
> Does not matter.  It's a bug.
> 
> A bug is almost always in the details, a corner-case, but it almost always 
> needs fixing nevertheless.

Of course ;)

> > Nonetheless, purely tested:
> 
> "Nevertheless", maybe?  "untested", maybe?

No, I tested it once. ;-)
(For the new single-quoted variant I've changed the author name in
t3350).

> > Johannes Schindelin wrote:
> > > I'd not check in sequencer for the strategy.  Especially given that we 
> > > want to support user-written strategies in the future.
> > 
> > I don't know how this is planned to look like, but perhaps 
> > --list-strategies may make sense here, too.
> 
> No.  You just do not check for strategies.  Period.  git-merge does that, 
> and you can easily abort a rebase if you explicitely asked for an invalid 
> strategy.

Hmm, my dream of the "robust sequencing after sanity check passed" is
dead with your "period".
So I'll have to check what happens, when e.g. "--strategy=hours" is used.
(I mean, you should be in a safe state to do git sequencer --edit and
correct "hours" to "ours'.)

Regards,
  Stephan

-- 
Stephan Beyer <s-beyer@gmx.net>, PGP 0x6EDDD207FCC5040F

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC/PATCH 1/4] Add git-sequencer shell prototype
  2008-07-04  0:38           ` Stephan Beyer
@ 2008-07-04  1:03             ` Johannes Schindelin
  2008-07-04  1:53               ` Stephan Beyer
  0 siblings, 1 reply; 52+ messages in thread
From: Johannes Schindelin @ 2008-07-04  1:03 UTC (permalink / raw)
  To: Stephan Beyer; +Cc: Junio C Hamano, git, Daniel Barkalow, Christian Couder

Hi,

On Fri, 4 Jul 2008, Stephan Beyer wrote:

> On Fri, Jul 04, 2008 at 01:53:21AM +0200, Johannes Schindelin wrote:
> > On Thu, 3 Jul 2008, Stephan Beyer wrote:
> > > Btw, another root commit problem is btw that it's not possible to 
> > > cherry-pick root commits.
> > 
> > That is a problem to be fixed in cherry-pick, not in sequencer.  Care 
> > to take care of that?
> 
> Not at the moment but that's one of the things I note down for later ;-)

Well, logically, it should come _before_ you use it in sequencer.  And you 
should use it in sequencer.

> And btw, somehow it is still open for me if builtin sequencer should be 
> a git-cherry-pick user (for pick) or if git-cherry-pick should be a 
> sequencer user (which would result in a change of usage on cherry-pick 
> conflicts).

The thing is, sequencer and cherry-pick _both_ use the same underlying 
functionality: cherry-picking a single commit.

So, logically, that part should be moved into libgit.a, and cherry-pick 
should use the to-be-introduced sequencer() function.

> > > Johannes Schindelin wrote:
> > > > > > +# Usage: pick_one (cherry-pick|revert) [-*|--edit] sha1
> > > > > > +pick_one () {
> > > > > > +	what="$1"
> > > > > > +	# we just assume that this is either cherry-pick or revert
> > > > > > +	shift
> > > > > > +
> > > > > > +	# check for fast-forward if no options are given
> > > > > > +	if expr "x$1" : 'x[^-]' >/dev/null
> > > > > > +	then
> > > > > > +		test "$(git rev-parse --verify "$1^")" = \
> > > > > > +			"$(git rev-parse --verify HEAD)" &&
> > > > > > +			output git reset --hard "$1" &&
> > > > > > +			return
> > > > > > +	fi
> > > > > > +	test "$1" != '--edit' -a "$what" = 'revert' &&
> > > > > > +		what='revert --no-edit'
> > > > > 
> > > > > This looks somewhat wrong.
> > > > > 
> > > > > When the history looks like ---A---B and we are at A, 
> > > > > cherry-picking B can be optimized to just advancing to B, but 
> > > > > that optimization has a slight difference (or two) in the 
> > > > > semantics.
> > > > > 
> > > > >  (1) The committer information would not record the user and 
> > > > >      time of the sequencer operation, which actually may be a 
> > > > >      good thing.
> > > > 
> > > > This is debatable.  But I think you are correct, for all the same 
> > > > reasons why a merge can result in a fast-forward.
> > > 
> > > Dscho, you mean me by referring to 'you' here, right?
> > 
> > Nope.
> > 
> > > Otherwise I'm a bit confused: "For the same reasons why a merge can 
> > > result in a fast-forward we should not do fast forward here" ;-)
> > 
> > What I meant: there is no use here to redo it.  It has already be 
> > done, and redoing just pretends that the girl calling sequencer tried 
> > to pretend that she did it.
> > 
> > If the merge has been done already, it should not be redone.
> > 
> > Only if the user _explicitely_ specified a merge strategy, there _might_ 
> > be a reason to redo the merge, but I still doubt it.
> 
> I don't get the light bulb.  You're talking about "the merge", I am
> talking about fast-forward on picks.

Ooops.  I think I was talking about a later comment of Junio's.

The thing is, if you try to pick a commit, and the current revision is 
already the parent of that commit, I think it would be wrong to redo the 
commit, pretending that the current time and committer were applying that 
commit.

> I try a simple example just to go sure that we're talking about the 
> same.
> 
> We have commits
> 
>   A ---- B ---- C ---- D
>        HEAD
> 
> A is parent of B, B of C, C of D.
> 
> Now we do:
> 	pick C
> 	pick --signoff D
> (Assume that the Signed-off-by: line is missing on D)
> 
> Without fast-forward, we get
> 
>   A ---- B ---- C ---- D
>           \
>            `--- C'---- D'
>                      HEAD
> 
> C' differs in C only in the committer data, perhaps only committer date.
> 
> With fast-forward, we get:
> 
>   A ---- B ---- C ---- D
>                  \
>                   `--- D'
>                      HEAD

IMO the --signoff should check if the sign off is present (must be the 
last one, as if we redid that commit), and if it is, fast-forward.

If it is missing, we need to redo the commit.

> > > Johannes Schindelin wrote:
> > > > I'd not check in sequencer for the strategy.  Especially given 
> > > > that we want to support user-written strategies in the future.
> > > 
> > > I don't know how this is planned to look like, but perhaps 
> > > --list-strategies may make sense here, too.
> > 
> > No.  You just do not check for strategies.  Period.  git-merge does 
> > that, and you can easily abort a rebase if you explicitely asked for 
> > an invalid strategy.
> 
> Hmm, my dream of the "robust sequencing after sanity check passed" is
> dead with your "period".
>
> So I'll have to check what happens, when e.g. "--strategy=hours" is 
> used.

Wow.  Is that the CVS/SVN merge strategy?

Do not care too much about the rare use cases.  Only experts will use -s.

But experts are known to shoot themselves, or inflict other pain on their 
bodies, so it is okay to let them suffer from a typo.

Ciao,
Dscho

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC/PATCH 1/4] Add git-sequencer shell prototype
  2008-07-03 23:53         ` Johannes Schindelin
  2008-07-04  0:38           ` Stephan Beyer
@ 2008-07-04  1:06           ` Stephan Beyer
  2008-07-04  1:11             ` Johannes Schindelin
  1 sibling, 1 reply; 52+ messages in thread
From: Stephan Beyer @ 2008-07-04  1:06 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: Junio C Hamano, git, Daniel Barkalow, Christian Couder

Hi again,

Johannes Schindelin wrote:
> > > I'd not check in sequencer for the strategy.  Especially given that we 
> > > want to support user-written strategies in the future.
> > 
> > I don't know how this is planned to look like, but perhaps 
> > --list-strategies may make sense here, too.

Funny, a merge with an unknown merge strategy acts somehow like my
proposed --list-strategies (at least on git-merge.sh, haven't checked
on builtin-merge)
But that is just a silly side note.

> No.  You just do not check for strategies.  Period.

Well, I've seen that my strategy_check (which is now removed) only
produces a warning, so it had no big effect at all.
So I've tested merge --strategy=hours (to simulate a typo):

-- -- snip paste -- --
Testing:
        git sequencer todotest1

available strategies are: recur recursive octopus resolve stupid ours subtree
Error merging
* FAIL 33: merge multiple branches and --reuse-commit works
-- -- snap paste -- --

That means, with the information that can be seen, a user should easily be
able to fix that, i.e. run git-sequencer --edit and fix the line.

So I accept your period sign now ;-)

> and you can easily abort a rebase if you explicitely asked for an invalid 
> strategy.

Aborting after fixing a lot of conflicts in the sequencer process is
really annoying.  So I've chosen to never abort automatically.
(That was one of the first things that I changed after I first used my
own sequencer for real work and not only test cases.)

Regards,
  Stephan

-- 
Stephan Beyer <s-beyer@gmx.net>, PGP 0x6EDDD207FCC5040F

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC/PATCH 1/4] Add git-sequencer shell prototype
  2008-07-04  1:06           ` [RFC/PATCH 1/4] Add git-sequencer shell prototype Stephan Beyer
@ 2008-07-04  1:11             ` Johannes Schindelin
  0 siblings, 0 replies; 52+ messages in thread
From: Johannes Schindelin @ 2008-07-04  1:11 UTC (permalink / raw)
  To: Stephan Beyer; +Cc: Junio C Hamano, git, Daniel Barkalow, Christian Couder

Hi,

On Fri, 4 Jul 2008, Stephan Beyer wrote:

> Johannes Schindelin wrote:
>
> > and you can easily abort a rebase if you explicitely asked for an 
> > invalid strategy.
> 
> Aborting after fixing a lot of conflicts in the sequencer process is 
> really annoying.

I think it should use rerere (if activated, which I have given up 
advocating to be the default).

> So I've chosen to never abort automatically.

Who said anything about automatically?  Of _course_, the user has to abort 
it.  Or try to continue with "git sequencer --continue -s ours".

Ciao,
Dscho

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC/PATCH 1/4] Add git-sequencer shell prototype
  2008-07-04  1:03             ` Johannes Schindelin
@ 2008-07-04  1:53               ` Stephan Beyer
  2008-07-04 15:19                 ` [PATCH] Allow cherry-picking root commits Johannes Schindelin
  0 siblings, 1 reply; 52+ messages in thread
From: Stephan Beyer @ 2008-07-04  1:53 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: Junio C Hamano, git, Daniel Barkalow, Christian Couder

Hi,

On Fri, Jul 04, 2008 at 03:03:37AM +0200, Johannes Schindelin wrote:
> On Fri, 4 Jul 2008, Stephan Beyer wrote:
> > On Fri, Jul 04, 2008 at 01:53:21AM +0200, Johannes Schindelin wrote:
> > > On Thu, 3 Jul 2008, Stephan Beyer wrote:
> > > > Btw, another root commit problem is btw that it's not possible to 
> > > > cherry-pick root commits.
> > > 
> > > That is a problem to be fixed in cherry-pick, not in sequencer.  Care 
> > > to take care of that?
> > 
> > Not at the moment but that's one of the things I note down for later ;-)
> 
> Well, logically, it should come _before_ you use it in sequencer.  And you 
> should use it in sequencer.

Yet nobody seems to have asked for a cherry-pick that is able to pick
root commits and sequencer is not closed source after GSoC, so this can
be added whenever there is need and time for it.
That's what I wanted to say with "note down for later". ;-)

> > I don't get the light bulb.  You're talking about "the merge", I am
> > talking about fast-forward on picks.
> 
> Ooops.  I think I was talking about a later comment of Junio's.
> 
> The thing is, if you try to pick a commit, and the current revision is 
> already the parent of that commit, I think it would be wrong to redo the 
> commit, pretending that the current time and committer were applying that 
> commit.

Right.

> IMO the --signoff should check if the sign off is present (must be the 
> last one, as if we redid that commit), and if it is, fast-forward.
> 
> If it is missing, we need to redo the commit.

That's a good point.
Checked that and it's even a bug in the sequencer: on fast-forwards, no
signoff is added.

This should fix that:
--- a/git-sequencer.sh
+++ b/git-sequencer.sh
@@ -219,8 +219,6 @@ pick_one () {
 		fi
 		;;
 	esac
-	test -n "$SIGNOFF" &&
-		what="$what -s"
 	$use_perform git $what "$@"
 }
 
@@ -973,16 +971,21 @@ insn_pick () {
 		$use_perform git commit --amend $EDIT $signoff --no-verify \
 			--author "$GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>" \
 			--message="$MESSAGE"
-	else
+	elif test -n "$AUTHOR"
+	then
 		# correct author if AUTHOR is set
-		test -n "$AUTHOR" &&
-			$use_perform git commit --amend $EDIT --no-verify -C HEAD \
-				--author "$GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>"
-		# XXX: a git-cherry-pick --author could be nicer here
+		$use_perform git commit --amend $EDIT --no-verify -C HEAD \
+			--author "$GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>"
+	elif test -n "$MESSAGE"
+	then
 		# correct commit message if MESSAGE is set
-		test -n "$MESSAGE" &&
-			$use_perform git commit --amend $EDIT $signoff --no-verify \
-				-C HEAD --message="$MESSAGE"
+		$use_perform git commit --amend $EDIT $signoff --no-verify \
+			-C HEAD --message="$MESSAGE"
+	elif test -n "$SIGNOFF"
+	then
+		# only add signoff
+		$use_perform git commit --amend $EDIT $signoff --no-verify \
+			-C HEAD
 	fi
 
 	return 0
###

Ah, and that is poorly tested, but not untested. :) See:
	$ git checkout -b seq-proto-dev3 HEAD^
	Switched to a new branch "seq-proto-dev3"
	$ git sequencer
	mark :1
	pick seq-proto-dev2
	ref refs/test/no
	reset :1
	pick --signoff seq-proto-dev2
Now: seq-proto-dev3 has signoff and seq-proto-dev2 and refs/test/no have the
same SHA1.  And the test suite passes.

Regards,
  Stephan

-- 
Stephan Beyer <s-beyer@gmx.net>, PGP 0x6EDDD207FCC5040F

^ permalink raw reply	[flat|nested] 52+ messages in thread

* [PATCH] Allow cherry-picking root commits
  2008-07-04  1:53               ` Stephan Beyer
@ 2008-07-04 15:19                 ` Johannes Schindelin
  2008-07-04 16:41                   ` Stephan Beyer
                                     ` (2 more replies)
  0 siblings, 3 replies; 52+ messages in thread
From: Johannes Schindelin @ 2008-07-04 15:19 UTC (permalink / raw)
  To: Stephan Beyer; +Cc: Junio C Hamano, git, Daniel Barkalow, Christian Couder


There is no good reason why cherry-picking root commits should not be 
allowed.

Further, since cherry-pick is the working horse of git-rebase, we _should_ 
allow picking root commits, so that you can rebase originally independent 
branches on top of another branch.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---

	On Fri, 4 Jul 2008, Stephan Beyer wrote:

	> On Fri, Jul 04, 2008 at 03:03:37AM +0200, Johannes Schindelin 
	> wrote:
	> 
	> > Well, logically, it should come _before_ you use it in 
	> > sequencer.  And you should use it in sequencer.
	> 
	> Yet nobody seems to have asked for a cherry-pick that is able to 
	> pick root commits and sequencer is not closed source after GSoC, so 
	> this can be added whenever there is need and time for it.

	However, this is the wrong sequence.

	Anyhow, I give up on trying to convince you.

 builtin-revert.c            |   26 ++++++++++++++++----------
 t/t3503-cherry-pick-root.sh |   30 ++++++++++++++++++++++++++++++
 2 files changed, 46 insertions(+), 10 deletions(-)
 create mode 100755 t/t3503-cherry-pick-root.sh

diff --git a/builtin-revert.c b/builtin-revert.c
index 0270f9b..f3d4524 100644
--- a/builtin-revert.c
+++ b/builtin-revert.c
@@ -206,6 +206,7 @@ static int merge_recursive(const char *base_sha1,
 {
 	char buffer[256];
 	const char *argv[6];
+	int i = 0;
 
 	sprintf(buffer, "GITHEAD_%s", head_sha1);
 	setenv(buffer, head_name, 1);
@@ -218,12 +219,13 @@ static int merge_recursive(const char *base_sha1,
 	 * and $prev on top of us (when reverting), or the change between
 	 * $prev and $commit on top of us (when cherry-picking or replaying).
 	 */
-	argv[0] = "merge-recursive";
-	argv[1] = base_sha1;
-	argv[2] = "--";
-	argv[3] = head_sha1;
-	argv[4] = next_sha1;
-	argv[5] = NULL;
+	argv[i++] = "merge-recursive";
+	if (base_sha1)
+		argv[i++] = base_sha1;
+	argv[i++] = "--";
+	argv[i++] = head_sha1;
+	argv[i++] = next_sha1;
+	argv[i++] = NULL;
 
 	return run_command_v_opt(argv, RUN_COMMAND_NO_STDIN | RUN_GIT_CMD);
 }
@@ -297,9 +299,12 @@ static int revert_or_cherry_pick(int argc, const char **argv)
 		discard_cache();
 	}
 
-	if (!commit->parents)
-		die ("Cannot %s a root commit", me);
-	if (commit->parents->next) {
+	if (!commit->parents) {
+		if (action == REVERT)
+			die ("Cannot revert a root commit");
+		parent = NULL;
+	}
+	else if (commit->parents->next) {
 		/* Reverting or cherry-picking a merge commit */
 		int cnt;
 		struct commit_list *p;
@@ -368,7 +373,8 @@ static int revert_or_cherry_pick(int argc, const char **argv)
 		}
 	}
 
-	if (merge_recursive(sha1_to_hex(base->object.sha1),
+	if (merge_recursive(base == NULL ?
+				NULL : sha1_to_hex(base->object.sha1),
 				sha1_to_hex(head), "HEAD",
 				sha1_to_hex(next->object.sha1), oneline) ||
 			write_cache_as_tree(head, 0, NULL)) {
diff --git a/t/t3503-cherry-pick-root.sh b/t/t3503-cherry-pick-root.sh
new file mode 100755
index 0000000..b0faa29
--- /dev/null
+++ b/t/t3503-cherry-pick-root.sh
@@ -0,0 +1,30 @@
+#!/bin/sh
+
+test_description='test cherry-picking a root commit'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+	echo first > file1 &&
+	git add file1 &&
+	test_tick &&
+	git commit -m "first" &&
+
+	git symbolic-ref HEAD refs/heads/second &&
+	rm .git/index file1 &&
+	echo second > file2 &&
+	git add file2 &&
+	test_tick &&
+	git commit -m "second"
+
+'
+
+test_expect_success 'cherry-pick a root commit' '
+
+	git cherry-pick master &&
+	test first = $(cat file1)
+
+'
+
+test_done
-- 
1.5.6.1.376.g6b0fd

^ permalink raw reply related	[flat|nested] 52+ messages in thread

* Re: [PATCH] Allow cherry-picking root commits
  2008-07-04 15:19                 ` [PATCH] Allow cherry-picking root commits Johannes Schindelin
@ 2008-07-04 16:41                   ` Stephan Beyer
  2008-07-06  1:05                   ` Junio C Hamano
  2008-07-06 11:35                   ` [PATCH] Allow cherry-picking root commits Stephan Beyer
  2 siblings, 0 replies; 52+ messages in thread
From: Stephan Beyer @ 2008-07-04 16:41 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: Junio C Hamano, git, Daniel Barkalow, Christian Couder

On Fri, Jul 04, 2008 at 04:19:52PM +0100, Johannes Schindelin wrote:
> 	Anyhow, I give up on trying to convince you.

Great :)
Big thanks for the patch!

It also works with my rebase-i variant with just an unimportant "fatal"
which can be shut up by
--- a/git-sequencer.sh
+++ b/git-sequencer.sh
@@ -211,7 +211,7 @@ pick_one () {
 		;;
 	cherry-pick,*)
 		# fast forward
-		if test "$(git rev-parse --verify "$1^")" = \
+		if test "$(git rev-parse --verify "$1^" 2>/dev/null)" = \
 			"$(git rev-parse --verify HEAD)"
 		then
 			perform git reset --hard "$1"
###

Regards.

-- 
Stephan Beyer <s-beyer@gmx.net>, PGP 0x6EDDD207FCC5040F

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: git sequencer prototype
  2008-07-01  2:38 git sequencer prototype Stephan Beyer
  2008-07-01  2:38 ` [RFC/PATCH 1/4] Add git-sequencer shell prototype Stephan Beyer
  2008-07-01  8:51 ` git sequencer prototype Junio C Hamano
@ 2008-07-04 21:00 ` Alex Riesen
  2008-07-04 22:09   ` Junio C Hamano
  2 siblings, 1 reply; 52+ messages in thread
From: Alex Riesen @ 2008-07-04 21:00 UTC (permalink / raw)
  To: Stephan Beyer; +Cc: git, Junio C Hamano, Johannes Schindelin

Stephan Beyer, Tue, Jul 01, 2008 04:38:30 +0200:
> Hi,
> 
> here is the patchset for the git-sequencer prototype, documentation,
> test suite and a first git-am and git-rebase-i migration.
> Indeed, monster patches. ;)

BTW, how about renaming it in something short: git seq. There is
already a seq(1) in GNU coreutils, which does roughly the same (prints
a sequence of numbers), why not reuse the name?

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: git sequencer prototype
  2008-07-04 21:00 ` Alex Riesen
@ 2008-07-04 22:09   ` Junio C Hamano
  2008-07-04 22:23     ` Stephan Beyer
  2008-07-05  8:13     ` Alex Riesen
  0 siblings, 2 replies; 52+ messages in thread
From: Junio C Hamano @ 2008-07-04 22:09 UTC (permalink / raw)
  To: Alex Riesen; +Cc: Stephan Beyer, git, Johannes Schindelin

Alex Riesen <raa.lkml@gmail.com> writes:

> Stephan Beyer, Tue, Jul 01, 2008 04:38:30 +0200:
>> Hi,
>> 
>> here is the patchset for the git-sequencer prototype, documentation,
>> test suite and a first git-am and git-rebase-i migration.
>> Indeed, monster patches. ;)
>
> BTW, how about renaming it in something short: git seq. There is
> already a seq(1) in GNU coreutils, which does roughly the same (prints
> a sequence of numbers), why not reuse the name?

Is it advantageous to use shorter but less descriptive name for this
command?  It will be a backend to am/rebase and not something the users
will type from the command line, won't it?

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: git sequencer prototype
  2008-07-04 22:09   ` Junio C Hamano
@ 2008-07-04 22:23     ` Stephan Beyer
  2008-07-05  8:13     ` Alex Riesen
  1 sibling, 0 replies; 52+ messages in thread
From: Stephan Beyer @ 2008-07-04 22:23 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Alex Riesen, git, Johannes Schindelin

Junio C Hamano <gitster@pobox.com> wrote:
> Alex Riesen <raa.lkml@gmail.com> writes:
> > BTW, how about renaming it in something short: git seq. There is
> > already a seq(1) in GNU coreutils, which does roughly the same (prints
> > a sequence of numbers), why not reuse the name?
> 
> Is it advantageous to use shorter but less descriptive name for this
> command?

I also think descriptive names are nice for git even if the user should
type that, since tab completion exists.
When I've started with git, I loved the fact that I could use tab
completion to learn new git commands and that often the name was
descriptive enough to get an imagination of what the tool could do (and
then read the manpage to verify...)

> It will be a backend to am/rebase and not something the users
> will type from the command line, won't it?

Usually, but for special cases it is also nicely usable by the user
and the --status and --edit subcommands are intended to be used by the
user, too.

Regards,
  Stephan

-- 
Stephan Beyer <s-beyer@gmx.net>, PGP 0x6EDDD207FCC5040F

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: git sequencer prototype
  2008-07-04 22:09   ` Junio C Hamano
  2008-07-04 22:23     ` Stephan Beyer
@ 2008-07-05  8:13     ` Alex Riesen
  2008-07-05 10:12       ` Thomas Adam
  2008-07-05 10:13       ` Johannes Schindelin
  1 sibling, 2 replies; 52+ messages in thread
From: Alex Riesen @ 2008-07-05  8:13 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Stephan Beyer, git, Johannes Schindelin

Junio C Hamano, Sat, Jul 05, 2008 00:09:41 +0200:
> Alex Riesen <raa.lkml@gmail.com> writes:
> 
> > Stephan Beyer, Tue, Jul 01, 2008 04:38:30 +0200:
> >> Hi,
> >> 
> >> here is the patchset for the git-sequencer prototype, documentation,
> >> test suite and a first git-am and git-rebase-i migration.
> >> Indeed, monster patches. ;)
> >
> > BTW, how about renaming it in something short: git seq. There is
> > already a seq(1) in GNU coreutils, which does roughly the same (prints
> > a sequence of numbers), why not reuse the name?
> 
> Is it advantageous to use shorter but less descriptive name for this
> command?  It will be a backend to am/rebase and not something the users
> will type from the command line, won't it?

There is not a huge lot of possible meanings of "seq" in the given
context. Somehow I find it hard to believe someone will be confused by
a backend command with a short name "seq" (seq-uence-something?)

It'll make the lines shorter, less need to wrap them.

BTW, what does "am" (git am) mean?

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: git sequencer prototype
  2008-07-05  8:13     ` Alex Riesen
@ 2008-07-05 10:12       ` Thomas Adam
  2008-07-05 10:13       ` Johannes Schindelin
  1 sibling, 0 replies; 52+ messages in thread
From: Thomas Adam @ 2008-07-05 10:12 UTC (permalink / raw)
  To: Alex Riesen; +Cc: Junio C Hamano, Stephan Beyer, git, Johannes Schindelin

2008/7/5 Alex Riesen <fork0@users.sourceforge.net>:
> BTW, what does "am" (git am) mean?

I always thought of it as "apply mbox".

-- Thomas Adam

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: git sequencer prototype
  2008-07-05  8:13     ` Alex Riesen
  2008-07-05 10:12       ` Thomas Adam
@ 2008-07-05 10:13       ` Johannes Schindelin
  1 sibling, 0 replies; 52+ messages in thread
From: Johannes Schindelin @ 2008-07-05 10:13 UTC (permalink / raw)
  To: Alex Riesen; +Cc: Junio C Hamano, Stephan Beyer, git

Hi,

On Sat, 5 Jul 2008, Alex Riesen wrote:

> Junio C Hamano, Sat, Jul 05, 2008 00:09:41 +0200:
> > Alex Riesen <raa.lkml@gmail.com> writes:
> > 
> > > Stephan Beyer, Tue, Jul 01, 2008 04:38:30 +0200:
> > >> 
> > >> here is the patchset for the git-sequencer prototype, 
> > >> documentation, test suite and a first git-am and git-rebase-i 
> > >> migration. Indeed, monster patches. ;)
> > >
> > > BTW, how about renaming it in something short: git seq. There is 
> > > already a seq(1) in GNU coreutils, which does roughly the same 
> > > (prints a sequence of numbers), why not reuse the name?
> > 
> > Is it advantageous to use shorter but less descriptive name for this 
> > command?  It will be a backend to am/rebase and not something the 
> > users will type from the command line, won't it?
> 
> There is not a huge lot of possible meanings of "seq" in the given 
> context. Somehow I find it hard to believe someone will be confused by a 
> backend command with a short name "seq" (seq-uence-something?)

It might be a bit confusing, since "seq" _produces_ sequences, and 
"sequencer" is kind of an assembly line, getting commits in a sequence and 
then applying the corresponding changes in order.

> It'll make the lines shorter, less need to wrap them.

By that reasoning, we should have git-a, git-b, ... but that would not 
improve readability.

> BTW, what does "am" (git am) mean?

It means "applymbox", but that name was already taken.  And "am" turned 
out _not_ to replace "applymbox" right away as was expected, so it is a 
bit of unfortunate history.

Ciao,
Dscho

^ permalink raw reply	[flat|nested] 52+ messages in thread

* [PATCH v2 1/4] Add git-sequencer shell prototype
  2008-07-01  2:38 ` [RFC/PATCH 1/4] Add git-sequencer shell prototype Stephan Beyer
                     ` (2 preceding siblings ...)
  2008-07-03 13:10   ` Jakub Narebski
@ 2008-07-05 16:58   ` Stephan Beyer
  3 siblings, 0 replies; 52+ messages in thread
From: Stephan Beyer @ 2008-07-05 16:58 UTC (permalink / raw)
  To: git
  Cc: Christian Couder, Daniel Barkalow, Jakub Narebski,
	Johannes Schindelin, Junio C Hamano, Stephan Beyer

git sequencer is planned as a backend for user scripts
that execute a sequence of git instructions and perhaps
need manual intervention, for example git-rebase or git-am.

Mentored-by: Christian Couder <chriscool@tuxfamily.org>
Mentored-by: Daniel Barkalow <barkalow@iabervon.org>
Signed-off-by: Stephan Beyer <s-beyer@gmx.net>
---
Hi,

this should now contain your suggestions (where I posted interdiffs)
and some further bugfixes of bugs I stumbled across.
(If you prefer an interdiff to v1, I can also send one...)

Regards,
  Stephan

 .gitignore       |    1 +
 Makefile         |    1 +
 command-list.txt |    1 +
 git-sequencer.sh | 1921 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 1924 insertions(+), 0 deletions(-)
 create mode 100755 git-sequencer.sh

diff --git a/.gitignore b/.gitignore
index 4ff2fec..65a075b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -111,6 +111,7 @@ git-revert
 git-rm
 git-send-email
 git-send-pack
+git-sequencer
 git-sh-setup
 git-shell
 git-shortlog
diff --git a/Makefile b/Makefile
index 78e08d3..9db1a43 100644
--- a/Makefile
+++ b/Makefile
@@ -250,6 +250,7 @@ SCRIPT_SH += git-rebase--interactive.sh
 SCRIPT_SH += git-rebase.sh
 SCRIPT_SH += git-repack.sh
 SCRIPT_SH += git-request-pull.sh
+SCRIPT_SH += git-sequencer.sh
 SCRIPT_SH += git-sh-setup.sh
 SCRIPT_SH += git-stash.sh
 SCRIPT_SH += git-submodule.sh
diff --git a/command-list.txt b/command-list.txt
index 3583a33..44bb5b0 100644
--- a/command-list.txt
+++ b/command-list.txt
@@ -101,6 +101,7 @@ git-rev-parse                           ancillaryinterrogators
 git-rm                                  mainporcelain common
 git-send-email                          foreignscminterface
 git-send-pack                           synchingrepositories
+git-sequencer                           plumbingmanipulators
 git-shell                               synchelpers
 git-shortlog                            mainporcelain
 git-show                                mainporcelain common
diff --git a/git-sequencer.sh b/git-sequencer.sh
new file mode 100755
index 0000000..e4e4aad
--- /dev/null
+++ b/git-sequencer.sh
@@ -0,0 +1,1921 @@
+#!/bin/sh
+# A git sequencer prototype.
+
+SUBDIRECTORY_OK=Yes
+OPTIONS_KEEPDASHDASH=
+OPTIONS_SPEC="\
+git-sequencer [options] [--] [<file>]
+git-sequencer (--continue | --abort | --skip | --edit | --status)
+--
+ Options to start a sequencing process
+B,batch        run in batch-mode
+onto=          checkout the given commit or branch first
+q,quiet        suppress output
+v,verbose      be more verbose
+ Options to restart/change a sequencing process or show information
+continue       continue paused sequencer process
+abort          restore original branch and abort
+skip           skip current patch and continue
+status         show the status of the sequencing process
+edit           invoke editor to let user edit the remaining insns
+ Options to be used by user scripts
+caller=        provide information string: name|abort|cont|skip
+"
+
+. git-sh-setup
+require_work_tree
+
+git var GIT_COMMITTER_IDENT >/dev/null ||
+	die 'You need to set your committer info first'
+
+SEQ_DIR="$GIT_DIR/sequencer"
+TODO="$SEQ_DIR/todo"
+DONE="$SEQ_DIR/done"
+MSG="$SEQ_DIR/message"
+PATCH="$SEQ_DIR/patch"
+AUTHOR_SCRIPT="$SEQ_DIR/author-script"
+ORIG_AUTHOR_SCRIPT="$SEQ_DIR/author-script.orig"
+CALLER_SCRIPT="$SEQ_DIR/caller-script"
+WHY_FILE="$SEQ_DIR/why"
+MARK_PREFIX='refs/sequencer-marks'
+
+warn () {
+	echo "$*" >&2
+}
+
+cleanup () {
+	for ref in $(git for-each-ref --format='%(refname)' "$MARK_PREFIX")
+	do
+		git update-ref -d "$ref" "$ref" || return
+	done
+	rm -rf "$SEQ_DIR"
+}
+
+# Die if there has been a conflict
+die_to_continue () {
+	warn "$*"
+	test -z "$BATCHMODE" ||
+		die_abort 'Aborting, because of batch mode.'
+	test -z "$WHY" &&
+		echo 'conflict' >"$WHY_FILE" ||
+		echo "$WHY" >"$WHY_FILE"
+	exit 3
+}
+
+die_abort () {
+	restore
+	cleanup
+	die "$1"
+}
+
+quit () {
+	cleanup
+	test -z "$*" ||
+		echo "$*"
+	exit 0
+}
+
+perform () {
+	case "$VERBOSE" in
+	0)
+		"$@" >/dev/null
+		;;
+	1)
+		output=$("$@" 2>&1 )
+		status=$?
+		test $status -ne 0 && printf '%s\n' "$output"
+		return $status
+		;;
+	2)
+		"$@"
+		;;
+	esac
+}
+
+require_clean_work_tree () {
+	# test if working tree is dirty
+	git rev-parse --verify HEAD >/dev/null &&
+	git update-index --ignore-submodules --refresh &&
+	git diff-files --quiet --ignore-submodules &&
+	git diff-index --cached --quiet HEAD --ignore-submodules -- ||
+	die 'Working tree is dirty'
+}
+
+ORIG_REFLOG_ACTION="$GIT_REFLOG_ACTION"
+
+comment_for_reflog () {
+	if test -z "$ORIG_REFLOG_ACTION"
+	then
+		GIT_REFLOG_ACTION='sequencer'
+		test -z "$CALLER" ||
+			GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION ($CALLER)"
+		export GIT_REFLOG_ACTION
+	fi
+}
+
+# Get commit message from commit $1
+commit_message () {
+	git cat-file commit "$1" | sed -e '1,/^$/d'
+}
+
+LAST_COUNT=
+mark_action_done () {
+	sed -e 1q <"$TODO" >>"$DONE"
+	sed -e 1d <"$TODO" >"$TODO.new"
+	mv -f "$TODO.new" "$TODO"
+	if test "$VERBOSE" -gt 0
+	then
+		count=$(grep -c '^[^#]' <"$DONE")
+		total=$(expr "$count" + "$(grep -c '^[^#]' <"$TODO")")
+		if test "$LAST_COUNT" != "$count"
+		then
+			LAST_COUNT="$count"
+			test "$VERBOSE" -lt 1 ||
+				printf 'Sequencing (%d/%d)\r' "$count" "$total"
+			test "$VERBOSE" -lt 2 || echo
+		fi
+	fi
+}
+
+# Generate message, patch and author script files
+make_patch () {
+	parent_sha1=$(git rev-parse --verify "$1^" 2>/dev/null ||
+		echo '--root')
+	git diff-tree -p "$parent_sha1" "$1" >"$PATCH"
+	test -f "$MSG" ||
+		commit_message "$1" >"$MSG"
+	test -f "$AUTHOR_SCRIPT" ||
+		get_author_ident_from_commit "$1" >"$AUTHOR_SCRIPT"
+}
+
+# Generate a patch and die with "conflict" status code
+die_with_patch () {
+	make_patch "$1"
+	git rerere
+	die_to_continue "$2"
+}
+
+restore () {
+	git rerere clear
+
+	read HEADNAME <"$SEQ_DIR/head-name"
+	read HEAD <"$SEQ_DIR/head"
+	case $HEADNAME in
+	refs/*)
+		git symbolic-ref HEAD "$HEADNAME"
+		;;
+	esac &&
+	perform git reset --hard "$HEAD"
+}
+
+has_action () {
+	grep '^[^#]' "$1" >/dev/null
+}
+
+# Check if text file $1 contains a commit message
+has_message () {
+	test -n "$(sed -n -e '/^Signed-off-by:/d;/^[^#]/p' <"$1")"
+}
+
+# Count parents of commit $1
+count_parents() {
+	git cat-file commit "$1" | sed -n -e '1,/^$/p' | grep -c '^parent'
+}
+
+# Evaluate the author script to get author information to
+# apply it with "with_author git foo" then.
+get_current_author () {
+	if test -f "$AUTHOR_SCRIPT"
+	then
+		. "$AUTHOR_SCRIPT"
+	else
+		. "$ORIG_AUTHOR_SCRIPT"
+	fi || die_abort 'Author script is damaged. This must not happen!'
+}
+
+# Run command with author information
+with_author () {
+	GIT_AUTHOR_NAME="$GIT_AUTHOR_NAME" \
+	GIT_AUTHOR_EMAIL="$GIT_AUTHOR_EMAIL" \
+	GIT_AUTHOR_DATE="$GIT_AUTHOR_DATE" \
+		"$@"
+}
+
+# clean WHY_FILE and reset WHY
+clean_why () {
+	rm -f "$WHY_FILE"
+	WHY=
+}
+
+# Usage: pick_one (cherry-pick|revert) [-*|--edit] sha1
+pick_one () {
+	what="$1"
+	shift
+
+	case "$what,$1" in
+	revert,*)
+		test "$1" != '--edit' &&
+			what='revert --no-edit'
+		;;
+	cherry-pick,-*)
+		;;
+	cherry-pick,*)
+		# fast forward
+		if test "$(git rev-parse --verify "$1^" 2>/dev/null)" = \
+			"$(git rev-parse --verify HEAD)"
+		then
+			perform git reset --hard "$1"
+			return
+		fi
+		;;
+	esac
+	$use_perform git $what "$@"
+}
+
+nth_string () {
+	case "$1" in
+	*1[0-9]|*[04-9])
+		echo "$1th"
+		;;
+	*1)
+		echo "$1st"
+		;;
+	*2)
+		echo "$1nd"
+		;;
+	*3)
+		echo "$1rd"
+		;;
+	esac
+}
+
+make_squash_message () {
+	if test -f "$squash_msg"
+	then
+		count=$(($(sed -n -e 's/^# This is [^0-9]*\([1-9][0-9]*\).*/\1/p' \
+			<"$squash_msg" | sed -n -e '$p')+1))
+		echo "# This is a combination of $count commits."
+		sed -e '1d' -e '2,/^./{
+			/^$/d
+		}' <"$squash_msg"
+	else
+		count=2
+		echo '# This is a combination of 2 commits.'
+		echo '# The first commit message is:'
+		echo
+		commit_message HEAD
+	fi
+	echo
+	echo "# This is the $(nth_string "$count") commit message:"
+	echo
+	commit_message "$1"
+}
+
+make_squash_message_multiple () {
+	revlist=$(git rev-list --reverse "$sha1..HEAD")
+	count=$(echo "$revlist" | wc -l)
+	squash_i=0
+	echo "# This is a combination of $count commits."
+	for cur_sha1 in $revlist
+	do
+		squash_i=$(($squash_i+1))
+		if test -f "$squash_msg"
+		then
+			count=$(($count + $(sed -n -e \
+				's/^# This is [^0-9]*\([1-9][0-9]*\).*/\1/p' \
+				<"$squash_msg" | sed -n -e '$p')+1))
+			sed -e '1d' -e '2,/^./{
+				/^$/d
+			}' <"$squash_msg"
+		fi
+		echo
+		echo "# This is the $(nth_string "$squash_i") commit message:"
+		echo
+		commit_message "$cur_sha1"
+	done
+}
+
+peek_next_command () {
+	sed -n -e '/^#/d' -e '1s/ .*$//p' <"$TODO"
+}
+
+# If $1 is a mark, make a ref from it; otherwise keep it.
+# Note on marks:
+#  * :0 is allowed
+#  * :01 is the same as :1
+mark_to_ref () {
+	arg="$1"
+	ref=$(expr "x$arg" : 'x:0*\([0-9][0-9]*\)$')
+	test -n "$ref" &&
+		arg="$MARK_PREFIX/$ref"
+	printf '%s\n' "$arg"
+}
+
+mark_to_commit () {
+	git rev-parse --verify "$(mark_to_ref "$1")"
+}
+
+
+cannot_fallback () {
+	echo "$1"
+	die_to_continue 'Cannot fall back to three-way merge. Please hand-edit.'
+}
+
+
+fallback_3way () {
+	O_OBJECT=$(cd "$GIT_OBJECT_DIRECTORY" && pwd)
+
+	rm -fr "$PATCH-merge-"*
+	mkdir "$PATCH-merge-tmp-dir"
+
+	# First see if the patch records the index info that we can use.
+	git apply --build-fake-ancestor "$PATCH-merge-tmp-index" "$PATCH" &&
+	GIT_INDEX_FILE="$PATCH-merge-tmp-index" \
+		git write-tree >"$PATCH-merge-base+" ||
+	cannot_fallback 'Repository lacks necessary blobs to fall back on 3-way merge.'
+
+	echo 'Using index info to reconstruct a base tree...'
+	if GIT_INDEX_FILE="$PATCH-merge-tmp-index" \
+		git apply --cached "$PATCH"
+	then
+		mv "$PATCH-merge-base+" "$PATCH-merge-base"
+		mv "$PATCH-merge-tmp-index" "$PATCH-merge-index"
+	else
+		cannot_fallback "Did you hand edit your patch?
+It does not apply to blobs recorded in its index."
+	fi
+
+	test -f "$PATCH-merge-index" &&
+	his_tree=$(GIT_INDEX_FILE="$PATCH-merge-index" git write-tree) &&
+	orig_tree=$(cat "$PATCH-merge-base") &&
+	rm -fr "$PATCH-merge-"* || exit 1
+
+	echo 'Falling back to patching base and 3-way merge...'
+
+	# This is not so wrong.  Depending on which base we picked,
+	# orig_tree may be wildly different from ours, but his_tree
+	# has the same set of wildly different changes in parts the
+	# patch did not touch, so recursive ends up canceling them,
+	# saying that we reverted all those changes.
+
+	eval GITHEAD_$his_tree='"$firstline"'
+	export GITHEAD_$his_tree
+	git-merge-recursive "$orig_tree" -- HEAD "$his_tree" || {
+		git rerere
+		die 'Failed to merge in the changes.'
+	}
+}
+
+# Run hook "$@" (with arguments) if executable
+run_hook () {
+	test -z "$1" || return
+	hookname="$1"
+	hook="$GIT_DIR/hooks/$hookname"
+	shift
+	if test -x "$hook"
+	then
+		"$hook" "$@" ||
+			die_to_continue "Hook $hookname failed."
+	fi
+}
+
+# Add Signed-off-by: line if general option --signoff is given
+dashdash_signoff () {
+	add_signoff=
+	if test -n "$SIGNOFF"
+	then
+		last_signed_off_by=$(
+			sed -n -e '/^Signed-off-by: /p' <"$MSG" | sed -n -e '$p'
+		)
+		test "$last_signed_off_by" = "$SIGNOFF" ||
+			add_signoff=$(
+				test '' = "$last_signed_off_by" && echo
+				echo "$SIGNOFF"
+			)
+	fi
+	{
+		test -s "$MSG" && cat "$MSG"
+		test -n "$add_signoff" && echo "$add_signoff"
+	} >"$MSG.new"
+	mv "$MSG.new" "$MSG"
+}
+
+
+### --caller-related functions
+
+# Show string for caller invocation for --abort/--continue/--skip
+print_caller_info () {
+	case "$1" in
+	--abort)
+		echo "$CALLER_ABRT"
+		;;
+	--continue)
+		echo "$CALLER_CONT"
+		;;
+	--skip)
+		echo "$CALLER_SKIP"
+		;;
+	*)
+		warn 'Internal error: Unknown print_caller argument!'
+		;;
+	esac
+}
+
+# Print the program to invoke to (--)abort/continue/skip
+print_caller() {
+	caller_info=$(print_caller_info "$1")
+	if test -n "$CALLERCOMPARE" -a -n "$caller_info"
+	then
+		test -n "$CALLER" && printf "$CALLER "
+		echo "$caller_info"
+	else
+		echo "git sequencer $1"
+	fi
+}
+
+# Test if --caller was set correctly
+# $1 must be abort/continue/skip
+test_caller () {
+	caller_info=$(print_caller_info "--$1")
+	test -n "$CALLERCOMPARE" -a \
+		"$CALLERCOMPARE" != "$CALLERSTRING" -a \
+		-n "$caller_info" &&
+		die "You must use '$CALLER $caller_info' to $1!"
+}
+
+# Generate $CALLER_SCRIPT file from "git foo|--abort|--continue|--skip"
+# string $1
+generate_caller_script () {
+	echo "$1" | sed -e 's/^\(.*\)|\(.*\)|\(.*\)|\(.*\)$/\
+CALLERCOMPARE="\0"\
+CALLER="\1"\
+CALLER_ABRT="\2"\
+CALLER_CONT="\3"\
+CALLER_SKIP="\4"/' >"$CALLER_SCRIPT"
+}
+
+# Run caller script and set GIT_CHERRY_PICK_HELP
+assure_caller () {
+	test -r "$CALLER_SCRIPT" &&
+		. "$CALLER_SCRIPT"
+
+	GIT_CHERRY_PICK_HELP="
+After resolving the conflicts, mark the corrected paths with
+	git add <paths>
+or
+	git rm <paths>
+and run
+	$(print_caller --continue)
+Note, that your working tree must match the index."
+	export GIT_CHERRY_PICK_HELP
+}
+
+
+### Helpers for check_* functions:
+
+# Print a warning on todo checking
+todo_warn () {
+	printf 'Warning at line %d, %s: %s\n' "$line" "$command" "$*"
+}
+
+# Raise an error on todo checking
+todo_error () {
+	printf 'Error at line %d, %s: %s\n' "$line" "$command" "$*"
+	retval=1
+}
+
+# Test if $1 is a commit on todo checking
+commit_check () {
+	test "$(git cat-file -t "$1")" = commit ||
+		todo_error "'$1' is not a commit."
+}
+
+# A helper function to check if $1 is a an available mark during check_*
+arg_is_mark_check () {
+	for cur_mark in $available_marks
+	do
+		test "$cur_mark" -eq "${1#:}" && return
+	done
+	todo_error "Mark $1 is not yet defined."
+}
+
+# A helper function for check_* and mark usage:
+# check if "$1" is a commit or a defined mark
+arg_is_mark_or_commit_check () {
+	if expr "x$1" : 'x:[0-9][0-9]*$' >/dev/null
+	then
+		arg_is_mark_check "$1"
+	else
+		commit_check "$1"
+	fi
+}
+
+
+### Author script functions
+
+# Take "Name <e-mail>" in stdin and outputs author script
+make_author_script_from_string () {
+	sed -e "s/'/'"'\\'"''/g" \
+	    -e 's/^\(.*\) <\(.*\)>.*$/GIT_AUTHOR_NAME='\''\1'\''\
+GIT_AUTHOR_EMAIL='\''\2'\''\
+GIT_AUTHOR_DATE=/'
+}
+
+
+### General option functions (and options spec)
+
+OPTIONS_GENERAL=' General options:
+author= Override author
+C,reuse-commit= Reuse message and authorship data from commit
+F,file= Take commit message from given file
+m,message= Specify commit message
+M,reuse-message= Reuse message from commit
+signoff Add signoff
+e,edit Invoke editor to edit commit message'
+
+# Check if option is a general option
+check_general_option () {
+	general_shift=1
+	case "$1" in
+	--signoff)
+		return 0
+		;;
+	--author)
+		general_shift=2
+		author_opt="t$author_opt"
+		expr "x$2" : 'x.* <.*>' >/dev/null ||
+			todo_error "Author \"$2\" not in the correct format \"Name <e-mail>\"."
+		;;
+	-m)
+		general_shift=2
+		msg_opt="t$msg_opt"
+		;;
+	-C)
+		general_shift=2
+		msg_opt="t$msg_opt"
+		author_opt="t$author_opt"
+		commit_check "$2"
+		;;
+	-M)
+		general_shift=2
+		msg_opt="t$msg_opt"
+		commit_check "$2"
+		;;
+	-F)
+		general_shift=2
+		msg_opt="t$msg_opt"
+		test -r "$2" ||
+			todo_error "Cannot read file '$2'."
+		;;
+	-e)
+		test -z "$BATCHMODE" ||
+			todo_error '--batch and --edit options do not make sense together.'
+		;;
+	*)
+		return 1
+		;;
+	esac
+}
+
+# Set a general option variable or return 1
+handle_general_option () {
+	general_shift=1
+	case "$1" in
+	--signoff)
+		SIGNOFF=$(git-var GIT_COMMITTER_IDENT | sed -e '
+				s/>.*/>/
+				s/^/Signed-off-by: /')
+		;;
+	--author)
+		general_shift=2
+		AUTHOR=t
+		echo "$2" |
+			make_author_script_from_string >"$AUTHOR_SCRIPT"
+		;;
+	-m)
+		general_shift=2
+		MESSAGE="$2"
+		;;
+	-C)
+		general_shift=2
+		AUTHOR=t
+		get_author_ident_from_commit "$2" >"$AUTHOR_SCRIPT"
+		MESSAGE=$(commit_message "$2")
+		;;
+	-M)
+		general_shift=2
+		MESSAGE=$(commit_message "$2")
+		;;
+	-F)
+		general_shift=2
+		MESSAGE=$(cat "$2")
+		;;
+	-e)
+		EDIT=--edit
+		;;
+	*)
+		return 1
+		;;
+	esac
+}
+
+
+### Functions for checking and realizing TODO instructions
+# Note that options_*, check_* and insn_* function names are reserved.
+
+options_pause="\
+pause
+--
+"
+
+# Check the "pause" instruction
+check_pause () {
+	shift
+	test -z "$BATCHMODE" ||
+		todo_error '"pause" instruction and --batch do not make sense together.'
+	test $# -eq 0 ||
+		todo_error 'The pause instruction takes no arguments.'
+	return 0
+}
+
+# Realize the "pause" instruction
+insn_pause () {
+	mark_action_done
+	make_patch HEAD
+	if test "$VERBOSE" -gt 0
+	then
+		warn
+		warn 'You can now edit files and add them to the index.'
+		warn 'Once you are satisfied with your changes, run'
+		warn
+		warn "	$(print_caller --continue)"
+		warn
+		warn 'If you only want to change the commit message, run'
+		warn 'git commit --amend before.'
+	fi
+	echo 'pause' >"$WHY_FILE"
+	exit 2
+}
+
+
+options_patch="\
+patch [options] <file>
+--
+3,3way Fall back to 3-way merge
+k Pass to git-mailinfo (keep subject)
+n Pass to git-mailinfo (no utf8)
+$OPTIONS_GENERAL
+ Options passed to git-apply:
+R,reverse Reverse changes
+context= Ensure context of ... lines
+p= Remove ... leading slashes
+unidiff-zero Bypass unidiff checks
+exclude= Do not apply changes to given files
+no-add Ignore additions of patch
+whitespace= Set whitespace error behavior
+inaccurate-eof Support inaccurate EOFs
+u no-op (backward compatibility)
+binary no-op (backward compatibility)
+"
+
+# Check the "file" instruction
+check_patch () {
+	while test $# -gt 1
+	do
+		case "$1" in
+		-3|-k|-n|-u|--binary|-R|--reverse|--unidiff-zero|--no-add|--inaccurate-eof)
+			:
+			;;
+		-p|--whitespace|--exclude|--context)
+			shift
+			;;
+		--)
+			shift
+			break
+			;;
+		-*)
+			check_general_option "$@" ||
+				todo_warn "Unknown option $1"
+			;;
+		*)
+			todo_error "Wrong number of arguments. ($# given, 1 wanted)"
+			break
+			;;
+		esac
+		shift $general_shift
+	done
+
+	if test -f "$1" -a -r "$1"
+	then
+		grep -e '^diff' "$1" >/dev/null ||
+			todo_error "File '$1' contains no patch."
+	else
+		todo_error "Cannot open file '$1'."
+	fi
+	return 0
+}
+
+# Realize the "patch" instruction
+insn_patch () {
+	comment_for_reflog patch
+
+	apply_opts=
+	mailinfo_opts=
+	threeway=
+
+	# temporary files
+	infofile="$SEQ_DIR/patch-info"
+	msgfile="$SEQ_DIR/patch-msg"
+
+	while test "$#" -gt 1
+	do
+		case "$1" in
+		-3)
+			threeway=t
+			;;
+		-k|-n)
+			mailinfo_opts="$mailinfo_opts $1"
+			;;
+		-u|--binary)
+			: Do nothing. It is there due to b/c only.
+			;;
+		-R|--reverse|--unidiff-zero|--no-add|--inaccurate-eof)
+			apply_opts="$apply_opts $1"
+			;;
+		-p)
+			shift
+			apply_opts="$apply_opts -p$1"
+			;;
+		--whitespace|--exclude)
+			apply_opts="$apply_opts $1=$2"
+			shift
+			;;
+		--context)
+			shift
+			apply_opts="$apply_opts -C$1"
+			;;
+		--)
+			shift
+			break
+			;;
+		*)
+			handle_general_option "$@"
+			;;
+		esac
+		shift $general_shift
+	done
+
+	filename="$1"
+
+	mark_action_done
+
+	git mailinfo $mailinfo_opts "$msgfile" "$PATCH" \
+		<"$filename" >"$infofile" ||
+		die_abort 'Could not read or parse mail'
+
+	# if author not set by option, read author information of patch
+	if test -z "$AUTHOR"
+	then
+		cp "$ORIG_AUTHOR_SCRIPT" "$AUTHOR_SCRIPT"
+		sed -e "s/'/'"'\\'"''/g" -n -e '
+			s/^Author: \(.*\)$/GIT_AUTHOR_NAME='\''\1'\''/p;
+			s/^Email: \(.*\)$/GIT_AUTHOR_EMAIL='\''\1'\''/p;
+			s/^Date: \(.*\)$/GIT_AUTHOR_DATE='\''\1'\''/p
+		' <"$infofile" >>"$AUTHOR_SCRIPT"
+		# If sed's result is empty, we keep the original
+		# author script by appending.
+	fi
+
+	# Ignore every mail that's not containing a patch
+	test -s "$PATCH" || {
+		warn 'Does not contain patch!'
+		return 0
+	}
+
+	edit_msg=
+	if grep -e '^Subject:' "$infofile" >/dev/null
+	then
+		# add subject to commit message
+		sed -n -e '/^Subject:/ s/Subject: //p' <"$infofile"
+		echo
+		echo
+		cat "$msgfile"
+	else
+		cat "$msgfile"
+		edit_msg=t
+	fi | git stripspace >"$MSG"
+	rm -f "$infofile" "$msgfile"
+
+	firstline=$(sed -e '1q' <"$MSG")
+
+	get_current_author
+
+	test -n "$MESSAGE" && printf '%s\n' "$MESSAGE" >"$MSG"
+	test -z "$firstline" && firstline=$(sed -e '1q' <"$MSG")
+
+	dashdash_signoff
+
+	with_author run_hook applypatch-msg "$MSG"
+	failed=
+	git apply $apply_opts --index "$PATCH" || failed=t
+
+	if test -n "$failed" -a -n "$threeway" && (with_author fallback_3way)
+	then
+		# Applying the patch to an earlier tree and merging the
+		# result may have produced the same tree as ours.
+		git diff-index --quiet --cached HEAD -- && {
+			echo 'No changes -- Patch already applied.'
+			return 0
+			# XXX: do we want that?
+		}
+		# clear apply_status -- we have successfully merged.
+		failed=
+	fi
+
+	if test -n "$failed"
+	then
+		die_to_continue 'Patch failed.'
+		# XXX: We actually needed a git-apply flag that creates
+		# conflict markers and sets the DIFF_STATUS_UNMERGED flag.
+	fi
+
+	with_author run_hook pre-applypatch
+
+	test -n "$EDIT" && edit_msg=t
+	if ! has_message "$MSG" || test -n "$edit_msg"
+	then
+		echo "
+# Please enter the commit message for the applied patch.
+# (Comment lines starting with '#' will not be included)" >>"$MSG"
+
+		git_editor "$MSG" ||
+			die_with_patch 'Editor returned error.'
+		has_message "$MSG" ||
+			die_with_patch 'No commit message given.'
+	fi
+
+	tree=$(git write-tree) &&
+	parent=$(git rev-parse --verify HEAD) &&
+	commit=$(with_author git commit-tree "$tree" -p "$parent" <"$MSG") &&
+	git update-ref -m "$GIT_REFLOG_ACTION: $firstline" HEAD "$commit" "$parent" ||
+		die_to_continue 'Could not commit tree.'
+
+	test -x "$GIT_DIR/hooks/post-applypatch" &&
+		with_author "$GIT_DIR/hooks/post-applypatch"
+
+	return 0
+}
+
+
+options_pick="\
+pick [options] <commit>
+--
+R,reverse Revert introduced changes
+mainline= Specify parent number to use for merge commits
+$OPTIONS_GENERAL
+"
+
+# Check the "pick" instruction
+check_pick () {
+	mainline=
+	while test $# -gt 1
+	do
+		case "$1" in
+		-R)
+			;;
+		--mainline)
+			shift
+			mainline="$1"
+			test "$mainline" -gt 0 || {
+				todo_error '--mainline needs an integer beginning from 1.'
+				mainline=
+			}
+			;;
+		--)
+			shift
+			break
+			;;
+		-*)
+			check_general_option "$@" ||
+				todo_warn "Unknown option $1"
+			;;
+		*)
+			todo_error "Wrong number of arguments. ($# given, 1 wanted)"
+			break
+			;;
+		esac
+		shift $general_shift
+	done
+
+	if test -n "$mainline"
+	then
+		parents=$(count_parents "$1")
+		test "$parents" -lt "$mainline" &&
+			todo_error "Commit has only $parents (less than $mainline) parents."
+		test "$parents" -eq 1 &&
+			todo_warn 'Commit is not a merge at all.'
+	fi
+
+	commit_check "$1"
+
+	return 0
+}
+
+# Realize the "pick" instruction
+insn_pick () {
+	op=cherry-pick
+	mainline=
+	while test $# -gt 1
+	do
+		case "$1" in
+		-R)
+			op=revert
+			;;
+		--mainline)
+			mainline="$1 $2"
+			shift
+			;;
+		--)
+			shift
+			break
+			;;
+		-*)
+			handle_general_option "$@"
+			;;
+		esac
+		shift $general_shift
+	done
+
+	sha1=$(git rev-parse --verify "$1")
+
+	comment_for_reflog pick
+
+	mark_action_done
+
+	edit_msg="$EDIT"
+
+	# Don't edit on pick, but later, if author or message given.
+	test -n "$AUTHOR" -o -n "$MESSAGE" && edit_msg=
+
+	# Be kind to users and ignore --mainline=1 on non-merge commits
+	test -n "$mainline" -a 2 -gt $(count_parents "$sha1") && mainline=
+
+	use_perform=
+	test -n "$edit_msg" ||
+		use_perform=perform
+
+	pick_one "$op" $edit_msg $mainline $sha1 ||
+		die_with_patch $sha1 "Could not apply $sha1"
+
+	test -n "$EDIT" ||
+		use_perform=perform
+
+	get_current_author
+	signoff=
+	test -n "$SIGNOFF" && signoff=-s
+	if test -n "$AUTHOR" -a -n "$MESSAGE"
+	then
+		# this is just because we only want to do ONE amending commit
+		$use_perform git commit --amend $EDIT $signoff --no-verify \
+			--author "$GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>" \
+			--message="$MESSAGE"
+	elif test -n "$AUTHOR"
+	then
+		# correct author if AUTHOR is set
+		$use_perform git commit --amend $EDIT --no-verify -C HEAD \
+			--author "$GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>"
+	elif test -n "$MESSAGE"
+	then
+		# correct commit message if MESSAGE is set
+		$use_perform git commit --amend $EDIT $signoff --no-verify \
+			-C HEAD --message="$MESSAGE"
+	elif test -n "$SIGNOFF"
+	then
+		# only add signoff
+		$use_perform git commit --amend $EDIT $signoff --no-verify \
+			-C HEAD
+	fi
+
+	return 0
+}
+
+options_edit="\
+edit <commit>
+--
+"
+
+# Check the "edit" instruction
+check_edit () {
+	shift
+	test -z "$BATCHMODE" ||
+		todo_error '"edit" instruction and --batch do not make sense together.'
+	test $# -eq 1 ||
+		todo_error "Wrong number of arguments. ($# given, 1 wanted)"
+	return 0
+}
+
+# Realize the "edit" instruction
+insn_edit () {
+	shift
+	insn_pick "$1"
+
+	# work around mark_action_done in insn_pause
+	echo '# pausing' >"$TODO.new"
+	cat "$TODO" >>"$TODO.new"
+	mv -f "$TODO.new" "$TODO"
+	insn_pause
+}
+
+
+options_squash="\
+squash <commit>
+squash [options] --from <mark>
+--
+from Squash all commits from <mark>
+collect-signoffs Collect Signed-off-by: lines
+include-merges Do not fail on merge commits
+$OPTIONS_GENERAL
+"
+
+# Check the "squash" instruction
+check_squash () {
+	from=
+	collect=
+	merges=
+	while test $# -gt 1
+	do
+		case "$1" in
+		--from)
+			from=t
+			;;
+		--collect-signoffs)
+			collect=t
+			todo_warn 'Not yet implemented.'
+			;;
+		--include-merges)
+			merges=t
+			;;
+		--)
+			shift
+			break
+			;;
+		*)
+			check_general_option "$@" ||
+				todo_error "Unknown option $1"
+			;;
+		esac
+		shift $general_shift
+	done
+
+	# in --from mode?
+	if test -n "$from"
+	then
+		test -z "$merges" &&
+			cat "$DONE" "$TODO" |
+			sed -n -e '/^[ \t]*mark[ \t]*:\{0,1\}'"${1#:}"'\($\|[^0-9]\)/,'"$line"'p' |
+			grep '^[ \t]*merge' >/dev/null &&
+			todo_error "$1..HEAD contains a merge commit. You may try --include-merges."
+
+		arg_is_mark_check "$1"
+	else
+		test -n "$merges" &&
+			todo_error '--include-merges only makes sense with --from <mark>.'
+		test -n "$collect" &&
+			todo_error '--collect-signoffs only makes sense with --from <mark>.'
+
+		commit_check "$1"
+	fi
+
+	return 0
+}
+
+# Realize the "squash" instruction
+insn_squash () {
+	squash_msg="$MSG-squash"
+	from=
+	while test $# -gt 1
+	do
+		case "$1" in
+		--from)
+			from=t
+			;;
+		--collect-signoffs)
+			warn '--collect-signoffs is not implemented.'
+			# XXX
+			;;
+		--include-merges)
+			: # This has to be done during check_squash
+			;;
+		--)
+			shift
+			break
+			;;
+		*)
+			handle_general_option "$@"
+			;;
+		esac
+		shift $general_shift
+	done
+
+	if test -n "$from"
+	then
+		sha1=$(mark_to_commit ":${1#:}")
+	else
+		sha1=$(git rev-parse --verify "$1")
+	fi
+
+	comment_for_reflog squash
+
+	# Hm, somehow I don't think --skip on a conflicting squash
+	# may be useful, but if someone wants to do it, it should
+	# do the obvious: skip what squash would do.
+	echo "$(git rev-parse HEAD)" >"$SEQ_DIR/skiphead"
+
+	mark_action_done
+
+	if test -n "$MESSAGE"
+	then
+		printf '%s\n' "$MESSAGE" >"$MSG"
+	else
+		if test -n "$from"
+		then
+			make_squash_message_multiple "$sha1" >"$MSG"
+		else
+			make_squash_message "$sha1" >"$MSG"
+		fi
+	fi
+
+	case "$(peek_next_command)" in
+	squash)
+		edit_commit=
+		use_perform=perform
+		cp "$MSG" "$squash_msg"
+		;;
+	*)
+		edit_commit=-e
+		use_perform=
+		rm -f "$squash_msg" || exit
+		;;
+	esac
+
+	test -n "$MESSAGE" && edit_commit=
+	test -n "$EDIT" && edit_commit=-e
+
+	# is --author (or equivalent) set?
+	if test -n "$AUTHOR"
+	then
+		# evaluate author script
+		get_current_author
+	else
+		# if --author is not given, we get the authorship
+		# information from the commit before.
+		eval "$(get_author_ident_from_commit HEAD)"
+		# but we do not write an author script
+	fi
+
+	# --from or not
+	failed=
+	if test -n "$from"
+	then
+		perform git reset --soft "$sha1"
+	else
+		perform git reset --soft HEAD^
+
+		pick_one cherry-pick -n "$sha1" || failed=t
+	fi
+
+	dashdash_signoff
+
+	if test -z "$failed"
+	then
+		# This is like --amend, but with a different message
+		with_author $use_perform git commit --no-verify \
+			-F "$MSG" $edit_commit || failed=t
+	else
+		cp "$MSG" "$GIT_DIR/MERGE_MSG"
+		warn
+		warn "Could not apply $sha1..."
+		die_with_patch $sha1 ""
+	fi
+
+	return 0
+}
+
+
+options_mark="\
+mark <mark>
+--
+"
+
+# Check the "mark" instruction
+check_mark () {
+	shift
+	test $# -eq 1 ||
+		todo_error "Wrong number of arguments. ($# given, 1 wanted)"
+	my_mark=$(expr "x${1#:}" : 'x0*\([0-9][0-9]*\)$')
+	test -n "$my_mark" ||
+		todo_error "Mark $1 not an integer."
+	expr "x$available_marks " : " $my_mark " >/dev/null &&
+		todo_error "Mark :$my_mark already defined. Choose another integer."
+	available_marks="$available_marks $my_mark"
+
+	return 0
+}
+
+# Realize the "mark" instruction
+insn_mark () {
+	shift
+	given="$1"
+
+	mark_action_done
+
+	mark=$(mark_to_ref ":${given#:}")
+	git update-ref "$mark" HEAD
+	return 0
+}
+
+
+options_merge="\
+merge [options] <commit-ish> ...
+--
+standard Generate default commit message
+s,strategy= Use merge strategy ...
+$OPTIONS_GENERAL
+"
+
+# Check the "merge" instruction
+check_merge () {
+	while test $# -gt 1
+	do
+		case "$1" in
+		--standard)
+			msg_opt="t$msg_opt"
+			;;
+		-s)
+			shift
+			;;
+		--)
+			shift
+			break
+			;;
+		-*)
+			check_general_option "$@" ||
+				todo_error "Unknown option $1"
+			;;
+		esac
+		shift $general_shift
+	done
+
+	test $# -gt 0 ||
+		todo_error 'What are my parents? Need new parents!'
+
+	while test $# -gt 0
+	do
+		arg_is_mark_or_commit_check "$1"
+		shift
+	done
+	return 0
+}
+
+# Realize the "merge" instruction
+insn_merge () {
+	comment_for_reflog merge
+
+	standard=
+
+	while test $# -gt 1
+	do
+		case "$1" in
+		--standard)
+			standard=t
+			;;
+		-s)
+			shift
+			strategy="-s $1"
+			;;
+		--)
+			shift
+			break
+			;;
+		-*)
+			handle_general_option "$@"
+			;;
+		esac
+		shift $general_shift
+	done
+
+	new_parents=
+	for p in "$@"
+	do
+		new_parents="$new_parents $(mark_to_ref $p)"
+	done
+	new_parents="${new_parents# }"
+
+	get_current_author
+
+	if test -n "$standard"
+	then
+		for cur_parent in $new_parents
+		do
+			printf '%s\t\t%s' \
+				"$(git rev-parse "$cur_parent")" "$cur_parent"
+		done | git fmt-merge-msg >"$MSG"
+	fi
+
+	test -n "$MESSAGE" &&
+		printf '%s\n' "$MESSAGE" >"$MSG"
+
+	dashdash_signoff
+	if ! has_message "$MSG" || test -n "$EDIT"
+	then
+		echo "
+# Please enter the merge commit message.
+# (Comment lines starting with '#' will not be included)" >>"$MSG"
+
+		git_editor "$MSG" ||
+			die_with_patch 'Editor returned error.'
+		has_message "$MSG" ||
+			die_with_patch 'No commit message given.'
+	fi
+
+	mark_action_done
+	if ! with_author perform git merge $strategy -m junk $new_parents
+	then
+		git rerere
+		cp "$MSG" "$GIT_DIR/MERGE_MSG"
+		die_to_continue 'Error merging'
+	fi
+	with_author perform git commit --amend -F "$MSG" --no-verify
+	return 0
+}
+
+
+options_reset="\
+reset <commit-ish>
+--
+"
+
+# Check the "reset" instruction
+check_reset () {
+	shift
+	test $# -eq 1 ||
+		todo_error "Wrong number of arguments. ($# given, 1 wanted)"
+	arg_is_mark_or_commit_check "$1"
+
+	return 0
+}
+
+# Realize the "reset" instruction
+insn_reset () {
+	shift
+	comment_for_reflog reset
+
+	mark_action_done
+	perform git reset --hard "$(mark_to_commit "$1")"
+}
+
+
+options_ref="\
+ref <ref>
+--
+"
+
+# Check the "ref" instruction
+check_ref () {
+	shift
+	test $# -eq 1 ||
+		todo_error "Wrong number of arguments. ($# given, 1 wanted)"
+	return 0
+}
+
+# Realize the "ref" instruction
+insn_ref () {
+	shift
+	comment_for_reflog ref
+
+	mark_action_done
+	perform git update-ref "$1" HEAD
+}
+
+
+### Instruction main loop
+
+# Run check_* or insn_* with massaged options
+# Usage: run_insn (check|do) <insn> <insn options>
+run_insn () {
+	c_or_i="$1"
+	insn="$2"
+	shift
+	shift
+	eval "option_spec=\"\$options_$insn\""
+	eval "$(printf "%s" "$option_spec" |
+		git rev-parse --parseopt -- "$@" ||
+		echo return)"
+	case "$c_or_i" in
+	check)
+		check_$insn "$@"
+		;;
+	do)
+		insn_$insn "$@"
+		;;
+	esac
+}
+
+# Execute the first line of the current TODO file
+execute_next () {
+	rm -f "$MSG"
+	echo 'HEAD' >"$SEQ_DIR/skiphead"
+	read command rol <"$TODO"
+	case "$command" in
+	'#'*|'')
+		mark_action_done
+		;;
+	*)
+		test "$VERBOSE" -gt 1 &&
+			echo "Next line: $command $rol"
+
+		# reset general options
+		rm -f "$AUTHOR_SCRIPT"
+		general_shift=1
+		AUTHOR=
+		EDIT=
+		MESSAGE=
+		SIGNOFF=
+		# XXX: eval is evil!
+		eval "run_insn do $command $rol" ||
+			die_to_continue 'An unexpected error occured.'
+		;;
+	esac
+}
+
+# Execute the rest of the TODO file and finish
+execute_rest () {
+	while has_action "$TODO"
+	do
+		execute_next
+	done
+
+	comment_for_reflog finish
+	if test -n "$ONTO"
+	then
+		git update-ref -m "$GIT_REFLOG_ACTION: $ONTO" "$ONTO" HEAD &&
+		git symbolic-ref HEAD "$ONTO"
+	fi &&
+	quit
+}
+
+
+# Main loop to check instructions
+todo_check () {
+	todo="$TODO"
+	test -n "$1" && todo="$1"
+
+	available_marks=' '
+	for cur_mark in $(git for-each-ref --format='%(refname)' "$MARK_PREFIX")
+	do
+		available_marks="$available_marks ${cur_mark##*/}"
+	done
+
+	retval=0
+	line=1
+	while read command rol
+	do
+		case "$command" in
+		'#'*|'')
+			;;
+		*)
+			eval 'test -n "$options_'"$command"'"' || {
+				retval=1
+				todo_error "Unknown $command instruction"
+				continue
+			}
+
+			general_shift=1
+			msg_opt=
+			author_opt=
+			eval "run_insn check $command $rol" ||
+				todo_error "Unknown option used"
+			expr "$msg_opt" : 'ttt*' >/dev/null &&
+				todo_error 'You can only provide one commit message option.'
+			expr "$author_opt" : 'ttt*' >/dev/null &&
+				todo_error 'You can only provide one author option.'
+			;;
+		esac
+		line=$(expr "$line" + 1)
+	done <"$todo"
+	return $retval
+}
+
+prepare_editable_todo () {
+	echo '# ALREADY DONE:'
+	sed -e 's/^/#  /' <"$DONE"
+	echo '# '
+	echo "$markline"
+	cat "$TODO"
+}
+
+get_saved_options () {
+	read VERBOSE <"$SEQ_DIR/verbose"
+	read ONTO <"$SEQ_DIR/onto"
+	test -f "$WHY_FILE" &&
+		read WHY <"$WHY_FILE"
+	return 0
+}
+
+# Realize sequencer invocation
+do_startup () {
+	test -d "$SEQ_DIR" &&
+		die 'sequencer already started'
+
+	require_clean_work_tree
+
+	HEAD=$(git rev-parse --verify HEAD) ||
+		die 'No HEAD?'
+
+	mkdir "$SEQ_DIR" ||
+		die "Could not create temporary $SEQ_DIR"
+
+	# save options
+	echo "$VERBOSE" >"$SEQ_DIR/verbose"
+	test -n "$CALLERSTRING" &&
+		generate_caller_script "$CALLERSTRING"
+	# generate empty DONE and "onto" file
+	: >"$DONE"
+	: >"$SEQ_DIR/onto"
+
+	if test -n "$BATCHMODE"
+	then
+		GIT_CHERRY_PICK_HELP='  Aborting (batch mode)'
+		export GIT_CHERRY_PICK_HELP
+	else
+		assure_caller
+	fi
+
+	comment_for_reflog start
+
+	# save old head before checking out the given <branch>
+	git symbolic-ref HEAD >"$SEQ_DIR/head-name" 2>/dev/null ||
+		echo 'detached HEAD' >"$SEQ_DIR/head-name"
+	echo $HEAD >"$SEQ_DIR/head"
+	# do it here so that die_abort can work ;)
+
+	if test -n "$ONTO"
+	then
+		# if ONTO is a branch name, then keep it, otherwise
+		# we don't care anymore and erase ONTO.
+		if git-show-ref --quiet --verify -- "refs/heads/${ONTO##*/}"
+		then
+			ONTO="refs/heads/${ONTO##*/}"
+			echo "$ONTO" >"$SEQ_DIR/onto"
+			perform git checkout "$(git rev-parse "$ONTO")" ||
+				die_abort "Could not checkout branch $ONTO"
+		else
+			perform git checkout "$ONTO" ||
+				die_abort "Could not checkout commit $ONTO"
+			ONTO=
+		fi
+	fi
+
+	(git var GIT_AUTHOR_IDENT || git var COMMITTER_IDENT) |
+		make_author_script_from_string >"$ORIG_AUTHOR_SCRIPT"
+
+	# read from file or from stdin?
+	if test -z "$1"
+	then
+		: >"$TODO" ||
+			die_abort "Could not generate TODO file $TODO"
+		while read line
+		do
+			printf '%s\n' "$line" >>"$TODO" ||
+				die_abort "Could not append to TODO file $TODO"
+		done
+	else
+		cp "$1" "$TODO" ||
+			die_abort "Could not find TODO file $1."
+	fi
+
+	has_action "$TODO" || die_abort 'Nothing to do'
+	todo_check || WHY=todo die_to_continue "TODO file contains errors.
+Fix with git sequencer --edit or abort with $(print_caller --abort)."
+
+	execute_rest
+	exit
+}
+
+# Realize --continue.
+do_continue () {
+	test -d "$SEQ_DIR" || die 'No sequencer running'
+	test_caller 'continue'
+
+	todo_check || WHY=todo die_to_continue "TODO file contains errors.
+Fix with git sequencer --edit or abort with $(print_caller --abort)."
+
+	comment_for_reflog continue
+
+	# Sanity check
+	git rev-parse --verify HEAD >/dev/null ||
+		die_to_continue 'Cannot read HEAD'
+	git update-index --ignore-submodules --refresh &&
+		git diff-files --quiet --ignore-submodules ||
+		die_to_continue 'Working tree is dirty. (Use git add or git stash first?)'
+
+	get_saved_options
+
+	# do we have anything to commit? (staged changes)
+	if ! git diff-index --cached --quiet --ignore-submodules HEAD --
+	then
+		get_current_author
+
+		# After "pause", we should amend (merge-safe!).
+		# On conflict, we do not have a commit to amend, so we
+		# should just commit.
+		case "$WHY" in
+		pause)
+			with_author git commit --amend --no-verify -F "$MSG" -e ||
+				die_to_continue 'Could not commit staged changes.'
+			;;
+		conflict)
+			with_author git commit --no-verify -F "$MSG" -e ||
+				die_to_continue 'Could not commit staged changes.'
+			echo '# resolved CONFLICTS of the last instruction' >>"$DONE"
+			;;
+		*)
+			die_to_continue 'There are staged changes. Do not know what to do with them.'
+		esac
+	fi
+
+	require_clean_work_tree
+	clean_why
+	execute_rest
+	exit
+}
+
+# Realize --abort.
+do_abort () {
+	test -d "$SEQ_DIR" || die 'No sequencer running'
+	test_caller 'abort'
+
+	comment_for_reflog abort
+	restore
+	quit
+}
+
+# Realize --skip.
+do_skip () {
+	test -d "$SEQ_DIR" || die 'No sequencer running'
+	test_caller 'skip'
+
+	todo_check || WHY=todo die_to_continue "TODO file contains errors.
+Fix with git sequencer --edit or abort with $(print_caller --abort)."
+	get_saved_options
+
+	comment_for_reflog skip
+	git rerere clear
+	clean_why
+
+	perform git reset --hard "$(cat "$SEQ_DIR/skiphead")" &&
+	echo '# SKIPPED the last instruction' >>"$DONE" &&
+		execute_rest
+	exit
+}
+
+# Realize --edit.
+do_edit () {
+	test -d "$SEQ_DIR" || die 'No sequencer running'
+
+	markline='### BEGIN EDITING BELOW THIS LINE ###'
+	prepare_editable_todo >"$TODO.new"
+
+	# XXX: does not make sense
+	#      when input does not come from a terminal
+	git_editor "$TODO.new" ||
+		die 'Editor returned an error.'
+
+	if test -t 0 -a -t 1
+	then
+		echo
+		# interactive:
+		until has_action "$TODO.new" && todo_check "$TODO.new"
+		do
+			has_action "$TODO.new" || echo 'TODO file empty.'
+			printf 'What to do with the file? [c]orrect/[e]dit again/[r]ewind/[s]ave/[?] '
+			read reply
+			case "$reply" in
+			[cC]*)
+				git_editor "$TODO.new"
+				;;
+			[eE]*)
+				prepare_editable_todo >"$TODO.new"
+				git_editor "$TODO.new"
+				;;
+			[rRxXqQ]*)
+				rm -f "$TODO.new"
+				exit 0
+				;;
+			[sS]*)
+				test "$WHY" != 'pause' ||
+					echo 'todo' >"$WHY_FILE"
+				break
+				;;
+			[?hH]*)
+				cat <<EOF
+
+Help:
+s - save TODO file and exit
+c - respawn editor to correct TODO file
+e - drop changes and respawn editor on original TODO file
+r - drop changes and exit as if nothing happened
+? - print this help
+EOF
+				;;
+			esac
+			echo
+		done
+	else
+		# defaults:
+		has_action "$TODO.new" || quit "Nothing to do"
+		todo_check || die 'TODO file contains errors. Aborting.'
+	fi
+	cp "$TODO" "$TODO.old"
+	if grep "^$markline" "$TODO.new" >/dev/null
+	then
+		sed -e "1,/^$markline/d" <"$TODO.new" >"$TODO"
+	else
+		cp "$TODO.new" "$TODO"
+	fi
+	rm -f "$TODO.new"
+	echo
+	echo 'TODO file contains:'
+	echo
+	cat "$TODO"
+	exit 0
+}
+
+# Realize --status.
+do_status () {
+	test -d "$SEQ_DIR" || die 'No sequencer running.'
+	get_saved_options
+
+	if has_action "$DONE"
+	then
+		echo 'Already done:'
+		sed -e 's/^/  /' <"$DONE"
+		echo
+	fi
+	case "$WHY" in
+	pause)
+		echo 'Intentionally paused.'
+		;;
+	conflict)
+		echo 'Interrupted by conflict at'
+		sed -n -e 's/^/  /;$p' <"$DONE"
+		;;
+	todo)
+		echo 'Interrupted because of errors in the TODO file.'
+		;;
+	*)
+		echo 'Current state is broken.'
+	esac
+	test "$VERBOSE" -gt 1 && echo 'Running verbosely.'
+	test "$VERBOSE" -lt 1 && echo 'Running quietly.'
+	test -n "$ONTO" &&
+		echo "Sequencing on branch ${ONTO##*/}."
+	if has_action "$TODO"
+	then
+		echo
+
+		echo 'Still to do:'
+		sed -e 's/^/  /' <"$TODO"
+	fi
+	if test "$WHY" = todo
+	then
+		echo
+		echo 'But there are errors. To edit, run:'
+		echo '    git sequencer --edit'
+	else
+		echo
+		echo 'To abort & restore, invoke:'
+		echo "    $(print_caller --abort)"
+		echo 'To continue, invoke:'
+		echo "    $(print_caller --continue)"
+		test "$WHY" = 'pause' || {
+			echo 'To skip the current instruction, invoke:'
+			echo "    $(print_caller --skip)"
+		}
+	fi
+	exit 0
+}
+
+is_standalone () {
+	test $# -eq 2 &&
+	test "$2" = '--' &&
+	test -z "$BATCHMODE" &&
+	test -z "$onto" &&
+	test "$VERBOSE" -eq 1
+}
+
+
+### Option handling and startup
+
+onto=
+BATCHMODE=
+CALLERSTRING=
+VERBOSE=1
+CALLER=
+CALLER_ABRT=
+CALLER_CONT=
+CALLER_SKIP=
+while test $# -gt 0
+do
+	case "$1" in
+	--continue)
+		is_standalone "$@" || usage
+		assure_caller
+		do_continue
+		;;
+	--abort)
+		is_standalone "$@" || usage
+		assure_caller
+		do_abort
+		;;
+	--skip)
+		is_standalone "$@" || usage
+		assure_caller
+		do_skip
+		;;
+	--status)
+		is_standalone "$@" || usage
+		assure_caller
+		do_status
+		;;
+	--edit)
+		is_standalone "$@" || usage
+		do_edit
+		;;
+	--caller)
+		###############################################################
+		# This feature is for user scripts only. (Hence undocumented.)
+		# User scripts should pass an argument like:
+		# --caller="git foo|abrt|go|next"
+		# on every git sequencer call. (It is only ignored on
+		# --edit and --status.)
+		# So git sequencer knows that
+		# "git foo abrt" will abort,
+		# "git foo go" will continue and
+		# "git foo next" will skip the sequencing process.
+		# This is useful if your user script does some extra
+		# preparations or cleanup before/after calling
+		#   git sequencer --caller="..." --abort|--continue|--skip
+		#
+		# Running git-sequencer without the same --caller string
+		# fails then, until the sequencing process has finished or
+		# aborted.
+		#
+		# Btw, --caller="my_tiny_script.sh|-a||" will be
+		# interpreted, that users must invoke "my_tiny_script.sh -a"
+		# to abort, but can invoke "git sequencer --continue" and
+		# "git sequencer --skip" to continue or skip.
+		# And it is also possible to provide three different scripts
+		# by --caller="|script 1|tool 2|util 3".
+		#
+		# If your user script does not need any special
+		# abort/continue/skip behavior, then just do NOT pass
+		# the --caller option.
+		###############################################################
+		shift
+		expr "x$1" : 'x.*|.*|.*|.*$' >/dev/null ||
+			die 'Wrong --caller format.'
+		CALLERSTRING="$1"
+		;;
+	-B)
+		BATCHMODE=t
+		# XXX: we still have abort on editor invokations
+		;;
+	--onto)
+		shift
+		ONTO="$1"
+		test $(git cat-file -t "$ONTO") = 'commit' ||
+			die "$ONTO is no commit or branch."
+		;;
+	-q)
+		VERBOSE=0
+		# XXX: If there were no editors,
+		# we could just do exec >/dev/null
+		;;
+	-v)
+		VERBOSE=2
+		# or increment it if we have several verbosity steps
+		;;
+	--)
+		shift
+		break
+		;;
+	*)
+		die "$1 currently not implemented."
+		;;
+	esac
+	shift
+done
+test $# -gt 1 && usage
+
+do_startup "$1"
-- 
1.5.6.361.g18ea7

^ permalink raw reply related	[flat|nested] 52+ messages in thread

* [PATCH v2 2/4] Add git-sequencer prototype documentation
  2008-07-01  2:38   ` [RFC/PATCH 2/4] Add git-sequencer prototype documentation Stephan Beyer
  2008-07-01  2:38     ` [RFC/PATCH 3/4] Add git-sequencer test suite (t3350) Stephan Beyer
  2008-07-01 13:02     ` [RFC/PATCH 2/4] Add git-sequencer prototype documentation Jakub Narebski
@ 2008-07-05 17:00     ` Stephan Beyer
  2008-07-08 10:37       ` Jakub Narebski
  2 siblings, 1 reply; 52+ messages in thread
From: Stephan Beyer @ 2008-07-05 17:00 UTC (permalink / raw)
  To: git
  Cc: Christian Couder, Daniel Barkalow, Jakub Narebski,
	Johannes Schindelin, Junio C Hamano, Stephan Beyer

Mentored-by: Christian Couder <chriscool@tuxfamily.org>
Mentored-by: Daniel Barkalow <barkalow@iabervon.org>
Signed-off-by: Stephan Beyer <s-beyer@gmx.net>
---
This contains some feedback of Jakub.

 Documentation/git-sequencer.txt |  387 +++++++++++++++++++++++++++++++++++++++
 1 files changed, 387 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/git-sequencer.txt

diff --git a/Documentation/git-sequencer.txt b/Documentation/git-sequencer.txt
new file mode 100644
index 0000000..1a9eda4
--- /dev/null
+++ b/Documentation/git-sequencer.txt
@@ -0,0 +1,387 @@
+git-sequencer(1)
+================
+
+NAME
+----
+git-sequencer - Execute a sequence of git instructions
+
+SYNOPSIS
+--------
+[verse]
+'git-sequencer' [--batch] [--onto=<base>] [--verbose|--quiet] [<file>]
+'git-sequencer' --continue | --skip | --abort | --edit | --status
+
+
+DESCRIPTION
+-----------
+Executes a sequence of git instructions to HEAD or `<base>`.
+The sequence is given by `<file>` or standard input.
+Also see 'TODO FILE FORMAT' below.
+
+Before doing anything, the TODO file is checked for correct syntax
+and sanity.
+
+In case of a conflict or request in the TODO file, git-sequencer will
+pause. On conflict you can use git-diff to locate the markers (`<<<<<<<`)
+and make edits to resolve the conflict.
+
+For each file you edit, you need to tell git the changes by doing
+
+    git add <file>
+
+After resolving the conflict manually and updating the index with the
+desired resolution, you can continue the sequencing process with
+
+    git sequencer --continue
+
+Alternatively, you can undo the git-sequencer progress with
+
+    git sequencer --abort
+
+or skip the current instruction with
+
+    git sequencer --skip
+
+or correct the TODO file with
+
+    git sequencer --edit
+
+During pauses or when finished with the sequencing task, the current
+HEAD will always be the result of the last processed instruction.
+
+
+OPTIONS
+-------
+<file>::
+	Filename of the TODO file.  If omitted, standard input is used.
+	See 'TODO FILE FORMAT' below.
+
+-B::
+--batch::
+	Run in batch mode. If unexpected user intervention is needed
+	(e.g. a conflict or the need to run an editor), git-sequencer fails.
++
+Note that the sanity check fails, if you use this option
+and an instruction like `edit` or `pause` is in the TODO file.
+
+--onto=<base>::
+	Checkout given commit or branch before sequencing.
+	If you provide a branch, sequencer will make the provided
+	changes on the branch, i.e. the branch will be changed.
+
+--continue::
+	Restart the sequencing process after having resolved a merge conflict.
+
+--abort::
+	Restore the original branch and abort the sequence operation.
+
+--skip::
+	Restart the sequencing process by skipping the current instruction.
+
+--status::
+	Show the current status of git-sequencer and what
+	operations can be done to change that status.
+
+--edit::
+	Invoke editor to edit the unprocessed part of the TODO file.
++
+The file is syntax- and sanity-checked afterwards, so that you can
+safely run `git sequencer --skip` or `--continue` after editing.
+If you nonetheless noticed that you made a mistake, you can
+overwrite `.git/sequencer/todo` with `.git/sequencer/todo.old` and
+rerun `git sequencer --edit`.
++
+If the check fails you are prompted if you want to correct your
+changes, edit again, cancel editing or really want to save.
+
+-q::
+--quiet::
+	Suppress output.
+	(Not yet implemented.)
+
+-v::
+--verbose::
+	Be more verbose.
+
+
+NOTES
+-----
+
+When sequencing, it is possible, that you are changing the history of
+a branch in a way that can cause problems for anyone who already has
+a copy of the branch in their repository and tries to pull updates from
+you.  You should understand the implications of using git-sequencer on
+a repository that you share.
+
+git-sequencer will usually be called by another git porcelain, like
+linkgit:git-am[1] or linkgit:git-rebase[1].
+
+
+TODO FILE FORMAT
+----------------
+
+The TODO file contains basically one instruction per line.
+
+Blank lines will be ignored.
+All characters after a `#` character will be ignored until the end of a line.
+
+The following instructions can be used:
+
+
+edit <commit>::
+	Picks a commit and pauses the sequencer process to let you
+	make changes.
++
+This is a short form for `pick <commit> and `pause` on separate lines.
+
+
+mark <mark>::
+	Set a symbolic mark for the last commit.
+	`<mark>` is an unsigned integer starting at 1 and
+	prefixed with a colon, e.g. `:1`.
++
+The marks can help if you want to refer to commits that you
+created during the sequencer process, e.g. if you want to
+merge such a commit.
++
+The set marks are removed after the sequencer has completed.
+
+
+merge [options] <commit-ish1> <commit-ish2> ... <commit-ishN>::
+	Merge commits into HEAD.
++
+You can refer to a commit by a mark.
++
+If you do not provide a commit message (using `-F`, `-m`, `-C`, `-M`,
+or `--standard`), an editor will be invoked.
++
+See the following list and 'GENERAL OPTIONS' for values of `options`:
+
+	--standard;;
+		Generates a commit message like 'Merge ... into HEAD'.
+		See also linkgit:git-fmt-merge-msg[1].
+
+	-s <strategy>;;
+	--strategy=<strategy>;;
+		Use the given merge strategy.
+		See also linkgit:git-merge[1].
+
+
+pick [options] <commit>::
+	Pick (see linkgit:git-cherry-pick[1]) a commit.
+	Sequencer will pause on conflicts.
++
+See the following list and 'GENERAL OPTIONS' for values of `options`:
+
+	-R;;
+	--reverse;;
+		Revert the changes introduced by pick <commit>.
+
+	--mainline=<n>;;
+		Allows you to pick merge commits by specifying the
+		parent number (beginning from 1) to let sequencer
+		replay the changes relative to the specified parent.
+		+
+This option does not work together with `-R`.
+
+
+patch [options] <file>::
+	If file `<file>` is a pure (diff) patch, then apply the patch.
+	If no `--message` option is given, an editor will
+	be invoked to enter a commit message.
++
+If `<file>` is a linkgit:git-format-patch[1]-formatted patch,
+then the patch will be commited.
++
+See the following list and 'GENERAL OPTIONS' for values of `options`:
+
+	-3;;
+	--3way;;
+		When the patch does not apply cleanly, fall back on
+		3-way merge, if the patch records the identity of blobs
+		it is supposed to apply to, and we have those blobs
+		available locally.
+
+	-k;;
+		Pass `-k` flag to `git-mailinfo` (see linkgit:git-mailinfo[1]).
+
+	-n;;
+		Pass `-n` flag to `git-mailinfo` (see linkgit:git-mailinfo[1]).
+
+	--exclude=<path-pattern>;;
+		Do not apply changes to files matching the given path pattern.
+		This can be useful when importing patchsets, where you want to
+		exclude certain files or directories.
+
+	-R;;
+	--reverse;;
+		Apply the patch in reverse.
+
+	--no-add;;
+		When applying a patch, ignore additions made by the
+		patch.  This can be used to extract the common part between
+		two files by first running `diff` on them and applying
+		the result with this option, which would apply the
+		deletion part but not addition part.
+
+	--whitespace=<action>;;
+		Specify behavior on whitespace errors.
+		See linkgit:git-apply[1] for a detailed description.
+
+	--context=<n>;;
+		Ensure at least <n> lines of surrounding context match before
+		and after each change.  When fewer lines of surrounding
+		context exist they all must match.  By default no context is
+		ever ignored.
+
+	--inaccurate-eof;;
+		Under certain circumstances, some versions of diff do not
+		correctly detect a missing new-line at the end of the file.
+		As a result, patches created by such diff programs do not
+		record incomplete lines correctly.
+		This option adds support for applying such patches by
+		working around this bug.
+
+	-p<n>;;
+		Remove <n> leading slashes from traditional diff paths.
+		The default is 1.
+
+	--unidiff-zero;;
+		By default, linkgit:git-apply[1] expects that the patch being
+		applied is a unified diff with at least one line of context.
+		This provides good safety measures, but breaks down when
+		applying a diff generated with --unified=0. To bypass these
+		checks use '--unidiff-zero'.
+
+
+pause::
+	Pauses the sequencer process to let you manually make changes.
+	For example, you can re-edit the done commit, fix bugs or typos,
+	or you can make further commits on top of HEAD before continuing.
++
+After you have finished your changes and added them to the index,
+invoke `git-sequencer --continue`.
+If you only want to edit the last commit message with an editor,
+run `git commit --amend` (see linkgit:git-commit[1]) before saying
+`--continue`.
+
+
+ref <ref>::
+	Set ref `<ref>` to the current HEAD, see also
+	linkgit:git-update-ref[1].
+
+
+reset <commit-ish>::
+	Go back (see linkgit:git-reset[1] `--hard`) to commit `<commit-ish>`.
+	`<commit-ish>` can also be given by a mark, if prefixed with a colon.
+
+
+squash [options] <commit>::
+	Add the changes introduced by `<commit>` to the last commit.
++
+See 'GENERAL OPTIONS' for values of `options`.
+
+squash [options] --from <mark>::
+	Squash all commits from the given mark into one commit.
+	There must not be any `merge` instructions between the
+	`mark` instruction and this `squash --from` instruction.
++
+See the following list and 'GENERAL OPTIONS' for values of `options`:
+
+	--collect-signoffs;;
+		Collect the Signed-off-by: lines of each commit and
+		add them to the squashed commit message.
+		(Not yet implemented.)
+
+	--include-merges;;
+		Sanity check does not fail if you have merges
+		between HEAD and <mark>.
+
+
+GENERAL OPTIONS
+---------------
+
+Besides some special options, the instructions
+`patch`, `merge`, `pick`, `squash` take the following general options:
+
+--author=<author>::
+	Override the author name and e-mail address used in the commit.
+	Use `A U Thor <author@example.com>` format.
+
+-C <commit-ish>::
+--reuse-commit=<commit-ish>::
+	Reuse message and authorship data from specified commit.
+
+-M <commit-ish>
+--reuse-message=<commit-ish>::
+	Reuse message from specified commit.
+	Note, that only the commit message is reused
+	and not the authorship information.
+
+-F <file>::
+--file=<file>::
+	Take the commit message from the given file.
+
+-m <msg>::
+--message=<msg>::
+	Use the given `<msg>` as the commit message.
+
+--signoff::
+	Add `Signed-off-by:` line to the commit message (if not yet there),
+	using the committer identity of yourself.
+
+-e::
+--edit::
+	Regardless what commit message options are given,
+	invoke the editor to allow editing of the commit message.
+
+
+RETURN VALUES
+-------------
+
+git-sequencer returns:
+
+* `0`, if git-sequencer successfully completed all the instructions
+       in the TODO file or successfully aborted after
+       `git sequencer --abort`,
+* `2`, on user-requested pausing, e.g.
+       when using the `edit` instruction.
+* `3`, on pauses that are not requested, e.g.
+       when there are conflicts to resolve
+       or errors in the TODO file.
+* any other value on error, e.g.
+  running git-sequencer on a bare repository.
+
+
+EXAMPLES
+--------
+
+TODO [Here the usage of all commands should become clear.]
+
+
+SEE ALSO
+--------
+
+linkgit:git-add[1],
+linkgit:git-am[1],
+linkgit:git-cherry-pick[1],
+linkgit:git-commit[1],
+linkgit:git-fmt-merge-msg[1],
+linkgit:git-format-patch[1],
+linkgit:git-rebase[1],
+linkgit:git-reset[1],
+linkgit:git-update-ref[1]
+
+
+Authors
+-------
+Written by Stephan Beyer <s-beyer@gmx.net>.
+
+
+Documentation
+-------------
+Documentation by Stephan Beyer and the git-list <git@vger.kernel.org>.
+
+GIT
+---
+Part of the linkgit:git[1] suite
-- 
1.5.6.361.g18ea7

^ permalink raw reply related	[flat|nested] 52+ messages in thread

* Re: [RFC/PATCH 3/4] Add git-sequencer test suite (t3350)
  2008-07-01  2:38     ` [RFC/PATCH 3/4] Add git-sequencer test suite (t3350) Stephan Beyer
  2008-07-01  2:38       ` [RFC/PATCH 4/4] Migrate git-am to use git-sequencer Stephan Beyer
@ 2008-07-05 17:31       ` Stephan Beyer
  2008-07-05 18:16         ` Junio C Hamano
  1 sibling, 1 reply; 52+ messages in thread
From: Stephan Beyer @ 2008-07-05 17:31 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Johannes Schindelin

Hi,

I've just noticed that those tag names:
  +	git tag A &&
  +	git tag B &&
  +	git tag C &&
  +	git tag D &&
  +	git tag E &&
  +	git tag F &&
  +	git tag I &&
  +ref refs/tags/CE
are perhaps a bad idea, because they could also be an abbreviated SHA1.
As it seems, git does not think this is ambiguous, since the exact tag
name exists.
So it could make sense to add a non-[0-9A-Fa-f] prefix, but it does not
seem to be necessary.  Hmmm, so this mail is just a mental note :)

Regards,
  Stephan

-- 
Stephan Beyer <s-beyer@gmx.net>, PGP 0x6EDDD207FCC5040F

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC/PATCH 3/4] Add git-sequencer test suite (t3350)
  2008-07-05 17:31       ` [RFC/PATCH 3/4] Add git-sequencer test suite (t3350) Stephan Beyer
@ 2008-07-05 18:16         ` Junio C Hamano
  0 siblings, 0 replies; 52+ messages in thread
From: Junio C Hamano @ 2008-07-05 18:16 UTC (permalink / raw)
  To: Stephan Beyer; +Cc: git, Johannes Schindelin

Stephan Beyer <s-beyer@gmx.net> writes:

> Hi,
>
> I've just noticed that those tag names:
>   +	git tag A &&
>   +	git tag B &&
>   +	git tag C &&
>   +	git tag D &&
>   +	git tag E &&
>   +	git tag F &&
>   +	git tag I &&
>   +ref refs/tags/CE
> are perhaps a bad idea, because they could also be an abbreviated SHA1.

I do not think we take a single letter abbrev so it should be fine.

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [PATCH] Allow cherry-picking root commits
  2008-07-04 15:19                 ` [PATCH] Allow cherry-picking root commits Johannes Schindelin
  2008-07-04 16:41                   ` Stephan Beyer
@ 2008-07-06  1:05                   ` Junio C Hamano
  2008-07-06  1:37                     ` Johannes Schindelin
  2008-07-06 11:35                   ` [PATCH] Allow cherry-picking root commits Stephan Beyer
  2 siblings, 1 reply; 52+ messages in thread
From: Junio C Hamano @ 2008-07-06  1:05 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: Stephan Beyer, git, Daniel Barkalow, Christian Couder

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:

> There is no good reason why cherry-picking root commits should not be 
> allowed.

Hmm, does "cherry-pick a root commit" even have a well defined semantics,
other than "if there is no overlap in files just add the files in"?  I
have a feeling that it is more likely to be a user error, a sign that the
user mistyped the name of the commit to pick.

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [PATCH] Allow cherry-picking root commits
  2008-07-06  1:05                   ` Junio C Hamano
@ 2008-07-06  1:37                     ` Johannes Schindelin
  2008-07-06 11:32                       ` t3503: Add test case for identical files Stephan Beyer
  0 siblings, 1 reply; 52+ messages in thread
From: Johannes Schindelin @ 2008-07-06  1:37 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Stephan Beyer, git, Daniel Barkalow, Christian Couder

Hi,

On Sat, 5 Jul 2008, Junio C Hamano wrote:

> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> 
> > There is no good reason why cherry-picking root commits should not be 
> > allowed.
> 
> Hmm, does "cherry-pick a root commit" even have a well defined 
> semantics, other than "if there is no overlap in files just add the 
> files in"?

Yes.  You can easily add the files identically, or some similar files, in 
which case you get an easily-resolved conflict.

> I have a feeling that it is more likely to be a user error, a sign that 
> the user mistyped the name of the commit to pick.

Now, now, that is too harsh a statement!

Often I start my work from a tarball, just because the git import takes so 
long that I can fiddle with the thing already while the import runs.

And guess what, it would be easier to rebase that series onto an imported 
tag for me.  The first commit would not result in changes, since it would 
import the identical tree.  Or it would barf, in which case I would know 
that I got the wrong branch point to rebase onto.

At the moment, I play games that need a deep knowledge of Git, which _I_ 
have, but not necessarily occasional Git users.

Ciao,
Dscho

^ permalink raw reply	[flat|nested] 52+ messages in thread

* t3503: Add test case for identical files
  2008-07-06  1:37                     ` Johannes Schindelin
@ 2008-07-06 11:32                       ` Stephan Beyer
  0 siblings, 0 replies; 52+ messages in thread
From: Stephan Beyer @ 2008-07-06 11:32 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: Junio C Hamano, git, Daniel Barkalow, Christian Couder

---

On Sun, Jul 06, 2008, Johannes Schindelin wrote:
> Hi,
> 
> On Sat, 5 Jul 2008, Junio C Hamano wrote:
> 
> > Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> > 
> > > There is no good reason why cherry-picking root commits should not be 
> > > allowed.
> > 
> > Hmm, does "cherry-pick a root commit" even have a well defined 
> > semantics, other than "if there is no overlap in files just add the 
> > files in"?
> 
> Yes.  You can easily add the files identically, or some similar files, in 
> which case you get an easily-resolved conflict.

I think this test case shows that it works for identical files.
This patch is btw just to show Junio that it works. ;)
Because I'd like to see Dscho's patch in git, too.

Regards.

 t/t3503-cherry-pick-root.sh |   21 ++++++++++++++++++++-
 1 files changed, 20 insertions(+), 1 deletions(-)

diff --git a/t/t3503-cherry-pick-root.sh b/t/t3503-cherry-pick-root.sh
index b0faa29..bcb6610 100755
--- a/t/t3503-cherry-pick-root.sh
+++ b/t/t3503-cherry-pick-root.sh
@@ -6,14 +6,23 @@ test_description='test cherry-picking a root commit'
 
 test_expect_success setup '
 
+	: > file0 &&
 	echo first > file1 &&
+	echo second > file2 &&
+	git add file0 &&
 	git add file1 &&
 	test_tick &&
 	git commit -m "first" &&
 
+	git symbolic-ref HEAD refs/heads/sharefile &&
+	rm .git/index file0 &&
+	git add file1 &&
+	git add file2 &&
+	test_tick &&
+	git commit -m "file1 and file2" &&
+
 	git symbolic-ref HEAD refs/heads/second &&
 	rm .git/index file1 &&
-	echo second > file2 &&
 	git add file2 &&
 	test_tick &&
 	git commit -m "second"
@@ -23,6 +32,16 @@ test_expect_success setup '
 test_expect_success 'cherry-pick a root commit' '
 
 	git cherry-pick master &&
+	test -f file0 &&
+	test first = $(cat file1)
+
+'
+
+test_expect_success 'cherry-pick a root commit with identical files' '
+
+	git checkout sharefile &&
+	git cherry-pick master &&
+	test -f file0 &&
 	test first = $(cat file1)
 
 '
-- 
1.5.6.361.g18ea7
Stephan Beyer <s-beyer@gmx.net>, PGP 0x6EDDD207FCC5040F

^ permalink raw reply related	[flat|nested] 52+ messages in thread

* Re: [PATCH] Allow cherry-picking root commits
  2008-07-04 15:19                 ` [PATCH] Allow cherry-picking root commits Johannes Schindelin
  2008-07-04 16:41                   ` Stephan Beyer
  2008-07-06  1:05                   ` Junio C Hamano
@ 2008-07-06 11:35                   ` Stephan Beyer
  2008-07-06 12:48                     ` Johannes Schindelin
  2 siblings, 1 reply; 52+ messages in thread
From: Stephan Beyer @ 2008-07-06 11:35 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: Junio C Hamano, git, Daniel Barkalow, Christian Couder

On Fri, Jul 04, Johannes Schindelin wrote:
> +	if (!commit->parents) {
> +		if (action == REVERT)
> +			die ("Cannot revert a root commit");

I btw wondered why _reverting_ a root commit should not work.
Then I tried to quickly add this feature and saw what's the point:
merge-recursive wants a remote -- this
> +	argv[i++] = next_sha1;
to be set :)
So the change is not as trivial as I thought.
And yes, there may even be no use case for reverting root commits.

Regards,
  Stephan

-- 
Stephan Beyer <s-beyer@gmx.net>, PGP 0x6EDDD207FCC5040F

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [PATCH] Allow cherry-picking root commits
  2008-07-06 11:35                   ` [PATCH] Allow cherry-picking root commits Stephan Beyer
@ 2008-07-06 12:48                     ` Johannes Schindelin
  0 siblings, 0 replies; 52+ messages in thread
From: Johannes Schindelin @ 2008-07-06 12:48 UTC (permalink / raw)
  To: Stephan Beyer; +Cc: Junio C Hamano, git, Daniel Barkalow, Christian Couder

Hi,

On Sun, 6 Jul 2008, Stephan Beyer wrote:

> And yes, there may even be no use case for reverting root commits.

That weighs much heavier than that it would be hard to implement.  
When writing the patch, I spent 2 minutes thinking about a possible case 
where reverting a root commit would make sense.  And I came up with none.

It can be sensible to undo _parts_ of it, but then it is no longer about 
reverting the root commit, but about applying a partial patch with a new 
commit message, and we support that quite nicely already.

Ciao,
Dscho

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [PATCH v2 2/4] Add git-sequencer prototype documentation
  2008-07-05 17:00     ` [PATCH v2 " Stephan Beyer
@ 2008-07-08 10:37       ` Jakub Narebski
  2008-07-08 11:49         ` Stephan Beyer
  2008-07-09  5:20         ` Karl Hasselström
  0 siblings, 2 replies; 52+ messages in thread
From: Jakub Narebski @ 2008-07-08 10:37 UTC (permalink / raw)
  To: Stephan Beyer
  Cc: git, Christian Couder, Daniel Barkalow, Johannes Schindelin,
	Junio C Hamano

On Sat, 5 July 2008, Stephan Beyer wrote:

> +git-sequencer(1)

> +TODO FILE FORMAT

> +edit <commit>::
> +mark <mark>::
> +merge [options] <commit-ish1> <commit-ish2> ... <commit-ishN>::
> +pick [options] <commit>::
> +patch [options] <file>::
> +pause::
> +ref <ref>::
> +reset <commit-ish>::
> +squash [options] <commit>::
> +squash [options] --from <mark>::

A few comments and ideas:

1. Splitting a patch

I cannot comment well on git-sequencer, as I have started using
StGIT patch management interface instead of git-rebase in times when
there were no "git rebase --interactive".  Nevertheless working with
StGIT is a bit similar to working with interactive rebase...

I don't find myself wanting to join two patches into one (to squadh
a commit) perhaps because when I want to add something to a commit
(to a patch) I simply go to this patch, edit files, and refresh the
patch.  From time to time however I find myself SPLITTING a patch,
for example extracting something added "by the way"/"while at it"
into separate commit (like late separate better documenting
project_index file format from adding optional description field
to project_index file format).

Currently I don't see easy way to do this with git-sequencer.


2. Patch based rebase

git-rebase by default, and for speed, uses git-format-patch / git-am
pipeline (utilizing '--rebasing' option to git-am to avoid changing
commit messages, even if they do not follow commit message conventions).
If you want for plain "git rebase" to use git-sequencer, it should be
easy to support this "engine"; therefore perhaps it would be good
to add some equivalent of "git format-patch" to the TODO file format.


3. Checking rebase

Usually when you are interacting with upstream by sending patches by
email, the last part before sending series of patches is git-rebase
on top of current work.  It would be nice if there were some way to
have "git rebase" (via git-sequencer) to check that all commits
(perhaps with some explicitly stated in TODO file exceptions) passes
pre-commit hook (checking for whitespaces and conflict markers), and
if possibly also either test suite, or relevant parts of test suite.

So perhaps extending TODO format by "check <script>" or
"check-all <script>"?


I'm just not sure if it is worth adding to TOD file format...
-- 
Jakub Narebski
Poland

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [PATCH v2 2/4] Add git-sequencer prototype documentation
  2008-07-08 10:37       ` Jakub Narebski
@ 2008-07-08 11:49         ` Stephan Beyer
  2008-07-09  5:20         ` Karl Hasselström
  1 sibling, 0 replies; 52+ messages in thread
From: Stephan Beyer @ 2008-07-08 11:49 UTC (permalink / raw)
  To: Jakub Narebski
  Cc: git, Christian Couder, Daniel Barkalow, Johannes Schindelin,
	Junio C Hamano

Hi,

On Tue, Jul 08, 2008, Jakub Narebski wrote:
> 1. Splitting a patch
[...]
> Currently I don't see easy way to do this with git-sequencer.

I've needed this, too, and I've added this use case in the EXAMPLES
section that will be included in the next patchset.
Basically it is the same as the rebase documentation says:
during a pause (by "edit" or "pause" insn) reset to HEAD^, add only
those parts to the index you want to have in the first patch and commit
and then repeat this.

When I've experienced this need the first time, I thought about how
sequencer could ease that.
A first idea was something like "take only files X, Y, Z from commit ..."
or a --exclude as patch has.
But often splitting a patch is not done file-wise, but hunk-wise or even
line-wise.
So my second idea was to add an option to "pause" that just invokes
the "reset HEAD^". That makes it easy, I think.
How should this option be called?

> 2. Patch based rebase
> 
> git-rebase by default, and for speed, uses git-format-patch / git-am
> pipeline (utilizing '--rebasing' option to git-am to avoid changing
> commit messages, even if they do not follow commit message conventions).
> If you want for plain "git rebase" to use git-sequencer, it should be
> easy to support this "engine"; therefore perhaps it would be good
> to add some equivalent of "git format-patch" to the TODO file format.

Imho the patch generation should be done by git-rebase.

> 3. Checking rebase
> 
> Usually when you are interacting with upstream by sending patches by
> email, the last part before sending series of patches is git-rebase
> on top of current work.  It would be nice if there were some way to
> have "git rebase" (via git-sequencer) to check that all commits
> (perhaps with some explicitly stated in TODO file exceptions) passes
> pre-commit hook (checking for whitespaces and conflict markers), and
> if possibly also either test suite, or relevant parts of test suite.
> 
> So perhaps extending TODO format by "check <script>" or
> "check-all <script>"?

That seems to be useful indeed and reminds me of
"git bisect run <script>".
Perhaps it's also better to call it "run" so that it is a generic
way of running scripts from sequencer and if they fail, sequencer
will pause, if they pass, everything goes on.

What about this?


run [--dir=<path>] [--] <cmd> <args>...::
	Run command `<cmd>` with arguments `<args>`.
	Pause (conflict-like) if exit status is non-zero.
+
If `<path>` is set, sequencer will change directory to `<path>`
before running the command and change back after exit.



Regards,
  Stephan


-- 
Stephan Beyer <s-beyer@gmx.net>, PGP 0x6EDDD207FCC5040F

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [PATCH v2 2/4] Add git-sequencer prototype documentation
  2008-07-08 10:37       ` Jakub Narebski
  2008-07-08 11:49         ` Stephan Beyer
@ 2008-07-09  5:20         ` Karl Hasselström
  1 sibling, 0 replies; 52+ messages in thread
From: Karl Hasselström @ 2008-07-09  5:20 UTC (permalink / raw)
  To: Jakub Narebski
  Cc: Stephan Beyer, git, Christian Couder, Daniel Barkalow,
	Johannes Schindelin, Junio C Hamano

(I've hijacked this thread and started talking mostly about StGit
instead of git-sequencer; if you're not interested, you can stop
reading now.)

On 2008-07-08 12:37:50 +0200, Jakub Narebski wrote:

> 1. Splitting a patch
>
> I cannot comment well on git-sequencer, as I have started using
> StGIT patch management interface instead of git-rebase in times when
> there were no "git rebase --interactive". Nevertheless working with
> StGIT is a bit similar to working with interactive rebase...
>
> I don't find myself wanting to join two patches into one (to squadh
> a commit) perhaps because when I want to add something to a commit
> (to a patch) I simply go to this patch, edit files, and refresh the
> patch.

You can do this without having to manually go to the right patch with
the -p <patchname> flag to stg refresh. In my experimental branch,
this even works together with path limited refresh, or refresh of just
the index.

My own workflow is different: I generally make a large number of
rather small "work-in-progress" commits without much of a commit
message, and every now and then (while I still have everything in
short-term memory) I use "stg coalesce" to make one or more "real"
patches out of them. Because I've committed such small pieces in the
first place, I rarely need to split a patch.

> From time to time however I find myself SPLITTING a patch, for
> example extracting something added "by the way"/"while at it" into
> separate commit (like late separate better documenting project_index
> file format from adding optional description field to project_index
> file format).

The best way I've found of splitting a patch in StGit is to open the
diff in an Emacs buffer, then pop the patch, and then use Emacs' cool
diff-mode features to apply hunks selectively, split hunks, edit files
in place, etc., and committing at the points where I want patch
boundaries. Occasionally, I'll push and pop the patch to get a new
diff with only the remaining stuff.

I imagine something like this could work without StGit as well, since
it's mostly Emacs doing all the hard work. (And I suppose there are
other tools besides Emacs that can do this?)

-- 
Karl Hasselström, kha@treskal.com
      www.treskal.com/kalle

^ permalink raw reply	[flat|nested] 52+ messages in thread

end of thread, other threads:[~2008-07-09  5:22 UTC | newest]

Thread overview: 52+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-07-01  2:38 git sequencer prototype Stephan Beyer
2008-07-01  2:38 ` [RFC/PATCH 1/4] Add git-sequencer shell prototype Stephan Beyer
2008-07-01  2:38   ` [RFC/PATCH 2/4] Add git-sequencer prototype documentation Stephan Beyer
2008-07-01  2:38     ` [RFC/PATCH 3/4] Add git-sequencer test suite (t3350) Stephan Beyer
2008-07-01  2:38       ` [RFC/PATCH 4/4] Migrate git-am to use git-sequencer Stephan Beyer
2008-07-01  2:39         ` git-rebase-i migration to sequencer Stephan Beyer
2008-07-01  2:39           ` [PATCH 1/2] Make rebase--interactive use OPTIONS_SPEC Stephan Beyer
2008-07-01  2:39             ` [RFC/PATCH 2/2] Migrate git-rebase--i to use git-sequencer Stephan Beyer
2008-07-05 17:31       ` [RFC/PATCH 3/4] Add git-sequencer test suite (t3350) Stephan Beyer
2008-07-05 18:16         ` Junio C Hamano
2008-07-01 13:02     ` [RFC/PATCH 2/4] Add git-sequencer prototype documentation Jakub Narebski
2008-07-01 16:03       ` Stephan Beyer
2008-07-01 18:04         ` Jakub Narebski
2008-07-01 19:50           ` Stephan Beyer
2008-07-02  0:39             ` Jakub Narebski
2008-07-02  1:20               ` Junio C Hamano
2008-07-02  3:01                 ` Stephan Beyer
2008-07-05 17:00     ` [PATCH v2 " Stephan Beyer
2008-07-08 10:37       ` Jakub Narebski
2008-07-08 11:49         ` Stephan Beyer
2008-07-09  5:20         ` Karl Hasselström
2008-07-03  1:45   ` [RFC/PATCH 1/4] Add git-sequencer shell prototype Junio C Hamano
2008-07-03 11:03     ` Johannes Schindelin
2008-07-03 21:09       ` Stephan Beyer
2008-07-03 22:11         ` Junio C Hamano
2008-07-03 22:34           ` Stephan Beyer
2008-07-03 23:53         ` Johannes Schindelin
2008-07-04  0:38           ` Stephan Beyer
2008-07-04  1:03             ` Johannes Schindelin
2008-07-04  1:53               ` Stephan Beyer
2008-07-04 15:19                 ` [PATCH] Allow cherry-picking root commits Johannes Schindelin
2008-07-04 16:41                   ` Stephan Beyer
2008-07-06  1:05                   ` Junio C Hamano
2008-07-06  1:37                     ` Johannes Schindelin
2008-07-06 11:32                       ` t3503: Add test case for identical files Stephan Beyer
2008-07-06 11:35                   ` [PATCH] Allow cherry-picking root commits Stephan Beyer
2008-07-06 12:48                     ` Johannes Schindelin
2008-07-04  1:06           ` [RFC/PATCH 1/4] Add git-sequencer shell prototype Stephan Beyer
2008-07-04  1:11             ` Johannes Schindelin
2008-07-03 22:51       ` Junio C Hamano
2008-07-03 23:33       ` Stephan Beyer
2008-07-03 13:10   ` Jakub Narebski
2008-07-03 21:12     ` Stephan Beyer
2008-07-05 16:58   ` [PATCH v2 " Stephan Beyer
2008-07-01  8:51 ` git sequencer prototype Junio C Hamano
2008-07-01 14:53   ` Stephan Beyer
2008-07-04 21:00 ` Alex Riesen
2008-07-04 22:09   ` Junio C Hamano
2008-07-04 22:23     ` Stephan Beyer
2008-07-05  8:13     ` Alex Riesen
2008-07-05 10:12       ` Thomas Adam
2008-07-05 10:13       ` Johannes Schindelin

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).