You can't. The attributes you can set in .gitattributes
apply only to what I call low level conflicts, but "modify/delete" is what I call a high level conflict.
It's worth mentioning, though, that just because A has b.txt
and B does not, does not guarantee you will get always get a modify/delete conflict. What kind of conflict you get—if any—depends as well on the merge base commit. The branch A resolves to the tip commit of that branch, and likewise for B, but git merge
does not use only those inputs. It finds a third input:
o--o--...--o <-- A
/
...--o--*
\
o--...--o <-- B
Each round o
represents some commit. The commit marked *
, which is where the two branches split apart originally—and hence, right now, the first place they come back togther—is the current merge base of the two branch tips.
You now run git checkout B && git merge A
. Git compares commit *
to the tip of A and finds b.txt
modified since *
. Git compares *
to the tip of B and finds b.txt
removed. This is what produces the high level conflict, about which .gitattributes
does nothing. So you must merge this file manually and then commit the result:
o--...--o <-- A
/ \
...--o--o \
\ \
o--...--o---o <-- B
Because this is a true merge, the new commit has two parents. The first parent is the previous tip of B (the straight line across the bottom) and the second parent is the previous and still current tip of A (going up and left). No big deal so far; but now let's make new commits on both A and B:
o--...--o--o--o <-- A
/ \
...--o--o \
\ \
o--...--o---o--o <-- B
If we now do that git checkout B && git merge A
again, Git will find a new merge base, by searching backwards from both tips as before. The first commit that's reachable from both branch tips will be the merge base—but this time, that's a commit on the top row, specifically the one just before the last merge:
o--...--*--o--o <-- A
/ \
...--o--o \
\ \
o--...--o---o--o <-- B
Git will now diff commit *
against the current tips of A and B.
Since presumably B still lacks b.txt
, Git will conclude that *
-to-ours removes the file. If you haven't touched b.txt
in the two new commits on A, though, *
-to-theirs doesn't change it. There will be no high level conflict: "unchanged in A, removed in B" resolves to "remove in merge".