Gibt es eine Möglichkeit, eine Reihe von Commits nicht interaktiv zu quetschen?

Antworten:

145

Stellen Sie dann sicher, dass Ihr Arbeitsbaum sauber ist

git reset --soft HEAD~3
git commit -m 'new commit message'
wilhelmtell
quelle
@ Wilhelmtell: Großartig! Könnte ich jetzt einen Git-Alias ​​erstellen, z. B. "mysquash 3 'some message'", um dies auf eine Zeile zu reduzieren?
Phillip
5
Wenn es nur die Anzahl der Zeilen ist:git reset --soft HEAD~3 && git commit -m "my message"
KingCrunch
7
@Phillip: Sie können eine Shell-Funktion in den Git-Alias ​​einbetten. git config alias.mysquash '!f(){ git reset --soft HEAD~$1 && git commit ${2:+-m "$2"}; };f'. git mysquash 3 'some message'wird funktionieren, aber ich habe es auch so git musquash 3angepasst, dass das Flag -m vollständig weggelassen wird, sodass Sie git commitin diesem Fall die interaktive Benutzeroberfläche erhalten.
Lily Ballard
3
Nur um es klar zu machen: Es ist nicht dasselbe wie ein Kürbis. Ein Squash führt auch die Commit-Nachrichten zusammen. Wenn Sie einen Soft-Reset durchführen, gehen alle Meldungen der Commits verloren. Wenn Sie Squash wollen, versuchen Sie stackoverflow.com/a/27697274/974186
René Link
1
@sebnukem - In diesem Fall versuchen wir, den Zweig zu pushen, und die Fernbedienung ist so konfiguriert, dass erzwungene Pushs abgelehnt werden.
Avmohan
30

Ich persönlich mag die Lösung von wilhelmtell :

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

Ich habe jedoch einen Alias ​​mit einer Fehlerprüfung erstellt, damit Sie dies tun können:

git squash 3 'my commit message'

Ich empfehle, Aliase einzurichten, in denen tatsächlich Skripte ausgeführt werden, damit es einfacher ist, (a) Ihre Skripte zu codieren und (b) komplexere Arbeiten mit der Fehlerprüfung durchzuführen. Unten finden Sie ein Skript, das die Arbeit des Squashs erledigt, und unten ein Skript zum Einrichten Ihrer Git-Aliase.

Skript zum Quetschen (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

Um dieses squash.sh- Skript mit einem Git-Alias ​​zu verbinden, erstellen Sie ein anderes Skript zum Einrichten Ihrer Git-Aliase wie folgt ( create_aliases.command oder 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 '----------------'
n8tr
quelle
4
Sie können es auch in $PATHnamed git-squash.sheinfügen und es wird automatisch als Alias ​​bezeichnet git squash. Ich habe Ihre Antwort nicht geändert, nur für den Fall, dass es einen Grund gibt, das create-aiases.shSkript zu verwenden, das mir nicht bekannt ist.
Grundsätzlich verwende ich das Skript create_aliases.command, damit auch unsere PMs und Designer problemlos eingerichtet werden können. Einfach ein Doppelklick auf das Skript und sie sind alle festgelegt (zumal ich das Setup-Skript in unserem Repo habe und der relative Pfad bekannt ist). Dann müssen sie das Terminal nicht einmal neu starten.
n8tr
1
Ich habe es versucht und es liest meine Commit-Nachricht als Squash-Anzahl und schlägt fehl, weil es keine Ganzzahl ist.
Minthos
1
Die Lösung bestand darin, - nach /squash.sh \ $ 1 \ $ 2 '
Minthos
1
Ich mag die Idee der Lösung, aber der obige Kommentar wird in der Lösung noch nicht berücksichtigt. Zwischen dem einfachen und dem doppelten Anführungszeichen muss ein Minuszeichen stehen.
körperliche
10

Um die Antwort von wilhelmtell zu ergänzen, finde ich es zweckmäßig, HEAD~2das Commit von zurückzusetzen und dann zu ändern HEAD~3:

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

Dadurch werden alle Commits mit dem HEAD~3Commit zusammengeführt und die Commit-Nachricht verwendet. Stellen Sie sicher, dass Sie von einem sauberen Arbeitsbaum ausgehen.

harmonisch
quelle
2
Dies ist die richtige Antwort, da eine Reihe von Commits bis zum Kopf gequetscht werden und die Nachricht des ersten Commits verwendet wird. Es ist nicht interaktiv.
Landon Kuhn
9

Ich benutzte:

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

Hat ganz gut funktioniert. Versuche nur nicht, ein Commit-Protokoll mit einer Zeile zu erstellen, die mit "pick" beginnt :)

Julien Wajsberg
quelle
Leider funktioniert das bei mir unter Windows nicht. Holen Sie sich immer noch den interaktiven Editor.
Abergmeier
3

Verwenden Sie den folgenden Befehl, um die letzten 4 Commits innerhalb des letzten Commits zu quetschen:

git squash 4

Mit dem 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 

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

Brauliobo
quelle
Sie haben nicht angegeben, welches Betriebssystem / welche Shell hier verwendet wird. Diese Lösung funktioniert wahrscheinlich nicht für alle anderen verwendeten Desktops.
Rick-777
Ja, Sie sollten Linux / Bash | Zsh für diese verwenden
Brauliobo
2

Hier ist ein Einzeiler, um die letzten 2 Commits zu quetschen. In diesem Beispiel wird die Nachricht vom vorletzten Commit beibehalten. Sie können die Nachricht nach Ihren Wünschen ändern.

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

Dieser Befehl ist sehr nützlich, wenn Sie einen Alias ​​für diesen Befehl erstellen und stattdessen den Alias ​​verwenden.

Jasir
quelle
1

Um alles zu zerquetschen, seit der Ast vom Meister gegabelt wurde:

git reset --soft $(git merge-base --fork-point master) \
  && git commit --verbose --reedit-message=HEAD --reset-author
Andy
quelle
--reedit-message=HEADverwendet die Nachricht des letzten Commits, die nicht Teil des Squashing ist . Dies ist wahrscheinlich nicht die, die Sie wollen. Um die Nachricht des ersten Commits zu erhalten, das aufgenommen werden soll , ersetzen Sie entweder (1) durch HEADden Hash des Commits, dessen Nachricht Sie aufnehmen möchten, oder (2) springen Sie zum ersten Commit, das aufgenommen werden soll, und git commit --amend --reedit-message=HEAD. Dies ist, was harmonische Antwort tut.
Maëlan
-1

Sie können ziemlich nah mit kommen

Git Rebase - auf HEAD ~ 4 HEAD ~ Master

Dies setzt voraus, dass Sie mit einer linearen Historie auf dem Master sind. Es ist kein Squash, weil es die Zwischen-Commits verwirft. Sie müssten den neuen HEAD ändern, um die Commit-Nachricht zu ändern.

Greg Bacon
quelle
Danke Greg; Mit "Verwerfen" meinen Sie, dass diese Zwischen-Commits von git gc bereinigt werden müssen?
Phillip
@Phillip Ja, die Zwischen-Commits werden ebenso wie der alte HEAD zu Müll, da er neu geschrieben wurde, um ihn HEAD~4als übergeordnetes Element zu haben .
Greg Bacon