Mantener siempre el estado de file eliminado durante la fusión

Tengo un caso especial de ignorar files durante la fusión .

Supongamos que tengo dos twigs: A y B.
A contiene los files a.txt y b.txt .
B contiene el file a.txt pero no b.txt . Y esa es la forma en que debería ser.

Ahora quiero fusionar los cambios en A en B , es decir, actualizar a.txt . Sin embargo, durante la fusión siempre me da un conflicto:

CONFLICT (modify/delete): b.txt deleted in HEAD and modified in development. Version development of b.txt left in tree. Automatic merge failed; fix conflicts and then commit the result.

¿Cómo puedo configurar .gitattributes para que se .gitattributes la versión del file de B (es decir, eliminada), es decir, b.txt ignorada durante la fusión?

No puedes. Los attributes que puede establecer en .gitattributes aplican solo a lo que llamo conflictos de bajo nivel , pero "modificar / eliminar" es lo que llamo un conflicto de alto nivel .

Vale la pena mencionar, sin embargo, que el hecho de que A tenga b.txt y B no, no garantiza que siempre obtendrá un conflicto de modificación / eliminación. El tipo de conflicto que obtienes, si lo hay, también depende de la confirmación de la combinación . La twig A resuelve el compromiso de la punta de esa twig, y ​​también para B , pero la git merge no usa solo esas inputs. Encuentra una tercera input:

  o--o--...--o <-- A / ...--o--* \ o--...--o <-- B 

Cada ronda o representa algún compromiso. La confirmación marcada con * , que es donde las dos twigs se separaron originalmente, y por lo tanto, ahora mismo , el primer lugar en el que regresan, es la base de combinación actual de las dos sugerencias de twigs.

Ahora ejecuta git checkout B && git merge A Git compara commit * con la punta de A y encuentra b.txt modificado desde * . Git compara * con la punta de B y encuentra b.txt eliminado. Esto es lo que produce el conflicto de alto nivel, sobre el cual .gitattributes no hace nada. Por lo tanto, debe fusionar este file manualmente y luego confirmar el resultado:

  o--...--o <-- A / \ ...--o--o \ \ \ o--...--o---o <-- B 

Debido a que esta es una combinación verdadera, el nuevo compromiso tiene dos padres. El primer padre es el consejo previo de B (la línea recta en la parte inferior) y el segundo padre es el consejo previo y todavía actual de A (ir hacia arriba y hacia la izquierda). No es gran cosa hasta el momento; pero ahora hagamos nuevos commits en A y B :

  o--...--o--o--o <-- A / \ ...--o--o \ \ \ o--...--o---o--o <-- B 

Si ahora hacemos el git checkout B && git merge A nuevo, Git encontrará una nueva base de fusión, buscando hacia atrás desde ambas puntas como antes. El primer compromiso al que se puede acceder desde ambos extremos de la twig será la base de combinación, pero esta vez, se trata de una confirmación en la fila superior, específicamente la que se encuentra justo antes de la última fusión:

  o--...--*--o--o <-- A / \ ...--o--o \ \ \ o--...--o---o--o <-- B 

Git ahora dif commit * contra las puntas actuales de A y B.

Dado que presumiblemente B todavía carece de b.txt , Git concluirá que * -to-ours elimina el file. Sin embargo, si no ha tocado b.txt en los dos nuevos commits en A , * -to-theirs no lo cambia. No habrá conflicto de alto nivel: "sin cambios en A , eliminado en B " se resuelve en "eliminar en fusión".