In welchen Fällen könnte "Git Pull" schädlich sein?

409

Ich habe einen Kollegen, der behauptet, das git pullsei schädlich, und der sich immer dann aufregt, wenn jemand es benutzt.

Der git pullBefehl scheint der kanonische Weg zu sein, um Ihr lokales Repository zu aktualisieren. Schafft die Verwendung git pullProbleme? Welche Probleme entstehen dadurch? Gibt es eine bessere Möglichkeit, ein Git-Repository zu aktualisieren?

Richard Hansen
quelle
53
@ j.karlsson: meta.stackexchange.com/questions/17463/…
Richard Hansen
8
Oder Sie können git pull --rebasediese Strategie einfach als Standard für neue Zweige git config branch.autosetuprebase
festlegen
4
knoopx hat es richtig gemacht, indem es ein --rebaseFlag hinzufügt , git pullum lokal mit remote zu synchronisieren , und dann Ihre lokalen Änderungen zusätzlich zu den aktualisierten lokalen wiedergibt . Wenn Sie dann drücken, müssen Sie nur Ihre neuen Commits an das Ende der Remote-Datei anhängen. Ziemlich einfach.
Heath Lilley
4
Danke @BenMcCormick. Ich hatte das bereits getan, aber die Diskussion über die Gültigkeit der Frage scheint in diesen Kommentaren unter der Frage zu stattfinden. Und ich denke, eine Frage zu stellen, um eine Plattform zu schaffen, auf der Sie Ihre persönliche Meinung als Tatsache präsentieren können, ist nicht das, wofür die Q & A-Struktur von SO wirklich ist.
McV
4
@RichardHansen, es scheint nur eine Möglichkeit zu sein, das Punktesystem zu betrügen, besonders wenn Ihre Antwort einen so drastischen Unterschied im Ton und eine so kurze Zeitlücke aufweist. Mit Ihrem Q & A-Modell könnten wir alle einfach Fragen stellen und sie selbst mit unseren bisherigen Kenntnissen beantworten. An diesem Punkt sollten Sie nur in Betracht ziehen, einen Blog-Beitrag zu schreiben, da dies um ein Vielfaches angemessener ist. Ein Q & A sucht speziell das Wissen anderer Leute. Ein Blog-Beitrag zeigt Ihre eigenen.
Josh Brown

Antworten:

546

Zusammenfassung

Erstellt standardmäßig git pullZusammenführungs-Commits, die dem Codeverlauf Rauschen und Komplexität hinzufügen. Darüber hinaus pullist es einfach, nicht darüber nachzudenken, wie Ihre Änderungen durch eingehende Änderungen beeinflusst werden könnten.

Der git pullBefehl ist sicher, solange er nur Schnellvorlaufzusammenführungen ausführt. Wenn git pullkonfiguriert ist, dass nur Schnellvorlaufzusammenführungen durchgeführt werden und eine Schnellvorlaufzusammenführung nicht möglich ist, wird Git mit einem Fehler beendet. Dies gibt Ihnen die Möglichkeit, die eingehenden Commits zu untersuchen, darüber nachzudenken, wie sie sich auf Ihre lokalen Commits auswirken könnten, und die beste Vorgehensweise (Zusammenführen, Wiederherstellen, Zurücksetzen usw.) festzulegen.

Mit Git 2.0 und neuer können Sie Folgendes ausführen:

git config --global pull.ff only

um das Standardverhalten auf nur schnellen Vorlauf zu ändern. Bei Git-Versionen zwischen 1.6.6 und 1.9.x müssen Sie sich angewöhnen, Folgendes zu tippen:

git pull --ff-only

Bei allen Git-Versionen empfehle ich jedoch, einen git upAlias ​​wie folgt zu konfigurieren :

git config --global alias.up '!git remote update -p; git merge --ff-only @{u}'

und mit git upanstelle von git pull. Ich bevorzuge diesen Alias, git pull --ff-onlyweil:

  • es funktioniert mit allen (nicht alten) Versionen von Git,
  • Es werden alle vorgelagerten Zweige abgerufen (nicht nur der Zweig, an dem Sie gerade arbeiten)
  • es reinigt alte origin/*Äste, die stromaufwärts nicht mehr existieren.

Probleme mit git pull

git pullist nicht schlecht, wenn es richtig verwendet wird. Einige kürzlich vorgenommene Änderungen an Git haben die git pullordnungsgemäße Verwendung vereinfacht , aber leider git pullweist das Standardverhalten einer Ebene mehrere Probleme auf:

  • es führt unnötige Nichtlinearitäten in die Geschichte ein
  • Dies macht es einfach, versehentlich wieder eingeführte Commits einzuführen, die absichtlich vorgelagert wurden
  • Es ändert Ihr Arbeitsverzeichnis auf unvorhersehbare Weise
  • Es ist ärgerlich, anzuhalten, was Sie tun, um die Arbeit eines anderen zu überprüfen git pull
  • Dies macht es schwierig, den Remote-Zweig korrekt wiederherzustellen
  • Es werden keine Zweige bereinigt, die im Remote-Repo gelöscht wurden

Diese Probleme werden nachstehend ausführlicher beschrieben.

Nichtlineare Geschichte

Standardmäßig entspricht der git pullBefehl der Ausführung git fetchgefolgt von git merge @{u}. Wenn sich im lokalen Repository nicht gepuschte Commits befinden, erstellt der Zusammenführungsteil von git pullein Zusammenführungs-Commit.

Merge Commits sind an sich nichts Schlechtes, aber sie können gefährlich sein und sollten mit Respekt behandelt werden:

  • Zusammenführungs-Commits sind von Natur aus schwer zu untersuchen. Um zu verstehen, was eine Zusammenführung bewirkt, müssen Sie die Unterschiede zu allen Eltern verstehen. Ein herkömmliches Diff vermittelt diese mehrdimensionalen Informationen nicht gut. Im Gegensatz dazu ist eine Reihe normaler Commits leicht zu überprüfen.
  • Die Lösung von Zusammenführungskonflikten ist schwierig, und Fehler bleiben oft lange Zeit unentdeckt, da Zusammenführungs-Commits schwer zu überprüfen sind.
  • Zusammenführungen können die Auswirkungen regelmäßiger Commits stillschweigend ersetzen. Der Code ist nicht länger die Summe der inkrementellen Commits, was zu Missverständnissen darüber führt, was sich tatsächlich geändert hat.
  • Zusammenführungs-Commits können einige kontinuierliche Integrationsschemata stören (z. B. automatisch nur den Pfad des ersten Elternteils gemäß der angenommenen Konvention erstellen, dass zweite Elternteile auf unvollständige laufende Arbeiten verweisen).

Natürlich gibt es eine Zeit und einen Ort für Zusammenführungen, aber das Verständnis, wann Zusammenführungen verwendet werden sollten und wann nicht, kann die Nützlichkeit Ihres Repositorys verbessern.

Beachten Sie, dass der Zweck von Git darin besteht, das Teilen und Konsumieren der Entwicklung einer Codebasis zu vereinfachen und den Verlauf nicht genau so aufzuzeichnen, wie er sich entfaltet hat. (Wenn Sie nicht einverstanden sind, berücksichtigen Sie den rebaseBefehl und warum er erstellt wurde.) Die von erstellten Zusammenführungs-Commits git pullvermitteln anderen keine nützliche Semantik. Sie sagen lediglich, dass jemand anderes zufällig in das Repository verschoben wurde, bevor Sie mit Ihren Änderungen fertig waren. Warum haben diese Zusammenführungsverpflichtungen, wenn sie für andere nicht von Bedeutung sind und gefährlich sein könnten?

Es ist möglich, git pulleine Neubasis anstelle einer Zusammenführung zu konfigurieren , dies hat jedoch auch Probleme (wird später erläutert). Sollte stattdessen git pullso konfiguriert werden, dass nur Schnellvorlaufzusammenführungen durchgeführt werden.

Wiedereinführung von Rebased-Out-Commits

Angenommen, jemand stößt einen Zweig zurück und drückt ihn mit Gewalt. Dies sollte im Allgemeinen nicht passieren, ist jedoch manchmal erforderlich (z. B. um eine versehentlich festgeschriebene und gepusste 50-GB-Protokolldatei zu entfernen). Durch die Zusammenführung von git pullwird die neue Version des Upstream-Zweigs mit der alten Version zusammengeführt, die noch in Ihrem lokalen Repository vorhanden ist. Wenn Sie das Ergebnis drücken, kommen Pitchgabeln und Taschenlampen auf Sie zu.

Einige mögen argumentieren, dass das eigentliche Problem darin besteht, Aktualisierungen zu erzwingen. Ja, es ist im Allgemeinen ratsam, Kraftstöße nach Möglichkeit zu vermeiden, aber manchmal sind sie unvermeidlich. Entwickler müssen bereit sein, mit Force-Updates umzugehen, da dies manchmal vorkommt. Dies bedeutet, dass die alten Commits nicht blind über ein gewöhnliches System zusammengeführt werden git pull.

Überraschen Sie die Änderungen im Arbeitsverzeichnis

Es gibt keine Möglichkeit vorherzusagen, wie das Arbeitsverzeichnis oder der Index aussehen wird, bis git pulldies abgeschlossen ist. Möglicherweise gibt es Zusammenführungskonflikte, die Sie lösen müssen, bevor Sie etwas anderes tun können. Möglicherweise wird eine 50-GB-Protokolldatei in Ihr Arbeitsverzeichnis eingefügt, weil jemand sie versehentlich verschoben hat. Möglicherweise wird ein Verzeichnis, in dem Sie arbeiten, umbenannt.

git remote update -p(oder git fetch --all -p) ermöglicht es Ihnen, die Commits anderer Personen zu überprüfen, bevor Sie sich für eine Zusammenführung oder Neugründung entscheiden. So können Sie einen Plan erstellen, bevor Sie Maßnahmen ergreifen.

Schwierigkeiten beim Überprüfen der Commits anderer Personen

Angenommen, Sie sind gerade dabei, einige Änderungen vorzunehmen, und jemand anderes möchte, dass Sie einige Commits überprüfen, die er gerade gepusht hat. git pullDurch die Zusammenführungs- (oder Rebase-) Operation werden das Arbeitsverzeichnis und der Index geändert. Dies bedeutet, dass Ihr Arbeitsverzeichnis und Ihr Index sauber sein müssen.

Sie könnten git stashund dann verwenden git pull, aber was tun Sie, wenn Sie mit der Überprüfung fertig sind? Um dorthin zurückzukehren, wo Sie waren, müssen Sie die von erstellte Zusammenführung rückgängig machen git pullund den Stash anwenden.

git remote update -p(oder git fetch --all -p) ändert das Arbeitsverzeichnis oder den Index nicht, sodass die Ausführung jederzeit sicher ist - selbst wenn Sie Änderungen vorgenommen und / oder nicht bereitgestellt haben. Sie können pausieren, was Sie tun, und das Commit eines anderen überprüfen, ohne sich Gedanken über das Verstecken oder Beenden des Commits machen zu müssen, an dem Sie arbeiten. git pullgibt Ihnen diese Flexibilität nicht.

Wiederherstellen auf einem Remote-Zweig

Ein gängiges Git-Verwendungsmuster besteht darin git pull, die neuesten Änderungen vorzunehmen, gefolgt von a git rebase @{u}, um das eingeführte Zusammenführungs-Commit zu beseitigen git pull. Es ist üblich genug , dass Git einige Konfigurationsoptionen hat diese beiden Schritte zu einem einzigen Schritt zu verringern , indem sie sagen , git pullein Fütterungsmaterial anstelle einer Serie auszuführen (siehe branch.<branch>.rebase, branch.autosetuprebaseund pull.rebaseOptionen).

Wenn Sie ein nicht gepushtes Zusammenführungs-Commit haben, das Sie beibehalten möchten (z. B. ein Commit master, in das ein Push- Feature-Zweig zusammengeführt wird ), gibt es leider weder ein Rebase-Pull ( git pullmit branch.<branch>.rebaseset to true) noch ein Merge-Pull (das Standardverhalten git pull), gefolgt von a Rebase wird funktionieren. Dies liegt daran, git rebasedass Zusammenführungen ohne die --preserve-mergesOption eliminiert werden (die DAG wird linearisiert) . Der Rebase-Pull-Vorgang kann nicht so konfiguriert werden, dass Zusammenführungen beibehalten werden, und ein Merge-Pull gefolgt von a git rebase -p @{u}beseitigt die durch das Merge-Pull verursachte Zusammenführung nicht. Update: Git v1.8.5 hinzugefügt git pull --rebase=preserveund git config pull.rebase preserve. Diese Ursache git pullzu tun , git rebase --preserve-mergesnachdem die Upstream - Commits holen. (Danke an Funkaster für das Heads-up!)

Bereinigte Zweige bereinigen

git pullBereinigt keine Remote-Tracking-Zweige, die Zweigen entsprechen, die aus dem Remote-Repository gelöscht wurden. Wenn beispielsweise jemand einen Zweig fooaus dem Remote-Repo löscht , wird dies weiterhin angezeigt origin/foo.

Dies führt dazu, dass Benutzer versehentlich getötete Zweige wiederbeleben, weil sie glauben, noch aktiv zu sein.

Eine bessere Alternative: Verwenden Sie git upanstelle vongit pull

Stattdessen git pullempfehle ich, den folgenden git upAlias ​​zu erstellen und zu verwenden :

git config --global alias.up '!git remote update -p; git merge --ff-only @{u}'

Dieser Alias ​​lädt die neuesten Commits von allen Upstream-Zweigen herunter (Bereinigen der toten Zweige) und versucht, den lokalen Zweig schnell auf den neuesten Commit im Upstream-Zweig vorzuspulen. Wenn dies erfolgreich war, gab es keine lokalen Commits, sodass kein Risiko für Zusammenführungskonflikte bestand. Der schnelle Vorlauf schlägt fehl, wenn lokale (nicht gepusste) Commits vorhanden sind, sodass Sie die Möglichkeit haben, die Upstream-Commits zu überprüfen, bevor Sie Maßnahmen ergreifen.

Dadurch wird Ihr Arbeitsverzeichnis immer noch auf unvorhersehbare Weise geändert, jedoch nur, wenn Sie keine lokalen Änderungen vorgenommen haben. Im Gegensatz zu git pull, git upwerden Sie nie auf eine Aufforderung fallen Sie einen Merge - Konflikt zu beheben erwarten.

Andere Option: git pull --ff-only --all -p

Das Folgende ist eine Alternative zum obigen git upAlias:

git config --global alias.up 'pull --ff-only --all -p'

Diese Version von git uphat das gleiche Verhalten wie der vorherige git upAlias, außer:

  • Die Fehlermeldung ist etwas kryptischer, wenn Ihr lokaler Zweig nicht mit einem Upstream-Zweig konfiguriert ist
  • Es basiert auf einer undokumentierten Funktion (dem -pArgument, an das übergeben wird fetch), die sich in zukünftigen Versionen von Git ändern kann

Wenn Sie Git 2.0 oder neuer ausführen

Mit Git 2.0 und neuer können Sie konfigurieren git pull, dass standardmäßig nur Schnellvorlaufzusammenführungen durchgeführt werden:

git config --global pull.ff only

Dies führt git pulldazu git pull --ff-only, dass es sich so verhält, aber es werden immer noch nicht alle Upstream-Commits abgerufen oder alte origin/*Zweige bereinigt, daher bevorzuge ich es immer noch git up.

Richard Hansen
quelle
6
@brianz: git remote update -pentspricht äquivalent zu git fetch --all -p. Ich habe die Angewohnheit zu tippen, git remote update -pweil es einmal fetchkeine -pOption gab. Bezüglich der Führung !siehe die Beschreibung von alias.*in git help config. Es heißt: "Wenn der Alias-Erweiterung ein Ausrufezeichen vorangestellt ist, wird sie als Shell-Befehl behandelt."
Richard Hansen
13
Git 2.0 fügt eine pull.ffKonfiguration hinzu, die anscheinend dasselbe ohne Aliase erreicht.
Danny Thomas
51
Einige der Gründe klingen wie "Ziehen kann Probleme verursachen, wenn andere verrückte Sachen machen". Nein, es ist verrücktes Zeug wie das Umbasieren eines Commits aus einem Upstream-Repo, das Probleme verursacht. IMO-Rebase ist nur dann sicher, wenn Sie es lokal für ein Commit ausführen, das noch nicht gepusht wurde. Wenn Sie beispielsweise vor dem Push ziehen, hilft das erneute Basieren lokaler Commits dabei, Ihren Verlauf linear zu halten (obwohl der lineare Verlauf keine so große Sache ist). Klingt dennoch git upnach einer interessanten Alternative.
McV
16
Die meisten Ihrer Punkte sind darauf zurückzuführen, dass Sie etwas falsch machen: Sie versuchen, Code in Ihrem eigenen Arbeitszweig zu überprüfen . Das ist keine gute Idee. Erstellen Sie einfach einen neuen Zweig, ziehen Sie --rebase = bewahren und werfen Sie diesen Zweig dann weg (oder führen Sie ihn zusammen, wenn Sie möchten).
Funkaster
5
Der Punkt von @ funkaster hier ist sehr sinnvoll, insbesondere in Bezug auf: "Schwierigkeiten beim Überprüfen der Commits anderer Leute". Dies ist nicht der Überprüfungsablauf, den die meisten Git-Benutzer verwenden. Es ist etwas, das ich noch nie empfohlen habe, und es ist die Ursache für all die unnötige zusätzliche Arbeit, die unter der Überschrift beschrieben wird, nicht git pull.
Ben Regenspan
195

Meine Antwort, aus der Diskussion gezogen , die entstand auf Hacker News:

Ich bin versucht, die Frage nur mit dem Betteridge-Gesetz der Schlagzeilen zu beantworten: Warum wird dies git pullals schädlich angesehen? Ist es nicht.

  • Nichtlinearitäten sind an sich nicht schlecht. Wenn sie die tatsächliche Geschichte darstellen, sind sie in Ordnung.
  • Die versehentliche Wiedereinführung von Commits, die vorgelagert umbasiert wurden, ist das Ergebnis einer fehlerhaften Umschreibung der Geschichte vorgelagert. Sie können den Verlauf nicht neu schreiben, wenn der Verlauf entlang mehrerer Repos repliziert wird.
  • Das Ändern des Arbeitsverzeichnisses ist ein erwartetes Ergebnis. von umstrittener Nützlichkeit, nämlich angesichts des Verhaltens von hg / monotone / darcs / other_dvcs_predating_git, aber auch nicht an sich schlecht.
  • Eine Pause, um die Arbeit anderer zu überprüfen, ist für eine Zusammenführung erforderlich und wiederum ein zu erwartendes Verhalten beim Git-Pull. Wenn Sie nicht zusammenführen möchten, sollten Sie git fetch verwenden. Auch dies ist eine Eigenart von Git im Vergleich zu früheren populären DVDs, aber es ist erwartetes Verhalten und nicht an sich schlecht.
  • Es ist gut, es schwierig zu machen, sich gegen einen Remote-Zweig zu behaupten. Schreiben Sie die Geschichte nicht neu, es sei denn, Sie müssen dies unbedingt tun. Ich kann dieses Streben nach einer (falschen) linearen Geschichte für mein ganzes Leben nicht verstehen
  • Zweige nicht aufzuräumen ist gut. Jedes Repo weiß, was es halten will. Git hat keine Ahnung von Master-Slave-Beziehungen.
Sérgio Carvalho
quelle
13
Genau. Es ist nichts von Natur aus schädlich git pull. Es kann jedoch zu Konflikten mit einigen schädlichen Praktiken kommen, z. B. dem Wunsch, die Geschichte mehr als unbedingt notwendig umzuschreiben. Aber git ist flexibel. Wenn Sie es also anders verwenden möchten, tun Sie dies auf jeden Fall. Aber das liegt daran, dass Sie (naja, @Richard Hansen) etwas Ungewöhnliches in Git machen wollen und nicht daran, dass git pulles schädlich ist.
McV
28
Konnte nicht mehr zustimmen. Menschen befürworten git rebaseund betrachten git pullschädlich? "Ja wirklich?"
Victor Moroz
10
Es wäre schön zu sehen, wie jemand ein Diagramm mit Moral als Achse erstellt und Git-Befehle als gut, schlecht oder irgendwo dazwischen klassifiziert. Dieses Diagramm würde sich zwischen den Entwicklern unterscheiden, obwohl es viel über die Verwendung von Git aussagen würde.
Michaelt
5
Mein Problem mit git pullohne die --rebaseOption ist die Richtung der Zusammenführung, die es erstellt. Wenn Sie sich den Unterschied ansehen, gehören jetzt alle Änderungen in dieser Zusammenführung eher der Person, die gezogen hat, als der Person, die die Änderungen vorgenommen hat. Ich mag einen Workflow, bei dem das Zusammenführen für zwei separate Zweige (A -> B) reserviert ist, sodass das Zusammenführungs-Commit klar ist, was eingeführt wurde, und das erneute Basieren für das Aktualisieren auf demselben Zweig (Remote A -> lokales A) reserviert ist )
Craig Kochis
4
Was bringt es Ihnen also zu wissen, ob jemand nur wenige Sekunden vor jemand anderem einen Zug gemacht hat oder umgekehrt? Ich denke, das ist nur Lärm und verschleiert nur die wirklich relevante Geschichte. Dies verringert sogar den Wert der Geschichte. Eine gute Geschichte sollte a) sauber sein und b) tatsächlich die wichtige Geschichte haben.
David Ongaro
26

Es wird nicht als schädlich angesehen, wenn Sie Git richtig verwenden. Ich sehe, wie sich dies angesichts Ihres Anwendungsfalls negativ auf Sie auswirkt, aber Sie können Probleme vermeiden, indem Sie den freigegebenen Verlauf nicht ändern.

Jage Burdick
quelle
10
Um dies näher zu erläutern: Wenn jeder in seinem eigenen Zweig arbeitet (was meiner Meinung nach die richtige Art ist, Git zu verwenden), git pullist dies kein Problem. Verzweigen in Git ist billig.
AlexQueue
18

Die akzeptierte Antwort behauptet

Der Rebase-Pull-Vorgang kann nicht so konfiguriert werden, dass Zusammenführungen erhalten bleiben

Aber ab Git 1.8.5 , das diese Antwort nachdatiert, können Sie dies tun

git pull --rebase=preserve

oder

git config --global pull.rebase preserve

oder

git config branch.<name>.rebase preserve

Die Dokumente sagen

Wenn Sie preserve,auch --preserve-mergesan 'git rebase' weitergeben, damit lokal festgeschriebene Merge-Commits nicht durch Ausführen von 'git pull' abgeflacht werden.

Diese vorherige Diskussion enthält detailliertere Informationen und Diagramme: git pull --rebase --preserve-merges . Es erklärt auch, warum git pull --rebase=preservenicht dasselbe ist wie git pull --rebase --preserve-merges, was nicht das Richtige tut.

Diese andere vorherige Diskussion erklärt, was die Preserve-Merges-Variante von Rebase tatsächlich bewirkt und wie sie viel komplexer ist als eine reguläre Rebase: Was genau macht Git's "Rebase - Preserve-Merges" (und warum?)

Marc Liyanage
quelle
0

Wenn Sie zum alten Git-Repository gehen, ist der Alias, den sie vorschlagen, anders. https://github.com/aanand/git-up

git config --global alias.up 'pull --rebase --autostash'

Das funktioniert perfekt für mich.

Nathan Redblur
quelle