web-dev-qa-db-fra.com

Comment dire à git de toujours sélectionner ma version locale pour les fusions en conflit sur un fichier spécifique?

Supposons que je collabore avec quelqu'un via un référentiel git et qu'il existe un fichier particulier pour lequel je ne souhaite jamais accepter de modifications externes.

Existe-t-il un moyen pour que je mette en place mon référentiel local afin de ne pas me plaindre d'une fusion conflictuelle à chaque fois que je me tire? Je voudrais toujours choisir ma version locale lors de la fusion de ce fichier.

90
saffsd

Sur l'instance spécifique d'un fichier de configuration, je serais d'accord avec la réponse de Ron :
une configuration doit être "privée" dans votre espace de travail (donc "ignorée", comme dans "déclarée dans un fichier .gitignore").
Vous pouvez avoir un fichier de configuration modèle avec des valeurs symbolisées, et un script transformant ce fichier config.template en un fichier de configuration privé (et ignoré).


Toutefois, cette remarque spécifique ne répond pas à une question plus large, plus générale, c’est-à-dire votre question (!):

Comment puis-je dire à git de toujours sélectionner ma version locale pour les fusions en conflit sur un fichier spécifique? (pour tout fichier ou groupe de fichier)

Ce type de fusion est une "copie de fusion" dans laquelle vous copiez toujours la version "la nôtre" ou "la leur" d'un fichier en cas de conflit.

(comme Brian Vandenberg notes dans les commentaires , 'ours' et 'theirs' sont ici utilisés pour une fusion.
Ils sont inversés pour un rebase: voir " Why is the meaning of “ours” and “theirs” reversed with git-svn ", qui utilise un rebase, " git rebase, en gardant une trace de 'local' et de 'remote' ")

Pour "un fichier" (un fichier en général, ne parlant pas d'un fichier "config", car c'est un mauvais exemple), vous obtiendriez cela avec un script personnalisé appelé par le biais de fusions.
Git appellera ce script car vous aurez défini une valeur gitattributes value , qui définit un pilote de fusion personnalisé.

Le "pilote de fusion personnalisé" est, dans ce cas, un script très simple qui conserve fondamentalement la version actuelle inchangée, vous permettant ainsi de toujours sélectionner votre version locale. 


Essayons cela dans un scénario simple, avec un msysgit 1.6.3 sous Windows, dans une simple session DOS:

cd f:\prog\git\test
mkdir copyMerge\dirWithConflicts
mkdir copyMerge\dirWithCopyMerge
cd copyMerge
git init
Initialized empty Git repository in F:/prog/git/test/copyMerge/.git/

Faisons maintenant deux fichiers, qui auront des conflits, mais qui seront fusionnés différemment.

echo a > dirWithConflicts\a.txt
echo b > dirWithCopyMerge\b.txt
git add -A
git commit -m "first commit with 2 directories and 2 files"
[master (root-commit) 0adaf8e] first commit with 2 directories and 2 files

Nous allons introduire un "conflit" dans le contenu de ces deux fichiers dans deux branches git différentes:

git checkout -b myBranch
Switched to a new branch 'myBranch'
echo myLineForA >> dirWithConflicts\a.txt
echo myLineForB >> dirWithCopyMerge\b.txt
git add -A
git commit -m "add modification in myBranch"
[myBranch 97eac61] add modification in myBranch

git checkout master
Switched to branch 'master'
git checkout -b hisBranch
Switched to a new branch 'hisBranch'
echo hisLineForA >> dirWithConflicts\a.txt
echo hisLineForB >> dirWithCopyMerge\b.txt
git add -A
git commit -m "add modification in hisBranch"
[hisBranch 658c31c] add modification in hisBranch

Essayons maintenant de fusionner "hisBranch" sur "myBranch" avec:

  • résolution manuelle des fusions en conflit
  • sauf pour dirWithCopyMerge\b.txt où je veux toujours conserver ma version de b.txt.

Puisque la fusion a lieu dans 'MyBranch', nous y reviendrons et ajouterons les directives 'gitattributes' qui personnaliseront le comportement de la fusion.

git checkout myBranch
Switched to branch 'myBranch'
echo b.txt merge=keepMine > dirWithCopyMerge\.gitattributes
git config merge.keepMine.name "always keep mine during merge"
git config merge.keepMine.driver "keepMine.sh %O %A %B"
git add -A
git commit -m "prepare myBranch with .gitattributes merge strategy"
[myBranch ec202aa] prepare myBranch with .gitattributes merge strategy

Nous avons un fichier .gitattributes défini dans le répertoire dirWithCopyMerge (défini uniquement dans la branche dans laquelle la fusion aura lieu: myBranch), et nous avons un fichier .git\config qui contient maintenant un pilote de fusion.

[merge "keepMine"]
        name = always keep mine during merge
        driver = keepMine.sh %O %A %B

Si vous ne définissez pas encore keepMine.sh et que vous lancez la fusion de toute façon, voici ce que vous obtenez.

git merge hisBranch
sh: keepMine.sh: command not found
fatal: Failed to execute internal merge
git st
# On branch myBranch
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   dirWithConflicts/a.txt
#
no changes added to commit (use "git add" and/or "git commit -a")

type dirWithConflicts\a.txt
a
<<<<<<< HEAD:dirWithConflicts/a.txt
myLineForA
=======
hisLineForA
>>>>>>> hisBranch:dirWithConflicts/a.txt

C'est bon:

  • a.txt est prêt à être fusionné et contient des conflits
  • b.txt n'est pas touché, car le pilote de fusion est censé s'en occuper (en raison de la directive contenue dans le fichier .gitattributes de son répertoire).

Définissez un keepMine.sh n'importe où dans votre %PATH% (ou $PATH pour notre ami Unix. Je fais les deux bien sûr: j'ai une session Ubuntu dans une session VirtualBox)

Comme commenté par lrkwz , et décrit dans la section " Stratégies de fusion " de Personnalisation de Git - Attributs Git , vous pouvez remplacer le script Shell par la commande Shell true.

git config merge.keepMine.driver true

Mais dans le cas général, vous pouvez définir un fichier de script:

keepMine.sh

# I want to keep MY version when there is a conflict
# Nothing to do: %A (the second parameter) already contains my version
# Just indicate the merge has been successfully "resolved" with the exit status
exit 0

(c'était un simple pilote de fusion;) (Encore plus simple dans ce cas, utilisez true)
(Si vous voulez conserver l’autre version, ajoutez simplement avant la ligne exit 0:
cp -f $3 $2.
C'est tout. Vous fusionnez le pilote garderait toujours la version en provenance de l’autre branche, en annulant tout changement local)

Maintenant, réessayons la fusion depuis le début:

git reset --hard
HEAD is now at ec202aa prepare myBranch with .gitattributes merge strategy

git merge hisBranch
Auto-merging dirWithConflicts/a.txt
CONFLICT (content): Merge conflict in dirWithConflicts/a.txt
Auto-merging dirWithCopyMerge/b.txt
Automatic merge failed; fix conflicts and then commit the result.

La fusion échoue ... uniquement pour a.txt.
Éditez a.txt et laissez la ligne dans 'hisBranch', puis:

git add -A
git commit -m "resolve a.txt by accepting hisBranch version"
[myBranch 77bc81f] resolve a.txt by accepting hisBranch version

Vérifions que b.txt a été préservé lors de cette fusion

type dirWithCopyMerge\b.txt
b
myLineForB

Le dernier commit représente la fusion complète:

git show -v 77bc81f5e
commit 77bc81f5ed585f90fc1ca5e2e1ddef24a6913a1d
Merge: ec202aa 658c31c
git merge hisBranch
Already up-to-date.

(La ligne commençant par Merge prouve cela)


Considérez que vous pouvez définir, combiner et/ou écraser le pilote de fusion, comme le fera Git:

  • examine <dir>/.gitattributes (qui est dans le même répertoire que le chemin en question): prévaudra sur l'autre .gitattributes dans les répertoires
  • Ensuite, il examine .gitattributes (qui se trouve dans le répertoire parent), ne définira que les directives s’il n’a pas déjà été défini.
  • Enfin, il examine $GIT_DIR/info/attributes. Ce fichier est utilisé pour remplacer les paramètres de l'arborescence. Cela écrasera les directives <dir>/.gitattributes.

Par "combinaison", je veux dire "agrégé", pilote de fusion multiple.
Nick Green tente, dans les commentaires , de combiner les pilotes de fusion: voir " Fusionner les pom avec le pilote python git ".
Cependant, comme mentionné dans son autre question , cela ne fonctionne qu'en cas de conflit (modification simultanée dans les deux branches).

133
VonC

Nous avons plusieurs fichiers de configuration que nous ne voulons jamais écraser. Cependant, .gitignore et .gitattributes ne fonctionnaient pas dans notre situation. Notre solution consistait à stocker les fichiers de configuration dans une branche de configuration. Ensuite, autorisez les fichiers à être modifiés lors de la fusion de git, mais immédiatement après la fusion, utilisez la "branche git checkout -." copier nos fichiers de configuration à partir de la branche configs après chaque fusion . Réponse détaillée de stackoverflow ici /

0
HamletHub