4

I'm using a merge strategy in .gitattributes to preserve files during merges. I also used git config --global merge.ours.driver true; to set the driver in my config (I checked the config for [merge "ours"] driver = true and it is there).

My merge strategy is set correctly:

src/public/bundle.js merge=ours
src/public/main.min.css merge=ours
server/middlewares/https_redirect.js merge=ours

Yet when I merge, I am still getting the files from the branch being merged.

What could I be doing wrong?

skwny
  • 2,930
  • 4
  • 25
  • 45

1 Answers1

3

What you need is a custom "merge strategy"

See git: How do I add a custom merge strategy? (Edit to add: writing a merge strategy handler is much harder than writing a custom merge driver. But it's the only way to get the behavior I believe you want.)

What you have is a custom merge driver

Your setup does seem correct (albeit a bit "cheat-y", using /bin/true or the shell built in equivalent to just leave the %A file alone and exit successfully :-) ). But I suspect you're running afoul of the usual problem, which is:

Git only invokes a three-way merge driver if there are two diffs to combine.

That is, assume we're actually doing a three-way merge. This means there is a merge base version of some file, such as src/public/bundle.js, since there is a merge-base commit that differs from the two branch tip commits. We're on some branch, whose tip commit is 1111111, and we're merging some other commit whose hash ID is 2222222 and git merge-base 1111111 2222222 is bbbbbbb. Hence Git has done git diff bbbbbbb 1111111 to get the change from base to ours, and git diff bbbbbbb 2222222 to get the change from base to theirs.

The base version of src/public/bundle.js is the one in commit bbbbbbb. But, maybe the diff from bbbbbbb:src/public/bundle.js to 1111111:src/public/bundle.js is empty, while the diff from bbbbbbb:src/public/bundle.js to 2222222:src/public/bundle.js is non-empty.

In this case, Git doesn't do a three-way merge of the file. It doesn't have to, so it doesn't bother. It never invokes your custom merge driver at all; it just grabs the 2222222 version of the file and says "I'm done, all merged!"

This is true whether you let Git use its own built-in merge code, or specify a custom driver: it never bothers invoking the merge code at all when it can—or thinks it can—get away without doing a merge at all. It just takes the one changed version and calls it good.

(Personally, I think this is wrong / a bug, but Git has done it this way for many years, and the Git folks seem not inclined to change it.)

Community
  • 1
  • 1
torek
  • 448,244
  • 59
  • 642
  • 775
  • I appreciate the response but not sure I follow entirely. This has worked countless times in the past, which leads me to believe it might have something to do with the base version you mentioned (`bbbbbbb`). What is that exactly? Recently I reformatted my computer, and so I pulled everything down that was previously working. Is this occurring b/c 'bbbbbbb' already existed when I cloned the repo? Should I remove it from tracking and from origin, then start over again? – skwny Jan 13 '17 at 20:46
  • 1
    If you run `git merge-base HEAD ` you'll get the ID of the merge-base commit. You can then run the two separate `git diff` commands to predict how Git will merge the files. These depend strictly on the *commit graph*. For much more on merging and merge bases, see http://stackoverflow.com/q/14961255/1256452 and http://stackoverflow.com/q/37048823/1256452 – torek Jan 13 '17 at 20:57
  • I have never used `git merge-base` before and don't intend to at this point. It seems to me that this 'base commit' might be the issue. I.e. it existed in Github already when I cloned the repo. This post seemingly points out the same issue: http://unix.stackexchange.com/questions/181522/why-is-gitattributes-applied-only-to-files-added-to-repo-after-defining-it. – skwny Jan 13 '17 at 21:09
  • 1
    That particular Q&A doesn't have enough information in it to diagnose the root cause (nor does yours, really—that's why I point to `git merge-base`; *finding* the three commit IDs, and then diffing the merge base against the two branch tips, will produce enough information). – torek Jan 13 '17 at 21:18
  • Ok so what I did to fix this was make a merge-base that would diff from the two branches in question (production vs. dev). For both branches, I deleted all the files that needed to be preserved, removed tracking, committed, then merged both branches (both ways - not sure if this was necessary but I did it anyways). I then tested the merge-base to ensure it was current (w/o the files in question). After that, I added the files-to-be-preserved back in, committed both branches, then merged dev into production again, and the production files were preserved. Thank you very much torek..you saved me. – skwny Jan 13 '17 at 22:23
  • i see this and try but this is not workd for me sooo plzz any help you? marge time both file marge how to solved? – kiran_ray Jan 05 '21 at 06:45
  • @kiran.ray: I have no idea what you are asking. The answer above merely points out that merge *drivers* don't get invoked in some situations. – torek Jan 05 '21 at 06:48
  • gitignore or gitattribute can't working for me – kiran_ray Jan 05 '21 at 06:53
  • @kiran.ray: `.gitignore` (note the leading dot in the file name) is for making `git status` stop complaining about untracked files. This has nothing to do with merging. `.gitattributes` (note leading dot and final `s`) is for defining particular things about particular pathnames and hence for doing particular tricks with *tracked* files. These are very different. If you have a question, see [ask]. – torek Jan 05 '21 at 08:08