web-dev-qa-db-fra.com

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

J'ai fait quelques changements dans ma branche principale et je veux les mettre en amont. lorsque 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 cherry-pick est la bonne chose à utiliser ici? La branche principale inclut les modifications apportées aux fichiers qui ont été modifiés dans la branche en amont. Par conséquent, je suis sûr qu'il y aura des conflits de fusion, mais ils ne sont pas trop difficiles à résoudre. Je sais quels changements sont nécessaires où.

Ce sont les commits que je veux mettre 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 ...
376
wufoo

La manière dont une cueillette choisit de fonctionner consiste à prendre le diff représenté par un changeset (la différence entre l'arbre de travail de ce point et celui de son parent) et à l'appliquer à votre branche actuelle.

Ainsi, si un commit a deux parents ou plus, il représente également deux diffs ou plus - lequel doit être appliqué?

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

Je ne peux pas dire avec certitude pour votre situation particulière, mais l'utilisation de git merge au lieu de git cherry-pick est généralement recommandée. Lorsque vous sélectionnez une validation de fusion, elle réduit tout les modifications apportées au parent que vous n'avez pas spécifiées à -m dans cette une validation. Vous perdez toute leur histoire et regardez ensemble leurs diffs. Ton appel.

464
Borealid

La réponse de @ Borealid est correcte, mais supposons que vous ne vous souciez pas de préserver l'historique de fusion exact d'une branche et que vous souhaitiez simplement sélectionner une version linéarisée de celle-ci. Voici un moyen facile et sûr de le faire:

Etat de départ: vous êtes sur la branche X et 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. (facultatif) git branch -D tempZ

Cela crée une branche tempZ basée sur Z, mais avec l'historique de Y suivant, puis linéarisée, puis sélectionne celle-ci sur une copie de X appelée newX. (Il est préférable de le faire sur une nouvelle branche plutôt que de muter X.) Bien sûr, il pourrait y avoir des conflits à l'étape 4, que vous devrez résoudre de la manière habituelle (cherry-pick fonctionne très bien comme rebase à cet égard). Enfin, il supprime la branche temporaire tempZ.

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

Puis examinez newX et voyez si cela répond à vos attentes.

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

20
Daira Hopwood

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

Vous essayez de choisir fd9f578, une fusion entre deux parents.

Au lieu de choisir une fusion, la solution la plus simple consiste à sélectionner le ou les commit (s) que vous voulez réellement dans chaque branche de la fusion.

Comme vous avez déjà fusionné, il est probable que tous les commits souhaités figurent dans votre liste. Choisissez-les directement, sans avoir à vous soucier de la validation de la fusion.

explication

Le choix d'un jeu de recherche s'effectue en prenant le diff représenté par un ensemble de modifications (la différence entre l'arbre de travail de ce point et celui de son parent) et en appliquant le groupe de modifications à votre branche actuelle. 

Si un commit a deux parents ou plus, comme dans le cas d'une fusion, ce commit représente également deux diffs ou plus. L'erreur se produit en raison de l'incertitude sur laquelle diff devrait s'appliquer.

des alternatives

Si vous déterminez que vous devez inclure la fusion et la sélection sélective des commits associés, vous avez deux options:

  1. (Plus compliqué et plus obscur; également l'historique des rejets), vous pouvez indiquer quel parent devrait postuler.

    • Utilisez l'option -m pour le faire. Par exemple, git cherry-pick -m 1 fd9f578 utilisera le premier parent répertorié dans la fusion en tant que base.

    • Pensez également que lorsque vous sélectionnez une validation de fusion, elle réduit tous les modifications apportées au parent que vous n'avez pas spécifiées à -m dans cette une validation. Vous perdez toute leur histoire et regardez ensemble leurs diffs. Ton appel.

  2. (Plus simple et plus familier; conserve l’historique), vous pouvez utiliser git merge au lieu de git cherry-pick.

    • Comme d'habitude avec git merge, il tentera d'appliquer tous les commits existants dans la branche que vous fusionnez et les listera individuellement dans votre journal git.
5
Kay V

-m signifie le numéro du parent.

De la doc git: 

En règle générale, 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 parent (à partir de 1) de la ligne principale et permet à cherry-pick de rejouer la modification relative au parent spécifié.

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

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

alors git cherry-pick E produira le problème que vous avez rencontré.

git cherry-pick E -m 1 signifie utiliser D-E, alors que git cherry-pick E -m 2 signifie utiliser B-C-E.

3
gavincook

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

Dans le cas de l'auteur:

  • Z est recherché (commit) (fd9f578) 
  • Y est commis avant cela 
  • X branche de travail 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
0
ephemerr