Nous avons un projet où nous avons dû à un moment revenir un peu en arrière dans l'histoire de Git et démarrer une nouvelle branche (que nous appellerons ici "new_branch") à partir d'un ancien commit. Maintenant, tout le développement se produit dans new_branch, et l'ancien maître est inutile après le point où la branche a été créée. Alors maintenant, nous avons une situation qui ressemble à ceci:
C1 = Commit 1, C2 = Commit 2, etc.
C1
|
C2
|\
C3 \
| \
... \
| \
Master --> C17 \
\
C18
|
...
|
C37 <-- new_branch
Donc ce que je veux faire maintenant, c'est abandonner (annuler?) Tous les commits faits dans Master après le point de branchement, que est, C3 à C17. Au moins, c'est ce que je pense vouloir faire? Je veux dire, C3 à C17 sont inutiles maintenant, et si je pouvais les annuler, je pourrais fusionner new_branch dans Master, pour rendre Master à nouveau utile.
Ma question est donc la suivante: est-ce la bonne approche à réaliser mon objectif de pouvoir à nouveau utiliser Master? Et quelle commande (j'utilise Git depuis la ligne de commande sous Linux) dois-je utiliser pour annuler les commits C3 à C17? C'est un projet pour le travail, il est donc très important que je réussisse.
3 Réponses :
donc si vous voulez effacer c3 - c17, vous pouvez extraire c3 et ensuite mettre les modifications de votre nouvelle branche par dessus en utilisant merge. Ensuite, si vous repoussez vers le maître, c3 - c17 disparaîtra
Quel serait le but de la fusion? Comme il se trouve au-dessus d'un état HEAD détaché, et qu'il ne sera donc pas automatiquement incorporé dans master , le push devra être forcé / sera une réécriture de l'historique; ce qui signifie que vous pouvez aussi bien renoncer à la fusion et simplement forcer la poussée à partir de la nouvelle branche
mais vous avez dit que vous vouliez à la fois le nouveau code des branches et le c2. Vous devrez les combiner d'une manière ou d'une autre, c'est-à-dire fusionner? Je veux dire ou vous pouvez rebaser ou le faire manuellement, mais peu importe ce que vous voulez, c'est votre projet
Ok ... d'abord, je ne suis pas OP, alors je n'ai rien dit de tel. Deuxièmement, C2 est déjà "dans" new_branch
La solution la plus simple consiste simplement à forcer le transfert de new_branch sur le master de l'origine :
git branch -f master new_branch && git push -f origin master
Puis à réparer votre maître local de plusieurs manières:
git reset --hard origin / master ( sur branche master ) git branch -D master ( pas sur branch master , tirez à nouveau pour obtenir la mise à jour) Il n'est pas nécessaire dans ce scénario de rebaser le master existant avec le new_branch .
Cela effacera (évidemment) l'existant master et remplacez-le par le contenu et l'historique de new_branch . Soyez sûr de votre intention.
Comme Romain le souligne ci-dessous, utilisez git branch -f empêche l'étape de correction locale:
git checkout new_branch git push -f master
puisqu'elle déplace également le pointeur de branche localement.
Comme pour de nombreux problèmes git, il existe de nombreuses façons de résoudre ce problème, et d'autres réponses peuvent être tout aussi viables.
C'est l'attrait indéniable d'être très simple. Merci beaucoup. Je pourrais bien finir par le faire, à moins que quelqu'un ne me donne une suggestion encore meilleure.
@Enfors Pleasure. Attendez de voir les autres réponses, il y a toujours plusieurs façons de résoudre les problèmes git, avec différents flux de travail ayant des avantages différents. Si vous l'acceptez, je saurai (heureusement) que cela a fonctionné pour vous!
Excellente réponse. Juste une variante pour la brièveté (de la branche master): git branch -f master new_branch && git push -f origin master . Juste pour donner une autre façon de parvenir à la même solution (mais l'idée est la même donc elle ne méritait pas une réponse complète)
Hé encore @RomainValeri! git tag team;) (jeu de mots)
@RomainValeri J'aime votre approche, car cela signifie faire les choses localement ( git branch -f master new_branch ) pour que je puisse valider que tout semble bon, avant je fais le push - droite?
@Endors Oui, c'est la principale raison pour laquelle j'ai tendance à procéder de cette façon. Une commande pour le définir correctement localement, une pour refléter l'opération sur le serveur distant.
@RomainValeri N'hésitez pas à poster une nouvelle réponse, c'est techniquement un workflow différent. Je reculerai celui-ci si vous le faites.
@msanford Je l'ai considéré un moment au début, mais pas de soucis, je pense que c'est assez clair comme vous l'avez présenté.
Il existe deux approches générales pour ce faire; chacun a des inconvénients, cela dépend donc de ce qui est le plus important dans votre situation. Chaque approche a également des variantes, avec des compromis plus petits ...
La première approche est une réécriture de l'historique du master . Cela signifie simplement déplacer le master d'une manière qui supprime de son historique certains commits qui font actuellement partie de son historique.
Toute réécriture d'historique a certains coûts, peu importe comment vous le faites - il est donc généralement préférable de le faire de la manière la plus simple qui répond à vos besoins. Dans ce cas, c'est probablement
C1 - C2 - C3 - ... - C17 - R -- M <--(master)
\ /
C18 - ........... - C37 <--(new_branch)
Ceci est similaire en effet à l'approche recommandée par msanford, mais présente deux avantages. Tout d'abord, c'est un peu plus concis (en ce sens qu'il met d'abord à jour la branche locale et l'utilise comme configuration pour mettre à jour le rmeote). Deuxièmement, le remplacement de -f par --force-with-bail offre un peu de sécurité supplémentaire, en ce sens qu'il garantit que la télécommande n'a rien de nouveau engagé que vous vous n'êtes pas au courant. (Dans votre cas spécifique, ce n'est probablement pas vraiment un problème; mais c'est une très bonne habitude à prendre.)
Le coût d'une réécriture de l'historique d'une branche qui a été partagée entre les dépôts ( c'est-à-dire push ed, puis - au moins potentiellement - fetch ed ou pull ed dans d'autres dépôts) est que d'autres dépôts partageant la branche verront la branche se déplace de manière "inattendue" et devra se remettre. La procédure de récupération est documentée dans la documentation git rebase , dans la section «récupération à partir du rebase en amont». Il est important de se coordonner avec toute l'équipe lors de cette opération, car si un membre de l'équipe fait la mauvaise chose lors de la récupération, il peut annuler la réécriture de l'historique.
L'avantage est que vous terminez avec une très belle histoire qui ne montre pas du tout les commits supprimés. (Attention, cependant, si ces commits ont été partagés, rien de ce que vous pouvez faire ne garantit qu'ils sont "partis pour toujours". S'ils contiennent des informations sensibles, c'est un plus gros problème et les informations doivent être considérées comme compromises - bien qu'il y en ait étapes que vous pouvez prendre pour au moins essayer d'empêcher la propagation de telles informations.)
Donc, si vous pouvez pratiquement vous coordonner avec tous les utilisateurs du dépôt, c'est une bonne façon de procéder. Mais si vous ne pouvez pas, il existe un autre moyen.
La deuxième approche consiste à annuler les modifications sur le maître - c'est-à-dire à le ramener à C2 - puis à fusionner new_branch in. Ceci est conforme à la façon dont les branches sont "censées" se déplacer habituellement, donc ne supporte pas les coûts d'une réécriture. Mais cela signifie que les commits abandonnés (et leur annulation) restent à jamais dans l’historique.
git checkout master git revert -n C3..HEAD git commit git merge new_branch
Cela vous donne un historique comme
git checkout master git reset --hard new_branch git push --force-with-lease
supprimer le maître, renommer new_branch en maître
Êtes-vous en train de dire que tous les commits de C3 à C17 doivent être abandonnés?
Oui, les C3 à C17 se sont produits il y a 7 semaines et se sont terminés dans une impasse. Nous avons évolué depuis longtemps.