Existe-t-il un moyen d'écraser un certain nombre de commits de manière non interactive?


Réponses:


147

Assurez-vous que votre arbre de travail est propre, puis

git reset --soft HEAD~3
git commit -m 'new commit message'

@wilhelmtell: Génial! Est-ce que je serais maintenant capable de construire un alias git, par exemple "mysquash 3 'some message'", pour réduire cela à une ligne?
Phillip

5
Si c'est juste le nombre de lignes:git reset --soft HEAD~3 && git commit -m "my message"
KingCrunch

7
@Phillip: Vous pouvez intégrer une fonction shell dans l'alias git. git config alias.mysquash '!f(){ git reset --soft HEAD~$1 && git commit ${2:+-m "$2"}; };f'. git mysquash 3 'some message'fonctionnera, mais je l'ai également modifié afin d' git musquash 3omettre complètement l'indicateur -m afin que vous obteniez l' git commitinterface utilisateur interactive dans ce cas.
Lily Ballard

3
Juste pour être clair: ce n'est pas la même chose qu'une courge. Un squash fusionnera également les messages de validation. Si vous effectuez une réinitialisation logicielle, vous perdrez tous les messages des validations. Si vous voulez écraser, essayez stackoverflow.com/a/27697274/974186
René Link

1
@sebnukem - C'est à ce moment que nous essayons de pousser la branche et que la télécommande est configurée pour rejeter les poussées forcées.
avmohan

30

J'aime personnellement la solution de Wilhelmtell :

git reset --soft HEAD~3
git commit -m 'new commit message'

Cependant, j'ai créé un alias avec une vérification d'erreur pour que vous puissiez le faire:

git squash 3 'my commit message'

Je recommande de configurer des alias qui exécutent réellement des scripts afin qu'il soit plus facile (a) de coder vos scripts et (b) d'effectuer un travail plus complexe avec la vérification des erreurs. Vous trouverez ci-dessous un script qui fait le travail de squash, puis ci-dessous un script pour configurer vos alias git.

Script pour écraser (squash.sh)

#!/bin/bash
#

#get number of commits to squash
squashCount=$1

#get the commit message
shift
commitMsg=$@

#regular expression to verify that squash number is an integer
regex='^[0-9]+$'

echo "---------------------------------"
echo "Will squash $squashCount commits"
echo "Commit message will be '$commitMsg'"

echo "...validating input"
if ! [[ $squashCount =~ $regex ]]
then
    echo "Squash count must be an integer."
elif [ -z "$commitMsg" ]
then
    echo "Invalid commit message.  Make sure string is not empty"
else
    echo "...input looks good"
    echo "...proceeding to squash"
    git reset --soft HEAD~$squashCount
    git commit -m "$commitMsg"
    echo "...done"
fi

echo
exit 0

Ensuite, pour connecter ce script squash.sh à un alias git, créez un autre script pour configurer vos alias git comme ceci ( create_aliases.command ou create_aliases.sh ):

#!/bin/sh
echo '-----------------------'
echo 'adding git aliases....'
echo '-----------------------'
echo
git config --global alias.squash "!bash -c 'bash <path to scripts directory>/squash.sh \$1 \$2' -"
#add your other git aliases setup here
#and here
#etc.
echo '------------------------------------'
echo 'here is your global gitconfig file:'
echo '------------------------------------'
more ~/.gitconfig
echo 
echo
echo '----------------'
echo 'end of script...'
echo '----------------'

4
Vous pouvez également le mettre dans $PATHnamed git-squash.shet il sera automatiquement aliasé en tant que git squash. Je n'ai pas changé votre réponse, juste au cas où il y aurait une raison d'utiliser le create-aiases.shscript dont je ne suis pas au courant.

J'utilise essentiellement le script create_aliases.command pour que même nos PM et nos concepteurs puissent facilement se configurer. Il suffit de double-cliquer sur le script et ils sont tous définis (d'autant plus que j'ai le script de configuration dans notre dépôt et que le chemin relatif est connu). Ensuite, ils n'ont même pas besoin de redémarrer le terminal.
n8tr

1
J'ai essayé cela et il lit mon message de validation comme un nombre de squash et échoue car ce n'est pas un entier.
Minthos

1
La solution était d'ajouter - après /squash.sh \ $ 1 \ $ 2 '
Minthos

1
J'aime l'idée de la solution, mais le commentaire ci-dessus n'est pas encore pris en compte dans la solution. Il doit y avoir un signe moins entre le guillemet simple et le guillemet double.
physicalattraction

10

Pour ajouter à la réponse de wilhelmtell, je trouve pratique de réinitialiser HEAD~2puis de modifier le commit de HEAD~3:

git reset --soft HEAD~2
git commit --all --amend --no-edit    

Cela fusionnera tous les commits dans le HEAD~3 validations de validation et utilisera son message de validation. Assurez-vous de partir d'un arbre de travail propre.


2
C'est la bonne réponse car elle écrase une série de commits menant à head et utilise le premier message de commit. Ce n'est pas interactif.
Landon Kuhn le

9

J'ai utilisé:

EDITOR="sed -i '2,/^$/s/^pick\b/s/'" git rebase -i <ref>

A bien fonctionné. N'essayez pas d'avoir un journal de validation avec une ligne commençant par "pick" :)


Malheureusement, cela ne fonctionne pas pour moi sous Windows. Obtenez toujours l'éditeur interactif.
abergmeier

3

Utilisez la commande suivante pour écraser les 4 derniers commits du dernier commit:

git squash 4

Avec l'alias:

squash = !"f() { NL=$1; GIT_EDITOR=\"sed -i '2,$NL s/pick/squash/;/# This is the 2nd commit message:/,$ {d}'\"; git rebase -i HEAD~$NL; }; f"
sq = !git squash $1
sqpsf = !git squash $1 && git psf 

Depuis https://github.com/brauliobo/gitconfig/blob/master/configs/.gitconfig


Vous n'avez pas spécifié quel OS / shell est utilisé ici. Cette solution ne fonctionnera probablement pas pour tous les autres ordinateurs de bureau que les gens utilisent.
Rick-777 du

oui, vous devriez utiliser linux / bash | zsh pour cela
brauliobo

2

Voici une ligne pour écraser les 2 derniers commits. Dans cet exemple, le message de l'avant-dernier commit sera conservé. Vous pouvez modifier le message à votre guise.

git commit -am "$(git log -1 --skip=1 --pretty=%B | xargs && git reset --soft HEAD~2)"

Cette commande sera très utile si vous créez un alias pour cette commande et utilisez l'alias à la place.


1

Pour tout écraser depuis que la branche a été dérivée de master:

git reset --soft $(git merge-base --fork-point master) \
  && git commit --verbose --reedit-message=HEAD --reset-author

--reedit-message=HEADutilisera le message du dernier commit qui ne fait pas partie de l'écrasement . Ce n'est probablement pas celui que vous souhaitez. Pour obtenir le message du premier commit à inclure , soit (1) remplacez HEADpar le hachage du commit dont vous voulez que le message soit inclus , ou (2) sautez au premier commit à inclure et git commit --amend --reedit-message=HEAD. C'est ce que fait la réponse harmonieuse.
Maëlan

-1

Vous pouvez être assez proche avec

git rebase --onto HEAD ~ 4 HEAD ~ master

Cela suppose que vous êtes maître avec une histoire linéaire. Ce n'est pas tout à fait un squash car il rejette les commits intermédiaires. Vous devrez modifier le nouveau HEAD pour modifier le message de validation.


Merci Greg; par 'discards' voulez-vous dire que ces commits intermédiaires sont soumis à un nettoyage par git gc?
Phillip

@Phillip Oui, les commits intermédiaires deviennent des ordures ainsi que l'ancien HEAD car il est réécrit pour avoir HEAD~4comme parent.
Greg Bacon
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.