git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* git rebase -i -p broken?
@ 2008-10-05 15:30 Avi Kivity
  2008-10-06 15:21 ` [PATCH RFC] rebase--interactive: if preserving merges, use first-parent to limit what is shown Stephen Haberman
  0 siblings, 1 reply; 8+ messages in thread
From: Avi Kivity @ 2008-10-05 15:30 UTC (permalink / raw)
  To: git

[-- Attachment #1: Type: text/plain, Size: 1009 bytes --]

Consider this scenario:
- commit some patch (named 'a')
- merge a random branch
- commit a fix to patch a
- since I haven't pushed yet, I want to squash a and a-fix together, to 
prevent bisect problems
- fire up 'git rebase -i -p a^'

Now the problems begin:
- the todo list shows up the branch's commits as well as my current 
branch.  But I don't want to commit the branch's commits in my own 
branch.  Replaying the merge should be enough.  Looks like a missing 
--first-parent somewhere.
- removing the spurious commit from the todo, and moving a-fix after a 
and marking it as a squash action, I get
   Refusing to squash a merge: 51ca22d7afb7433332ae41d0c2e3bab598048c21
  even though that's not a merge
- using git commit --amend instead of squash confuses git in some other way

Attached is a script that generates a test case.  With some $EDITOR 
hacks it can even be convinced to be an automated test case.

All this using 1.6.0.2.

-- 
error compiling committee.c: too many arguments to function


[-- Attachment #2: rebase-merge --]
[-- Type: text/plain, Size: 385 bytes --]

!#/bin/sh -e

git --version
mkdir repo
cd repo
git init
touch a
touch 0
git add 0
git commit -m 'zeroth commit'
git add a
git commit -m 'first commit'
git checkout -b branch
touch b
git add b
git commit -m 'second commit (branch)'
git checkout master
touch c
git add c
git commit -m 'third commit'
git merge branch
touch d
git add d
git commit -m 'fifth commit'
git rebase -i -p HEAD~4

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

* [PATCH RFC] rebase--interactive: if preserving merges, use first-parent to limit what is shown.
  2008-10-05 15:30 git rebase -i -p broken? Avi Kivity
@ 2008-10-06 15:21 ` Stephen Haberman
  2008-10-07  2:20   ` Stephen Haberman
  0 siblings, 1 reply; 8+ messages in thread
From: Stephen Haberman @ 2008-10-06 15:21 UTC (permalink / raw)
  To: Avi Kivity; +Cc: git

This commit fixes Avi Kivity's use case of squashing two commits on either side
of a merge together.

Changes include:

- Delaying storing the rewrite information if the current commit we are
  applying is being squashed. This means storing multiple lines in the
  current-commit file and recording each of them as rewritten to the
  same HEAD on the next commit.

- Move the "no squashing merges" check into the case statement for
  merges as previously it was catching catching even single-parent
  squashes.

- Conditionally pass "--first-parent" to `git rev-list` based on whether
  this is a rebase is preserving merges or not.

- At the end, just take the current head for the new branch ref instead
  of trying to look up what the OLDHEAD was rewritten to. This fails
  because the OLDHEAD was squashed/moved up earlier in the timeline, so
  even if we can find its rewritten HEAD, that is no longer what we
  ended up at.

Signed-off-by: Stephen Haberman <stephen@exigencecorp.com>
---

I agree with Avi on what the rebase -i -p behavior should be for his
scenario. This patch makes it so. However, the bane of my existence,
t3404 is failing ~12 tests in, which is a real PITA to debug, so please
let me know if this is a worthwhile tangent to continue on.

(That last change of dropping the OLDHEAD->NEWHEAD guessing is probably
what is causing t3404 to fail, but I can't reason why it'd need to do
that rather than just use HEAD.)

I've read in the archives about the git-sequencer stuff, which sounds
cool, my thought is that, if anything, this will clarify git rebase -i -p
behavior and add tests that can later be ensured to still pass when
git-sequencer is dropped in.

 git-rebase--interactive.sh               |   53 ++++++++++---------
 t/t3411-rebase-preserve-around-merges.sh |   83 ++++++++++++++++++++++++++++++
 2 files changed, 111 insertions(+), 25 deletions(-)
 create mode 100644 t/t3411-rebase-preserve-around-merges.sh

diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index edb6ec6..9914111 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -159,13 +159,18 @@ pick_one_preserving_merges () {
 
 	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"
+		if [ "$fast_forward" == "t" ]
+		then
+			cat "$DOTEST"/current-commit | while read current_commit
+			do
+				git rev-parse HEAD > "$REWRITTEN"/$current_commit
+			done
+			rm "$DOTEST"/current-commit ||
+			die "Cannot write current commit's replacement sha1"
+		fi
 	fi
 
-	echo $sha1 > "$DOTEST"/current-commit
+	echo $sha1 >> "$DOTEST"/current-commit
 
 	# rewrite parents; if none were rewritten, we can fast-forward.
 	new_parents=
@@ -193,15 +198,19 @@ pick_one_preserving_merges () {
 			die "Cannot fast forward to $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"
+
+		if [ "$1" != "-n" ]
+		then
+			# detach HEAD to current parent
+			output git checkout $first_parent 2> /dev/null ||
+				die "Cannot move HEAD to $first_parent"
+		fi
 
 		case "$new_parents" in
 		' '*' '*)
+			test "a$1" = a-n && die "Refusing to squash a merge: $sha1"
+
 			# redo merge
 			author_script=$(get_author_ident_from_commit $sha1)
 			eval "$author_script"
@@ -350,20 +359,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)" &&
@@ -561,11 +557,18 @@ first and then run 'git rebase --continue' again."
 			MERGES_OPTION=--no-merges
 		fi
 
+		if test t = "$PRESERVE_MERGES"
+		then
+			first_parent="--first-parent"
+		else
+			first_parent=""
+		fi
+
 		SHORTUPSTREAM=$(git rev-parse --short $UPSTREAM)
 		SHORTHEAD=$(git rev-parse --short $HEAD)
 		SHORTONTO=$(git rev-parse --short $ONTO)
 		git rev-list $MERGES_OPTION --pretty=oneline --abbrev-commit \
-			--abbrev=7 --reverse --left-right --cherry-pick \
+			--abbrev=7 --reverse --left-right --cherry-pick $first_parent \
 			$UPSTREAM...$HEAD | \
 			sed -n "s/^>/pick /p" > "$TODO"
 		cat >> "$TODO" << EOF
diff --git a/t/t3411-rebase-preserve-around-merges.sh b/t/t3411-rebase-preserve-around-merges.sh
new file mode 100644
index 0000000..b130f5f
--- /dev/null
+++ b/t/t3411-rebase-preserve-around-merges.sh
@@ -0,0 +1,83 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Stephen Haberman
+#
+
+test_description='git rebase preserve merges
+
+This test runs git rebase with and tries to squash a commit from after a merge
+to before the merge.
+'
+. ./test-lib.sh
+
+# Copy/paste from t3404-rebase-interactive.sh
+echo "#!$SHELL_PATH" >fake-editor.sh
+cat >> fake-editor.sh <<\EOF
+case "$1" in
+*/COMMIT_EDITMSG)
+	test -z "$FAKE_COMMIT_MESSAGE" || echo "$FAKE_COMMIT_MESSAGE" > "$1"
+	test -z "$FAKE_COMMIT_AMEND" || echo "$FAKE_COMMIT_AMEND" >> "$1"
+	exit
+	;;
+esac
+test -z "$EXPECT_COUNT" ||
+	test "$EXPECT_COUNT" = $(sed -e '/^#/d' -e '/^$/d' < "$1" | wc -l) ||
+	exit
+test -z "$FAKE_LINES" && exit
+grep -v '^#' < "$1" > "$1".tmp
+rm -f "$1"
+cat "$1".tmp
+action=pick
+for line in $FAKE_LINES; do
+	case $line in
+	squash|edit)
+		action="$line";;
+	*)
+		echo sed -n "${line}s/^pick/$action/p"
+		sed -n "${line}p" < "$1".tmp
+		sed -n "${line}s/^pick/$action/p" < "$1".tmp >> "$1"
+		action=pick;;
+	esac
+done
+EOF
+
+test_set_editor "$(pwd)/fake-editor.sh"
+chmod a+x fake-editor.sh
+
+# set up two branches like this:
+#
+# A - B - D - E - F
+#      \     /
+#       - C -
+
+test_expect_success 'setup' '
+	touch a &&
+	touch b &&
+	git add a &&
+	git commit -m A &&
+	git add b &&
+	git commit -m B &&
+	git tag B &&
+	git checkout -b branch &&
+	touch c &&
+	git add c &&
+	git commit -m C &&
+	git checkout master &&
+	touch d &&
+	git add d &&
+	git commit -m D &&
+	git merge branch &&
+	touch f &&
+	git add f &&
+	git commit -m F &&
+	git tag F
+'
+
+test_expect_success 'squash F into D' '
+	FAKE_LINES="1 squash 3 2" git rebase -i -p B &&
+	test "$(git rev-parse HEAD^2)" = "$(git rev-parse branch)" &&
+	test "$(git rev-parse HEAD~2)" = "$(git rev-parse B)"
+'
+
+test_done
+
-- 
1.6.0.2

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

* Re: [PATCH RFC] rebase--interactive: if preserving merges, use first-parent to limit what is shown.
  2008-10-06 15:21 ` [PATCH RFC] rebase--interactive: if preserving merges, use first-parent to limit what is shown Stephen Haberman
@ 2008-10-07  2:20   ` Stephen Haberman
  2008-10-07  6:36     ` Stephen Haberman
  2008-10-07  9:57     ` Avi Kivity
  0 siblings, 2 replies; 8+ messages in thread
From: Stephen Haberman @ 2008-10-07  2:20 UTC (permalink / raw)
  To: Avi Kivity; +Cc: git


> I agree with Avi on what the rebase -i -p behavior should be for his
> scenario. This patch makes it so. However, the bane of my existence,
> t3404 is failing ~12 tests in, which is a real PITA to debug, so
> please let me know if this is a worthwhile tangent to continue on.

Ah, good old t3404--it caught me on an aspect I had considered but
wanted to avoid--Avi's (and my) preferred "--first-parent" way of
listing merges works great if the right hand side of the merge commits
are outside of the branch getting rebased.

E.g. my use case is when I merge in a stable release with ~100 commits
or so and could potentially want to move it around, perhaps squash
around it as Avi pointed it, I don't want all 100 commits that are in
the stable branch to be listed in my todo file.

However, t3404 makes a good point that if the right hand of the merge
has parents that are going to get rebased, the right hand side does
need to be included/shown/rewritten.

I also went and looked at the git-sequencer rewrite of rebase-i and it
looks slick. I don't fully understand it yet, but I'm much more
inclined now to just let Stephan (& sponors/list) ably handle the
problem. Especially since its moving to builtin, it moves the required
technical ability to contribute above my current skillset--perhaps that
is the intent. :-)

So, unless I think of something else, I'm done hacking on this and
am withdrawing the patch.

Though I am curious--with the sequencer, is the Avi/my request of not
listing out-of-band commits in the todo file going to be handled?

Some sort of "--first-parent-unless-included-in-rebase" flag.

Thanks,
Stephen

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

* Re: [PATCH RFC] rebase--interactive: if preserving merges, use first-parent to limit what is shown.
  2008-10-07  2:20   ` Stephen Haberman
@ 2008-10-07  6:36     ` Stephen Haberman
  2008-10-07 14:38       ` Shawn O. Pearce
  2008-10-07  9:57     ` Avi Kivity
  1 sibling, 1 reply; 8+ messages in thread
From: Stephen Haberman @ 2008-10-07  6:36 UTC (permalink / raw)
  To: Avi Kivity; +Cc: git


> So, unless I think of something else, I'm done hacking on this and am
> withdrawing the patch.
>
> Though I am curious--with the sequencer, is the Avi/my request of not
> listing out-of-band commits in the todo file going to be handled?
> 
> Some sort of "--first-parent-unless-included-in-rebase" flag.

Okay, I lied--I have a patch that implements this behavior, passes Avi's
script-turned-test, and passes t3404. It implements the above algorithm
of, if in preserving merges mode, start with only first parents in the
todo list, and then recursively prepend right hand side commits to the
todo list only if their parents are going to be rewritten. This drops a
lot of cruft from rebase-i-p with large merges and is very cool, IMHO.

However, lest I burn my "PATCH v2" opportunity, I'm holding off on
posting the updated patch. It works and passes tests but I'm sure I'll
tinker with it some more over the next few days. It will also likely
conflict with my pu sh/maint-rebase3 patch, so I don't know whether to
base it on top of that one or not (guessing not).

Also, I think the patch itself is less interesting than the discussion
of whether this "first parent only" behavior is desired or not.

Obviously I think so--do others agree/disagree?

I've read more into the sequencer, and from what I can tell it still
just drives off a todo of pick/etc. input, and does not generate the
todo itself. So I think my patch is still fair game in terms of how to
generate either the current or the next generation rebase-i-p todo list.

I could be wrong on that though.

Thanks,
Stephen

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

* Re: [PATCH RFC] rebase--interactive: if preserving merges, use first-parent to limit what is shown.
  2008-10-07  2:20   ` Stephen Haberman
  2008-10-07  6:36     ` Stephen Haberman
@ 2008-10-07  9:57     ` Avi Kivity
  2008-10-07 12:07       ` Stephan Beyer
  1 sibling, 1 reply; 8+ messages in thread
From: Avi Kivity @ 2008-10-07  9:57 UTC (permalink / raw)
  To: Stephen Haberman; +Cc: git

Stephen Haberman wrote:
> However, t3404 makes a good point that if the right hand of the merge
> has parents that are going to get rebased, the right hand side does
> need to be included/shown/rewritten.
>
>   

But, won't those commits get linearized?  Won't git rebase pick the 
commits into the left-hand side of the merge instead of into the right 
hand side?

If git rebase is to handle nonlinear history, it needs much more 
expressive commands; not only saying which commit to pick, but also what 
the commit's parents shall be.

-- 
error compiling committee.c: too many arguments to function

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

* Re: [PATCH RFC] rebase--interactive: if preserving merges, use first-parent to limit what is shown.
  2008-10-07  9:57     ` Avi Kivity
@ 2008-10-07 12:07       ` Stephan Beyer
  2008-10-07 12:22         ` Avi Kivity
  0 siblings, 1 reply; 8+ messages in thread
From: Stephan Beyer @ 2008-10-07 12:07 UTC (permalink / raw)
  To: Avi Kivity; +Cc: Stephen Haberman, git

Hi,

Avi Kivity wrote:
> If git rebase is to handle nonlinear history, it needs much more
> expressive commands; not only saying which commit to pick, but also what  
> the commit's parents shall be.

git-sequencer has a "merge" command for that. I'm really sorry that this has
not been sent to the list yet. Nevertheless I'm always glad to find testers
for sequencer, so if you like, fetch
	git://repo.or.cz/git/sbeyer.git seq-builtin-dev

Regards,
  Stephan

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

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

* Re: [PATCH RFC] rebase--interactive: if preserving merges, use first-parent to limit what is shown.
  2008-10-07 12:07       ` Stephan Beyer
@ 2008-10-07 12:22         ` Avi Kivity
  0 siblings, 0 replies; 8+ messages in thread
From: Avi Kivity @ 2008-10-07 12:22 UTC (permalink / raw)
  To: Stephan Beyer; +Cc: Stephen Haberman, git

Stephan Beyer wrote:
> Hi,
>
> Avi Kivity wrote:
>   
>> If git rebase is to handle nonlinear history, it needs much more
>> expressive commands; not only saying which commit to pick, but also what  
>> the commit's parents shall be.
>>     
>
> git-sequencer has a "merge" command for that. I'm really sorry that this has
> not been sent to the list yet. Nevertheless I'm always glad to find testers
> for sequencer, so if you like, fetch
> 	git://repo.or.cz/git/sbeyer.git seq-builtin-dev
>
>   

But this isn't a merge; it's more of a 'pick into this branch' instead.

Maybe 'merge' can do this, but we also need to populate the todo with 
the required information (otherwise, git rebase -i without changes to 
the todo file will not be a no-op).


-- 
error compiling committee.c: too many arguments to function

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

* Re: [PATCH RFC] rebase--interactive: if preserving merges, use first-parent to limit what is shown.
  2008-10-07  6:36     ` Stephen Haberman
@ 2008-10-07 14:38       ` Shawn O. Pearce
  0 siblings, 0 replies; 8+ messages in thread
From: Shawn O. Pearce @ 2008-10-07 14:38 UTC (permalink / raw)
  To: Stephen Haberman; +Cc: Avi Kivity, git

Stephen Haberman <stephen@exigencecorp.com> wrote:
> However, lest I burn my "PATCH v2" opportunity, I'm holding off on
> posting the updated patch. It works and passes tests but I'm sure I'll
> tinker with it some more over the next few days. It will also likely
> conflict with my pu sh/maint-rebase3 patch, so I don't know whether to
> base it on top of that one or not (guessing not).

When a patch series is in `pu` it can be rebased/replaced/amended
at any time.  That's why I parked it there.  The pu branch rewinds
and is rebuilt on a daily basis.  Any commits not yet merged into
maint, master or next are automatically rebased onto the latest
maint or master branch and get merged into that day's pu.

So don't hold back on posting patches.  Folks expect to see
patches on this list; talking is less productive than posting code.
Showing code that purports to solve a problem, or that at least
displays a problem concretely is worthy of discussion.

And don't worry about replacing what is currently in pu.  Its easily
done by the maintainer.  However don't expect daily updates to a
topic in pu.  Junio (and I) just don't have the bandwidth to keep
replacing patches every day.
 
-- 
Shawn.

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

end of thread, other threads:[~2008-10-07 14:39 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-10-05 15:30 git rebase -i -p broken? Avi Kivity
2008-10-06 15:21 ` [PATCH RFC] rebase--interactive: if preserving merges, use first-parent to limit what is shown Stephen Haberman
2008-10-07  2:20   ` Stephen Haberman
2008-10-07  6:36     ` Stephen Haberman
2008-10-07 14:38       ` Shawn O. Pearce
2008-10-07  9:57     ` Avi Kivity
2008-10-07 12:07       ` Stephan Beyer
2008-10-07 12:22         ` Avi Kivity

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).