views:

1349

answers:

2

I'm trying to have two branches with binary files in git - one "development" and one "stable". The development branch can have several changes of these files before I want to "release" them to the stable branch (and the stable branch has those files renamed, in case that's relevant).

I could do a normal merge, this works fine, but preserves too much history - when pulling the "stable" branch, all intermediate commits from the "development" branch are pulled as well (because they're parent commits). But we're talking about binary files that don't have any sensible merging strategy (except theirs/ours), so the actual history of the files on the development branch is useless. When I pull the "stable" branch, I get this:

          X-------------------G stable
         /                   /
    a---b---c---d---e---f---g development

Because E has a parent in the development branch, I get the whole history of the development branch (data objects for c, d, e, f and g) in my repository, which I'm not interested in (X is the same as b, with some file renaming applied).

So I tried to git merge --squash changes from the development branch to the stable branch. The first such merge and commit went OK, the results were as expected (nice change log in the commit message, no relation to the development branch):

          X-------------------G stable
         /                   
    a---b---c---d---e---f---g development
After I pull this squashed stable branch, I get this in my repository, which is what I want:
a---b---X---G

But the second merge failed (because git had no way to know how much I merged already and got confused).

  • Is it possible to somehow record the merge, without producing a "merge commit" with two parents?
  • Or, is it possible to tell git to merge only a certain "range" of revisions, like in SVN?
  • Or, is it possible to do a normal merge without having to download all refs from the other branch when pulling?
  • Or should I provide a custom merge driver for the files in question, that simply renames "their" version to "our", thereby resolving the conflicts? I'm still afraid that --squash will always try to merge the whole history, up to the common parent, solving only half of my problem.

Update: rebasing

If I understand rebasing correctly, I'll end up with this:

                              X stable
                             /
    a---b---c---d---e---f---g development

Which gets me all the data I'm not interested in (c, d, e, f) and as a bonus, I'll loose the information that b was a stable version in the branch.

Each development revision adds about 5MB to the repository size (and repacking the whole repo shrinks it only about 10%), the "stable" branch comes almost for free (the data is already there). I want pulling a single new revision from the stable branch to pull only the new 5MB, but instead updating from X to G downloads 25MB, because I'm somehow not able to say that I don't care about the contents of c, d, e and f.

+2  A: 

This isn't the right place to use merge --squash. One good place to use it is in a throwaway topic branch, which you're going to merge into your main branch and then get rid of. All the development done in the topic branch is shown as one commit in the main branch. In your situation, you should merge from the development branch normally, and then use git-rebase --interactive to squash the commits you want.

Is it possible to somehow record the merge, without producing a "merge commit" with two parents?

If I understand the question correctly, no. Absolutely not.

Is it possible to tell git to merge only a certain "range" of revisions, like in SVN?

Yes. git merge [commit hash]

Or, is it possible to do a normal merge without having to download all refs from the other branch when pulling?

See answer to previous question.

Or should I provide a custom merge driver for the files in question, that simply renames "their" version to "our", thereby resolving the conflicts? I'm still afraid that --squash will always try to merge the whole history, up to the common parent, solving only half of my problem.

No! Just don't use git merge --squash. This isn't the right place to use it!

Ramkumar Ramachandra
Thanks for your answer! I'm not sure that rebasing is what I need. I do need to record some history in the "stable" branch, so I can go back and forth between "releases". This is what I want:`D -> SD |D |D -> SD |D -> S`But I only need the history from the S branch - I don't care about the history from the D branch. The problem I need to solve is how to pull the S branch without pulling all parent refs from the D branch (if I understand it correctly, rebasing gets rid of all previous S commits which is not what I want).`D -> xDDD -> xD D -> S`
Kim Sullivan
Yes, I understand that you need to preserve the existing history from the S branch. No, rebasing doesn't get rid of all previous S commits; before merging development into stable, note the [commit hash] and then git rebase -i [commit hash] - then you'll effectively only be squashing the commits you just merged. Also, as far as releases are concerned, why not just tag specific commits as releases?
Ramkumar Ramachandra
I'm sorry I don't quite understand your D -> S D | D diagram too well. I hope my previous comment clarified everything.
Ramkumar Ramachandra
I didn't realize comments don't support formatting, I'll update my question.
Kim Sullivan
I hope the question makes more sense now. The problem is with repository size and slow connections. I was hoping there was a way to use a single git repository for deployment for both testing and stable binaries. But the more I think about this, the more I come to realize that Git just wasn't designed to do this. I'll either have to accept the high bandwidth cost associated with pulling both deployment and stable binaries, or use some other mechanism (maybe a centralised versioning system, or lots of shell scripts that "version" the binaries on a plain filesystem and deploy using plain copy).
Kim Sullivan
"But the more I think about this, the more I come to realize that Git just wasn't designed to do this" -- Exactly. A plausible solution: Remove S. Create discrete flat-file (tarball?) releases from tags in D. Git was never meant to handle binaries.
Ramkumar Ramachandra
+1  A: 

you could use git rebase -i (interactive mode) to sqash all your changes, just change the lines to m or meld

you will than have a single commit which you can merge. if you want every step of yours reserved, you have to create another branch on your hacking branch before doing the rebase, this can be done with git branch small-dirty-changes-i-don’t-want-anybody-to-see

all this has to be done before attempting to merge; step by step:

# on branch "hacking": hack commit hack commit hack commit

# create a reference to your history
$ git branch dirty-history

# sqash your commits by setting all lines to "meld"
$ git rebase -i

# checkout master or the branch you want to merge to
$ git checkout master

# merge your squashed commit
$ git merge hacking
# or: $ git pull hacking
knittl
Hm, I probably could rebase the changes on the development branch ("dirty") once in a while (actually, before merging to stable and releasing), so I don't have all those intermediate binaries I don't care about much. The only important thing between stable releases is the hash and the commit messages... The hash is important for testing (which build was tested and which was not). I'll have to think about it some more.
Kim Sullivan