PEP 103 – Informationen über git sammeln
- Autor:
- Oleg Broytman <phd at phdru.name>
- Status:
- Zurückgezogen
- Typ:
- Informational
- Erstellt:
- 01-Jun-2015
- Post-History:
- 12-Sep-2015
Inhaltsverzeichnis
- Zusammenfassung
- Dokumentation
- Schnellstart
- Beispiele in diesem PEP
- Zweige und Branches
- Remote-Repositories und Remote-Branches
- Commit-Bearbeitung und Haftungsausschlüsse
- Rückgängig machen
- Merge oder Rebase?
- Null-Merges
- Branching-Modelle
- Erweiterte Konfiguration
- Fortgeschrittene Themen
- Tipps und Tricks
- Git auf dem Server
- Von Mercurial zu Git
- Git und GitHub
- Urheberrecht
Zusammenfassung
Dieser Informations-PEP sammelt Informationen über Git. Es gibt natürlich viel Dokumentation für Git, daher konzentriert sich der PEP auf komplexere (und mehr mit der Python-Entwicklung verwandte) Probleme, Szenarien und Beispiele.
Der Plan ist, den PEP in Zukunft zu erweitern und Informationen über die Äquivalenz von Mercurial- und Git-Szenarien zu sammeln, um die Migration von Python-Entwicklungen von Mercurial zu Git zu unterstützen.
Der Autor des PEP plant derzeit nicht, einen Prozess-PEP zur Migration von Python-Entwicklungen von Mercurial zu Git zu schreiben.
Dokumentation
Git wird von viel Dokumentation begleitet, sowohl online als auch offline.
Dokumentation für Einsteiger
Git Benutzerhandbuch. Alltag GIT mit 20 Befehlen oder so. Git-Workflows.
Fortgeschrittene Dokumentation
Git Magic, mit einer Reihe von Übersetzungen.
Pro Git. Das Buch über Git. Kaufen Sie es bei Amazon oder laden Sie es im PDF-, Mobi- oder ePub-Format herunter. Es gibt Übersetzungen in viele verschiedene Sprachen. Russische Übersetzung von GArik herunterladen.
Git Buch (Deutsch).
Offline-Dokumentation
Git hat integrierte Hilfe: führen Sie git help $THEMA aus. Führen Sie zum Beispiel git help git oder git help help aus.
Schnellstart
Download und Installation
Unix-Benutzer: Herunterladen und installieren mit Ihrem Paketmanager.
Microsoft Windows: Laden Sie git-for-windows herunter.
MacOS X: Verwenden Sie Git, das mit XCode installiert wurde, oder laden Sie es von MacPorts oder git-osx-installer herunter, oder installieren Sie Git mit Homebrew: brew install git.
git-cola (Repository) ist eine Git GUI, die in Python und unter GPL-Lizenz geschrieben wurde. Linux, Windows, MacOS X.
TortoiseGit ist eine Windows Shell-Schnittstelle zu Git, die auf TortoiseSVN basiert; Open Source.
Erste Konfiguration
Dieser einfache Code erscheint oft in der Dokumentation, ist aber wichtig, daher wiederholen wir ihn hier. Git speichert Autor- und Committer-Namen/E-Mails in jedem Commit, also konfigurieren Sie Ihren echten Namen und Ihre bevorzugte E-Mail-Adresse.
$ git config --global user.name "User Name"
$ git config --global user.email user.name@example.org
Beispiele in diesem PEP
Beispiele für Git-Befehle in diesem PEP verwenden den folgenden Ansatz. Es wird davon ausgegangen, dass Sie, der Benutzer, mit einem lokalen Repository namens python arbeiten, das ein Upstream-Remote-Repo namens origin hat. Ihr lokales Repo hat zwei Branches v1 und master. Für die meisten Beispiele ist der aktuell ausgecheckte Branch master. Das heißt, es wird angenommen, dass Sie etwas wie das getan haben.
$ git clone https://git.python.org/python.git
$ cd python
$ git branch v1 origin/v1
Der erste Befehl klont das Remote-Repository in das lokale Verzeichnis python, erstellt einen neuen lokalen Branch master, setzt remotes/origin/master als dessen Upstream-Remote-Tracking-Branch und checkt ihn in das Arbeitsverzeichnis aus.
Der letzte Befehl erstellt einen neuen lokalen Branch v1 und setzt remotes/origin/v1 als dessen Upstream-Remote-Tracking-Branch.
Das gleiche Ergebnis kann mit folgenden Befehlen erzielt werden:
$ git clone -b v1 https://git.python.org/python.git
$ cd python
$ git checkout --track origin/master
Der letzte Befehl erstellt einen neuen lokalen Branch master, setzt remotes/origin/master als dessen Upstream-Remote-Tracking-Branch und checkt ihn in das Arbeitsverzeichnis aus.
Zweige und Branches
Die Git-Terminologie kann etwas irreführend sein. Nehmen Sie zum Beispiel den Begriff "Branch". In Git hat er zwei Bedeutungen. Ein Branch ist eine gerichtete Linie von Commits (möglicherweise mit Merges). Und ein Branch ist eine Markierung oder ein Zeiger, der einer Linie von Commits zugewiesen ist. Es ist wichtig zu unterscheiden, wann Sie über Commits sprechen und wann über deren Bezeichnungen. Linien von Commits sind an sich unbenannt und werden normalerweise nur länger und zusammengeführt. Bezeichnungen hingegen können frei erstellt, verschoben, umbenannt und gelöscht werden.
Remote-Repositories und Remote-Branches
Remote-Tracking-Branches sind Branches (Zeiger auf Commits) in Ihrem lokalen Repository. Sie sind dafür da, dass Git (und Sie) sich merken, welche Branches und Commits von welchen Remote-Repos (Sie können von vielen Remotes ziehen und zu vielen pushen) gezogen und gepusht wurden. Remote-Tracking-Branches leben unter den Namespaces remotes/$REMOTE, z.B. remotes/origin/master.
Um den Status von Remote-Tracking-Branches anzuzeigen, führen Sie aus:
$ git branch -rv
Um lokale und Remote-Tracking-Branches (und Tags) anzuzeigen, die auf Commits zeigen
$ git log --decorate
Sie führen Ihre eigene Entwicklung niemals auf Remote-Tracking-Branches durch. Sie erstellen einen lokalen Branch, der einen Remote-Branch als Upstream hat, und erledigen die Entwicklung auf diesem lokalen Branch. Beim Pushen pusht Git Commits zum Remote-Repo und aktualisiert Remote-Tracking-Branches, beim Pullen holt Git Commits vom Remote-Repo, aktualisiert Remote-Tracking-Branches und führt einen Fast-Forward, Merge oder Rebase durch.
Wenn Sie einen initialen Klon wie diesen durchführen:
$ git clone -b v1 https://git.python.org/python.git
Git klont das Remote-Repository https://git.python.org/python.git in das Verzeichnis python, erstellt ein Remote namens origin, erstellt Remote-Tracking-Branches, erstellt einen lokalen Branch v1, konfiguriert ihn zum Tracen des Upstream-remotes/origin/v1-Branches und checkt v1 in das Arbeitsverzeichnis aus.
Einige Befehle, wie git status --branch und git branch --verbose, berichten über den Unterschied zwischen lokalen und Remote-Branches. Bitte denken Sie daran, dass sie nur Vergleiche mit Remote-Tracking-Branches in Ihrem lokalen Repository durchführen und der Zustand dieser Remote-Tracking-Branches veraltet sein kann. Um Remote-Tracking-Branches zu aktualisieren, holen Sie entweder Commits vom Remote-Repository und führen einen Merge (oder Rebase) durch oder aktualisieren Sie Remote-Tracking-Branches, ohne lokale Branches zu aktualisieren.
Lokale und Remote-Tracking-Branches aktualisieren
Um Remote-Tracking-Branches zu aktualisieren, ohne lokale Branches zu aktualisieren, führen Sie git remote update [$REMOTE...] aus. Zum Beispiel:
$ git remote update
$ git remote update origin
Fetch und Pull
Es gibt einen großen Unterschied zwischen
$ git fetch $REMOTE $BRANCH
und
$ git fetch $REMOTE $BRANCH:$BRANCH
Der erste Befehl holt Commits aus dem benannten $BRANCH im $REMOTE-Repository, die nicht in Ihrem Repository vorhanden sind, aktualisiert den Remote-Tracking-Branch und hinterlässt die ID (den Hash) des Head-Commits in der Datei .git/FETCH_HEAD.
Der zweite Befehl holt Commits aus dem benannten $BRANCH im $REMOTE-Repository, die nicht in Ihrem Repository vorhanden sind, und aktualisiert sowohl den lokalen Branch $BRANCH als auch seinen Upstream-Remote-Tracking-Branch. Aber er weigert sich, Branches bei Nicht-Fast-Forward zu aktualisieren. Und er weigert sich, den aktuellen Branch (aktuell ausgecheckter Branch, auf den HEAD zeigt) zu aktualisieren.
Der erste Befehl wird intern von git pull verwendet.
$ git pull $REMOTE $BRANCH
ist äquivalent zu
$ git fetch $REMOTE $BRANCH
$ git merge FETCH_HEAD
Sicherlich sollte $BRANCH in diesem Fall Ihr aktueller Branch sein. Wenn Sie einen anderen Branch in Ihren aktuellen Branch mergen möchten, aktualisieren Sie zuerst diesen nicht-aktuellen Branch und mergen Sie ihn dann.
$ git fetch origin v1:v1 # Update v1
$ git pull --rebase origin master # Update the current branch master
# using rebase instead of merge
$ git merge v1
Wenn Sie jedoch noch keine Commits auf v1 gepusht haben, muss das Szenario etwas komplexer werden. Git weigert sich, einen nicht-fast-forwardable Branch zu aktualisieren, und Sie möchten keinen Force-Pull durchführen, da dies Ihre nicht-gepushten Commits entfernen würde und Sie sie wiederherstellen müssten. Sie möchten also v1 rebasen, können aber keinen nicht-aktuellen Branch rebasen. Daher checken Sie v1 aus und rebasen ihn, bevor Sie mergen.
$ git checkout v1
$ git pull --rebase origin v1
$ git checkout master
$ git pull --rebase origin master
$ git merge v1
Es ist möglich, Git so zu konfigurieren, dass es ein paar Branches oder alle Branches gleichzeitig fetcht/pullt, sodass Sie einfach ausführen können:
$ git pull origin
oder sogar
$ git pull
Das Standard-Remote-Repository für Fetching/Pulling ist origin. Der Standard-Satz von Referenzen zum Fetchen wird mit einem Abgleichsalgorithmus berechnet: Git holt alle Branches, die auf beiden Seiten denselben Namen haben.
Push
Pushen ist etwas einfacher. Es gibt nur einen Befehl: push. Wenn Sie ausführen:
$ git push origin v1 master
Git pusht lokales v1 nach remote v1 und lokales master nach remote master. Dasselbe wie:
$ git push origin v1:v1 master:master
Git pusht Commits zum Remote-Repo und aktualisiert Remote-Tracking-Branches. Git weigert sich, Commits zu pushen, die nicht fast-forwardable sind. Sie können trotzdem forcet-pushen, aber denken Sie daran: Sie können zu Ihren eigenen Repos forcet-pushen, aber nicht zu öffentlichen oder geteilten Repos. Wenn Git sich weigert, Commits zu pushen, die nicht fast-forwardable sind, ist es besser, Commits vom Remote-Repo zu fetchen und zu mergen (oder Ihre Commits über die gefetchten Commits zu rebasen), dann zu pushen. Nur forcet-pushen, wenn Sie wissen, was Sie tun und warum Sie es tun. Siehe den Abschnitt Commit-Bearbeitung und Haftungsausschlüsse unten.
Es ist möglich, Git so zu konfigurieren, dass es ein paar Branches oder alle Branches gleichzeitig pusht, sodass Sie einfach ausführen können:
$ git push origin
oder sogar
$ git push
Das Standard-Remote-Repository für das Pushen ist origin. Der Standard-Satz von Referenzen zum Pushen in Git vor 2.0 wird mit einem Abgleichsalgorithmus berechnet: Git pusht alle Branches, die auf beiden Seiten denselben Namen haben. Der Standard-Satz von Referenzen zum Pushen in Git 2.0+ wird mit einem einfachen Algorithmus berechnet: Git pusht den aktuellen Branch zurück zu seinem @{upstream}.
Um Git vor 2.0 auf das neue Verhalten zu konfigurieren, führen Sie aus:
$ git config push.default simple
Um Git 2.0+ auf das alte Verhalten zu konfigurieren, führen Sie aus:
$ git config push.default matching
Git erlaubt es nicht, einen Branch zu pushen, wenn es der aktuelle Branch im Remote-Nicht-Bare-Repository ist: Git weigert sich, das Remote-Arbeitsverzeichnis zu aktualisieren. Sie sollten wirklich nur zu Bare-Repositories pushen. Für Nicht-Bare-Repositories bevorzugt Git einen Pull-basierten Workflow.
Wenn Sie Code auf einem Remote-Host bereitstellen möchten und nur Push verwenden können (weil Ihre Workstation hinter einer Firewall steht und Sie nicht von dort pullen können), tun Sie dies in zwei Schritten mit zwei Repositories: Sie pushen von der Workstation zu einem Bare-Repo auf dem Remote-Host, verbinden Sie sich per SSH mit dem Remote-Host und pullen Sie aus dem Bare-Repo in ein Nicht-Bare-Deployment-Repo.
Das änderte sich in Git 2.3, aber siehe den Blogbeitrag für Haftungsausschlüsse; in 2.4 wurde die Push-to-Deploy-Funktion weiter verbessert.
Private Informationen
Beim Klonen/Fetchen/Pullen/Pushen kopiert Git nur Datenbankobjekte (Commits, Bäume, Dateien und Tags) und symbolische Referenzen (Branches und Lightweight-Tags). Alles andere ist privat für das Repository und wird niemals geklont, aktualisiert oder gepusht. Das sind Ihre Konfigurationen, Ihre Hooks, Ihre privaten .git/info/exclude-Dateien.
Wenn Sie Hooks verteilen möchten, kopieren Sie sie in den Arbeitsbaum, fügen Sie sie hinzu, committen Sie sie, pushen Sie sie und weisen Sie das Team an, die Hooks manuell zu aktualisieren und zu installieren.
Commit-Bearbeitung und Haftungsausschlüsse
Eine Warnung, keine veröffentlichten (gepushten) Commits zu bearbeiten, erscheint auch in der Dokumentation, wird hier aber trotzdem wiederholt, da sie sehr wichtig ist.
Es ist möglich, sich von einem Force-Push zu erholen, aber es ist für das gesamte Team ein PITA (Pain In The Ass). Bitte vermeiden Sie es.
Um zu sehen, welche Commits noch nicht veröffentlicht wurden, vergleichen Sie den Kopf des Branches mit seinem Upstream-Remote-Tracking-Branch:
$ git log origin/master.. # from origin/master to HEAD (of master)
$ git log origin/v1..v1 # from origin/v1 to the head of v1
Für jeden Branch, der einen Upstream-Remote-Tracking-Branch hat, verwaltet Git einen Alias @{upstream} (kurze Version @{u}), sodass die obigen Befehle wie folgt gegeben werden können:
$ git log @{u}..
$ git log v1@{u}..v1
Um den Status aller Branches anzuzeigen
$ git branch -avv
Um den Status lokaler Branches mit einem Remote-Repo zu vergleichen
$ git remote show origin
Lesen Sie wie man sich von einem Upstream-Rebase erholt. Es ist in git help rebase.
Andererseits sollten Sie sich nicht zu sehr vor der Commit-Bearbeitung fürchten. Sie können Commits, die noch nicht gepusht wurden, sicher bearbeiten, neu ordnen, entfernen, kombinieren und aufteilen. Sie können sogar Commits in Ihr eigenes (Backup-)Repo pushen, sie später bearbeiten und die bearbeiteten Commits forcet-pushen, um das bereits Gepushte zu ersetzen. Kein Problem, solange die Commits nicht in einem öffentlichen oder geteilten Repository sind.
Rückgängig machen
Was auch immer Sie tun, geraten Sie nicht in Panik. Fast alles in Git kann rückgängig gemacht werden.
git checkout: Dateiinhalt wiederherstellen
git checkout kann zum Beispiel verwendet werden, um den Inhalt von Datei(en) auf den Stand eines Commits zurückzusetzen. So:
git checkout HEAD~ README
Die Befehle stellen den Inhalt der Datei README auf den vorletzten Commit im aktuellen Branch wieder her. Standardmäßig ist die Commit-ID einfach HEAD; d.h. git checkout README stellt README auf den letzten Commit wieder her.
(Verwenden Sie git checkout nicht, um den Inhalt einer Datei in einem Commit anzuzeigen, verwenden Sie git cat-file -p; z.B. git cat-file -p HEAD~:path/to/README).
git reset: (noch nicht gepushte) Commits entfernen
git reset verschiebt den Kopf des aktuellen Branches. Der Kopf kann auf jeden Commit verschoben werden, wird aber oft verwendet, um einen Commit oder einige (vorzugsweise nicht gepushte) Commits vom oberen Ende des Branches zu entfernen - das heißt, den Branch zurückzubewegen, um einige (nicht gepushte) Commits rückgängig zu machen.
git reset hat drei Betriebsmodi: soft, hard und mixed. Standard ist mixed. ProGit erklärt den Unterschied sehr klar. Bare-Repositories haben keine Indizes oder Arbeitsbäume, daher ist in einem Bare-Repo nur ein Soft-Reset möglich.
Unstaging
Mixed-Mode-Reset mit einem oder mehreren Pfaden kann verwendet werden, um Änderungen aus dem Staging-Bereich zu entfernen - das heißt, Änderungen, die mit git add zum Committen hinzugefügt wurden, aus dem Index zu entfernen. Details zum Unstaging und andere Undo-Tricks finden Sie in The Book.
git reflog: Referenzprotokoll
Commits mit git reset zu entfernen oder den Kopf eines Branches zu verschieben, klingt gefährlich und ist es auch. Aber es gibt eine Möglichkeit, dies rückgängig zu machen: ein weiterer Reset zurück zum ursprünglichen Commit. Git entfernt Commits nicht sofort; nicht referenzierte Commits (in Git-Terminologie "dangling commits" genannt) bleiben für eine gewisse Zeit in der Datenbank (standardmäßig zwei Wochen), sodass Sie dorthin zurücksetzen oder einen neuen Branch erstellen können, der auf den ursprünglichen Commit zeigt.
Für jede Bewegung des Kopfes eines Branches - mit git commit, git checkout, git fetch, git pull, git rebase, git reset und so weiter - speichert Git ein Referenzprotokoll (kurz reflog). Für jede Bewegung speichert Git, wo der Kopf war. Der Befehl git reflog kann verwendet werden, um das Protokoll anzuzeigen (und zu manipulieren).
Zusätzlich zu den Verschiebungen des Kopfes jedes Branches speichert Git die Verschiebungen von HEAD - einer symbolischen Referenz, die (normalerweise) den aktuellen Branch benennt. HEAD wird mit git checkout $BRANCH geändert.
Standardmäßig zeigt git reflog die Verschiebungen von HEAD an, d.h. der Befehl ist äquivalent zu git reflog HEAD. Um die Verschiebungen des Kopfes eines Branches anzuzeigen, verwenden Sie den Befehl git reflog $BRANCH.
Um also einen git reset rückgängig zu machen, suchen Sie den ursprünglichen Commit in git reflog, verifizieren Sie ihn mit git show oder git log und führen Sie git reset $COMMIT_ID aus. Git speichert die Bewegung des Branch-Kopfes im Reflog, sodass Sie dieses Rückgängigmachen später wieder rückgängig machen können.
In einer komplexeren Situation möchten Sie einige Commits verschieben und gleichzeitig den Kopf des Branches zurücksetzen. Cherry-Picken Sie sie auf den neuen Branch. Wenn Sie zum Beispiel den Branch master auf den ursprünglichen Commit zurücksetzen, aber zwei Commits beibehalten möchten, die im aktuellen Branch erstellt wurden, tun Sie etwas wie:
$ git branch save-master # create a new branch saving master
$ git reflog # find the original place of master
$ git reset $COMMIT_ID
$ git cherry-pick save-master~ save-master
$ git branch -D save-master # remove temporary branch
git revert: Commit rückgängig machen
git revert macht einen oder mehrere Commits rückgängig, d.h. er erstellt einen oder mehrere neue Commits, die die Auswirkungen der angegebenen Commits rückgängig machen. Es ist die einzige Möglichkeit, veröffentlichte Commits rückgängig zu machen ( git commit --amend, git rebase und git reset ändern den Branch auf Nicht-Fast-Forward-Weise, daher sollten sie nur für nicht gepushte Commits verwendet werden.)
Es gibt ein Problem beim Rückgängigmachen eines Merge-Commits. git revert kann den Code, der durch den Merge-Commit erstellt wurde, rückgängig machen, aber es kann nicht die Tatsache des Merges rückgängig machen. Siehe die Diskussion Wie man einen fehlerhaften Merge rückgängig macht.
Eine Sache, die nicht rückgängig gemacht werden kann
Was auch immer Sie rückgängig machen, es gibt eine Sache, die nicht rückgängig gemacht werden kann: überschriebene uncommittete Änderungen. Uncommittete Änderungen gehören nicht zu Git, daher kann Git nicht helfen, sie zu erhalten.
Die meiste Zeit warnt Sie Git, wenn Sie einen Befehl ausführen möchten, der uncommittete Änderungen überschreibt. Git erlaubt Ihnen nicht, Branches mit git checkout zu wechseln. Es hält Sie auf, wenn Sie mit einem nicht sauberen Arbeitsbaum rebachen wollen. Es weigert sich, neue Commits über nicht committete Dateien zu pullen.
Aber es gibt Befehle, die genau das tun - Dateien im Arbeitsbaum überschreiben. Befehle wie git checkout $PFADE oder git reset --hard überschreiben stillschweigend Dateien, einschließlich Ihrer uncommitteten Änderungen.
Mit diesem Wissen können Sie die Haltung "früh committen, oft committen" verstehen. Committen Sie so oft wie möglich. Committen Sie bei jeder Speicherung in Ihrem Editor oder IDE. Sie können Ihre Commits vor dem Pushen bearbeiten - Commit-Nachrichten ändern, Commits ändern, neu ordnen, kombinieren, aufteilen, entfernen. Aber speichern Sie Ihre Änderungen in der Git-Datenbank, entweder committen Sie die Änderungen oder stashen Sie sie zumindest mit git stash.
Merge oder Rebase?
Das Internet ist voller hitziger Diskussionen zum Thema: "Merge oder Rebase?" Die meisten davon sind bedeutungslos. Wenn ein DVCS in einem großen Team mit einem großen und komplexen Projekt mit vielen Branches verwendet wird, gibt es einfach keine Möglichkeit, Merges zu vermeiden. Die Frage reduziert sich also auf "sollte man Rebase verwenden, und wenn ja - wann sollte man Rebase verwenden?" Unter Berücksichtigung der dringenden Empfehlung, veröffentlichte Commits nicht zu rebasen, reduziert sich die Frage noch weiter: "sollte man Rebase auf nicht gepushte Commits anwenden?"
Diese kleine Frage muss das Team entscheiden. Um die Schönheit der linearen Historie zu bewahren, wird empfohlen, Rebase beim Pulling zu verwenden, d.h. git pull --rebase auszuführen oder sogar eine automatische Einrichtung von Rebase für jeden neuen Branch zu konfigurieren.
$ git config branch.autosetuprebase always
und Rebase für bestehende Branches zu konfigurieren:
$ git config branch.$NAME.rebase true
Zum Beispiel:
$ git config branch.v1.rebase true
$ git config branch.master.rebase true
Danach wird git pull origin master äquivalent zu git pull --rebase origin master.
Es wird empfohlen, neue Commits in einem separaten Feature- oder Topic-Branch zu erstellen, während Sie Rebase verwenden, um den Mainline-Branch zu aktualisieren. Wenn der Topic-Branch fertig ist, mergen Sie ihn in den Mainline-Branch. Um eine mühsame Aufgabe der gleichzeitigen Auflösung einer großen Anzahl von Konflikten zu vermeiden, können Sie den Topic-Branch von Zeit zu Zeit in den Mainline-Branch mergen und zum Topic-Branch zurückkehren, um weiter daran zu arbeiten. Der gesamte Workflow wäre etwas wie:
$ git checkout -b issue-42 # create a new issue branch and switch to it
...edit/test/commit...
$ git checkout master
$ git pull --rebase origin master # update master from the upstream
$ git merge issue-42
$ git branch -d issue-42 # delete the topic branch
$ git push origin master
Wenn der Topic-Branch gelöscht wird, wird nur das Label entfernt, Commits bleiben in der Datenbank, sie sind jetzt in master gemerged.
o--o--o--o--o--M--< master - the mainline branch
\ /
--*--*--* - the topic branch, now unnamed
Der Topic-Branch wird gelöscht, um die Branch-Namensraum nicht mit kleinen Topic-Branches zu überladen. Informationen darüber, welches Problem behoben oder welche Funktion implementiert wurde, sollten in den Commit-Nachrichten stehen.
Aber selbst diese geringe Menge an Rebasing könnte zu viel sein, wenn es sich um langlebige gemergte Branches handelt. Stellen Sie sich vor, Sie arbeiten sowohl im v1- als auch im master-Branch und mergen regelmäßig v1 in master. Nach einiger Zeit werden Sie viele Merge- und Nicht-Merge-Commits in master haben. Dann möchten Sie Ihre abgeschlossene Arbeit in ein gemeinsames Repository pushen und stellen fest, dass jemand einige Commits nach v1 gepusht hat. Nun haben Sie die Wahl zwischen zwei gleich schlechten Alternativen: Entweder holen Sie v1 und rebasen es, und müssen dann all Ihre Arbeit in master neu erstellen (setzen Sie master auf origin zurück, mergen Sie v1 und cherry-picken Sie alle Nicht-Merge-Commits vom alten master); oder Sie mergen das neue v1 und verlieren die Schönheit der linearen Historie.
Null-Merges
Git hat eine integrierte Merge-Strategie für das, was die Python-Core-Entwickler "Null-Merge" nennen.
$ git merge -s ours v1 # null-merge v1 into master
Branching-Modelle
Git geht von keinem bestimmten Entwicklungsmodell bezüglich Branching und Merging aus. Einige Projekte bevorzugen es, Patches vom ältesten Branch zum neuesten zu verschieben, andere bevorzugen es, Commits rückwärts zu cherry-picken, einige verwenden Squashing (Kombination einer Anzahl von Commits in einen). Alles ist möglich.
Hier sind ein paar Beispiele für den Anfang. git help workflows beschreibt, wie die Git-Autoren selbst Git entwickeln.
Das ProGit-Buch hat einige Kapitel, die sich mit Branch-Management in verschiedenen Projekten beschäftigen: Git Branching - Branching Workflows und Distributed Git - Contributing to a Project.
Es gibt auch einen bekannten Artikel Ein erfolgreiches Git-Branching-Modell von Vincent Driessen. Er empfiehlt eine Reihe sehr detaillierter Regeln für die Erstellung und Verwaltung von Mainline-, Topic- und Bugfix-Branches. Um das Modell zu unterstützen, implementierte der Autor die git flow-Erweiterung.
Erweiterte Konfiguration
Zeilenenden
Git verfügt über integrierte Mechanismen zur Behandlung von Zeilenenden zwischen Plattformen mit unterschiedlichen End-of-Line-Stilen. Um Git die CRLF-Konvertierung zu ermöglichen, weisen Sie Dateien, die .gitattributes verwenden, das Attribut text zu. Für Dateien, die bestimmte Zeilenenden haben müssen, weisen Sie das Attribut eol zu. Für Binärdateien ist das Attribut natürlich binary.
Zum Beispiel:
$ cat .gitattributes
*.py text
*.txt text
*.png binary
/readme.txt eol=CRLF
Um zu überprüfen, welche Attribute Git für Dateien verwendet, verwenden Sie den Befehl git check-attr. Zum Beispiel:
$ git check-attr -a -- \*.py
Nützliche Hilfsmittel
GitAlias (Repository) ist eine große Sammlung von Aliases. Eine sorgfältige Auswahl von Aliases für häufig verwendete Befehle könnte Ihnen viele Tastatureingaben ersparen!
GitIgnore und https://github.com/github/gitignore sind Sammlungen von .gitignore-Dateien für alle Arten von IDEs und Programmiersprachen. Python inbegriffen!
pre-commit (Repositories) ist ein Framework zur Verwaltung und Wartung von Multi-Language Pre-Commit-Hooks. Das Framework ist in Python geschrieben und verfügt über viele Plugins für viele Programmiersprachen.
Fortgeschrittene Themen
Staging-Bereich
Der Staging-Bereich, auch Index oder Cache genannt, ist ein Unterscheidungsmerkmal von Git. Der Staging-Bereich ist der Ort, an dem Git Patches sammelt, bevor er sie committet. Die Trennung zwischen dem Sammeln von Patches und der Commit-Phase bietet eine sehr nützliche Funktion von Git: Sie können die gesammelten Patches vor dem Commit überprüfen und sogar bearbeiten - einige Hunks entfernen, neue Hunks hinzufügen und erneut überprüfen.
Um Dateien zum Index hinzuzufügen, verwenden Sie git add. Das Sammeln von Patches vor dem Committen bedeutet, dass Sie dies für jede Änderung tun müssen, nicht nur zum Hinzufügen neuer (nicht getrackter) Dateien. Um das Committen zu vereinfachen, wenn Sie einfach alles ohne Überprüfung committen möchten, führen Sie git commit --all (oder einfach -a) aus - der Befehl fügt jede geänderte getrackte Datei zum Index hinzu und committet dann. Um eine oder mehrere Dateien unabhängig von den im Index gesammelten Patches zu committen, führen Sie git commit [--only|-o] -- $DATEI... aus.
Um Hunks von Patches zum Index hinzuzufügen, verwenden Sie git add --patch (oder einfach -p). Um gesammelte Dateien aus dem Index zu entfernen, verwenden Sie git reset HEAD -- $DATEI.... Um gesammelte Hunks hinzuzufügen/zu inspizieren/zu entfernen, verwenden Sie git add --interactive (-i).
Um die Unterschiede zwischen dem Index und dem letzten Commit (d.h. gesammelte Patches) anzuzeigen, verwenden Sie git diff --cached. Um die Unterschiede zwischen dem Arbeitsbaum und dem Index (d.h. gesammelte Patches) anzuzeigen, verwenden Sie einfach git diff. Um die Unterschiede zwischen dem Arbeitsbaum und dem letzten Commit (d.h. sowohl gesammelte als auch ungesammelte Patches) anzuzeigen, führen Sie git diff HEAD aus.
Siehe WhatIsTheIndex und IndexCommandQuickref im Git Wiki.
Root
Git wechselt in das Stammverzeichnis (oberstes Verzeichnis des Projekts, in dem sich das Unterverzeichnis .git befindet), bevor es einen Befehl ausführt. Git merkt sich jedoch das Verzeichnis, das vor dem Wechsel aktuell war. Einige Programme berücksichtigen das aktuelle Verzeichnis. Z.B. zeigt git status Dateipfade von geänderten und unbekannten Dateien relativ zum aktuellen Verzeichnis an; git grep sucht unter dem aktuellen Verzeichnis; git apply wendet nur die Hunks aus dem Patch an, die Dateien unter dem aktuellen Verzeichnis berühren.
Aber die meisten Befehle werden vom Stammverzeichnis aus ausgeführt und ignorieren das aktuelle Verzeichnis. Stellen Sie sich zum Beispiel vor, Sie haben zwei Arbeitsbäume, einen für den Branch v1 und den anderen für master. Wenn Sie v1 aus einem Unterverzeichnis innerhalb des zweiten Arbeitsbaums mergen möchten, müssen Sie Befehle so schreiben, als wären Sie im obersten Verzeichnis. Nehmen wir zum Beispiel zwei Arbeitsbäume, projekt-v1 und projekt:
$ cd project/subdirectory
$ git fetch ../project-v1 v1:v1
$ git merge v1
Beachten Sie den Pfad in git fetch ../project-v1 v1:v1: ../project-v1 und nicht ../../project-v1, obwohl wir die Befehle von einem Unterverzeichnis aus ausführen und nicht vom Stammverzeichnis aus.
ReReRe
Rerere ist ein Mechanismus, der hilft, wiederkehrende Merge-Konflikte zu lösen. Die häufigste Ursache für wiederkehrende Merge-Konflikte sind Topic-Branches, die in den Mainline gemerged und dann die Merge-Commits entfernt werden; dies wird oft durchgeführt, um die Topic-Branches zu testen und Rerere zu trainieren; Merge-Commits werden entfernt, um eine saubere lineare Historie zu haben und den Topic-Branch mit nur einem letzten Merge-Commit abzuschließen.
Rerere funktioniert, indem es die Zustände des Baums vor und nach einem erfolgreichen Commit speichert. Auf diese Weise kann Rerere Konflikte automatisch lösen, wenn sie in denselben Dateien auftreten.
Rerere kann manuell mit dem Befehl git rerere verwendet werden, wird aber meistens automatisch eingesetzt. Aktivieren Sie rerere mit diesen Befehlen in einem Arbeitsverzeichnis.
$ git config rerere.enabled true
$ git config rerere.autoupdate true
Sie müssen rerere nicht global aktivieren – Sie wollen rerere nicht in Bare-Repositories oder Single-Branch-Repositories; Sie benötigen rerere nur in Repositories, in denen Sie häufig Merges durchführen und Merge-Konflikte lösen.
Siehe Rerere im Buch.
Datenbankwartung
Die Git-Objektdatenbank und andere Dateien/Verzeichnisse unter .git erfordern periodische Wartung und Bereinigung. Beispielsweise hinterlässt das Bearbeiten von Commits unreferenzierte Objekte (im Git-Jargon „dangling objects“) und diese Objekte sollten bereinigt werden, um die Ansammlung von unnötigem Ballast in der Datenbank zu vermeiden. Der Befehl git gc wird für die Wartung verwendet. Git führt automatisch git gc --auto als Teil einiger Befehle aus, um schnelle Wartungsarbeiten durchzuführen. Benutzern wird empfohlen, von Zeit zu Zeit git gc --aggressive auszuführen; git help gc empfiehlt, es alle paar hundert Commits auszuführen; für intensivere Projekte sollte dies etwa einmal pro Woche erfolgen und für weniger aktive Projekte seltener (zweiwöchentlich oder monatlich).
git gc --aggressive entfernt nicht nur unreferenzierte Objekte, sondern packt auch die Objektdatenbank in indizierte und besser optimierte Packs; es packt auch symbolische Referenzen (Branches und Tags). Eine andere Möglichkeit, dies zu tun, ist die Ausführung von git repack.
Es gibt eine bekannte Nachricht von Linus Torvalds bezüglich der „Dummheit“ von git gc --aggressive. Die Nachricht kann jetzt sicher ignoriert werden. Sie ist alt und veraltet, git gc --aggressive ist seitdem viel besser geworden.
Für diejenigen, die git repack immer noch git gc --aggressive vorziehen, sind die empfohlenen Parameter git repack -a -d -f --depth=20 --window=250. Lesen Sie dieses detaillierte Experiment für eine Erklärung der Auswirkungen dieser Parameter.
Führen Sie von Zeit zu Zeit git fsck [--strict] aus, um die Integrität der Datenbank zu überprüfen. git fsck kann eine Liste von unreferenzierten Objekten ausgeben; das ist kein Fehler, sondern nur eine Erinnerung daran, regelmäßige Wartung durchzuführen.
Tipps und Tricks
Kommandozeilenoptionen und Argumente
git help cli empfiehlt, kurze Optionen/Flags nicht zu kombinieren. Meistens funktioniert die Kombination: git commit -av funktioniert einwandfrei, aber es gibt Situationen, in denen dies nicht der Fall ist. Z.B. kann git log -p -5 nicht als git log -p5 kombiniert werden.
Einige Optionen haben Argumente, einige sogar Standardargumente. In diesem Fall muss das Argument für eine solche Option zusammenhängend angegeben werden: -Oarg, niemals -O arg, denn für eine Option mit einem Standardargument bedeutet letzteres „Standardwert für Option -O verwenden und arg weiter an den Optionsparser übergeben“. Zum Beispiel hat git grep die Option -O, die eine Liste von Namen der gefundenen Dateien an ein Programm übergibt; das Standardprogramm für -O ist ein Pager (normalerweise less), aber Sie können Ihren Editor verwenden.
$ git grep -Ovim # but not -O vim
Übrigens, wenn Git angewiesen wird, less als Pager zu verwenden (d.h. wenn der Pager in Git gar nicht konfiguriert ist, verwendet es standardmäßig less, oder wenn es less über die Umgebungsvariablen GIT_PAGER oder PAGER erhält, oder wenn es mit git config [--global] core.pager less konfiguriert wurde, oder wenn less im Befehl git grep -Oless verwendet wird), übergibt git grep die Option +/$pattern an less, was sehr praktisch ist. Leider übergibt git grep das Muster nicht, wenn der Pager nicht exakt less ist, selbst wenn es less mit Parametern ist (etwas wie git config [--global] core.pager less -FRSXgimq); glücklicherweise übergibt git grep -Oless immer das Muster.
bash/zsh-Vervollständigung
Es ist etwas mühsam, git rebase --interactive --preserve-merges HEAD~5 manuell einzugeben, selbst für diejenigen, die gerne die Kommandozeile benutzen, und hier leistet die Shell-Vervollständigung große Hilfe. Bash/zsh verfügen über programmierbare Vervollständigung, die oft automatisch installiert und aktiviert wird. Wenn Sie also Bash/zsh und Git installiert haben, sind Sie wahrscheinlich bereits fertig – gehen Sie einfach hin und verwenden Sie es auf der Kommandozeile.
Wenn Sie die erforderlichen Komponenten nicht installiert haben, installieren und aktivieren Sie das Paket bash_completion. Wenn Sie Ihre Git-Vervollständigung auf die neueste und beste Version aktualisieren möchten, laden Sie die erforderliche Datei von git contrib herunter.
Git-for-windows wird mit git-bash geliefert, für das bash completion installiert und aktiviert ist.
bash/zsh-Prompt
Für Liebhaber der Kommandozeile kann die Shell-Eingabeaufforderung viele nützliche Informationen enthalten. Um Git-Informationen in die Eingabeaufforderung aufzunehmen, verwenden Sie git-prompt.sh. Lesen Sie die detaillierten Anweisungen in der Datei.
Suchen Sie im Internet nach „git prompt“, um andere Eingabeaufforderungsvarianten zu finden.
SSH-Verbindungsfreigabe
SSH-Verbindungsfreigabe ist eine Funktion von OpenSSH und vielleicht Ablegern wie PuTTY. SSH-Verbindungsfreigabe ist eine Methode, um die Startzeit des SSH-Clients zu verkürzen, indem eine Verbindung aufgebaut und diese für alle nachfolgenden Clients wiederverwendet wird, die sich mit demselben Server verbinden. SSH-Verbindungsfreigabe kann verwendet werden, um viele kurze SSH-Sitzungen wie scp, sftp, rsync und natürlich Git über SSH zu beschleunigen. Wenn Sie regelmäßig von/zu Remote-Repositories fetchen/pullen/pushen, die über SSH zugänglich sind, wird die Verwendung der SSH-Verbindungsfreigabe empfohlen.
Um die SSH-Verbindungsfreigabe zu aktivieren, fügen Sie etwas wie folgt zu Ihrer ~/.ssh/config hinzu.
Host *
ControlMaster auto
ControlPath ~/.ssh/mux-%r@%h:%p
ControlPersist 600
Siehe OpenSSH wikibook und Suche für weitere Informationen.
SSH-Verbindungsfreigabe kann für GitHub, GitLab und SourceForge-Repositories verwendet werden, aber bitte beachten Sie, dass BitBucket dies nicht erlaubt und die Master-Verbindung nach kurzer Inaktivität zwangsweise schließt, sodass Sie Fehler wie diesen von SSH sehen werden: „Connection to bitbucket.org closed by remote host.“
Git auf dem Server
Die einfachste Methode, ein Repository oder eine Gruppe von Repositories zu veröffentlichen, ist git daemon. Der Daemon bietet anonymen Zugriff, standardmäßig nur Lesezugriff. Die Repositories sind über das Git-Protokoll (git://-URLs) zugänglich. Schreibzugriff kann aktiviert werden, aber das Protokoll verfügt über keine Authentifizierungsmittel, daher sollte es nur innerhalb eines vertrauenswürdigen LANs aktiviert werden. Siehe git help daemon für Details.
Git über SSH bietet Authentifizierung und Repository-weite Autorisierung, da Repositories benutzer- oder gruppenweise beschreibbar gemacht werden können (siehe Parameter core.sharedRepository in git help config). Wenn dies für die Bedürfnisse eines Projekts zu permissiv oder zu restriktiv ist, gibt es eine Wrapper-Lösung namens gitolite, die so konfiguriert werden kann, dass der Zugriff mit hoher Granularität ermöglicht wird; gitolite ist in Perl geschrieben und verfügt über viel Dokumentation.
Webschnittstellen zum Durchsuchen von Repositories können mit gitweb oder cgit erstellt werden. Beide sind CGI-Skripte (geschrieben in Perl bzw. C). Zusätzlich zur Webschnittstelle bieten beide Lesezugriff über HTTP für Git (http(s)://-URLs) im „dumb“-Modus. Klaus ist ein kleiner und einfacher WSGI-Webserver, der sowohl eine Webschnittstelle als auch den Git Smart HTTP-Transport implementiert; unterstützt Python 2 und Python 3, bietet Syntax-Hervorhebung.
Es gibt auch fortgeschrittenere webbasierte Entwicklungsumgebungen, die die Verwaltung von Benutzern, Gruppen und Projekten; private, gruppenzugängliche und öffentliche Repositories umfassen; sie enthalten oft Issue-Tracker, Wiki-Seiten, Pull-Requests und andere Werkzeuge für Entwicklung und Kommunikation. Zu diesen Umgebungen gehören Kallithea und pagure, beide in Python geschrieben; pagure wurde von Fedora-Entwicklern geschrieben und wird zur Entwicklung einiger Fedora-Projekte verwendet. GitPrep ist ein weiterer GitHub-Klon, geschrieben in Perl. Gogs ist in Go geschrieben. GitBucket ist in Scala geschrieben.
Und nicht zuletzt GitLab. Es ist vielleicht die fortschrittlichste webbasierte Entwicklungsumgebung für Git. In Ruby geschrieben, ist die Community-Edition kostenlos und Open Source (MIT-Lizenz).
Von Mercurial zu Git
Es gibt viele Werkzeuge zur Konvertierung von Mercurial-Repositories in Git. Die berühmtesten sind wahrscheinlich hg-git und fast-export (vor vielen Jahren war es unter dem Namen hg2git bekannt).
Aber ein besseres Werkzeug, vielleicht das beste, ist git-remote-hg. Es bietet transparenten bidirektionalen (Pull und Push) Zugriff auf Mercurial-Repositories von Git aus. Sein Autor hat einen Vergleich von Alternativen geschrieben, der weitgehend objektiv zu sein scheint.
Um git-remote-hg zu verwenden, installieren oder klonen Sie es, fügen Sie es zu Ihrem PATH hinzu (oder kopieren Sie das Skript git-remote-hg in ein Verzeichnis, das bereits im PATH ist) und stellen Sie hg:: voran, wenn Sie Mercurial-URLs verwenden. Zum Beispiel.
$ git clone https://github.com/felipec/git-remote-hg.git
$ PATH=$PATH:"`pwd`"/git-remote-hg
$ git clone hg::https://hg.python.org/peps/ PEPs
Um mit dem Repository zu arbeiten, verwenden Sie einfach normale Git-Befehle, einschließlich git fetch/pull/push.
Um Ihre Mercurial-Gewohnheiten in Git zu konvertieren, siehe die Seite Mercurial for Git users im Mercurial-Wiki. In der zweiten Hälfte der Seite gibt es eine Tabelle mit entsprechenden Mercurial- und Git-Befehlen. Dies sollte in beide Richtungen perfekt funktionieren.
Das Python Developer’s Guide hat auch ein Kapitel Mercurial for git developers, das einige Unterschiede zwischen Git und Hg dokumentiert.
Git und GitHub
gitsome – Git/GitHub Kommandozeilen-Interface (CLI). Geschrieben in Python, funktioniert auf MacOS, Unix, Windows. Git/GitHub CLI mit Autovervollständigung, enthält viele integrierte GitHub-Befehle, die mit allen Shells funktionieren, eingebautes xonsh mit Python REPL zum Ausführen von Python-Befehlen neben Shell-Befehlen, Befehlshistorie, anpassbare Hervorhebung, gründlich dokumentiert.
Urheberrecht
Dieses Dokument wurde gemeinfrei erklärt.
Quelle: https://github.com/python/peps/blob/main/peps/pep-0103.rst
Zuletzt geändert: 2024-04-14 20:08:31 GMT