git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
From: "Jörg Sommer" <joerg@alea.gnuu.de>
To: git@vger.kernel.org
Cc: gitster@pobox.com, Johannes.Schindelin@gmx.de,
	"Jörg Sommer" <joerg@alea.gnuu.de>
Subject: [PATCH v2 10/13] Do rebase with preserve merges with advanced TODO list
Date: Mon, 14 Apr 2008 02:21:06 +0200	[thread overview]
Message-ID: <1208132469-26471-10-git-send-email-joerg@alea.gnuu.de> (raw)
In-Reply-To: <1208132469-26471-9-git-send-email-joerg@alea.gnuu.de>

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 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 $new_p"
-				;;
-			esac
-		fi
-	done
-	case $fast_forward in
-	t)
-		output warn "Fast forward to $sha1"
-		test $preserve = f || echo $sha1 > "$REWRITTEN"/$sha1
-		;;
-	f)
-		test "a$1" = a-n && die "Refusing to squash a merge: $sha1"
-
-		first_parent=$(expr "$new_parents" : ' \([^ ]*\)')
-		# detach HEAD to current parent
-		output git checkout $first_parent 2> /dev/null ||
-			die "Cannot move HEAD to $first_parent"
-
-		echo $sha1 > "$DOTEST"/current-commit
-		case "$new_parents" in
-		' '*' '*)
-			# No point in merging the first parent, that's HEAD
-			redo_merge $sha1 ${new_parents# $first_parent}
-			;;
-		*)
-			output git cherry-pick "$@" ||
-				die_with_patch $sha1 "Could not pick $sha1"
-			;;
-		esac
-		;;
-	esac
-}
-
 nth_string () {
 	case "$1" in
 	*1[0-9]|*[04-9]) echo "$1"th;;
@@ -398,20 +334,7 @@ do_next () {
 	HEADNAME=$(cat "$DOTEST"/head-name) &&
 	OLDHEAD=$(cat "$DOTEST"/head) &&
 	SHORTONTO=$(git rev-parse --short $(cat "$DOTEST"/onto)) &&
-	if test -d "$REWRITTEN"
-	then
-		test -f "$DOTEST"/current-commit &&
-			current_commit=$(cat "$DOTEST"/current-commit) &&
-			git rev-parse HEAD > "$REWRITTEN"/$current_commit
-		if test -f "$REWRITTEN"/$OLDHEAD
-		then
-			NEWHEAD=$(cat "$REWRITTEN"/$OLDHEAD)
-		else
-			NEWHEAD=$OLDHEAD
-		fi
-	else
-		NEWHEAD=$(git rev-parse HEAD)
-	fi &&
+	NEWHEAD=$(git rev-parse HEAD) &&
 	case $HEADNAME in
 	refs/*)
 		message="$GIT_REFLOG_ACTION: $HEADNAME onto $SHORTONTO)" &&
@@ -436,6 +359,130 @@ do_rest () {
 	done
 }
 
+get_value_from_list () {
+	# args: "key" " key1#value1 key2#value2"
+	case "$2" in
+	*" $1#"*)
+		stm_tmp="${2#* $1#}"
+		echo "${stm_tmp%% *}"
+		unset stm_tmp
+		;;
+	*)
+		return 1
+		;;
+	esac
+}
+
+insert_value_at_key_into_list () {
+	# args: "value" "key" " key1#value1 key2#value2"
+	case "$3 " in
+	*" $2#$1 "*)
+		echo "$3"
+		;;
+	*" $2#"*)
+		echo "$3"
+		return 1
+		;;
+	*)
+		echo "$3 $2#$1"
+		;;
+	esac
+}
+
+create_extended_todo_list () {
+	(
+	while IFS=_ read commit parents subject
+	do
+		if test "${last_parent:-$commit}" != "$commit"
+		then
+			if test t = "${delayed_mark:-f}"
+			then
+				marked_commits=$(insert_value_at_key_into_list \
+					dummy $last_parent "${marked_commits:-}")
+				delayed_mark=f
+			fi
+			test "$last_parent" = $SHORTUPSTREAM && \
+				last_parent=$SHORTONTO
+			echo "reset $last_parent"
+		fi
+		last_parent="${parents%% *}"
+
+		get_value_from_list $commit "${marked_commits:-}" \
+			>/dev/null && echo mark
+
+		case "$parents" in
+		*' '*)
+			delayed_mark=t
+			new_parents=
+			for p in ${parents#* }
+			do
+				marked_commits=$(insert_value_at_key_into_list \
+					dummy "$p" "${marked_commits:-}")
+				if test "$p" = $SHORTUPSTREAM
+				then
+					new_parents="$new_parents $SHORTONTO"
+				else
+					new_parents="$new_parents $p"
+				fi
+			done
+			unset p
+			echo merge $commit $new_parents
+			unset new_parents
+			;;
+		*)
+			echo "pick $commit $subject"
+			;;
+		esac
+	done
+	test -n "${last_parent:-}" -a "${last_parent:-}" != $SHORTUPSTREAM && \
+		echo reset $last_parent
+	) | \
+	tac | \
+	while read cmd args
+	do
+		: ${commit_mark_list:=} ${last_commit:=000}
+		case "$cmd" in
+		pick)
+			last_commit="${args%% *}"
+			;;
+		mark)
+			: ${next_mark:=0}
+			if commit_mark_list=$(insert_value_at_key_into_list \
+				$next_mark $last_commit "$commit_mark_list")
+			then
+				args=":$next_mark"
+				next_mark=$(($next_mark + 1))
+			else
+				die "Internal error: two marks for" \
+					"the same commit"
+			fi
+			;;
+		reset)
+			if tmp=$(get_value_from_list $args "$commit_mark_list")
+			then
+				args=":$tmp"
+			fi
+			;;
+		merge)
+			new_args=
+			for i in ${args#* }
+			do
+				if tmp=$(get_value_from_list $i \
+					"$commit_mark_list")
+				then
+					new_args="$new_args :$tmp"
+				else
+					new_args="$new_args $i"
+				fi
+			done
+			last_commit="${args%% *}"
+			args="$last_commit ${new_args# }"
+			;;
+		esac
+		echo "$cmd $args"
+	done
+}
+
 while test $# != 0
 do
 	case "$1" in
@@ -568,33 +615,23 @@ do
 		echo $ONTO > "$DOTEST"/onto
 		test -z "$STRATEGY" || echo "$STRATEGY" > "$DOTEST"/strategy
 		test t = "$VERBOSE" && : > "$DOTEST"/verbose
-		if test t = "$PRESERVE_MERGES"
-		then
-			# $REWRITTEN contains files for each commit that is
-			# reachable by at least one merge base of $HEAD and
-			# $UPSTREAM. They are not necessarily rewritten, but
-			# their children might be.
-			# This ensures that commits on merged, but otherwise
-			# unrelated side branches are left alone. (Think "X"
-			# in the man page's example.)
-			mkdir "$REWRITTEN" &&
-			for c in $(git merge-base --all $HEAD $UPSTREAM)
-			do
-				echo $ONTO > "$REWRITTEN"/$c ||
-					die "Could not init rewritten commits"
-			done
-			MERGES_OPTION=
-		else
-			MERGES_OPTION=--no-merges
-		fi
 
 		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 | \
-			sed -n "s/^>/pick /p" > "$TODO"
+		common_rev_list_opts="--abbrev-commit --abbrev=7
+			--left-right --cherry-pick $UPSTREAM...$HEAD"
+		if test t = "$PRESERVE_MERGES"
+		then
+			git rev-list --pretty='format:%h_%p_%s' --topo-order \
+				$common_rev_list_opts | \
+				grep -v ^commit | \
+				create_extended_todo_list
+		else
+			git rev-list --no-merges --reverse --pretty=oneline \
+				 $common_rev_list_opts | sed -n "s/^>/pick /p"
+		fi > "$TODO"
+
 		cat >> "$TODO" << EOF
 
 # Rebase $SHORTUPSTREAM..$SHORTHEAD onto $SHORTONTO
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index 0a8d065..f919aaf 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -258,7 +258,44 @@ test_expect_success 'preserve merges with -p' '
 	test $(git show HEAD~2:file1) = B
 '
 
+test_expect_success 'rebase with preserve merge forth and back is a noop' '
+	git checkout -b big-branch-1 master &&
+	test_tick &&
+	: > bb1a &&
+	git add bb1a &&
+	git commit -m "big branch commit 1" &&
+	: > bb1b &&
+	git add bb1b &&
+	git commit -m "big branch commit 2" &&
+	: > bb1c &&
+	git add bb1c &&
+	git commit -m "big branch commit 3" &&
+	git checkout -b big-branch-2 master &&
+	: > bb2a &&
+	git add bb2a &&
+	git commit -m "big branch commit 4" &&
+	: > bb2b &&
+	git add bb2b &&
+	git commit -m "big branch commit 5" &&
+	git merge big-branch-1~1 &&
+	git merge to-be-preserved &&
+	tbp_merge=$(git rev-parse HEAD) &&
+	: > bb2c &&
+	git add bb2c &&
+	git commit -m "big branch commit 6" &&
+	git merge big-branch-1 &&
+	head=$(git rev-parse HEAD) &&
+	FAKE_LINES="16 6 19 20 4 1 2 5 22" \
+		git rebase -i -p --onto dead-end master &&
+	test "$head" != "$(git rev-parse HEAD)" &&
+	FAKE_LINES="3 7 mark:10 8 9 5 1 2 merge$tbp_merge~1/:10 \
+		merge$tbp_merge/to-be-preserved 6 11" \
+		git rebase -i -p --onto master dead-end &&
+	test "$head" = "$(git rev-parse HEAD)"
+'
+
 test_expect_success '--continue tries to commit' '
+	git checkout to-be-rebased &&
 	test_tick &&
 	! git rebase -i --onto new-branch1 HEAD^ &&
 	echo resolved > file1 &&
-- 
1.5.5

  reply	other threads:[~2008-04-14  7:39 UTC|newest]

Thread overview: 104+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2008-03-23 21:42 [PATCH 1/4] Move redo merge code in a function Jörg Sommer
2008-03-23 21:42 ` [PATCH 2/4] Rework redo_merge Jörg Sommer
2008-03-23 21:42   ` [PATCH 3/4] Add a function for get the parents of a commit Jörg Sommer
2008-03-23 21:42     ` [PATCH 4/4] git-rebase -i: New option to support rebase with merges Jörg Sommer
2008-03-23 22:41       ` Johannes Schindelin
2008-03-24 11:14         ` Jörg Sommer
2008-03-24 13:08           ` Johannes Schindelin
2008-03-24 23:40             ` Jörg Sommer
2008-03-24 18:35           ` Junio C Hamano
2008-03-24 23:30             ` Junio C Hamano
2008-03-25 10:13             ` Jörg Sommer
2008-03-26  8:02               ` Junio C Hamano
2008-04-09 23:58             ` Teach rebase interactive more commands to do better preserve merges Jörg Sommer
2008-04-09 23:58               ` [PATCH/RFC 01/10] Teach rebase interactive the mark command Jörg Sommer
2008-04-09 23:58                 ` [PATCH/RFC 02/10] Teach rebase interactive the reset command Jörg Sommer
2008-04-09 23:58                   ` [PATCH/RFC 03/10] Teach rebase interactive the merge command Jörg Sommer
2008-04-09 23:58                     ` [PATCH/RFC 04/10] Move redo merge code in a function Jörg Sommer
2008-04-09 23:58                       ` [PATCH/RFC 05/10] Rework redo_merge Jörg Sommer
2008-04-09 23:58                         ` [PATCH/RFC 06/10] Unify the lenght of $SHORT* and the commits in the TODO list Jörg Sommer
2008-04-09 23:58                           ` [PATCH/RFC 07/10] fake-editor: output TODO list if unchanged Jörg Sommer
2008-04-09 23:58                             ` [PATCH/RFC 08/10] Don't append default merge message to -m message Jörg Sommer
2008-04-09 23:58                               ` [PATCH/RFC 09/10] Select all lines with fake-editor Jörg Sommer
2008-04-09 23:58                                 ` [PATCH/RFC 10/10] Do rebase with preserve merges with advanced TODO list Jörg Sommer
2008-04-12  0:00                           ` [PATCH/RFC 06/10] Unify the lenght of $SHORT* and the commits in the " Junio C Hamano
2008-04-12  9:13                             ` Jörg Sommer
2008-04-13  6:20                               ` Junio C Hamano
2008-04-13 16:39                                 ` Jörg Sommer
2008-04-14  1:06                                 ` Tarmigan
2008-04-11 23:56                   ` [PATCH/RFC 02/10] Teach rebase interactive the reset command Junio C Hamano
2008-04-12  9:37                     ` Jörg Sommer
2008-04-10  9:33                 ` [PATCH/RFC 01/10] Teach rebase interactive the mark command Mike Ralphson
2008-04-12 10:17                   ` Jörg Sommer
2008-04-11 23:48                 ` Junio C Hamano
2008-04-12 10:11                   ` Jörg Sommer
2008-04-13  3:56                     ` Shawn O. Pearce
2008-04-13 16:50                       ` Jörg Sommer
2008-04-14  6:24                         ` Shawn O. Pearce
2008-04-14  6:54                           ` Junio C Hamano
2008-04-14 10:06                           ` Jörg Sommer
2008-04-14  0:20             ` [PATCH v2 01/13] fake-editor: output TODO list if unchanged Jörg Sommer
2008-04-14  0:20               ` [PATCH v2 02/13] Don't append default merge message to -m message Jörg Sommer
2008-04-14  0:20                 ` [PATCH v2 03/13] Move cleanup code into it's own function Jörg Sommer
2008-04-14  0:21                   ` [PATCH v2 04/13] Teach rebase interactive the mark command Jörg Sommer
2008-04-14  0:21                     ` [PATCH v2 05/13] Teach rebase interactive the reset command Jörg Sommer
2008-04-14  0:21                       ` [PATCH v2 06/13] Move redo merge code in a function Jörg Sommer
2008-04-14  0:21                         ` [PATCH v2 07/13] Teach rebase interactive the merge command Jörg Sommer
2008-04-14  0:21                           ` [PATCH v2 08/13] Unify the lenght of $SHORT* and the commits in the TODO list Jörg Sommer
2008-04-14  0:21                             ` [PATCH v2 09/13] Select all lines with fake-editor Jörg Sommer
2008-04-14  0:21                               ` Jörg Sommer [this message]
2008-04-14  0:21                                 ` [PATCH v2 11/13] Add option --first-parent Jörg Sommer
2008-04-14  0:21                                   ` [PATCH v2 12/13] Teach rebase interactive the tag command Jörg Sommer
2008-04-14  0:21                                     ` [PATCH v2 13/13] Add option --preserve-tags Jörg Sommer
2008-04-22  5:32                     ` [PATCH v2 04/13] Teach rebase interactive the mark command Junio C Hamano
2008-04-22  8:13                       ` Junio C Hamano
2008-04-22  8:52                       ` Johannes Schindelin
2008-04-22  9:55                       ` Jörg Sommer
2008-04-22 10:31                         ` Johannes Schindelin
2008-04-22 16:56                           ` Junio C Hamano
2008-04-22 17:12                             ` Johannes Schindelin
2008-04-29  0:25                               ` Junio C Hamano
2008-04-29  0:39                                 ` Johannes Schindelin
2008-04-29  5:17                                   ` Junio C Hamano
2008-04-29  7:12                                     ` Johannes Sixt
2008-04-29 10:52                                       ` Johannes Schindelin
2008-04-29 21:16                                         ` Junio C Hamano
2008-04-29 21:25                                           ` Johannes Schindelin
2008-04-29 22:23                                             ` Junio C Hamano
2008-04-29 22:55                                               ` Johannes Schindelin
2008-04-29 23:06                                                 ` Junio C Hamano
2008-04-29 23:31                                                   ` Johannes Schindelin
2008-04-30  1:23                                                     ` Junio C Hamano
2008-04-30  6:25                                                       ` Johannes Sixt
2008-04-30  7:10                                                         ` Junio C Hamano
2008-04-30  8:47                                                       ` Johannes Schindelin
2008-04-30  9:19                                                         ` Junio C Hamano
2008-04-30 10:29                                                           ` Johannes Sixt
2008-04-30 11:56                                                           ` Johannes Schindelin
2008-05-01 19:04                                                             ` Junio C Hamano
2008-05-03 12:45                                                               ` Johannes Schindelin
2008-05-03 17:09                                                                 ` Junio C Hamano
2008-05-04  9:38                                                                   ` Johannes Schindelin
2008-05-04 12:52                                                                     ` Jörg Sommer
2008-04-30 13:06                                                         ` Dmitry Potapov
2008-05-01 12:59                                                           ` Johannes Schindelin
2008-04-22 18:04                         ` Junio C Hamano
2008-04-25  9:11                           ` Jörg Sommer
2008-04-25  9:44                             ` [PATCH v2.2] " Jörg Sommer
2008-04-27  6:13                               ` Junio C Hamano
2008-04-27  8:28                                 ` Jörg Sommer
2008-04-14 10:39                   ` [PATCH v2.1] " Jörg Sommer
2008-04-14 23:29                     ` Shawn O. Pearce
2008-04-20 23:44                       ` mark parsing in fast-import Jörg Sommer
2008-04-21  0:26                         ` Shawn O. Pearce
2008-04-21  8:41                           ` Jörg Sommer
2008-04-21 23:59                             ` Shawn O. Pearce
2008-04-22  9:39                               ` Jörg Sommer
2008-04-22 23:15                                 ` Shawn O. Pearce
2008-04-25  9:04                                   ` [PATCH v2] Make mark parsing much more restrictive Jörg Sommer
2008-04-20 16:52                 ` [PATCH v2 02/13] Don't append default merge message to -m message Junio C Hamano
2008-04-21  0:17                   ` Jörg Sommer
2008-04-22  5:27                     ` Junio C Hamano
2008-03-23 22:33     ` [PATCH 3/4] Add a function for get the parents of a commit Johannes Schindelin
2008-03-23 22:29   ` [PATCH 2/4] Rework redo_merge Johannes Schindelin
2008-03-23 22:26 ` [PATCH 1/4] Move redo merge code in a function Johannes Schindelin

Reply instructions:

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

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

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

  List information: http://vger.kernel.org/majordomo-info.html

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

  git send-email \
    --in-reply-to=1208132469-26471-10-git-send-email-joerg@alea.gnuu.de \
    --to=joerg@alea.gnuu.de \
    --cc=Johannes.Schindelin@gmx.de \
    --cc=git@vger.kernel.org \
    --cc=gitster@pobox.com \
    /path/to/YOUR_REPLY

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

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://80x24.org/mirrors/git.git

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 read-only IMAP folder(s) and NNTP newsgroup(s).