git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
* Ignoring commits when merging
@ 2010-08-17 20:21 Mike Strauch
  2010-08-18  1:59 ` Jonathan Nieder
  0 siblings, 1 reply; 4+ messages in thread
From: Mike Strauch @ 2010-08-17 20:21 UTC (permalink / raw)
  To: git

Hi,

I'm fairly new to git and I'm trying to figure out the best way to
ignore certain commits when merging one branch into another.  I've
found a solution that suggests that I merge everything before the
commit I want to skip, then merge the commit I want to skip using the
"ours" merging strategy, then merge everything after the skipped
commit.  This sounds like an adequate solution to the problem, but I'm
wondering if there's a better way to do it.

I was thinking that I could just cherry-pick the few commits that I
want from Branch 2 in order to exclude the commit I don't want, but
this will result in there being no link between the branches for those
commits.  Are there any other ways this can be done?

--
Mike Strauch
www.hannonhill.com

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

* Re: Ignoring commits when merging
  2010-08-17 20:21 Ignoring commits when merging Mike Strauch
@ 2010-08-18  1:59 ` Jonathan Nieder
  2010-08-18 18:08   ` Mike Strauch
  0 siblings, 1 reply; 4+ messages in thread
From: Jonathan Nieder @ 2010-08-18  1:59 UTC (permalink / raw)
  To: Mike Strauch; +Cc: git

Hi,

Mike Strauch wrote:

> I'm fairly new to git and I'm trying to figure out the best way to
> ignore certain commits when merging one branch into another.

An interesting question.  The answer (as so often) depends on what
you want to do.  "man 7 gitworkflows" might help.

Below I will pretend you are trying to backport some changes to a more
stable branch; others may chime in with other scenarios.

First, a general hint: when using git and similar systems, it is
generally best if each merged result is somehow "better" than all of
its parents.  I will give an example below of what can go wrong if
this invariant is violated.

Merging to a maintenance branch
-------------------------------

Suppose given a history like this (1):

 o --- v1.0 [maint]
           \
            feature --- feature --- bugfix --- bugfix [master]

Development has been happening on the "master" branch and now you
want to merge back the relevant fixes to make a new point release,
something like the following:

[*] o --- v1.0 ------------------------------------ v1.1 [maint]
              \                                    /
               master-only feature --- bugfix --- M [master]

  "a dangerous history"

Let's consider what that would mean.  Someone builds some new
work off of maint:

 o --- v1.0 --- v1.1 --- o ... o --- A [someone]
               /

What happens when you pull the "someone" branch into master?  The
relevant piece of history looks like this:

        v1.1 --- o ... o --- A [someone]
       /
  ... M --- new development --- ... --- B [master]

When you try to pull A into B, git runs a three-way merge to
apply the changes from the someone branch after the branch point (M)
on the master branch.  In particular, the changes from M to v1.1
are pulled in.  The main change from M to v1.1 is to drop a
bunch of features.  So by pulling from someone, you lose features
on the master branch.

So a merge like [*] that drops desirable changes is generally not
a good idea.

Cherry-picking to a maintenance branch
--------------------------------------

As a result, starting from a history like (1), there is only one
choice: cherry-pick only the bugfixes, so the new features are not
incorporated into the history of the maint branch.

 $ git checkout maint
 $ git cherry-pick bugfix1 bugfix2

Afterwards, it is best to merge the maint branch into master, so
later changes on the maint branch can be merged into master more
easily.  Usually despite the duplicate changes will not result in
conflicts.

 $ git checkout master
 $ git merge maint
 $ git diff HEAD^
 $ : looks good
 $ git push public maint master

If there are conflicts, no need to worry: make sure that "master"
really includes all desirable changes from maint and merge with
strategy ours instead.

 $ git reset --merge
 $ git merge -s ours maint

Writing a new bugfix
--------------------

Suppose you have an idea for a new bugfix.  As discussed above, if
you write it directly on top of master, when it is time to apply
it to maint it will need cherry-picking.  If you base the patch
on maint, you can avoid that:

 $ git checkout -b bugfix maint
 ... hack hack hack ...
 $ make test
 $ : looks good
 $ git checkout master
 $ git merge bugfix
 $ make test
 $ git push public bugfix master

 v1.1 [maint] --- X [bugfix]
     \             \
      o --- ... --- N [master]


Once the patch gets enough testing from users of master, it is
time to apply it to maint.

 $ git checkout maint
 $ git merge bugfix
 $ make test
 $ git checkout master
 $ git merge maint
 $ git push public maint master

 v1.1 ----------- X [maint]
     \             \
      o --- ... --- N [master]

One benefit of this approach is that during development, the patch
is tested against the maintenance branch, which is incidentally
probably where it is most important that it get testing.

Hope that helps,
Jonathan

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

* Re: Ignoring commits when merging
  2010-08-18  1:59 ` Jonathan Nieder
@ 2010-08-18 18:08   ` Mike Strauch
  2010-08-18 23:38     ` Jonathan Nieder
  0 siblings, 1 reply; 4+ messages in thread
From: Mike Strauch @ 2010-08-18 18:08 UTC (permalink / raw)
  To: Jonathan Nieder; +Cc: git

Jonathan,

Thanks for the info.  The scenario that I am in is most like the
"Writing a new bugfix" scenario you've described.  We have a
maintenance branch in which the majority of commits are bug fixes.  We
would then like to merge those bug fixes into our master branch.  The
difference being that there will occasionally be a commit in our
maintenance branch that we do not want to merge into our master branch
because it contains application version information which is only
relevant when we release something out of our maintenance branch.  So,
the tree looks like:

              Release

                   |
       ------- previous bug fixes --------- version info commit----
v1.0 --- bug fix commits --- X [maint]
      /                                     \ M1
--- o --------- ...
-------------------------------------------------------------------- N
[master]
               \                                      /
                -----------...--------- F [new feature branch]

You'll also notice I've included a new feature branch here to give
more of a big picture view of our setup.

So, what happens is this:

1. We fix some bugs in the maint branch (previous bug fixes)
2. We test those fixes
3. Merge the fixes back into master (M1)
4. Commit version information to maint branch (version info commit)
5. Release from maint branch (v1.0)
6. Start the process over from step 1 but with the version information
commit in the maint branch commit history.

So, in this scenario there is really only ever 1 commit that we don't
want to be merged into master from the maint branch.  This is not
always the case of course.  There could be some other commits in maint
branch that we don't want to merge back into master.  Anyway, if you
have any more feedback that would be great.  Either way, I'll have to
take a look at the man page you suggested.

Thanks again,

-Mike

On Tue, Aug 17, 2010 at 9:59 PM, Jonathan Nieder <jrnieder@gmail.com> wrote:
> Hi,
>
> Mike Strauch wrote:
>
>> I'm fairly new to git and I'm trying to figure out the best way to
>> ignore certain commits when merging one branch into another.
>
> An interesting question.  The answer (as so often) depends on what
> you want to do.  "man 7 gitworkflows" might help.
>
> Below I will pretend you are trying to backport some changes to a more
> stable branch; others may chime in with other scenarios.
>
> First, a general hint: when using git and similar systems, it is
> generally best if each merged result is somehow "better" than all of
> its parents.  I will give an example below of what can go wrong if
> this invariant is violated.
>
> Merging to a maintenance branch
> -------------------------------
>
> Suppose given a history like this (1):
>
>  o --- v1.0 [maint]
>           \
>            feature --- feature --- bugfix --- bugfix [master]
>
> Development has been happening on the "master" branch and now you
> want to merge back the relevant fixes to make a new point release,
> something like the following:
>
> [*] o --- v1.0 ------------------------------------ v1.1 [maint]
>              \                                    /
>               master-only feature --- bugfix --- M [master]
>
>  "a dangerous history"
>
> Let's consider what that would mean.  Someone builds some new
> work off of maint:
>
>  o --- v1.0 --- v1.1 --- o ... o --- A [someone]
>               /
>
> What happens when you pull the "someone" branch into master?  The
> relevant piece of history looks like this:
>
>        v1.1 --- o ... o --- A [someone]
>       /
>  ... M --- new development --- ... --- B [master]
>
> When you try to pull A into B, git runs a three-way merge to
> apply the changes from the someone branch after the branch point (M)
> on the master branch.  In particular, the changes from M to v1.1
> are pulled in.  The main change from M to v1.1 is to drop a
> bunch of features.  So by pulling from someone, you lose features
> on the master branch.
>
> So a merge like [*] that drops desirable changes is generally not
> a good idea.
>
> Cherry-picking to a maintenance branch
> --------------------------------------
>
> As a result, starting from a history like (1), there is only one
> choice: cherry-pick only the bugfixes, so the new features are not
> incorporated into the history of the maint branch.
>
>  $ git checkout maint
>  $ git cherry-pick bugfix1 bugfix2
>
> Afterwards, it is best to merge the maint branch into master, so
> later changes on the maint branch can be merged into master more
> easily.  Usually despite the duplicate changes will not result in
> conflicts.
>
>  $ git checkout master
>  $ git merge maint
>  $ git diff HEAD^
>  $ : looks good
>  $ git push public maint master
>
> If there are conflicts, no need to worry: make sure that "master"
> really includes all desirable changes from maint and merge with
> strategy ours instead.
>
>  $ git reset --merge
>  $ git merge -s ours maint
>
> Writing a new bugfix
> --------------------
>
> Suppose you have an idea for a new bugfix.  As discussed above, if
> you write it directly on top of master, when it is time to apply
> it to maint it will need cherry-picking.  If you base the patch
> on maint, you can avoid that:
>
>  $ git checkout -b bugfix maint
>  ... hack hack hack ...
>  $ make test
>  $ : looks good
>  $ git checkout master
>  $ git merge bugfix
>  $ make test
>  $ git push public bugfix master
>
>  v1.1 [maint] --- X [bugfix]
>     \             \
>      o --- ... --- N [master]
>
>
> Once the patch gets enough testing from users of master, it is
> time to apply it to maint.
>
>  $ git checkout maint
>  $ git merge bugfix
>  $ make test
>  $ git checkout master
>  $ git merge maint
>  $ git push public maint master
>
>  v1.1 ----------- X [maint]
>     \             \
>      o --- ... --- N [master]
>
> One benefit of this approach is that during development, the patch
> is tested against the maintenance branch, which is incidentally
> probably where it is most important that it get testing.
>
> Hope that helps,
> Jonathan
>

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

* Re: Ignoring commits when merging
  2010-08-18 18:08   ` Mike Strauch
@ 2010-08-18 23:38     ` Jonathan Nieder
  0 siblings, 0 replies; 4+ messages in thread
From: Jonathan Nieder @ 2010-08-18 23:38 UTC (permalink / raw)
  To: Mike Strauch; +Cc: git

Mike Strauch wrote:

> We have a
> maintenance branch in which the majority of commits are bug fixes.  We
> would then like to merge those bug fixes into our master branch.  The
> difference being that there will occasionally be a commit in our
> maintenance branch that we do not want to merge into our master branch
> because it contains application version information

I see.  Yes, this is interesting (and not covered by gitworkflows though
it should be).

> 1. We fix some bugs in the maint branch (previous bug fixes)
> 2. We test those fixes
> 3. Merge the fixes back into master (M1)
> 4. Commit version information to maint branch (version info commit)
> 5. Release from maint branch (v1.0)
> 6. Start the process over from step 1 but with the version information
> commit in the maint branch commit history.
> 
> So, in this scenario there is really only ever 1 commit that we don't
> want to be merged into master from the maint branch.

Now your "merge -s ours" solution is looking like a good one.  So the
workflow on the release day might look like this:

 1. Fix some bugs on maint
 2. Test them
 3. Merge fixes back into master
 4. Version info commit on the maint branch
 5. Test a little more, tag the release.
 6. "merge -s ours" from maint to master
 7. Do some other work on topic branches and master.
 8. Push.

Ideally, in the published history, maint would _always_ be an ancestor
of master.  Life is simpler that way.

> This is not
> always the case of course.  There could be some other commits in maint
> branch that we don't want to merge back into master.

Sure.  Another possibility is to explicitly revert patches from maint,
either as a separate commit or as part of a merge.  The latter would
generally not make sense unless it is a very small patch.

For example:

 1. Fix some bugs on maint.
 2. Notice a test on maint is passing now.  Mark it as such.
 3. ... more work on maint ...
 4. Switch to master.  Merge from maint.  Since the test is
    failing:
    i.   "git reset --keep HEAD^" (undo the merge)
    ii.  "git merge --no-commit maint"
    iii. Mark the test as expected to fail.
    iv.  "git commit", mentioning the test failure in the log message.

This is called an "evil merge" because it introduces changes that
are not from either side.  Used judiciously it can be useful for
ensuring that every commit passes some basic tests (which might be
wanted if you use "git bisect" heavily).

"git revert" is simpler, of course.

Hope that helps.
Jonathan

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

end of thread, other threads:[~2010-08-18 23:39 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-08-17 20:21 Ignoring commits when merging Mike Strauch
2010-08-18  1:59 ` Jonathan Nieder
2010-08-18 18:08   ` Mike Strauch
2010-08-18 23:38     ` Jonathan Nieder

Code repositories for project(s) associated with this public inbox

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

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).