git cherry-pick dit "... 38c74d est une fusion mais aucune option -m n'a été donnée"

519

J'ai fait quelques changements dans ma branche principale et je veux les apporter en amont. quand je sélectionne les commits suivants, je reste bloqué sur fd9f578 où git dit:

$ git cherry-pick fd9f578
fatal: Commit fd9f57850f6b94b7906e5bbe51a0d75bf638c74d is a merge but no -m option was given.

Qu'est-ce que Git essaie de me dire et est-ce que c'est la bonne chose à utiliser ici? La branche principale inclut des changements aux fichiers qui ont été modifiés dans la branche en amont, donc je suis sûr qu'il y aura des conflits de fusion mais ceux-ci ne sont pas trop mauvais à redresser. Je sais quels changements sont nécessaires où.

Ce sont les engagements que je veux apporter en amont.

e7d4cff added some comments...
23e6d2a moved static strings...
44cc65a incorporated test ...
40b83d5 whoops delete whitspace...
24f8a50 implemented global.c...
43651c3 cleaned up ...
068b2fe cleaned up version.c ...
fd9f578 Merge branch 'master' of ssh://extgit/git/sessions_common
4172caa cleaned up comments in sessions.c ...
wufoo
la source

Réponses:

607

La façon dont fonctionne une sélection consiste à prendre le diff que représente un ensemble de modifications (la différence entre l'arbre de travail à ce point et l'arbre de travail de son parent) et à l'appliquer à votre branche actuelle.

Donc, si un commit a deux parents ou plus, il représente également deux différences ou plus - lequel devrait être appliqué?

Vous essayez de choisir fd9f578, ce qui était une fusion avec deux parents. Vous devez donc indiquer à la commande cherry-pick celle par rapport à laquelle le diff doit être calculé, en utilisant l' -moption. Par exemple, git cherry-pick -m 1 fd9f578pour utiliser le parent 1 comme base.

Je ne peux pas dire avec certitude pour votre situation particulière, mais l'utilisation git mergeau lieu de git cherry-pickest généralement recommandée. Lorsque vous choisissez un commit de fusion, il réduit toutes les modifications apportées au parent que vous n'avez pas spécifié -mdans ce seul commit . Vous perdez toute leur histoire et glomez tous leurs différends. Ton appel.

Borealid
la source
3
@wufoo Vous devriez probablement aussi en savoir plus git rebase- c'est comme une fusion, mais au lieu d'intégrer deux branches, il transplante l'une pour s'asseoir au-dessus de l'autre.
Borealid
92
comment connaissez-vous le numéro de parent?
Anentropic
66
@Anentropic 1 est le "premier parent", 2 est le "deuxième parent", etc. L'ordre est celui dans lequel ils sont répertoriés dans le commit (tels que visualisés par git showet similaires).
Borealid
2
@lkraav Vous auriez également pu faire un git reset --hard HEAD@{1}pour récupérer votre commit manquant. git resetne se limite pas à reculer "dans l'histoire". git checkout -b mybranch HEAD@{1}fonctionnerait également.
Borealid
4
AVERTISSEMENT: git mergepeut avoir des conséquences imprévues. Cette commande ajoutera toutes les autres validations (plus anciennes) qui existent sur la branche parent. Habituellement, les gens choisissent de choisir parce qu'ils ne veulent pas que les autres commettent. Assurez-vous de vérifier que vous n'implémentez que les modifications que vous souhaitez!
Kay V
52

-m signifie le numéro de parent.

Du git doc:

Habituellement, vous ne pouvez pas choisir une fusion car vous ne savez pas quel côté de la fusion doit être considéré comme la ligne principale. Cette option spécifie le numéro du parent (à partir de 1) de la ligne principale et permet à cherry-pick de rejouer la modification par rapport au parent spécifié.

Par exemple, si votre arbre de validation est comme ci-dessous:

- A - D - E - F -   master
   \     /
    B - C           branch one

puis git cherry-pick Eproduira la question que vous face.

git cherry-pick E -m 1signifie utiliser D-E, tout en git cherry-pick E -m 2utilisant B-C-E.

gavincook
la source
32

@ La réponse de Borealid est correcte, mais supposez que vous ne vous souciez pas de conserver l'historique de fusion exact d'une branche et que vous souhaitiez simplement en sélectionner une version linéarisée. Voici un moyen simple et sûr de le faire:

État de départ: vous êtes sur une branche Xet vous souhaitez sélectionner les commits Y..Z.

  1. git checkout -b tempZ Z
  2. git rebase Y
  3. git checkout -b newX X
  4. git cherry-pick Y..tempZ
  5. (optionnel) git branch -D tempZ

Ce que cela fait, c'est de créer une branche tempZbasée sur Z, mais avec l'historique à Ypartir de la linéarisation, puis de la sélectionner sur une copie de Xappelé newX. (Il est plus sûr de le faire sur une nouvelle branche plutôt que de muter X.) Bien sûr, il peut y avoir des conflits à l'étape 4, que vous devrez résoudre de la manière habituelle ( cherry-pickfonctionne très bien rebaseà cet égard). Enfin, il supprime la tempZbranche temporaire .

Si l'étape 2 donne le message "La branche actuelle tempZ est à jour", alors elle Y..Zétait déjà linéaire, alors ignorez simplement ce message et passez aux étapes 3 suivantes.

Ensuite, vérifiez newXet voyez si cela a fait ce que vous vouliez.

(Remarque: ce n'est pas la même chose qu'un simple git rebase Xsur une branche Z, car cela ne dépend en aucune façon de la relation entre Xet Y; il peut y avoir des commits entre l'ancêtre commun et ce Yque vous ne vouliez pas.)

Daira Hopwood
la source
1
git rebase YditCurrent branch tempZ is up to date
Basilevs
Je pense que cela signifie que Y..Zc'était déjà linéaire. Vous pouvez donc ignorer ce message et passer aux étapes 3 et 4.
Daira Hopwood
1
Idée intéressante, j'ai dû la dessiner sur papier pour apprécier pleinement ce qui se passait = D
Chris
2
Brillant. git cherry-pick pour une gamme entière se plaignait soit de l'absence de l'option -m, soit de son utilisation. Votre solution était d'or. (Une suggestion: supprimer la branche tempZ après)
Otheus
1
Ceci est incroyable! j'ai eu du mal avec le choix de cerise et seulement cela avait du sens. J'ai eu un peu de mal avec les lettres choisies (certaines branches et commits sont en majuscules et certaines branches en minuscules)
pcarvalho
19

Simplifier. Choisissez les commits. Ne choisissez pas la fusion.

Voici une réécriture de la réponse acceptée qui clarifie idéalement les avantages / risques des approches possibles:

Vous essayez de choisir le fd9f578, qui était une fusion avec deux parents.

Au lieu de sélectionner une fusion par cerise, la chose la plus simple est de sélectionner par cerise le ou les commit (s) que vous voulez réellement de chaque branche de la fusion.

Puisque vous avez déjà fusionné, il est probable que tous les commits souhaités figurent dans votre liste. Choisissez-les directement et vous n'avez pas besoin de jouer avec le commit de fusion.

explication

La façon dont fonctionne une sélection de cerises consiste à prendre le diff que représente un ensemble de modifications (la différence entre l'arbre de travail à ce point et l'arbre de travail de son parent) et à appliquer l'ensemble de modifications à votre branche actuelle.

Si une validation a deux parents ou plus, comme c'est le cas avec une fusion, cette validation représente également deux différences ou plus. L'erreur se produit en raison de l'incertitude sur laquelle diff devrait s'appliquer.

alternatives

Si vous déterminez que vous devez inclure la fusion par rapport à la sélection des validations associées, vous avez deux options:

  1. (Plus compliqué et obscur; jette également l'historique), vous pouvez indiquer quel parent doit appliquer.

    • Utilisez l' -moption pour le faire. Par exemple, git cherry-pick -m 1 fd9f578utilisera le premier parent répertorié dans la fusion comme base.

    • Considérez également que lorsque vous choisissez un commit de fusion, il réduit toutes les modifications apportées dans le parent que vous n'avez pas spécifié -mdans ce commit . Vous perdez toute leur histoire et glomez tous leurs différends. Ton appel.

  2. (Plus simple et plus familier; conserve l'historique), vous pouvez utiliser à la git mergeplace de git cherry-pick.

    • Comme d'habitude avec git merge, il essaiera d'appliquer toutes les validations qui existent sur la branche que vous fusionnez, et les listera individuellement dans votre journal git.
Kay V
la source
2

Simplification de la méthode @Daira Hopwood idéale pour choisir un seul commit. Pas besoin de branches temporaires.

Dans le cas de l'auteur:

  • Z est un commit voulu (fd9f578)
  • Y est commis avant
  • X branche active actuelle

alors fais:

git checkout Z   # move HEAD to wanted commit
git reset Y      # have Z as changes in working tree
git stash        # save Z in stash
git checkout X   # return to working branch
git stash pop    # apply Z to current branch
git commit -a    # do commit
éphémère
la source
2
Cela perd bien sûr les métadonnées associées au commit d'origine. Je suppose que c'est une question d'opinion que ce soit plus simple. Je l'utilise parfois lorsque je veux perdre les métadonnées et ne conserver que les modifications globales du code. Notez que cela fonctionne même si Y n'est pas un parent immédiat de Z (auquel cas les modifications seront écrasées).
Daira Hopwood