Comment pouvez-vous fusionner deux branches dans git, en conservant les fichiers nécessaires d'une branche?
Lors de la fusion de deux branches, si un fichier a été supprimé dans une branche et non dans une autre, le fichier est finalement supprimé.
Par exemple:
Comment reproduire:
Créez un dépôt Git avec un fichier.
git init
echo "test" > test.txt
git add .
git commit -m "initial commit"
Créer une branche
git branch branchA
Supprimer le fichier en master
git rm test.txt
git commit -m "removed file from master"
Effectuez TOUTE modification dans branchA qui ne touche pas le fichier supprimé (il doit rester inchangé pour éviter les conflits).
git checkout branchA
touch something.txt
git add .
git commit -m "some branch changes"
À partir de là, quelle que soit la méthode utilisée pour fusionner ces deux branches, le fichier test.txt est supprimé. En supposant que nous utilisions sur le fichier pour branchA
, il s’agit d’un gros problème.
Exemples d'échec:
Fusionner 1
git checkout branchA
git merge master
ls test.txt
Fusionner 2
git checkout master
git merge branchA
ls test.txt
Rebase 1
git checkout branchA
git rebase master
ls test.txt
C'est un problème intéressant. Étant donné que vous avez supprimé le fichier après la création de BranchA
, puis que vous avez fusionné master
dans BranchA
, je ne sais pas comment Git pourrait se rendre compte qu'il existe un conflit.
Après la mauvaise fusion, vous pouvez annuler, puis fusionner à nouveau, mais rajouter le fichier:
git checkout HEAD@{1} .
git merge --no-commit master
git checkout master test.txt
git add test.txt
git commit
Pour une solution rapide dans ce cas, "git revert" le commit qui a supprimé le fichier.
Lorsque cette situation se présente à l'avenir, la meilleure façon de la gérer est de s'assurer que la création du nouveau fichier a bien lieu sur la branche. Ensuite, il est ajouté sur master lorsque vous fusionnez, mais vous n'avez pas le fichier traîner dans master entre-temps.
L'exemple de Casey ne fonctionnait pas dans mon cas. Je ne pouvais pas extraire test.txt
de master
, car il ne se trouvait plus dans cette branche:
$ git checkout master test.txt
error: pathspec 'test.txt' did not match any file(s) known to git.
Heureusement, je pouvais extraire le fichier de branchA
's propre HEAD
:
$ git checkout branchA
$ git merge --no-commit master
$ git checkout HEAD test.txt
$ git add test.txt
$ git commit
Vous devez modifier le fichier dans la branche afin qu'il y ait un conflit de fusion avec la suppression dans le coffre.
La même chose se produira si, par exemple, vous supprimez une déclaration pour quelque chose dans un fichier d’en-tête dans le coffre (car rien n’en a besoin) et que vous ajoutez une dépendance à cette déclaration dans un ou plusieurs fichiers autres que ceux de la branche. Lorsque vous fusionnez, étant donné que la branche ne touche pas (cette partie de) l'en-tête, elle supprimera simplement la déclaration et les choses se casseront.
Chaque fois que vous avez des éléments interdépendants situés à plusieurs endroits et qui doivent rester synchronisés, il est très facile pour une fusion d’introduire des problèmes en silence. C’est l’une des choses que vous devez connaître et vérifier lors de la fusion. Idéalement, vous utilisez des assertions au moment de la compilation ou d'autres vérifications de la compilation qui rendront immédiatement évidentes les échecs.
Ma solution à cela était simplement de modifier les fichiers que je devais conserver (avec un commentaire nécessaire de toute façon) et de valider ces modifications sur la branche cible, générant ainsi un conflit de fusion pouvant être facilement résolu avec un git add
et une validation normale.
Mon histoire a quelque chose comme ça. Les noms des branches ont été modifiés pour protéger les innocents.
git merge master
sur feature_branch entraîne la suppression des fichiers d'origine (bien sûr), git reset --hard
avant la fusion