Écraser mon dernier X commet ensemble en utilisant Git
Comment puis-je squash mon dernier X s'engage dans un commit avec Git?
29 réponses
  utiliser  git rebase -i <after-this-commit>  et remplacer" pick "sur la deuxième et les suivantes engage avec" squash "ou" fixup", comme décrit dans  le manuel  .  
 dans cet exemple, <after-this-commit>  est soit le hachage SHA1 soit l'emplacement relatif de la tête de la branche courante à partir de laquelle les propagations sont analysées pour la commande rebase. Par exemple, si l'Utilisateur souhaite voir 5 commits de la tête courante dans le passé, 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 dernières propagations.  
si vous voulez écrire le nouveau message de propagation à partir de zéro, cela suffit:
git reset --soft HEAD~3 &&
git commit
  si vous voulez commencer à éditer le nouveau message de propagation avec une concaténation des messages de propagation existants (c'est-à-dire similaire à ce qu'une liste d'instructions pick/squash/squash/.../squash  git rebase -i  commencerait par vous), alors vous besoin d'extraire ces messages et de 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 éliminent les trois dernières commits en un seul nouveau commit de la même manière. La réinitialisation douce juste re-points tête à la dernière commit que vous ne voulez pas écraser. Ni l'index ni l'arbre de travail ne sont touchés par la réinitialisation douce, laissant l'index dans l'état désiré pour votre nouvelle propagation (i.e. il a déjà tous les changements des propagations que vous êtes sur le point de "lancer" loin.)"
 vous pouvez utiliser git merge --squash pour cela, qui est légèrement plus élégant que git rebase -i .  Supposons que vous êtes sur master et que vous voulez écraser les 12 derniers commits en un.  
  avertissement: tout d'abord, assurez-vous de vous engager dans votre travail-vérifiez que  git status  est propre (puisque  git reset --hard  jettera des changements mis en scène et non-staged)  
puis:
# 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
  Le    documentation  git merge      décrit plus en détail l'option --squash .  
     Maj:    le seul avantage réel de cette méthode par rapport au plus simple  git reset --soft HEAD~12 && git commit  suggéré par Chris Johnsen dans    sa réponse    est que vous obtenez le message de propagation pré-peuplé avec chaque message de propagation que vous supprimez.  
 je recommande d'éviter git reset  quand c'est possible, surtout pour les novices.  Sauf si vous avez vraiment besoin pour automatiser un processus basé sur un    nombre    commits, il est moins exotique...  
- mettez Le to-be-squashed commits sur une branche de travail (s'ils ne le sont pas déjà) -- utilisez gitk pour ce
- vérifier la branche cible (par exemple 'master')
-   git merge --squash (working branch name)
-     git commit
le message de propagation sera pré-peuplé sur la base du squash.
D'après réponse de Chris Johnsen ,
Ajouter un alias global" squash " de bash: (ou Git Bash sur Windows)
git config --global alias.squash '!f(){ git reset --soft HEAD~ && git commit --edit -m"$(git log --format=%B --reverse HEAD..HEAD@{1})"; };f'
... ou en utilisant L'invite de commande de Windows:
git config --global alias.squash "!f(){ git reset --soft HEAD~ && git commit --edit -m\"$(git log --format=%B --reverse HEAD..HEAD@{1})\"; };f"
  
     
Votre ~/.gitconfig doit maintenant contenir cet alias: 
[alias]
    squash = "!f(){ git reset --soft HEAD~ && git commit --edit -m\"$(git log --format=%B --reverse HEAD..HEAD@{1})\"; };f"
  
     
Usage:  
git squash N
 ... Qui écrase automatiquement ensemble le dernier  N  commet, y compris.  
  Note: le message de propagation résultant est une combinaison de toutes les propagation écrasées, dans l'ordre. Si vous n'êtes pas satisfait, vous pouvez toujours  git commit --amend  pour le modifier manuellement. (Ou, modifiez l'alias pour qu'il corresponde à vos goûts.)  
merci à ce blog pratique j'ai trouvé que vous pouvez utiliser cette commande pour écraser les 3 derniers commits:
git rebase -i HEAD~3
cela est pratique car cela fonctionne même si vous êtes sur une branche locale sans informations de suivi/repo à distance.
la commande va ouvrir l'éditeur de rebase interactif qui vous permet ensuite de réordonner, squash, reword, etc comme d'habitude.
utilisant l'éditeur de base interactif:
  l'éditeur de rebase interactif montre les trois dernières propagations.  Cette contrainte a été déterminée par  HEAD~3  lors de l'exécution de la commande  git rebase -i HEAD~3  .  
 le plus récent commit, HEAD , est affiché en premier à la ligne 1.  Les lignes commençant par un  #  sont commentaires/documentation.  
  la documentation affichée est assez claire.  Sur tout line vous pouvez changer la commande  pick  en une commande de votre choix.  
 je préfère utiliser la commande fixup car elle" écrase " les modifications de la propagation sur la ligne ci-dessus et écarte le message de propagation.    
 comme le commit sur la ligne 1 est HEAD , dans la plupart des cas vous laisseriez cela comme pick . 
 Vous ne pouvez pas utiliser  squash  ou  fixup  car il n'y a pas d'autre commit pour écraser le commit.  
  si vous utilisez TortoiseGit, vous pouvez la fonction  Combine to one commit :  
- Ouvrir TortoiseGit menu contextuel
-   sélectionner  Show Log
- marquer les propagations pertinentes dans la vue log
-   sélectionner  Combine to one commitdans le menu contextuel
cette fonction exécute automatiquement tous les étapes simples git nécessaires. Malheureusement seulement disponible pour Windows.
Basé sur cet article je trouve cette méthode plus facile pour mon cas d'utilisation.
ma branche ' dev 'était en avance de' origin/dev ' de 96 propagations (donc ces propagations n'ont pas encore été poussées à la télécommande).
je voulais écraser ces commits en un avant de pousser le changement. Je préfère réinitialiser la branche dans l'état "origin / dev" (cela laissera toutes les modifications de la directive 96 commits non initialisées) et ensuite commit les modifications à la fois:
git reset origin/dev
git add --all
git commit -m 'my commit message'
1) Identifier le hachage court de propagation
# 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 le hachage court.  
2) Si vous voulez écraser (fusionner) les deux dernières commit
# git rebase -i deab3412 
 3) Ceci ouvre un éditeur nano  pour la fusion. Et il ressemble à ci-dessous  
....
pick cdababcd Fix issue B
pick abcd1234 Update to Fix for issue B
....
  4) renommer le mot  pick  en  squash  qui est présent avant  abcd1234  . Après renommer il doit être comme ci-dessous.  
....
pick cdababcd Fix issue B
squash abcd1234 Update to Fix for issue B
....
 5) sauvegardez et fermez L'éditeur nano . Appuyez sur  ctrl + o et appuyez sur Enter  pour sauvegarder. Puis appuyez sur  ctrl + x  pour quitter l'éditeur.  
  6) Puis  nano  l'éditeur s'ouvre à nouveau pour mettre à jour les commentaires, si nécessaire le mettre à jour.  
7) Maintenant son écrasé avec succès, 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 vers repo. Remarque pour ajouter  +  signer avant le nom de la branche. Cela signifie une poussée forcée.  
# git push origin +master
  Note: Ceci est basé sur l'utilisation de git sur  ubuntu  shell. Si vous utilisez un os différent ( Windows ou Mac ), alors les commandes ci-dessus sont les mêmes sauf editor. Vous pourriez obtenir un autre éditeur.  
pour ce faire, vous pouvez utiliser la commande git suivante.
 git rebase -i HEAD~n
n (=4 ici) est le nombre de dernière propagation. Alors 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 branche distante (appelée  feature-branch  ) clonée à partir D'un dépôt doré ( golden_repo_name ), alors voici la technique pour écraser vos commits en un seul:  
-    check out the golden repo git checkout golden_repo_name
-   créer une nouvelle branche à partir de celui-ci(Golden repo) comme suit git checkout -b dev-branch
-   fusionnez avec votre succursale locale que vous avez déjà git merge --squash feature-branch
-    valider vos modifications (ce sera le seul qui va s'engager dans dev-branche) git commit -m "My feature complete"
-    Pousser la branche à votre dépôt local git push origin dev-branch
Anomies réponse est bonne, mais je me sentais en insécurité au sujet de ce j'ai donc décidé d'ajouter quelques captures d'écran.
Step 0: git log
  voir où vous en êtes avec git log . Le plus important, trouver le hachage de commit du premier commit vous    ne pas    voulez écraser. Donc seulement le:  
Étape 1: git rebase
  Exécuter     git rebase -i [your hash]     , dans mon cas:  
$ git rebase -i 2d23ea524936e612fae1ac63c95b705db44d937d
Étape 2: choisir / écraser ce que vous voulez
  dans mon cas, je veux écraser tout ce qui a été commis pour la première fois. L'ordre est du premier au dernier, donc exactement l'inverse comme dans  git log . Dans mon cas, je veux:  
Étape 3: ajuster le(S) message (s)
si vous n'avez choisi qu'un seul commit et écrasé le reste, vous pouvez ajuster un message de commit:
  C'est ça. Une fois que vous avez sauvegardé ceci (  :wq  ), vous avez terminé. Regardez avec  git log .  
c'est super-duper kludgy, mais d'une manière assez cool, donc je vais juste le jeter dans le ring:
GIT_EDITOR='f() { if [ "$(basename )" = "git-rebase-todo" ]; then sed -i "2,$s/pick/squash/" ; else vim ; fi }; f' git rebase -i foo~5 foo
  Traduction: fournir un nouvel "éditeur" pour git qui, si le nom de fichier à modifier est  git-rebase-todo  (le rebase interactif invite les changements de tous, mais la première "ramasser" pour "squash", et sinon engendre vim - de sorte que lorsque vous êtes invité à modifier l'écrasé le message de validation, vous obtenez vim. (Et évidemment j'étais en train d'écraser les cinq dernières attaques sur branch foo, mais tu peux changer ça comme tu veux.)  
je ferais probablement ce que Mark Longair a suggéré , cependant.
si vous voulez écraser chaque commit en une seule commit (par exemple lors de la publication d'un projet pour la première fois), essayez:
git checkout --orphan <new-branch>
git commit
  Ce qui peut être vraiment pratique:
Trouvez le hash de commit que vous voulez écraser sur le dessus de; dire que c'est  d43e15  
Maintenant, utilisez  
  git reset d43e15   
  git commit -am 'new commit name'   
je pense que la meilleure façon de le faire est de faire une nouvelle branche du maître et de faire une fusion --squash de la branche.
git checkout master
git checkout -b feature_branch_squashed
git merge --squash feature_branch
alors vous avez tous les changements prêts à commettre.
je trouve qu'une solution plus générique n'est pas de spécifier 'n' commits, mais plutôt la branche/commit-id que vous voulez écraser sur le dessus. Ceci est moins sujet aux erreurs que de compter les propagations jusqu'à une propagation spécifique-il suffit de spécifier la balise directement, ou si vous voulez vraiment compter, vous pouvez spécifier HEAD~N.
  dans mon workflow, je démarre une branche, et mon premier engagement sur cette branche résume l'objectif (i.e. c'est habituellement ce que je vais pousser comme le message "final" pour la fonctionnalité à la dépôt public. Donc quand j'ai fini, tout ce que je veux faire c'est  git squash master  revenir au premier message et puis je suis prêt à pousser.  
j'utilise l'alias:
squash = !EDITOR="\"_() { sed -n 's/^pick //p' \"\\"; sed -i .tmp '2,\$s/^pick/f/' \"\\"; }; _\"" git rebase -i
  cela dump l'historique étant écrasé avant qu'il ne le soit-cela vous donne une chance de récupérer en saisissant un ancien identifiant de propagation de la console si vous voulez revenir en arrière.  (Les utilisateurs de Solaris remarquent qu'il utilise L'option GNU sed  -i , les utilisateurs Mac et Linux devraient être d'accord avec cela.)  
en question, il pourrait être ambigu ce que l'on entend par"Dernier".
  par exemple  git log --graph  produit ce qui suit (simplifié):  
* commit H0
|
* merge
|\
| * commit B0
| |
| * commit B1
| | 
* | commit H1
| |
* | commit H2
|/
|
puis les dernières propagations par le temps sont H0, merge, B0. Pour les écraser, vous devrez rebaser votre branche fusionnée sur commit H1.
le problème est que H0 contient H1 et H2 (et généralement plus de propagations avant la fusion et après la ramification) alors que B0 n'en contient pas. Vous devez donc gérer les changements de H0, Fusion, H1, H2, B0 au moins.
il est possible d'utiliser rebase mais de manière différente que 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
mettre squash au lieu de pick à H0:
pick B1
pick B0
s H0
Après l'enregistrer et quitter rebase appliquera s'engage à son tour après H1. Cela signifie qu'il vous demandera résoudre les conflits à nouveau (où la tête sera H1 d'abord et ensuite accumuler les propagations au fur et à mesure qu'elles sont appliquées).
après rebase va finir, vous pouvez choisir le message pour squashed H0 et B0:
* commit squashed H0 and B0
|
* commit B1
| 
* commit H1
|
* commit H2
|
  P. S. Si vous faites juste quelques réinitialisation à 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'
puis tu écrases dans B0 les changements de H0, H1, H2 (la perte engage complètement pour les changements après la ramification et avant la fusion.
git rebase -i HEAD^^
où le nombre de ^ 's est X
(dans ce cas, les courges, les deux derniers commits)
Avoir un oeil sur ce principe:
  Tapez par exemple git-squash 3  et c'est tout. Les trois derniers commits fusionnés en un avec leurs messages concaténés.  
  en plus d'autres excellentes réponses, j'aimerais ajouter que  git rebase -i  me confond toujours avec l'ordre de propagation - plus ancien à nouveau ou vice versa? Voici donc mon workflow:  
-    git rebase -i HEAD~[N], où N est le nombre de commits que je veux rejoindre, à partir du plus récent . Ainsigit rebase -i HEAD~5signifierait "écraser les 5 derniers engage dans un nouveau";
- l'éditeur apparaît, montrant la liste des commet je veux fusionner. Maintenant ils sont affichés dans ordre inverse : l'ancien commit est en haut. Marquer comme "squash" ou "s "tous les commits dans là sauf le premier/plus ancien : il sera utilisé comme point de départ. Enregistrer et fermer l'éditeur;
- l'éditeur apparaît de nouveau avec un message par défaut pour le nouveau commit: changement de vos besoins, enregistrer et fermer. Squash terminé!
Pour écraser les 10 dernières validations dans 1 seul commit:
git reset --soft HEAD~10 && git commit -m "squashed commit"
si vous voulez aussi mettre à jour la branche distante avec la propagation écrasée:
git push -f
dans la branche où vous souhaitez combiner les propagations, exécutez:
git rebase -i HEAD~(n number of commits back to review)
ceci ouvrira l'éditeur de texte et vous devez changer le 'pick' devant chaque commit avec 'squash' si vous souhaitez que ces commits soient fusionnés ensemble. Par exemple, si vous cherchez à fusionner toutes les propagations en une seule, le 'pick' est la première propagation que vous avez faite et toutes les futures (placées en dessous du premier) devraient être définies à 'squash'. Si vous utilisez vim, utilisez : x en mode insertion pour enregistrer et quitter l'éditeur.
puis terminer le rebase:
git rebase --continue
pour plus d'informations sur cette et d'autres façons de réécrire votre histoire de commit, voir cet utile post
Qu'en est-il d'une réponse à la question liée à un workflow comme celui-ci?
- beaucoup de locaux qui s'engage, mélangé avec de multiples fusions DE maître ,
- enfin un push à distance,
-   PR et de fusion DE maître    par l'évaluateur    .
(Oui, il serait plus facile pour le développeur de  merge --squashaprès le PR, mais l'équipe a pensé que cela ralentirait le processus.)
  Je n'ai pas vu un flux de travail comme ça sur cette page. (C'est peut-être mes yeux.) Si je comprends bien rebase correctement, les fusions multiples nécessiteraient  multiple conflict resolution  . Je ne veux PAS même de penser à cela!  
Donc, cela semble fonctionner pour nous.
-    git pull master
-   git checkout -b new-branch
-   git checkout -b new-branch-temp
- éditer et s'engager beaucoup localement, fusionner maître régulièrement
-   git checkout new-branch
-    git merge --squash new-branch-temp// met tous les changements en scène
-    git commit 'one message to rule them all'
-   git push
- le réviseur fait PR et fusionne avec master.
si vous utilisez GitUp , sélectionnez la propagation que vous voulez fusionner avec son parent et appuyez sur S . Vous devez le faire une fois pour chaque commit, mais c'est beaucoup plus simple que de trouver la bonne incantation en ligne de commande. Surtout si c'est quelque chose qu'on ne fait qu'une fois de temps en temps.
il suffit d'ajouter cette fonction bash à votre bash de .zshrc fichier.
# 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 "" -o -z "" ]; 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~""
    git add . && git ci -m "" # With 100 emoji
    git push --force
}
puis il suffit d'exécuter
squash X 'New Commit Message'
et c'est fini.
  une autre façon de le faire si vous avez une tonne de commits est de faire un squash après un commit comme  git rebase -i <hashbeforeyouwanttosquash>   
ceci ouvrira votre éditeur pour choisir/écraser comme normal.
je découvre D'abord le nombre de propagations entre ma branche principale et la branche principale actuelle par
git checkout master
git rev-list master.. --count
 ensuite, je crée une autre branche basée sur la branche my-feature, garder my-feature  branche intacte.  
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 vous aidera, merci.
passez à la branche principale et assurez-vous que vous êtes à jour:
  sh
git checkout master && git fetch && git pull
   
fusionner votre branche principale dans la branche principale localement:
  sh
git merge feature_branch
   
réinitialiser la branche principale locale à l'état d'origine:
  sh
git reset origin/master
   
maintenant, tous vos changements sont considérés comme non marqués. Vous pouvez mettre en scène et les engager dans un ou plusieurs engager.
  sh
git add . --all
git commit
   
 
       
       
       
      