tags:

views:

388

answers:

3

Say I have two repositories that were created from the same initial content. For example, if I were using git to manage my apache config files in /etc/apache2 and I ran git init on machine-a and machine-b separately.

I've since made some configuration changes to machine-b that I would like to apply to the configuration on machine-a without overwriting un-common things like hostnames, etc.

If I do this:

machine-a% remote add machine-b ssh://...
machine-a% git fetch machine-b

I get all the commits on machine-b and GitX shows a wholy disconnected set of commits, as I would expect.

However, if I try to merge any of machine-b's changes I get conflicts on all modified lines.

For my purposes, this is good enough, but I wonder, is there a cleaner solution?

A: 

How can Git tell between what is a "configuration change" and what is an "uncommon thing"? Are there only certain files you want to share between the two, like mime-type files?

How about breaking up the shared parts into separate files and using Apache's Include directive and adding any files you want to ignore to .gitignore?

spilth
Apache is only one of the cases I want to use something like this.Think of this as using git as a patch distribution mechanism. I make the same configuration change to several servers on the same stock config files frequently.
Otto
+2  A: 

That's an interesting use of Git; I could have used it to handle several websites that are based on the same wiki codebase, but have very different configurations.

Set up a Git repo with your initial content and make separate branches for each machine which hold its current configuration. Clone to the other machine and set each machine as the remote of the other.

Now you can make changes on one machine and push them onto its branch on the other machine whenever you commit them.

If you commit the common elements separately from the non-common elements, you can then

$ git checkout [localmachine bracnch]
$ git cherry-pick [SHA1 of common commit1]
$ git cherry-pick [SHA1 of common commit2]

And, if you don't know what you'll want to reuse until after committing on one machine, you can use git add --patch to pluck individual diffs from one branch and stage them them to be committed to the other branch.

For more than two machines/sites, you could merge common changes onto MASTER and merge changes to MASTER onto each of the branches.

Paul
That would work if I had the forethought to clone the config repo. In this case, I usually do git init before editing some config files and later realize I want to patch some stuff I did on another machine into this machine's config. Will investigate the branch per machine idea, though.
Otto
Yes, the takeaway idea is a branch for each machine. You can use grafts or create a new repo which starts with the file content that is common each machine and rebase each machine's history on top of that as a separate branch. Pieter has given a nice explanation of grafts.
Paul
+3  A: 

You might want to look at git grafts. Some information is available here: http://www.kernel.org/pub/software/scm/git/docs/git-filter-branch.html

basically, what you want to do is to fake an ancestry.

You did an initial commit on both machines -- let's call them 'baseA' and 'baseB'. Then, you created a second commit on top of that on both sides -- 'secondA' and 'secondB'. What you want to do is tell git that 'secondB' does not have 'baseB' as ancestor, but instead has 'baseA' as parent.

You can this by using grafts. For example, put this in your .git/info/grafts file:

secondB baseA

(but then using the real SHA's). Now, if you look in GitX, you will see that the histories are connected. To make the change permanent, you need to run git filter-branch.

Update from the comments

I'm not sure what you did is 'correct', though it will work. If you do what you did, you will get as history:

 BaseA --- SecondA -- .. - ..  - Merge
   \ BaseB --- SecondB ... - .. /

Which will work (and the merge will succeed without problems). However, what's a bit nicer is this:

 BaseA --- SecondA -- .. - ..  - Merge
   \ SecondB ... - .. /

which basically drops "BaseA" as commit and sets "BaseA" as the parent for SecondB. This should be possible using the graft rule I told you.

To demonstrate this, I created a small repository you can download here.

If you look at the history in GitX ("gitx master second_master"), you will see that the histories are disconnected. If you move the .git/info/_grafts file to .git/info/grafts, you will get the second, nice-looking history.

I also added a .git/info/wrong_grafts file. If you use that one, you will get the history you created.

If you look at the SHA's in the grafts file, you'll see that the _grafts file basically has this:

SecondB BaseA

and the wrong_grafts file has this:

BaseB BaseA
Pieter
I think this is really close, but it's not working for me at the moment. GitX doesn't show the update after I edit .git/info/grafts and git-filter-branch gives me "error: bad graft data:" I'm trying various combos of commits in grafts now.
Otto
Ok I got this to work. If you could update I'll accept the answer..git/info/grafts was baseB baseAAnd then I ran:git filter-branch baseB..HEADAlso with the correct grafts file gitx showed the history properly before running filter-branch.
Otto
I updated my answer with a reply to your comments, as it was too big to put in the comments section.
Pieter
Yours works for me, and now for some reason my test repo works. I'm not sure what I was messing up in the grafts file, but from what I read it could have been as simple as whitespace. Thanks.In any case, I think I may have to do it my way if the new repo doesn't have any new commits, yet.
Otto