All of lore.kernel.org
 help / color / mirror / Atom feed
* How to merge from newer branch to older branches?
@ 2009-04-21 19:24 skillzero
  2009-04-21 19:36 ` Jeff King
  2009-04-22  4:46 ` John M. Dlugosz
  0 siblings, 2 replies; 9+ messages in thread
From: skillzero @ 2009-04-21 19:24 UTC (permalink / raw)
  To: git

I'm not sure how to merge a topic branch onto multiple branches
without conflicts or unwanted changes. My workflow probably isn't
ideal, but I often run into cases like this and wondered if there's a
way to solve it with git.

We have a "future" branch for new development. I wanted to add a new
feature so I branched from "future" to create a branch named
"feature". I completed the work, but now they've decided they need
that on two older branches. It looks like this:

future o-o-o-o-o-o-o
        \   \       \
1.2          o-o-o   \
          \           \
1.1        o-o-o       \
                        \
feature                  o-o-o
                         a b c

I've read I should have started from the oldest common denominator
branch that I might ever want, but at the time I created the branch,
"future" was it because we thought 1.1 and 1.2 were locked down and
not going to add new features. But things change and I need to roll my
feature into 1.1 and 1.2.

What's the best way to merge changes on the "feature" branch into the
1.1 and 1.2 branches? If I try to checkout 1.1 then 'git merge
feature', it (obviously) merges in a bunch of changes from "future"
that I don't want (and has a bunch of conflicts). I also tried just
cherry-picking commits a, b, and c from the "feature" branch. That
mostly worked, but there were some surprising conflicts. Text that had
been inserted into a couple files (which had been untouched in 1.1 and
1.2) caused a conflict when it seemed like it should have just added
the lines without conflict. I realize it's difficult to know why it
conflicted without seeing the changes, but I'm just asking in a more
general way how I should be doing merges like this.

Is cherry-picking the best way? It seems pretty tedious because I have
to do each commit in reverse order (although I could script it). I was
hoping there was a way to say "apply all the changes from when
"feature" branched up to its tip". I would also like to preserve the
commit history rather than just making making one big commit of all
the changes.

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

* Re: How to merge from newer branch to older branches?
  2009-04-21 19:24 How to merge from newer branch to older branches? skillzero
@ 2009-04-21 19:36 ` Jeff King
  2009-04-22  5:13   ` Junio C Hamano
  2009-04-22  4:46 ` John M. Dlugosz
  1 sibling, 1 reply; 9+ messages in thread
From: Jeff King @ 2009-04-21 19:36 UTC (permalink / raw)
  To: skillzero; +Cc: git

On Tue, Apr 21, 2009 at 12:24:26PM -0700, skillzero@gmail.com wrote:

> We have a "future" branch for new development. I wanted to add a new
> feature so I branched from "future" to create a branch named
> "feature". I completed the work, but now they've decided they need
> that on two older branches. It looks like this:
> [...]
> I've read I should have started from the oldest common denominator
> branch that I might ever want, but at the time I created the branch,

Right. Junio branches all topics in git from "master", but then merges
promising ones into "next". When the topic is ready for master, he can
then just merge the topic branch, pulling in that topic but not the rest
of next.

> "future" was it because we thought 1.1 and 1.2 were locked down and
> not going to add new features. But things change and I need to roll my
> feature into 1.1 and 1.2.

Yep. Advanced planning is not always possible. :)

> What's the best way to merge changes on the "feature" branch into the
> 1.1 and 1.2 branches? If I try to checkout 1.1 then 'git merge

If it is just one or two commits, you probably just want to "git
cherry-pick" them onto your older branches.

If it is a larger series, or one which might see more development, you
might want to use "git rebase" to rewrite the branch as if it were based
on 1.1, and then merge that into 1.1, 1.2, and feature.

> feature', it (obviously) merges in a bunch of changes from "future"
> that I don't want (and has a bunch of conflicts). I also tried just
> cherry-picking commits a, b, and c from the "feature" branch. That
> mostly worked, but there were some surprising conflicts. Text that had
> been inserted into a couple files (which had been untouched in 1.1 and
> 1.2) caused a conflict when it seemed like it should have just added
> the lines without conflict. I realize it's difficult to know why it
> conflicted without seeing the changes, but I'm just asking in a more
> general way how I should be doing merges like this.

Yes, without seeing the conflicts it's hard to say. But presumably the
added lines provide context, so git was having a hard time trying to
figure out where in the old file your changes would go (you can think of
cherry-pick as the moral equivalent of "git show $COMMIT | git apply";
so you are basically applying a patch on top of something that it is
not quite the same).

If you are having trouble resolving conflicts, you might try using "git
mergetool" to fire up an external resolver that may provide a richer
interface (I use xxdiff with mergetool, myself).

> Is cherry-picking the best way? It seems pretty tedious because I have
> to do each commit in reverse order (although I could script it). I was
> hoping there was a way to say "apply all the changes from when
> "feature" branched up to its tip". I would also like to preserve the
> commit history rather than just making making one big commit of all
> the changes.

In its simplest form, rebase is basically cherry-picking a series of
commits. So that would make things a bit less tedious for you, but you
will probably still end up with the same set of conflicts.

-Peff

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

* Re: How to merge from newer branch to older branches?
  2009-04-21 19:24 How to merge from newer branch to older branches? skillzero
  2009-04-21 19:36 ` Jeff King
@ 2009-04-22  4:46 ` John M. Dlugosz
  1 sibling, 0 replies; 9+ messages in thread
From: John M. Dlugosz @ 2009-04-22  4:46 UTC (permalink / raw)
  To: git

skillzero@gmail.com wrote:
> 
> future o-o-o-o-o-o-o
>         \   \       \
> 1.2          o-o-o   \
>           \           \
> 1.1        o-o-o       \
>                         \
> feature                  o-o-o
>                          a b c
> 
> Is cherry-picking the best way? It seems pretty tedious because I have
> to do each commit in reverse order (although I could script it). I was
> hoping there was a way to say "apply all the changes from when
> "feature" branched up to its tip". I would also like to preserve the
> commit history rather than just making making one big commit of all
> the changes.

I think any other command that "just" moves those deltas would be the exact equivalent of 
cherry picking in terms of the conflicts it sees.

You can, however, collapse a,b,c into one commit and pick that into each place it is 
needed, calling it a good comment for the whole feature.  That might be more difficult to 
merge than doing one at a time, but if not, and the whole string is tedious (i.e. 50 
instead of 3, mostly incremental typo fixes and stuff) I can see the benefit of making it 
a gestalt.

Furthermore... (thinking out loud)... the conflicts are caused by things after the other 
branches, right?  After you do the resolving for (say) 1.2, then instead of picking a,b,c 
again (with the same merge issues), pick the one you put onto 1.2 already.  Work your way 
that way from branches that came off nearest to the original feature, back down toward the 
  root.  Each time pick the one you just planted, because it already contains merges part 
way down.  Then you only have to deal with the changes between those two branches.

So that's what I'd do:  pick a, b, and c onto the most recient other branch (1.2 in your 
drawing), fixing merge as I go.  Then reset mixed back to the original tip before the 
picks, and commit everything with a good name for the feature.  Then, pick that onto the 
next newest branch (1,1 in your drawing), etc.

--John

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

* Re: How to merge from newer branch to older branches?
  2009-04-21 19:36 ` Jeff King
@ 2009-04-22  5:13   ` Junio C Hamano
  2009-04-22 13:34     ` Jeff King
  2009-04-22 17:44     ` skillzero
  0 siblings, 2 replies; 9+ messages in thread
From: Junio C Hamano @ 2009-04-22  5:13 UTC (permalink / raw)
  To: Jeff King; +Cc: skillzero, git

Jeff King <peff@peff.net> writes:

> On Tue, Apr 21, 2009 at 12:24:26PM -0700, skillzero@gmail.com wrote:
>
>> We have a "future" branch for new development. I wanted to add a new
>> feature so I branched from "future" to create a branch named
>> "feature". I completed the work, but now they've decided they need
>> that on two older branches. It looks like this:
>> [...]
>> I've read I should have started from the oldest common denominator
>> branch that I might ever want, but at the time I created the branch,
>
> Right. Junio branches all topics in git from "master", but then merges
> promising ones into "next". When the topic is ready for master, he can
> then just merge the topic branch, pulling in that topic but not the rest
> of next.

No, I don't.

I often branch from somewhere older than 'master', often tip from 'maint'
sometimes even older.  And that is not necessarily because I am better
than other people in planning ahead.  There are branches that forked from
older series (like 1.6.0.X) that are merged to next and then master,
without ever getting merged to produce 1.6.0.(X+1).

> Yep. Advanced planning is not always possible. :)
>
>> What's the best way to merge changes on the "feature" branch into the
>> 1.1 and 1.2 branches? If I try to checkout 1.1 then 'git merge
>
> If it is just one or two commits, you probably just want to "git
> cherry-pick" them onto your older branches.

I would suggest against it.  Just like you did not manage to plan in
advance for these two patches to fork from older branches, you will regret
later that these two were still full of bugs and need to be fixed up with
more patches on top.  Even if they are only a few right now, keeping a
cleaned up series that applies to the oldest would be easier to manage in
the long run.

I.e., rebase feature onto somewhere older (like 1.1):

	git rebase --onto v1.1 feature~2 feature

then let q&a people merge that to v1.1 and v1.2, while keeping the tip of
the feature still open.  You will queue further fix-ups on that branch and
tell q&a people to merge again to get the fixed to the feature.

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

* Re: How to merge from newer branch to older branches?
  2009-04-22  5:13   ` Junio C Hamano
@ 2009-04-22 13:34     ` Jeff King
  2009-04-22 17:44     ` skillzero
  1 sibling, 0 replies; 9+ messages in thread
From: Jeff King @ 2009-04-22 13:34 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: skillzero, git

On Tue, Apr 21, 2009 at 10:13:23PM -0700, Junio C Hamano wrote:

> > Right. Junio branches all topics in git from "master", but then merges
> > promising ones into "next". When the topic is ready for master, he can
> > then just merge the topic branch, pulling in that topic but not the rest
> > of next.
> 
> No, I don't.
> 
> I often branch from somewhere older than 'master', often tip from 'maint'
> sometimes even older.  And that is not necessarily because I am better
> than other people in planning ahead.  There are branches that forked from
> older series (like 1.6.0.X) that are merged to next and then master,
> without ever getting merged to produce 1.6.0.(X+1).

Sure, but I assumed a new topic that came in would get branched from
'master', since that is presumably what the person developing it based
it on. Is that not true?

That is also what is in Documentation/howto/maintain-git.txt:

   - Anything unobvious that is applicable to 'master' (in other
     words, does not depend on anything that is still in 'next'
     and not in 'master') is applied to a new topic branch that
     is forked from the tip of 'master'.  This includes both
     enhancements and unobvious fixes to 'master'.  A topic
     branch is named as ai/topic where "ai" is typically
     author's initial and "topic" is a descriptive name of the
     topic (in other words, "what's the series is about").

   - An unobvious fix meant for 'maint' is applied to a new
     topic branch that is forked from the tip of 'maint'.  The
     topic is named as ai/maint-topic.

-Peff

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

* Re: How to merge from newer branch to older branches?
  2009-04-22  5:13   ` Junio C Hamano
  2009-04-22 13:34     ` Jeff King
@ 2009-04-22 17:44     ` skillzero
  2009-04-22 20:15       ` Jeff King
  1 sibling, 1 reply; 9+ messages in thread
From: skillzero @ 2009-04-22 17:44 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Jeff King, git

Based on the help from this discussion and the rebase documenation,
what I ended up doing that seemed to require the fewest conflicts is
to use git merge-base to find the merge-base of master and 1.1 (the
oldest branch I wanted the feature) then I found the commit
immediately before the first commit on the feature branch (feature~3
in this case or commit a^). Then I rebased feature:

git rebase --onto merge-base-of-1.1-and-master feature-before-first feature

Turning this (hopefully not mangled by gmail):

future o-o-o-o-o-o-o
        \   \       \
1.2      \   o-o-o   \
          \           \
1.1        o-o-o       \
                        \
feature                  o-o-o
                         a b c

into this:

future  o-o-o-o-o-o-o
        |\   \
1.2     | \   o-o-o
        |  \
1.1     |   o-o-o
         \
feature   o-o-o
          a b c

I can merge 'feature' into 1.1, 1.2, etc. then theoretically, I should
be able to apply bug fixes to 'feature' later and re-merge into 1.1,
1.2, etc. to pick up just the bug fixes, right?

When I merged 'feature' into 'v1.1', it resulted in the same conflicts
I originally ran into when cherry-picking, but when I looked at it, I
realized this was because I had manually integrated some changes into
1.1 from master (I'm trying to move our tree from CVS to git so I had
to manually move in some stuff from CVS instead of doing a proper git
merge).

It seems like this merge conflict is going to cause problems if I try
to re-merge to pick up a bug fix to 'feature' in the future. Looking
at the documentation, it sounds like 'git rerere' can help by
re-applying the manual conflict resolution I did?

BTW...Is there a way to find the first commit of a branch? I had to
just look at the log manually to see what I remembered to be the first
commit.

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

* Re: How to merge from newer branch to older branches?
  2009-04-22 17:44     ` skillzero
@ 2009-04-22 20:15       ` Jeff King
  2009-04-22 21:01         ` skillzero
  0 siblings, 1 reply; 9+ messages in thread
From: Jeff King @ 2009-04-22 20:15 UTC (permalink / raw)
  To: skillzero; +Cc: Junio C Hamano, git

On Wed, Apr 22, 2009 at 10:44:06AM -0700, skillzero@gmail.com wrote:

> Then I rebased feature:
> 
> git rebase --onto merge-base-of-1.1-and-master feature-before-first feature
>
> [...]
> 
> I can merge 'feature' into 1.1, 1.2, etc. then theoretically, I should
> be able to apply bug fixes to 'feature' later and re-merge into 1.1,
> 1.2, etc. to pick up just the bug fixes, right?

Exactly.

> It seems like this merge conflict is going to cause problems if I try
> to re-merge to pick up a bug fix to 'feature' in the future. Looking
> at the documentation, it sounds like 'git rerere' can help by
> re-applying the manual conflict resolution I did?

It shouldn't. Remember that your merges into 1.1, 1.2, etc, created a
new merge base. So when you have bug fixes to make to this topic, you
don't put them on the "feature" branch, but onto this topic branch. Then
you re-merge the topic branch into 1.1., 1.2, feature, etc, and it will
look only at the new bugfix.

> BTW...Is there a way to find the first commit of a branch? I had to
> just look at the log manually to see what I remembered to be the first
> commit.

Sort of. Branches in git are really just refs pointing to the tip of the
branch. So once the tip advances, the ref no longer remembers "oh, this
is where I branched from". If there are other commits along the thing
you branched off of, you can calculate the merge-base of the thing you
branched from, and your branch.

However, the first entry in the reflog for that branch ref should
indicate branch creation (unless it is so old that it has expired).
Something like:

  $ git branch foo origin/master
  $ git reflog show foo
  0be9bc0 foo@{0}: branch: Created from origin/master

So 0be9bc0 would be the creation point in this case. But you might have
totally rewritten the branch after that point, so 0be9bc0 might not be a
useful value anymore.

-Peff

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

* Re: How to merge from newer branch to older branches?
  2009-04-22 20:15       ` Jeff King
@ 2009-04-22 21:01         ` skillzero
  2009-04-22 21:17           ` Jeff King
  0 siblings, 1 reply; 9+ messages in thread
From: skillzero @ 2009-04-22 21:01 UTC (permalink / raw)
  To: Jeff King; +Cc: Junio C Hamano, git

On Wed, Apr 22, 2009 at 1:15 PM, Jeff King <peff@peff.net> wrote:
>
> On Wed, Apr 22, 2009 at 10:44:06AM -0700, skillzero@gmail.com wrote:
>
>> It seems like this merge conflict is going to cause problems if I try
>> to re-merge to pick up a bug fix to 'feature' in the future. Looking
>> at the documentation, it sounds like 'git rerere' can help by
>> re-applying the manual conflict resolution I did?
>
> It shouldn't. Remember that your merges into 1.1, 1.2, etc, created a
> new merge base. So when you have bug fixes to make to this topic, you
> don't put them on the "feature" branch, but onto this topic branch. Then
> you re-merge the topic branch into 1.1., 1.2, feature, etc, and it will
> look only at the new bugfix.

I'm not sure I understand. When I did the original rebase of "feature"
onto the merge-base of all the branches I wanted to merge to (v1.1 and
v1.2 in this case), the end result was that "feature" is now based on
the merge-base. When I merged "feature" into 1.1, I had to fix some
conflicts so in the log I see my conflict fix commit then a merge
commit, but "feature" wasn't changed (only v1.1 was).

I was thinking that if I find a bug in my original "feature" branch, I
would commit the fix to the "feature" branch then merge that into
v1.1, v1.2, master, etc. But I was thinking that when I tried to merge
"feature" into v1.1 (which previously needed a commit to fix
conflicts), I'd need to re-fix those same conflicts.

When I look at the log for v1.1 though, maybe I just misunderstood the
way the conflicts are resolved in git? I was thinking the conflicting
merge would end up as one big commit that's a combination of the
"feature"'s commits and my conflict fixes.

But in the log for v1.1, it looks like my conflict fixing commit may
have just "fixed" the v1.1 branch such that it could accept a
"feature" merge without conflict. If that's true then a future merge
of "feature" containing new commits should just merge without
conflict. That would be awesome if that's the way conflicting
resolving in git works.

> However, the first entry in the reflog for that branch ref should
> indicate branch creation (unless it is so old that it has expired).
> Something like:
>
>  $ git branch foo origin/master
>  $ git reflog show foo
>  0be9bc0 foo@{0}: branch: Created from origin/master
>
> So 0be9bc0 would be the creation point in this case. But you might have
> totally rewritten the branch after that point, so 0be9bc0 might not be a
> useful value anymore.

Thanks, that shows what I need. For my case, it happened to be @{58}.
Is there a way to reverse the order or a special syntax to say "0 from
the end"? Trying --reverse said "cannot combine --reverse with
--walk-reflogs" and trying @{-1} show me something else.

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

* Re: How to merge from newer branch to older branches?
  2009-04-22 21:01         ` skillzero
@ 2009-04-22 21:17           ` Jeff King
  0 siblings, 0 replies; 9+ messages in thread
From: Jeff King @ 2009-04-22 21:17 UTC (permalink / raw)
  To: skillzero; +Cc: Junio C Hamano, git

On Wed, Apr 22, 2009 at 02:01:01PM -0700, skillzero@gmail.com wrote:

> I'm not sure I understand. When I did the original rebase of "feature"
> onto the merge-base of all the branches I wanted to merge to (v1.1 and
> v1.2 in this case), the end result was that "feature" is now based on

Err, sorry, I was confusing your "future" branch and your "feature"
branch. Wherever I said "feature", I meant "future", and "topic" I meant
"feature". Yikes.

So you would make bug-fixes on "feature", and then just re-merge it to
1.1, 1.2, and future.

> the merge-base. When I merged "feature" into 1.1, I had to fix some
> conflicts so in the log I see my conflict fix commit then a merge
> commit, but "feature" wasn't changed (only v1.1 was).

Right. So now the merge-base between feature and 1.1 is the new merge
commit. And when you re-merge them, you will only look at things that
happened on the feature branch since that merge-base.

> I was thinking that if I find a bug in my original "feature" branch, I
> would commit the fix to the "feature" branch then merge that into
> v1.1, v1.2, master, etc. But I was thinking that when I tried to merge
> "feature" into v1.1 (which previously needed a commit to fix
> conflicts), I'd need to re-fix those same conflicts.

Nope, because the merge commit already records the state of the tree
once those conflicts are fixed. Now it's possible that the _bugfix_ may
have its own conflicts. But you shouldn't see the same conflicts again.

> When I look at the log for v1.1 though, maybe I just misunderstood the
> way the conflicts are resolved in git? I was thinking the conflicting
> merge would end up as one big commit that's a combination of the
> "feature"'s commits and my conflict fixes.

Sort of.  It is a new commit with two parents: the previous tip of v1.1,
and the tip of "feature". But its tree contains the state with all of
v1.1, all of feature's commits, and your fixes.

-Peff

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

end of thread, other threads:[~2009-04-22 21:18 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-04-21 19:24 How to merge from newer branch to older branches? skillzero
2009-04-21 19:36 ` Jeff King
2009-04-22  5:13   ` Junio C Hamano
2009-04-22 13:34     ` Jeff King
2009-04-22 17:44     ` skillzero
2009-04-22 20:15       ` Jeff King
2009-04-22 21:01         ` skillzero
2009-04-22 21:17           ` Jeff King
2009-04-22  4:46 ` John M. Dlugosz

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.