Comment puis-je écraser mon dernier X en un seul commit en utilisant Git?
Utilisez git rebase -i
et remplacez “pick” lors de la deuxième git rebase -i
par “squash” ou “fixup”, comme décrit dans le manuel .
Dans cet exemple,
est soit le hachage SHA1, soit l’emplacement relatif du HEAD de la twig actuelle à partir duquel les validations sont analysées pour la commande rebase. Par exemple, si l’utilisateur souhaite afficher 5 commits du HEAD actuel, la commande est git rebase -i HEAD~5
.
Vous pouvez le faire assez facilement sans git rebase
ou git merge --squash
. Dans cet exemple, nous écraserons les 3 derniers commits.
Si vous voulez écrire le nouveau message de validation à partir de zéro, cela suffit:
git reset --soft HEAD~3 && git commit
Si vous voulez commencer à éditer le nouveau message de validation avec une concaténation des messages de validation existants (c.-à-d. Similaire à ce qu’une liste de commandes d’ git rebase -i
pick / squash / squash /… / squash git rebase -i
vous lancerait), vous devez extraire ces messages et les passer à git commit
:
git reset --soft HEAD~3 && git commit --edit -m"$(git log --format=%B --reverse HEAD..HEAD@{1})"
Ces deux méthodes écrasent les trois derniers commits en un seul et même nouvel engagement de la même manière. La réinitialisation logicielle ne fait que redirect HEAD vers le dernier commit que vous ne voulez pas écraser. Ni l’index ni l’arborescence de travail ne sont touchés par la réinitialisation logicielle, laissant l’index dans l’état souhaité pour votre nouvel commit (c’est-à-dire qu’il contient déjà toutes les modifications des commits que vous allez «jeter»).
Vous pouvez utiliser git merge --squash
pour cela, qui est légèrement plus élégant que git rebase -i
. Supposons que vous soyez maître et que vous vouliez écraser les 12 derniers commits en un.
AVERTISSEMENT: Assurez-vous d’abord de valider votre travail – vérifiez que l’ git status
est propre (car git reset --hard
rejettera les modifications par étapes ou non)
Alors:
# Reset the current branch to the commit just before the last 12: git reset --hard HEAD~12 # HEAD@{1} is where the branch was just before the previous command. # This command sets the state of the index to be as it would just # after a merge from that commit: git merge --squash HEAD@{1} # Commit those squashed changes. The commit message will be helpfully # prepopulated with the commit messages of all the squashed commits: git commit
La documentation de git merge
décrit plus en détail l’option --squash
.
Mise à jour: le seul avantage réel de cette méthode par rapport à la git reset --soft HEAD~12 && git commit
simplifiée git reset --soft HEAD~12 && git commit
suggérée par Chris Johnsen dans sa réponse est que le message de validation est rempli avec chaque message de validation que vous écrasez.
Je recommande d’éviter les git reset
lorsque cela est possible – en particulier pour les novices Git. A moins que vous ayez vraiment besoin d’automatiser un processus basé sur un certain nombre de commits, il existe une manière moins exotique …
git merge --squash (working branch name)
git commit
Le message de validation sera pré-rempli en fonction du squash.
Basé sur la réponse de Chris Johnsen ,
Ajouter un alias global “squash” de bash: (ou Git Bash sous Windows)
git config --global alias.squash '!f(){ git reset --soft HEAD~${1} && git commit --edit -m"$(git log --format=%B --reverse HEAD..HEAD@{1})"; };f'
… ou en utilisant l’invite de commandes de Windows:
git config --global alias.squash "!f(){ git reset --soft HEAD~${1} && git commit --edit -m\"$(git log --format=%B --reverse HEAD..HEAD@{1})\"; };f"
Votre ~/.gitconfig
devrait maintenant contenir cet alias:
[alias] squash = "!f(){ git reset --soft HEAD~${1} && git commit --edit -m\"$(git log --format=%B --reverse HEAD..HEAD@{1})\"; };f"
Usage:
git squash N
… Qui écrase automatiquement les N
derniers commits inclus.
Note: Le message de validation résultant est une combinaison de tous les commits écrasés, dans l’ordre. Si vous n’êtes pas satisfait de cela, vous pouvez toujours vous git commit --amend
pour le modifier manuellement. (Ou éditez l’alias pour correspondre à vos goûts.)
Grâce à cet article de blog, j’ai trouvé que vous pouvez utiliser cette commande pour écraser les 3 derniers commits:
git rebase -i HEAD~3
C’est pratique car cela fonctionne même lorsque vous êtes sur une twig locale sans informations de suivi / repo à distance.
La commande ouvre l’éditeur de rebase interactif qui vous permet ensuite de réorganiser, de squash, de reword, etc.
Si vous utilisez TortoiseGit, vous pouvez Combine to one commit
la fonction Combine to one commit
:
Show Log
Combine to one commit
dans le menu contextuel.
Cette fonction exécute automatiquement toutes les étapes simples nécessaires. Malheureusement, uniquement disponible pour Windows.
Sur la base de cet article, j’ai trouvé cette méthode plus facile pour mon compte utilisateur.
Ma twig “dev” était en avance de “origine / dev” par 96 commits (donc ces commits n’ont pas encore été poussés vers la télécommande).
Je voulais écraser ces commits en un avant de pousser le changement. Je préfère réinitialiser la twig à l’état “origine / dev” (cela laissera toutes les modifications des 96 commits non mises en scène) et ensuite valider les modifications immédiatement:
git reset origin/dev git add --all git commit -m 'my commit message'
1) Identifier le hash court commit
# git log --pretty=oneline --abbrev-commit abcd1234 Update to Fix for issue B cdababcd Fix issue B deab3412 Fix issue A ....
Ici même, git log --oneline
peut également être utilisé pour obtenir un hachage court.
2) Si vous voulez écraser (fusionner) les deux derniers commits
# git rebase -i deab3412
3) Cela ouvre un éditeur nano
pour la fusion. Et on dirait ci-dessous
.... pick cdababcd Fix issue B pick abcd1234 Update to Fix for issue B ....
4) Renommez le mot pick
en squash
qui est présent avant abcd1234
. Après le renommer, il devrait être comme ci-dessous.
.... pick cdababcd Fix issue B squash abcd1234 Update to Fix for issue B ....
5) Maintenant, enregistrez et fermez l’éditeur nano
. Appuyez sur ctrl + o
et appuyez sur Enter
pour enregistrer. Et puis appuyez sur ctrl + x
pour quitter l’éditeur.
6) Ensuite, l’éditeur nano
s’ouvre à nouveau pour mettre à jour les commentaires, le cas échéant le mettre à jour.
7) Maintenant, il est correctement écrasé, vous pouvez le vérifier en vérifiant les journaux.
# git log --pretty=oneline --abbrev-commit 1122abcd Fix issue B deab3412 Fix issue A ....
8) Maintenant, poussez pour repo. Notez pour append +
signe avant le nom de la twig. Cela signifie une poussée forcée.
# git push origin +master
Note: Ceci est basé sur l’utilisation de git sur un shell ubuntu
. Si vous utilisez différents Windows
exploitation ( Windows
ou Mac
), les commandes ci-dessus sont identiques, à l’exception de l’éditeur. Vous pourriez avoir un éditeur différent.
Pour ce faire, vous pouvez utiliser la commande git suivante.
git rebase -i HEAD~n
n (= 4 ici) est le numéro du dernier commit. Ensuite, vous avez les options suivantes,
pick 01d1124 Message.... pick 6340aaa Message.... pick ebfd367 Message.... pick 30e0ccb Message....
Mise à jour comme ci-dessous,
p 01d1124 Message.... s 6340aaa Message.... s ebfd367 Message.... s 30e0ccb Message....
Pour plus de détails, cliquez sur le lien
Bonne chance!!
Si vous êtes sur une twig distante (appelée feature-branch
) clonée à partir d’un référentiel d’or ( golden_repo_name
), alors voici la technique pour écraser vos commits en un seul:
Commander le repo d’or
git checkout golden_repo_name
Créez une nouvelle twig (repo en or) comme suit
git checkout -b dev-branch
Squash fusionne avec votre twig locale que vous avez déjà
git merge --squash feature-branch
Validez vos modifications (ce sera le seul commit qui passe dans la twig dev)
git commit -m "My feature complete"
Poussez la twig dans votre référentiel local
git push origin dev-branch
La réponse aux anomalies est bonne, mais je ne me sentais pas en sécurité , alors j’ai décidé d’append quelques captures d’écran.
Voyez où vous êtes avec git log
. Plus important encore, trouvez le hash de validation du premier commit que vous ne voulez pas écraser. Donc seulement le:
Exécutez git rebase -i [your hash]
, dans mon cas:
$ git rebase -i 2d23ea524936e612fae1ac63c95b705db44d937d
Dans mon cas, je veux tout écraser sur le commit qui était le premier dans le temps. La commande va du premier au dernier, donc exactement comme dans git log
. Dans mon cas, je veux:
Si vous avez sélectionné un seul commit et écrasé le rest, vous pouvez ajuster un message de validation:
C’est tout. Une fois que vous avez enregistré ceci ( :wq
), vous avez terminé. Regardez-le avec le git log
.
C’est du super kludgy, mais d’une manière plutôt cool, alors je vais le jeter dans le ring:
GIT_EDITOR='f() { if [ "$(basename $1)" = "git-rebase-todo" ]; then sed -i "2,\$s/pick/squash/" $1; else vim $1; fi }; f' git rebase -i foo~5 foo
Traduction: fournir un nouvel “éditeur” pour git qui, si le nom de fichier à éditer est git-rebase-todo
(l’invite interactive de rebase) modifie tout sauf le premier “pick” pour “squash”, et génère autrement vim – pour que Lorsque vous êtes invité à modifier le message de validation écrasé, vous obtenez vim. (Et évidemment, j’écrasais les cinq derniers commits sur foo, mais vous pouviez changer cela comme vous le souhaitez.)
Je ferais probablement ce que Mark Longair a suggéré , cependant.
Si vous souhaitez écraser chaque validation dans un seul commit (par exemple, lors de la première publication publique d’un projet), essayez:
git checkout --orphan git commit
Ce qui peut être vraiment pratique: trouvez le hash de validation que vous voulez écraser par dessus; dis que c’est d43e15
maintenant utiliser
git reset d43e15
git commit -am 'new commit name'
Je pense que le moyen le plus simple de le faire est de créer une nouvelle twig de master et de faire une fusion –squash de la twig de fonctions.
git checkout master git checkout -b feature_branch_squashed git merge --squash feature_branch
Ensuite, vous avez toutes les modifications prêtes à être validées.
Je trouve qu’une solution plus générique ne consiste pas à spécifier les commits «N», mais plutôt la twig / l’ID de validation que vous voulez utiliser. C’est moins enclin aux erreurs que de compter les commits jusqu’à une validation spécifique – il suffit de spécifier directement la balise, ou si vous voulez vraiment compter, vous pouvez spécifier HEAD ~ N.
Dans mon stream de travail, je lance une twig et mon premier engagement sur cette twig résume l’objective (c’est généralement ce que je vais envoyer comme message final pour la fonctionnalité dans le référentiel public). Je veux faire est git squash master
retour au premier message et puis je suis prêt à pousser.
J’utilise l’alias:
squash = !EDITOR="\"_() { sed -n 's/^pick //p' \"\\$1\"; sed -i .tmp '2,\\$s/^pick/f/' \"\\$1\"; }; _\"" git rebase -i
Cela videra l’historique écrasé avant cela – cela vous donne une chance de récupérer en récupérant un ancien identifiant de validation sur la console si vous voulez revenir. (Les utilisateurs de Solaris notent qu’il utilise l’option GNU sed -i
, les utilisateurs de Mac et de Linux devraient s’y retrouver.)
En question, ce que l’on entend par “dernier” peut être ambigu.
Par exemple, git log --graph
les éléments suivants (simplifiés):
* commit H0 | * merge |\ | * commit B0 | | | * commit B1 | | * | commit H1 | | * | commit H2 |/ |
Les derniers commits par heure sont alors H0, merge, B0. Pour les écraser, vous devrez rebaser votre twig fusionnée sur commit H1.
Le problème est que H0 contient H1 et H2 (et généralement plus de commits avant la fusion et après la ramification) alors que B0 ne le fait pas. Donc, vous devez gérer les changements à partir de H0, fusionner, H1, H2, B0 au moins.
Il est possible d’utiliser rebase mais de manière différente puis dans d’autres réponses mentionnées:
rebase -i HEAD~2
Cela vous montrera les options de choix (comme mentionné dans d’autres réponses):
pick B1 pick B0 pick H0
Mettez la courge au lieu de la pioche à H0:
pick B1 pick B0 s H0
Après l’enregistrement et la sortie, rebase appliquera les commits après H1. Cela signifie qu’il vous demandera de résoudre les conflits à nouveau (où HEAD sera H1 au début, puis les commits lors de leur application).
Une fois que rebase aura fini, vous pouvez choisir un message pour H0 et B0 écrasés:
* commit squashed H0 and B0 | * commit B1 | * commit H1 | * commit H2 |
PS Si vous ne faites que réinitialiser à BO: (par exemple, en utilisant reset --mixed
qui est expliqué plus en détail ici https://stackoverflow.com/a/18690845/2405850 ):
git reset --mixed hash_of_commit_B0 git add . git commit -m 'some commit message'
ensuite, vous écrasez en B0 changements de H0, H1, H2 (la perte complète commet pour les changements après la ramification et avant la fusion).
git rebase -i HEAD^^
où le nombre de ^ est X
(dans ce cas, écraser les deux derniers commits)
Jetez un coup d’oeil à cet aperçu:
Gist – Facile git-squash
Vous devrez taper par exemple git-squash 3
et c’est tout. Les trois derniers commits ont fusionné en un avec leurs messages concaténés.
En plus d’autres excellentes réponses, j’aimerais append que git rebase -i
me confond toujours avec l’ordre de validation – plus ancien que plus récent ou vice versa? C’est donc mon workflow:
git rebase -i HEAD~[N]
, où N est le nombre de commits que je veux rejoindre, en commençant par le plus récent . Donc, git rebase -i HEAD~5
signifierait “squash les 5 derniers commits dans un nouveau”; Sources et lectures supplémentaires: # 1 , # 2 .
Dans la twig sur laquelle vous souhaitez combiner les validations, exécutez:
git rebase -i HEAD~(n number of commits back to review)
Cela ouvrira l’éditeur de texte et vous devrez changer le “pick” devant chaque commit avec “squash” si vous souhaitez que ces validations soient fusionnées. Par exemple, si vous souhaitez fusionner tous les commits en un seul, le «choix» est le premier que vous avez effectué et tous les futurs doivent être définis sur «squash». Si vous utilisez vim, utilisez : x en mode insertion pour enregistrer et quitter l’éditeur.
Alors pour finir le rebase:
git rebase --continue
Pour plus d’informations à ce sujet et d’autres moyens de réécrire votre historique de validation, consultez cet article utile.
Qu’en est-il d’une réponse à la question liée à un workflow comme celui-ci?
merge --squash
après le PR, mais l’équipe pensait que cela ralentirait le processus.) Je n’ai pas vu de workflow comme celui-ci sur cette page. (Cela peut être mes yeux.) Si je comprends bien rebase
, plusieurs fusions nécessiteraient plusieurs résolutions de conflit . Je ne veux même pas y penser!
Donc, cela semble fonctionner pour nous.
git pull master
git checkout -b new-branch
git checkout -b new-branch-temp
git checkout new-branch
git merge --squash new-branch-temp
// met tous les changements dans l’étape git commit 'one message to rule them all'
git push
Pour écraser les 10 derniers commits en 1 seul engagement:
git reset --soft HEAD~10 && git commit -m "squashed commit"
Si vous souhaitez également mettre à jour la twig distante avec la validation écrasée:
git push -f
Si vous utilisez GitUp , sélectionnez la validation à fusionner avec son parent et appuyez sur S. Vous devez le faire une fois pour chaque validation, mais c’est beaucoup plus simple que de trouver la ligne de commande correcte. Surtout si c’est quelque chose que vous ne faites qu’une fois de temps en temps.
Ajoutez simplement cette fonction bash à votre fichier bas de .zshrc.
# Squash last X commits with a Commit message. # Usage: squash X 'COMMIT_MSG' # where X= Number of last commits. # where COMMIT_MSG= New commit msg. function squash() { if [ -z "${1}" -o -z "${2}" ]; then echo "Usage: \`squash X COMMIT_MSG\`" echo "X= Number of last commits." echo "COMMIT_MSG= New commit msg." return 1 fi git reset --soft HEAD~"$1" git add . && git ci -m "$2" # With 100 emoji git push --force }
Alors lancez-vous
squash X 'New Commit Message'
Et tu as fini.
Une autre façon de procéder est de faire un squash APRÈS un commit comme git rebase -i
Cela ouvrira votre éditeur pour choisir / squash comme d’habitude.
D’abord, je découvre le nombre de commits entre ma twig et ma twig principale par
git checkout master git rev-list master.. --count
Ensuite, je crée une autre twig basée sur la twig my-feature, sans modifier my-feature
twig.
Enfin, je cours
git checkout my-feature git checkout -b my-rebased-feature git checkout master git checkout my-rebased-feature git rebase master git rebase head^x -i // fixup/pick/rewrite git push origin my-rebased-feature -f // force, if my-rebased-feature was ever pushed, otherwise no need for -f flag // make a PR with clean history, delete both my-feature and my-rebased-feature after merge
J’espère que ça aide, merci.
Passez à la twig principale et assurez-vous d’être à jour:
sh git checkout master && git fetch && git pull
Fusionnez localement votre twig d’entités dans la twig principale:
sh git merge feature_branch
Réinitialiser la twig principale locale à l’état d’origine:
sh git reset origin/master
Maintenant, tous vos changements sont considérés comme non modifiés. Vous pouvez mettre en scène et les engager dans un ou plusieurs commits.
sh git add . --all git commit