C'è un modo per schiacciare un certo numero di commit in modo non interattivo?


Risposte:


146

Assicurati che il tuo albero di lavoro sia pulito, quindi

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

@wilhelmtell: fantastico! Ora sarei in grado di costruire un alias git, ad esempio "mysquash 3 'some message'", per ridurlo a una riga?
Phillip

5
Se è solo il numero di righe:git reset --soft HEAD~3 && git commit -m "my message"
KingCrunch

7
@Phillip: puoi incorporare una funzione di shell nell'alias git. git config alias.mysquash '!f(){ git reset --soft HEAD~$1 && git commit ${2:+-m "$2"}; };f'. git mysquash 3 'some message'funzionerà, ma l'ho anche ottimizzato in modo git musquash 3da omettere completamente il flag -m in modo da ottenere l' git commitinterfaccia utente interattiva in quel caso.
Lily Ballard

3
Giusto per chiarire: non è la stessa cosa di una zucca. Uno squash unirà anche i messaggi di commit. Se esegui un soft reset perderai tutti i messaggi dei commit. Se vuoi schiacciare, prova stackoverflow.com/a/27697274/974186
René Link

1
@sebnukem - Questo è quando proviamo a spingere il ramo e il telecomando è configurato per rifiutare i push forzati.
avmohan

30

Personalmente mi piace la soluzione di Wilhelmtell :

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

Tuttavia, ho creato un alias con alcuni controlli di errore in modo che tu possa farlo:

git squash 3 'my commit message'

Consiglio di impostare alias che eseguano effettivamente gli script in modo che sia più facile (a) codificare gli script e (b) svolgere un lavoro più complesso con il controllo degli errori. Di seguito è riportato uno script che fa il lavoro di squash e poi di seguito è uno script per impostare gli alias git.

Script per schiacciare (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

Quindi per collegare lo script squash.sh a un alias git, crea un altro script per impostare i tuoi alias git in questo modo ( create_aliases.command o 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
Puoi anche inserirlo in $PATHnamed git-squash.she verrà automaticamente definito come alias git squash. Non ho cambiato la tua risposta, nel caso ci fosse un motivo per usare la create-aiases.shsceneggiatura di cui non sono a conoscenza.

Fondamentalmente utilizzo lo script create_aliases.command in modo che anche i nostri PM e designer possano essere facilmente configurati. Basta un doppio clic sullo script e sono tutti impostati (soprattutto perché ho lo script di installazione nel nostro repository e il percorso relativo è noto). Quindi non hanno nemmeno bisogno di riavviare il terminale.
n8tr

1
Ho provato questo e legge il mio messaggio di commit come conteggio squash e fallisce perché non è un numero intero.
Minthos

1
La soluzione era aggiungere - dopo /squash.sh \ $ 1 \ $ 2 '
Minthos

1
Mi piace l'idea della soluzione, ma il commento sopra non è ancora preso in considerazione nella soluzione. Ci deve essere un segno meno tra le virgolette singole e doppie.
fisica

10

Per aggiungere alla risposta di Wilhelmtell, trovo conveniente eseguire il soft reset HEAD~2e quindi modificare il commit di HEAD~3:

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

Questo unirà tutti i commit al HEAD~3commit e utilizzerà il suo messaggio di commit. Assicurati di iniziare da un albero funzionante pulito.


2
Questa è la risposta corretta perché schiaccia una serie di commit che portano a head e utilizza il messaggio del primo commit. Non è interattivo.
Landon Kuhn

9

Ero solito:

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

Ha funzionato abbastanza bene. Basta non provare ad avere un registro di commit con una riga che inizia con "pick" :)


Purtroppo questo non funziona per me su Windows. Ottieni ancora l'editor interattivo.
abergmeier

3

Usa il seguente comando per schiacciare gli ultimi 4 commit nell'ultimo commit:

git squash 4

Con 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 

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


Non hai specificato quale sistema operativo / shell viene utilizzato qui. Questa soluzione probabilmente non funzionerà per tutti gli altri desktop utilizzati dalle persone.
Rick-777

sì, dovresti usare linux / bash | zsh per questo
brauliobo

2

Ecco una riga per eliminare gli ultimi 2 commit. In questo esempio, il messaggio del penultimo commit verrà mantenuto. Puoi modificare il messaggio come desideri.

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

Questo comando sarà molto utile se crei un alias per questo comando e utilizzi invece l'alias.


1

Per schiacciare tutto da quando il ramo è stato biforcato dal maestro:

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

--reedit-message=HEADuserà il messaggio dell'ultimo commit che non fa parte dello schiacciamento . Questo probabilmente non è quello che desideri. Per ottenere piuttosto il messaggio del primo commit da includere , (1) sostituire HEADcon l'hash del commit di cui si desidera includere il messaggio, oppure (2) passare al primo commit da includere e git commit --amend --reedit-message=HEAD. Questo è ciò che fa la risposta armoniosa.
Maëlan

-1

Puoi avvicinarti abbastanza

git rebase --onto HEAD ~ 4 HEAD ~ master

Questo presume che tu sia in master con una storia lineare. Non è proprio uno squash perché scarta i commit intermedi. Dovresti modificare il nuovo HEAD per modificare il messaggio di commit.


Grazie Greg; con "discards" intendi che quei commit intermedi sono soggetti a cleanup da parte di git gc?
Phillip

@Phillip Sì, i commit intermedi diventano spazzatura così come il vecchio HEAD perché viene riscritto per avere HEAD~4come genitore.
Greg Bacon
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.