Question Comment annuler plusieurs validations git?


J'ai un dépôt git qui ressemble à ceci:

A -> B -> C -> D -> HEAD

Je veux que la tête de la branche pointe vers A, c'est-à-dire que je veux que B, C, D et HEAD disparaissent et je veux que la tête soit synonyme de A.

Il semble que je peux soit essayer de rebaser (ne s'applique pas, puisque j'ai poussé les changements entre les deux), ou revenir. Mais comment puis-je annuler plusieurs validations? Est-ce que je reviens un à la fois? L'ordre est-il important?


602
2017-09-23 00:27


origine


Réponses:


Expansion de ce que j'ai écrit dans un commentaire

La règle générale est que vous ne devez pas réécrire (modifier) ​​l'historique que vous avez publié, car quelqu'un pourrait avoir basé son travail dessus. Si vous réécrivez (changez) l'historique, vous feriez des problèmes avec la fusion de leurs modifications et la mise à jour pour celles-ci.

Donc la solution est de créer un nouveau commit lequel retourne les changements que vous voulez vous débarrasser. Vous pouvez le faire en utilisant Git Revert commander.

Vous avez la situation suivante:

A <- B <- C <- D <- maître <- HEAD

(les flèches désignent ici la direction du pointeur: la référence "parent" dans le cas de commits, la validation supérieure dans le cas de la tête de branche (branche ref) et le nom de branche dans le cas de référence HEAD).

Ce que vous devez créer est le suivant:

A <- B <- C <- D <- [(BCD) ^ - 1] <- maître <- HEAD

où "[(BCD) ^ - 1]" signifie le commit qui retourne les changements dans les commits B, C, D. Les mathématiques nous disent que (BCD) ^ - 1 = D ^ -1 C ^ -1 B ^ -1, donc vous pouvez obtenir la situation requise en utilisant les commandes suivantes:

$ git revert --no-commit D
$ git revert --no-commit C
$ git revert --no-commit B
$ git commit -m "the commit message"

Une autre solution serait de check-out  Contenu de commettre A, et commettre cet état:

$ git checkout -f A -- .
$ git commit -a

Ensuite, vous auriez la situation suivante:

A <- B <- C <- D <- A '<- maître <- HEAD

Le commit A 'a le même contenu que commit A, mais est un commit différent (commit, parents, date de commit).

le solution par Jeff Ferland, modifié par Charles Bailey s'appuie sur la même idée, mais utilise git reset:

$ git reset --hard A
$ git reset --soft @{1}  # (or ORIG_HEAD), which is D
$ git commit

914
2017-09-24 08:44



Pour ce faire, il vous suffit d'utiliser le revenir commande, en spécifiant la plage de validations que vous souhaitez annuler.

En tenant compte de votre exemple, vous devrez le faire (en supposant que vous êtes sur le «maître» de la branche):

git revert master~3..master

Cela va créer un nouveau commit dans votre local avec la validation inverse de B, C et D (ce qui signifie qu'il annulera les changements introduits par ces commits):

A <- B <- C <- D <- BCD' <- HEAD

150
2017-07-31 15:00



Voie propre que j'ai trouvé utile

git revert --no-commit HEAD~3..

Cette commande rétablit les 3 dernières validations avec une seule validation.

Également ne réécrit pas l'histoire.


53
2018-03-29 00:00



Similaire à la réponse de Jakub, cela vous permet de sélectionner facilement les commits consécutifs à annuler.

# revert all commits from B to HEAD, inclusively
$ git revert --no-commit B..HEAD  
$ git commit -m 'message'

45
2017-07-31 18:49



git reset --hard a
git reset --mixed d
git commit

Cela agira comme un retour pour tous à la fois. Donnez un bon message de validation.


40
2017-09-23 00:45



Assurez-vous d'abord que votre copie de travail n'est pas modifiée. Alors:

git diff HEAD commit_sha_you_want_to_revert_to | git apply

et juste commettre. Ne pas oublier de documenter quelle est la raison de revenir.


22
2017-09-09 13:13



Je suis tellement frustré qu'on ne peut pas répondre à cette question. Toute autre question concerne la manière de rétablir correctement et de préserver l'histoire. Cette question dit "Je veux que la tête de la branche pointe vers A, c'est-à-dire que je veux que B, C, D et HEAD disparaître et je veux que la tête soit synonyme de A. "

git checkout <branch_name>
git reset --hard <commit Hash for A>
git push -f

J'ai beaucoup appris en lisant le post de Jakub, mais un gars de l'entreprise (avec accès à notre branche "testing" sans Pull-Request) a poussé comme 5 mauvais commits en essayant de réparer et corriger une erreur qu'il a commise il y a 5 ans. Non seulement cela, mais une ou deux demandes Pull ont été acceptées, qui étaient maintenant mauvaises. Alors oubliez-le, j'ai trouvé le dernier bon commit (abc1234) et j'ai juste exécuté le script de base:

git checkout testing
git reset --hard abc1234
git push -f

J'ai dit aux 5 autres gars travaillant dans ce repo qu'ils feraient mieux de noter leurs changements au cours des dernières heures et essuyer / Re-Branche des derniers tests. Fin de l'histoire.


16
2017-07-11 23:29



Le moyen facile de rétablir un groupe de validations sur un référentiel partagé (que les utilisateurs utilisent et que vous souhaitez conserver) est d'utiliser git revert en conjonction avec git rev-list. Le dernier vous fournira une liste de commits, le premier fera le retour lui-même.

Il y a deux façons de le faire. Si vous voulez que les validations multiples soient rétablies dans un seul commit, utilisez:

for i in `git rev-list <first-commit-sha>^..<last-commit-sha>`; do git revert -n $i; done

cela va inverser un groupe de commits dont vous avez besoin, mais laissez tous les changements sur votre arbre de travail, vous devriez les commettre tous comme d'habitude.

Une autre option consiste à avoir un seul commit par modification annulée:

for i in `git rev-list <first-commit-sha>^..<last-commit-sha>`; do git revert --no-edit -s $i; done

Par exemple, si vous avez un arbre de commit comme

 o---o---o---o---o---o--->    
fff eee ddd ccc bbb aaa

pour annuler les changements de eee à bbb, courir

for i in `git rev-list eee^..bbb`; do git revert --no-edit -s $i; done

4
2017-08-04 11:29



Ceci est une extension de l'une des solutions fournies dans la réponse de Jakub

J'étais confronté à une situation où les commits dont j'avais besoin pour revenir en arrière étaient quelque peu complexes, avec plusieurs commits étant des commits de fusion, et j'avais besoin d'éviter de réécrire l'histoire. Je n'ai pas pu utiliser une série de git revert commandes parce que j'ai finalement rencontré des conflits entre les changements de réversion en cours d'ajout. J'ai fini par utiliser les étapes suivantes.

Tout d'abord, vérifiez le contenu du commit cible tout en laissant HEAD à la pointe de la branche:

$ git checkout -f <target-commit> -- .

(Le - s'assure <target-commit> est interprété comme un commit plutôt qu'un fichier; la . fait référence au répertoire courant.)

Ensuite, déterminez quels fichiers ont été ajoutés dans les validations en cours de restauration et doivent donc être supprimés:

$ git diff --name-status --cached <target-commit>

Les fichiers qui ont été ajoutés devraient apparaître avec un "A" au début de la ligne, et il ne devrait pas y avoir d'autres différences. Maintenant, si des fichiers doivent être supprimés, mettez ces fichiers en attente de suppression:

$ git rm <filespec>[ <filespec> ...]

Enfin, commettez la réversion:

$ git commit -m 'revert to <target-commit>'

Si vous le souhaitez, assurez-vous que nous sommes de retour à l'état souhaité:

$git diff <target-commit> <current-commit>

Il ne devrait pas y avoir de différences.


4
2017-11-08 06:39



Aucun de ceux qui travaillaient pour moi, donc j'ai eu trois commits à revenir (les trois derniers commits), alors j'ai fait:

git revert HEAD
git revert HEAD~2
git revert HEAD~4
git rebase -i HEAD~3 # pick, squash, squash

Travaillé comme un charme :)


1
2018-01-24 00:28