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>

914voto

VonC Points 414372

Lorsqu'il s'agit d'une série de commits, le choix de l'un d'entre eux est était pas pratique.

Comme mentionnés ci-dessous par Keith Kim Git 1.7.2+ a introduit la possibilité d'extraire une série de commits (mais vous devez toujours être conscient de l'existence de l'option d'extraire des commits). les conséquences de la sélection pour les fusions futures )

git cherry-pick" appris à choisir une gamme de commits
(par exemple, " cherry-pick A..B " et " cherry-pick --stdin "), tout comme " git revert "Ces derniers ne supportent pas la commande de séquençage plus agréable " rebase [-i] " l'a fait, cependant.

damian commentaires et nous met en garde :

Dans le " cherry-pick A..B ", A doit être plus ancienne que B .
S'ils sont dans le mauvais ordre, la commande échouera silencieusement. .

Si vous voulez choisir le gamme B par le biais de D (y compris B ) qui serait B^..D (au lieu de B..D ).
Voir " Git crée une branche à partir d'une série de commits précédents ? " à titre d'illustration.

Comme Jubobs mentionne dans les commentaires :

Cela suppose que B n'est pas un commit Root ; vous obtiendrez un " unknown revision Sinon, erreur ".

Note : à partir de Git 2.9.x/2.10 (Q3 2016), vous pouvez choisir une gamme de commit directement sur une branche orpheline (tête vide) : voir " Comment rendre une branche existante orpheline dans git ? ".


Réponse originale (janvier 2010)

A rebase --onto serait mieux, où vous rejouez la gamme donnée de commit sur votre branche d'intégration, comme Charles Bailey décrit ici .
(recherchez également "Voici comment vous transplanteriez une branche de sujet basée sur une branche vers une autre" dans le document 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 ce qui se trouve entre les deux :

  • après le parent de first_SHA-1_of_working_branch_range (d'où le ~1 ) : le premier commit que vous voulez rejouer
  • jusqu'à " integration "(qui pointe vers la dernière commande que vous voulez rejouer, à partir de la page d'accueil de l'application working branche)

à " tmp "(qui indique l'endroit où integration pointait avant)

S'il y a un conflit lorsque l'un de ces commits est rejoué :

  • soit le résoudre et exécuter " git rebase --continue ".
  • ou ignorez ce patch, et exécutez plutôt " git rebase --skip "
  • ou annuler le tout avec un " git rebase --abort "(et remettre le integration sur la tmp branche)

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

Avec le "cherry-picking" ou rebase --onto n'oubliez pas qu'elle a des conséquences sur les fusions ultérieures, comme le montre le tableau ci-dessous. décrit ici .


Un pur " cherry-pick "La solution est discuté ici et il s'agirait de quelque chose comme :

Si vous souhaitez utiliser une approche par patch, "git format-patch|git am" et "git cherry" sont vos options.
Actuellement, git cherry-pick n'accepte qu'un seul commit, mais si vous voulez choisir l'intervalle B par le biais de D qui serait B^..D en jargon git, donc

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

Mais quoi qu'il en soit, lorsque vous avez besoin de "rejouer" une série de commits, le mot "rejouer" devrait vous pousser à utiliser l'option ". rebase "de Git.

2 votes

Si vous avez des commits qui ont des parents qui requièrent l'option -m comment gérez-vous ces commits ? Ou y a-t-il un moyen de filtrer ces commits ?

0 votes

@aug -m est censé les gérer pour vous, en sélectionnant la ligne principale référencée par la balise -m paramètre que vous avez choisi pour cette sélection.

0 votes

Le problème est que si vous choisissez une gamme de commits, il choisira les commits parents correctement 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 faire pour qu'il passe le test de l'utilisateur ? -m seulement lorsqu'il touche un commit parent lorsqu'il sélectionne une gamme de commits ? Actuellement, si je passe -m comme git cherry-pick a87afaaf..asfa789 -m 1 il s'applique à tous les commits dans la plage.

144voto

Keith Kim Points 481

Depuis git v1.7.2, cherry pick peut accepter une gamme de commits :

git cherry-pick a appris à choisir une gamme de commits (par ex. cherry-pick A..B et cherry-pick --stdin ), ainsi que git revert Ces derniers ne prennent pas en charge le contrôle de séquençage plus agréable. rebase [-i] a, cependant.

116 votes

Notez que cherry-pick A..B n'obtiendra pas l'engagement A (vous auriez besoin A~1..B pour cela), et s'il y a des conflits, git ne continuera pas automatiquement comme rebase le fait (au moins à partir de 1.7.3.1).

4 votes

Il est également bon de noter que git cherry-pick A..B C ne fonctionne pas comme vous l'attendez, naïvement. Il ne prendra pas tout ce qui se trouve dans l'intervalle A..B et commettre C ! Pour cela, il faut diviser en deux lignes, la première git cherry-pick A..B et ensuite git cherry-pick C . Ainsi, chaque fois que vous avez une gamme, vous devez l'exécuter séparément.

79voto

KostasA Points 332

Supposons que vous ayez 2 branches,

"branchA" : inclut les commits que vous voulez copier (de "commitA" à "commitB")

"branchB" : la branche sur laquelle vous souhaitez que les commits soient transférés depuis "branchA".

1)

 git checkout <branchA>

2) Obtenir les ID de "commitA" et "commitB".

3)

git checkout <branchB>

4)

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

5) Au cas où vous auriez un conflit, résolvez-le et tapez

git cherry-pick --continue

pour continuer le processus de sélection des cerises.

0 votes

Cela a fonctionné comme un charme ! Merci beaucoup !

1 votes

Que fait le "^" dans la commande "git cherry-pick <commitA>^..<commitB>" au point 4) ?

0 votes

32voto

djs Points 1764

Etes-vous sûr de ne pas vouloir fusionner les branches ? Si la branche de travail a des commits récents que vous ne voulez pas, vous pouvez simplement créer une nouvelle branche avec un HEAD au point que vous voulez.

Maintenant, si vous voulez vraiment sélectionner une série de commits, pour quelque raison que ce soit, une façon élégante de le faire est de tirer un patchset 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 fait git-rebase de toute façon, mais sans avoir besoin de jouer. Vous pouvez ajouter --3way à git-am si vous avez besoin de fusionner. Assurez-vous qu'il n'y a pas d'autres fichiers *.patch dans le répertoire où vous faites cela, si vous suivez les instructions mot pour mot...

2 votes

Notez que, comme pour les autres fourchettes de révision, il doit être A^ d'inclure A .

9voto

Adam Franco Points 5859

J'ai emballé Le code de VonC en un court bash script, git-multi-cherry-pick pour une course 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 

Je l'utilise actuellement pour reconstruire l'historique d'un projet dont le code tiers et les personnalisations sont mélangés dans le même tronc svn. Je sépare maintenant le code principal de la tierce partie, les modules de la tierce partie et les personnalisations dans leurs propres branches git pour une meilleure compréhension des personnalisations à venir. git-cherry-pick est utile dans cette situation puisque j'ai deux arbres dans le même référentiel, mais sans ancêtre commun.

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