web-dev-qa-db-fra.com

Comment annuler plusieurs commits git?

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

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

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

On dirait que je peux soit essayer de rebaser (ne s'applique pas, car j'ai poussé les changements entre les deux), soit revenir. Mais comment puis-je annuler plusieurs commits? Est-ce que je retourne un à la fois? L'ordre est-il important?

821
Bill

Développer ce que j'ai écrit dans un commentaire

La règle générale est que vous ne devriez pas réécrire (changer) l'historique que vous avez publié, car quelqu'un aurait pu baser son travail sur celui-ci. Si vous réécrivez (modifiez) l'historique, vous rencontrerez des problèmes de fusion de leurs modifications et de mise à jour.

La solution consiste donc à créer une nouvelle validation qui rétablit les modifications dont vous souhaitez vous débarrasser. Vous pouvez le faire en utilisant la commande git revert .

Vous avez la situation suivante:

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

(Les flèches font ici référence à la direction du pointeur: la référence "parent" dans le cas des validations, la validation supérieure dans le cas de la tête de branche (ref ref) et le nom de la branche dans le cas de HEAD référence).

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 annule les modifications 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 à l'aide des 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 commander le contenu de la validation A et de valider cet état:

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

Alors vous auriez la situation suivante:

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

La validation A 'a le même contenu que la validation A, mais est une validation différente (message de validation, parents, date de validation).

Le solution de Jeff Ferland, modifié par Charles Bailey repose 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
1163
Jakub Narębski

Pour ce faire, vous devez simplement utiliser la commande revert, en spécifiant la plage des commits que vous souhaitez annuler.

En prenant en compte votre exemple, vous devriez faire ceci (en supposant que vous êtes sur le "maître" de la branche):

git revert master~3..master

Cela créera un nouveau commit dans votre section locale avec le commit inverse de B, C et D (ce qui signifie qu’il annulera les modifications introduites par ces commits):

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

Manière propre que j'ai trouvé utile

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

Cette commande annule les 3 derniers commits avec un seul commit.

Aussi ne réécrit pas l'histoire.

164
deepdive

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

# revert all commits from B to HEAD, inclusively
$ git revert --no-commit B..HEAD  
$ git commit -m 'message'
62
konyak
git reset --hard a
git reset --mixed d
git commit

Cela agira comme un revers pour tous à la fois. Donne un bon message de commit.

54
Jeff Ferland

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

git diff HEAD commit_sha_you_want_to_revert_to | git apply

et puis juste commettre. N'oubliez pas de documenter quelle est la raison du retour.

35
mateusz.fiolka

Je suis tellement frustré que cette question ne peut pas être simplement répondu. Toute autre question concerne la manière de revenir en arrière 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 B, C, D et HEAD à disparaît = 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 message de Jakub, mais un membre de la société (ayant accès à Push pour notre branche "testing" sans Pull-Request) a poussé comme 5 mauvais commets en essayant de réparer une erreur qu'il avait faite il y a 5 commits. Non seulement cela, mais une ou deux demandes de tirage ont été acceptées, qui étaient maintenant mauvaises. Alors oubliez ça, j'ai trouvé le dernier bon commit (abc1234) et viens d'exécuter le script de base:

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

J'ai dit aux 5 autres membres de ce dépôt qu'ils feraient mieux de prendre note de leurs modifications des dernières heures et de Wipe/Re-Branch des derniers tests. Fin de l'histoire.

26
Suamere

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

J'ai été confronté à une situation dans laquelle les commits que je devais annuler étaient assez complexes, plusieurs commits étant des commits de fusion et je devais éviter de réécrire l'historique. Je n'ai pas pu utiliser une série de commandes git revert parce que j'ai fini par rencontrer des conflits entre les modifications de réversion ajoutées. J'ai fini par suivre les étapes suivantes.

Commencez par consulter le contenu du commit cible tout en laissant HEAD à l'extrémité de la branche:

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

(Le - s'assure que <target-commit> est interprété comme une validation plutôt que comme un fichier; le. Fait référence au répertoire actuel.)

Ensuite, déterminez quels fichiers ont été ajoutés aux validations en cours d'annulation, et doivent donc être supprimés:

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

Les fichiers ajoutés doivent apparaître avec un "A" au début de la ligne et il ne doit y avoir aucune autre différence. Maintenant, si des fichiers doivent être supprimés, organisez ces fichiers en vue de leur suppression:

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

Enfin, commettez la réversion:

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

Si vous le souhaitez, assurez-vous de revenir à l'état souhaité:

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

Il ne devrait y avoir aucune différence.

8
Warren Dew

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 l'historique) consiste à utiliser git revert conjointement 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 souhaitez rétablir plusieurs validations en une seule utilisation, procédez comme suit:

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

cela annulera un groupe de commits dont vous avez besoin, mais laissez toutes les modifications dans votre arbre de travail, vous devriez toutes les commettre comme d'habitude.

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

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 tel que

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

pour revenir aux modifications de eee à bbb, exécutez

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

À mon avis, un moyen très simple et propre pourrait être:

retourner à A

git checkout -f A

pointez la tête du maître à l'état actuel

git symbolic-ref HEAD refs/heads/master

enregistrer

git commit
0
nulll