Disons que j'ai:

A - B - C - D - E - F  master
            \ 
             \- G - H  new feature branch

Maintenant, je me rends compte que les commits B et C appartiennent en fait à la nouvelle fonctionnalité, donc je veux les déplacer vers la "nouvelle branche de fonctionnalité". En d'autres termes, je veux que la "nouvelle branche de fonctionnalité" commence à A et inclue les commits B et C:

A - D - E - F  master
 \ 
  \- B - C - G - H  new feature branch

Comment puis-je faire cela? D'après ce que j'ai lu, il semble que rebase soit la fonctionnalité que je recherche, mais j'aimerais être sûr avant de gâcher mon dépôt.

(J'ai recherché et trouvé beaucoup de questions et d'exemples très similaires, mais aucun ne ressemble exactement au scénario que j'ai décrit, donc je demande d'être sûr (un dépôt est un chose précieuse à ruiner, après tout)).

2
PaulJ 21 avril 2017 à 01:53

3 réponses

Meilleure réponse

Pour réparer le maître:

git checkout A
git cherry-pick master~3..master # apply the changes we actually want to keep on master
git branch -f master # reposition master on new position
git checkout master

Pour supprimer le commit D de la branche de fonctionnalité:

git checkout feature-branch~3 # or git checkout C
git cherry-pick feature-branch~2..feature-branch # apply the last 2 revisions
git branch -f feature-branch
git checkout feature-branch
3
Code-Apprentice 21 avril 2017 à 01:27

Vous pouvez le faire avec git rebase.

Tout d'abord, rebasons certains commits de master:

$ git checkout master
$ git rebase --onto A C

Cela déplacera tous les commits dans la plage de C à master (mais n'incluant pas C lui-même) sur le commit A.

Maintenant rebase feature mais jetez le commit D:

$ git checkout feature
$ git rebase --onto C D

Semblable à la commande précédente, cela déplacera les commits dans la plage de D à feature (sans inclure D lui-même) sur C.

3
Code-Apprentice 21 avril 2017 à 01:25

La réponse d'Edmundo est juste (et a été votée pour), mais il convient de souligner quelques éléments supplémentaires.

Tout d'abord, votre question parle de "déplacer la racine de la branche" - mais c'est Git; les branches n'ont pas de racines, pas la façon dont vous pensez de toute façon. Regardons votre dessin graphique:

A - B - C - D - E - F  master
            \ 
             \- G - H  new feature branch

Je pense que vous, comme moi quand j'ai commencé à utiliser Git, aimeriez penser aux commits A-B-C-D-E-F comme étant sur la branche master et aux commits G-H comme étant sur la branche de fonctionnalité. Mais ce n'est pas ainsi que fonctionne Git. Re-dessinons ceci, sans changer aucun des liens de commit:

           E--F   <-- master
          /
A--B--C--D
          \
           G--H   <-- feature

Cela devrait préciser que, en ce qui concerne Git, les commits A-B-C-D sont sur les deux branches. Il n'y a qu'une seule racine. C'est commit A: c'est une racine car il n'a pas de commit parent, pas de lien vers l'arrière dans la chaîne du commit au parent.

Deuxièmement, quelle que soit la façon dont vous vous y prenez, vous finissez par devoir copier certains commits. La raison en est que l'ID parent de chaque validation fait partie de l'identité de cette validation. Le "vrai nom" de tout commit est son ID de hachage, et l'ID de hachage est construit en lisant le contenu complet du commit: arborescence source, message de commit, nom et date de l'auteur, etc., mais incluant toujours aussi l'ID parent. 1 Vous voulez que votre graphique final ressemble à:

  D--E--F   <-- master
 /
A
 \ 
  B--C--G--H   <-- feature

Mais les liens D existants (ou pointent) vers le C existant, pas vers A, et le G existant pointe vers le D existant.

C'est pourquoi le cherry-pick fonctionne: git cherry-pick essentiellement copie un commit. La nouvelle copie "fait la même chose" que l'original, mais a quelque chose de différent, même si c'est aussi simple que "mon parent est ...". (Habituellement, il a également un objet tree attaché différent, c'est juste que le changement qu'il apporte, par rapport à son nouveau parent, est le même que le changement d'origine fait lorsque l'original est comparé au parent de l'original). Cela signifie que vous ne pouvez pas réellement obtenir ce que vous voulez, juste ce dont vous avez besoin :

  D'-E'-F'  <-- master
 /
A
 \ 
  B--C--G'-H'  <-- feature

Où la petite coche indique que le résultat est une copie de l'original.

Qu'arrive-t-il aux originaux? La réponse est: ils sont toujours là, dans le référentiel, au cas où vous en auriez besoin. L'image complète ressemble plus à ceci:

    D'-E'-F'   <-- master
   /
  /        E--F   [abandoned]
 /        /
A--B--C--D
       \  \
        \  G--H   [abandoned]
         \
          G'-H'   <-- feature

Alors que git cherry-pick fonctionne, ce que git rebase - en particulier git rebase -i - fait est de faire ces copies de manière automatisée sophistiquée, avec une étape finale pour déplacer le nom de la branche, abandonnant les commits d'origine. Donc git rebase -i est parfois une manière plus simple de s’y prendre.

Si vous exécutez git rebase -i, vous voyez toutes ces commandes pick, et celles-ci exécutent littéralement git cherry-pick: il fait vraiment une série de sélections. Si vous faites cela vous-même, ce qui se passe peut être plus clair et vous avez beaucoup plus de contrôle sur le moment où les étiquettes de branche sont déplacées.


1 Pour les validations de fusion, il s'agit des s parents, au pluriel. Tous les parents participent au hachage.

6
Community 23 mai 2017 à 12:26