Comment appliquer un patch Git à un fichier avec un nom et un chemin différents?

100

J'ai deux référentiels. Dans l'un, je modifie le fichier ./hello.test. Je valide les modifications et crée un patch à partir de ce commit avec git format-patch -1 HEAD. Maintenant, j'ai un second référentiel qui contient un fichier qui a le même contenu que hello.test mais est placé dans un autre répertoire sous un autre nom: ./blue/red/hi.test. Comment puis-je appliquer le correctif susmentionné au hi.testfichier? J'ai essayé git am --directory='blue/red' < patch_filemais cela se plaint bien sûr que les fichiers ne sont pas nommés de la même manière (ce qui ne me préoccupait pas de Git?). Je sais que je pourrais probablement modifier le diff pour l'appliquer à ce fichier spécifique, mais je recherche une solution de commande.

mart1n
la source
Lié à: stackoverflow.com/q/3367254/1959808
Ioannis Filippidis

Réponses:

97

Vous pouvez créer le correctif à l'aide de git diff, puis l'appliquer à l'aide de l' patchutilitaire, qui vous permet de spécifier le fichier auquel vous souhaitez appliquer la différence.

Par exemple:

cd first-repo
git diff HEAD^ -- hello.test > ~/patch_file

cd ../second-repo
patch -p1 blue/red/hi.test ~/patch_file
Georgebrock
la source
7
Ah, gentil, je n'ai pas pensé à ça. Cependant, y a-t-il un moyen de le faire avec les commandes Git afin que les données de validation (date et heure, auteur de la validation, message de validation) restent les mêmes?
mart1n
2
Il est possible que vous puissiez faire quelque chose avec amou apply, mais je ne le trouve pas. Si vous vous retrouvez à dupliquer beaucoup les modifications, il pourrait y avoir une meilleure solution en utilisant des sous-modules, ou tout ce que votre choix de langue fournit pour partager du code (par exemple, dans Ruby, vous pouvez extraire le code dupliqué sous forme de gemme).
georgebrock
1
Ceci est en fait lié à la documentation (les fichiers sources sont des XML). Les sous-modules ne sont pas vraiment une option, car je devrais les défendre avec force dans notre infrastructure existante.
mart1n
52

Il existe une solution simple qui n'implique pas d'édition manuelle de patch ni de script externe.

Dans le premier référentiel (cela peut également exporter une plage de commit, utilisez -1si vous ne voulez sélectionner qu'un seul commit):

git format-patch --relative <committish> --stdout > ~/patch

Dans le deuxième référentiel:

git am --directory blue/red/ ~/patch

Au lieu d'utiliser --relativein git format-patch, une autre solution consiste à utiliser l' -p<n>option in git ampour supprimer les nrépertoires du chemin des correctifs, comme mentionné dans une réponse à une question similaire .

Il est également possible de s'exécuter git format-patch --relative <committish>sans le --stdout, et cela générera un ensemble de .patchfichiers. Ces fichiers peuvent ensuite être alimentés directement git amavec git am --directory blue/red/ path/to/*.patch.

magiraud
la source
9
Cela repose toujours sur le fait que les noms de fichiers sont les mêmes, non?
mart1n
3
Il convient de noter que l' --directoryoption semble vous obliger à spécifier le chemin complet du répertoire par rapport à la racine du dépôt; quelque chose comme --directory=./while chdir'd dans un sous-répertoire du dépôt ne fonctionnera pas.
Reid
1
Utiliser des --3wayaides avec does not exist in index:git am --3way --directory (relative-path) (patch)
Brent Bradburn
Utilisez la -kclé dans les deux commandes pour ne pas supprimer la première ligne du message de validation.
ruvim
L'utilisation --3waynon seulement aide à résoudre les erreurs "n'existe pas dans l'index" (comme indiqué par @nobar), mais vous permet également de gérer proprement les conflits de fusion. Au lieu de laisser les fichiers en conflit intacts, un bloc de conflit est ajouté qui peut ensuite être résolu.
Daniel Wolf
11

Répondre à ma propre question avec un script qui fait exactement cela: https://github.com/mprpic/apply-patch-to-file

Plutôt que de modifier le fichier de correctif manuellement, il demande à l'utilisateur le fichier cible, modifie le correctif et l'applique au dépôt dans lequel vous vous trouvez actuellement.

mart1n
la source
5

Sur la base de la réponse de @georgebrock, voici une solution que j'ai utilisée:

Commencez par créer les fichiers de patch comme d'habitude (par exemple git format-patch commitA..commitB).

Assurez-vous ensuite que votre référentiel cible est propre (il ne devrait y avoir aucun fichier modifié ou non suivi) et appliquez les correctifs comme ceci:

cd second-repo
git am ~/00*.patch

Pour chaque fichier de correctif, vous obtiendrez une erreur du type "erreur: XYZ n'existe pas dans l'index". Vous pouvez maintenant appliquer ce fichier de correctif manuellement:

patch --directory blue/red < ~/0001-*.patch
git add -a
git am --continue

Vous devez effectuer ces trois étapes pour chaque fichier de correctif.

Cela conservera le message de validation d'origine, etc. sans nécessiter de git format-patchcommande spéciale ni modifier les fichiers de patch.

oliver
la source
1
Bonne réponse, je pense que c'est la meilleure base pour tout type de manipulation de patch "non standard". Je le fais en 3 étapes. (1) S'engager à écrire -git format-patch -1 commitA --stdout > thing.diff ; (2) Modifiez le fichier de patch jusqu'à ce qu'il fasse ce dont j'ai besoin; (3) Texte à valider git am --3way thing.diff qui présente l'avantage que vous pouvez accepter les parties du correctif qui s'appliquent proprement, et utiliser gitle processus de résolution de conflit standard pour les parties qui ne le font pas.
We Are All Monica
2

Je comprends que les deux fichiers sont exactement les mêmes dans votre situation, donc le correctif est susceptible de réussir.

Cependant, si vous souhaitez appliquer un correctif à un fichier similaire, mais pas exactement le même, ou si vous souhaitez effectuer un correctif interactif, vous utiliserez la fusion à trois.

Supposons que vous ayez modifié le fichier A, désignons A~1la version précédente et que vous souhaitiez appliquer la différence entre A~1à et Aau fichier B.

Ouvrez un outil de fusion à trois voies, par exemple Au-delà de la comparaison, le chemin du panneau de gauche est A, le panneau du milieu est l'ancêtre commun donc le chemin est A~1, le chemin du panneau de droite est B. Ensuite, le panneau inférieur affiche le résultat de l'application de la différence entre A~1à Adans un fichier B.

La figure suivante illustre l'idée.

entrez la description de l'image ici

Gqqnbig
la source
0

FYI: J'ai récemment eu des problèmes en essayant de télécharger un correctif depuis Github et de l'appliquer à un fichier local (qui était un «remplacement» dans un nouvel emplacement).

git amn'appliquerait pas le correctif non plus parce que le fichier n'était «pas dans l'index» ou «sale». Mais, j'ai trouvé que la simple patchcommande pouvait appliquer le correctif. Cela m'a demandé le nom du fichier à corriger.

J'ai fait le travail, de toute façon ...

Mike Robinson
la source