J'aimerais déplacer les derniers commits que je me suis engagé à maîtriser dans une nouvelle branche et à les ramener avant que ces commits ne soient effectués. Malheureusement, mon Git-fu n'est pas encore assez fort, aucune aide?
C'est à dire. Comment puis-je partir de ça
master A - B - C - D - E
pour ça?
newbranch C - D - E
/
master A - B
WARNING: Cette méthode fonctionne parce que vous créez une nouvelle branche avec la première commande: git branch newbranch
. Si vous souhaitez déplacer les validations vers une branche existante, vous devez fusionner vos modifications dans la branche existante avant d'exécuter git reset --hard HEAD~3
(voir Passage vers une branche existante ci-dessous). Si vous ne fusionnez pas vos modifications en premier, elles seront perdues.
À moins que d’autres circonstances ne soient impliquées, cela peut être facilement fait en ramifiant et en rétrogradant.
# Note: Any changes not committed will be lost.
git branch newbranch # Create a new branch, saving the desired commits
git reset --hard HEAD~3 # Move master back by 3 commits (GONE from master)
git checkout newbranch # Go to the new branch that still has the desired commits
Mais assurez-vous combien de personnes s’engagent à revenir en arrière. Vous pouvez également, au lieu de HEAD~3
, fournir simplement le hachage de la validation (ou la référence telle que Origine/maître) que vous souhaitez "rétablir" sur le maître (/ current ) branche, par exemple:
git reset --hard a1b2c3d4
* 1 Vous seulement serez "perdant" les commits de la branche principale, mais ne vous inquiétez pas, vous les aurez dans newbranch!
AVERTISSEMENT: Avec Git version 2.0 et ultérieure, si vous définissez plus tard git rebase
la nouvelle branche sur la branche d'origine (master
), vous aurez peut-être besoin d'une option explicite --no-fork-point
lors de la refonte pour éviter de perdre les validations reportées. Si branch.autosetuprebase always
est défini, cela est plus probable. Voir Réponse de John Mellor pour plus de détails.
Si vous souhaitez déplacer vos commits vers une branche existante, cela ressemblera à ceci:
git checkout existingbranch
git merge master
git checkout master
git reset --hard HEAD~3 # Go back 3 commits. You *will* lose uncommitted work.
git checkout existingbranch
Pour ceux qui se demandent pourquoi cela fonctionne (comme je l'étais au début):
Vous souhaitez revenir en C et déplacer D et E dans la nouvelle branche. Voici à quoi ça ressemble au début:
A-B-C-D-E (HEAD)
↑
master
Après git branch newBranch
:
newBranch
↓
A-B-C-D-E (HEAD)
↑
master
Après git reset --hard HEAD~2
:
newBranch
↓
A-B-C-D-E (HEAD)
↑
master
Puisqu’une branche n’est qu’un pointeur, master a indiqué le dernier commit. Lorsque vous avez créé newBranch , vous avez simplement créé un nouveau pointeur sur le dernier commit. Ensuite, en utilisant git reset
, vous avez déplacé le pointeur master de deux commits. Mais puisque vous n'avez pas déplacé newBranch , il pointe toujours sur le commit qu'il a fait à l'origine.
La méthode exposée par sykora est la meilleure option dans ce cas. Mais parfois, ce n'est pas la plus simple et ce n'est pas une méthode générale. Pour une méthode générale, utilisez git cherry-pick:
Pour réaliser ce que veut OP, il s’agit d’un processus en deux étapes:
newbranch
Execute
git checkout master
git log
Notez les hachages de (disons 3) que vous voulez sur newbranch
. Ici je vais utiliser:
C commit: 9aa1233
D commit: 453ac3d
E commit: 612ecb3
Remarque: Vous pouvez utiliser les sept premiers caractères ou l'ensemble commet le hachage
newbranch
git checkout newbranch
git cherry-pick 612ecb3
git cherry-pick 453ac3d
git cherry-pick 9aa1233
git checkout newbranch
git cherry-pick 612ecb3~1..9aa1233
git cherry-pick applique ces trois commits à newbranch.
Encore une autre façon de faire cela, en utilisant seulement 2 commandes. Garde également votre arbre de travail actuel intact.
git checkout -b newbranch # switch to a new branch
git branch -f master HEAD~3 # make master point to some older commit
Ancienne version - avant d'avoir entendu parler de git branch -f
git checkout -b newbranch # switch to a new branch
git Push . +HEAD~3:master # make master point to some older commit
Être capable de Push
à .
est une bonne astuce à connaître.
Ne faites pas cela:
git branch -t newbranch
git reset --hard HEAD~3
git checkout newbranch
Lors de la prochaine exécution de git rebase
(ou git pull --rebase
), ces 3 commits seraient ignorés de newbranch
! (voir explication ci-dessous)
Au lieu de cela, faites ceci:
git reset --keep HEAD~3
git checkout -t -b newbranch
git cherry-pick ..HEAD@{2}
--keep
est semblable à --hard
, mais plus sûr, car échoue plutôt que de jeter les modifications non validées).newbranch
.newbranch
. Puisqu'elles ne sont plus référencées par une branche, elle le fait en utilisant reflog : HEAD@{2}
de git est le commit que HEAD
utilisait pour désigner 2 opérations déjà, c'est-à-dire avant que 1. nous ayons extrait newbranch
et 2. used git reset
. éliminer les 3 commits.Attention: le reflog est activé par défaut, mais si vous l'avez désactivé manuellement (en utilisant par exemple un référentiel git "nu"), vous ne pourrez pas récupérer les 3 validations après avoir exécuté git reset --keep HEAD~3
.
Une alternative qui ne repose pas sur le reflog est:
# newbranch will omit the 3 most recent commits.
git checkout -b newbranch HEAD~3
git branch --set-upstream-to=oldbranch
# Cherry-picks the extra commits from oldbranch.
git cherry-pick ..oldbranch
# Discards the 3 most recent commits from oldbranch.
git branch --force oldbranch oldbranch~3
(Si vous préférez, vous pouvez écrire @{-1}
- la branche précédemment extraite - au lieu de oldbranch
).
Pourquoi git rebase
écarterait-il les 3 commits après le premier exemple? C'est parce que git rebase
sans argument active l'option --fork-point
par défaut, qui utilise le reflog local pour essayer d'être robuste contre la branche en amont étant forcée.
Supposons que vous ayez dérivé de Origin/Master quand il contenait les commits M1, M2, M3, puis que vous avez effectué trois commits:
M1--M2--M3 <-- Origin/master
\
T1--T2--T3 <-- topic
mais alors quelqu'un réécrit l'histoire en forçant Origin/master à supprimer M2:
M1--M3' <-- Origin/master
\
M2--M3--T1--T2--T3 <-- topic
En utilisant votre refog local, git rebase
peut voir que vous avez hérité d’une incarnation antérieure de la branche Origin/master et que, par conséquent, les commits M2 et M3 ne font pas vraiment partie de votre branche de sujet. Par conséquent, il est raisonnable de supposer que, puisque M2 a été supprimé de la branche en amont, vous ne le souhaitez plus dans votre branche de sujet non plus lorsque la branche de sujet est rebasée:
M1--M3' <-- Origin/master
\
T1'--T2'--T3' <-- topic (rebased)
Ce comportement a du sens et est généralement la bonne chose à faire lors du changement de base.
Donc, la raison pour laquelle les commandes suivantes échouent:
git branch -t newbranch
git reset --hard HEAD~3
git checkout newbranch
est parce qu'ils laissent le reflog dans le mauvais état. Git considère que newbranch
a jeté la branche amont dans une révision qui inclut les 3 commits, puis le reset --hard
réécrit l'historique du amont pour supprimer les commits, et la prochaine fois que vous exécuterez git rebase
, il les supprimera comme tout autre commit qui a été supprimé de l'amont.
Mais dans ce cas particulier, nous voulons que ces 3 commits soient considérés comme faisant partie de la branche thématique. Pour atteindre cet objectif, nous avons besoin de bifurquer en amont lors de la précédente révision qui n'incluait pas les 3 commits. C’est ce que mes solutions suggérées font, donc elles laissent toutes deux le bon état dans le bon état.
Pour plus de détails, voir la définition de --fork-point
dans les git rebase et git merge-base docs.
Si:
master
, et La procédure suivante est beaucoup plus simple (à partir de la branche master
comportant trois commits erronés):
git reset HEAD~3
git stash
git checkout newbranch
git stash pop
master
, tout en laissant tous les fichiers de travail intactsmaster
exactement égal à l'état HEAD ~ 3newbranch
Vous pouvez maintenant utiliser git add
et git commit
comme vous le feriez normalement. Tous les nouveaux commits seront ajoutés à newbranch
.
Le PO a déclaré que l'objectif était de "ramener le maître avant que ces commissions ne soient effectuées" sans perdre de modifications et cette solution le fait.
Je le fais au moins une fois par semaine lorsque je commets accidentellement de nouveaux commits sur master
au lieu de develop
. Habituellement, je n'ai qu'un seul commit à annuler, auquel cas utiliser git reset HEAD^
sur la ligne 1 est un moyen plus simple d'annuler un commit.
Ne faites pas cela si vous avez poussé les modifications du maître en amont
Quelqu'un d'autre peut avoir retiré ces changements. Si vous ne faites que réécrire votre maître local, l'impact en amont n'est pas impactant, mais le fait de transmettre un historique réécrit aux collaborateurs peut entraîner des problèmes.
Cela ne les "déplace" pas au sens technique, mais a le même effet:
A--B--C (branch-foo)
\ ^-- I wanted them here!
\
D--E--F--G (branch-bar)
^--^--^-- Opps wrong branch!
While on branch-bar:
$ git reset --hard D # remember the SHAs for E, F, G (or E and G for a range)
A--B--C (branch-foo)
\
\
D-(E--F--G) detached
^-- (branch-bar)
Switch to branch-foo
$ git cherry-pick E..G
A--B--C--E'--F'--G' (branch-foo)
\ E--F--G detached (This can be ignored)
\ /
D--H--I (branch-bar)
Now you won't need to worry about the detached branch because it is basically
like they are in the trash can waiting for the day it gets garbage collected.
Eventually some time in the far future it will look like:
A--B--C--E'--F'--G'--L--M--N--... (branch-foo)
\
\
D--H--I--J--K--.... (branch-bar)
Pour faire cela sans réécrire l’historique (c’est-à-dire si vous avez déjà poussé les commits):
git checkout master
git revert <commitID(s)>
git checkout -b new-branch
git cherry-pick <commitID(s)>
Les deux branches peuvent alors être poussées sans force!
Avait juste cette situation:
Branch one: A B C D E F J L M
\ (Merge)
Branch two: G I K N
J'ai joué:
git branch newbranch
git reset --hard HEAD~8
git checkout newbranch
Je m'attendais à ce que je commette je serais le HEAD, mais commettez L est-ce maintenant ...
Pour être sûr d’atterrir au bon endroit de l’histoire, il est plus facile de travailler avec le hash du commit
git branch newbranch
git reset --hard #########
git checkout newbranch
1) Créez une nouvelle branche, qui transfère toutes vos modifications vers new_branch.
git checkout -b new_branch
2) Puis retournez à l'ancienne branche.
git checkout master
3) Est-ce que git rebase
git rebase -i <short-hash-of-B-commit>
4) Ensuite, l'éditeur ouvert contient les 3 dernières informations de validation.
...
pick <C's hash> C
pick <D's hash> D
pick <E's hash> E
...
5) Remplacez pick
par drop
dans ces 3 commits. Puis enregistrez et fermez l'éditeur.
...
drop <C's hash> C
drop <D's hash> D
drop <E's hash> E
...
6) Les 3 derniers commits sont maintenant supprimés de la branche actuelle (master
). Maintenant, forcez la branche avec le signe +
avant le nom de la branche.
git Push Origin +master
Comment puis-je partir de ça
A - B - C - D - E
|
master
pour ça?
A - B - C - D - E
| |
master newbranch
Avec deux commandes
donnant
A - B - C - D - E
|
newbranch
et
donnant
A - B - C - D - E
| |
master newbranch
Si vous avez juste besoin de déplacer tous vos non pressés s’engage dans une nouvelle branche , alors il vous suffit de le faire,
créer a nouvelle branche de l'actuelle: git branch new-branch-name
Push votre nouvelle branche : git Push Origin new-branch-name
revenez votre ancienne branche (actuelle) vers le dernier état poussé/stable: git reset --hard Origin/old-branch-name
Certaines personnes ont aussi un autre upstreams
plutôt que Origin
, elles devraient utiliser un upstream
approprié
Vous pouvez faire cela est juste 3 étapes simples que j'ai utilisées.
1) faire une nouvelle branche où vous voulez vous engager la mise à jour récente.
git branch <branch name>
2) Trouver l'ID de validation récente pour la validation sur la nouvelle branche.
git log
3) Copiez cette ID de validation en notant que la liste de validation la plus récente est placée en haut. afin que vous puissiez trouver votre commit. vous trouvez aussi cela via un message.
git cherry-pick d34bcef232f6c...
vous pouvez également fournir des identifiants de numéro de commit.
git cherry-pick d34bcef...86d2aec
Maintenant, votre travail est fait. Si vous avez choisi l'id correct et la branche correcte, alors vous réussirez. Alors avant cela, faites attention. sinon un autre problème peut survenir.
Maintenant, vous pouvez pousser votre code
git Push