Écraser mon dernier X commet ensemble en utilisant Git

Comment puis-je squash mon dernier X s'engage dans un commit avec Git?

2453
demandé sur Vadim Kotov 2011-03-04 07:11:26

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 .

1342
répondu Anomie 2016-07-23 06:16:02

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.)"

2693
répondu Chris Johnsen 2015-03-19 14:40:49

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.

592
répondu Mark Longair 2018-06-01 15:53:48

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...

  1. mettez Le to-be-squashed commits sur une branche de travail (s'ils ne le sont pas déjà) -- utilisez gitk pour ce
  2. vérifier la branche cible (par exemple 'master')
  3. git merge --squash (working branch name)
  4. git commit

le message de propagation sera pré-peuplé sur la base du squash.

141
répondu nobar 2014-03-14 23:24:34

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.)

101
répondu EthanB 2017-05-23 12:26:42

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.

interactive rebase editor

52
répondu br3nt 2018-09-10 03:24:38

si vous utilisez TortoiseGit, vous pouvez la fonction Combine to one commit :

  1. Ouvrir TortoiseGit menu contextuel
  2. sélectionner Show Log
  3. marquer les propagations pertinentes dans la vue log
  4. sélectionner Combine to one commit dans le menu contextuel

Combine commits

cette fonction exécute automatiquement tous les étapes simples git nécessaires. Malheureusement seulement disponible pour Windows.

47
répondu Matthias M 2015-11-06 12:51:23

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'
34
répondu trudolf 2014-05-22 00:41:28

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.

27
répondu rashok 2018-08-23 05:41:07

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!!

26
répondu Jakir Hosen Khan 2017-04-06 05:34:37

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:

  1. check out the golden repo

    git checkout golden_repo_name
    
  2. créer une nouvelle branche à partir de celui-ci(Golden repo) comme suit

    git checkout -b dev-branch
    
  3. fusionnez avec votre succursale locale que vous avez déjà

    git merge --squash feature-branch
    
  4. valider vos modifications (ce sera le seul qui va s'engager dans dev-branche)

    git commit -m "My feature complete"
    
  5. Pousser la branche à votre dépôt local

    git push origin dev-branch
    
20
répondu Sandesh Kumar 2016-03-18 12:10:45

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:

enter image description here

É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:

enter image description here

É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:

enter image description here

C'est ça. Une fois que vous avez sauvegardé ceci ( :wq ), vous avez terminé. Regardez avec git log .

20
répondu Martin Thoma 2018-06-26 18:03:55

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.

14
répondu Cascabel 2017-05-23 11:47:36

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
11
répondu William Denniss 2016-08-20 20:39:00

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'

8
répondu Ariel Gabizon 2017-09-14 10:33:24

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.

6
répondu user1376350 2018-03-20 15:28:21

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.)

3
répondu Ethan 2014-11-04 20:40:26

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.

3
répondu littlewhywhat 2017-05-25 01:49:16
git rebase -i HEAD^^

où le nombre de ^ 's est X

(dans ce cas, les courges, les deux derniers commits)

3
répondu Yoaz Menda 2018-02-19 12:32:20

Avoir un oeil sur ce principe:

Gist - Easy git-squash

Tapez par exemple git-squash 3 et c'est tout. Les trois derniers commits fusionnés en un avec leurs messages concaténés.

2
répondu jotaelesalinas 2016-12-21 02:43:30

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:

  1. git rebase -i HEAD~[N] , où N est le nombre de commits que je veux rejoindre, à partir du plus récent . Ainsi git rebase -i HEAD~5 signifierait "écraser les 5 derniers engage dans un nouveau";
  2. 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;
  3. 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é!

Sources & autres lectures: #1 , #2 .

2
répondu Ignorant 2018-03-09 09:51:26

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
2
répondu Ayan 2018-08-21 19:25:34

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

2
répondu aabiro 2018-09-11 13:47:40

Qu'en est-il d'une réponse à la question liée à un workflow comme celui-ci?

  1. beaucoup de locaux qui s'engage, mélangé avec de multiples fusions DE maître ,
  2. enfin un push à distance,
  3. PR et de fusion DE maître par l'évaluateur . (Oui, il serait plus facile pour le développeur de merge --squash aprè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.

  1. git pull master
  2. git checkout -b new-branch
  3. git checkout -b new-branch-temp
  4. éditer et s'engager beaucoup localement, fusionner maître régulièrement
  5. git checkout new-branch
  6. git merge --squash new-branch-temp // met tous les changements en scène
  7. git commit 'one message to rule them all'
  8. git push
  9. le réviseur fait PR et fusionne avec master.
1
répondu allenjom 2018-06-20 19:28:08

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.

0
répondu SSteve 2016-02-19 00:52:02

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.

0
répondu Ahmad Awais 2017-05-23 12:33:57

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.

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

0
répondu Jazzy 2018-05-26 18:47:47

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.

0
répondu Alan Dong 2018-08-29 06:37:40

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

-3
répondu Leo Lanese 2017-09-20 15:27:14