11

Can git merge mostly ignore some file deletions and file renames between two branches, while merging only subsequent changes to the files that were renamed (and ignoring changes to the files that were deleted in one branch), back and forth between the two branches?

Thus, I would like to be able to merge changes both ways between two branches. The development branch is initially a copy of the master branch, but with numerous deletions and file renames.

This last point is what breaks the method I tried:

  • On master, I did a merge -s ours development (no file changed).
  • Similarly on development, I did merge -s ours master (no file changed).

I was hoping that this would somehow define some starting point, so that any subsequent git merge from any of the branches would only apply the changes made since the two merges above. Instead, the merge results in the branches becoming identical (as far as I can see), whereas I need to keep the development branch lean and clean (fewer files, better file locations), and the master branch complete (all the files, in locations appropriate for deployment)—while being able to import all changes (after the initial deletions and renames) both ways. Can this be done with git?

Eric O. Lebigot
  • 91,433
  • 48
  • 218
  • 260
  • If I understand what your doing correctly, you are renaming and moving files around in the development branch, but don't want those actions to get merged into master when you merge in your changes to those files content. If I understood that correctly, could you please explain why you are doing this and what the purpose/goal of that is. It seems like a strange thing to do, and there may be another way to achieve that. – eddiemoya Aug 18 '15 at 16:56
  • You are right, but in addition I also delete files. :) The reason why I would like to do this is that the production branch is full of distracting and slightly confusing stuff (old configuration files that are unused, programs with a name that makes sense for production ["main.py"] but not for module imports, etc., etc.), and I don't have full control over it (or don't want to clean it up now). So I would be very happy to only extract from this the programs I am working on: this makes updating my code easier, as I would readily see only the relevant bits (which gives a lot of information). – Eric O. Lebigot Aug 19 '15 at 02:33
  • 1
    I dug up a couple of notes you may or may not find interesting. I wouldn't dare call this a proper answer so you'll find them [there](http://pastebin.com/dpBjPaps) if you want to take a peek. – tne Aug 19 '15 at 11:43

1 Answers1

8

After carefully reviewing the documentation at https://www.kernel.org/pub/software/scm/git/docs/git-merge.html:

The main idea of merge is, in a sense, to actually try to make the current branch identical to another branch or a specific commit in another branch, by incorporating changes that happened since they diverged from one of them into the other, which is exactly the behavior you are getting. Keyword being "diverged". You can specify which commit to merge 'up to' but not a commit since when you want to merge, merge always goes back in history up to the moment the two threads where identical or to the beginning of them, then replays the changes that happened in the merge-source again on the merge-target. This part of the behavior (where it first tries to find a common ancestor for both points and replay the changes from that point) is not changeable by any option in the merge command, no merge-strategy can change that. ours instructs it to ignore changes from other branches and always end up having the current branch's tree as the end result. recursive -Xours makes it merge and favor the current branch's version in conflicts, but both first go back to the common point or to the beginning of history for the merge source.

What I understood is this: you have a project with say a large tree and lots of files. While developing features you want to concentrate on those files you will be modifying so you delete some files, rearrange them to be easier to handle, work on them, then want to integrate the changes into the master branch. This is simply not what merge is intended to do obviously. There's no option for the command that simply tells it to start the merge from a specific point forwards

However, you might, with a small change of strategy, achieve the desired effect with a combination of git format-patch and git apply/git am First, when the branches are identical, in development, remove all unnecessary files but keep the structure. Start working on the files you want.

When the changes are ready to go to master, let's say you have made 5 commits by then. Execute something like:

git format patch -5 --stdout > /some_folder/patchname.patch

Now you can checkout master and apply the patch you created in /some_folder/patchname.patch using something like

git am --signoff < /some_folder/patchname.patch

first you might wanna check if everything will be fine by running:

git apply --check /some_folder/patchname.patch

Another way is to use git cherry-pick (see https://www.kernel.org/pub/software/scm/git/docs/git-cherry-pick.html) to manually choose what commits you want to transfer between both of branches.

Hope this helps in any way.

MythWTS
  • 166
  • 4
  • Thank you for this lucid description. Since "`ours` instructs it to ignore changes from other branches and always end up having the current branch's tree as the end result", but what else does it do? My naive understanding was that it precisely created this "common ancestor" that merges go back to, no? (As for the second part of your answer, the renames/file moves I made unfortunately preclude me from being able to use it.) – Eric O. Lebigot Aug 18 '15 at 01:23
  • 1
    As the documentation describes it [here](https://www.kernel.org/pub/software/scm/git/docs/git-merge.html#_merge_strategies): `ours: This resolves any number of heads, but the resulting tree of the merge is always that of the current branch head, effectively ignoring all changes from all other branches. It is meant to be used to supersede old development history of side branches. Note that this is different from the -Xours option to the recursive merge strategy.` – MythWTS Aug 18 '15 at 05:09
  • This unfortunately does not really explain what ours does. Furthermore, since it is "used to supersede old development history of side branches", it even looks exactly like what I am looking for (that side-branch history before the `-s ours` merge be skipped when merging again later after some additional changes on the side branch). I am still confused as to what `-s ours` does, but I am sure I can find this somewhere. In any case, I can accept that git cannot do what the question asks for. – Eric O. Lebigot Aug 18 '15 at 10:41
  • 1
    `git merge -s ours` does not skip the history before, in fact it only stores the history without actually applying it. As mentioned [here](https://cbx33.github.io/gitt/afterhours4-1.html), it can be used to keep the history of an old branch without actually keeping the changes the branch brought. This unfortunately include the changes "after" your desired point. Mainly, it's for those cases where you want to keep the history for auditing and historical reasons but you don't want that branch anymore. Maybe a failed experiment or something like that. – MythWTS Aug 18 '15 at 11:23
  • That's a good use case (after deleting the merged branch). So, I guess that subsequent regular (non-`ours`) merges use the stored history but this time apply it, because the branches diverged when they stopped being identical; my confusion came from the fact that I thought that this point of divergence was the point of the last merge (include a `-s ours` merge). Thanks! – Eric O. Lebigot Aug 18 '15 at 15:49