Réorganisation des commits

105

Je travaille actuellement sur une branche et je souhaite que certains commits fusionnent dans d'autres branches:

    a-b-c-d-e-f-g (branchA)
   /
--o-x-x-x-x-x-x-x-x-x-x (master)
   \
    x-x-x-x-x (branchB)

(Les lettres indiquent des validations et les "x" sont des validations non pertinentes.)

Cependant, j'ai remarqué que ce serait une bonne idée de regrouper certains commits. Je veux "concaténer" le commit a, d, e et g dans un patch et le commettre en master. Les validations b et f doivent aller comme une seule validation dans branchB. Existe-t-il une bonne façon de le réaliser?

Peter
la source
4
Les deux réponses déjà données mentionnent la commande correcte, mais je pense qu'avec une tâche aussi basique que celle-ci, cela vaut la peine d'avoir de vraies instructions ici sur StackOverflow, donc j'en ai posté quelques-unes. (Je suis surpris de ne pas trouver une bonne question précédente vers laquelle créer un lien.)
Cascabel

Réponses:

126

La commande que vous recherchez est git rebase, en particulier, l' -i/--interactiveoption.

Je vais supposer que vous voulez laisser le commit c sur la branche A, et que vous voulez vraiment dire que vous voulez déplacer les autres commits vers les autres branches, plutôt que de fusionner, car les fusions sont simples. Commençons par manipuler la branche A.

git rebase -i <SHA1 of commit a>^ branchA

Le ^signifie le commit précédent, donc cette commande dit de rebaser la branche A en utilisant le commit avant "a" comme base. Git vous présentera une liste des commits dans cette plage. Réorganisez-les et dites à git d'écraser les bons:

pick c ...
pick a ...
squash d ...
squash e ...
squash g ...
pick b
squash f

Maintenant, l'histoire devrait ressembler à ceci:

    c - [a+d+e+g] - [b+f] (branchA)
   /
--o-x-x-x-x-x-x-x-x-x-x (master)

Maintenant, prenons le commit b + f nouvellement écrasé pour branchB.

git checkout branchB
git cherry-pick branchA  # cherry-pick one commit, the tip of branchA

Et pareil pour a + d + e + g pour master:

git checkout master
git cherry-pick branchA^

Enfin, mettez à jour branchA pour qu'il pointe vers c:

git branch -f branchA branchA^^

Nous devrions maintenant avoir:

    c (branch A) - [a+d+e+g] - [b+f] (dangling commits)
   /
--o-x-x-x-x-x-x-x-x-x-x-[a+d+e+g] (master)
   \
    x-x-x-x-x-[b+f] (branchB)

Notez que si vous avez plusieurs commits que vous souhaitez déplacer entre les branches, vous pouvez utiliser rebase à nouveau (de manière non interactive):

# create a temporary branch
git branch fromAtoB branchA
# move branchA back two commits
git branch -f branchA branchA~2
# rebase those two commits onto branchB
git rebase --onto branchB branchA fromAtoB
# merge (fast-forward) these into branchB
git checkout branchB
git merge fromAtoB
# clean up
git branch -d fromAtoB

Enfin, un avertissement: il est tout à fait possible de réorganiser les commits de telle sorte que certains ne s'appliquent plus proprement. Cela peut être dû au fait que vous avez choisi un mauvais ordre (mettre un correctif avant le commit introduisant la fonctionnalité qu'il a corrigée); dans ce cas, vous voudrez abandonner le rebase ( git rebase --abort). Sinon, vous devrez résoudre intelligemment les conflits (comme vous le faites avec les conflits de fusion), ajouter les correctifs, puis exécuter git rebase --continuepour passer à autre chose. Ces instructions sont également fournies par le message d'erreur imprimé lorsque le conflit se produit.

Cascabel
la source
le diagramme n'est-il pas après le git branch -f branchA branchA^^faux? À savoir, branchA ne devrait-il pas pointer vers c à ce stade, de sorte que la première ligne du diagramme soit c (branchA)et non c - [a+d+e+g] - [b+f] (branchA)?
ntc2
@enoksrd: Oui, il y a une erreur, mais votre édition contenait des erreurs. Je vais le réparer quand j'aurai une chance ici.
Cascabel
Au lieu de faire, git branch -f branchA branchA^^pourquoi ne pouvez-vous pas simplement faire un git reset --hard <sha1 of c>sur la branche A?
Mikhail Vasilyev le
17
git rebase -i HEAD~3

3est le nombre de commits qui doivent être réorganisés ( source ).

Cela ouvrira vi , listant les commits du plus ancien (en haut) au plus récent (en bas).
Maintenant, réorganisez les lignes, enregistrez et quittez l'éditeur.

ddpdéplacera la ligne actuelle vers le bas
ddkP déplacera la ligne actuelle vers le haut ( source )

Nelu
la source
9

git rebase --interactive est la commande souhaitée.

Exemple:

L'état actuel est le suivant:

bernt@le3180:~/src/stackoverflow/reordering_of_commits
$ git status
On branch master
nothing to commit, working tree clean
bernt@le3180:~/src/stackoverflow/reordering_of_commits
$ git log --oneline
a6e3c6a (HEAD -> master) Fourth commit
9a24b81 Third commit
7bdfb68 Second commit
186d1e0 First commit

Je veux réorganiser les commits 9a24b81(troisième commit) et 7bdfb68(deuxième commit). Pour ce faire, je trouve d'abord le commit avant le premier commit que nous voulons changer . C'est commit 186d1e0(premier commit).

La commande à exécuter est git rebase --interactive COMMIT-BEFORE-FIRST-COMMIT-WE-WANT-TO-CHANGE, dans ce cas:

bernt@le3180:~/src/stackoverflow/reordering_of_commits
$ git rebase --interactive 186d1e0

Cela ouvre un fichier dans l'éditeur par défaut avec le contenu suivant:

pick 7bdfb68 Second commit
pick 9a24b81 Third commit
pick a6e3c6a Fourth commit

# Rebase 186d1e0..a6e3c6a onto 186d1e0 (3 commands)
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#

Notez que l'ordre des commits dans le fichier est opposé à celui utilisé pour git log. Dans git log, le commit le plus récent est en haut. Dans ce fichier, le commit le plus récent se trouve en bas.

Comme l'explique le commentaire au bas du fichier, je peux faire différentes choses, comme écraser, supprimer et réorganiser les commits. Pour réorganiser les commits, j'édite le fichier pour qu'il ressemble à ceci (les commentaires du bas ne sont pas affichés):

pick 9a24b81 Third commit
pick 7bdfb68 Second commit
pick a6e3c6a Fourth commit

La pickcommande au début de chaque ligne signifie "utiliser (c'est-à-dire inclure) ce commit" et quand j'enregistre le fichier temporaire avec les commandes rebase et quitte l'éditeur, git exécutera les commandes et mettra à jour le référentiel et le répertoire de travail:

$ git rebase --interactive 186d1e0
Successfully rebased and updated refs/heads/master.
bernt@le3180:~/src/stackoverflow/reordering_of_commits
$ git log --oneline
ba48fc9 (HEAD -> master) Fourth commit
119639c Second commit
9716581 Third commit
186d1e0 First commit

Notez l'historique de validation réécrit.

Liens:

codeape
la source
1
Veuillez ajouter les détails pertinents à partir de votre lien pour fournir une réponse complète et autonome. SO fronce les sourcils sur les "réponses aux liens uniquement". Plus précisément, comment utiliserait-on git rebase --interactivepour atteindre l'objectif du PO?
SherylHohman
Le deuxième lien n'existe pas maintenant.
devsaw
Ajout de contenu pour rendre la réponse complète et autonome. Suppression du deuxième lien.
codeape
-1

git rebase est ce que vous voulez. Découvrez l'option --interactive.


la source
4
Veuillez ajouter les détails pertinents à partir de votre lien pour fournir une réponse complète et autonome. SO fronce les sourcils sur les "réponses aux liens uniquement".
SherylHohman