740 votes

Comment choisir une série de commits et fusionner dans une autre branche ?

J'ai la disposition de dépôt suivante :

  • branche master (production)
  • intégration
  • travail

Ce que je veux faire, c'est choisir une série de commits de la branche de travail et les fusionner dans la branche d'intégration. Je suis assez novice en matière de git et je n'arrive pas à comprendre comment faire exactement cela (le "cherry picking" de plages de commits en une seule opération, pas la fusion) sans perturber le référentiel. Avez-vous des conseils ou des idées à ce sujet ? Merci !

1 votes

2 votes

Le TLDR ; est : git cherry-pick <one-sha-before-the-oldest-sha-to-pick>..<sha-of-latest-to-p‌​ick>

4voto

Saik0 Points 346

$ git cherry-pick start_commit_sha_id ^.. end_commit_sha_id

par exemple git cherry-pick 3a7322ac^..7d7c123c

En supposant que vous êtes sur branchA où vous voulez choisir les commits ( le SHA de début et de fin de la plage est donné et le SHA de gauche est plus ancien. ) de branchB . L'ensemble des commits (les deux inclus) sera sélectionné dans la liste suivante branchA .

Le site exemples données dans la documentation officielle sont très utiles.

3voto

Toutes les options ci-dessus vous inviteront à résoudre les conflits de fusion. Si vous fusionnez des modifications effectuées par une équipe, il est difficile de résoudre les conflits de fusion avec les développeurs et de poursuivre. Cependant, "git merge" fera la fusion en une seule fois mais vous ne pouvez pas passer une plage de révisions comme argument. Nous devons utiliser les commandes "git diff" et "git apply" pour faire la fusion d'une plage de révisions. J'ai observé que "git apply" échouera si le fichier de patch a diff pour trop de fichier, donc nous devons créer un patch par fichier et ensuite appliquer. Notez que le script ne sera pas en mesure de supprimer les fichiers qui sont supprimés dans la branche source. C'est un cas rare, vous pouvez supprimer manuellement de tels fichiers dans la branche cible. Le statut de sortie de "git apply" n'est pas zéro s'il n'est pas en mesure d'appliquer le patch, cependant si vous utilisez l'option -3way, il retombera en fusion à 3 voies et vous n'aurez pas à vous soucier de cet échec.

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"

3voto

phili_b Points 86

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

Mes pas

Début

  • Branche dev : A B C D E F G H I J
  • Branche target : A B C D
  • Je ne veux pas E ni H

Étapes pour copier les caractéristiques sans l'étape 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 D où j'ai mis drop devant E et H dans l'éditeur de rebase
  • résoudre les conflits, poursuivre et commit

Etapes pour copier la branche dev_feature_wo_E_H sur la cible.

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

Quelques remarques

  • J'ai fait ça à cause de trop cherry-pick dans les jours qui précèdent
  • git cherry-pick est puissant et simple mais

    • cela crée des commits en double
    • et quand je le veux merge Je dois résoudre les conflits des commits initiaux et des commits dupliqués, donc pour un ou deux cherry-pick Pour les branches, il est possible de faire du "cherry-picking", mais pour plus, c'est trop verbeux et la branche deviendra trop complexe.
  • Dans mon esprit, les étapes que j'ai franchies sont plus claires que git rebase --onto

1 votes

Bon article. Upvoted. Il me rappelle stackoverflow.com/a/38418941/6309 . J'ai récapitulé les inconvénients du "cherry-picking" en 2012 : stackoverflow.com/a/13524494/6309 .

2voto

kxr Points 31

git cherry-pick FIRST^..LAST ne fonctionne que pour des scénarios simples.

Pour obtenir un "merge it into the integration branch" décent tout en ayant le confort habituel avec des choses comme l'auto-skipping des picks déjà intégrés, la transplantation des diamond-merges, le contrôle interactif ...) mieux vaut utiliser un rebase. Une réponse ici a pointé vers cela, cependant le protocole incluait un délicat git branch -f et un jonglage avec une branche temporaire. Voici une méthode directe et robuste :

git rebase -i FIRST LAST~0 --onto integration
git rebase @ integration

Le site -i permet un contrôle interactif. Le site ~0 assure un rebasement détaché (sans déplacer le / d'une autre branche) dans le cas où LAST est un nom de branche. Il peut être omis sinon. La deuxième commande de rebasement déplace simplement le integration La branche ref en toute sécurité vers la tête détachée intermédiaire - elle n'introduit pas de nouveaux commits. Pour rebaser une structure complexe avec des diamants de fusion etc. considérez --rebase-merges ou --rebase-merges=rebase-cousins dans le premier rebasement.

1voto

user267129 Points 1

Une autre option pourrait être de fusionner avec la stratégie ours jusqu'au commit précédant la plage et ensuite une fusion 'normale' avec le dernier commit de cette plage (ou une branche quand c'est le dernier). Supposons donc que seuls 2345 et 3456 commits de master soient fusionnés dans la branche feature :

master:
1234
2345
3456
4567

dans la branche des fonctionnalités :

git merge -s ours 4567
git merge 2345

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X