Git: Comment rebase vers un commit spécifique?

156

Je voudrais rebaser 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 parvenir?

Ondra Žižka
la source
4
Avez-vous essayé de faire git checkout Bavant de courir git rebase?
Peter-Paul van Gemerden
Non, cela devrait-il aider? Je suppose que seules les références de la rebasecommande sont ce qui compte.
Ondra Žižka

Réponses:

98

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 simple:

git branch temp master^
git checkout topic
git rebase temp
git branch -d temp
Adam Dymitruk
la source
5
J'aime davantage cette approche de type RISC :) J'essaierai. Merci.
Ondra Žižka
10
Je me demande pourquoi cela ne fonctionne pas pour moi, dans un scénario légèrement différent . Je veux que le groupe saute le pep8 et soit basé sur le maître . git rebase temp(quand sur le groupe ) abandonne avec "Les groupes de branche actuels sont à jour.".
Alois Mahdal
4
Cette solution ne fonctionnera pas pour le scénario où le sujet a déjà été rebasé sur maître, mais vous souhaitez le rebaser sur un ancêtre à maîtriser. Dans ce cas, vous devez utiliser git rebase --onto <target> <from> <to>pour pouvoir spécifier le commit <from>.
mirzmaster
C'est de loin l'option la plus simple si vous voulez rebaser sur le même commit sur lequel la branche est basée.
Ash
1
Je semble fonctionner, mais le GitLab dit autre chose. Si j'avais 10 commits en retard, 5 commits en avant, je m'attendais à avoir 8 commits derrière, 5 commits en avant après avoir obtenu 2 commits. Mais au lieu de cela, il ajoute plus de commits à ces 5.
ROMANIA_engineer
68

Vous pouvez même adopter une approche directe:

git checkout topic
git rebase <commitB>
r0hitsharma
la source
6
Pour moi, cela ne fait pas vraiment ce qui est prévu. Autant que je sache, il essaie de rebaser sur le "dernier ancêtre commun" de topicet commitB.
Dan Lenski
2
@DanLenski, ce n'est pas ainsi que le rebase fonctionne.En citant la documentation , It works by going to the common ancestor of the two branches (the one you’re on and the one you’re rebasing onto), getting the diff introduced by each commit of the branch you’re on, saving those diffs to temporary files, resetting the current branch to the same commit as the branch you are rebasing onto, and finally applying each change in turn. je l'ai réessayé maintenant et semblait fonctionner très bien.
r0hitsharma le
1
Super facile et fonctionne! J'ai maintenant: commitB_from_master-> topicCommit1-> topicCommit2.
Martin Konicek
Cela n'a pas fonctionné pour moi. Avant cela, GitLab disait "n commits en avant". Et maintenant, il dit "m commet en avant" où m > n.
ROMANIA_engineer
48

Utilisez l'option "sur":

git rebase --onto master^ D^ D
Adam Dymitruk
la source
2
Det D^serait le hachage du dernier et de l'avant-dernier commit de "topic"?
Ondra Žižka
39
La syntaxe est comme git rebase --onto <new-parent> <old-parent>. Voir Définition du pointeur de parent git sur un autre parent . Dans votre cas, <new-parent> est B, et <old-parent> est A.
jsz
7
J'utilise toujours les 3 arguments: desitnation, start et end of commits to rebase.
Adam Dymitruk
14
Cela a fonctionné pour moi:git rebase --onto <commit-ID> master
4
Le commentaire de @ jsz est correct, contrairement au commentaire de Simon South, c'est l'inverse: git rebase --onto master <commit-ID-of-old-parent>et pour OP git rebase --onto B A.
gaborous
19

Le commentaire de jsz ci-dessus m'a sauvé des tonnes de douleur, alors voici une recette étape par étape basée sur celle-ci que j'ai utilisée pour rebaser / déplacer tout commit par-dessus tout autre commit:

  1. Trouvez un point de branchement précédent de la branche à rebaser (déplacer) - appelez-le ancien parent. Dans l'exemple ci-dessus, c'est A
  2. Trouvez le commit sur lequel vous voulez déplacer la branche - appelez-le nouveau parent. Dans l'exemple c'est B
  3. Vous devez être sur votre agence (celle que vous déménagez):
  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
Nestor Milyaev
la source
6
Cela devrait être la bonne réponse. Sauf que j'utilise git rebase --onto B master, voir ma réponse pour une explication plus approfondie.
Zack Morris
Cela n'a pas fonctionné pour moi. J'ai choisi 2 commits consécutifs (le dernier du master qui était dans la branche actuelle et le premier du master qui n'était pas dans la branche actuelle). J'ai commencé avec 100 derrière - 10 devant et au lieu d'avoir 99 derrière - 10 devant, maintenant j'ai 105 derrière - 13 devant.
ROMANIA_engineer
Désolé d'entendre que cela n'a pas fonctionné. On dirait que vos branches ont un peu divergé - je suggérerais d'écraser d'abord avant d'essayer de rebaser les branches avec autant de différences.
Nestor Milyaev
11

Sujet Solution

La commande correcte pour répondre à la question posée peut être l'une des suivantes (en supposant que la branche topicest 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 topicn'est pas extrait, vous ajoutez simplement topicà la commande (sauf la dernière) comme suit:

git rebase --onto B master topic

Sinon, vérifiez d'abord la branche avec:

git checkout topic

Rebase toute chaîne de validations vers une validation cible

La forme de base de la commande dont nous avons besoin, tirée de la documentation, est:

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

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

<Target>est le commit auquel nous attacherons notre chaîne de commits. Lorsque vous fournissez un nom de branche, vous spécifiez simplement le commit principal de cette branche. <Target>peut être n'importe quel commit qui ne sera pas contenu dans la chaîne de commits déplacés. Par exemple:

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

Pour déplacer l'ensemble de la branche de fonction, vous ne pouvez pas sélectionner X, Y, Zou featurecomme <Target>puisque ce sont tous les commits à l' intérieur du groupe déplacé.

<Upstream>est spécial car cela peut signifier deux choses différentes. S'il s'agit d'un commit qui est un ancêtre de la branche extraite, alors il sert de point de coupure. Dans l'exemple que j'ai fourni, ce serait tout ce qui est pas C, Dou 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 if trouve un ancêtre commun avec la branche extraite (et abandonne s'il n'en trouve pas). Dans notre cas, un <Upstream>de B, C, Dou mastergenerera COMMIT Bservant de point de coupe. <Upstream>est elle-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 à entrermaster .

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

Exemples et résultats intéressants

En utilisant ce point de départ:

A --- B --- C --- D --- E         master
            \
             \-- X --- Y --- Z    feature
  • git rebase --onto D A feature
    Appliquera commits B, C, X, Y, Zde commettre Det finissent par sauter Bet Cparce qu'ils ont déjà été appliquées.

  • git rebase --onto C X feature
    Appliquera les commits Yet Zs'engagera C, supprimant effectivement le commitX

Isaac Brown
la source
4

Une solution plus simple est git rebase <SHA1 of B> topic. Cela fonctionne indépendamment de l'endroit où vous vous trouvez HEAD.

Nous pouvons confirmer ce comportement à partir de git rebase doc

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


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

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

Cela fonctionnera également, mais le rebase ne fera pas Topicpointer vers la nouvelle branche ainsi créée et HEADsera à l'état détaché. Donc, à partir de là, vous devez supprimer manuellement l'ancienne Topicet créer une nouvelle référence de branche sur la nouvelle branche créée par rebase.

Numéro945
la source
3

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

J'ai trouvé cela beaucoup plus facile à lire et à comprendre. La solution acceptée m'a conduit à un conflit de fusion (trop paresseux pour être résolu à la main):

$ 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".
malat
la source
1
même ici, plusieurs fichiers ont des conflits quand je les 2 réponses plus populaires (par exemple par r0hitsharma et Dymitruk)
Oliver
3

Puisque le rebasage est si fondamental, voici une extension de la réponse de Nestor Milyaev . Combinaison des commentaires de jsz et de Simon South à partir de la réponse d' Adam Dymitruk donne cette commande qui fonctionne sur la topicbranche indépendamment du fait qu'elle se branche à partir masterdu commit de la branche Aou 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 obligatoire (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.

Zack Morris
la source
-2

Il existe une autre façon de le faire ou si vous souhaitez revenir à plus d'un seul commit.

Voici un exemple pour revenir au nnombre de commits:

git branch topic master~n

Pour répondre à cette question, cela peut également être fait:

git branch topic master~1

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

Talha Ashraf
la source
Avez-vous mal interprété cela comme une question sur le branchement? C'est en fait une question sur le rebasage.
NetherGranite