git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* Is there a way to exclude user-specified files or directories from  participating in merges?
@ 2009-02-18  0:49 Brent Goodrick
  2009-02-18  1:05 ` Junio C Hamano
  0 siblings, 1 reply; 9+ messages in thread
From: Brent Goodrick @ 2009-02-18  0:49 UTC (permalink / raw)
  To: git

Suppose I create a git repo called central.git on a machine I will
call "central". In that central.git repo, I put these files:

  work.sh
  home.sh
  generic.sh

When I clone the central.git repo on to a different machine I will
call "work", I want this fileset to be pulled:

  work.sh
  generic.sh

But not the home.sh file.

Similarly, when I clone the central.git repo on a machine I will call
"home", I want this fileset to be pulled:

  home.sh
  generic.sh

But not the work.sh file.

What I think I need are two branches, one called "home_branch" and
"work_branch", but read on for the twist:

Say I'm working on editing the work machines fileset on the work repo
I had cloned originally from central.git, and commit a change to both
generic.sh and work.sh.  I do a git-push to an appropriate remote
branch I have set up on the central.git repo, so that I can do a
git-merge type of integration on the central machine in the
central.git repo into the other branches (i.e., into the home_branch),
specifically so that the home_branch gets updated with the change to
the generic.sh file.  However, I want the home_branch to be updated
with the change I made to generic.sh, but I don't ever want the
work.sh to show up in the home_branch that would occur during a normal
merge. Likewise, I would not ever want the home.sh file to participate
in merges from the work fileset back over to the home fileset (and
likewise I would not ever want to see the work.sh file show up on the
home_branch).

The above should apply for all files certain special directories. For
instance, if I were to have a work_files directory and a home_files
directory, then the the work_files is for the "work" machine (and
work_branch) and the "home_files" is for the "home" machine (and
home_branch).

How do I mark certain files and/or directories (via relative file
paths or with file globbing) on certain branches to be excluded from
being merged into all other (or a specified list of) branches?
Ideally I would want to only have to add some logic to the .git/config
file in the central.git repo that specifies the exclusions/exceptions,
and not have to remember to make corresponding changes into any of the
other repos that are cloned from it. Is this possible?

Also, is there a way to avoid the home.sh file from ever being added
to any file underneath the .git directory of the repo I cloned on the
"work" machine. I would not want there to be any risk that anyone with
network access to the "work" machine (say, a sysadmin with sufficient
privileges) to be able to see any form of the home.sh file since it
exists on some branch in the .git directory.

Thanks,
Brent

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

* Re: Is there a way to exclude user-specified files or directories from  participating in merges?
  2009-02-18  0:49 Is there a way to exclude user-specified files or directories from participating in merges? Brent Goodrick
@ 2009-02-18  1:05 ` Junio C Hamano
  2009-02-18  1:32   ` Brent Goodrick
                     ` (2 more replies)
  0 siblings, 3 replies; 9+ messages in thread
From: Junio C Hamano @ 2009-02-18  1:05 UTC (permalink / raw)
  To: Brent Goodrick; +Cc: git

Brent Goodrick <bgoodr@gmail.com> writes:

> Suppose I create a git repo called central.git on a machine I will
> call "central". In that central.git repo, I put these files:
>
>   work.sh
>   home.sh
>   generic.sh
>
> When I clone the central.git repo on to a different machine I will
> call "work", I want this fileset to be pulled:
>
>   work.sh
>   generic.sh
>
> But not the home.sh file.

You would have one common branch and one branch per deployment.

A common branch would host only common files (e.g. generic.sh file from
your example).  Per deployment branch, e.g. home, would branch from the
common branch (so it starts with some version of generic.sh) and may add
its own private files (e.g. home.sh).

And stick to the following two rules:

 - You make edits to common files only on the common branch.
 - You merge from common to deployment, never the other way.

So at work, you would have a checkout of your work "deployment branch",
and find needs to change things.  It is Ok to edit both work.sh and
generic.sh (without being able to edit both, it would be hard to verify if
the changes would work together) at this time, but don't commit the result
in the work branch.

Save the changes to work.sh away (e.g. "git diff work.sh >P.diff" and then
"git checkout HEAD work.sh"), switch to the common branch, and commit the
changes to the generic file.  Switch back to the deployment branch, merge
the common branch (to pick up the changes to home.sh), reapply the changes
specific to the deployment you saved earlier (e.g. "git apply P.diff"),
tne commit the result.

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

* Re: Is there a way to exclude user-specified files or directories  from participating in merges?
  2009-02-18  1:05 ` Junio C Hamano
@ 2009-02-18  1:32   ` Brent Goodrick
  2009-02-18  1:58     ` Junio C Hamano
  2009-02-18  1:36   ` Junio C Hamano
  2009-02-18 13:33   ` Sitaram Chamarty
  2 siblings, 1 reply; 9+ messages in thread
From: Brent Goodrick @ 2009-02-18  1:32 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

On Tue, Feb 17, 2009 at 5:05 PM, Junio C Hamano <gitster@pobox.com> wrote:
> So at work, you would have a checkout of your work "deployment branch",
> and find needs to change things.  It is Ok to edit both work.sh and
> generic.sh (without being able to edit both, it would be hard to verify if
> the changes would work together) at this time, but don't commit the result
> in the work branch.
>
> Save the changes to work.sh away (e.g. "git diff work.sh >P.diff" and then
> "git checkout HEAD work.sh"), switch to the common branch, and commit the
> changes to the generic file.  Switch back to the deployment branch, merge
> the common branch (to pick up the changes to home.sh), reapply the changes
> specific to the deployment you saved earlier (e.g. "git apply P.diff"),
> tne commit the result.
>

Thanks. Well, I should have said in my initial request: "Without
manually forwarding changes from branch to branch and without having
to remember special rules about what I can and cannot merge into which
branch", since that is likely to get forgotten. :)

The answer I am hearing you say is that git doesn't have a way to
automatically exclude files akin to how rsync handles include/exclude.
 Is that what you are saying? Or, could the hook mechanism be
exploited to get this behavior?

bg

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

* Re: Is there a way to exclude user-specified files or directories from  participating in merges?
  2009-02-18  1:05 ` Junio C Hamano
  2009-02-18  1:32   ` Brent Goodrick
@ 2009-02-18  1:36   ` Junio C Hamano
  2009-02-18 13:33   ` Sitaram Chamarty
  2 siblings, 0 replies; 9+ messages in thread
From: Junio C Hamano @ 2009-02-18  1:36 UTC (permalink / raw)
  To: Brent Goodrick; +Cc: git

Junio C Hamano <gitster@pobox.com> writes:

> Brent Goodrick <bgoodr@gmail.com> writes:
>
>> Suppose I create a git repo called central.git on a machine I will
>> call "central". In that central.git repo, I put these files:
>>
>>   work.sh
>>   home.sh
>>   generic.sh
>>
>> When I clone the central.git repo on to a different machine I will
>> call "work", I want this fileset to be pulled:
>>
>>   work.sh
>>   generic.sh
>>
>> But not the home.sh file.
>
> You would have one common branch and one branch per deployment.
>
> A common branch would host only common files (e.g. generic.sh file from
> your example).  Per deployment branch, e.g. home, would branch from the
> common branch (so it starts with some version of generic.sh) and may add
> its own private files (e.g. home.sh).
>
> And stick to the following two rules:
>
>  - You make edits to common files only on the common branch.
>  - You merge from common to deployment, never the other way.
>
> So at work, you would have a checkout of your work "deployment branch",
> and find needs to change things.  It is Ok to edit both work.sh and
> generic.sh (without being able to edit both, it would be hard to verify if
> the changes would work together) at this time, but don't commit the result
> in the work branch.
>
> Save the changes to work.sh away (e.g. "git diff work.sh >P.diff" and then
> "git checkout HEAD work.sh"), switch to the common branch, and commit the
> changes to the generic file.  Switch back to the deployment branch, merge
> the common branch (to pick up the changes to home.sh), reapply the changes
> specific to the deployment you saved earlier (e.g. "git apply P.diff"),
> tne commit the result.

By the way, earlier in a different thread, somebody wondered if being able
to make a commit on detached HEAD is a good thing, and this is a good
example of why it is convenient.  When I use the above "one-way merge,
never merging back" workflow in real life [*1*], I do not use a simple
"git diff work.sh >P.diff && git checkout HEAD work.sh" to save away the
tentative changes I made and verified on the deployment branch.  The above
is an oversimplified example.

For one, in real life projects, there are many files that are specific to
individual deployments, and for another, distinction between generic vs
deployment specific changes does not cleanly appear at file boundaries.

Instead, I would make a real commit, with full intention of discarding it
later.

                  T work
                 /
         ...o---o
               / 
       ...o---o common

I would go back to common branch ("git checkout common"), cherry-pick the
commit from the deployment branch ("git cherry-pick --no-commit work").
This would conflict heavily because the common branch does not have any
changes specific to the deployment branch (i.e. files that were modified
since the deployment forked from the generic, and files the deployment
added).  That is Ok.  I would use "git add -i" and editor to sift out the
deployment specific parts to discard, and commit only the parts of the
change T made that are truly generic:

                  T work
                 /
         ...o---o
               / 
       ...o---o---A common

Then I would go back to the deployment, and detach the head at commit
before the tentative commit T.  From here, I can merge the common branch
in.

                  T work
                 /
         ...o---o HEAD
               / 
       ...o---o---A common

"git merge common" would make something like this:

                  T work
                 /
         ...o---o---B HEAD
               /   /
       ...o---o---A common

I know that the remainder of the change (i.e. difference between B and T)
should be the parts that are specific to the deployment.  After reading
through the output of "git diff HEAD work" to make sure that it has all
deployment specific changes I made (and nothing that should have been in
the commit A on common), I would:

	git read-tree -m -u work && git commit

to grow the history of detached HEAD.

                  T work
                 /
         ...o---o---B---C HEAD
               /   /
       ...o---o---A common

After this step, just wrap it up with:

	git branch -f work && git checkout work

to reach the final state:

                  T
                 /
         ...o---o---B---C work
               /   /
       ...o---o---A common

The tentative commit T becomes dangling but we know "diff T C" are empty
and records the good state we verified when we made T.

This is one case I still use the plumbing "read-tree -m -u".


[Footnote]

*1* http://gitster.livejournal.com/26540.html

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

* Re: Is there a way to exclude user-specified files or directories  from participating in merges?
  2009-02-18  1:32   ` Brent Goodrick
@ 2009-02-18  1:58     ` Junio C Hamano
  2009-02-18  5:39       ` Brent Goodrick
  0 siblings, 1 reply; 9+ messages in thread
From: Junio C Hamano @ 2009-02-18  1:58 UTC (permalink / raw)
  To: Brent Goodrick; +Cc: git

Brent Goodrick <bgoodr@gmail.com> writes:

> Thanks. Well, I should have said in my initial request: "Without
> manually forwarding changes from branch to branch and without having
> to remember special rules about what I can and cannot merge into which
> branch", since that is likely to get forgotten. :)
>
> The answer I am hearing you say is that git doesn't have a way to
> automatically exclude files akin to how rsync handles include/exclude.
>  Is that what you are saying? Or, could the hook mechanism be
> exploited to get this behavior?

A merge is defined as a whole tree operation simply because there is no
sane way to support repeated merges (even a single direction merges)
otherwise.  What I explained was one (note that I am not saying "one true"
here) workflow that naturally supports "common version and multiple
variants" pattern that logically follows the definition of what a merge
is.

I would imagine you could define a custom merge strategy that knows to
ignore changes you made to work.sh file when you merge from work to common
(and home.sh file when you merge from home to common) to implement what
you would want, but for one thing the resulting history would not make
sense (e.g. a merge from work to central would appear as if it reverts all
the changes work made to certain files).  It would be Ok if you were using
git as a mere backup+sneakernet medium (in such a case you would not care
what the history would show you), but that is not the intended target of
git, so there is no such built-in support.

Also such a custom merge strategy would be very project specific and as
your project grows and/or as you add more deployments, I suspect its rules
will have to become a lot more complicated.  I haven't even thought about
what should happen in such a merge strategy when you try to merge work to
home.

Compared to that, the two simple rules "commit chagnes to generic things
only to the generic branch" and "merge only from generic to specific" will
not grow as your project grows complexity.

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

* Re: Is there a way to exclude user-specified files or directories  from participating in merges?
  2009-02-18  1:58     ` Junio C Hamano
@ 2009-02-18  5:39       ` Brent Goodrick
  0 siblings, 0 replies; 9+ messages in thread
From: Brent Goodrick @ 2009-02-18  5:39 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

On Tue, Feb 17, 2009 at 5:58 PM, Junio C Hamano <gitster@pobox.com> wrote:
> Compared to that, the two simple rules "commit changes to generic things
> only to the generic branch" and "merge only from generic to specific" will
> not grow as your project grows in complexity.

I now I see the wisdom of the above statement.  You've given me a lot
of approaches to think about and try. Thanks for your help!

bg

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

* Re: Is there a way to exclude user-specified files or directories from  participating in merges?
  2009-02-18  1:05 ` Junio C Hamano
  2009-02-18  1:32   ` Brent Goodrick
  2009-02-18  1:36   ` Junio C Hamano
@ 2009-02-18 13:33   ` Sitaram Chamarty
  2009-02-18 19:02     ` Junio C Hamano
  2 siblings, 1 reply; 9+ messages in thread
From: Sitaram Chamarty @ 2009-02-18 13:33 UTC (permalink / raw)
  To: git

On 2009-02-18, Junio C Hamano <gitster@pobox.com> wrote:
> And stick to the following two rules:
>
>  - You make edits to common files only on the common branch.
>  - You merge from common to deployment, never the other way.
>
> So at work, you would have a checkout of your work "deployment branch",
> and find needs to change things.  It is Ok to edit both work.sh and
> generic.sh (without being able to edit both, it would be hard to verify if
> the changes would work together) at this time, but don't commit the result
> in the work branch.
>
> Save the changes to work.sh away (e.g. "git diff work.sh >P.diff" and then
> "git checkout HEAD work.sh"), switch to the common branch, and commit the
> changes to the generic file.  Switch back to the deployment branch, merge
> the common branch (to pick up the changes to home.sh), reapply the changes
> specific to the deployment you saved earlier (e.g. "git apply P.diff"),
> tne commit the result.

[I did read your followup also; my question applies to both
versions of the technique]

Let me explain where I'm coming from: this is very often
needed when you maintain customer specific branches, and the
workflows in both your posts in this thread so far are too
complex for, err, me <sheepish grin> :-)

Would it not be easier to do something like this?  (I suck
at 2-d drawing, even line... but this should still be
understandable)

(W = work, T = temporary, C = common)

  - make granular commits and test etc, from W to T

        O---a---b+1---c---2---d---3
        W is pointing at commit O
        T is pointing at commit 3
        b+1 is a commit that contains both types of changes

  - use rebase -i (including split commits if needed, as
    described in 'git help rebase') to put all the changes
    that go to master before the ones that only go to work.

        O---a---b---c---d---1---2---3

  - (retest if needed)

  - cherry pick the first set of changes to common (in this
    example, a, b, c, d will become a', etc on common)

  - merge from common to work (x, y, etc are some other
    changes that went into common since the last time you
    merged)

        O---x---y---a'---b'---c'---d'
        W is now pointing at d'

  - cherry pick the stuff that remains

        O---x---y---a'---b'---c'---d'---1'---2'---3'
        W is now pointing at 3'

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

* Re: Is there a way to exclude user-specified files or directories from  participating in merges?
  2009-02-18 13:33   ` Sitaram Chamarty
@ 2009-02-18 19:02     ` Junio C Hamano
  2009-02-19 13:37       ` Sitaram Chamarty
  0 siblings, 1 reply; 9+ messages in thread
From: Junio C Hamano @ 2009-02-18 19:02 UTC (permalink / raw)
  To: Sitaram Chamarty; +Cc: git

Sitaram Chamarty <sitaramc@gmail.com> writes:

> Let me explain where I'm coming from: this is very often needed when you
> maintain customer specific branches, and the workflows in both your
> posts in this thread so far are too complex for, err, me <sheepish grin>
> :-)
>
> Would it not be easier to do something like this?  (I suck at 2-d
> drawing, even line... but this should still be understandable)

What you drew is a detailed discussion on a technique to use to group
together common part and customer specific part, and I think it is Ok to
do whatever you feel comfortable with.  It is essentially the same as my
"in real life, 'git diff >P.diff' is not how I would do this" example,
just going into more detail on what you would do to sift 'common only' vs
'specific to work branch' apart, and I think what you are doing is sane.

But if you wrote it as a draft of a document to explain how-to to new
people, I think you need to clarify a few things.

It is unclear in your description how the "common" branch progressed in
the whole process, and how the resulting history looks.  I can guess that
you meant commits marked with alphabet letters are of common kind and
numbers are of work kind, but you do not want to force readers to guess.

It also is not quite clear that you are using a temporary branch in
addition to common and work, and where in your sequence you are doing "git
checkout" to switch branches.

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

* Re: Is there a way to exclude user-specified files or directories from  participating in merges?
  2009-02-18 19:02     ` Junio C Hamano
@ 2009-02-19 13:37       ` Sitaram Chamarty
  0 siblings, 0 replies; 9+ messages in thread
From: Sitaram Chamarty @ 2009-02-19 13:37 UTC (permalink / raw)
  To: git

On 2009-02-18, Junio C Hamano <gitster@pobox.com> wrote:
> Sitaram Chamarty <sitaramc@gmail.com> writes:
>
> > Let me explain where I'm coming from: this is very often needed when you
> > maintain customer specific branches, and the workflows in both your
> > posts in this thread so far are too complex for, err, me <sheepish grin>
> > :-)
> >
> > Would it not be easier to do something like this?  (I suck at 2-d
> > drawing, even line... but this should still be understandable)
> 
> What you drew is a detailed discussion on a technique to use to group
> together common part and customer specific part, and I think it is Ok to
> do whatever you feel comfortable with.  It is essentially the same as my
> "in real life, 'git diff >P.diff' is not how I would do this" example,
> just going into more detail on what you would do to sift 'common only' vs
> 'specific to work branch' apart, and I think what you are doing is sane.

Thanks -- I mainly wanted confirmation that one does *not*
have to do diff/patch/apply or plumbing commands to achieve
what the original poster was asking.

> But if you wrote it as a draft of a document to explain how-to to new
> people, I think you need to clarify a few things.

> It is unclear in your description how the "common" branch progressed in
> the whole process, and how the resulting history looks.  I can guess that
> you meant commits marked with alphabet letters are of common kind and
> numbers are of work kind, but you do not want to force readers to guess.

> It also is not quite clear that you are using a temporary branch in
> addition to common and work, and where in your sequence you are doing "git
> checkout" to switch branches.

I did not write it in that light then, but I will do so now,
and if you have a few minutes to critique it that would be
great.  If it's crap, sorry for the noise.

----->8-----

The following document explains how to maintain a 'common'
branch, with one or more 'customer specific' branches that
hang off of the common branch.  The inspiration was a
question about maintaining 'work' and 'home' configurations
which differ perhaps slightly from a 'common' configuration,
which leads to the same sort of situation.

The basic rules are best described by Junio in
http://permalink.gmane.org/gmane.comp.version-control.git/110489

>  - You make edits to common files only on the common branch.
>  - You merge from common to deployment, never the other way.

This is one way to follow those rules.

We use the following terminology:

'common' is the branch representing the base product.  All
regular customers get this version.

'special' is a special branch for a specific customer.  This
customer needs changes unique to his environment, which
should not be merged back into the common branch.  However,
this branch must regularly get the benefit of changes in the
mainline 'common' branch.  (This is the genesis of the two
rules above).

If you have more than one special customer the same logic
will apply for each of them separately, although if you have
too many such customers you may also want to look at "Never
merging back" (http://gitster.livejournal.com/26540.html)
for an additional tip on this.

Also, we assume the actual development and testing needs to
be done on one branch, so you will have a mix of 'common'
and 'special' changes all together.

The history looks like this in the beginning:

    -----o          <- special branch (current)
        /
    ---o            <- common branch

Before you start, make a temporary branch TEMP from
'special'; leave special where it was.

    git checkout -b TEMP special

You now make some changes for the special customer, which
also involve some 'common' changes that need to be ported
back to the common branch.  The topology now looks like
this, where A, B, C, are changes that logically belong on
'common', and 1, 2, 3, are changes that are specific to this
customer.

Notice that the 2 sets of changes are intermixed because
that is how the development happened, and that there is even
one commit where common and special changes are mixed
(perhaps you realised this only later).

    -----o--A--B1--2--3--C          <- "TEMP" branch (current)
        /
    ---o                            <- common

Meanwhile, just to make things interesting, the common
branch has also had some other, unrelated changes which you
eventually want on the special branch as well.

    -----o--A--B1--2--3--C          <- "TEMP" branch (current)
        /                                            
    ---o--X--Y                      <- common

The first thing to do is to tease the tangled commits apart
using rebase.

Using 'git rebase -i special', get the topology into this
shape.  Note that we have split the "B1" commit into B and 1
separately.  'git help rebase' has a very simple and clear
section on splitting commits, so I will not detail that
here.

    -----o--A--B--C--1--2--3        <- "TEMP" branch (current)
        /                                              
    ---o--X--Y                      <- common

At this point you may want to retest, just to be sure.

Now switch to the 'common' branch and cherry pick the
changes that belong on 'common'.  The simplest way to cherry
pick is gitk.

    git checkout common
    gitk
    # and cherry pick first A, then B, then C, in order

    -----o--A--B--C--1--2--3        <- "TEMP" branch
        /                                              
    ---o--X--Y--A'--B'--C'          <- common (current)

Now merge from 'common' to 'special'

    git checkout special
    git merge common

           A--B--C--1--2--3     <- TEMP
          /
     ----o-----------------o    <- special (current)
        /                 /
    ---o--X--Y--A'--B'--C'

Now all we need is to get commits 1, 2, and 3 onto
'special'; this is easiest done by rebasing TEMP first...

    git rebase special TEMP

                             1'--2'--3'     <- TEMP (current)
                            /
     ----o-----------------o    <- special
        /                 /
    ---o--X--Y--A'--B'--C'

...and then making special equal to TEMP

    git checkout special
    git merge TEMP

                             1'--2'--3'     <- special (current)
                            /
     ----o-----------------o
        /                 /
    ---o--X--Y--A'--B'--C'

Now you don't need TEMP anymore, and can delete it:

    git branch -d TEMP

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

end of thread, other threads:[~2009-02-19 13:39 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-02-18  0:49 Is there a way to exclude user-specified files or directories from participating in merges? Brent Goodrick
2009-02-18  1:05 ` Junio C Hamano
2009-02-18  1:32   ` Brent Goodrick
2009-02-18  1:58     ` Junio C Hamano
2009-02-18  5:39       ` Brent Goodrick
2009-02-18  1:36   ` Junio C Hamano
2009-02-18 13:33   ` Sitaram Chamarty
2009-02-18 19:02     ` Junio C Hamano
2009-02-19 13:37       ` Sitaram Chamarty

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