web-dev-qa-db-fra.com

Git: Comment rebaser sur un commit spécifique?

J'aimerais revenir sur un commit spécifique, pas sur un HEAD de l'autre branche:

A --- B --- C          master
 \
  \-- D                topic

à

A --- B --- C          master
       \
        \-- D          topic

au lieu de 

A --- B --- C          master
             \
              \-- D    topic

Comment puis-je y arriver?

107
Ondra Žižka

Vous pouvez éviter d'utiliser le paramètre --onto en créant une branche temporaire sur le commit que vous aimez, puis en utilisant rebase sous sa forme la plus simple:

git branch temp master^
git checkout topic
git rebase temp
git branch -d temp
70
Adam Dymitruk

Vous pouvez même prendre une approche directe:

git checkout topic
git rebase <commitB>
51
r0hitsharma

Utilisez l'option "on":

git rebase --onto master^ D^ D
40
Adam Dymitruk

Le commentaire de jsz ci-dessus m'a évité beaucoup de douleur. Voici donc une recette pas à pas basée sur ce que j'ai utilisé pour rebaser/déplacer tout commit par-dessus tout autre commit:

  1. Recherchez un point de branchement précédent de la branche à rebaser (déplacé) - appelez-le ancien parent. Dans l'exemple ci-dessus, c'estA
  2. Recherchez le commit sur lequel vous souhaitez déplacer la branche - appelez-le nouveau parent. Dans l'exampe c'estB
  3. Vous devez être sur votre branche (celle que vous déplacez): 
  4. Appliquez votre rebase: git rebase --onto <new parent> <old parent>

Dans l'exemple ci-dessus, c'est aussi simple que:

   git checkout topic
   git rebase --onto B A
6
Nestor Milyaev

J'ai utilisé un mélange de solutions décrites ci-dessus:

$ git branch temp <specific sha1>
$ git rebase --onto temp master topic
$ git branch -d temp

Je trouvais cela beaucoup plus facile à lire et à comprendre. La solution acceptée m'a amené à un conflit de fusion (trop paresseux pour être résolu manuellement):

$ git rebase temp
First, rewinding head to replay your work on top of it...
Applying: <git comment>
Using index info to reconstruct a base tree...
M       pom.xml
.git/rebase-apply/patch:10: trailing whitespace.
    <some code>
.git/rebase-apply/patch:17: trailing whitespace.
        <some other code>
warning: 2 lines add whitespace errors.
Falling back to patching base and 3-way merge...
Auto-merging pom.xml
CONFLICT (content): Merge conflict in pom.xml
error: Failed to merge in the changes.
Patch failed at 0001 <git comment>
The copy of the patch that failed is found in: .git/rebase-apply/patch

When you have resolved this problem, run "git rebase --continue".
If you prefer to skip this patch, run "git rebase --skip" instead.
To check out the original branch and stop rebasing, run "git rebase --abort".
2
malat

Sujet Solution

La commande correcte pour répondre à la question publiée peut être l’une des options suivantes (en supposant que la branche topic est déjà extraite):

git rebase --onto B master
git rebase --onto master~1 master
git rebase --onto B A
git rebase --onto B C
git rebase --onto B

Si topic n'est pas extrait, vous ajoutez simplement topic à la commande (sauf la dernière) de la manière suivante:

git rebase --onto B master topic

Vous pouvez également consulter la branche en premier avec:

git checkout topic

Rebasez n'importe quelle chaîne de validations vers une validation cible

La forme de base de la commande dont nous avons besoin, décrite dans la documentation, est la suivante:

git rebase --onto <Target> [<Upstream> [<Branch>]]

<Branch> est facultatif et ne fait que vérifier la branche spécifiée avant d'exécuter le reste de la commande. Si vous avez déjà vérifié la branche que vous souhaitez rebaser, vous n'en avez pas besoin. Notez que vous devez avoir spécifié <Upstream> afin de spécifier <Branch> ou git pensera que vous spécifiez <Upstream>.

<Target> est le commit auquel nous allons attacher notre chaîne de commits. Lorsque vous indiquez un nom de branche, vous spécifiez simplement le commit en-tête de cette branche. <Target> peut être n'importe quelle validation qui ne sera pas contenue dans la chaîne des validations déplacées. Par exemple:

A --- B --- C --- D         master
      \
       \-- X --- Y --- Z    feature

Pour déplacer l'intégralité de la branche de fonctionnalité, vous ne pouvez pas sélectionner X, Y, Z ou feature en tant que <Target> car ils sont tous des commits au sein du groupe en cours de déplacement.

<Upstream> est spécial car il peut signifier deux choses différentes. S'il s'agit d'un commit qui est un ancêtre de la branche extraite, il sert alors de point de coupure. Dans l'exemple que j'ai fourni, il s'agirait de tout ce qui n'est pas C, D ou master. Tous les commits après <Upstream> jusqu'à ce que la tête de la branche extraite soient ceux qui seront déplacés.

Cependant, si <Upstream> n'est pas un ancêtre, alors git sauvegarde la chaîne à partir du commit spécifié jusqu'à ce que si trouve un ancêtre commun avec la branche extraite (et abandonne s'il ne parvient pas à en trouver un). Dans notre cas, un <Upstream> de B, C, D ou master aura pour résultat que commit B servira de point de coupure. <Upstream> est lui-même une commande facultative et si elle n'est pas spécifiée, alors git regarde le parent de la branche extraite, ce qui équivaut à entrer master.

Maintenant que git a sélectionné les commits qu'il va couper et déplacer, il les applique afin de <Target>, en ignorant ceux qui sont déjà appliqués à la cible.

Exemples intéressants et résultats

En utilisant ce point de départ:

A --- B --- C --- D --- E         master
            \
             \-- X --- Y --- Z    feature
  • git rebase --onto D A feature
    S'appliquera commits B, C, X, Y, Z à commettre D et finissent par ignorer B et C car ils ont déjà été appliqués.

  • git rebase --onto C X feature
    Appliquera les commits Y et Z à commettre C, en supprimant effectivement commit X

1
Isaac Brown

Une solution plus simple est git rebase <SHA1 of B> topic. Cela fonctionne quel que soit votre HEAD.

Nous pouvons confirmer ce comportement depuis git rebase doc

<upstream> Branche en amont à comparer. Peut être valide commit, pas seulement un nom de branche existant. Par défaut, le configuré en amont pour la branche actuelle.


Vous pensez peut-être ce qui se passera si je mentionne SHA1 de topic également dans la commande ci-dessus?

git rebase <SHA1 of B> <SHA1 of topic>

Cela fonctionnera aussi, mais la base ne permettra pas à Topic de pointer sur la nouvelle branche ainsi créée et HEAD sera à l'état détaché. Donc, à partir de là, vous devez supprimer manuellement l’ancienne Topic et créer une nouvelle référence de branche sur la nouvelle branche créée par rebase.

0
Breaking Benjamin

Il y a une autre façon de le faire ou si vous souhaitez revenir à plus d'un engagement.

Voici un exemple pour revenir au nombre n:

git branch topic master~n

Pour le bien de cette question, cela peut aussi être fait:

git branch topic master~1

La commande fonctionne parfaitement sur git version 2.7.4. Je ne l'ai pas testé sur une autre version.

0
Talha Ashraf

Puisque le rebasement est si fondamental, voici une extension de La réponse de Nestor Milyaev . La combinaison de jsz et de Simon South des commentaires de de la réponse d'Adam Dymitruk donne cette commande qui fonctionne sur la branche topic, qu'elle soit ou non issue de la branche master commit A ou C:

git checkout topic
git rebase --onto <commit-B> <pre-rebase-A-or-post-rebase-C-or-base-branch-name>

Notez que le dernier argument est requis (sinon, il rembobine votre branche pour valider B).

Exemples:

# if topic branches from master commit A:
git checkout topic
git rebase --onto <commit-B> <commit-A>
# if topic branches from master commit C:
git checkout topic
git rebase --onto <commit-B> <commit-C>
# regardless of whether topic branches from master commit A or C:
git checkout topic
git rebase --onto <commit-B> master

Donc, la dernière commande est celle que j'utilise généralement.

0
Zack Morris