Squash die ersten beiden Commits in Git?

Mit git rebase --interactive Sie beliebig viele Commits zu einem einzigen zusammenfassen.

Das ist alles toll, es sei denn, Sie wollen Commits in den initialen Commit zerquetschen. Das scheint unmöglich zu sein.

Gibt es Möglichkeiten, dies zu erreichen?


Mäßig verwandt:

In einer verwandten Frage gelang es mir, eine andere Herangehensweise an die Notwendigkeit zu finden, gegen das erste Commit zu quetschen, das heißt, um es zum zweiten zu machen.

Wenn Sie interessiert sind: git: Wie fügt man als erstes ein Commit ein und verschiebt alle anderen?

    Update Juli 2012 ( Git 1.7.12+ )

    Sie können nun alle Commits bis auf root zurücksetzen und den zweiten Commit Y auswählen, der mit dem ersten X gequetscht werden soll.

     git rebase -i --root master pick sha1 X squash sha1 Y pick sha1 Z 
     git rebase [-i] --root $tip 

    Dieser Befehl kann nun verwendet werden, um den gesamten Verlauf von ” $tip ” bis zum Root-Commit neu zu schreiben.

    Siehe commit df5df20c1308f936ea542c86df1e9c6974168472 auf GitHub von Chris Webb ( arachsys ) .


    Ursprüngliche Antwort (Februar 2009)

    Ich glaube, dass Sie dafür in der SO-Frage ” Wie kombiniere ich die ersten beiden Commits eines Git-Repositories ” verschiedene Rezepte finden werden.

    Charles Bailey lieferte dort die ausführlichste Antwort und erinnerte uns daran, dass ein Commit ein vollständiger Baum ist (nicht nur Diffs von früheren Zuständen).
    Und hier haben das alte Commit (das “initiale Commit”) und das neue Commit (Ergebnis des Squashings) keinen gemeinsamen Vorfahren.
    Das bedeutet, dass Sie das ursprüngliche Commit nicht in ein neues commit --amendcommit --amend ” können und dann auf das neue initiale Commit des Verlaufs des vorherigen initialen Commits (viele Konflikte) rebasieren.

    (Dieser letzte Satz stimmt nicht mehr mit git rebase -i --root )

    Eher (mit A das ursprüngliche “initiale Commit”, und B ein nachfolgendes Commit musste in das ursprüngliche Commit gequetscht werden):

    1. Kehren Sie zum letzten Commit zurück, das den initialen Commit bilden soll (LIST HEAD):

       git checkout  
    2. Setzen Sie den Verzweigungszeiger auf das initiale Commit zurück, lassen Sie den Index und den Arbeitsbaum jedoch intakt:

       git reset --soft  
    3. Ändere den ursprünglichen Baum mit dem Baum von ‘B’:

       git commit --amend 
    4. Markieren Sie vorübergehend dieses neue initiale Commit (oder Sie könnten sich das neue Commit sha1 manuell merken):

       git tag tmp 
    5. Gehen Sie zurück zum ursprünglichen Zweig (nehmen Sie für dieses Beispiel den Master an):

       git checkout master 
    6. Wiederholen Sie alle Commits nach B auf das neue initiale Commit:

       git rebase --onto tmp  
    7. Entfernen Sie das temporäre Tag:

       git tag -d tmp 

    Auf diese Weise verursacht die ” rebase --onto ” keine Konflikte während der Zusammenführung, da sie die nach dem letzten Commit ( B ) erstellte Historie in die ursprüngliche (die A ) bis tmp (repräsentiert den gequetschten neuen Initial) tmp commit): nur triviale Fast-Forward-Merges.

    Das funktioniert für ” AB “, aber auch ” A-...-...-...-B ” (beliebig viele Commits können auf diese Weise in die Initiale gequetscht werden)

    Ich habe das Skript von VonC überarbeitet, um alles automatisch zu machen und mich um nichts zu bitten. Du gibst es zwei SHA1s committieren und es wird alles zwischen ihnen in ein Commit namens “zerquetschte Geschichte” zerquetschen:

     #!/bin/sh # Go back to the last commit that we want # to form the initial commit (detach HEAD) git checkout $2 # reset the branch pointer to the initial commit (= $1), # but leaving the index and working tree intact. git reset --soft $1 # amend the initial tree using the tree from $2 git commit --amend -m "squashed history" # remember the new commit sha1 TARGET=`git rev-list HEAD --max-count=1` # go back to the original branch (assume master for this example) git checkout master # Replay all the commits after $2 onto the new initial commit git rebase --onto $TARGET $2 

    Für das, was es wert ist, vermeide ich dieses Problem, indem ich immer ein “no-op” first commit erstelle, bei dem das einzige im Repository ein leeres .gitignore ist:

    https://github.com/DarwinAwardWinner/git-custom-commands/blob/master/bin/git-myinit

    Auf diese Weise gibt es nie einen Grund, sich mit dem ersten Commit anzulegen.

    Wenn Sie alle Commits einfach in einen einzigen Commit zerlegen möchten, setzen Sie einfach das Repository zurück und ändern Sie den ersten Commit:

     git reset hash-of-first-commit git add -A git commit --amend 

    Git Reset wird den Arbeitsbaum intakt lassen, also ist alles noch da. Fügen Sie einfach die Dateien mit den git add-Befehlen hinzu und ändern Sie das erste Commit mit diesen Änderungen. Im Vergleich zu Rebase -i verlierst du die Fähigkeit, die Git Kommentare zu merge.

    Dies wird den zweiten Commit in den ersten Squash quetschen:

    ABC-... -> AB-C-...

     git filter-branch --commit-filter ' if [ "$GIT_COMMIT" =  ]; then skip_commit "$@"; else git commit-tree "$@"; fi ' HEAD 

    Commit Nachricht für AB wird von B genommen (obwohl ich von A bevorzugen würde).

    Hat die gleiche Wirkung wie Uwe Kleine-Königs Antwort, arbeitet aber auch für Nicht-Initiale A.

    Das Quetschen des ersten und zweiten Commits würde dazu führen, dass das erste Commit überschrieben wird. Wenn Sie mehr als einen Zweig haben, der auf dem ersten Commit basiert, würden Sie diesen Zweig abschneiden.

    Betrachten Sie das folgende Beispiel:

     a---b---HEAD \ \ '---d 

    Das Zerlegen von a und b in ein neues Commit “ab” würde zu zwei unterschiedlichen Bäumen führen, was in den meisten Fällen nicht wünschenswert ist, da Git-Merge und Git-Rebase nicht länger über die zwei Zweige hinweg arbeiten werden.

     ab---HEAD a---d 

    Wenn du das wirklich willst, kann es gemacht werden. Werfen Sie einen Blick auf git-filter-branch für ein mächtiges (und gefährliches) Werkzeug zum History-Neuschreiben.

    Sie können git filter-branch dafür verwenden. z.B

     git filter-branch --parent-filter \ 'if test $GIT_COMMIT != ; then cat; fi' 

    Dies führt dazu, dass AB-C das Commit-Protokoll von A wegwirft.

    Sie können rebase interactive verwenden, um die letzten beiden Commits zu ändern, bevor sie an einen Remote gesendet werden

     git rebase HEAD^^ -i 

    Es gibt einen einfacheren Weg, dies zu tun. Nehmen wir an, Sie sind im master Zweig

    Erstellen Sie eine neue verwaiste Verzweigung, die den gesamten Commitverlauf entfernt:

     $ git checkout --orphan new_branch 

    Fügen Sie Ihre erste Commit-Nachricht hinzu:

     $ git commit -a 

    Befreien Sie sich von dem alten, nicht zusammenhängenden Master-Zweig:

     $ git branch -D master 

    Benennen Sie Ihren aktuellen Zweig new_branch in master :

     $ git branch -m master