Kombinieren mehrerer Commits zu einem vor dem Push

130

Diese Frage bezieht sich nicht nur darauf, wie diese Aufgabe zu erfüllen ist, sondern auch darauf, ob dies mit Git eine gute oder eine schlechte Praxis ist.

Bedenken Sie, dass ich lokal am meisten am Hauptzweig arbeite, aber einen aktuellen Zweig erstellt habe, den ich "topical_xFeature" nennen werde. Während ich an "topical_xFeature" arbeite und hin und her wechsle, um andere Arbeiten am Hauptzweig auszuführen, habe ich mehr als ein Commit für den Zweig "topical_xFeature" ausgeführt, aber zwischen jedem Commit habe ich nein getan drücken.

Erstens , würden Sie diese schlechte Praxis in Betracht ziehen? Wäre es nicht klüger, sich an ein Commit pro Zweig und Push zu halten? In welchen Fällen wäre es gut, mehrere Commits in einem Zweig zu haben, bevor ein Push ausgeführt wird?

Zweitens , wie kann ich am besten erreichen, dass die mehreren Commits im Zweig topical_xFeature für einen Push in den Hauptzweig gebracht werden? Ist es ein Ärgernis, sich keine Sorgen zu machen und nur den Push auszuführen, bei dem mehrere Commits gepusht werden, oder ist es weniger ärgerlich, die Commits irgendwie zu einem zusammenzuführen und dann zu pushen? Wie geht das nochmal?

Todd Hopkinson
quelle

Antworten:

139

Bei Ihrer ersten Frage, nein, es ist nichts Falsches daran, mehrere Commits gleichzeitig zu pushen. Oft möchten Sie Ihre Arbeit vielleicht in ein paar kleine, logische Commits aufteilen, aber erst dann hochschieben, wenn Sie das Gefühl haben, dass die gesamte Serie fertig ist. Oder Sie machen mehrere Commits lokal, während die Verbindung getrennt ist, und Sie pushen sie alle, sobald Sie wieder verbunden sind. Es gibt keinen Grund, sich auf ein Commit pro Push zu beschränken.

Ich finde es im Allgemeinen eine gute Idee, jedes Commit für eine einzelne, logische, kohärente Änderung beizubehalten, die alles enthält, was zum Arbeiten erforderlich ist (damit Ihr Code nicht in einem fehlerhaften Zustand bleibt). Wenn Sie zwei Commits haben, die jedoch dazu führen würden, dass der Code beschädigt wird, wenn Sie nur den ersten anwenden, ist es möglicherweise eine gute Idee, den zweiten Commit in den ersten zu quetschen. Wenn Sie jedoch zwei Commits haben, bei denen jeder eine vernünftige Änderung vornimmt, ist es in Ordnung, sie als separate Commits zu verschieben.

Wenn Sie mehrere Commits zusammenfassen möchten, können Sie verwenden git rebase -i. Wenn Sie auf Zweig sind topical_xFeature, würden Sie laufen git rebase -i master. Dies öffnet ein Editorfenster mit einer Reihe von Commits, denen ein Präfix vorangestellt ist pick. Sie können alle bis auf die erste in ändern squash, wodurch Git angewiesen wird, alle diese Änderungen beizubehalten, sie jedoch in das erste Commit zu quetschen. Nachdem Sie dies getan haben, checken Sie masterIhren Feature-Zweig aus und führen Sie ihn zusammen:

git checkout topical_xFeature
git rebase -i master
git checkout master
git merge topical_xFeature

Alternativ, wenn Sie nur in zum Squash alles wollen topical_xFeaturein master, könnten Sie nur Folgendes tun:

git checkout master
git merge --squash topical_xFeature
git commit

Welches Sie wählen, liegt bei Ihnen. Im Allgemeinen würde ich mir keine Sorgen um mehrere kleinere Commits machen, aber manchmal möchten Sie sich nicht mit zusätzlichen kleinen Commits beschäftigen, also quetschen Sie sie einfach zu einem.

Brian Campbell
quelle
1
Nach dem Zusammenführen mit --squash kann ich den Themenzweig mit nicht mehr löschen git branch -d topic. Warum kann git nicht erkennen, dass alle Änderungen zusammengeführt wurden?
Balki
7
@balki Weil Git erkennt, ob Patches zusammengeführt werden, basierend darauf, ob sie im Verlauf des angegebenen Zweigs erscheinen. Squashing Commits ändern sie; Sie werden zu einem neuen Commit, und während dieses neue Commit dasselbe tut wie die anderen, kann Git das nicht sagen, es kann nur sagen, ob Commits gleich sind, wenn sie dieselbe Commit-ID haben (SHA-1). . Sobald Sie es gequetscht haben, müssen Sie git anweisen, den alten Zweig mit git branch -D topiczu löschen, um ihn zwangsweise zu löschen.
Brian Campbell
66

Auf diese Weise befolge ich im Allgemeinen mehrere Commits zu einem einzigen Commit, bevor ich den Code weitergebe.

Um dies zu erreichen, schlage ich vor, dass Sie das von GIT bereitgestellte Squash -Konzept verwenden.

Befolgen Sie die folgenden Schritte.

1) git rebase -i master (anstelle von master können Sie auch ein bestimmtes Commit verwenden)

Öffnen Sie den interaktiven Rebase-Editor, in dem alle Ihre Commits angezeigt werden. Grundsätzlich müssen Sie die Commits identifizieren, die Sie zu einem einzigen Commit zusammenführen möchten.

Stellen Sie sich vor, dies sind Ihre Commits und zeigen Sie so etwas im Editor.

pick f7f3f6d changed my name a bit    
pick 310154e updated README formatting and added blame   
pick a5f4a0d added cat-file  

Es ist wichtig zu beachten, dass diese Commits in der umgekehrten Reihenfolge aufgelistet werden, als Sie sie normalerweise mit dem Befehl log sehen. Das heißt, das ältere Commit wird zuerst angezeigt.

2) Ändern Sie 'pick' in 'squash' für die letzten festgeschriebenen Änderungen. so etwas wie unten gezeigt. Wenn Sie dies tun, werden Ihre letzten 2 Commits mit dem ersten zusammengeführt.

pick f7f3f6d changed my name a bit         
squash 310154e updated README formatting and added blame   
squash a5f4a0d added cat-file

Sie können auch eine Kurzform verwenden, wenn Sie viele Commits kombinieren müssen:

p f7f3f6d changed my name a bit         
s 310154e updated README formatting and added blame   
s a5f4a0d added cat-file

Zum Bearbeiten mit 'i' wird der Editor zum Einfügen aktiviert. Beachten Sie, dass die meisten (ältesten) Commits nicht gequetscht werden können, da es kein vorheriges Commit gibt, mit dem sie kombiniert werden können. Also muss es ausgewählt werden oder 'p'. Verwenden Sie 'Esc', um den Einfügemodus zu verlassen.

3) Speichern Sie nun den Editor mit dem folgenden Befehl. : wq

Wenn Sie das speichern, haben Sie ein einziges Commit, das die Änderungen aller drei vorherigen Commits übernimmt.

Hoffe das wird dir helfen.

Kondal Kolipaka
quelle
5
Vielleicht ist dies für andere offensichtlich, aber wenn Sie "git rebase -i" sagen, müssen Sie auch angeben, bei welchem ​​Commit Sie beginnen. Dies ist mir nicht klar geworden, als ich versucht habe, diesem Beispiel zu folgen. In diesem Beispiel wäre es also "git rebase -i xxxxx", wobei xxxxx das Commit direkt vor f7f3f6d chronologisch ist. Nachdem ich das herausgefunden hatte, funktionierte alles genau wie oben beschrieben.
Nukeguy
Das ist interessant @nukeguy, ich hatte kein Problem damit, kein bestimmtes Commit anzugeben. Es wurde nur standardmäßig das verwendet, was dort war.
JCrooks
Vielleicht war @nukeguy git rebase -i HEAD~2ein hilfreicher Ort für mich, um anzufangen. Dann war diese Antwort hilfreich. Dann git statuszeigte ich "Ihr Zweig und 'origin / feature / xyz' sind auseinander gegangen und haben jeweils 1 und 1 verschiedene Commits." So musste ich git push origin feature/xyz --force-with-leasesehen stackoverflow.com/a/59309553/470749 und freecodecamp.org/forum/t/...
Ryan
11

Erstens : Nichts sagt Ihnen, dass Sie nur einen Commit pro Zweig pro Push haben sollen: Ein Push ist ein Veröffentlichungsmechanismus, mit dem Sie einen lokalen Verlauf (dh eine Sammlung von Commits) auf einem Remote-Repo veröffentlichen können.

Zweitens : a git merge --no-ff topical_xFeaturewürde auf Master als einzelnes Commit Ihre Themenarbeit aufzeichnen, bevor Sie pushen master.
(Auf diese Weise bleiben Sie topical_xFeaturefür weitere Entwicklungen, die Sie masterbei der nächsten Zusammenführung als einzelnes neues Commit aufzeichnen können - no-ff.
Wenn topical_xFeaturedas Ziel das Entfernen ist, git merge --squashist dies die richtige Option, wie in Brian Campbell beschrieben ‚s Antwort .)

VonC
quelle
Ich denke das --squashist nicht --no-ffwas du willst. --no-ffwürde ein Merge-Commit erstellen, aber auch alle Commits von belassen topical_xFeature.
Brian Campbell
@Brian: Ich stimme zu und habe Ihre Antwort positiv bewertet, aber ich habe zuerst an die Option --no-ff gedacht, weil ich die topical_featureVerzweigung beibehalten und nur ein einzelnes Commit für die masterVerzweigung aufzeichnen wollte .
VonC
8

Wechseln Sie zur Hauptniederlassung und stellen Sie sicher, dass Sie auf dem neuesten Stand sind.

git checkout master

git fetch Dies kann erforderlich sein (abhängig von Ihrer Git-Konfiguration), um Updates zu Origin / Master zu erhalten

git pull

Führen Sie den Feature-Zweig in den Hauptzweig ein.

git merge feature_branch

Setzen Sie den Hauptzweig auf den Ursprungsstatus zurück.

git reset origin/master

Git betrachtet nun alle Änderungen als nicht bereitgestellte Änderungen. Wir können diese Änderungen als ein Commit hinzufügen. Hinzufügen. fügt auch nicht verfolgte Dateien hinzu.

git add --all

git commit

Ref: https://makandracards.com/makandra/527-squash-several-git-commits-into-a-single-commit

Shiva Kumar
quelle
3
Diese Antwort ist leicht zu folgen und wirklich leicht zu visualisieren.
Jokab
6
  1. Wählen Sie zuerst aus, nach welchem ​​Commit alles kommen soll.

    git reflog
    5976f2b HEAD@{0}: commit: Fix conflicts
    80e85a1 HEAD@{1}: commit: Add feature
    b860ddb HEAD@{2}: commit: Add something
    
  2. Auf den ausgewählten Kopf zurücksetzen (ich habe ausgewählt HEAD@{2})

    git reset b860ddb --soft
    
  3. git status (Nur um sicher zu gehen)

  4. Fügen Sie Ihr neues Commit hinzu

    git commit -m "Add new commit"
    

Hinweis: HEAD@{0}& HEAD@{1}Sind jetzt zu einem Commit zusammengeführt, kann dies auch für mehrere Commits durchgeführt werden.

git reflog sollte wieder anzeigen:

git reflog
5976f2b HEAD@{0}: commit: Add new commit
b860ddb HEAD@{1}: commit: Add something
Eddy Ekofo
quelle
0

Ein Tool zum Automatisieren mehrerer Commits in einem

wie Kondal Kolipaka sagt . Verwenden von "git rebase -i"

Die Logik von "Git Rebase"

Bei Verwendung von "git rebase -i" generiert git die git-rebase-todo-Datei im aktuellen .git / rebase-merge-Verzeichnis und ruft dann den git-Editor auf, damit Benutzer die git-rebase-todo-Datei zur Verarbeitung bearbeiten können. Das Tool muss also Folgendes erfüllen:

  1. Ändern Sie den Git-Editor in das von uns bereitgestellte Tool.
  2. Das Tool verarbeitet die Datei git-rebase-todo.

Ändern Sie den Standard-Git-Editor

git config core.editor #show current default git editor
git config --local --replace-all  core.editor NEW_EDITOR # set the local branch using NEW_EDITOR as git editor

Daher muss das Tool den Git-Editor ändern und die Git-Rebase-ToDo-Datei verarbeiten. Das Tool mit Python unten:

#!/usr/bin/env python3
#encoding: UTF-8

import os
import sys

def change_editor(current_file):
    os.system("git config --local --replace-all  core.editor " + current_file) # Set current_file as git editor
    os.system("git rebase -i") # execute the "git rebase -i" and will invoke the python file later with git-rebase-todo file as argument
    os.system("git config --local --replace-all core.editor vim") # after work reset the git editor to default

def rebase_commits(todo_file):
    with open(todo_file, "r+") as f:
        contents = f.read() # read git-rebase-todo's content
        contents = contents.split("\n")
        first_commit = True
        f.truncate()
        f.seek(0)
        for content in contents:
            if content.startswith("pick"):
                if first_commit:
                    first_commit = False
                else:
                    content = content.replace("pick", "squash") # replace the pick to squash except for the first pick
            f.write(content + "\n")

def main(args):
    if len(args) == 2:
        rebase_commits(args[1]) # process the git-rebase-todo
    else:
        change_editor(os.path.abspath(args[0])) # set git editor

if __name__ == "__main__":
    main(sys.argv)

Ref: https://liwugang.github.io/2019/12/30/git_commits_en.html

Liwugang
quelle
4
Bitte reduzieren Sie Ihre Website-Werbung. Siehe auch Wie man kein Spammer ist.
Tripleee