Comment fusionner un commit spécifique dans Git

1035

J'ai bifurqué une branche à partir d'un référentiel dans GitHub et j'ai commis quelque chose de spécifique pour moi. Maintenant, j'ai trouvé que le dépôt d'origine avait une bonne fonctionnalité qui était à HEAD.

Je veux le fusionner uniquement sans commits précédents. Que devrais-je faire? Je sais comment fusionner tous les commits:

git branch -b a-good-feature
git pull repository master
git checkout master
git merge a-good-feature
git commit -a
git push
netawater
la source
Si vous essayez de le faire en relation avec github, cet article vous guidera. markosullivan.ca/how-to-handle-a-pull-request-from-github
johndpope

Réponses:

1177

' git cherry-pick' devrait être votre réponse ici.

Appliquez la modification introduite par un commit existant.

N'oubliez pas de lire la réponse de bdonlan sur les conséquences de la sélection des cerises dans ce post:
"Tirez tous les commits d'une branche, poussez les commits spécifiés vers une autre" , où:

A-----B------C
 \
  \
   D

devient:

A-----B------C
 \
  \
   D-----C'

Le problème avec ce commit est que git considère que les commit incluent tout l'historique avant eux

Où C 'a un SHA-1ID différent .
De même, choisir un commit d'une branche à une autre implique de générer un patch, puis de l'appliquer, perdant ainsi l'historique de cette manière également.

Ce changement d'ID de commit rompt entre autres la fonctionnalité de fusion de git (bien que s'il est utilisé avec parcimonie, il y a des heuristiques qui vont s'en occuper).
Plus important encore, il ignore les dépendances fonctionnelles - si C a réellement utilisé une fonction définie en B, vous ne le saurez jamais .

VonC
la source
1
@ openid000: "branches plus fines": c'est en effet exactement ce que bdonlan a suggéré dans sa réponse.
VonC
8
Remarque: "git rebase" modifie également SHA-1. Voir aussi "git rebase vs. git merge ( stackoverflow.com/questions/804115/git-rebase-vs-git-merge ) et" git workflow "( stackoverflow.com/questions/457927/… ) pour les cas où" git rebase "est légitime.
VonC
1
Entre "branches à grains fins", "cerise-pick" et "rebase", vous aurez alors toutes les possibilités pour gérer le code dans les branches avec git.
VonC
@VonC "branches à grains fins" oui. J'avais deux succursales et j'avais effectué des mises à niveau vers un module de visualisation particulier dans une succursale. Ce module était indépendant de tout autre code, donc cherry-pick était sacrément pratique pour appliquer ces changements à d'autres branches également
Cheeku
1
Mais notez que lorsque vous fusionnez, les validations C 'et C prévalent dans l'historique des validations.
Rahul Shah
739

Vous pouvez utiliser git cherry-pick pour appliquer un seul commit par lui-même à votre branche actuelle.

Exemple: git cherry-pick d42c389f

bdonlan
la source
68
+1 pour votre ancien article sur la cueillette des cerises ( stackoverflow.com/questions/880957/… ). J'ai pris la liberté d'en copier un extrait dans ma propre réponse ci-dessus.
VonC
4
Probablement git cherry-pick d42cou git cherry-pick d42c3 fonctionnera. Git est intelligent. ;)
guneysus
2
fatal: mauvaise révision
Ievgen Naida
3
Je viens d'exécuter cette commande et cela semble avoir fonctionné, mais quand je fais le statut git, il dit "rien à valider, arbre de travail propre". Lorsque je rafraîchis la page Commits de ma page Web Bitbucket, elle ne s'affiche pas. Mais cela apparaît lorsque j'exécute git log. Et je vois le code modifié. Quelqu'un peut-il expliquer si je dois faire des étapes supplémentaires?
Ray
1
Malheureusement, cela ne répond pas à la question: cela ne crée pas réellement de fusion. Il n'y a aucune ascendance pointant vers d42c389f. Peut-être que le PO ne se souciait pas de créer une fusion en soi, mais la différence importe parfois.
LarsH
29

Essayons de prendre un exemple et de comprendre:

J'ai une branche, disons maître , pointant vers X <commit-id>, et j'ai une nouvelle branche pointant vers Y <sha1>.

Y <commit-id> = <master> branche commits - peu de commits

Maintenant, disons que pour la branche Y, je dois combler les commits entre la branche principale et la nouvelle branche. Voici la procédure que nous pouvons suivre:

Étape 1:

git checkout -b local origin/new

où local est le nom de la branche. N'importe quel nom peut être donné.

Étape 2:

  git merge origin/master --no-ff --stat -v --log=300

Fusionnez les validations de la branche principale vers la nouvelle branche et créez également une validation de fusion du message de journal avec des descriptions d'une seule ligne d'au plus <n> validations réelles qui sont fusionnées.

Pour plus d'informations et de paramètres sur la fusion Git, veuillez vous référer à:

git merge --help

De plus, si vous devez fusionner un commit spécifique, vous pouvez utiliser:

git cherry-pick <commit-id>
Spyder
la source
avez-vous changé la définition de Ydans votre phrase 3d? "J'ai une nouvelle branche pointant vers Y" vs "Maintenant, disons pour la branche Y", on dirait que Y était un commit puis est devenu une branche
Purefan
comment je peux le faire à partir d'un certain point de validation d'une branche que je veux fusionner
Kulbhushan Singh
3

Dans mon cas d'utilisation, nous avions un besoin similaire de CD CI. Nous avons utilisé git flow avec les branches develop et master. Les développeurs sont libres de fusionner leurs modifications directement à développer ou via une demande de pull depuis une branche de fonctionnalité. Cependant, pour maîtriser, nous fusionnons uniquement les validations stables de la branche develop de manière automatisée via Jenkins.

Dans ce cas, la sélection à la cerise n'est pas une bonne option. Cependant, nous créons une branche locale à partir de l'ID de validation, puis fusionnons cette branche locale pour la maîtriser et effectuer une vérification propre mvn (nous utilisons maven). En cas de succès, libérez l'artefact de version de production sur Nexus à l'aide du plug-in de libération Maven avec l'option localCheckout = true et pushChanges = false. Enfin, lorsque tout est réussi, poussez les modifications et le tag à l'origine.

Un exemple d'extrait de code:

En supposant que vous êtes maître si cela est fait manuellement. Cependant, sur jenkins, lorsque vous retirez le dépôt, vous serez sur la branche par défaut (maître si configuré).

git pull  // Just to pull any changes.
git branch local-<commitd-id> <commit-id>  // Create a branch from the given commit-id
git merge local-<commit-id>  // Merge that local branch to master.
mvn clean verify   // Verify if the code is build able
mvn <any args> release:clean release:prepare release:perform // Release artifacts
git push origin/master  // Push the local changes performed above to origin.
git push origin <tag>  // Push the tag to origin

Cela vous donnera un contrôle total avec une fusion sans peur ou un enfer de conflit.

N'hésitez pas à le conseiller en cas de meilleure option.

nrkkalyan
la source
3

Les principales réponses décrivent comment appliquer les modifications d' une validation spécifique à la branche actuelle. Si c'est ce que vous entendez par «comment fusionner», utilisez simplement la sélection de cerises comme ils le suggèrent.

Mais si vous voulez réellement une fusion , c'est-à-dire que vous voulez un nouveau commit avec deux parents - la validation existante sur la branche actuelle et la validation à partir de laquelle vous vouliez appliquer des modifications - alors une sélection de cerise ne fera pas cela.

Avoir un véritable historique de fusion peut être souhaitable, par exemple, si votre processus de génération tire parti de l'ascendance git pour définir automatiquement les chaînes de version en fonction de la dernière balise (à l'aide git describe).

Au lieu de choisir, vous pouvez faire un réel git merge --no-commit, puis ajuster manuellement l'index pour supprimer les modifications que vous ne souhaitez pas.

Supposons que vous soyez sur une branche Aet que vous souhaitiez fusionner le commit à l'extrémité de la branche B:

git checkout A
git merge --no-commit B

Maintenant, vous êtes configuré pour créer un commit avec deux parents, le conseil actuel valide AetB . Cependant, vous pouvez avoir appliqué plus de modifications que vous le souhaitez, y compris les modifications apportées par des validations antérieures sur la branche B. Vous devez annuler ces modifications indésirables, puis valider.

(Il peut y avoir un moyen facile de rétablir l'état du répertoire de travail et de l'index tel qu'il était avant la fusion, de sorte que vous ayez une table blanche sur laquelle choisir le commit que vous vouliez en premier lieu. Mais Je ne sais pas comment obtenir cette table rase git checkout HEADet git reset HEADsupprimera tous les deux l'état de fusion, ce qui ira à l'encontre du but de cette méthode.)

Annulez donc manuellement les modifications indésirables. Par exemple, vous pourriez

git revert --no-commit 012ea56

pour chaque commit indésirable 012ea56.

Lorsque vous avez terminé d'ajuster les choses, créez votre commit:

git commit -m "Merge in commit 823749a from B which tweaked the timeout code"

Maintenant, vous n'avez que le changement que vous vouliez, et l'arbre d'ascendance montre que vous avez fusionné techniquement à partir de B.

LarsH
la source