Comment choisir une gamme de validations et fusionner dans une autre branche?

640

J'ai la disposition de référentiel suivante:

  • branche maître (production)
  • l'intégration
  • travail

Ce que je veux réaliser, c'est de sélectionner une gamme de validations de la branche de travail et de la fusionner dans la branche d'intégration. Je suis assez nouveau sur git et je ne peux pas comprendre comment faire exactement cela (la sélection des plages de commit en une seule opération et non la fusion) sans gâcher le référentiel. Des conseils ou des réflexions à ce sujet? Merci!

crazybyte
la source
1
draconianoverlord.com/2013/09/07/no-cherry-picking.html (pas mon blog)
Christophe Roussy

Réponses:

809

En ce qui concerne une gamme de commits, la sélection des cerises est n'était pas pratique.

Comme mentionné ci-dessous par Keith Kim , Git 1.7.2+ a introduit la possibilité de sélectionner une gamme de validations (mais vous devez toujours être conscient des conséquences de la sélection à la cerise pour une fusion future )

git cherry-pick "a appris à choisir une gamme de commits
(par exemple" cherry-pick A..B"et" cherry-pick --stdin")," git revert"; ils ne supportent pas le meilleur contrôle de séquençage" rebase [-i]".

damian commente et nous prévient:

Dans le cherry-pick A..Bformulaire " ", Adoit être plus ancien queB .
S'ils sont du mauvais ordre, la commande échouera silencieusement .

Si vous voulez choisir la plage à Btravers D( y compris) ce serait B^..D.
Voir " Git create branch from range of previous commits? " Comme illustration.

Comme le mentionne Jubobs dans les commentaires :

Cela suppose que ce Bn'est pas un commit racine; unknown revisionsinon, vous obtiendrez une erreur " ".

Remarque: à partir de Git 2.9.x / 2.10 (Q3 2016), vous pouvez sélectionner une plage de commit directement sur une branche orpheline (tête vide): voir " Comment faire d'une branche existante orpheline dans git ".


Réponse originale (janvier 2010)

Un rebase --ontoserait mieux, où vous rejouez la plage de validation donnée au-dessus de votre branche d'intégration, comme Charles Bailey l'a décrit ici .
(recherchez également "Voici comment vous transplanteriez une branche de sujet basée sur une branche à une autre" dans la page de manuel de git rebase , pour voir un exemple pratique de git rebase --onto)

Si votre branche actuelle est l'intégration:

# Checkout a new temporary branch at the current location
git checkout -b tmp

# Move the integration branch to the head of the new patchset
git branch -f integration last_SHA-1_of_working_branch_range

# Rebase the patchset onto tmp, the old location of integration
git rebase --onto tmp first_SHA-1_of_working_branch_range~1 integration

Cela rejouera tout entre:

  • après le parent de first_SHA-1_of_working_branch_range(d'où le ~1): le premier commit que vous souhaitez rejouer
  • jusqu'à " integration" (qui pointe vers le dernier commit que vous souhaitez rejouer, depuis la workingbranche)

à " tmp" (qui indique où integrationpointait auparavant)

En cas de conflit lors de la relecture de l'un de ces validations:

  • soit le résoudre et exécuter " git rebase --continue".
  • ou ignorer ce correctif et exécuter à la place " git rebase --skip"
  • ou annuler le tout avec un " git rebase --abort" (et remettre la integrationbranche sur la tmpbranche)

Après cela rebase --onto, integrationsera de retour au dernier commit de la branche d'intégration (c'est-à-dire " tmp" branche + tous les commits rejoués)

Avec la cueillette des cerises ou rebase --onto, n'oubliez pas que cela a des conséquences sur les fusions ultérieures, comme décrit ici .


Une cherry-picksolution " " pure est discutée ici et impliquerait quelque chose comme:

Si vous souhaitez utiliser une approche par patch, "git format-patch | git am" et "git cherry" sont vos options.
À l' heure actuelle, git cherry-pickaccepte un seul commit, mais si vous voulez choisir la plage Bpar Dce serait B^..Ddans git Lingo, donc

git rev-list --reverse --topo-order B^..D | while read rev 
do 
  git cherry-pick $rev || break 
done 

Mais de toute façon, lorsque vous devez "rejouer" une série de validations, le mot "rejouer" devrait vous pousser à utiliser la fonctionnalité " rebase" de Git.

VonC
la source
1
Si vous avez des commits dont les parents ont besoin de l' -moption, comment gérez-vous ces commits? Ou existe-t-il un moyen de filtrer ces commits?
août
@aug -mest censé les gérer pour vous, en sélectionnant la ligne principale référencée par le -mparamètre que vous avez choisi pour cette sélection.
VonC
Le truc, c'est que si vous choisissez une gamme de validations, il sélectionne correctement les validations parent mais quand il atteint un commit normal, il échoue et dit que le commit n'est pas une fusion. Je suppose que ma question est mieux formulée comment lui faire passer l' -moption uniquement quand elle frappe un commit parent lors de la sélection de cerises de commits? En ce moment , si je passe -mcomme git cherry-pick a87afaaf..asfa789 -m 1il applique à tous les commits dans la gamme.
août
@aug Strange, je n'ai pas reproduit le problème. Quelle est votre version de git et quel est le message d'erreur exact que vous voyez?
VonC
Ah, je lance la version 2.6.4 de git (Apple Git-63). L'erreur que je vois serait quelque chose comme error: Commit 8fcaf3b61823c14674c841ea88c6067dfda3af48 is a merge but no -m option was given.je réellement réalisé que vous pourriez juste git cherry-pick --continueet ce serait bien (mais il ne comprendrait pas l'engagement des parents)
août
136

Depuis git v1.7.2, Cherry Pick peut accepter une gamme de commits:

git cherry-pickappris à choisir une gamme de commits (par exemple, cherry-pick A..Bet cherry-pick --stdin), a fait de même git revert; rebase [-i]Cependant, ceux-ci ne prennent pas en charge le contrôle de séquençage plus agréable .

Keith Kim
la source
103
Notez que cherry-pick A..Ble commit A ne sera pas obtenu (vous en aurez besoin A~1..B), et s'il y a des conflits, git ne continuera pas automatiquement comme rebase (au moins à partir de 1.7.3.1)
Gabe Moothart
3
Il est également bon de noter que git cherry-pick A..B Ccela ne fonctionne pas comme vous vous en doutez, naïvement. Il ne choisira pas tout dans la gamme A..Bet s'engage C! Pour ce faire, vous devez diviser en deux lignes, tout d' abord git cherry-pick A..B, puis git cherry-pick C. Ainsi, chaque fois que vous avez une plage, vous devez l'exécuter séparément.
MicroVirus
42

Supposons que vous ayez 2 branches,

"branchA": inclut les validations que vous souhaitez copier (de "commitA" à "commitB"

"branchB": la branche dont vous souhaitez que les validations soient transférées de "branchA"

1)

 git checkout <branchA>

2) obtenir les ID de "commitA" et "commitB"

3)

git checkout <branchB>

4)

git cherry-pick <commitA>^..<commitB>

5) En cas de conflit, résolvez-le et saisissez

git cherry-pick --continue

de poursuivre le processus de sélection.

KostasA
la source
A travaillé comme un charme! Merci beaucoup!
snukone
28

Êtes-vous sûr de ne pas vouloir réellement fusionner les branches? Si la branche de travail a des validations récentes que vous ne voulez pas, vous pouvez simplement créer une nouvelle branche avec une HEAD au point souhaité.

Maintenant, si vous voulez vraiment choisir une gamme de commits, pour une raison quelconque, une façon élégante de le faire est de simplement extraire un jeu de patchs et de l'appliquer à votre nouvelle branche d'intégration:

git format-patch A..B
git checkout integration
git am *.patch

C'est essentiellement ce que git-rebase fait de toute façon, mais sans avoir besoin de jouer à des jeux. Vous pouvez ajouter --3wayà git-amsi vous avez besoin de fusionner. Assurez-vous qu'il n'y a pas d'autres fichiers * .patch déjà dans le répertoire où vous faites cela, si vous suivez les instructions textuellement ...

djs
la source
1
Notez que, comme pour les autres plages de révision, il doit être A^inclus A.
Gino Mempin
9

J'ai encapsulé le code de VonC dans un court script bash git-multi-cherry-pick, pour une exécution facile:

#!/bin/bash

if [ -z $1 ]; then
    echo "Equivalent to running git-cherry-pick on each of the commits in the range specified.";
    echo "";
    echo "Usage:  $0 start^..end";
    echo "";
    exit 1;
fi

git rev-list --reverse --topo-order $1 | while read rev 
do 
  git cherry-pick $rev || break 
done 

J'utilise actuellement cela alors que je reconstruis l'historique d'un projet qui avait à la fois du code tiers et des personnalisations mélangés dans le même tronc svn. Je sépare maintenant le code tiers principal, les modules tiers et les personnalisations sur leurs propres branches git pour une meilleure compréhension des personnalisations à venir. git-cherry-pickest utile dans cette situation car j'ai deux arbres dans le même référentiel, mais sans ancêtre partagé.

Adam Franco
la source
3

Toutes les options ci-dessus vous inviteront à résoudre les conflits de fusion. Si vous fusionnez des modifications validées pour une équipe, il est difficile de résoudre les conflits de fusion des développeurs et de continuer. Cependant, "git merge" fera la fusion en une seule fois mais vous ne pouvez pas passer une série de révisions en argument. nous devons utiliser les commandes "git diff" et "git apply" pour faire la plage de fusion des tours. J'ai remarqué que "git apply" échouera si le fichier de correctif présente des différences pour un trop grand nombre de fichiers, nous devons donc créer un correctif par fichier, puis appliquer. Notez que le script ne pourra pas supprimer les fichiers supprimés dans la branche source. Ceci est un cas rare, vous pouvez supprimer manuellement ces fichiers de la branche cible. Le statut de sortie de "git apply" n'est pas nul s'il n'est pas en mesure d'appliquer le patch,

Voici le script.

enter code here



  #!/bin/bash

    # This script will merge the diff between two git revisions to checked out branch
    # Make sure to cd to git source area and checkout the target branch
    # Make sure that checked out branch is clean run "git reset --hard HEAD"


    START=$1
    END=$2

    echo Start version: $START
    echo End version: $END

    mkdir -p ~/temp
    echo > /tmp/status
    #get files
    git --no-pager  diff  --name-only ${START}..${END} > ~/temp/files
    echo > ~/temp/error.log
    # merge every file
    for file in `cat  ~/temp/files`
    do
      git --no-pager diff --binary ${START}..${END} $file > ~/temp/git-diff
      if [ $? -ne 0 ]
      then
#      Diff usually fail if the file got deleted 
        echo Skipping the merge: git diff command failed for $file >> ~/temp/error.log
        echo Skipping the merge: git diff command failed for $file
        echo "STATUS: FAILED $file" >>  /tmp/status
        echo "STATUS: FAILED $file"
    # skip the merge for this file and continue the merge for others
        rm -f ~/temp/git-diff
        continue
      fi

      git apply  --ignore-space-change --ignore-whitespace  --3way --allow-binary-replacement ~/temp/git-diff

      if [ $? -ne 0 ]
       then
#  apply failed, but it will fall back to 3-way merge, you can ignore this failure
         echo "git apply command filed for $file"
       fi
       echo
       STATUS=`git status -s $file`


       if [ ! "$STATUS" ]
       then
#   status is null if the merged diffs are already present in the target file
         echo "STATUS:NOT_MERGED $file"
         echo "STATUS: NOT_MERGED $file$"  >>  /tmp/status
       else
#     3 way merge is successful
         echo STATUS: $STATUS
         echo "STATUS: $STATUS"  >>  /tmp/status
       fi
    done

    echo GIT merge failed for below listed files

    cat ~/temp/error.log

    echo "Git merge status per file is available in /tmp/status"
Yoganand Bijapur
la source
2

Je l'ai testé il y a quelques jours, après avoir lu l'explication très claire de Vonc.

Mes pas

Début

  • Succursale dev: ABCDEFGHIJ
  • Succursale target: ABCD
  • Je ne veux pas EniH

Étapes pour copier des fonctionnalités sans les étapes E et H dans la branche dev_feature_wo_E_H

  • git checkout dev
  • git checkout -b dev_feature_wo_E_H
  • git rebase --interactive --rebase-merges --no-ff Doù je mets dropdevant Eet Hdans l'éditeur de rebase
  • résoudre les conflits, continuer et commit

Étapes pour copier la branche dev_feature_wo_E_Hsur la cible.

  • git checkout target
  • git merge --no-ff --no-commit dev_feature_wo_E_H
  • résoudre les conflits, continuer et commit

Quelques remarques

  • Je l'ai fait à cause de trop de choses cherry-pickles jours précédents
  • git cherry-pick est puissant et simple mais

    • il crée des doublons commet
    • et quand je veux, mergeje dois résoudre les conflits des validations initiales et des doublons, donc pour un ou deux cherry-pick, c'est OK de "choisir" mais pour plus c'est trop verbeux et la branche deviendra trop complexe
  • Dans mon esprit, les étapes que j'ai accomplies sont plus claires que git rebase --onto
phili_b
la source
1
Bon post. A voté. Cela me rappelle stackoverflow.com/a/38418941/6309 . J'ai récapitulé les inconvénients de la cueillette des cerises en 2012: stackoverflow.com/a/13524494/6309 .
VonC
1

Une autre option pourrait être de fusionner avec la nôtre de la stratégie à la validation avant la plage, puis une fusion «normale» avec la dernière validation de cette plage (ou une branche lorsqu'elle est la dernière). Supposons donc que seuls 2345 et 3456 commits de master soient fusionnés dans la branche de fonctionnalité:

Maître:
1234
2345
3456
4567

dans la branche de fonctionnalité:

git merge -s ours 4567
git merge 2345
Koos Vriezen
la source