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?
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
Vous pouvez même prendre une approche directe:
git checkout topic
git rebase <commitB>
Utilisez l'option "on":
git rebase --onto master^ D^ D
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:
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
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".
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
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.
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
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.
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.
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.
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.