Squash mon dernier X commet en utilisant Git

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 …

  1. Placez les commits à écraser sur une twig de travail (s’ils ne le sont pas déjà) – utilisez gitk pour cela
  2. Découvrez la twig cible (par exemple, «maître»)
  3. git merge --squash (working branch name)
  4. 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 :

  1. Ouvrir le menu contextuel TortoiseGit
  2. Sélectionnez Show Log
  3. Marquer les validations pertinentes dans la vue du journal
  4. Sélectionnez Combine to one commit dans le menu contextuel.

Combiner les commits

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:

  1. Commander le repo d’or

     git checkout golden_repo_name 
  2. Créez une nouvelle twig (repo en or) comme suit

     git checkout -b dev-branch 
  3. Squash fusionne avec votre twig locale que vous avez déjà

     git merge --squash feature-branch 
  4. Validez vos modifications (ce sera le seul commit qui passe dans la twig dev)

     git commit -m "My feature complete" 
  5. 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.

Étape 0: Journal de Git

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:

entrer la description de l'image ici

Etape 1: Git rebase

Exécutez git rebase -i [your hash] , dans mon cas:

 $ git rebase -i 2d23ea524936e612fae1ac63c95b705db44d937d 

Étape 2: choisir ce que vous voulez

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:

entrer la description de l'image ici

Étape 3: Ajuster le (s) message (s)

Si vous avez sélectionné un seul commit et écrasé le rest, vous pouvez ajuster un message de validation:

entrer la description de l'image ici

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:

  1. 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”;
  2. l’éditeur apparaît, montrant la liste des commits que je veux fusionner. Maintenant, ils sont affichés dans l’ordre inverse : l’ancien commit est en haut. Marquez comme “squash” ou “s” tous les commits, sauf le premier / ancien : il sera utilisé comme sharepoint départ. Enregistrez et fermez l’éditeur.
  3. l’éditeur apparaît à nouveau avec un message par défaut pour le nouveau commit: modifiez-le selon vos besoins, enregistrez et fermez. Squash terminé!

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?

  1. de nombreux commits locaux, mélangés à plusieurs fusions
  2. enfin un coup de pouce à distance,
  3. PR et fusion TO TO par le réviseur . (Oui, il serait plus facile pour le développeur de 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.

  1. git pull master
  2. git checkout -b new-branch
  3. git checkout -b new-branch-temp
  4. éditer et valider beaucoup localement, fusionner régulièrement le maître
  5. git checkout new-branch
  6. git merge --squash new-branch-temp // met tous les changements dans l’étape
  7. git commit 'one message to rule them all'
  8. git push
  9. Le réviseur exécute PR et fusionne pour maîsortingser.

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.

Voir https://git-scm.com/docs/git-rebase#_interactive_mode

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