[PATCH/RFC 03/10] Teach rebase interactive the merge command

Previous thread: none

Next thread: Freedesktop.org's Git Pages by Jon Loeliger on Sunday, March 23, 2008 - 3:33 pm. (1 message)
From: Jörg Sommer
Date: Sunday, March 23, 2008 - 2:42 pm

Signed-off-by: Jörg Sommer <joerg@alea.gnuu.de>
---
 git-rebase--interactive.sh |   31 +++++++++++++++++--------------
 1 files changed, 17 insertions(+), 14 deletions(-)

diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 8aa7371..1b2381e 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -113,6 +113,22 @@ has_action () {
 	grep '^[^#]' "$1" >/dev/null
 }
 
+redo_merge() {
+	author_script=$(get_author_ident_from_commit $sha1)
+	eval "$author_script"
+	msg="$(git cat-file commit $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" \
+			$new_parents
+	then
+		git rerere
+		printf "%s\n" "$msg" > "$GIT_DIR"/MERGE_MSG
+		die Error redoing merge $sha1
+	fi
+}
+
 pick_one () {
 	no_ff=
 	case "$1" in -n) sha1=$2; no_ff=t ;; *) sha1=$1 ;; esac
@@ -180,22 +196,9 @@ pick_one_preserving_merges () {
 		echo $sha1 > "$DOTEST"/current-commit
 		case "$new_parents" in
 		' '*' '*)
-			# redo merge
-			author_script=$(get_author_ident_from_commit $sha1)
-			eval "$author_script"
-			msg="$(git cat-file commit $sha1 | sed -e '1,/^$/d')"
 			# No point in merging the first parent, that's HEAD
 			new_parents=${new_parents# $first_parent}
-			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" \
-					$new_parents
-			then
-				git rerere
-				printf "%s\n" "$msg" > "$GIT_DIR"/MERGE_MSG
-				die Error redoing merge $sha1
-			fi
+			redo_merge
 			;;
 		*)
 			output git cherry-pick "$@" ||
-- 
1.5.4.4

--

From: Jörg Sommer
Date: Sunday, March 23, 2008 - 2:42 pm

Signed-off-by: Jörg Sommer <joerg@alea.gnuu.de>
---
 git-rebase--interactive.sh |   13 +++++++------
 1 files changed, 7 insertions(+), 6 deletions(-)

diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 1b2381e..ffd4823 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -114,14 +114,16 @@ has_action () {
 }
 
 redo_merge() {
-	author_script=$(get_author_ident_from_commit $sha1)
-	eval "$author_script"
+	sha1=$1
+	shift
+
+	eval "$(get_author_ident_from_commit $sha1)"
 	msg="$(git cat-file commit $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" \
-			$new_parents
+		output git merge $STRATEGY -m "$msg" "$@"
 	then
 		git rerere
 		printf "%s\n" "$msg" > "$GIT_DIR"/MERGE_MSG
@@ -197,8 +199,7 @@ pick_one_preserving_merges () {
 		case "$new_parents" in
 		' '*' '*)
 			# No point in merging the first parent, that's HEAD
-			new_parents=${new_parents# $first_parent}
-			redo_merge
+			redo_merge $sha1 ${new_parents# $first_parent}
 			;;
 		*)
 			output git cherry-pick "$@" ||
-- 
1.5.4.4

--

From: Jörg Sommer
Date: Sunday, March 23, 2008 - 2:42 pm

Signed-off-by: Jörg Sommer <joerg@alea.gnuu.de>
---
 git-rebase--interactive.sh |    6 +++++-
 1 files changed, 5 insertions(+), 1 deletions(-)

diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index ffd4823..94c6827 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -131,6 +131,10 @@ redo_merge() {
 	fi
 }
 
+parents_of_commit() {
+	git rev-list --parents -1 "$1" | cut -d' ' -f2-
+}
+
 pick_one () {
 	no_ff=
 	case "$1" in -n) sha1=$2; no_ff=t ;; *) sha1=$1 ;; esac
@@ -166,7 +170,7 @@ pick_one_preserving_merges () {
 	fast_forward=t
 	preserve=t
 	new_parents=
-	for p in $(git rev-list --parents -1 $sha1 | cut -d' ' -f2-)
+	for p in $(parents_of_commit $sha1)
 	do
 		if test -f "$REWRITTEN"/$p
 		then
-- 
1.5.4.4

--

From: Jörg Sommer
Date: Sunday, March 23, 2008 - 2:42 pm

The option --preserve-merges does not allow to change the order of
commits or squash them. The new option --linear-history does support
this, but doing so it can only look at the commits reachable with through
the first parent of each merge.

Joining merge commits with other commits leads to problems, because git
merge fails with a dirty index (the case “COMMIT squash MERGE”) and
squashing a merge leads to the lost of the parents (case “MERGE squash
COMMIT”). Therefore, I've prohibited these cases.

Signed-off-by: Jörg Sommer <joerg@alea.gnuu.de>
---
 Documentation/git-rebase.txt  |    8 ++++
 git-rebase--interactive.sh    |   27 +++++++++++++++-
 t/t3404-rebase-interactive.sh |   72 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 106 insertions(+), 1 deletions(-)

I had no better idea for a name of this new option. Propositions are
welcome.

diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index e0412e0..354b6f0 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -11,6 +11,7 @@ SYNOPSIS
 'git-rebase' [-i | --interactive] [-v | --verbose] [-m | --merge]
 	[-s <strategy> | --strategy=<strategy>]
 	[-C<n>] [ --whitespace=<option>] [-p | --preserve-merges]
+	[-l | --linear-history]
 	[--onto <newbase>] <upstream> [<branch>]
 'git-rebase' --continue | --skip | --abort
 
@@ -247,6 +248,13 @@ OPTIONS
 	Instead of ignoring merges, try to recreate them.  This option
 	only works in interactive mode.
 
+-l, \--linear-history::
+	Use only commits of the branch they are not merged in, i.e.
+	follow only the first parent of a merge. Merges are part of this
+	list and they will be redone. It's possible to move merges in the
+	history forward and backward, but they can't take part on a join
+	(squash). This option only works in interactive mode.
+
 include::merge-strategies.txt[]
 
 NOTES
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 94c6827..a2a61f8 100755
--- ...
From: Johannes Schindelin
Date: Sunday, March 23, 2008 - 3:41 pm

Hi,

On Sun, 23 Mar 2008, J
From: Jörg
Date: Monday, March 24, 2008 - 4:14 am

Hi Johannes,


Me too, but I think it's not possible to do what I want with -p. -p
misses a definition of the (new) parent of a commit. It tries to preserve
all commits from all branches. But going through the _list_ of commands
couldn't preserve this structure.

o--A--B
 \     \
  C--D--M--E

How should the graph look like after these commands:

pick A
pick C
squash E
# pick D
pick B
pick M

Should

pick A
pick B
pick C
pick D
pick M
pick E

give a same graph like

pick C
pick A
pick D
pick B
pick M
pick E

Bye, J=F6rg.
--=20
< Mr X.> jo: contact an admin to mount it for you
< jo> The admin is not, well how should I say it, he isn't very familiar wi=
th
      the system. What should I tell my admin, what he should do?
< Mr X.> taking a sun solaris administration course.
From: Junio C Hamano
Date: Monday, March 24, 2008 - 11:35 am

I am beginning to suspect that the root cause of this is that the todo
language is not expressive enough to reproduce a merge _and_ allow end
user editing.

Let's step back a bit.

If you have this history:

      o---o---o---o---o---Z
     /
    X---Y---A---B
         \       \
          C---D---M---E

and you want to transplant the history  between X..E on top of Z, from the
command line you would say:

	$ git rebase --interactive -p --onto Z X E

First let's think what you would do if you want to do this by hand.  The
sequence would be:

	$ git checkout Z^0 ;# detach at Z

        $ git cherry-pick Y
        $ git tag new-Y ;# remember it
        $ git cherry-pick A
        $ git cherry-pick B
        $ git tag new-B ;# remember it
        $ git checkout new-Y
        $ git cherry-pick C
        $ git cherry-pick D
        $ git merge new-B ;# this reproduces M
        $ git cherry-pick E

	$ git branch -f $the_branch && git checkout $the_branch


Now how does the todo file before you edit look like?

	pick Y
        pick A
        pick B
        pick C
        pick D
        pick M
        pick E

The todo file expects the initial detaching and the final switching back
outside of its control, so it is Ok that the first "checkout Z^0" and the
last "branch && checkout" do not appear, but it should be able to express
the remainder and let you tweak.  Is it expressive enough to do so?  

Most of the "pick" from the above list literally translate to the
"cherry-pick", and if you change any of them to "edit", that is
"cherry-pick --no-edit" followed by a return of control to you with insn
to "rebase --continue" to resume.  There appears nothing magical.

Not really.  There already are two gotchas even without talking about
end-user editing.

First, "pick M" is not "cherry-pick M".  You do not want to end up with
merging an old parent before rewriting.  It has to be something like
"merge rewritten-Y".

Second, before you start picking C, if you ...
From: Jörg Sommer
Date: Wednesday, April 9, 2008 - 4:58 pm

Junio proposed to add the commands mark, merge and reset to rebase
interactive for a better support of rebase with preserve merges. The
current format can only cope with flat lineare lists of commits and not
with the non
From: Jörg Sommer
Date: Wednesday, April 9, 2008 - 4:58 pm

This new command can be used to set symbolic marks for an commit while
doing a rebase. This symbolic name can later be used for merges or
resets.
---
 git-rebase--interactive.sh    |   36 ++++++++++++++++++++++++++++++++++++
 t/t3404-rebase-interactive.sh |   21 +++++++++++++++++++++
 2 files changed, 57 insertions(+), 0 deletions(-)

diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 8aa7371..b001ddf 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -23,6 +23,7 @@ DONE="$DOTEST"/done
 MSG="$DOTEST"/message
 SQUASH_MSG="$DOTEST"/message-squash
 REWRITTEN="$DOTEST"/rewritten
+MARKS="$DOTEST"/marks
 PRESERVE_MERGES=
 STRATEGY=
 VERBOSE=
@@ -240,6 +241,23 @@ peek_next_command () {
 	sed -n "1s/ .*$//p" < "$TODO"
 }
 
+parse_mark() {
+	local tmp
+	tmp="$*"
+
+	case "$tmp" in
+	'#'[0-9]*)
+		tmp="${tmp#\#}"
+		if test "$tmp" = $(printf %d "$tmp")
+		then
+			echo $tmp
+			return 0
+		fi
+		;;
+	esac
+	return 1
+}
+
 do_next () {
 	rm -f "$DOTEST"/message "$DOTEST"/author-script \
 		"$DOTEST"/amend || exit
@@ -317,6 +335,23 @@ do_next () {
 			die_with_patch $sha1 ""
 		fi
 		;;
+	mark|a)
+		if ! mark=$(parse_mark $sha1)
+		then
+			warn "Invalid mark given: $command $sha1 $rest"
+			die_with_patch $sha1 \
+				"Please fix this in the file $TODO."
+		fi
+		mark_action_done
+
+		test -d "$MARKS" || mkdir "$MARKS"
+
+		test -e "$MARKS"/$mark && \
+			warn "mark $mark already exist; overwriting it"
+
+		git rev-parse --verify HEAD > "$MARKS"/$mark || \
+			die "HEAD is invalid"
+		;;
 	*)
 		warn "Unknown command: $command $sha1 $rest"
 		die_with_patch $sha1 "Please fix this in the file $TODO."
@@ -533,6 +568,7 @@ do
 #  pick = use commit
 #  edit = use commit, but stop for amending
 #  squash = use commit, but meld into previous commit
+#  mark #NUM = mark the current HEAD for later reference
 #
 # If you remove a line here THAT COMMIT WILL BE LOST.
 # However, if you remove everything, the ...
From: Jörg Sommer
Date: Wednesday, April 9, 2008 - 4:58 pm

---
 git-rebase--interactive.sh    |   20 ++++++++++++++++++++
 t/t3404-rebase-interactive.sh |   10 ++++++++++
 2 files changed, 30 insertions(+), 0 deletions(-)

diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index b001ddf..7dac51b 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -258,6 +258,16 @@ parse_mark() {
 	return 1
 }
 
+mark_to_sha1() {
+	local mark
+	if mark=$(parse_mark $1)
+	then
+		cat "$MARKS"/$mark
+	else
+		return 1
+	fi
+}
+
 do_next () {
 	rm -f "$DOTEST"/message "$DOTEST"/author-script \
 		"$DOTEST"/amend || exit
@@ -352,6 +362,15 @@ do_next () {
 		git rev-parse --verify HEAD > "$MARKS"/$mark || \
 			die "HEAD is invalid"
 		;;
+	reset|r)
+		comment_for_reflog reset
+
+		mark_action_done
+		tmp=$(mark_to_sha1 $sha1) || \
+			tmp=$(git rev-parse --verify $sha1) ||
+			die "Invalid parent '$sha1' in $command $sha1 $rest"
+		output git reset --hard $tmp
+		;;
 	*)
 		warn "Unknown command: $command $sha1 $rest"
 		die_with_patch $sha1 "Please fix this in the file $TODO."
@@ -569,6 +588,7 @@ do
 #  edit = use commit, but stop for amending
 #  squash = use commit, but meld into previous commit
 #  mark #NUM = mark the current HEAD for later reference
+#  reset #NUM|commit = reset HEAD to a previous set mark or a commit
 #
 # If you remove a line here THAT COMMIT WILL BE LOST.
 # However, if you remove everything, the rebase will be aborted.
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index 4461674..521206f 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -85,6 +85,9 @@ for line in $FAKE_LINES; do
 	mark*)
 		echo "mark ${line#mark}"
 		echo "mark ${line#mark}" >> "$1";;
+	reset*)
+		echo "reset ${line#reset}"
+		echo "reset ${line#reset}" >> "$1";;
 	*)
 		echo sed -n "${line}s/^pick/$action/p"
 		sed -n "${line}p" < "$1".tmp
@@ -210,6 +213,13 @@ test_expect_success 'setting marks works' '
 	git rebase --abort
 '
 ...
From: Jörg Sommer
Date: Wednesday, April 9, 2008 - 4:58 pm

This command redoes merges. It's useful if you rebase a branch that
contains merges and you want to preserve these merges. You can also use
it to add new merges.
---
 git-rebase--interactive.sh    |   25 +++++++++++++++++++++++++
 t/t3404-rebase-interactive.sh |    3 +++
 2 files changed, 28 insertions(+), 0 deletions(-)

diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 7dac51b..18cdf3d 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -362,6 +362,29 @@ do_next () {
 		git rev-parse --verify HEAD > "$MARKS"/$mark || \
 			die "HEAD is invalid"
 		;;
+	merge|m)
+		comment_for_reflog merge
+
+		if ! git rev-parse --verify $sha1
+		then
+			die "Invalid reference merge '$sha1' in $command $sha1 $rest"
+		fi
+
+		new_parents=
+		for p in $rest
+		do
+			tmp=$(mark_to_sha1 $p) || \
+				tmp=$(git rev-parse --verify $p) ||
+				die "Invalid parent '$sha1' in $command $sha1 $rest"
+			new_parents="$new_parents $tmp"
+		done
+		new_parents="${new_parents# }"
+		test -n "$new_parents" || \
+			die "You forgot to give the parents for the merge"
+
+		mark_action_done
+		redo_merge $sha1 $new_parents
+		;;
 	reset|r)
 		comment_for_reflog reset
 
@@ -589,6 +612,8 @@ do
 #  squash = use commit, but meld into previous commit
 #  mark #NUM = mark the current HEAD for later reference
 #  reset #NUM|commit = reset HEAD to a previous set mark or a commit
+#  merge commit-M #NUM|commit-P ... = redo merge commit-M with the
+#         current HEAD and the parents marked with #NUM or the commit-P
 #
 # If you remove a line here THAT COMMIT WILL BE LOST.
 # However, if you remove everything, the rebase will be aborted.
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index 521206f..892fd5d 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -88,6 +88,9 @@ for line in $FAKE_LINES; do
 	reset*)
 		echo "reset ${line#reset}"
 		echo "reset ${line#reset}" >> ...
From: Jörg Sommer
Date: Wednesday, April 9, 2008 - 4:58 pm

---
 git-rebase--interactive.sh |   31 +++++++++++++++++--------------
 1 files changed, 17 insertions(+), 14 deletions(-)

diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 18cdf3d..973770e 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -114,6 +114,22 @@ has_action () {
 	grep '^[^#]' "$1" >/dev/null
 }
 
+redo_merge() {
+	author_script=$(get_author_ident_from_commit $sha1)
+	eval "$author_script"
+	msg="$(git cat-file commit $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" \
+			$new_parents
+	then
+		git rerere
+		printf "%s\n" "$msg" > "$GIT_DIR"/MERGE_MSG
+		die Error redoing merge $sha1
+	fi
+}
+
 pick_one () {
 	no_ff=
 	case "$1" in -n) sha1=$2; no_ff=t ;; *) sha1=$1 ;; esac
@@ -181,22 +197,9 @@ pick_one_preserving_merges () {
 		echo $sha1 > "$DOTEST"/current-commit
 		case "$new_parents" in
 		' '*' '*)
-			# redo merge
-			author_script=$(get_author_ident_from_commit $sha1)
-			eval "$author_script"
-			msg="$(git cat-file commit $sha1 | sed -e '1,/^$/d')"
 			# No point in merging the first parent, that's HEAD
 			new_parents=${new_parents# $first_parent}
-			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" \
-					$new_parents
-			then
-				git rerere
-				printf "%s\n" "$msg" > "$GIT_DIR"/MERGE_MSG
-				die Error redoing merge $sha1
-			fi
+			redo_merge
 			;;
 		*)
 			output git cherry-pick "$@" ||
-- 
1.5.4.5

--

From: Jörg Sommer
Date: Wednesday, April 9, 2008 - 4:58 pm

---
 git-rebase--interactive.sh |   14 ++++++++------
 1 files changed, 8 insertions(+), 6 deletions(-)

diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 973770e..1698c3e 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -115,14 +115,17 @@ has_action () {
 }
 
 redo_merge() {
-	author_script=$(get_author_ident_from_commit $sha1)
-	eval "$author_script"
+	local sha1
+	sha1=$1
+	shift
+
+	eval "$(get_author_ident_from_commit $sha1)"
 	msg="$(git cat-file commit $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" \
-			$new_parents
+		output git merge $STRATEGY -m "$msg" "$@"
 	then
 		git rerere
 		printf "%s\n" "$msg" > "$GIT_DIR"/MERGE_MSG
@@ -198,8 +201,7 @@ pick_one_preserving_merges () {
 		case "$new_parents" in
 		' '*' '*)
 			# No point in merging the first parent, that's HEAD
-			new_parents=${new_parents# $first_parent}
-			redo_merge
+			redo_merge $sha1 ${new_parents# $first_parent}
 			;;
 		*)
 			output git cherry-pick "$@" ||
-- 
1.5.4.5

--

From: Jörg Sommer
Date: Wednesday, April 9, 2008 - 4:58 pm

---
 git-rebase--interactive.sh |    6 +++---
 1 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 1698c3e..060b40f 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -600,9 +600,9 @@ do
 			MERGES_OPTION=--no-merges
 		fi
 
-		SHORTUPSTREAM=$(git rev-parse --short $UPSTREAM)
-		SHORTHEAD=$(git rev-parse --short $HEAD)
-		SHORTONTO=$(git rev-parse --short $ONTO)
+		SHORTUPSTREAM=$(git rev-parse --short=7 $UPSTREAM)
+		SHORTHEAD=$(git rev-parse --short=7 $HEAD)
+		SHORTONTO=$(git rev-parse --short=7 $ONTO)
 		git rev-list $MERGES_OPTION --pretty=oneline --abbrev-commit \
 			--abbrev=7 --reverse --left-right --cherry-pick \
 			$UPSTREAM...$HEAD | \
-- 
1.5.4.5

--

From: Jörg Sommer
Date: Wednesday, April 9, 2008 - 4:58 pm

It's much helpful to see the TODO list generated by rebase in the verbose
output. This makes it easier to check, if the list was not broken by
design.
---
 t/t3404-rebase-interactive.sh |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index 892fd5d..aa4bb8d 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -73,7 +73,7 @@ esac
 test -z "$EXPECT_COUNT" ||
 	test "$EXPECT_COUNT" = $(sed -e '/^#/d' -e '/^$/d' < "$1" | wc -l) ||
 	exit
-test -z "$FAKE_LINES" && exit
+test -z "$FAKE_LINES" && { grep -v '^#' "$1"; exit; }
 grep -v '^#' < "$1" > "$1".tmp
 rm -f "$1"
 cat "$1".tmp
-- 
1.5.4.5

--

From: Jörg Sommer
Date: Wednesday, April 9, 2008 - 4:58 pm

Picked from <7vabko3dm2.fsf@gitster.siamese.dyndns.org>
From: gitster@pobox.com (Junio C Hamano)
Date: Sun, 23 Mar 2008 22:17:09 -0700
---
 git-merge.sh |   24 +++++++++++++-----------
 1 files changed, 13 insertions(+), 11 deletions(-)

diff --git a/git-merge.sh b/git-merge.sh
index 7dbbb1d..bd9699d 100755
--- a/git-merge.sh
+++ b/git-merge.sh
@@ -250,17 +250,19 @@ else
 	# We are invoked directly as the first-class UI.
 	head_arg=HEAD
 
-	# All the rest are the commits being merged; prepare
-	# the standard merge summary message to be appended to
-	# the given message.  If remote is invalid we will die
-	# later in the common codepath so we discard the error
-	# in this loop.
-	merge_name=$(for remote
-		do
-			merge_name "$remote"
-		done | git fmt-merge-msg
-	)
-	merge_msg="${merge_msg:+$merge_msg$LF$LF}$merge_name"
+	if test -z "$merge_msg"
+	then
+		# All the rest are the commits being merged; prepare
+		# the standard merge summary message to be appended to
+		# the given message.  If remote is invalid we will die
+		# later in the common codepath so we discard the error
+		# in this loop.
+		merge_msg=$(for remote
+			do
+				merge_name "$remote"
+			done | git fmt-merge-msg
+		)
+	fi
 fi
 head=$(git rev-parse --verify "$head_arg"^0) || usage
 
-- 
1.5.4.5

--

From: Jörg Sommer
Date: Wednesday, April 9, 2008 - 4:58 pm

The old fake-editor selects only lines they start with pick, if you give
the number of a line. With the new commands mark, merge and reset it was
not possible to select such lines for the new TODO list. The new
fake-editor selects all kinds of lines, but replaces only the command
“pick” with a different action.
---
 t/t3404-rebase-interactive.sh |    5 ++---
 1 files changed, 2 insertions(+), 3 deletions(-)

diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index aa4bb8d..be26a78 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -92,9 +92,8 @@ for line in $FAKE_LINES; do
 		echo "merge ${line#merge}" | tr / ' '
 		echo "merge ${line#merge}" | tr / ' ' >> "$1";;
 	*)
-		echo sed -n "${line}s/^pick/$action/p"
-		sed -n "${line}p" < "$1".tmp
-		sed -n "${line}s/^pick/$action/p" < "$1".tmp >> "$1"
+		sed -n "${line}{s/^pick/$action/; p;}" < "$1".tmp
+		sed -n "${line}{s/^pick/$action/; p;}" < "$1".tmp >> "$1"
 		action=pick;;
 	esac
 done
-- 
1.5.4.5

--

From: Jörg Sommer
Date: Wednesday, April 9, 2008 - 4:58 pm

---
 git-rebase--interactive.sh    |  248 ++++++++++++++++++++++++-----------------
 t/t3404-rebase-interactive.sh |   34 ++++++
 2 files changed, 181 insertions(+), 101 deletions(-)

diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 060b40f..27bd87e 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -22,12 +22,10 @@ TODO="$DOTEST"/git-rebase-todo
 DONE="$DOTEST"/done
 MSG="$DOTEST"/message
 SQUASH_MSG="$DOTEST"/message-squash
-REWRITTEN="$DOTEST"/rewritten
 MARKS="$DOTEST"/marks
 PRESERVE_MERGES=
 STRATEGY=
 VERBOSE=
-test -d "$REWRITTEN" && PRESERVE_MERGES=t
 test -f "$DOTEST"/strategy && STRATEGY="$(cat "$DOTEST"/strategy)"
 test -f "$DOTEST"/verbose && VERBOSE=t
 
@@ -137,8 +135,6 @@ 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"
-	test -d "$REWRITTEN" &&
-		pick_one_preserving_merges "$@" && return
 	parent_sha1=$(git rev-parse --verify $sha1^) ||
 		die "Could not get the parent of $sha1"
 	current_sha1=$(git rev-parse --verify HEAD)
@@ -152,66 +148,6 @@ pick_one () {
 	fi
 }
 
-pick_one_preserving_merges () {
-	case "$1" in -n) sha1=$2 ;; *) sha1=$1 ;; esac
-	sha1=$(git rev-parse $sha1)
-
-	if test -f "$DOTEST"/current-commit
-	then
-		current_commit=$(cat "$DOTEST"/current-commit) &&
-		git rev-parse HEAD > "$REWRITTEN"/$current_commit &&
-		rm "$DOTEST"/current-commit ||
-		die "Cannot write current commit's replacement sha1"
-	fi
-
-	# rewrite parents; if none were rewritten, we can fast-forward.
-	fast_forward=t
-	preserve=t
-	new_parents=
-	for p in $(git rev-list --parents -1 $sha1 | cut -d' ' -f2-)
-	do
-		if test -f "$REWRITTEN"/$p
-		then
-			preserve=f
-			new_p=$(cat "$REWRITTEN"/$p)
-			test $p != $new_p && fast_forward=f
-			case "$new_parents" in
-			*$new_p*)
-				;; # do nothing; that parent is already there
-			*)
-				new_parents="$new_parents ...
From: Junio C Hamano
Date: Friday, April 11, 2008 - 5:00 pm

Lacking is a better justification as to why this is a good change and 7 is
a good number.
--

From: Jörg
Date: Saturday, April 12, 2008 - 2:13 am

Hi Junio,


=E2=80=9CThis makes it easier to test for equality of a commit in the TODO =
list

I don't know why 7 is a good number. It was there from the beginning:

% git show 1b1dce4b:git-rebase--interactive.sh G -C1 \=3D7
                git rev-list --no-merges --pretty=3Doneline --abbrev-commit=
 \
                        --abbrev=3D7 --reverse $UPSTREAM..$HEAD | \
                        sed "s/^/pick /" >> "$TODO"

Bye, J=C3=B6rg.
--=20
Damit das M=C3=B6gliche entsteht, mu=C3=9F immer wieder das Unm=C3=B6gliche=
 versucht
werden.                                       (Hermann Hesse)
From: Junio C Hamano
Date: Friday, April 11, 2008 - 4:56 pm

This cat may become "rev-parse --verify" if we decide to move $MARKS



"to a previously set mark".  But I would say upfront "in the todo insn
whenever you need to refer to a commit, in addition to
the usual commit object name, you can use '#num' syntax to refer to a
commit previously marked with the 'mark' insn." and make this line just
read:

    #  reset commit = reset HEAD to the commit
--

From: Jörg
Date: Saturday, April 12, 2008 - 2:37 am

Hi Junio,


I don't expect it. tmp is a valid sha1 and reset may fail if the working
directory is dirty, but then the previous command should have failed,

Does this mean pick, edit and squash should understand marks, too? But
how useful is this? You can only set a mark if you've picked a commit and
using this commit again, e.g. pick it twice, doesn't sound useful.

--=20
$ cat /dev/random
#!/usr/bin/perl -WT
print "hello world\n";
From: Junio C Hamano
Date: Friday, April 11, 2008 - 4:48 pm

I wonder if this should live somewhere inside $GIT_DIR/refs namespace, so
that if any object pruning is triggered ever while rebasing interactively


Wouldn't

	pick 5cc8f37 (init: show "Reinit" message even in ...)
	mark 1
	pick 18d077c (quiltimport: fix misquoting of parse...)
	mark 2
	reset 1
        merge #2

be easier for people?

When you reference a commit with mark name, it is reasonable to require it
to be prefixed with '#', which is a character that cannot be either in
refname nor hex representation of a commit object.  I think it is Ok to
accept an optional prefix when defining one, e.g. "mark #47", but I do not

I understand that the reason why you did not pick a more obvious 'm' is
because you would want to add 'merge' later and give it 'm', but we do not
have to give a single-letter equivalent to all commands especially when
there is not an appropriate one.
--

From: Jörg
Date: Saturday, April 12, 2008 - 3:11 am

Hi Junio,




=E2=80=9CIEEE P1003.2 Draft 11.2 - September 1991=E2=80=9D, page 277:

 Local variables within a function were considered and included in Draft
 9 (controlled by the special built-in local), but were removed because
 they do not fit the simple model developed for the scoping of functions
 and there was some opposition to adding yet another new special built-in
 from outside existing practice.  Implementations should reserve the
 identifier local (as well as typeset, as used in the KornShell) in case
 this local variable mechanism is adopted in a future version of POSIX.2.

=E2=80=A6 but I didn't found it in =E2=80=9CIEEE Std 1003.1, 2004 Edition=

=E2=80=9Creset 18d077c~2=E2=80=9D or =E2=80=9Creset some-tag=E2=80=9D or =

I don't know. Using the special sign everywhere a mark is used looks more
consistent to me. The only case where it might be omitted is the mark

That sounds useful.

BTW: Should the mark be a number or a string, e.g. 001 =3D=3D 1 or 001 !=3D=

That's fine. I thought it's a requirement.

Bye, J=C3=B6rg.
--=20
Was der Bauer nicht kennt, das frisst er nicht. W=C3=BCrde der St=C3=A4dter=
 kennen,
was er frisst, er w=C3=BCrde umgehend Bauer werden.
                                                       Oliver Hassencamp
From: Shawn O. Pearce
Date: Saturday, April 12, 2008 - 8:56 pm

Why not use the mark syntax that fast-import uses?  In fast-import
we use ":n" anytime we need to refer to a mark, e.g. ":1" or ":5".
Its the same idea.  We already have a language for it.  Heck, the
commands above are bordering on a language not too far from the
one that fast-import accepts.  :-)

-- 
Shawn.
--

From: Jörg
Date: Sunday, April 13, 2008 - 9:50 am

Hi Shawn,



Currently, I don't restrict the mark to be a number. It can anything that
is a valid ref. Should I restrict it?

And how do you handle the :/ syntax? =E2=80=9Creset :/Bla=E2=80=9D is than =
not valid.
Mmm, I'll add an exception for :/.

Except of this, I prefer to use the colon to be much closer to the syntax
of fast-import.

Bye, J=C3=B6rg.
--=20
Der Wunsch, klug zu erscheinen, verhindert oft, es zu werden.
    	    	    			      (Francois de la Rochefoucauld)
From: Shawn O. Pearce
Date: Sunday, April 13, 2008 - 11:24 pm

In fast-import a mark can *only* be a number.  It cannot be a ref
string or anything complex like that.  This reduces the memory load

I think the ':/' syntax came along after fast-import had already started
to use ':' as the mark syntax.  I forgot to object to this bastard form
of looking up a commit when it was introduced by Dscho and now we have
a SHA-1 expression syntax that fast-import will confuse with a mark.  I
originally had chosen to start a mark off with ':' because it is not an
allowed character in a ref, due to its use to split src:dst in a fetch

Me too, but it looks like in a human edited "TODO" script we may want
to be more friendly and allow named marks.  Though I'm not sure that is
really all that useful.  If you are merging something because it used to
be merged before the rebase I doubt we'd generate a meaningful mark name
when the TODO script is initially formatted.

-- 
Shawn.
--

From: Junio C Hamano
Date: Sunday, April 13, 2008 - 11:54 pm

I'd say a small integer is the only thing we would need.  The TODO insn
sequence would be machine generated then manually tweaked, not the other
way around.

Regarding colon vs pound, I would say that the 'mark' insn can just say
"2" to set the second mark, and then store it in refs/marks/2; the
commands that refer to the commit later can use the usual "refs/marks/2"
or "marks/2" syntax without colon nor pound nor any other special syntax
that way.

--

From: Jörg
Date: Monday, April 14, 2008 - 3:06 am

Hi Shawn,


Only for the record: fast-import uses numbers and not strings of
digits, i.e. 001 =3D=3D 1, and it ignores stuff following digits, i.e.

I don't think so. There shouldn't be so much marks that a user can't
remember them.

Thanks for your comments.

J=F6rg.
--=20
The UNIX Guru's View of Sex:
# unzip ; strip ; touch ; finger ; mount ; fsck ; more ; yes ; umount ; sle=
ep
From: Mike Ralphson
Date: Thursday, April 10, 2008 - 2:33 am

What would be wrong with using the existing tag machinery for this instead?

Mike

PS: Apologies to anyone who got an html version of this mail.
--

From: Jörg
Date: Saturday, April 12, 2008 - 3:17 am

Hallo Mike,

d?

You may have to deal with conflicts if users named tags 03 or 10. But
Junio suggested to use a ref, too. I think refs/rebase-marks/ is a good
prefix.

Bye, J=F6rg.
--=20
Der Hase l=E4uft schneller als der Fuchs,
denn der Hase l=E4uft um sein Leben.
From: Jörg Sommer
Date: Sunday, April 13, 2008 - 5:20 pm

It's much helpful to see the TODO list generated by rebase in the verbose
output of the test. This makes it easier to check, if the list was not
broken from the beginning.

Signed-off-by: Jörg Sommer <joerg@alea.gnuu.de>
---
 t/t3404-rebase-interactive.sh |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index 9cf873f..8d29878 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -73,7 +73,7 @@ esac
 test -z "$EXPECT_COUNT" ||
 	test "$EXPECT_COUNT" = $(sed -e '/^#/d' -e '/^$/d' < "$1" | wc -l) ||
 	exit
-test -z "$FAKE_LINES" && exit
+test -z "$FAKE_LINES" && { grep -v '^#' "$1"; exit; }
 grep -v '^#' < "$1" > "$1".tmp
 rm -f "$1"
 cat "$1".tmp
-- 
1.5.5

--

From: Jörg Sommer
Date: Sunday, April 13, 2008 - 5:20 pm

Picked from <7vabko3dm2.fsf@gitster.siamese.dyndns.org>
From: gitster@pobox.com (Junio C Hamano)
Date: Sun, 23 Mar 2008 22:17:09 -0700

Signed-off-by: Jörg Sommer <joerg@alea.gnuu.de>
---
 git-merge.sh |   24 +++++++++++++-----------
 1 files changed, 13 insertions(+), 11 deletions(-)

diff --git a/git-merge.sh b/git-merge.sh
index 7dbbb1d..bd9699d 100755
--- a/git-merge.sh
+++ b/git-merge.sh
@@ -250,17 +250,19 @@ else
 	# We are invoked directly as the first-class UI.
 	head_arg=HEAD
 
-	# All the rest are the commits being merged; prepare
-	# the standard merge summary message to be appended to
-	# the given message.  If remote is invalid we will die
-	# later in the common codepath so we discard the error
-	# in this loop.
-	merge_name=$(for remote
-		do
-			merge_name "$remote"
-		done | git fmt-merge-msg
-	)
-	merge_msg="${merge_msg:+$merge_msg$LF$LF}$merge_name"
+	if test -z "$merge_msg"
+	then
+		# All the rest are the commits being merged; prepare
+		# the standard merge summary message to be appended to
+		# the given message.  If remote is invalid we will die
+		# later in the common codepath so we discard the error
+		# in this loop.
+		merge_msg=$(for remote
+			do
+				merge_name "$remote"
+			done | git fmt-merge-msg
+		)
+	fi
 fi
 head=$(git rev-parse --verify "$head_arg"^0) || usage
 
-- 
1.5.5

--

From: Jörg Sommer
Date: Sunday, April 13, 2008 - 5:20 pm

Signed-off-by: Jörg Sommer <joerg@alea.gnuu.de>
---
 git-rebase--interactive.sh |   10 +++++++---
 1 files changed, 7 insertions(+), 3 deletions(-)

diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 8aa7371..531ee94 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -104,8 +104,12 @@ die_with_patch () {
 	die "$2"
 }
 
-die_abort () {
+cleanup_before_quit () {
 	rm -rf "$DOTEST"
+}
+
+die_abort () {
+	cleanup_before_quit
 	die "$1"
 }
 
@@ -352,7 +356,7 @@ do_next () {
 		test ! -f "$DOTEST"/verbose ||
 			git diff-tree --stat $(cat "$DOTEST"/head)..HEAD
 	} &&
-	rm -rf "$DOTEST" &&
+	cleanup_before_quit &&
 	git gc --auto &&
 	warn "Successfully rebased and updated $HEADNAME."
 
@@ -414,7 +418,7 @@ do
 			;;
 		esac &&
 		output git reset --hard $HEAD &&
-		rm -rf "$DOTEST"
+		cleanup_before_quit
 		exit
 		;;
 	--skip)
-- 
1.5.5

--

From: Jörg Sommer
Date: Sunday, April 13, 2008 - 5:21 pm

This new command can be used to set symbolic marks for an commit while
doing a rebase. This symbolic name can later be used for merges or
resets.

The decision to use references for the marks and not files like done with
the rewritten commits for preserve merges was made to ensure no commit
objects get lost if prune is started while (a long term) rebase is
running. This also unifies the checking of the validity of marks and
references by using rev-parse for it.

The usage of : as the sign for marks conforms with the tag sign of
fast-export and fast-import.

Signed-off-by: Jörg Sommer <joerg@alea.gnuu.de>
---
 git-rebase--interactive.sh    |   37 ++++++++++++++++++++++++++++++++++++-
 t/t3404-rebase-interactive.sh |   17 +++++++++++++++++
 2 files changed, 53 insertions(+), 1 deletions(-)

diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 531ee94..6ac316a 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -35,6 +35,8 @@ 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
 }
@@ -105,7 +107,13 @@ die_with_patch () {
 }
 
 cleanup_before_quit () {
-	rm -rf "$DOTEST"
+	rm -rf "$DOTEST" &&
+	for ref in "$GIT_DIR/$mark_prefix"*
+	do
+		test "$ref" = "$GIT_DIR/$mark_prefix*" && continue
+		git update-ref -d "${ref#$GIT_DIR/}" "${ref#$GIT_DIR/}" || \
+			return 1
+	done
 }
 
 die_abort () {
@@ -244,6 +252,19 @@ peek_next_command () {
 	sed -n "1s/ .*$//p" < "$TODO"
 }
 
+mark_to_ref () {
+	case "$1" in
+	:[!/]*)
+		# :/SOMETHING is a reference for the last commit whose
+                # message starts with SOMETHING
+		echo "$mark_prefix${1#:}"
+		;;
+	*)
+		echo "$1"
+		;;
+	esac
+}
+
 do_next () {
 	rm -f "$DOTEST"/message "$DOTEST"/author-script \
 		"$DOTEST"/amend || exit
@@ -321,6 +342,15 @@ do_next () {
 			die_with_patch $sha1 ""
 		fi
 ...
From: Jörg Sommer
Date: Sunday, April 13, 2008 - 5:21 pm

This command does a hard reset of the HEAD, i.e. the next operation used
this commit as parent. This is necessary to redo the commits of different
branches they become merged later.

Signed-off-by: Jörg Sommer <joerg@alea.gnuu.de>
---
 git-rebase--interactive.sh    |   10 ++++++++++
 t/t3404-rebase-interactive.sh |   17 +++++++++++++++++
 2 files changed, 27 insertions(+), 0 deletions(-)

diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 6ac316a..a4b7aad 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -351,6 +351,15 @@ do_next () {
 
 		git update-ref "$mark" HEAD || die "update-ref failed"
 		;;
+	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
+		;;
 	*)
 		warn "Unknown command: $command $sha1 $rest"
 		die_with_patch $sha1 "Please fix this in the file $TODO."
@@ -572,6 +581,7 @@ do
 #  edit = use commit, but stop for amending
 #  squash = use commit, but meld into previous commit
 #  mark :mark = mark the current HEAD for later reference
+#  reset commit = reset HEAD to the commit
 #
 # If you remove a line here THAT COMMIT WILL BE LOST.
 # However, if you remove everything, the rebase will be aborted.
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index fa3560e..78673a6 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -85,6 +85,9 @@ for line in $FAKE_LINES; do
 	mark*)
 		echo "mark ${line#mark}"
 		echo "mark ${line#mark}" >> "$1";;
+	reset*)
+		echo "reset ${line#reset}"
+		echo "reset ${line#reset}" >> "$1";;
 	*)
 		echo sed -n "${line}s/^pick/$action/p"
 		sed -n "${line}p" < "$1".tmp
@@ -206,6 +209,20 @@ test_expect_success 'setting marks works' '
 	ls $marks_dir | wc -l | grep -Fx 0
 '
 
+test_expect_success 'reset with nonexistent mark fails' '
+	export ...
From: Jörg Sommer
Date: Sunday, April 13, 2008 - 5:21 pm

Signed-off-by: Jörg Sommer <joerg@alea.gnuu.de>
---
 git-rebase--interactive.sh |   35 ++++++++++++++++++++---------------
 1 files changed, 20 insertions(+), 15 deletions(-)

diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index a4b7aad..19145b1 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -125,6 +125,25 @@ 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
@@ -192,22 +211,8 @@ pick_one_preserving_merges () {
 		echo $sha1 > "$DOTEST"/current-commit
 		case "$new_parents" in
 		' '*' '*)
-			# redo merge
-			author_script=$(get_author_ident_from_commit $sha1)
-			eval "$author_script"
-			msg="$(git cat-file commit $sha1 | sed -e '1,/^$/d')"
 			# No point in merging the first parent, that's HEAD
-			new_parents=${new_parents# $first_parent}
-			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" \
-					$new_parents
-			then
-				git rerere
-				printf "%s\n" "$msg" > "$GIT_DIR"/MERGE_MSG
-				die Error redoing merge $sha1
-			fi
+			redo_merge $sha1 ${new_parents# $first_parent}
 			;;
 		*)
 			output git cherry-pick "$@" ||
-- 
1.5.5

--

From: Jörg Sommer
Date: Sunday, April 13, 2008 - 5:21 pm

This command redoes merges. It's useful if you rebase a branch that
contains merges and you want to preserve these merges. You can also use
it to add new merges.

Signed-off-by: Jörg Sommer <joerg@alea.gnuu.de>
---
 git-rebase--interactive.sh    |   24 ++++++++++++++++++++++++
 t/t3404-rebase-interactive.sh |   13 +++++++++++++
 2 files changed, 37 insertions(+), 0 deletions(-)

diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 19145b1..fd41ca0 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -356,6 +356,28 @@ do_next () {
 
 		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
 
@@ -587,6 +609,8 @@ 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
 #
 # If you remove a line here THAT COMMIT WILL BE LOST.
 # However, if you remove everything, the rebase will be aborted.
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index 78673a6..ceb9d74 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -88,6 +88,9 @@ for line in $FAKE_LINES; do
 	reset*)
 		echo "reset ${line#reset}"
 		echo "reset ${line#reset}" >> "$1";;
+	merge*)
+		echo "merge ${line#merge}" | tr / ' '
+		echo "merge ...
From: Jörg Sommer
Date: Sunday, April 13, 2008 - 5:21 pm

This makes it easier to test for equality of a commit in the TODO list
and one of SHORTUPSTREAM, SHORTHEAD or SHORTONTO.

Signed-off-by: Jörg Sommer <joerg@alea.gnuu.de>
---
 git-rebase--interactive.sh |    6 +++---
 1 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index fd41ca0..d0a7e5c 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -588,9 +588,9 @@ do
 			MERGES_OPTION=--no-merges
 		fi
 
-		SHORTUPSTREAM=$(git rev-parse --short $UPSTREAM)
-		SHORTHEAD=$(git rev-parse --short $HEAD)
-		SHORTONTO=$(git rev-parse --short $ONTO)
+		SHORTUPSTREAM=$(git rev-parse --short=7 $UPSTREAM)
+		SHORTHEAD=$(git rev-parse --short=7 $HEAD)
+		SHORTONTO=$(git rev-parse --short=7 $ONTO)
 		git rev-list $MERGES_OPTION --pretty=oneline --abbrev-commit \
 			--abbrev=7 --reverse --left-right --cherry-pick \
 			$UPSTREAM...$HEAD | \
-- 
1.5.5

--

From: Jörg Sommer
Date: Sunday, April 13, 2008 - 5:21 pm

The old fake-editor selects only lines they start with pick, if you give
the number of a line. With the new commands mark, merge and reset it was
not possible to select such lines for the new TODO list. The new
fake-editor selects all kinds of lines, but replaces only the command
“pick” with a different action.

Signed-off-by: Jörg Sommer <joerg@alea.gnuu.de>
---
 t/t3404-rebase-interactive.sh |    5 ++---
 1 files changed, 2 insertions(+), 3 deletions(-)

diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index ceb9d74..0a8d065 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -92,9 +92,8 @@ for line in $FAKE_LINES; do
 		echo "merge ${line#merge}" | tr / ' '
 		echo "merge ${line#merge}" | tr / ' ' >> "$1";;
 	*)
-		echo sed -n "${line}s/^pick/$action/p"
-		sed -n "${line}p" < "$1".tmp
-		sed -n "${line}s/^pick/$action/p" < "$1".tmp >> "$1"
+		sed -n "${line}{s/^pick/$action/; p;}" < "$1".tmp
+		sed -n "${line}{s/^pick/$action/; p;}" < "$1".tmp >> "$1"
 		action=pick;;
 	esac
 done
-- 
1.5.5

--

From: Jörg Sommer
Date: Sunday, April 13, 2008 - 5:21 pm

The current algorithmus used to rebase a branch with merges on top of
another has some drawbacks: it's not possible to squash commits, it's not
possible to change the order of commits, particularly the tip of the
branch can't change.

This new algorithmus uses the idea from Junio to create a TODO list with
the commands mark, merge and reset to represent the nonlinear structure
of merges.

Signed-off-by: Jörg Sommer <joerg@alea.gnuu.de>
---
 git-rebase--interactive.sh    |  239 ++++++++++++++++++++++++-----------------
 t/t3404-rebase-interactive.sh |   37 +++++++
 2 files changed, 175 insertions(+), 101 deletions(-)

diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index d0a7e5c..d3327a8 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -22,11 +22,9 @@ TODO="$DOTEST"/git-rebase-todo
 DONE="$DOTEST"/done
 MSG="$DOTEST"/message
 SQUASH_MSG="$DOTEST"/message-squash
-REWRITTEN="$DOTEST"/rewritten
 PRESERVE_MERGES=
 STRATEGY=
 VERBOSE=
-test -d "$REWRITTEN" && PRESERVE_MERGES=t
 test -f "$DOTEST"/strategy && STRATEGY="$(cat "$DOTEST"/strategy)"
 test -f "$DOTEST"/verbose && VERBOSE=t
 
@@ -148,8 +146,6 @@ 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"
-	test -d "$REWRITTEN" &&
-		pick_one_preserving_merges "$@" && return
 	parent_sha1=$(git rev-parse --verify $sha1^) ||
 		die "Could not get the parent of $sha1"
 	current_sha1=$(git rev-parse --verify HEAD)
@@ -163,66 +159,6 @@ pick_one () {
 	fi
 }
 
-pick_one_preserving_merges () {
-	case "$1" in -n) sha1=$2 ;; *) sha1=$1 ;; esac
-	sha1=$(git rev-parse $sha1)
-
-	if test -f "$DOTEST"/current-commit
-	then
-		current_commit=$(cat "$DOTEST"/current-commit) &&
-		git rev-parse HEAD > "$REWRITTEN"/$current_commit &&
-		rm "$DOTEST"/current-commit ||
-		die "Cannot write current commit's replacement sha1"
-	fi
-
-	# rewrite parents; if none were rewritten, we ...
From: Jörg Sommer
Date: Sunday, April 13, 2008 - 5:21 pm

With this new option it's possible to narrow the list of commits in the
TODO list to only those commits you get following the first parent of
each merge, i.e. not those from the merged branches.

Signed-off-by: Jörg Sommer <joerg@alea.gnuu.de>
---
 Documentation/git-rebase.txt  |    7 ++++++-
 git-rebase--interactive.sh    |   15 +++++++++++----
 t/t3404-rebase-interactive.sh |   12 ++++++++++++
 3 files changed, 29 insertions(+), 5 deletions(-)

diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index e0412e0..9ebbb90 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -11,7 +11,7 @@ SYNOPSIS
 'git-rebase' [-i | --interactive] [-v | --verbose] [-m | --merge]
 	[-s <strategy> | --strategy=<strategy>]
 	[-C<n>] [ --whitespace=<option>] [-p | --preserve-merges]
-	[--onto <newbase>] <upstream> [<branch>]
+	[-f | --first-parent] [--onto <newbase>] <upstream> [<branch>]
 'git-rebase' --continue | --skip | --abort
 
 DESCRIPTION
@@ -247,6 +247,11 @@ OPTIONS
 	Instead of ignoring merges, try to recreate them.  This option
 	only works in interactive mode.
 
+-f, \--first-parent::
+	This option implies the option --preserve-merges, but instead of
+	showing all commits from the merged branches show only the
+	commits and merges following the first parent of each commit.
+
 include::merge-strategies.txt[]
 
 NOTES
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index d3327a8..ea67942 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -10,8 +10,8 @@
 # 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] [--verbose]
-	[--onto <branch>] <upstream> [<branch>])'
+USAGE='(--continue | --abort | --skip | [--preserve-merges] [--first-parent]
+	[--verbose] [--onto <branch>] <upstream> [<branch>])'
 
 OPTIONS_SPEC=
 . git-sh-setup
@@ -565,6 +565,10 @@ do
 ...
From: Jörg Sommer
Date: Sunday, April 13, 2008 - 5:21 pm

The intent of the tag command is to (re)set tags for commits in the TODO
list. This way it's possible to rebase a commit together with its tag.

Signed-off-by: Jörg Sommer <joerg@alea.gnuu.de>
---
 git-rebase--interactive.sh    |    7 +++++++
 t/t3404-rebase-interactive.sh |   13 +++++++++++++
 2 files changed, 20 insertions(+), 0 deletions(-)

diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index ea67942..c601655 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -323,6 +323,12 @@ do_next () {
 		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."
@@ -655,6 +661,7 @@ do
 #  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
 #
 # If you remove a line here THAT COMMIT WILL BE LOST.
 # However, if you remove everything, the rebase will be aborted.
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index 8da7829..9901555 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -91,6 +91,9 @@ for line in $FAKE_LINES; do
 	merge*)
 		echo "merge ${line#merge}" | tr / ' '
 		echo "merge ${line#merge}" | tr / ' ' >> "$1";;
+	tag*)
+		echo "tag ${line#tag}"
+		echo "tag ${line#tag}" >> "$1";;
 	*)
 		sed -n "${line}{s/^pick/$action/; p;}" < "$1".tmp
 		sed -n "${line}{s/^pick/$action/; p;}" < "$1".tmp >> "$1"
@@ -306,6 +309,16 @@ test_expect_success 'interactive --first-parent gives a linear list' '
 	test "$head" = "$(git rev-parse HEAD)"
 '
 
+test_expect_success 'tag sets 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 ...
From: Jörg Sommer
Date: Sunday, April 13, 2008 - 5:21 pm

With this new option tags set on commits, they are part of a rebase, are
reset to the rebased commits. This way the tags on a branch are kept
across rebases.

Signed-off-by: Jörg Sommer <joerg@alea.gnuu.de>
---
 Documentation/git-rebase.txt  |    6 ++++-
 git-rebase--interactive.sh    |   42 ++++++++++++++++++++++++++++++++++++++--
 t/t3404-rebase-interactive.sh |   10 +++++++++
 3 files changed, 54 insertions(+), 4 deletions(-)

diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 9ebbb90..cc4e94f 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -11,7 +11,8 @@ SYNOPSIS
 'git-rebase' [-i | --interactive] [-v | --verbose] [-m | --merge]
 	[-s <strategy> | --strategy=<strategy>]
 	[-C<n>] [ --whitespace=<option>] [-p | --preserve-merges]
-	[-f | --first-parent] [--onto <newbase>] <upstream> [<branch>]
+	[-f | --first-parent] [-t | --preserve-tags]
+	[--onto <newbase>] <upstream> [<branch>]
 'git-rebase' --continue | --skip | --abort
 
 DESCRIPTION
@@ -252,6 +253,9 @@ OPTIONS
 	showing all commits from the merged branches show only the
 	commits and merges following the first parent of each commit.
 
+-t, \--preserve-tags::
+	If one of the commits has a tag, reset it to the new commit object.
+
 include::merge-strategies.txt[]
 
 NOTES
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index c601655..e874c31 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -11,7 +11,7 @@
 # http://article.gmane.org/gmane.comp.version-control.git/22407
 
 USAGE='(--continue | --abort | --skip | [--preserve-merges] [--first-parent]
-	[--verbose] [--onto <branch>] <upstream> [<branch>])'
+	[--preserve-tags] [--verbose] [--onto <branch>] <upstream> [<branch>])'
 
 OPTIONS_SPEC=
 . git-sh-setup
@@ -397,9 +397,31 @@ insert_value_at_key_into_list () {
 
 create_extended_todo_list () {
 	(
+	if test t = "${PRESERVE_TAGS:-}"
+	then
+		tag_list=$(git show-ref --abbrev=7 --tags | ...
From: Junio C Hamano
Date: Monday, April 21, 2008 - 10:32 pm

In practice nobody would "run" pack-refs during the rebase session, but I
have to wonder if it can be triggered to run as part of automated gc or
something, in which case this loop does not work as intended. It needs to

What was the conclusion of the mark-syntax discussion?

While I know the bang in ":[!negated]" is POSIX, I wonder if everybody's
shell we care about groks it.

Could people run this with the shell they care about being supported
(Solaris /bin/sh does not count) try this and yell loudly if you get
"matches" please?  I know bash and dash are Ok, but I do not have easy
access to various flabours of BSDs (OSX included).

	case ":/foo" in
        :[!/]*)	echo matches ;;
	*)	echo does not ;;
	esac
--

From: Junio C Hamano
Date: Tuesday, April 22, 2008 - 1:13 am

Eh, sorry, I was commenting on a stale one.  Disregard this part please.

But the "$GIT_DIR/$mark_prefix/*" comment still stands.  I've applied the
series as is to 'next' so let's fix them up in-tree as needed.

--

From: Johannes Schindelin
Date: Tuesday, April 22, 2008 - 1:52 am

Hi,

On Mon, 21 Apr 2008, Junio C Hamano wrote:

> J
From: Jörg
Date: Tuesday, April 22, 2008 - 2:55 am

Hi Junio,


What do you think about this version:

cleanup_before_quit () {
	rm -rf "$DOTEST" &&
	for ref in $(git for-each-ref --format=3D'%(refname)' ${mark_prefix%/})
	do
		git update-ref -d "$ref" "$ref" || return 1
	done

Use the same as fast-import and fix fast-import. :)

I've posted a new version
<1208169584-15931-1-git-send-email-joerg@alea.gnuu.de>

Bye, J=F6rg.
--=20
Nutze die Talente, die du hast. Die W=E4lder w=E4ren sehr still,
wenn nur die begabtesten V=F6gel s=E4ngen.                (Henry van Dyke)
From: Johannes Schindelin
Date: Tuesday, April 22, 2008 - 3:31 am

Hi,

On Tue, 22 Apr 2008, J
From: Junio C Hamano
Date: Tuesday, April 22, 2008 - 11:04 am

Yeah, except you would want to dqquote "${mark_prefix%/}" part.

Also this being a "clean-up" phase, I wonder if we want to stop at the
first error (e.g. should unremovable "$DOTEST" leave marks behind?  should
unremovable one mark leave other marks that happen to sort after it
behind?).


--

From: Jörg
Date: Friday, April 25, 2008 - 2:11 am

Hi Junio,



I think it should be the other way: unremovable marks should leave the
DOTEST behind. This way a rebase should refuse to start a new session and
stumble accross the old marks and it's possible to run git rebase --abort
after manually removing the marks.

Bye, J=F6rg.
--=20
Damit das M=F6gliche entsteht, mu=DF immer wieder das Unm=F6gliche versucht
werden.                                       (Hermann Hesse)
From: Jörg Sommer
Date: Friday, April 25, 2008 - 2:44 am

This new command can be used to set symbolic marks for an commit while
doing a rebase. This symbolic name can later be used for merges or
resets.

The decision to use references for the marks and not files like done with
the rewritten commits for preserve merges was made to ensure no commit
objects get lost if prune is started while (a long term) rebase is
running. This also unifies the checking of the validity of marks and
references by using rev-parse for it.

The format of the marks is as close as possible to the format of the
marks used by fast-export and fast-import, i.e. :001 == :1 and “:12a” is
an invalid mark. It differs from the format of fast-import in that point
that the colon is not required after the mark command, i.e. “mark 12” is
the same as “mark :12”. This should ease the writing of commads.

Signed-off-by: Jörg Sommer <joerg@alea.gnuu.de>
---
 git-rebase--interactive.sh    |   31 +++++++++++++++++++++++++++++++
 t/t3404-rebase-interactive.sh |   17 +++++++++++++++++
 2 files changed, 48 insertions(+), 0 deletions(-)



diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 531ee94..c0abc01 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -35,6 +35,8 @@ 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
 }
@@ -105,6 +107,10 @@ die_with_patch () {
 }
 
 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"
 }
 
@@ -244,6 +250,15 @@ peek_next_command () {
 	sed -n "1s/ .*$//p" < "$TODO"
 }
 
+mark_to_ref () {
+	if expr match "$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
@@ -321,6 +336,17 ...
From: Junio C Hamano
Date: Saturday, April 26, 2008 - 11:13 pm

Thanks; as I already queued the series to 'next', I took the liberty of
applying the interdiff as a separate fix-up/improvement commit to the tip
of the topic.
--

From: Jörg
Date: Sunday, April 27, 2008 - 1:28 am

Hallo Junio,


That's alright.

Bye, J=C3=B6rg.
--=20
Je planm=C3=A4=C3=9Figer ein Mensch vorgeht,
desto st=C3=A4rker mag ihn der Zufall treffen.
		    Erich Krunau =E2=80=9ADie Physiker=E2=80=98
From: Jörg Sommer
Date: Monday, April 14, 2008 - 3:39 am

This new command can be used to set symbolic marks for an commit while
doing a rebase. This symbolic name can later be used for merges or
resets.

The decision to use references for the marks and not files like done with
the rewritten commits for preserve merges was made to ensure no commit
objects get lost if prune is started while (a long term) rebase is
running. This also unifies the checking of the validity of marks and
references by using rev-parse for it.

The format of the marks is as close as possible to the format of the
marks used by fast-export and fast-import, i.e. :001 == :1 and
“:12a” == :12. It differs from the format of fast-import in that point
that it requires a digit after the colon, i.e. “:abc” != :0 and “:-12”
and “:+12” aren't allowed.

Signed-off-by: Jörg Sommer <joerg@alea.gnuu.de>
---
 git-rebase--interactive.sh    |   35 ++++++++++++++++++++++++++++++++++-
 t/t3404-rebase-interactive.sh |   17 +++++++++++++++++
 2 files changed, 51 insertions(+), 1 deletions(-)

The difference to the v2 patch is the definition of the mark as

diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 531ee94..05d04da 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -35,6 +35,8 @@ 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
 }
@@ -105,7 +107,13 @@ die_with_patch () {
 }
 
 cleanup_before_quit () {
-	rm -rf "$DOTEST"
+	rm -rf "$DOTEST" &&
+	for ref in "$GIT_DIR/$mark_prefix"*
+	do
+		test "$ref" = "$GIT_DIR/$mark_prefix*" && continue
+		git update-ref -d "${ref#$GIT_DIR/}" "${ref#$GIT_DIR/}" || \
+			return 1
+	done
 }
 
 die_abort () {
@@ -244,6 +252,17 @@ peek_next_command () {
 	sed -n "1s/ .*$//p" < "$TODO"
 }
 
+mark_to_ref () {
+	case "$1" in
+	:[0-9]*)
+		echo "$mark_prefix$(printf %d ${1#:} 2>/dev/null)"
+		;;
+	*)
+		echo "$1"
+		;;
+	esac
+}
+
 do_next () ...
From: Shawn O. Pearce
Date: Monday, April 14, 2008 - 4:29 pm

Uh, that's a bug in fast-import.  ":4abc" is _not_ a mark if you
read the language specification.  Only ":4" is a mark.  So we are
accepting crap and reading it in odd ways.  Not good.

-- 
Shawn.
--

From: Jörg
Date: Sunday, April 20, 2008 - 4:44 pm

Hallo Shawn,


What about this:

diff --git a/fast-import.c b/fast-import.c
index 73e5439..f60e4ab 100644
--- a/fast-import.c
+++ b/fast-import.c
@@ -1690,10 +1690,31 @@ static void skip_optional_lf(void)
 		ungetc(term_char, stdin);
 }
=20
+static inline int parse_mark(const const char *str, uintmax_t* mark,
+	char **after_mark)
+{
+	if (!str || str[0] !=3D ':' || !isdigit(str[1]))
+		return 1;
+
+	char *am;
+	const uintmax_t m =3D strtoumax(&str[1], &am, 10);
+	if (errno =3D=3D 0) {
+		*mark =3D m;
+		*after_mark =3D am;
+		return 0;
+	}
+	return 1;
+}
+
 static void cmd_mark(void)
 {
-	if (!prefixcmp(command_buf.buf, "mark :")) {
-		next_mark =3D strtoumax(command_buf.buf + 6, NULL, 10);
+	uintmax_t mark =3D 0;
+	char *after_mark =3D NULL;
+
+	if (!prefixcmp(command_buf.buf, "mark ") &&
+		parse_mark(&command_buf.buf[5], &mark, &after_mark) &&
+		*after_mark =3D=3D '\0') {
+		next_mark =3D mark;
 		read_next_command();
 	}
 	else
@@ -1878,7 +1899,10 @@ static void file_change_m(struct branch *b)
=20
 	if (*p =3D=3D ':') {
 		char *x;
-		oe =3D find_mark(strtoumax(p + 1, &x, 10));
+		uintmax_t m;
+		if (parse_mark(p, &m, &x))
+			die("Invalid mark: %s", p);
+		oe =3D find_mark(m);
 		hashcpy(sha1, oe->sha1);
 		p =3D x;
 	} else if (!prefixcmp(p, "inline")) {
@@ -2045,7 +2069,11 @@ static int cmd_from(struct branch *b)
 		hashcpy(b->branch_tree.versions[0].sha1, t);
 		hashcpy(b->branch_tree.versions[1].sha1, t);
 	} else if (*from =3D=3D ':') {
-		uintmax_t idnum =3D strtoumax(from + 1, NULL, 10);
+		char *after_mark;
+		uintmax_t idnum;
+		if (parse_mark(from, &idnum, &after_mark) ||
+			*after_mark !=3D '\0')
+			die("Not a valid mark: %s", from);
 		struct object_entry *oe =3D find_mark(idnum);
 		if (oe->type !=3D OBJ_COMMIT)
 			die("Mark :%" PRIuMAX " not a commit", idnum);
@@ -2080,7 +2108,11 @@ static struct hash_list *cmd_merge(unsigned int *cou=
nt)
 		if (s)
 			hashcpy(n->sha1, s->sha1);
 		else if (*from =3D=3D ':') ...
From: Shawn O. Pearce
Date: Sunday, April 20, 2008 - 5:26 pm

Although we conform to mostly C99 style, variables should be

Hmm.  Shouldn't this be ! parse_mark given that it returns 0
on success and 1 on failure?


-- 
Shawn.
--

From: Jörg
Date: Monday, April 21, 2008 - 1:41 am

Hi Shawn,




Yes, you're right. I've checked some other functions and found this
behaviour. Can I use a different behabiour, i.e. return 0 on failure and
!0 on success?

Bye, J=C3=B6rg.
--=20
=E2=80=9EWer im Usenet gelesen werden will, sollte leserorientiert schreibe=
n. Wer nur
 f=C3=BCr sich schreiben will, dem ist mit einem Tagebuch vielleicht besser
 geholfen. Gelesen zu werden ist kein Recht, sondern ein Privileg.=E2=80=9C
     Thore Tams in <90tfv8$49b$1@keks.kruemel.dyndns.org>
From: Shawn O. Pearce
Date: Monday, April 21, 2008 - 4:59 pm

Yea, inline is fine.  We use "static inline" often in Git when it

I wasn't objected to the return values as written, but more to the
fact that it seemed like a logic error to me.  We use both patterns
in Git.  Perhaps the best example to follow is get_sha1_hex();
it returns -1 on error and 0 on success.  So a common pattern is
"!get_sha1_hex()" to ensure a successful conversion of a hex string
to an unsigned char array.

-- 
Shawn.
--

From: Jörg
Date: Tuesday, April 22, 2008 - 2:39 am

Hallo Shawn,


Thanks for this explanation. This was what I was looking for.

Another question: Is :0 a valid mark? In import_marks() is a check for
!mark, but I haven't seen it anywhere else.

Bye, J=C3=B6rg.
--=20
Du hast keine Chance =E2=80=93 also nutze sie.
From: Shawn O. Pearce
Date: Tuesday, April 22, 2008 - 4:15 pm

No, in fast-import ":0" is _not_ a valid mark.  We burn the first
entry in the marks table (always leaving it empty) as then we can
use the idiom "!mark" to say "no mark was requested/given" and
"mark" to say "mark was requested/given".  Hence we do not need an
extra flag to tell us either way.

Given that a mark is just a pointer, and that extra flag would
likely have been a global "static int have_mark" or some such it
works out to be about the same amount of memory - 4 or 8 bytes.
No big deal, and the code is probably easier to follow as a result.

-- 
Shawn.
--

From: Jörg Sommer
Date: Friday, April 25, 2008 - 2:04 am

The current implementation of mark parsing doesn't care for trailing
garbage like in :12a and doesn't check for unsigned numbers, i.e. it
accepts :-12 as a valid mark.

This patch enforces a number follows the colon and there comes nothing
after the bignum.

Signed-off-by: Jörg Sommer <joerg@alea.gnuu.de>
---
 fast-import.c |   49 ++++++++++++++++++++++++++++++++++++++-----------
 1 files changed, 38 insertions(+), 11 deletions(-)


Then I propose the following patch.

diff --git a/fast-import.c b/fast-import.c
index 73e5439..0c71da8 100644
--- a/fast-import.c
+++ b/fast-import.c
@@ -1690,12 +1690,31 @@ static void skip_optional_lf(void)
 		ungetc(term_char, stdin);
 }
 
+static inline uintmax_t parse_mark(const const char *str, char **after_mark)
+{
+	char *am;
+	uintmax_t m;
+
+	if (!str || str[0] != ':' || !isdigit(str[1]))
+		return 0;
+
+	m = strtoumax(&str[1], &am, 10);
+	if (m != UINTMAX_MAX || errno == 0) {
+		*after_mark = am;
+		return m;
+	}
+	return 0;
+}
+
 static void cmd_mark(void)
 {
-	if (!prefixcmp(command_buf.buf, "mark :")) {
-		next_mark = strtoumax(command_buf.buf + 6, NULL, 10);
+	uintmax_t mark;
+	char *after_mark = NULL;
+
+	if (!prefixcmp(command_buf.buf, "mark ") &&
+		(next_mark = parse_mark(&command_buf.buf[5], &after_mark)) &&
+		*after_mark == '\0')
 		read_next_command();
-	}
 	else
 		next_mark = 0;
 }
@@ -1877,8 +1896,8 @@ static void file_change_m(struct branch *b)
 	}
 
 	if (*p == ':') {
-		char *x;
-		oe = find_mark(strtoumax(p + 1, &x, 10));
+		char *x = NULL;
+		oe = find_mark(parse_mark(p, &x));
 		hashcpy(sha1, oe->sha1);
 		p = x;
 	} else if (!prefixcmp(p, "inline")) {
@@ -2045,7 +2064,10 @@ static int cmd_from(struct branch *b)
 		hashcpy(b->branch_tree.versions[0].sha1, t);
 		hashcpy(b->branch_tree.versions[1].sha1, t);
 	} else if (*from == ':') {
-		uintmax_t idnum = strtoumax(from + 1, NULL, 10);
+		char *after_mark;
+		uintmax_t idnum = parse_mark(from, &after_mark);
+		if (!idnum || ...
From: Junio C Hamano
Date: Sunday, April 20, 2008 - 9:52 am

I'd admit that this was taken from my "You could do this" patch, and I am
inclined to think that the users would probably want this behaviour of
dropping the default merge summary when giving their own message with -m,
but I am not absolutely convinced that doing this unconditionally is the
right thing to do (iow, some people might have relied on the current
behaviour).

--

From: Jörg
Date: Sunday, April 20, 2008 - 5:17 pm

Hello Junio,


What about a new option -M? But I doubt someone expects this behaviour
because the manpage says:

  The second syntax (<msg> HEAD <remote>) is supported for historical
  reasons. Do not use it from the command line or in new scripts. It is
  the same as git merge -m <msg> <remote>.
      ^^^^              ^^

Currently, it's not the same, but someone might expect it.

Bye, J=F6rg.
--=20
Die Erde ist das einzigste Irrenhaus, das von seinen eigenen Insassen
verwaltet wird.
                                                (U. Schmidt)
From: Junio C Hamano
Date: Monday, April 21, 2008 - 10:27 pm

Yeah, the manpage is loosely written and does not reflect the reality.  I
am _VERY_ tempted to apply your change as-is and see if anybody screams.
Because git is designed to be very scriptable, people who want the current
behaviour can still call fmt-merge-msg themselves.
--

From: Jörg
Date: Tuesday, March 25, 2008 - 3:13 am

Hi Junio,



I would like to extend this example:

      o---o---o---o---o---Z
     /
    X---Y---A-------B
         \           \
          C---D---N---M---E
                 /
merge V


This way we can also merge more than two branches. Your idea sounds good.

Bye, J=C3=B6rg.
--=20
Nichts ist so langweilig, wie die Wiederholung seinerselbst.
                                        (Marcel Reich=E2=80=90Ranicki)
From: Junio C Hamano
Date: Monday, March 24, 2008 - 4:30 pm

I'll extend this topic a bit for the last time, but first a word of
caution.  What I am going to draw is probably not what the current -p
implementation does.  They illustrate what I think should happen.

Again, starting from this topology:

       o---o---o---o---o---Z
      /
     X---Y---A---B
          \       \
           C---D---M---E

and the goal is to rebase your development leading to E on top of the
updated mainline Z.

The earlier example was when you want to end up with this topology:

       o---o---o---o---o---Z---Y'--A'--B'      
      /                         \       \      
     X---Y---A---B               C'--D'--M'--E'
          \       \
           C---D---M---E

In this case, "pick M" cannot be "merge B after pick D".  It needs to
merge in the rewritten B (which is B').

But if you want to end up with this topology:

       o---o---o---o---o---Z---Y'--A'--B'
      /                                 \     
     X---Y---A---B                       M'--E'
          \       \                     /
           C---D---M---E               /
                \                     /
                 `-------------------'

redoing the merge from D when reproducing M' is the right thing to do.

Unfortunately, you cannot express that you would want to rewrite only the
Y--A--B--M--E ancestry from the command line.  We would need a syntax to
do this cleanly first if we want to pursue this.

The "first parent" hack can be used in this case (--first-parent X..E),
but it will probably meet with the same resistance at the philosophical
level (i.e. "merge parents are equal") as the --first-parent option was
criticised for.  But other than that, a sequence to pick Y A B M E in this
order can be presented in the todo list to be edited, and swapping A and M
(for example) should result in this:

       o---o---o---o---o---Z---Y'
      /                         \
     X---Y---A---B               M'--A'--B'
          \       \             /
           ...
From: Johannes Schindelin
Date: Monday, March 24, 2008 - 6:08 am

Hi,

On Mon, 24 Mar 2008, J
From: Jörg
Date: Monday, March 24, 2008 - 4:40 pm

Hallo Johannes,


Thanks for answering my question. You are really cooperatively.

Bye, J=F6rg.
--=20
IRC: Der [Prof. Andreas Pfitzmann, TU Dresden] hat gerade vorgeschlagen, sie
  sollen doch statt Trojanern die elektromagnetische Abstrahlung nutzen. Das
  sei nicht massenf=E4hig, ginge ohne Eingriff ins System, sei zielgerichte=
t,
  und, der Hammer, das funktioniere ja bei Wahlcomputern schon sehr gut.
From: Johannes Schindelin
Date: Sunday, March 23, 2008 - 3:33 pm

Hi,

On Sun, 23 Mar 2008, J
From: Johannes Schindelin
Date: Sunday, March 23, 2008 - 3:29 pm

Hi,

On Sun, 23 Mar 2008, J
From: Johannes Schindelin
Date: Sunday, March 23, 2008 - 3:26 pm

Hi,

On Sun, 23 Mar 2008, J
Previous thread: none

Next thread: Freedesktop.org's Git Pages by Jon Loeliger on Sunday, March 23, 2008 - 3:33 pm. (1 message)