Choisir en partie un commit avec Git

501

Je travaille sur 2 branches différentes: la sortie et le développement .

J'ai remarqué que je dois encore intégrer certaines modifications qui ont été validées dans la branche de publication dans la branche de développement .

Le problème est que je n'ai pas besoin de tout le commit, seulement quelques mecs dans certains fichiers, donc un simple

git cherry-pick bc66559

ne fait pas l'affaire.

Quand je fais un

git show bc66559

Je peux voir la différence, mais je ne connais pas vraiment une bonne façon de l'appliquer partiellement à mon arbre de travail actuel.

Oliver
la source

Réponses:

793

La chose principale que vous voudrez ici est git add -p( -pest un synonyme de --patch). Cela fournit un moyen interactif pour archiver le contenu, vous permettant de décider si chaque morceau doit entrer, et même de vous permettre de modifier manuellement le correctif si nécessaire.

Pour l'utiliser en combinaison avec du cerisier:

git cherry-pick -n <commit> # get your patch, but don't commit (-n = --no-commit)
git reset                   # unstage the changes from the cherry-picked commit
git add -p                  # make all your choices (add the changes you do want)
git commit                  # make the commit!

(Merci à Tim Henigan de m'avoir rappelé que git-cherry-pick a une option --no-commit, et merci à Felix Rabe d'avoir souligné que vous devez réinitialiser! Si vous ne voulez laisser que quelques éléments en dehors du commit , vous pouvez utiliser git reset <path>...pour supprimer uniquement ces fichiers.)

Vous pouvez bien sûr fournir des chemins spécifiques vers add -psi nécessaire. Si vous commencez avec un patch, vous pouvez remplacer le cherry-pickpar apply.


Si vous voulez vraiment un git cherry-pick -p <commit>(cette option n'existe pas), vous pouvez utiliser

git checkout -p <commit>

Cela diffère la validation actuelle par rapport à la validation que vous spécifiez et vous permet d'appliquer des morceaux à partir de cette différence individuellement. Cette option peut être plus utile si la validation que vous tirez contient des conflits de fusion dans une partie de la validation qui ne vous intéresse pas. (Notez cependant que cela checkoutdiffère de cherry-pick: checkoutessaie d'appliquer <commit>entièrement le contenu cherry-pickde la validation spécifiée de son parent. Cela signifie que vous checkoutpouvez appliquer plus que cette validation, qui peut être supérieure à celle que vous souhaitez.)

Cascabel
la source
En fait, si je suis le conseil de git 1.7.5.4, 'git add -p' dit 'No changes' car tout est déjà dans l'index. Je dois faire un 'git reset HEAD' avant 'git add' - comment éviter cette réinitialisation avec une option?
Felix Rabe
@FelixRabe: Je suis en fait surpris qu'à un moment donné, cherry-pick -napparemment , les modifications n'aient pas été mises en scène - la convention est définitivement que les --no-commitoptions s'arrêtent juste avant la validation, c'est-à-dire avec toutes les modifications mises en scène. Je vais ajouter la réinitialisation dans la réponse.
Cascabel
1
@Blixt Autrement dit, si les commits qui sont sur l'autre branche , mais pas votre branche actuelle sont ABCDEF, git checkout -p <F>ne pas seulement vous obtenir les changements de F, il vous permet de vous ABCDEF tous ensemble et réduites en purée vous permet de trier quelle partie de ce que vous voulez . Réduire cela à quelques-uns des changements de F est une douleur. D'un autre côté, git cherry-pick -n <F>vous obtenez uniquement les modifications de F - et si certaines de ces modifications entrent en conflit, il vous indique utilement afin que vous puissiez comprendre comment fusionner correctement.
Cascabel
J'ai eu des problèmes avec cela lorsque le changement était un ajout de fichier. Le git resetsupprimerait les fichiers intermédiaires et le add -pdirait simplement «rien à ajouter».
Ian Grainger
36

En supposant que les modifications que vous souhaitez sont à la tête de la branche dont vous souhaitez les modifications, utilisez git checkout

pour un seul fichier:

git checkout branch_that_has_the_changes_you_want path/to/file.rb

pour plusieurs fichiers, connectez simplement en guirlande:

git checkout branch_that_has_the_changes_you_want path/to/file.rb path/to/other_file.rb
Jay Swain
la source
5
Cela copie tout le fichier: pas seulement les changements.
Jeremy List
1
Cette réponse, comme toute autre réponse jusqu'ici, a un gros inconvénient: elle ne préserve pas l'auteur d'origine de la modification, mais s'engage à votre place. Si un changement est bon, vous volez le crédit de quelqu'un, si un changement est mauvais, vous vous mettez le feu. Il semble qu'il n'y ait aucun moyen d'avoir git cherry-pick -p, et dommage qu'il ne soit toujours pas là.
pfalcon
15

En vous appuyant sur la réponse de Mike Monkiewicz, vous pouvez également spécifier un ou plusieurs fichiers à extraire de la branche sha1 / branchée fournie.

git checkout -p bc66559 -- path/to/file.java 

Cela vous permettra de sélectionner de manière interactive les modifications que vous souhaitez appliquer à votre version actuelle du fichier.

Christian.D
la source
5
Cela pose problème si la version actuelle du fichier est sensiblement différente de cette version. Vous serez invité à effectuer de nombreuses modifications non pertinentes (qui n'apparaissent pas dans la validation). En outre, les modifications que vous souhaitez réellement peuvent apparaître sous une "forme déguisée" en tant que différence par rapport à la différence actuelle, pas la différence d'origine. Il est possible que la différence d'origine que vous souhaitez soit en conflit et doit être correctement fusionnée; vous n'aurez pas cette opportunité ici.
Kaz
1

Si vous souhaitez spécifier une liste de fichiers sur la ligne de commande et obtenir le tout en une seule commande atomique, essayez:

git apply --3way <(git show -- list-of-files)

--3way: Si un correctif ne s'applique pas correctement, Git créera un conflit de fusion afin que vous puissiez l'exécuter git mergetool. L'omission obligera --3wayGit à abandonner les correctifs qui ne s'appliquent pas proprement.

nyanpasu64
la source
0

Si "cueillette en partie" signifie "dans les fichiers, en choisissant certaines modifications mais en en supprimant d'autres", cela peut être fait en introduisant git stash:

  1. Faites le plein choix de cerises.
  2. git reset HEAD^ pour convertir l'intégralité du commit choisi par la cerise en changements de travail non organisés.
  3. Maintenant git stash save --patch : sélectionnez interactivement le matériel indésirable à ranger.
  4. Git annule les modifications cachées de votre copie de travail.
  5. git commit
  6. Jeter la réserve de modifications non souhaitées: git stash drop.

Astuce: si vous donnez un nom à la cachette des modifications indésirables: git stash save --patch junkalors si vous oubliez de faire (6) maintenant, plus tard, vous reconnaîtrez la cachette pour ce qu'elle est.

Kaz
la source
Si vous venez de choisir, puis réinitialisez ... vous n'aurez rien à cacher. Comme indiqué ci-dessus, vous devez soit faire un choix avec --no-commit, soit réinitialiser --hard HEAD ~. Notez que vous procédez de manière négative (en sélectionnant ce que vous ne voulez pas). La solution acceptée ci-dessus permet une approche positive ou négative.
Pierre-Olivier Vares
0

Utilisez git format-patchpour découper la partie du commit qui vous intéresse et git ampour l'appliquer à une autre branche

git format-patch <sha> -- path/to/file
git checkout other-branch
git am *.patch
adrock20
la source