J'ai une branche qui a un historique de commit défectueux en raison d'une poussée forcée dans notre branche master. Fondamentalement, j'ai les commits suivants dans cet historique de branche: A, B, C, D, E où E doit être préservé mais ABCD doit être supprimé. Ils ont été ajoutés en raison d'une fusion principale dans la branche avant que ces validations ne soient supprimées de force de l'origine principale. Comment puis-je accomplir cela?
Si A était celui à préserver, je pourrais simplement réinitialiser --hard A mais c'est dans l'autre sens ....
3 Réponses :
# make a backup of the current state of your branch git branch backup your_branch # reset to the commit prior to A git reset --hard A^ # then re-apply E git cherry-pick E would be a way to do this. Rebase is another (see msanford's very detailed answer on the subject)
@TimBiegeleisen Oui, il ne peut pas faire ça car il perdrait alors E. Cherry. Le ramasser semble être un bon moyen d'y parvenir. Non ?
Votre réponse serait de réécrire l'histoire à distance, et en général, ce n'est pas la chose à faire.
ABCD est fusionné à partir du maître (et non plus dans le maître distant) pour cela doit être supprimé. E est mon dernier commit et où HEAD est en ce moment. Je veux juste qu'E soit dans l'histoire de la branche
@joelgullander C'est ce que je comprends, c'est le besoin auquel j'aborde dans ma réponse.
Merci cela a fonctionné. J'ai toujours peur de faire des trucs de force alors je suis content d'avoir obtenu de l'aide ici.
@joelgullander Vous pouvez facilement récupérer presque tout dans git. Cependant, voyez la note dans ma réponse sur la force de poussée. La solution ci-dessus est de loin la plus simple!
Vous pouvez utiliser git reset --hard A ^
pour accéder au dernier "bon" commit, puis git cherry-pick E
pour appliquer le commit que vous voulez garder.
Ensuite, vous devez forcer le pousser vers la branche pour réinitialiser les choses. Assurez-vous de faire savoir à tous les autres membres de l'équipe ce qui se passe.
drop
Comme Romain l'a suggéré "rebase is another", voici une façon de le faire, en supposant que vous vouliez le résultat final de @ -ABCDE pour être
@ -E
, comme Lasse l'a demandé.
Je propose ceci comme un autre outil dans votre toolbelt : c'est pas la solution la plus simple à ce problème. Il vous permet cependant de supprimer les commits qui ne sont pas dans l'ordre (supprimer A, C, E et conserver B, D, par exemple):
git push -f
ce qui ouvrira votre éditeur (probablement vi
) avec un tampon qui ressemble à ceci:
pick 4231648cb4 Some previous commit d 4bccf2ce81 A some message d 7b4cd5ff17 B some message d faa44efb7c C some message d 0ce0525a79 D some message pick f104648cc3 E some message
Oui, l'ordre des commits de haut en bas est dans l'ordre temporel inverse (l'inverse de git log
) avec le plus récent en bas. C'est pourquoi "les lignes sont exécutées de haut en bas" - du plus ancien au plus récent.
En suivant les instructions, remplacez le mot pick
par d code > (ou
déposer
) sur les lignes que vous souhaitez supprimer.
pick 4231648cb4 Some previous commit pick 4bccf2ce81 A some message pick 7b4cd5ff17 B some message pick faa44efb7c C some message pick 0ce0525a79 D some message pick f104648cc3 E some message # Rebase 76eb9131b5..ed71142fcb onto 4231648cb4 (6 commands) # # Commands: # p, pick <commit> = use commit # r, reword <commit> = use commit, but edit the commit message # e, edit <commit> = use commit, but stop for amending # s, squash <commit> = use commit, but meld into previous commit # f, fixup <commit> = like "squash", but discard this commit's log message # x, exec <command> = run command (the rest of the line) using shell # b, break = stop here (continue rebase later with 'git rebase --continue') # d, drop <commit> = remove commit # l, label <label> = label current HEAD with a name # t, reset <label> = reset HEAD to a label # m, merge [-C <commit> | -c <commit>] <label> [# <oneline>] # . create a merge commit using the original merge commit's # . message (or the oneline, if no original merge commit was # . specified). Use -c <commit> to reword the commit message. # # These lines can be re-ordered; they are executed from top to bottom. # # If you remove a line here THAT COMMIT WILL BE LOST. # # However, if you remove everything, the rebase will be aborted. # # Note that empty commits are commented out
Si vous avez commis une erreur irrémédiable, comme la suppression d'une ligne, abandonnez en quittant sans enregistrer (: q!
) et réessayez.
Si tout semble bon, enregistrez et quittez le tampon (: wq
) et continuez à rebaser jusqu'à ce que votre branche soit corrigée.
Si quelque chose de bizarre se produit après cela (comme vous avez changé un hachage de validation en un qui n'existe pas, ou le rebase s'arrête pour faire quelque chose et vous ne savez pas pourquoi) vous pouvez abandonner complètement le rebase avec git rebase --abort
qui vous ramènera à votre état initial.
Si votre branche semble correcte, forcez le push. p >
git rebase -i HEAD~6
Probablement peu connue, mais la stratégie push par défaut avant git 2 est matching
, c'est-à-dire g lorsque vous git push
, cela va pousser toutes vos branches locales avec les noms de branche distante correspondants, pas seulement votre branche actuelle.
Donc, quand vous git push -f cela forcera le push toutes de vos branches (ceci est arrivé à un collègue hier). Vérifiez avec git config --global push.default
. Cela signifie que si vous jouiez avec une autre branche, cela pourrait aussi forcer la pousser.
Je suggérerais changer la stratégie push par défaut en simple
si ce n'est pas déjà le cas. Il s'agit de la valeur par défaut de git 2.
Si vous utilisez une solution git hébergée de manière centralisée comme Stash / BitBucket, Gitlab ou Github, elles offrent toutes une solution so- appelées règles de "protection des branches" pour empêcher, entre autres, les développeurs de pousser de force vers les branches.
Ajouter une règle pour empêcher le push forcé vers le master
et probablement la version
branches.
Merci beaucoup pour la partie que je n'ai pas eu le courage (et pour les parties, les connaissances) de m'écrire!
@RomainValeri Je suis heureux de vous aider! Je prépare en fait (à donner) une session de formation git et ce genre de chose m'aide à résoudre ces problèmes. Mon objectif principal en donnant mon prochain discours est "ne vous inquiétez pas - il y a 5 façons différentes de faire presque n'importe quoi dans git, et vous pouvez faire la plupart d'entre elles avec confiance que vous pouvez annuler la plupart des erreurs"
@RomainValeri De même, confronté moi-même à ce problème, j'aurais fait ce que j'ai écrit ci-dessus, sans même envisager votre solution beaucoup plus directe. Merci pour ça!
Pouvez-vous nous en dire plus sur les commits que vous souhaitez supprimer? Les commits
A
àD
ne sont-ils pas des validations de fusion ou sont-ils des validations de fusion?ABCD est fusionné à partir du maître (et non plus dans le maître distant) pour cela doit être supprimé. E est mon dernier commit et où HEAD est en ce moment. Je veux juste qu'E soit dans l'histoire de la branche
Pour être clair, voulez-vous annuler les modifications introduites dans ces commits (qui laisseraient les commits dans votre référentiel), ou préférez-vous vous débarrasser complètement des commits comme s'ils ne s'étaient jamais produits?
Débarrassez-vous d'eux si c'est sûr .. :) @ LasseVågsætherKarlsen
En d'autres termes, voulez-vous que le résultat final de
@ -ABCDE
soit@ -E
ou@ -ABCDEFG
oùG < / code> est "le changement inverse de
ABCD
"?Se débarrasser d'eux réécrira l'histoire, ce qui pourrait vous obliger à faire une autre poussée forcée, encore une fois, vous êtes sûr que c'est ce que vous voulez? Si tel est le cas, vous avez déjà obtenu deux réponses en utilisant
git reset
qui vous aideront avec cela.Merci de m'avoir guidé vers une réponse @ LasseVågsætherKarlsen