Comment sélectionner plusieurs commits

875

J'ai deux branches. Commit aest la tête d'un, tandis que l'autre a b, c, d, eet fsur le dessus a. Je veux bouger c, d, eet fà première branche sans commettre b. En utilisant la cerise choisir , il est facile: la caisse première branche cerise choisir un par un cà fet rebasage seconde branche sur la première. Mais existe-t-il un moyen de tout sélectionner c- fen une seule commande?

Voici une description visuelle du scénario (merci JJD ):

entrez la description de l'image ici

tig
la source
3
le rebase que vous mentionnez n'est pas vraiment pertinent pour la question, n'est-ce pas? (Je comprends que vous voudrez peut- bêtre vous baser fplus tard, mais cela n'a rien à voir avec la cueillette des cerises.)
Superole

Réponses:

1282

Git 1.7.2 a introduit la possibilité de choisir une gamme de commits. D'après les notes de version :

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 .

Pour sélectionner toutes les validations de commit Aà commit B(où Aest plus ancien que B), exécutez:

git cherry-pick A^..B

Si vous voulez ignorer A lui-même, exécutez:

git cherry-pick A..B

(Nous remercions Damian, JB Rainsberger et sschaef dans les commentaires)

Eric Darchis
la source
249
Dans le formulaire "cherry-pick A..B", A doit être plus ancien que B. S'ils sont dans le mauvais ordre, la commande échouera silencieusement.
damian
294
En outre, cela ne choisira pas A, mais tout tout après A jusqu'à B. inclus
JB Rainsberger
455
Pour inclure un type justegit cherry-pick A^..B
kiritsuku
17
Si vous avez git 1.7.1 ou une version antérieure et que vous ne pouvez pas mettre à jour, vous pouvez les sélectionner rapidement dans l'ordre en exécutant git cherry-pick f~3puis git cherry-pick f~2etc. jusqu'à git cherry-pick f(en appuyant sur la flèche vers le haut , vous obtenez la commande précédente afin que je puisse rapidement changer le nombre et exécuter devrait être similaire dans la plupart des consoles).
David Mason
19
Il peut être bon de savoir que cette syntaxe fonctionne également avec les noms de branche. git cherry-pick master..somebranchsélectionnera tous les commits sur une branche depuis master (en supposant qu'il est déjà rebasé sur master) et les appliquera à votre branche actuelle.
Tor Klingberg
103

La façon la plus simple de le faire est avec l' ontooption de rebase. Supposons que la branche qui se termine en cours à aest appelé mybranch et c'est la branche que vous souhaitez déplacer c- fsur.

# checkout mybranch
git checkout mybranch

# reset it to f (currently includes a)
git reset --hard f

# rebase every commit after b and transplant it onto a
git rebase --onto a b
CB Bailey
la source
1
Je vous remercie! Pourriez-vous également ajouter git checkout secondbranch && git rebase mybranchpour une réponse complète
tig
1
Cette réponse m'a beaucoup aidé à comprendre quel commit est lequel dans ce scénario. Et: vous pouvez également utiliser rebasele mode interactif de. Merci, @Charles!
Oliver
1
La beauté de cette approche est que vous pouvez utiliser --interactivepour supprimer certains commits de la séquence ou les réorganiser avant la "sélection de cerise". +1
Michael Merickel
c'est une commande géniale, un peu délicate à envelopper, mais ça fait des merveilles.
Valerio
Sauvage que vous devez donner le commit que vous ne voulez pas rebaser comme l'un des arguments ( bdans cet exemple) mais oui, cela a fonctionné pour moi.
asontu
85

Ou le monoplace demandé:

git rebase --onto a b f
Wolfc
la source
5
Ne serait-ce que pour sa brièveté, c'est la meilleure réponse.
Nate Chandler
9
Surévalué mais vous laissera dans un état HEAD détaché si f est un commit (par opposition à une branche) - vous devez modifier pour ajouter que l'on devrait extraire une branche comme dans la réponse ci
Mr_and_Mrs_D
68

Vous pouvez utiliser une combinaison série de git rebaseet git branchpour appliquer un groupe de validations à une autre branche. Comme déjà signalé par wolfc, la première commande copie en fait les commits. Cependant, la modification n'est pas visible tant que vous n'ajoutez pas un nom de branche au plus haut commit du groupe.

Veuillez ouvrir l'image dans un nouvel onglet ...

Workflow

Pour résumer les commandes sous forme de texte:

  1. Ouvrez gitk comme un processus indépendant en utilisant la commande: gitk --all &.
  2. Courez git rebase --onto a b f.
  3. Appuyez F5sur gitk . Rien ne change. Mais nonHEAD est marqué.
  4. Courir git branch selection
  5. Appuyez F5sur gitk . La nouvelle branche avec ses validations apparaît.

Cela devrait clarifier les choses:

  • Commit aest la nouvelle destination racine du groupe.
  • Commit best le commit avant le premier commit du groupe (exclusif).
  • Commit fest le dernier commit du groupe (inclus).

Ensuite, vous pouvez utiliser git checkout feature && git reset --hard bpour supprimer les commits ctill fde la featurebranche.

En plus de cette réponse, j'ai écrit un article de blog qui décrit les commandes dans un autre scénario qui devrait aider à l'utiliser généralement.

JJD
la source
2
Si mybranch (les commissions a..f) ne sont plus nécessaires, cela peut être simplifié pour: git rebase --onto a b mybranchet btw - quel programme ces images git astucieuses?
Mr_and_Mrs_D
2
@Mr_and_Mrs_D Merci pour votre commentaire. Je pense que j'ai utilisé cacoo.com pour dessiner les images.
JJD
48

Pour appliquer les commentaires de JB Rainsberger et sschaef pour répondre spécifiquement à la question ... Pour utiliser une plage de sélection sur cet exemple:

git checkout a
git cherry-pick b..f

ou

git checkout a
git cherry-pick c^..f
Andy
la source
3
J'utilise git 2.7.0.windows.1et j'ai remarqué que lorsque j'essaie de choisir une gamme de validations, tout est ok mais git ne vous dit nulle part ce que vous devez faire git cherry-pick --continue | --abort | --quitavant d'essayer de valider / cherry-pick à nouveau. Donc, si vous choisissez une gamme de validations, vous devrez exécuter git cherry-pick --continuechaque fois que vous êtes prêt (résolution de conflits ou autre) avec une validation de la plage donnée.
kuskmen
J'ai fait exactement la même chose, mais fatal: impossible de trouver 'a..b'
Amit Karnik
Je ne sais pas où je me trompe mais quand je fais 'git cherry-pick c ^ .. f' de mon côté, cela inclut le commit f mais pas le commit c. Mais comme je l'ai lu partout, il est censé définir c et f comme inclusifs. Ou ai-je tort?
Samuel
@Samuel oui, c'est exact. le^ après le c signifie en fait "la validation avant c" qui est b dans ce cas. C'est pourquoi c^..fest également synonyme de b..f. Essayez de faire git log c^..fet vous devriez voir les commits c à f, exactement comme si vous l'avez faitgit log b..f
Andy
41

Si vous avez des révisions sélectives à fusionner, dites A, C, F, J à partir des validations A, B, C, D, E, F, G, H, I, J, utilisez simplement la commande ci-dessous:

git cherry-pick ACFJ

Shrikant Wandhare
la source
1
agréable et simple
spinup
21
git rev-list --reverse b..f | xargs -n 1 git cherry-pick
Dustin
la source
2
Fonctionne parfaitement s'il n'y a pas de conflits, sinon "rebaser sur" pourrait être plus facile car vous n'aurez pas à déterminer où il s'est arrêté et à réappliquer le reste des correctifs.
Ruslan Kabalin
6
Veuillez ajouter des commentaires expliquant ce que cela fait
Mr_and_Mrs_D
7
puisque personne n'a expliqué ... git rev-list imprime toutes les révisions de la branche b à f (inversé) de sorte que lorsque chaque ligne (le hachage de validation) est passée dans l'ordre, elle choisira chacune sur le git HEAD actuel. iegit cherry-pick {hash of c}; git cherry-pick {hash of d}; ...
coderatchet
8

Pour sélectionner un ID de validation jusqu'à la pointe de la branche, vous pouvez utiliser:

git cherry-pick commit_id^..branch_name

ut9081
la source
Cela fait déjà partie de la réponse stackoverflow.com/a/31640427/96823
tig
1
Cette réponse est en fait différente et m'a été utile. Il spécifie le nom de la branche, pas le SHA de validation final.
Subtletree
7

Une autre variante qui mérite d'être mentionnée est que si vous voulez que les dernières nvalidations proviennent d'une branche, la ~syntaxe peut être utile:

git cherry-pick some-branch~4..some-branch

Dans ce cas, la commande ci-dessus sélectionnerait les 4 derniers commits d'une branche appelée some-branch(bien que vous puissiez également utiliser un hachage de validation à la place d'un nom de branche)

Nick F
la source
1
Réponse TRÈS utile, merci! :)
Jacek Dziurdzikowski
3

En fait, la façon la plus simple de le faire pourrait être de:

  1. enregistrer la base de fusion entre les deux branches: MERGE_BASE=$(git merge-base branch-a branch-b)
  2. avance rapide ou rebase de l'ancienne branche sur la branche la plus récente
  3. rebase la branche résultante sur elle-même, en commençant à la base de fusion de l'étape 1, et supprimez manuellement les validations qui ne sont pas souhaitées:

    git rebase ${SAVED_MERGE_BASE} -i
    

    Sinon, s'il n'y a que quelques nouveaux validations, sautez l'étape 1 et utilisez simplement

    git rebase HEAD^^^^^^^ -i
    

    dans la première étape, en utilisant suffisamment ^pour dépasser la base de fusion.

Vous verrez quelque chose comme ça dans le rebase interactif:

pick 3139276 commit a
pick c1b421d commit b
pick 7204ee5 commit c
pick 6ae9419 commit d
pick 0152077 commit e
pick 2656623 commit f

Supprimez ensuite les lignes b (et toutes les autres que vous souhaitez)

ealfonso
la source
1
git format-patch --full-index --binary --stdout range... | git am -3
Roger Wang
la source
42
Veuillez ajouter des commentaires expliquant ce que cela fait
Mr_and_Mrs_D
0

Voici un script qui vous permettra de sélectionner plusieurs validations consécutives en indiquant simplement au script les branches source et cible des sélections de cerises et le nombre de validations:

https://gist.github.com/nickboldt/99ac1dc4eb4c9ff003a1effef2eb2d81

Pour sélectionner à partir de votre branche vers master (utilise la branche actuelle comme source):

./gcpl.sh -m

Pour sélectionner les 5 derniers commits de votre branche 6.19.x à masteriser:

./gcpl.sh -c 5 -s 6.19.x -t master
nickboldt
la source
Pourquoi ce script serait-il nécessaire alors que vous pouvez le faire directement avec Git? ...
code_dredd
Moins de frappe, et mon script sélectionne automatiquement les commits pour vous, vous n'avez donc pas à choisir chacun d'eux par son SHA. Quoi qu'il en soit, YMMV.
nickboldt