* [BUG] rebase -p loses commits @ 2011-05-16 10:33 Jeff King 2011-05-16 19:42 ` Andrew Wong 2011-05-16 20:36 ` Junio C Hamano 0 siblings, 2 replies; 28+ messages in thread From: Jeff King @ 2011-05-16 10:33 UTC (permalink / raw) To: git I was trying to reproduce somebody's issue with a minimal test case, and I ran across this setup wherein "rebase -p" silently drops some commits: commit() { echo $1 >file && git add file && git commit -m $1 } # repo with two branches, each with conflicting content git init repo && cd repo && commit base && commit master && git checkout -b feature HEAD^ && commit feature && # now merge them, with some fake resolution ! git merge master && commit resolved && # now try to "rebase -p" on top of master. git rebase -p master The rebase completes successfully, but the "feature" commit and the merge resolution are gone! I'm totally unfamiliar with the preserve-merges code, and I won't have time to dig further until later today or tomorrow, so I thought I'd throw it out here and see if anybody has any clues. -Peff ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [BUG] rebase -p loses commits 2011-05-16 10:33 [BUG] rebase -p loses commits Jeff King @ 2011-05-16 19:42 ` Andrew Wong 2011-05-16 20:36 ` Junio C Hamano 1 sibling, 0 replies; 28+ messages in thread From: Andrew Wong @ 2011-05-16 19:42 UTC (permalink / raw) To: Jeff King; +Cc: git On 05/16/2011 06:33 AM, Jeff King wrote: > I was trying to reproduce somebody's issue with a minimal test case, and > I ran across this setup wherein "rebase -p" silently drops some commits: > This particular patch seems to have something to do with the bug: d80d6bc146232d81f1bb4bc58e5d89263fd228d4 http://thread.gmane.org/gmane.comp.version-control.git/98247/focus=98251 However, I can't figure out what the "odd boundary case" that this patch was supposed to fix is. Anyone have any idea? Andrew ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [BUG] rebase -p loses commits 2011-05-16 10:33 [BUG] rebase -p loses commits Jeff King 2011-05-16 19:42 ` Andrew Wong @ 2011-05-16 20:36 ` Junio C Hamano 2011-05-17 0:33 ` Andrew Wong 2011-05-17 5:39 ` [BUG] rebase -p loses commits Jeff King 1 sibling, 2 replies; 28+ messages in thread From: Junio C Hamano @ 2011-05-16 20:36 UTC (permalink / raw) To: Jeff King; +Cc: git Jeff King <peff@peff.net> writes: > I was trying to reproduce somebody's issue with a minimal test case, and > I ran across this setup wherein "rebase -p" silently drops some commits: > > commit() { > echo $1 >file && git add file && git commit -m $1 > } > > # repo with two branches, each with conflicting content > git init repo && cd repo && > commit base && > commit master && > git checkout -b feature HEAD^ && > commit feature && > > # now merge them, with some fake resolution > ! git merge master && > commit resolved && > > # now try to "rebase -p" on top of master. > git rebase -p master Hmm, I am confused. You have this: F---* feature / / B---M master and you are at "*". If it were to rebase to linearize, B---M---F' with F' that has the same the contents as '*', possibly autoresolved by "am -3" and/or "rerere", should be what you would get. But what does it mean to rebase that on top of master, preserving merges in the first place? You are already on top of 'master' and '*' itself should be what you should get, no? IOW, shouldn't you already be up-to-date? > I'm totally unfamiliar with the preserve-merges code, and I won't have > time to dig further until later today or tomorrow, so I thought I'd > throw it out here and see if anybody has any clues. I don't use preserve-merge rebase either, but at least when you are strictly ahead of the target, nothing should happen, I think. Perhaps this should be a good start? git-rebase.sh | 11 +++++++---- 1 files changed, 7 insertions(+), 4 deletions(-) diff --git a/git-rebase.sh b/git-rebase.sh index 7a54bfc..2be10d6 100755 --- a/git-rebase.sh +++ b/git-rebase.sh @@ -466,10 +466,13 @@ require_clean_work_tree "rebase" "Please commit or stash them." # but this should be done only when upstream and onto are the same # and if this is not an interactive rebase. mb=$(git merge-base "$onto" "$orig_head") -if test "$type" != interactive && test "$upstream" = "$onto" && - test "$mb" = "$onto" && - # linear history? - ! (git rev-list --parents "$onto".."$orig_head" | sane_grep " .* ") > /dev/null +if test "$upstream" = "$onto" && test "$mb" = "$onto" && { + { + test "$type" != interactive && + # linear history? + ! (git rev-list --parents "$onto".."$orig_head" | sane_grep " .* ") > /dev/null + } || test t,implied = "$preserve_merges,$interactive_rebase" + } then if test -z "$force_rebase" then ^ permalink raw reply related [flat|nested] 28+ messages in thread
* Re: [BUG] rebase -p loses commits 2011-05-16 20:36 ` Junio C Hamano @ 2011-05-17 0:33 ` Andrew Wong 2011-05-17 0:54 ` Junio C Hamano 2011-05-17 5:44 ` Jeff King 2011-05-17 5:39 ` [BUG] rebase -p loses commits Jeff King 1 sibling, 2 replies; 28+ messages in thread From: Andrew Wong @ 2011-05-17 0:33 UTC (permalink / raw) To: Junio C Hamano; +Cc: Jeff King, git On 05/16/2011 04:36 PM, Junio C Hamano wrote: > F---* feature > / / > B---M master > > But what does it mean to rebase that on top of master, preserving merges > in the first place? You are already on top of 'master' and '*' itself > should be what you should get, no? IOW, shouldn't you already be > up-to-date? > Since preserve-merge uses the interactive-rebase, I think interactive-rebase should still pick the merge commit, which will then be consistent with what's happening if we rebase onto "F". So, without knowing whether "F" or "M" is the first-parent, I think interactive-rebase onto "F" and onto "M" should have the same effect. i.e. interactive-rebase picks the merge commit ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [BUG] rebase -p loses commits 2011-05-17 0:33 ` Andrew Wong @ 2011-05-17 0:54 ` Junio C Hamano 2011-05-17 1:02 ` Junio C Hamano 2011-05-17 5:44 ` Jeff King 1 sibling, 1 reply; 28+ messages in thread From: Junio C Hamano @ 2011-05-17 0:54 UTC (permalink / raw) To: Andrew Wong; +Cc: Jeff King, git Andrew Wong <andrew.w@sohovfx.com> writes: > On 05/16/2011 04:36 PM, Junio C Hamano wrote: >> F---* feature >> / / >> B---M master >> >> But what does it mean to rebase that on top of master, preserving merges >> in the first place? You are already on top of 'master' and '*' itself >> should be what you should get, no? IOW, shouldn't you already be >> up-to-date? >> > Since preserve-merge uses the interactive-rebase, I think > interactive-rebase should still pick the merge commit, which will then > be consistent with what's happening if we rebase onto "F". So, without > knowing whether "F" or "M" is the first-parent, I think > interactive-rebase onto "F" and onto "M" should have the same > effect. i.e. interactive-rebase picks the merge commit I agree that changing the behaviour based on the "first-ness" of the parent is a wrong thing to do in general. But even then, I have a more fundamental question. What does rebasing that '*' on top of M really mean? Does it mean this? F---* F'--*' / / ---> / / B---M B---M---/ In other words: Take all the commits reachable from '*', exclude the ones that are ancestors of M, and make sure that commit that corresponds to each of the remaining commits (in this case, F' and *' correspond to F and * respectively) are decendants of M, and reconstruct the graph preserving the parenthood relationship between corresponding commits. I would understand why some project may want to require you to rebase to the tip to keep a linear history, and the above sentence would be the right specification for linearizing rebase except for the "and reconstruct the graph" part. But the above "preserving" rewrite does not even preserve the topology of the graph (the original * is a true merge between two forks, but *' is not) to begin with. Also, if you want to _usefully_ place F' on top of M, such a rewrite should resolve possible conflicts that was resolved at * in the original graph at F' anyway, which would mean that the resulting *' should become a totally empty commit. Why would anybody want to do such a thing to begin with? ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [BUG] rebase -p loses commits 2011-05-17 0:54 ` Junio C Hamano @ 2011-05-17 1:02 ` Junio C Hamano 0 siblings, 0 replies; 28+ messages in thread From: Junio C Hamano @ 2011-05-17 1:02 UTC (permalink / raw) To: Junio C Hamano; +Cc: Andrew Wong, Jeff King, git Junio C Hamano <gitster@pobox.com> writes: > But the above "preserving" rewrite does not even preserve the topology of > the graph (the original * is a true merge between two forks, but *' is > not) to begin with. Also, if you want to _usefully_ place F' on top of M, > such a rewrite should resolve possible conflicts that was resolved at * in > the original graph at F' anyway, which would mean that the resulting *' > should become a totally empty commit. > > Why would anybody want to do such a thing to begin with? Note that I am not saying "rebase -p" is not useful in general. If you had x---x---x---W---X / \ \ ---M Y---Z it is entirely sensible to want to have this history to exclude 'x' x---x---x---W---X / \ \ ---M---W'--X' Y---Z \ \ Y'--Z' I think the patch I posted earlier should stop the problematic case Jeff mentioned from happening, but I am trying to see if it makes sense to stop without doing anything even when it is forced when onto and merge-base are the same commit (which is not true for this "sensible" case). ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [BUG] rebase -p loses commits 2011-05-17 0:33 ` Andrew Wong 2011-05-17 0:54 ` Junio C Hamano @ 2011-05-17 5:44 ` Jeff King 2011-05-17 16:07 ` Andrew Wong 1 sibling, 1 reply; 28+ messages in thread From: Jeff King @ 2011-05-17 5:44 UTC (permalink / raw) To: Andrew Wong; +Cc: Junio C Hamano, git On Mon, May 16, 2011 at 08:33:59PM -0400, Andrew Wong wrote: > On 05/16/2011 04:36 PM, Junio C Hamano wrote: > > F---* feature > > / / > > B---M master > > > >But what does it mean to rebase that on top of master, preserving merges > >in the first place? You are already on top of 'master' and '*' itself > >should be what you should get, no? IOW, shouldn't you already be > >up-to-date? > Since preserve-merge uses the interactive-rebase, I think > interactive-rebase should still pick the merge commit, which will > then be consistent with what's happening if we rebase onto "F". So, > without knowing whether "F" or "M" is the first-parent, I think > interactive-rebase onto "F" and onto "M" should have the same effect. > i.e. interactive-rebase picks the merge commit Is it really the first-parentness here that is important to the asymmetry? I thought it was more the fact that "feature" has the merge, but "master" does not. Therefore, "git rebase -p master feature" knows that there is nothing to do; we already contain all of "master". But "git rebase -p feature master" sees that we have an extra commit in "feature" not in "master", and therefore we must attempt to rewrite on top of that merge commit. Of course, when we try to do so, there are no commits that are not already on "feature", so there is nothing to rewrite. So the outcomes are the same, but the reasoning is different. And isn't that what happens with Junio's patch (I tried a simple test and it seemed to be)? -Peff ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [BUG] rebase -p loses commits 2011-05-17 5:44 ` Jeff King @ 2011-05-17 16:07 ` Andrew Wong 2011-05-17 16:12 ` Jeff King 0 siblings, 1 reply; 28+ messages in thread From: Andrew Wong @ 2011-05-17 16:07 UTC (permalink / raw) To: Jeff King; +Cc: Junio C Hamano, git On 05/17/2011 01:44 AM, Jeff King wrote: > Is it really the first-parentness here that is important to the > asymmetry? I thought it was more the fact that "feature" has the merge, > but "master" does not. To know the fact that "feature" has the merge, don't we need to know that "feature" is the first parent of the merge? For example, if "F" is the head of "feature" and we're on "*" as a detached head, then we can only say "feature" has the merge if we know "feature" is the first parent. > So the outcomes are the same, but the reasoning is different. And isn't > that what happens with Junio's patch (I tried a simple test and it > seemed to be)? I agree that the outcome of both should be the same. Junio's patch will fix the case when we do "git rebase -p", but the bug will still appear as soon as we do "git rebase -p -i", which I think is where the source of the problem is. So we should be looking to fix the issue with "git rebase -p -i", which will also fix "git rebase -p" too. I think it's pretty reasonable for "rebase -p -i" to pick the merge commit, which is already happening with "git rebase -p F". A possible use case for picking the merge commit is to do a fixup/squash/reword on "G" in the following graph: F---G---H / / B---M ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [BUG] rebase -p loses commits 2011-05-17 16:07 ` Andrew Wong @ 2011-05-17 16:12 ` Jeff King 2011-05-21 5:51 ` [RFC] Interactive-rebase doesn't pick all children of "upstream" Andrew Wong 2011-06-05 5:32 ` [PATCH] " Andrew Wong 0 siblings, 2 replies; 28+ messages in thread From: Jeff King @ 2011-05-17 16:12 UTC (permalink / raw) To: Andrew Wong; +Cc: Junio C Hamano, git On Tue, May 17, 2011 at 12:07:38PM -0400, Andrew Wong wrote: > > Is it really the first-parentness here that is important to the > > asymmetry? I thought it was more the fact that "feature" has the merge, > > but "master" does not. > To know the fact that "feature" has the merge, don't we need to know > that "feature" is the first parent of the merge? For example, if "F" is > the head of "feature" and we're on "*" as a detached head, then we can > only say "feature" has the merge if we know "feature" is the first parent. No, if "F" is the head of "feature", then it does _not_ have the merge. But it's not the merge that is important, it is really the fact that if we are rebasing "feature" on "master", that "master ^feature" is empty. IOW, we are already a superset. In this example, though, the merge is the thing that gives us that superset. > I agree that the outcome of both should be the same. Junio's patch will > fix the case when we do "git rebase -p", but the bug will still appear > as soon as we do "git rebase -p -i", which I think is where the source > of the problem is. So we should be looking to fix the issue with "git > rebase -p -i", which will also fix "git rebase -p" too. Ah, I see. Yes, in that case, we should definitely be fixing "git rebase -p -i". -Peff ^ permalink raw reply [flat|nested] 28+ messages in thread
* [RFC] Interactive-rebase doesn't pick all children of "upstream" 2011-05-17 16:12 ` Jeff King @ 2011-05-21 5:51 ` Andrew Wong 2011-05-21 5:51 ` Andrew Wong 2011-06-05 5:32 ` [PATCH] " Andrew Wong 1 sibling, 1 reply; 28+ messages in thread From: Andrew Wong @ 2011-05-21 5:51 UTC (permalink / raw) To: git; +Cc: Andrew Wong This is a work-in-progress patch for this issue. Since this patch changes a fairly significant behavior in interactive-rebase, I want to try to get some feedbacks before I go ahead and start writing some tests for this new behavior. Andrew Wong (1): Interactive-rebase doesn't pick all children of "upstream" git-rebase--interactive.sh | 7 +++++-- t/t3404-rebase-interactive.sh | 2 +- t/t3411-rebase-preserve-around-merges.sh | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) -- 1.7.5.2.316.gd7d8c.dirty ^ permalink raw reply [flat|nested] 28+ messages in thread
* [RFC] Interactive-rebase doesn't pick all children of "upstream" 2011-05-21 5:51 ` [RFC] Interactive-rebase doesn't pick all children of "upstream" Andrew Wong @ 2011-05-21 5:51 ` Andrew Wong 2011-05-21 7:34 ` Andrew Wong 0 siblings, 1 reply; 28+ messages in thread From: Andrew Wong @ 2011-05-21 5:51 UTC (permalink / raw) To: git; +Cc: Andrew Wong Consider this graph: D---E (topic, HEAD) / / A---B---C (master) \ F (topic2) and the following three commands: 1. git rebase -i A 2. git rebase -i --onto F B 3. git rebase -i B Currently, (1) and (2) will pick B, D, C, and E onto A and F, respectively. However, (3) will only pick D and E onto B. This behavior of (3) is inconsistent with (1) and (2). This also creates a bug if we do: 4. git rebase -i C In (4), E is never picked. And since interactive-rebase resets "HEAD" to "onto", E is lost after the interactive-rebase. This patch fixes the inconsistency and bug by ensuring that all children of upstream are always picked. Two of the tests contain a scenario like (3). Since the new behavior added more commits for picking, these tests need to be updated to edit the "todo" list properly. --- git-rebase--interactive.sh | 7 +++++-- t/t3404-rebase-interactive.sh | 2 +- t/t3411-rebase-preserve-around-merges.sh | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index 41ba96a..b6d1e5b 100644 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -711,7 +711,7 @@ then # parents to rewrite and skipping dropped commits would # prematurely end our probe merges_option= - first_after_upstream="$(git rev-list --reverse --first-parent $upstream..$orig_head | head -n 1)" + commits_after_upstream="$(git rev-list --reverse --parents $upstream..$orig_head | sane_grep " $upstream" | cut -d' ' -s -f1)" else merges_option="--no-merges --cherry-pick" fi @@ -744,7 +744,10 @@ do preserve=t for p in $(git rev-list --parents -1 $sha1 | cut -d' ' -s -f2-) do - if test -f "$rewritten"/$p -a \( $p != $onto -o $sha1 = $first_after_upstream \) + if test -f "$rewritten"/$p && ( + test $p != $onto || + expr "$commits_after_upstream" ":" ".*$sha1.*" + ) then preserve=f fi diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh index 7d8147b..c3cddcd 100755 --- a/t/t3404-rebase-interactive.sh +++ b/t/t3404-rebase-interactive.sh @@ -295,7 +295,7 @@ test_expect_success 'preserve merges with -p' ' ' test_expect_success 'edit ancestor with -p' ' - FAKE_LINES="1 edit 2 3 4" git rebase -i -p HEAD~3 && + FAKE_LINES="1 2 edit 3 4" git rebase -i -p HEAD~3 && echo 2 > unrelated-file && test_tick && git commit -m L2-modified --amend unrelated-file && diff --git a/t/t3411-rebase-preserve-around-merges.sh b/t/t3411-rebase-preserve-around-merges.sh index 14a23cd..ace8e54 100755 --- a/t/t3411-rebase-preserve-around-merges.sh +++ b/t/t3411-rebase-preserve-around-merges.sh @@ -37,7 +37,7 @@ test_expect_success 'setup' ' # -- C1 -- # test_expect_success 'squash F1 into D1' ' - FAKE_LINES="1 squash 3 2" git rebase -i -p B1 && + FAKE_LINES="1 squash 4 2 3" git rebase -i -p B1 && test "$(git rev-parse HEAD^2)" = "$(git rev-parse C1)" && test "$(git rev-parse HEAD~2)" = "$(git rev-parse B1)" && git tag E2 -- 1.7.5.2.316.gd7d8c.dirty ^ permalink raw reply related [flat|nested] 28+ messages in thread
* Re: [RFC] Interactive-rebase doesn't pick all children of "upstream" 2011-05-21 5:51 ` Andrew Wong @ 2011-05-21 7:34 ` Andrew Wong 0 siblings, 0 replies; 28+ messages in thread From: Andrew Wong @ 2011-05-21 7:34 UTC (permalink / raw) To: Andrew Wong; +Cc: git On 11-05-21 1:51 AM, Andrew Wong wrote: > preserve=t > for p in $(git rev-list --parents -1 $sha1 | cut -d' ' -s -f2-) > do > - if test -f "$rewritten"/$p -a \( $p != $onto -o $sha1 = $first_after_upstream \) > + if test -f "$rewritten"/$p&& ( > + test $p != $onto || > + expr "$commits_after_upstream" ":" ".*$sha1.*" > + ) > then > preserve=f > fi Actually, I think this change might be effectively the same as removing both of the OR-conditions, which leaves only the "test -f" condition. That means commits are never skipped. I guess this goes back to my first response to this issue. What kind of commits are skipped by the changes introduced in commit d80d6bc146232d81f1bb4bc58e5d89263fd228d4 ? ^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH] Interactive-rebase doesn't pick all children of "upstream" 2011-05-17 16:12 ` Jeff King 2011-05-21 5:51 ` [RFC] Interactive-rebase doesn't pick all children of "upstream" Andrew Wong @ 2011-06-05 5:32 ` Andrew Wong 2011-06-05 9:16 ` Johannes Sixt 1 sibling, 1 reply; 28+ messages in thread From: Andrew Wong @ 2011-06-05 5:32 UTC (permalink / raw) To: git; +Cc: Andrew Wong Consider this graph: D---E (topic, HEAD) / / A---B---C (master) \ F (topic2) and the following three commands: 1. git rebase -i A 2. git rebase -i --onto F A 3. git rebase -i B Currently, (1) and (2) will pick B, D, C, and E onto A and F, respectively. However, (3) will only pick D and E onto B. This behavior of (3) is inconsistent with (1) and (2), and we cannot modify C in the interactive-rebase. The current behavior also creates a bug if we do: 4. git rebase -i C In (4), E is never picked. And since interactive-rebase resets "HEAD" to "onto" before picking any commits, D and E are lost after the interactive-rebase. This patch fixes the inconsistency and bug by ensuring that all children of upstream are always picked. This essentially reverts the commit: d80d6bc146232d81f1bb4bc58e5d89263fd228d4 Commits reachable from "upstream" should never be skipped under any condition. Otherwise we lose the chance to modify them like (3), and create bug like (4). Two of the tests contain a scenario like (3). Since the new behavior added more commits for picking, these tests need to be updated to edit the "todo" list properly. Also added test for scenario (4). --- git-rebase--interactive.sh | 3 +-- t/t3404-rebase-interactive.sh | 2 +- t/t3409-rebase-preserve-merges.sh | 28 +++++++++++++++++++++++++++- t/t3411-rebase-preserve-around-merges.sh | 2 +- 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index 65690af..c6ba7c1 100644 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -713,7 +713,6 @@ then # parents to rewrite and skipping dropped commits would # prematurely end our probe merges_option= - first_after_upstream="$(git rev-list --reverse --first-parent $upstream..$orig_head | head -n 1)" else merges_option="--no-merges --cherry-pick" fi @@ -746,7 +745,7 @@ do preserve=t for p in $(git rev-list --parents -1 $sha1 | cut -d' ' -s -f2-) do - if test -f "$rewritten"/$p -a \( $p != $onto -o $sha1 = $first_after_upstream \) + if test -f "$rewritten"/$p then preserve=f fi diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh index 47c8371..8538813 100755 --- a/t/t3404-rebase-interactive.sh +++ b/t/t3404-rebase-interactive.sh @@ -295,7 +295,7 @@ test_expect_success 'preserve merges with -p' ' ' test_expect_success 'edit ancestor with -p' ' - FAKE_LINES="1 edit 2 3 4" git rebase -i -p HEAD~3 && + FAKE_LINES="1 2 edit 3 4" git rebase -i -p HEAD~3 && echo 2 > unrelated-file && test_tick && git commit -m L2-modified --amend unrelated-file && diff --git a/t/t3409-rebase-preserve-merges.sh b/t/t3409-rebase-preserve-merges.sh index 08201e2..16d316d 100755 --- a/t/t3409-rebase-preserve-merges.sh +++ b/t/t3409-rebase-preserve-merges.sh @@ -37,7 +37,15 @@ export GIT_AUTHOR_EMAIL # \ # B2 <-- origin/topic # -# In all cases, 'topic' is rebased onto 'origin/topic'. +# Clone 4 (): +# +# A1--A2--B3 <-- origin/master +# \ +# B1--A3--M <-- topic +# \ / +# \--A4 <-- topic2 +# \ +# B2 <-- origin/topic test_expect_success 'setup for merge-preserving rebase' \ 'echo First > A && @@ -57,6 +65,13 @@ test_expect_success 'setup for merge-preserving rebase' \ git merge origin/master ) && + git clone ./. clone4 && + ( + cd clone4 && + git checkout -b topic origin/topic && + git merge origin/master + ) && + echo Fifth > B && git add B && git commit -m "Add different B" && @@ -123,4 +138,15 @@ test_expect_success 'rebase -p preserves no-ff merges' ' ) ' +test_expect_success '' ' + ( + cd clone4 && + git fetch && + git rebase -p HEAD^2 && + test 1 = $(git rev-list --all --pretty=oneline | grep "Modify A" | wc -l) && + test 1 = $(git rev-list --all --pretty=oneline | grep "Modify B" | wc -l) && + test 1 = $(git rev-list --all --pretty=oneline | grep "Merge remote-tracking branch " | wc -l) + ) +' + test_done diff --git a/t/t3411-rebase-preserve-around-merges.sh b/t/t3411-rebase-preserve-around-merges.sh index 14a23cd..ace8e54 100755 --- a/t/t3411-rebase-preserve-around-merges.sh +++ b/t/t3411-rebase-preserve-around-merges.sh @@ -37,7 +37,7 @@ test_expect_success 'setup' ' # -- C1 -- # test_expect_success 'squash F1 into D1' ' - FAKE_LINES="1 squash 3 2" git rebase -i -p B1 && + FAKE_LINES="1 squash 4 2 3" git rebase -i -p B1 && test "$(git rev-parse HEAD^2)" = "$(git rev-parse C1)" && test "$(git rev-parse HEAD~2)" = "$(git rev-parse B1)" && git tag E2 -- 1.7.6.rc0 ^ permalink raw reply related [flat|nested] 28+ messages in thread
* Re: [PATCH] Interactive-rebase doesn't pick all children of "upstream" 2011-06-05 5:32 ` [PATCH] " Andrew Wong @ 2011-06-05 9:16 ` Johannes Sixt 2011-06-05 14:11 ` Andrew Wong 2011-06-07 4:08 ` [PATCH v2] rebase -i -p: doesn't pick certain merge commits that are " Andrew Wong 0 siblings, 2 replies; 28+ messages in thread From: Johannes Sixt @ 2011-06-05 9:16 UTC (permalink / raw) To: Andrew Wong; +Cc: git Am 05.06.2011 07:32, schrieb Andrew Wong: > Consider this graph: > > D---E (topic, HEAD) > / / > A---B---C (master) > \ > F (topic2) > > and the following three commands: > 1. git rebase -i A > 2. git rebase -i --onto F A > 3. git rebase -i B > > Currently, (1) and (2) will pick B, D, C, and E onto A and F, > respectively. However, (3) will only pick D and E onto B. This > behavior of (3) is inconsistent with (1) and (2), and we cannot modify C > in the interactive-rebase. I cannot reproduce your claims: - (1) and (2) picks B,C,D top A and F, but not E because E is a merge. - (3) picks C and D, but not E because E is a merge. > The current behavior also creates a bug if we do: > 4. git rebase -i C > > In (4), E is never picked. And since interactive-rebase resets "HEAD" to > "onto" before picking any commits, D and E are lost after the > interactive-rebase. (4) picks only D, because E is a merge. I don't understand what you mean that "D and E are lost"; E is not picked in the first place, but D is in the todo-list; how can D be lost? BTW, rebase never picks merges by design. I don't see anything wrong so far with the current behavior. Please explain! -- Hannes ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH] Interactive-rebase doesn't pick all children of "upstream" 2011-06-05 9:16 ` Johannes Sixt @ 2011-06-05 14:11 ` Andrew Wong 2011-06-07 4:08 ` [PATCH v2] rebase -i -p: doesn't pick certain merge commits that are " Andrew Wong 1 sibling, 0 replies; 28+ messages in thread From: Andrew Wong @ 2011-06-05 14:11 UTC (permalink / raw) To: Johannes Sixt; +Cc: git On 11-06-05 5:16 AM, Johannes Sixt wrote: > Am 05.06.2011 07:32, schrieb Andrew Wong: >> Currently, (1) and (2) will pick B, D, C, and E onto A and F, >> respectively. However, (3) will only pick D and E onto B. This >> behavior of (3) is inconsistent with (1) and (2), and we cannot modify C >> in the interactive-rebase. > I cannot reproduce your claims: > > - (1) and (2) picks B,C,D top A and F, but not E because E is a merge. > > - (3) picks C and D, but not E because E is a merge. Ah, all those commands should have "-p" on them to preserve the merges. Thanks for the catch! ^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH v2] rebase -i -p: doesn't pick certain merge commits that are children of "upstream" 2011-06-05 9:16 ` Johannes Sixt 2011-06-05 14:11 ` Andrew Wong @ 2011-06-07 4:08 ` Andrew Wong 2011-06-07 4:08 ` [PATCH] " Andrew Wong 1 sibling, 1 reply; 28+ messages in thread From: Andrew Wong @ 2011-06-07 4:08 UTC (permalink / raw) To: git; +Cc: Andrew Wong This patch contains an updated commit message. The "-p" flags, which are vital to this issue, were completely missing from the previous message. Andrew Wong (1): rebase -i -p: doesn't pick certain merge commits that are children of "upstream" git-rebase--interactive.sh | 3 +-- t/t3404-rebase-interactive.sh | 2 +- t/t3409-rebase-preserve-merges.sh | 28 +++++++++++++++++++++++++++- t/t3411-rebase-preserve-around-merges.sh | 2 +- 4 files changed, 30 insertions(+), 5 deletions(-) -- 1.7.6.rc0.1.gf20d7 ^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH] rebase -i -p: doesn't pick certain merge commits that are children of "upstream" 2011-06-07 4:08 ` [PATCH v2] rebase -i -p: doesn't pick certain merge commits that are " Andrew Wong @ 2011-06-07 4:08 ` Andrew Wong 2011-06-12 16:28 ` Andrew Wong 2011-06-13 16:01 ` Junio C Hamano 0 siblings, 2 replies; 28+ messages in thread From: Andrew Wong @ 2011-06-07 4:08 UTC (permalink / raw) To: git; +Cc: Andrew Wong Consider this graph: D---E (topic, HEAD) / / A---B---C (master) \ F (topic2) and the following three commands: 1. git rebase -i -p A 2. git rebase -i -p --onto F A 3. git rebase -i -p B Currently, (1) and (2) will pick B, D, C, and E onto A and F, respectively. However, (3) will only pick D and E onto B, but not C, which is inconsistent with (1) and (2). As a result, we cannot modify C during the interactive-rebase. The current behavior also creates a bug if we do: 4. git rebase -i -p C In (4), E is never picked. And since interactive-rebase resets "HEAD" to "onto" before picking any commits, D and E are lost after the interactive-rebase. This patch fixes the inconsistency and bug by ensuring that all children of upstream are always picked. This essentially reverts the commit: d80d6bc146232d81f1bb4bc58e5d89263fd228d4 When compiling the "todo" list, commits reachable from "upstream" should never be skipped under any conditions. Otherwise, we lose the ability to modify them like (3), and create a bug like (4). Two of the tests contain a scenario like (3). Since the new behavior added more commits for picking, these tests need to be updated to edit the "todo" list properly. A new test has also been added for (4). Signed-off-by: Andrew Wong <andrew.kw.w@gmail.com> --- git-rebase--interactive.sh | 3 +-- t/t3404-rebase-interactive.sh | 2 +- t/t3409-rebase-preserve-merges.sh | 28 +++++++++++++++++++++++++++- t/t3411-rebase-preserve-around-merges.sh | 2 +- 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index 65690af..c6ba7c1 100644 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -713,7 +713,6 @@ then # parents to rewrite and skipping dropped commits would # prematurely end our probe merges_option= - first_after_upstream="$(git rev-list --reverse --first-parent $upstream..$orig_head | head -n 1)" else merges_option="--no-merges --cherry-pick" fi @@ -746,7 +745,7 @@ do preserve=t for p in $(git rev-list --parents -1 $sha1 | cut -d' ' -s -f2-) do - if test -f "$rewritten"/$p -a \( $p != $onto -o $sha1 = $first_after_upstream \) + if test -f "$rewritten"/$p then preserve=f fi diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh index 47c8371..8538813 100755 --- a/t/t3404-rebase-interactive.sh +++ b/t/t3404-rebase-interactive.sh @@ -295,7 +295,7 @@ test_expect_success 'preserve merges with -p' ' ' test_expect_success 'edit ancestor with -p' ' - FAKE_LINES="1 edit 2 3 4" git rebase -i -p HEAD~3 && + FAKE_LINES="1 2 edit 3 4" git rebase -i -p HEAD~3 && echo 2 > unrelated-file && test_tick && git commit -m L2-modified --amend unrelated-file && diff --git a/t/t3409-rebase-preserve-merges.sh b/t/t3409-rebase-preserve-merges.sh index 08201e2..16d316d 100755 --- a/t/t3409-rebase-preserve-merges.sh +++ b/t/t3409-rebase-preserve-merges.sh @@ -37,7 +37,15 @@ export GIT_AUTHOR_EMAIL # \ # B2 <-- origin/topic # -# In all cases, 'topic' is rebased onto 'origin/topic'. +# Clone 4 (): +# +# A1--A2--B3 <-- origin/master +# \ +# B1--A3--M <-- topic +# \ / +# \--A4 <-- topic2 +# \ +# B2 <-- origin/topic test_expect_success 'setup for merge-preserving rebase' \ 'echo First > A && @@ -57,6 +65,13 @@ test_expect_success 'setup for merge-preserving rebase' \ git merge origin/master ) && + git clone ./. clone4 && + ( + cd clone4 && + git checkout -b topic origin/topic && + git merge origin/master + ) && + echo Fifth > B && git add B && git commit -m "Add different B" && @@ -123,4 +138,15 @@ test_expect_success 'rebase -p preserves no-ff merges' ' ) ' +test_expect_success '' ' + ( + cd clone4 && + git fetch && + git rebase -p HEAD^2 && + test 1 = $(git rev-list --all --pretty=oneline | grep "Modify A" | wc -l) && + test 1 = $(git rev-list --all --pretty=oneline | grep "Modify B" | wc -l) && + test 1 = $(git rev-list --all --pretty=oneline | grep "Merge remote-tracking branch " | wc -l) + ) +' + test_done diff --git a/t/t3411-rebase-preserve-around-merges.sh b/t/t3411-rebase-preserve-around-merges.sh index 14a23cd..ace8e54 100755 --- a/t/t3411-rebase-preserve-around-merges.sh +++ b/t/t3411-rebase-preserve-around-merges.sh @@ -37,7 +37,7 @@ test_expect_success 'setup' ' # -- C1 -- # test_expect_success 'squash F1 into D1' ' - FAKE_LINES="1 squash 3 2" git rebase -i -p B1 && + FAKE_LINES="1 squash 4 2 3" git rebase -i -p B1 && test "$(git rev-parse HEAD^2)" = "$(git rev-parse C1)" && test "$(git rev-parse HEAD~2)" = "$(git rev-parse B1)" && git tag E2 -- 1.7.6.rc0.1.gf20d7 ^ permalink raw reply related [flat|nested] 28+ messages in thread
* Re: [PATCH] rebase -i -p: doesn't pick certain merge commits that are children of "upstream" 2011-06-07 4:08 ` [PATCH] " Andrew Wong @ 2011-06-12 16:28 ` Andrew Wong 2011-06-13 16:01 ` Junio C Hamano 1 sibling, 0 replies; 28+ messages in thread From: Andrew Wong @ 2011-06-12 16:28 UTC (permalink / raw) To: Andrew Wong; +Cc: git Any chance we can look into getting this patch into git? http://permalink.gmane.org/gmane.comp.version-control.git/175185 ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH] rebase -i -p: doesn't pick certain merge commits that are children of "upstream" 2011-06-07 4:08 ` [PATCH] " Andrew Wong 2011-06-12 16:28 ` Andrew Wong @ 2011-06-13 16:01 ` Junio C Hamano 2011-06-13 17:30 ` Andrew Wong 1 sibling, 1 reply; 28+ messages in thread From: Junio C Hamano @ 2011-06-13 16:01 UTC (permalink / raw) To: Andrew Wong; +Cc: git, Stephen Haberman Andrew Wong <andrew.kw.w@gmail.com> writes: > This patch fixes the inconsistency and bug by ensuring that all children > of upstream are always picked. This essentially reverts the commit: > > d80d6bc (rebase-i-p: do not include non-first-parent commits touching UPSTREAM, 2008-10-15) ... whose commit log message mumbles about somebody's script but came with no tests, so we will not know if this is breaking the other guy's workflow while adding support to yours (Cc'ed Stephen Haberman who wrote the previous one). > > +test_expect_success '' ' There is no title to this test? > + ( > + cd clone4 && > + git fetch && > + git rebase -p HEAD^2 && > + test 1 = $(git rev-list --all --pretty=oneline | grep "Modify A" | wc -l) && > + test 1 = $(git rev-list --all --pretty=oneline | grep "Modify B" | wc -l) && > + test 1 = $(git rev-list --all --pretty=oneline | grep "Merge remote-tracking branch " | wc -l) > + ) > +' > + > test_done In general I think it is wrong to change behaviour depending on which parent of a merge we are looking at (unless of course the user tells us to, like "git log --first-parent"), so in that sense philosophically I think the patch is going in the right direction, but I do worry about potential regressions. ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH] rebase -i -p: doesn't pick certain merge commits that are children of "upstream" 2011-06-13 16:01 ` Junio C Hamano @ 2011-06-13 17:30 ` Andrew Wong 2011-06-16 22:24 ` Stephen Haberman 0 siblings, 1 reply; 28+ messages in thread From: Andrew Wong @ 2011-06-13 17:30 UTC (permalink / raw) To: Junio C Hamano; +Cc: Andrew Wong, git, Stephen Haberman On 06/13/2011 12:01 PM, Junio C Hamano wrote: > There is no title to this test? > Ah, that's embarrassing. I'll fix that. Thanks! > In general I think it is wrong to change behaviour depending on which > parent of a merge we are looking at (unless of course the user tells us > to, like "git log --first-parent"), so in that sense philosophically I > think the patch is going in the right direction, but I do worry about > potential regressions. > I totally agree. Ever since Jeff brought up this issue, I've been wondering what issue/workflow is that patch trying to fix. If the "todo" list doesn't change the parent of the merge commits, git should be able to do a fast-forward on the merge, which means the merge won't be rewritten anyway. Just a wild guess: maybe back then, git will actually rewrite the merge regardless? Anyway, let's wait for a reply from Stephen. ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH] rebase -i -p: doesn't pick certain merge commits that are children of "upstream" 2011-06-13 17:30 ` Andrew Wong @ 2011-06-16 22:24 ` Stephen Haberman 2011-06-18 6:40 ` Andrew Wong 0 siblings, 1 reply; 28+ messages in thread From: Stephen Haberman @ 2011-06-16 22:24 UTC (permalink / raw) To: Andrew Wong; +Cc: Junio C Hamano, Andrew Wong, git Hey, > Ever since Jeff brought up this issue, I've been wondering what > issue/workflow is that patch trying to fix. I'm fairly sure the case the patch was fixing is t3411's "squash F1 into D1". Where, starting with a tree like: # A1 - B1 - D1 - E1 - F1 # \ / # -- C1 -- The user is on F1 and issues: "git rebase -i -p B1", the todo list is "D1, E1, F1" (no C1), and they choose "D1 squash F1, E1", the resulting tree should be: # A1 - B1 - D2 - E2 # \ / # -- C1 -- And, the fix was that C1 should not be in the todo list. Perhaps that is unreasonable with whatever you guys are looking at now, but, IIRC, the use case was that B1=some old commit, like a 2.0 release, and a bunch of work happened on the C1 branch, it was merged in E1, but now when you want to rebase D1/E1/F1 on top of B1, you don't want all of the noise of the C1 commit(s), since when rewriting E1 into E2, you can just reuse the un-rewritten C1 as its 2nd parent. Well, and not just the noise--since the todo is still flat, if C1 was listed in the todo, there's no way to recreate E2 as a merge and maintain the C1 commit(s) as a separate branch. I think C1 would get flattened between D2/E2, depending on where it was in the todo. You'd lose a merge, contrary to the -p flag. That sounds like the core issue that was being fixed. The patch in question: http://article.gmane.org/gmane.comp.version-control.git/98251 Did actually have a test (t3411) but it was still failing until the following commit: http://article.gmane.org/gmane.comp.version-control.git/98253 Where the test changed from expect failure to expect success. I remember that looking odd at the time, but for some reason liked the commits being separate. - Stephen ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH] rebase -i -p: doesn't pick certain merge commits that are children of "upstream" 2011-06-16 22:24 ` Stephen Haberman @ 2011-06-18 6:40 ` Andrew Wong 2011-06-18 15:17 ` Stephen Haberman 0 siblings, 1 reply; 28+ messages in thread From: Andrew Wong @ 2011-06-18 6:40 UTC (permalink / raw) To: Stephen Haberman; +Cc: Junio C Hamano, Andrew Wong, git Here's a list of those commits in "git log"-order for easy reference: 80fe82e rebase-i-p: if todo was reordered use HEAD as the rewritten parent d80d6bc rebase-i-p: do not include non-first-parent commits touching UPSTREAM acc8559 rebase-i-p: only list commits that require rewriting in todo a4f25e3 rebase-i-p: fix 'no squashing merges' tripping up non-merges bb64507 rebase-i-p: delay saving current-commit to REWRITTEN if squashing 72583e6 rebase-i-p: use HEAD for updating the ref instead of mapping OLDHEAD 42f939e rebase-i-p: test to exclude commits from todo based on its parents On 11-06-16 6:24 PM, Stephen Haberman wrote: > Perhaps that is unreasonable with whatever you guys are looking at now, > but, IIRC, the use case was that B1=some old commit, like a 2.0 > release, and a bunch of work happened on the C1 branch, it was merged > in E1, but now when you want to rebase D1/E1/F1 on top of B1, you don't > want all of the noise of the C1 commit(s), since when rewriting E1 into > E2, you can just reuse the un-rewritten C1 as its 2nd parent. In commit a4f25e3, we could already rebase B1 and squash F1 onto D1, while reusing C1 and recreating the merge. That means we could already pass t3411.2if we adjusted the todo-list to account for the extra "pick C1" line. > Well, and not just the noise--since the todo is still flat, if C1 > was listed in the todo, there's no way to recreate E2 as a merge and > maintain the C1 commit(s) as a separate branch. I think C1 would get > flattened between D2/E2, depending on where it was in the todo. You'd > lose a merge, contrary to the -p flag. That sounds like the core issue > that was being fixed The merge shouldn't get flattened when the "-p" is used. As long as the merge commit appears in the todo-list, git will trace the parents of the merge commit, find the original or rewritten parents, and perform the merge. Slightly off-topic, but I believe the branches will remain intact as long as the branch commits remain in the same topo-order relative to each other in the todo-list. i.e. git will be confused if we try to move a commit from one branch into the other. The "noise" is filtered out by by commit d80d6bc. However, I think we should keep the commits from branch C1, since there could be a scenario where we actually want to squash F1 onto C1 instead. That commit also introduced a bug that Jeff King was running into: if we do "git rebase -i -p C1", the todo-list becomes a "noop", which means HEAD is reset to C1 and we lose the merge commit and F1. So what I did in my patch is essentially revert the changes from d80d6bc, and adjust t3411.2 to account for the extra "pick C1" line. ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH] rebase -i -p: doesn't pick certain merge commits that are children of "upstream" 2011-06-18 6:40 ` Andrew Wong @ 2011-06-18 15:17 ` Stephen Haberman 2011-06-18 16:47 ` Andrew Wong 0 siblings, 1 reply; 28+ messages in thread From: Stephen Haberman @ 2011-06-18 15:17 UTC (permalink / raw) To: Andrew Wong; +Cc: Junio C Hamano, Andrew Wong, git > In commit a4f25e3, we could already rebase B1 and squash F1 onto D1, > while reusing C1 and recreating the merge. That means we could > already pass t3411.2if we adjusted the todo-list to account for the > extra "pick C1" line. You're right. I was wrong about that. > Slightly off-topic, but I believe the branches will remain intact as > long as the branch commits remain in the same topo-order relative to > each other in the todo-list. If in topo-order, yeah, I guess that is right. > i.e. git will be confused if we try to move a commit from one branch > into the other. Right. If I do `rebase -i -p B1` and in the todo put C1 after F1, I get a fatal message that E1 cannot be cherry picked. Given rebase-i-p's limited ability to reorder graphs, e.g. the error above, my understanding was that, when -p is used, only first-parent changes should be in the todo. This straight line, non-graph list does limit what the user can do, but, AFAIK, the benefit is that rebase-i-p can then actually handle any given reordering of the todo. Letting C1 into the todo would mean having to explain to the user why some of their reorderings worked and others didn't. Or else making rebase-i-p smart enough to handle all cases. Which, IIRC, was something considered unlikely just given the fact that todo is flat and there isn't a way for the user to express topo reorderings. At the time, there was talk of another rewriting tool that would use marks and other hints to handle graphs and it was considered what, if anything, would eventually handle complex rewrites like this. I think that Jeff's use case of rebase-i-p'ing C1, which is not on the first-parent list of commits, should be an error as it delves into territory (topo reordering) that rebase-i-p can't fully handle. (If -p isn't used, just regular rebase, everything is being flattened, so there is no concern of topo reordering, so things are a lot simpler and C1 can/should be in the list.) - Stephen ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH] rebase -i -p: doesn't pick certain merge commits that are children of "upstream" 2011-06-18 15:17 ` Stephen Haberman @ 2011-06-18 16:47 ` Andrew Wong 2011-06-18 17:12 ` Stephen Haberman 0 siblings, 1 reply; 28+ messages in thread From: Andrew Wong @ 2011-06-18 16:47 UTC (permalink / raw) To: Stephen Haberman; +Cc: Junio C Hamano, Andrew Wong, git On 11-06-18 11:17 AM, Stephen Haberman wrote: > Letting C1 into the todo would mean having to explain to the user why > some of their reorderings worked and others didn't. The bug section in rebase's documentation does mention that "attempts to reorder commits tend to produce counterintuitive results", which I think serves as a fairly good warning saying "reorder at your own risk". Also, if we do a "rebase-i-p A1", the C1 branch will appear in the todo list. A while ago I actually ran into this scenario, and I want to squash a commit onto the C1 branch, which I can't if I simply choose B1 as the base. To workaround it, I just made A1 the base so that the C1 branch will appear in the todo for me to squash upon. Otherwise, doing the squash onto C1 manually would've involved several more steps. > I think that Jeff's use case of rebase-i-p'ing C1, which is not on the > first-parent list of commits, should be an error as it delves into > territory (topo reordering) that rebase-i-p can't fully handle. There shouldn't be any topo-reordering unless the user explicitly changes the order of the commit. The user is faced with the same limitations (and bugs) as rebase-i-p'ing D1, so we shouldn't have to handle the C1 case any different. rebase is perfectly capable of handling the D1 case, just as how the C1 case is handled. We're only running into this issue because we're trying to filter out C1 when rebase-i-p'ing B1. ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH] rebase -i -p: doesn't pick certain merge commits that are children of "upstream" 2011-06-18 16:47 ` Andrew Wong @ 2011-06-18 17:12 ` Stephen Haberman 2011-06-18 22:12 ` [PATCH] rebase -i -p: include non-first-parent commits in todo list Andrew Wong 2011-06-18 22:13 ` [PATCH] rebase -i -p: doesn't pick certain merge commits that are children of "upstream" Andrew Wong 0 siblings, 2 replies; 28+ messages in thread From: Stephen Haberman @ 2011-06-18 17:12 UTC (permalink / raw) To: Andrew Wong; +Cc: Junio C Hamano, Andrew Wong, git > The bug section in rebase's documentation does mention that "attempts > to reorder commits tend to produce counterintuitive results", which I > think serves as a fairly good warning saying "reorder at your own > risk". True. > Also, if we do a "rebase-i-p A1", the C1 branch will appear in > the todo list. Hm, good point. > There shouldn't be any topo-reordering unless the user explicitly > changes the order of the commit. The user is faced with the same > limitations (and bugs) as rebase-i-p'ing D1, so we shouldn't have to > handle the C1 case any different. rebase is perfectly capable of > handling the D1 case, just as how the C1 case is handled. We're only > running into this issue because we're trying to filter out C1 when > rebase-i-p'ing B1. Okay, that makes sense. I agree with you then, with the behavior of "rebase-i-p A1" plus the disclaimer in the docs warrants C1 showing up, C1 should be in the todo list for "rebase-i-p B1" as well. ...I can think of cases where personally I'd want to only move around commits on the first-parent line, e.g. even in the case of "rebase-i-p A1", to have less noise (C1 and any others on its branch) in the todo, but at that point it sounds like I'm projecting behavior onto rebase-i-p that isn't actually there. - Stephen ^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH] rebase -i -p: include non-first-parent commits in todo list 2011-06-18 17:12 ` Stephen Haberman @ 2011-06-18 22:12 ` Andrew Wong 2011-06-18 22:13 ` [PATCH] rebase -i -p: doesn't pick certain merge commits that are children of "upstream" Andrew Wong 1 sibling, 0 replies; 28+ messages in thread From: Andrew Wong @ 2011-06-18 22:12 UTC (permalink / raw) To: git; +Cc: Andrew Wong Consider this graph: D---E (topic, HEAD) / / A---B---C (master) \ F (topic2) and the following three commands: 1. git rebase -i -p A 2. git rebase -i -p --onto F A 3. git rebase -i -p B Currently, (1) and (2) will pick B, D, C, and E onto A and F, respectively. However, (3) will only pick D and E onto B, but not C, which is inconsistent with (1) and (2). As a result, we cannot modify C during the interactive-rebase. The current behavior also creates a bug if we do: 4. git rebase -i -p C In (4), E is never picked. And since interactive-rebase resets "HEAD" to "onto" before picking any commits, D and E are lost after the interactive-rebase. This patch fixes the inconsistency and bug by ensuring that all children of upstream are always picked. This essentially reverts the commit: d80d6bc146232d81f1bb4bc58e5d89263fd228d4 When compiling the todo list, commits reachable from "upstream" should never be skipped under any conditions. Otherwise, we lose the ability to modify them like (3), and create a bug like (4). Two of the tests contain a scenario like (3). Since the new behavior added more commits for picking, these tests need to be updated to account for the additional pick lines. A new test has also been added for (4). Signed-off-by: Andrew Wong <andrew.kw.w@gmail.com> --- git-rebase--interactive.sh | 3 +-- t/t3404-rebase-interactive.sh | 2 +- t/t3409-rebase-preserve-merges.sh | 28 +++++++++++++++++++++++++++- t/t3411-rebase-preserve-around-merges.sh | 2 +- 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index 65690af..c6ba7c1 100644 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -713,7 +713,6 @@ then # parents to rewrite and skipping dropped commits would # prematurely end our probe merges_option= - first_after_upstream="$(git rev-list --reverse --first-parent $upstream..$orig_head | head -n 1)" else merges_option="--no-merges --cherry-pick" fi @@ -746,7 +745,7 @@ do preserve=t for p in $(git rev-list --parents -1 $sha1 | cut -d' ' -s -f2-) do - if test -f "$rewritten"/$p -a \( $p != $onto -o $sha1 = $first_after_upstream \) + if test -f "$rewritten"/$p then preserve=f fi diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh index 47c8371..8538813 100755 --- a/t/t3404-rebase-interactive.sh +++ b/t/t3404-rebase-interactive.sh @@ -295,7 +295,7 @@ test_expect_success 'preserve merges with -p' ' ' test_expect_success 'edit ancestor with -p' ' - FAKE_LINES="1 edit 2 3 4" git rebase -i -p HEAD~3 && + FAKE_LINES="1 2 edit 3 4" git rebase -i -p HEAD~3 && echo 2 > unrelated-file && test_tick && git commit -m L2-modified --amend unrelated-file && diff --git a/t/t3409-rebase-preserve-merges.sh b/t/t3409-rebase-preserve-merges.sh index 08201e2..6de4e22 100755 --- a/t/t3409-rebase-preserve-merges.sh +++ b/t/t3409-rebase-preserve-merges.sh @@ -37,7 +37,15 @@ export GIT_AUTHOR_EMAIL # \ # B2 <-- origin/topic # -# In all cases, 'topic' is rebased onto 'origin/topic'. +# Clone 4 (merge using second parent as base): +# +# A1--A2--B3 <-- origin/master +# \ +# B1--A3--M <-- topic +# \ / +# \--A4 <-- topic2 +# \ +# B2 <-- origin/topic test_expect_success 'setup for merge-preserving rebase' \ 'echo First > A && @@ -57,6 +65,13 @@ test_expect_success 'setup for merge-preserving rebase' \ git merge origin/master ) && + git clone ./. clone4 && + ( + cd clone4 && + git checkout -b topic origin/topic && + git merge origin/master + ) && + echo Fifth > B && git add B && git commit -m "Add different B" && @@ -123,4 +138,15 @@ test_expect_success 'rebase -p preserves no-ff merges' ' ) ' +test_expect_success 'rebase -p works when base inside second parent' ' + ( + cd clone4 && + git fetch && + git rebase -p HEAD^2 && + test 1 = $(git rev-list --all --pretty=oneline | grep "Modify A" | wc -l) && + test 1 = $(git rev-list --all --pretty=oneline | grep "Modify B" | wc -l) && + test 1 = $(git rev-list --all --pretty=oneline | grep "Merge remote-tracking branch " | wc -l) + ) +' + test_done diff --git a/t/t3411-rebase-preserve-around-merges.sh b/t/t3411-rebase-preserve-around-merges.sh index 14a23cd..ace8e54 100755 --- a/t/t3411-rebase-preserve-around-merges.sh +++ b/t/t3411-rebase-preserve-around-merges.sh @@ -37,7 +37,7 @@ test_expect_success 'setup' ' # -- C1 -- # test_expect_success 'squash F1 into D1' ' - FAKE_LINES="1 squash 3 2" git rebase -i -p B1 && + FAKE_LINES="1 squash 4 2 3" git rebase -i -p B1 && test "$(git rev-parse HEAD^2)" = "$(git rev-parse C1)" && test "$(git rev-parse HEAD~2)" = "$(git rev-parse B1)" && git tag E2 -- 1.7.2.2 ^ permalink raw reply related [flat|nested] 28+ messages in thread
* Re: [PATCH] rebase -i -p: doesn't pick certain merge commits that are children of "upstream" 2011-06-18 17:12 ` Stephen Haberman 2011-06-18 22:12 ` [PATCH] rebase -i -p: include non-first-parent commits in todo list Andrew Wong @ 2011-06-18 22:13 ` Andrew Wong 1 sibling, 0 replies; 28+ messages in thread From: Andrew Wong @ 2011-06-18 22:13 UTC (permalink / raw) To: Stephen Haberman; +Cc: Junio C Hamano, Andrew Wong, git On 11-06-18 1:12 PM, Stephen Haberman wrote: > ...I can think of cases where personally I'd want to only move > around commits on the first-parent line, e.g. even in the case of > "rebase-i-p A1", to have less noise (C1 and any others on its branch) > in the todo, but at that point it sounds like I'm projecting behavior > onto rebase-i-p that isn't actually there. Yes, it would definitely be useful to be able to do that. In fact, there's a somewhat relevant expect-failure-test t3404.18 that is testing for that. Like you said, we need a way to express topology in the todo list, which I'm not sure what a good representation is. ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [BUG] rebase -p loses commits 2011-05-16 20:36 ` Junio C Hamano 2011-05-17 0:33 ` Andrew Wong @ 2011-05-17 5:39 ` Jeff King 1 sibling, 0 replies; 28+ messages in thread From: Jeff King @ 2011-05-17 5:39 UTC (permalink / raw) To: Junio C Hamano; +Cc: git On Mon, May 16, 2011 at 01:36:51PM -0700, Junio C Hamano wrote: > Hmm, I am confused. You have this: > > F---* feature > / / > B---M master > > and you are at "*". If it were to rebase to linearize, > > B---M---F' > > with F' that has the same the contents as '*', possibly autoresolved by > "am -3" and/or "rerere", should be what you would get. > > But what does it mean to rebase that on top of master, preserving merges > in the first place? You are already on top of 'master' and '*' itself > should be what you should get, no? IOW, shouldn't you already be > up-to-date? To be honest, I am not sure what should happen. How this example came about was that somebody had the same graph, except that a third branch, "origin", also pointed at "B". They were confused why "git rebase --pull" made them re-resolve the same conflict that they had already handled during the merge. The answer, of course, is that rebase is linearizing the commits instead of trying to preserve the shape of history, and that they really wanted "rebase -p". I constructed the simplified example to show that the issue didn't have to do with origin, but rather with linearizing. So it's not a real-world example, in that sense. If it had done one of: 1. Said "you are up to date" and one nothing. 2. Put F' on top of M. 3. Bailed and said "what you're doing is silly". I would probably have shrugged and left it alone. But claiming success and losing F entirely is pretty bad. > I don't use preserve-merge rebase either, but at least when you are > strictly ahead of the target, nothing should happen, I think. > > Perhaps this should be a good start? Aside from how unreadable that shell conditional is getting, I think it's an improvement. -Peff ^ permalink raw reply [flat|nested] 28+ messages in thread
end of thread, other threads:[~2011-06-18 22:16 UTC | newest] Thread overview: 28+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2011-05-16 10:33 [BUG] rebase -p loses commits Jeff King 2011-05-16 19:42 ` Andrew Wong 2011-05-16 20:36 ` Junio C Hamano 2011-05-17 0:33 ` Andrew Wong 2011-05-17 0:54 ` Junio C Hamano 2011-05-17 1:02 ` Junio C Hamano 2011-05-17 5:44 ` Jeff King 2011-05-17 16:07 ` Andrew Wong 2011-05-17 16:12 ` Jeff King 2011-05-21 5:51 ` [RFC] Interactive-rebase doesn't pick all children of "upstream" Andrew Wong 2011-05-21 5:51 ` Andrew Wong 2011-05-21 7:34 ` Andrew Wong 2011-06-05 5:32 ` [PATCH] " Andrew Wong 2011-06-05 9:16 ` Johannes Sixt 2011-06-05 14:11 ` Andrew Wong 2011-06-07 4:08 ` [PATCH v2] rebase -i -p: doesn't pick certain merge commits that are " Andrew Wong 2011-06-07 4:08 ` [PATCH] " Andrew Wong 2011-06-12 16:28 ` Andrew Wong 2011-06-13 16:01 ` Junio C Hamano 2011-06-13 17:30 ` Andrew Wong 2011-06-16 22:24 ` Stephen Haberman 2011-06-18 6:40 ` Andrew Wong 2011-06-18 15:17 ` Stephen Haberman 2011-06-18 16:47 ` Andrew Wong 2011-06-18 17:12 ` Stephen Haberman 2011-06-18 22:12 ` [PATCH] rebase -i -p: include non-first-parent commits in todo list Andrew Wong 2011-06-18 22:13 ` [PATCH] rebase -i -p: doesn't pick certain merge commits that are children of "upstream" Andrew Wong 2011-05-17 5:39 ` [BUG] rebase -p loses commits Jeff King
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).