Puis-je supprimer un git commit mais conserver les modifications

1057

Dans l'une de mes branches de développement, j'ai apporté quelques modifications à ma base de code. Avant de pouvoir terminer les fonctionnalités sur lesquelles je travaillais, j'ai dû passer de ma branche actuelle à master pour démo certaines fonctionnalités. Mais le simple fait d'utiliser un "git checkout master" a préservé les modifications que j'ai également apportées dans ma branche de développement, brisant ainsi certaines des fonctionnalités de master. Donc, ce que j'ai fait, c'est de valider les modifications sur ma branche de développement avec un message de validation "commit temporaire", puis de vérifier le maître pour la démo.

Maintenant que j'en ai terminé avec la démo et que je retourne travailler sur ma branche de développement, je voudrais supprimer le "commit temporaire" que j'ai fait tout en conservant les modifications que j'ai apportées. Est-ce possible?

tanookiben
la source
65
La prochaine fois:git stash
Matt Ball le
51
@MattBall, pas nécessairement. Bien que ce git stashsoit un bon outil, les validations jetables "en cours" sont également un appareil tout à fait légitime.
kostix
3
Ceci est un grand détroit de ressources de Github: Comment annuler (presque) n'importe quoi avec Git
jasonleonhard
9
@MattBall @kostix Ouais, le stash est particulièrement mal adapté au stashing "à long terme", étant donné qu'il s'agit d'une pile globale pour l'ensemble du référentiel. Je veux pouvoir cacher les modifications sur une branche, puis aller dans d'autres branches, faire quoi que ce soit d'autre, revenir quelques jours plus tard sans craindre que j'aurais pu l'utiliser git stashsur une autre branche dans l'intervalle.
Alec
4
Une chose à noter stashest qu'il est complètement local et sera sujet à la perte de code due à la suppression ou à la recréation ou à la défaillance ou à la perte de matériel. OMI, il ne devrait vraiment être utilisé que pour les travaux en cours à très court terme. Aimez un engagement WIP avant de partir en vacances: P .. appelez cela un commit de vidage de cerveau!
ϹοδεMεδιϲ

Réponses:

1620

C'est aussi simple que ça:

git reset HEAD^

Remarque: certains shells sont traités ^comme un caractère spécial (par exemple, certains shells Windows ou ZSH avec globbing activé ), vous devrez donc peut-être citer "HEAD^"dans ces cas.

git resetsans --hardou --softdéplace votre HEADpour pointer vers le commit spécifié, sans modifier aucun fichier. HEAD^fait référence au (premier) commit parent de votre commit actuel, qui dans votre cas est le commit avant le commit temporaire.

Notez qu'une autre option consiste à continuer normalement, puis au prochain point de validation à la place, exécutez:

git commit --amend [-m … etc]

qui modifiera à la place le commit le plus récent, ayant le même effet que ci-dessus.

Notez que cela (comme avec presque toutes les réponses git) peut causer des problèmes si vous avez déjà poussé le mauvais commit vers un endroit où quelqu'un d'autre peut l'avoir retiré. Essayez d'éviter cela

Gareth
la source
47
C'est de loin le plus simple, mais si vous avez déjà poussé votre commit vers une télécommande et que quelqu'un d'autre l'a retiré, je serais très hésitant à faire autre chose que des excuses.
atw13
11
@sicophrenic, ne manquez pas l'occasion de lire "Reset Demystified" à cette occasion.
kostix
33
Je reçois More?après avoir fait ça. Tout ce que je tape à cette invite me donnefatal: ambiguous argument 'HEADwhateverItypedIn': unknown revision or path not in the working tree.
DaAwesomeP
50
@DaAwesomeP sonne comme si vous utilisiez un shell qui traite ^comme un caractère spécial. Vous pouvez soit citer la référence "HEAD^", soit utiliser la syntaxe alternative HEAD~1non citée
Gareth
8
J'ai travaillé pour moi, mais j'ai dû échapper au personnagegit reset HEAD\^
cevaris
189

Il existe deux façons de gérer cela. Ce qui est plus facile dépend de votre situation

Réinitialiser

Si le commit dont vous souhaitez vous débarrasser était le dernier commit, et que vous n'avez effectué aucun travail supplémentaire, vous pouvez simplement utiliser git-reset

git reset HEAD^

Ramène votre branche au commit juste avant votre HEAD actuel. Cependant, cela ne change pas réellement les fichiers dans votre arborescence de travail. En conséquence, les changements qui étaient dans ce commit apparaissent comme modifiés - c'est comme une commande «non validée». En fait, j'ai un alias pour ça.

git config --global alias.uncommit 'reset HEAD^'

Ensuite, vous pouvez simplement utiliser git uncommità l'avenir pour sauvegarder un commit.

Écrasement

Écraser un commit signifie combiner deux ou plusieurs commits en un seul. Je le fais assez souvent. Dans votre cas, une fonctionnalité à moitié terminée est validée, puis vous la terminez et la validez à nouveau avec le message de validation approprié et permanent.

git rebase -i <ref>

Je dis ci-dessus parce que je tiens à préciser que cela pourrait être un nombre illimité de retours. Exécutez git loget trouvez le commit dont vous voulez vous débarrasser, copiez son SHA1 et utilisez-le à la place <ref>. Git vous fera passer en mode rebase interactif. Il montrera tous les commits entre votre état actuel et tout ce que vous mettez en place <ref>. Donc, s'il <ref>y a 10 commits, il vous montrera les 10 commits.

Devant chaque commit, il aura le mot pick. Trouvez le commit dont vous voulez vous débarrasser et changez-le picken fixupou squash. L'utilisation fixupsupprime simplement le message et valide les modifications dans son prédécesseur immédiat dans la liste. Le squashmot-clé fait la même chose, mais vous permet de modifier le message de validation du commit nouvellement combiné.

Notez que les validations seront réengagées dans l'ordre dans lequel elles apparaissent dans la liste lorsque vous quittez l'éditeur. Donc, si vous avez effectué une validation temporaire, puis effectué d'autres travaux sur la même branche et terminé la fonctionnalité dans une validation ultérieure, l'utilisation de rebase vous permettrait de trier à nouveau les validations et de les écraser.

ATTENTION:

Le remodelage modifie l'historique - NE FAITES PAS cela sur les commits que vous avez déjà partagés avec d'autres développeurs.

Stashing

À l'avenir, pour éviter ce problème, envisagez d'utiliser git stashpour stocker temporairement le travail non engagé.

git stash save 'some message'

Cela stockera vos modifications actuelles sur le côté dans votre liste de dissimulation. Ci-dessus est la version la plus explicite de la commande stash, permettant un commentaire pour décrire ce que vous cachez. Vous pouvez également simplement exécuter git stashet rien d'autre, mais aucun message ne sera stocké.

Vous pouvez parcourir votre liste de dissimulations avec ...

git stash list

Cela vous montrera toutes vos cachettes, sur quelles branches elles ont été faites, et le message et au début de chaque ligne, et l'identifiant de cette cachette qui ressemble à ceci stash@{#}où # est sa position dans le tableau des cachettes.

Pour restaurer une cachette (ce qui peut être fait sur n'importe quelle branche, peu importe où la cachette a été créée à l'origine), vous exécutez simplement ...

git stash apply stash@{#}

Encore une fois, il y a la position dans le tableau des stashes. Si la cachette que vous souhaitez restaurer est en 0position - c'est-à-dire, si c'était la cachette la plus récente. Ensuite , vous pouvez simplement exécuter la commande sans spécifier la position de Stash, git suppose que vous voulez dire que le dernier: git stash apply.

Ainsi, par exemple, si je me retrouve à travailler sur la mauvaise branche - je peux exécuter la séquence de commandes suivante.

git stash
git checkout <correct_branch>
git stash apply

Dans votre cas, vous avez déplacé un peu plus les branches, mais la même idée s'applique toujours.

J'espère que cela t'aides.

eddiemoya
la source
6
git config --global alias.uncommit reset HEAD^juste des alias non validés pour réinitialiser. Au lieu de cela, faitesgit config --global alias.uncommit 'reset HEAD^'
mernst
3
Notez que vous devrez utiliser ^^ au lieu de ^ si vous l'utilisez dans une invite de commande Windows.
Pramod BR
106

Je pense que tu cherches ça

git reset --soft HEAD~1

Il annule la validation la plus récente tout en conservant les modifications apportées à cette validation dans le transfert.

Sudhanshu Jain
la source
7
Merci. Cela a fonctionné pour moi. Appeler git reset HEAD^sous Windows demande simplement "Plus?" - quoi que cela signifie
Tyron
12
@Tyron ^est un caractère d'échappement sous DOS. Lorsqu'il est associé à une nouvelle ligne, il sert d'invite de continuation pour la commande précédente. La saisie git reset HEAD^^devrait fonctionner sous Windows.
trk
40

Oui, vous pouvez supprimer votre commit sans supprimer les modifications: git reset @ ~

DURGESH
la source
7
C'est vraiment ce que je veux, et ce sera la réponse acceptée à mon avis, merci beaucoup!
Carlos Liu
2
Syntaxe intéressante et compacte, je ne l'ai jamais vue ou utilisée auparavant. En quoi est-ce différent de git reset --softou git reset --keep?
user776686
18

Vous recherchez soit git reset HEAD^ --softou git reset HEAD^ --mixed.

Il y a 3 modes à la commande de réinitialisation comme indiqué dans la documentation :

git reset HEAD^ --soft

annuler le git commit. Des modifications existent toujours dans l'arborescence de travail (le dossier du projet) + l'index (--cached)

git reset HEAD^ --mixed

annuler git commit+ git add. Des modifications existent toujours dans l'arborescence de travail

git reset HEAD^ --hard

Comme si vous n'aviez jamais apporté ces modifications à la base de code. Les modifications ont disparu de l'arborescence de travail.

Bar Horing
la source
9

Pour ceux qui utilisent zsh, vous devrez utiliser ce qui suit:

git reset --soft HEAD\^

Expliqué ici: https://github.com/robbyrussell/oh-my-zsh/issues/449

Dans le cas où l'URL devient morte, la partie importante est:

Échappez au ^ dans votre commande

Vous pouvez également utiliser HEAD ~ pour ne pas avoir à y échapper à chaque fois.

Greg Hilston
la source
15
Je ne me souviens jamais de cette commande et je dois la rechercher sur Google et trouver ma propre réponse hahahaha
Greg Hilston
2
git reset HEAD^travaille dans zsh pour moi, peut avoir été corrigé.
Ben Kolya Mansley
@BenKolyaMansley Quelle version de zsh utilisez-vous?
Greg Hilston
J'utilisezsh 5.3 (x86_64-apple-darwin18.0)
Ben Kolya Mansley
7

Dans mon cas, j'ai déjà poussé au repo. Aie!

Vous pouvez annuler une validation spécifique tout en conservant les modifications dans vos fichiers locaux en procédant comme suit:

git revert -n <sha>

De cette façon, j'ai pu conserver les modifications dont j'avais besoin et annuler un commit déjà poussé.

Wim Feijen
la source
Dans mon cas, je devais rétablir quelque chose que j'avais déjà poussé vers le référentiel distant. Encore plus aïe! Le meilleur moyen était git revert bad-commit-sha, alors git revert -n revert-commit-just-created-sha, puis la réparation de là. Tu m'as à mi-chemin. Merci!
TinkerTenorSoftwareGuy
Cela semble faire le contraire, non? Il crée de nouvelles modifications qui correspondent à une annulation des modifications effectuées dans le commit sélectionné. Si vous deviez valider ces nouvelles modifications, vous auriez annulé le travail que vous vouliez réellement conserver.
svaens
3

L'utilisation de git 2.9 (précisément 2.9.2.windows.1) vous git reset HEAD^invite à en savoir plus; Je ne sais pas ce que l'on attend ici. Veuillez vous référer à la capture d'écran ci-dessous

entrez la description de l'image ici

Trouvé une autre solution à l' git reset HEAD~#numberOfCommitsaide de laquelle nous pouvons choisir de sélectionner le nombre de validations locales que vous souhaitez réinitialiser en gardant vos modifications intactes. Par conséquent, nous avons la possibilité de supprimer tous les commits locaux ainsi qu'un nombre limité de commits locaux.

Reportez-vous ci-dessous des captures d'écran montrant git reset HEAD~1en action: entrez la description de l'image ici

entrez la description de l'image ici

nkharche
la source
Vous devez probablement échapper au caractère ^ - essayez git reset "HEAD ^" ou git reset HEAD \ ^
Mark Fisher
De plus, un git reset HEAD ^^ fonctionne comme un seul ^ est traité comme une nouvelle ligne.
John Oss du
2

Une autre façon de le faire.

Ajoutez la validation au-dessus de la validation temporaire, puis procédez comme suit:

git rebase -i

Pour fusionner deux commits en un (la commande ouvrira un fichier texte avec des instructions explicites, modifiez-le).

Ruslan Osipov
la source
2
Techniquement correct mais loin d'être aussi élégant que de le faire simplement git reset HEAD^. Git rebase a beaucoup de place pour l'erreur ici.
TheBuzzSaw
0

2020 Simple:

git reset <commit_hash>

(Le hachage de validation du dernier commit que vous souhaitez conserver).

Si le commit a été poussé, vous pouvez alors faire:

git push -f

Vous conserverez localement les modifications non validées

Xys
la source