Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python Enhancement Proposals

PEP 374 – Wahl eines verteilten VCS für das Python-Projekt

Autor:
Brett Cannon <brett at python.org>, Stephen J. Turnbull <stephen at xemacs.org>, Alexandre Vassalotti <alexandre at peadrop.com>, Barry Warsaw <barry at python.org>, Dirkjan Ochtman <dirkjan at ochtman.nl>
Status:
Final
Typ:
Prozess
Erstellt:
07-Nov-2008
Post-History:
07-Nov-2008, 22-Jan-2009

Inhaltsverzeichnis

Begründung

Python verwendet seit Jahren ein zentralisiertes Versionskontrollsystem (VCS; zuerst CVS, jetzt Subversion) mit großem Erfolg. Eine Master-Kopie der offiziellen Python-Version zu haben, bietet den Leuten einen einzigen Ort, an dem sie immer den offiziellen Python-Quellcode erhalten können. Sie hat auch die Speicherung der Historie der Sprache ermöglicht, hauptsächlich zur Unterstützung der Entwicklung, aber auch für die Nachwelt. Und natürlich ist das V in VCS bei der Entwicklung sehr hilfreich.

Aber ein zentralisiertes Versionskontrollsystem hat seine Nachteile. In erster Linie muss man ein „Core-Entwickler“ (d.h. jemand mit Commit-Berechtigungen für die Master-Kopie von Python) sein, um die Vorteile der Versionskontrolle nahtlos mit Python nutzen zu können. Personen, die keine Core-Entwickler sind, aber mit dem Revisionsbaum von Python arbeiten möchten, z.B. jeder, der einen Patch für Python schreibt oder eine benutzerdefinierte Version erstellt, haben keine direkte Tool-Unterstützung für Revisionen. Dies kann eine erhebliche Einschränkung sein, da diese Nicht-Core-Entwickler keine einfachen grundlegenden Aufgaben ausführen können, wie z.B. Änderungen auf einen zuvor gespeicherten Zustand zurückzusetzen, Branches zu erstellen, eigene Änderungen mit vollständiger Revisionshistorie zu veröffentlichen usw. Für Nicht-Core-Entwickler ist der letzte sichere Baumzustand einer, den die Python-Entwickler zufällig festlegen, und dies verhindert eine sichere Entwicklung. Diese Bürger zweiter Klasse sind ein Hindernis für Personen, die zu Python beitragen möchten, mit einem Patch jeder Komplexität und die eine Möglichkeit suchen, ihre Fortschritte inkrementell zu speichern, um ihr Entwicklerleben zu erleichtern.

Es gibt auch das Problem, dass man online sein muss, um seine Arbeit committen zu können. Da zentrale VCSs eine zentrale Kopie speichern, die alle Revisionen enthält, muss man Internetzugang haben, damit die eigenen Revisionen gespeichert werden können; kein Netz, kein Commit. Das kann ärgerlich sein, wenn man reist und kein Internet hat. Es gibt auch die Situation, dass jemand zu Python beitragen möchte, aber eine schlechte Internetverbindung hat, bei der das Committen zeitaufwendig und teuer ist und es besser sein könnte, dies in einem einzigen Schritt zu tun.

Ein weiterer Nachteil eines zentralen VCS ist, dass ein häufiger Anwendungsfall darin besteht, dass ein Entwickler Patches als Reaktion auf Überprüfungs-Kommentare überarbeitet. Dies ist im zentralen Modell schwieriger, da es keinen Ort gibt, um Zwischenarbeiten aufzubewahren. Es wird entweder alles committet oder nichts davon. Im zentralen VCS ist es auch sehr schwierig, Änderungen am Trunk zu verfolgen, während sie committet werden, während man an seinem Feature- oder Bugfix-Branch arbeitet. Dies erhöht das Risiko, dass solche Branches veralten, veraltet werden oder dass das Zusammenführen in den Trunk zu viele Konflikte erzeugt, die leicht gelöst werden können.

Zuletzt gibt es die Frage der Wartung von Python. Zu jedem Zeitpunkt ist mindestens eine Hauptversion von Python in Entwicklung (zum Zeitpunkt des Schreibens sind es zwei). Für jede Hauptversion von Python, die sich in Entwicklung befindet, gibt es mindestens die Wartungsversion der letzten Nebenversion und die in Entwicklung befindliche Nebenversion (z.B. da gerade 2.6 veröffentlicht wurde, bedeutet dies, dass sowohl an 2.6 als auch an 2.7 gearbeitet wird). Sobald eine Veröffentlichung abgeschlossen ist, wird ein Branch zwischen den Codebasen erstellt, wobei Änderungen in einer Version nicht (aber könnten) in der anderen Version gehören. Derzeit gibt es in zentralen VCSs keine natürliche Unterstützung für diesen Branch in der Zeit; man muss Tools verwenden, die das Branching simulieren. Das Nachverfolgen von Merges ist für Entwickler ähnlich mühsam, da Revisionen oft zwischen vier aktiven Branches zusammengeführt werden müssen (z.B. 2.6 Wartung, 3.0 Wartung, 2.7 Entwicklung, 3.1 Entwicklung). In diesem Fall unterstützen VCSs wie Subversion dies nur über obskure Drittanbieter-Tools.

Verteilte VCSs (DVCSs) lösen all diese Probleme. Während man eine Master-Kopie eines Revisionsbaums behalten kann, kann jeder diesen Baum für seine eigene Nutzung kopieren. Dies gibt jedem die Möglichkeit, Änderungen in seiner Kopie online oder offline zu committen. Es passt auch natürlicher in die Idee des Branching in der Historie eines Revisionsbaums für Wartung und die Entwicklung neuer Funktionen für Python. DVCSs bieten auch eine Vielzahl zusätzlicher Funktionen, die zentrale VCSs nicht oder nicht bieten können.

Dieses PEP untersucht die Möglichkeit, die Nutzung von Subversion durch Python auf eines der derzeit beliebten DVCSs umzustellen, um die oben genannten Vorteile zu nutzen. Dieses PEP garantiert nicht, dass am Ende dieses PEPs ein Wechsel zu einem DVCS stattfindet. Es ist durchaus möglich, dass kein klarer Gewinner gefunden wird und svn weiterhin verwendet wird. Wenn dies geschieht, wird dieses PEP zu einem späteren Zeitpunkt überarbeitet und aktualisiert, sobald sich der Zustand von DVCSs weiterentwickelt.

Terminologie

Es ist überraschend schwierig, sich auf eine gemeinsame Terminologie zu einigen, hauptsächlich weil jedes VCS diese Begriffe verwendet, um subtil unterschiedliche Aufgaben, Objekte und Konzepte zu beschreiben. Wo möglich, versuchen wir, eine generische Definition der Konzepte zu geben, aber Sie sollten die Glossare des jeweiligen Systems für Details konsultieren. Hier sind einige grundlegende Referenzen für die Terminologie aus einigen der Standard-Webreferenzen zu jedem VCS. Sie können sich auch auf Glossare für jedes DVCS beziehen.

branch
Eine Entwicklungslinie; eine Sammlung von Revisionen, geordnet nach Zeit.
checkout/working copy/working tree
Ein Codebaum, den der Entwickler bearbeiten kann, verknüpft mit einem Branch.
index
Ein „Staging-Bereich“, in dem eine Revision erstellt wird (nur bei git).
repository
Eine Sammlung von Revisionen, die in Branches organisiert sind.
clone
Eine vollständige Kopie eines Branches oder Repositorys.
commit
Eine Revision in einem Repository aufzeichnen.
merge
Anwenden aller Änderungen und der Historie von einem Branch/Repository auf ein anderes.
pull
Einen Checkout/Clone vom ursprünglichen Branch/Repository aktualisieren, das remote oder lokal sein kann.
push/publish
Eine Revision und alle Revisionen, von denen sie abhängt, von einem Repository auf ein anderes kopieren.
cherry-pick
Eine oder mehrere spezifische Revisionen von einem Branch auf einen anderen zusammenführen, möglicherweise in einem anderen Repository, möglicherweise ohne deren abhängige Revisionen.
rebase
Einen Branch „ablösen“ und ihn auf einen neuen Branchpunkt verschieben; Commits am Anfang eines Branches statt dort platzieren, wo sie zeitlich aufgetreten sind.

Typischer Workflow

Im Moment ist der typische Workflow für einen Python-Core-Entwickler:

  • Code in einem Checkout bearbeiten, bis er stabil genug ist, um committet/gepusht zu werden.
  • In das Master-Repository committen.

Dies ist ein eher einfacher Workflow, aber er hat Nachteile. Erstens, da alle Arbeiten, die das Repository betreffen, aufgrund des Netzwerks Zeit benötigen, sind Commits/Pushes tendenziell nicht unbedingt so atomar wie möglich. Es gibt auch den Nachteil, dass es keine notwendigerweise kostengünstige Möglichkeit gibt, neue Checkouts zu erstellen, abgesehen von einer rekursiven Kopie des Checkout-Verzeichnisses.

Ein DVCS würde zu einem Workflow führen, der eher diesem ähnelt:

  • Von einem lokalen Clone des Master-Repositorys abzweigen.
  • Code bearbeiten und in atomaren Teilen committen.
  • Den Branch in die Hauptlinie integrieren und
  • Alle Commits an das Master-Repository pushen.

Obwohl es mehr mögliche Schritte gibt, ist der Workflow unabhängiger vom Master-Repository als derzeit möglich. Durch die Möglichkeit, lokal mit Festplattengeschwindigkeit zu committen, kann ein Core-Entwickler atomare Commits häufiger durchführen, wodurch minimiert wird, dass Commits mehrere Dinge am Code ändern. Auch durch die Verwendung eines Branches werden die Änderungen (falls gewünscht) von anderen von anderen Entwicklern vorgenommenen Änderungen isoliert. Da Branches kostengünstig sind, ist es einfach, viele kleinere Branches zu erstellen und zu pflegen, die ein bestimmtes Problem lösen, z.B. einen Fehler oder eine neue Funktion. Anspruchsvollere Funktionen von DVCSs ermöglichen es dem Entwickler, langfristige Entwicklungsbranches leichter zu verfolgen, während die offizielle Hauptlinie fortschreitet.

Kandidaten

Name Kurzbezeichnung Version 2.x Trunk-Spiegel 3.x Trunk-Spiegel
Bazaar bzr 1.12 http://code.python.org/python/trunk http://code.python.org/python/3.0
Mercurial hg 1.2.0 http://code.python.org/hg/trunk/ http://code.python.org/hg/branches/py3k/
git N/A 1.6.1 git://code.python.org/python/trunk git://code.python.org/python/branches/py3k

Dieses PEP berücksichtigt nicht darcs, arch oder monotone. Das Hauptproblem mit diesen DVCSs ist, dass sie einfach nicht populär genug sind, um die Unterstützung zu rechtfertigen, wenn sie keine sehr überzeugenden Funktionen bieten, die die anderen DVCSs bieten. Arch und darcs haben auch erhebliche Leistungsprobleme, die wahrscheinlich nicht in naher Zukunft behoben werden.

Interoperabilität

Für diejenigen, die sich bereits für bestimmte DVCSs entschieden haben und bereit sind, lokale Spiegel selbst zu warten, unterstützen alle drei DVCSs den Austausch über das git „fast-import“-Änderungssatzformat. git tut dies nativ, natürlich, und die native Unterstützung für Bazaar ist in aktiver Entwicklung und erhält Mitte Februar 2009 gute frühe Rezensionen. Mercurial hat eine idiosynkratische Unterstützung für den Import über seinen Befehl hg convert, und Unterstützung für Drittanbieter-fast-import ist für den Export verfügbar. Außerdem unterstützt das Tool Tailor die automatische Wartung von Spiegeln basierend auf einem offiziellen Repository in einem der Kandidatenformate mit einem lokalen Spiegel in irgendeinem Format.

Nutzungsszenarien

Wahrscheinlich der beste Weg, um zu entscheiden, ob/welches DVCS Subversion ersetzen sollte, ist zu sehen, was erforderlich ist, um einige reale Nutzungsszenarien durchzuführen, mit denen Entwickler (Core und Nicht-Core) arbeiten müssen. Jedes Nutzungsszenario beschreibt, was es ist, eine Aufzählung der grundlegenden Schritte (die je nach VCS leicht variieren können) und wie das Nutzungsszenario in den verschiedenen VCSs (einschließlich Subversion) durchgeführt wird.

Jeder VCS hatte einen einzelnen Autor, der für die Implementierung jedes Szenarios verantwortlich war (sofern nicht anders angegeben).

Name VCS
Brett svn
Barry bzr
Alexandre hg
Stephen git

Ersteinrichtung

Einige DVCSs haben einige Vorteile, wenn Sie im Voraus eine Ersteinrichtung vornehmen. Dieser Abschnitt behandelt, was getan werden kann, bevor irgendwelche Nutzungsszenarien ausgeführt werden, um die Werkzeuge besser nutzen zu können.

Alle DVCSs unterstützen die Konfiguration Ihrer Projektidentifikation. Im Gegensatz zu zentralen Systemen verwenden sie Ihre E-Mail-Adresse zur Identifizierung Ihrer Commits. (Zugriffskontrolle wird im Allgemeinen durch Mechanismen außerhalb des DVCS durchgeführt, wie z.B. ssh oder Konsolenanmeldung). Diese Identität kann mit einem vollständigen Namen verknüpft sein.

Alle DVCSs werden das System abfragen, um eine Annäherung an diese Informationen zu erhalten, aber das ist vielleicht nicht das, was Sie wollen. Sie unterstützen auch die Einstellung dieser Informationen auf pro-Benutzer-Basis und auf pro-Projekt-Basis. Die Komfortbefehle zum Setzen dieser Attribute variieren, aber alle erlauben die direkte Bearbeitung von Konfigurationsdateien.

Einige VCSs unterstützen die Konvertierung von Zeilenenden (EOL) beim Auschecken/Einchecken.

svn

Keine erforderlich, aber es wird empfohlen, die Richtlinien im Dev FAQ zu befolgen.

bzr

Es ist keine Einrichtung erforderlich, aber für wesentlich schnellere und speichereffizientere lokale Branches sollten Sie ein gemeinsam genutztes Repository erstellen, um alle Ihre Python-Branches zu speichern. Ein gemeinsam genutztes Repository ist eigentlich nur ein übergeordnetes Verzeichnis, das ein .bzr-Verzeichnis enthält. Wenn bzr eine Revision committet, sucht es vom lokalen Verzeichnis aufwärts im Dateisystem nach einem .bzr-Verzeichnis, um die Revision zu speichern. Durch das Teilen von Revisionen über mehrere Branches hinweg reduzieren Sie den verwendeten Speicherplatz. Tun Sie dies

cd ~/projects
bzr init-repo python
cd python

Nun sollten alle Ihre Python-Branches innerhalb von ~/projects/python erstellt werden.

Es gibt auch einige Einstellungen, die Sie in Ihre Datei ~/.bzr/bazaar.conf und ~/.bzr/locations.conf einfügen können, um Standardeinstellungen für die Interaktion mit Python-Code festzulegen. Keine davon ist erforderlich, obwohl einige empfohlen werden. Z.B. würde ich empfehlen, alle Commits mit GPG zu signieren, aber das könnte für Entwickler eine zu hohe Hürde sein. Außerdem können Sie standardmäßige Push-Speicherorte festlegen, je nachdem, wohin Sie Branches standardmäßig pushen möchten. Wenn Sie Schreibzugriff auf die Master-Branches haben, könnte dieser Push-Speicherort code.python.org sein. Andernfalls könnte es ein kostenloser Bazaar-Code-Hosting-Dienst wie Launchpad sein. Wenn Bazaar gewählt wird, sollten wir die Richtlinien und Empfehlungen festlegen.

Mindestens sollten Sie Ihre E-Mail-Adresse einrichten

bzr whoami "Firstname Lastname <email.address@example.com>"

Wie bei hg und git unten gibt es Möglichkeiten, Ihre E-Mail-Adresse (oder eigentlich fast jeden Parameter) pro Repository einzustellen. Dies geschieht mit Einstellungen in Ihrer Datei $HOME/.bazaar/locations.conf, die wie die anderen DVCSs ein Ini-ähnliches Format hat. Weitere Einzelheiten finden Sie in der Bazaar-Dokumentation, die für diese Diskussion meist nicht relevant sind.

hg

Minimal sollten Sie Ihren Benutzernamen festlegen. Tun Sie dies, indem Sie die Datei .hgrc in Ihrem Home-Verzeichnis erstellen und Folgendes hinzufügen:

[ui]
username = Firstname Lastname <email.address@example.com>

Wenn Sie Windows verwenden und Ihre Tools keine Unix-Style-Newlines unterstützen, können Sie die automatische Zeilenende-Übersetzung aktivieren, indem Sie Ihrer Konfiguration Folgendes hinzufügen:

[extensions]
win32text =

Diese Optionen können auch lokal für ein bestimmtes Repository eingestellt werden, indem <repo>/.hg/hgrc anstelle von ~/.hgrc angepasst wird.

git

Keine erforderlich. Git unterstützt jedoch eine Reihe von Funktionen, die Ihre Arbeit mit ein wenig Vorbereitung erleichtern können. Git unterstützt das Setzen von Standardwerten auf Arbeitsbereichs-, Benutzer- und Systemebene. Die Systemebene liegt außerhalb des Umfangs dieses PEP. Die Benutzerkonfigurationsdatei ist $HOME/.gitconfig auf Unix-ähnlichen Systemen, und die Arbeitsbereichskonfigurationsdatei ist $REPOSITORY/.git/config.

Sie können das Tool git-config verwenden, um Präferenzen für user.name und user.email entweder global (für Ihr System-Login-Konto) oder lokal (für einen bestimmten Git-Arbeitsbereich) festzulegen, oder Sie können die Konfigurationsdateien bearbeiten (die das gleiche Format haben wie im Mercurial-Abschnitt oben gezeigt).

# my full name doesn't change
# note "--global" flag means per user
# (system-wide configuration is set with "--system")
git config --global user.name 'Firstname Lastname'
# but use my Pythonic email address
cd /path/to/python/repository
git config user.email email.address@python.example.com

Wenn Sie Windows verwenden, möchten Sie wahrscheinlich die Präferenzen core.autocrlf und core.safecrlf mit git-config auf true setzen.

# check out files with CRLF line endings rather than Unix-style LF only
git config --global core.autocrlf true
# scream if a transformation would be ambiguous
# (eg, a working file contains both naked LF and CRLF)
# and check them back in with the reverse transformation
git config --global core.safecrlf true

Obwohl das Repository normalerweise eine .gitignore-Datei enthält, die Dateinamen angibt, die selten oder nie im VCS registriert werden sollten, haben Sie möglicherweise persönliche Konventionen (z.B. immer Log-Nachrichten in einer temporären Datei namens „.msg“ bearbeiten), die Sie angeben möchten.

# tell git where my personal ignores are
git config --global core.excludesfile ~/.gitignore
# I use .msg for my long commit logs, and Emacs makes backups in
# files ending with ~
# these are globs, not regular expressions
echo '*~' >> ~/.gitignore
echo '.msg' >> ~/.gitignore

Wenn Sie mehrere Branches verwenden, können Sie wie bei den anderen VCSs viel Speicherplatz sparen, indem Sie alle Objekte in einem gemeinsamen Objektspeicher speichern. Dies kann auch die Downloadzeit sparen, wenn die Ursprünge der Branches in verschiedenen Repositories lagen, da Objekte über Branches in Ihrem Repository geteilt werden, auch wenn sie in den Upstream-Repositories nicht vorhanden waren. Git ist sehr speicher- und zeiteffizient und wendet eine Reihe von Optimierungen automatisch an, sodass diese Konfiguration optional ist. (Beispiele sind weggelassen.)

Einmaliger Checkout

Als Nicht-Core-Entwickler möchte ich einen einmaligen Patch erstellen und veröffentlichen, der einen Fehler behebt, damit ein Core-Entwickler ihn zur Aufnahme in die Hauptlinie überprüfen kann.

  • Trunk auschecken/branchen/clonen.
  • Einige Codezeilen bearbeiten.
  • Einen Patch generieren (basierend darauf, was vom VCS am besten unterstützt wird, z.B. Branch-Historie).
  • Reviewer-Kommentare erhalten und Probleme beheben.
  • Einen zweiten Patch für den Core-Entwickler erstellen, der ihn committen kann.

svn

svn checkout http://svn.python.org/projects/python/trunk
cd trunk
# Edit some code.
echo "The cake is a lie!" > README
# Since svn lacks support for local commits, we fake it with patches.
svn diff >> commit-1.diff
svn diff >> patch-1.diff
# Upload the patch-1 to bugs.python.org.
# Receive reviewer comments.
# Edit some code.
echo "The cake is real!" > README
# Since svn lacks support for local commits, we fake it with patches.
svn diff >> commit-2.diff
svn diff >> patch-2.diff
# Upload patch-2 to bugs.python.org

bzr

bzr branch http://code.python.org/python/trunk
cd trunk
# Edit some code.
bzr commit -m 'Stuff I did'
bzr send -o bundle
# Upload bundle to bugs.python.org
# Receive reviewer comments
# Edit some code
bzr commit -m 'Respond to reviewer comments'
bzr send -o bundle
# Upload updated bundle to bugs.python.org

Die Datei bundle ist wie ein Super-Patch. Sie kann von patch(1) gelesen werden, enthält aber zusätzliche Metadaten, sodass sie an bzr merge übergeben werden kann, um einen vollständig nutzbaren Branch zu erzeugen, komplett mit Historie. Siehe unten im Abschnitt Patch-Überprüfung.

hg

hg clone http://code.python.org/hg/trunk
cd trunk
# Edit some code.
hg commit -m "Stuff I did"
hg outgoing -p > fixes.patch
# Upload patch to bugs.python.org
# Receive reviewer comments
# Edit some code
hg commit -m "Address reviewer comments."
hg outgoing -p > additional-fixes.patch
# Upload patch to bugs.python.org

Obwohl hg outgoing keine entsprechende Option hat, unterstützen die meisten Mercurial-Befehle das erweiterte Patch-Format von git über einen Befehl mit der Option --git. Dies kann in der Datei .hgrc eingestellt werden, sodass alle Befehle, die einen Patch generieren, das erweiterte Format verwenden.

git

Die Patches könnten auch mit git diff master > stuff-i-did.patch erstellt werden, aber git format-patch | git am kennt einige Tricks (leere Dateien, Umbenennungen usw.), die ein normaler Patch nicht verarbeiten kann. Git greift „Stuff I did“ aus der Commit-Nachricht auf, um den Dateinamen 0001-Stuff-I-did.patch zu erstellen. Siehe unten Patch Review für eine Beschreibung des git-format-patch-Formats.

# Get the mainline code.
git clone git://code.python.org/python/trunk
cd trunk
# Edit some code.
git commit -a -m 'Stuff I did.'
# Create patch for my changes (i.e, relative to master).
git format-patch master
git tag stuff-v1
# Upload 0001-Stuff-I-did.patch to bugs.python.org.
# Time passes ... receive reviewer comments.
# Edit more code.
git commit -a -m 'Address reviewer comments.'
# Make an add-on patch to apply on top of the original.
git format-patch stuff-v1
# Upload 0001-Address-reviewer-comments.patch to bugs.python.org.

Änderungen rückgängig machen

Als Core-Entwickler möchte ich eine Änderung rückgängig machen, die noch nicht für die Aufnahme in die Hauptlinie bereit war.

  • Die unerwünschte Änderung rückgängig machen.
  • Patch an den Server pushen.

svn

# Assume the change to revert is in revision 40
svn merge -c -40 .
# Resolve conflicts, if any.
svn commit -m "Reverted revision 40"

bzr

# Assume the change to revert is in revision 40
bzr merge -r 40..39
# Resolve conflicts, if any.
bzr commit -m "Reverted revision 40"

Beachten Sie, dass Sie, wenn die rückgängig zu machende Änderung die letzte war, einfach bzr uncommit verwenden können.

hg

# Assume the change to revert is in revision 9150dd9c6d30
hg backout --merge -r 9150dd9c6d30
# Resolve conflicts, if any.
hg commit -m "Reverted changeset 9150dd9c6d30"
hg push

Beachten Sie, dass Sie „hg rollback“ und „hg strip“ verwenden können, um Änderungen rückgängig zu machen, die Sie in Ihrem lokalen Repository committet, aber noch nicht an andere Repositories gepusht haben.

git

# Assume the change to revert is the grandfather of a revision tagged "newhotness".
git revert newhotness~2
# Resolve conflicts if any.  If there are no conflicts, the commit
# will be done automatically by "git revert", which prompts for a log.
git commit -m "Reverted changeset 9150dd9c6d30."
git push

Patch-Überprüfung

Als Core-Entwickler möchte ich Patches von anderen Personen überprüfen, damit ich sicherstellen kann, dass nur genehmigte Änderungen zu Python hinzugefügt werden.

Core-Entwickler müssen Patches von anderen Personen überprüfen. Dies erfordert das Anwenden des Patches, das Testen und dann das Verwerfen der Änderungen. Es kann davon ausgegangen werden, dass ein Core-Entwickler bereits einen Checkout/Branch/Clone des Trunks hat.

  • Vom Trunk abzweigen.
  • Patch ohne Kommentare anwenden, wie vom Patch-Einreicher generiert.
  • Patch an den Server pushen.
  • Nun nutzlosen Branch löschen.

svn

Subversion passt nicht sehr gut zu diesem Entwicklungsstil, da es keine „Branches“ gibt, wie sie in diesem PEP definiert sind. Stattdessen muss ein Entwickler entweder einen weiteren Checkout zum Testen eines Patches erstellen oder einen Branch auf dem Server erstellen. Bis zu diesem Zeitpunkt haben Core-Entwickler nicht den Ansatz „Branch auf dem Server“ zur Bearbeitung einzelner Patches verfolgt. Für dieses Szenario wird davon ausgegangen, dass der Entwickler einen lokalen Checkout des Trunks erstellt, um damit zu arbeiten.

cp -r trunk issue0000
cd issue0000
patch -p0 < __patch__
# Review patch.
svn commit -m "Some patch."
cd ..
rm -r issue0000

Eine andere Möglichkeit ist, nur zu einem Zeitpunkt einen einzigen Checkout laufen zu lassen und svn diff zusammen mit svn revert -R zu verwenden, um unabhängige Änderungen, die Sie möglicherweise vorgenommen haben, zu speichern.

bzr

bzr branch trunk issueNNNN
# Download `patch` bundle from Roundup
bzr merge patch
# Review patch
bzr commit -m'Patch NNN by So N. So' --fixes python:NNNN
bzr push bzr+ssh://me@code.python.org/trunk
rm -rf ../issueNNNN

Alternativ dazu, da Sie diese Änderungen wahrscheinlich in den Trunk committen werden, könnten Sie einfach einen Checkout durchführen. Dies würde Ihnen einen lokalen Arbeitsbaum verschaffen, während der Branch (d.h. alle Revisionen) auf dem Server verbleibt. Dies ähnelt dem SVN-Modell und ermöglicht Ihnen möglicherweise eine schnellere Überprüfung des Patches. In diesem Fall ist kein Push erforderlich.

bzr checkout trunk issueNNNN
# Download `patch` bundle from Roundup
bzr merge patch
# Review patch
bzr commit -m'Patch NNNN by So N. So' --fixes python:NNNN
rm -rf ../issueNNNN

hg

hg clone trunk issue0000
cd issue0000
# If the patch was generated using hg export, the user name of the
# submitter is automatically recorded. Otherwise,
# use hg import --no-commit submitted.diff and commit with
# hg commit -u "Firstname Lastname <email.address@example.com>"
hg import submitted.diff
# Review patch.
hg push ssh://alexandre@code.python.org/hg/trunk/

git

Wir gehen von einem Patch aus, der mit git-format-patch erstellt wurde. Dies ist eine Unix-mbox-Datei, die einen oder mehrere Patches enthält, die jeweils als RFC 2822-Nachricht formatiert sind. git-am interpretiert jede Nachricht als Commit wie folgt: Der Autor des Patches wird aus dem From:-Header übernommen, das Datum aus dem Date:-Header. Das Commit-Log wird durch Verketten der Betreffzeile, einer Leerzeile und des Nachrichtenkörpers bis zum Beginn des Patches erstellt.

cd trunk
# Create a branch in case we don't like the patch.
# This checkout takes zero time, since the workspace is left in
# the same state as the master branch.
git checkout -b patch-review
# Download patch from bugs.python.org to submitted.patch.
git am < submitted.patch
# Review and approve patch.
# Merge into master and push.
git checkout master
git merge patch-review
git push

Backport

Als Core-Entwickler möchte ich einen Patch auf 2.6, 2.7, 3.0 und 3.1 anwenden, um ein Problem in allen drei Versionen zu beheben.

Aufgrund der ständigen Verfügbarkeit der aktuellsten Version und der neuesten veröffentlichten Version, die sich in Entwicklung befindet, werden bei Python derzeit vier Branches gleichzeitig bearbeitet. Das macht es wichtig, dass sich eine Änderung leicht über verschiedene Branches hinweg verbreitet.

svn

Aufgrund der Verwendung von svnmerge durch Python beginnen Änderungen mit dem Trunk (2.7) und werden dann in die Release-Version 2.6 gemerged. Um die Änderung in die 3.x-Serie zu übernehmen, wird die Änderung in 3.1 gemerged, korrigiert und dann in 3.0 gemerged (2.7 -> 2.6; 2.7 -> 3.1 -> 3.0).

Dies steht im Gegensatz zu einer Port-Forward-Strategie, bei der der Patch auf 2.6 angewendet und dann in neuere Versionen übernommen worden wäre (2.6 -> 2.7 -> 3.0 -> 3.1).

# Assume patch applied to 2.7 in revision 0000.
cd release26-maint
svnmerge merge -r 0000
# Resolve merge conflicts and make sure patch works.
svn commit -F svnmerge-commit-message.txt  # revision 0001.
cd ../py3k
svnmerge merge -r 0000
# Same as for 2.6, except Misc/NEWS changes are reverted.
svn revert Misc/NEWS
svn commit -F svnmerge-commit-message.txt  # revision 0002.
cd ../release30-maint
svnmerge merge -r 0002
svn commit -F svnmerge-commit-message.txt  # revision 0003.

bzr

Bazaar ist hier ziemlich unkompliziert, da es das manuelle Cherry-Picking von Revisionen unterstützt. Im Beispiel unten hätten wir eine Revisions-ID anstelle einer Revisionsnummer angeben können, aber das ist normalerweise nicht notwendig. Martin Pool schlägt vor: „Wir würden generell empfehlen, die Korrektur zuerst im ältesten unterstützten Branch vorzunehmen und sie dann zu den späteren Releases zu mergen.“

# Assume patch applied to 2.7 in revision 0000
cd release26-maint
bzr merge ../trunk -c 0000
# Resolve conflicts and make sure patch works
bzr commit -m 'Back port patch NNNN'
bzr push bzr+ssh://me@code.python.org/trunk
cd ../py3k
bzr merge ../trunk -r 0000
# Same as for 2.6 except Misc/NEWS changes are reverted
bzr revert Misc/NEWS
bzr commit -m 'Forward port patch NNNN'
bzr push bzr+ssh://me@code.python.org/py3k

hg

Mercurial unterstützt, wie andere DVCS, den aktuellen Workflow von Python-Core-Entwicklern zum Backporting von Patches nicht gut. Derzeit werden Bugfixes zuerst in die Entwicklungs-Hauptlinie (d.h. Trunk) angewendet, dann in die Wartungs-Branches zurückportiert und bei Bedarf in den py3k-Branch vorwärts portiert. Dieser Workflow erfordert die Möglichkeit, einzelne Änderungen per Cherry-Picking auszuwählen. Mercurials Transplant-Erweiterung bietet diese Möglichkeit. Hier ist ein Beispiel für das Szenario mit diesem Workflow:

cd release26-maint
# Assume patch applied to 2.7 in revision 0000
hg transplant -s ../trunk 0000
# Resolve conflicts, if any.
cd ../py3k
hg pull ../trunk
hg merge
hg revert Misc/NEWS
hg commit -m "Merged trunk"
hg push

Im obigen Beispiel verhält sich Transplant ähnlich wie der aktuelle svnmerge-Befehl. Wenn Transplant ohne die Revision aufgerufen wird, startet der Befehl eine interaktive Schleife, die nützlich ist, um mehrere Änderungen zu transplantieren. Eine weitere nützliche Funktion ist die Option –filter, die zum programmatischen Ändern von Änderungen verwendet werden kann (z.B. könnte sie zum automatischen Entfernen von Änderungen an Misc/NEWS verwendet werden).

Alternativ zum traditionellen Workflow könnten wir das Transplantieren von Änderungen vermeiden, indem wir Bugfixes im ältesten unterstützten Release committen und diese Fixes dann in die neueren Branches mergen.

cd release25-maint
hg import fix_some_bug.diff
# Review patch and run test suite. Revert if failure.
hg push
cd ../release26-maint
hg pull ../release25-maint
hg merge
# Resolve conflicts, if any. Then, review patch and run test suite.
hg commit -m "Merged patches from release25-maint."
hg push
cd ../trunk
hg pull ../release26-maint
hg merge
# Resolve conflicts, if any, then review.
hg commit -m "Merged patches from release26-maint."
hg push

Obwohl dieser Ansatz die Historie nicht-linear und etwas schwieriger zu verfolgen macht, fördert er die Fehlerkorrektur über alle unterstützten Releases hinweg. Darüber hinaus skaliert er besser, wenn viele Änderungen zu backporten sind, da wir nicht nach den spezifischen Revisions-IDs suchen müssen, um zu mergen.

git

In Git hätte ich einen Arbeitsbereich, der alle relevanten Master-Repository-Branches enthält. Git cherry-pick funktioniert nicht über Repositorys hinweg; Sie müssen die Branches im selben Repository haben.

# Assume patch applied to 2.7 in revision release27~3 (4th patch back from tip).
cd integration
git checkout release26
git cherry-pick release27~3
# If there are conflicts, resolve them, and commit those changes.
# git commit -a -m "Resolve conflicts."
# Run test suite. If fixes are necessary, record as a separate commit.
# git commit -a -m "Fix code causing test failures."
git checkout master
git cherry-pick release27~3
# Do any conflict resolution and test failure fixups.
# Revert Misc/NEWS changes.
git checkout HEAD^ -- Misc/NEWS
git commit -m 'Revert cherry-picked Misc/NEWS changes.' Misc/NEWS
# Push both ports.
git push release26 master

Wenn Sie regelmäßig von einem bestimmten Branch mergen (anstatt Cherry-Picking zu betreiben), können Sie verhindern, dass ein bestimmter Commit versehentlich in Zukunft gemerged wird, indem Sie ihn mergen und dann rückgängig machen. Dies verhindert nicht, dass ein Cherry-Pick den unerwünschten Patch einbezieht, und diese Technik erfordert, dass alles blockiert wird, was Sie nicht mergen möchten. Ich bin mir nicht sicher, ob dies in diesem Punkt von svn abweicht.

cd trunk
# Merge in the alpha tested code.
git merge experimental-branch
# We don't want the 3rd-to-last commit from the experimental-branch,
# and we don't want it to ever be merged.
# The notation "^N" means Nth parent of the current commit. Thus HEAD^2^1^1
# means the first parent of the first parent of the second parent of HEAD.
git revert HEAD^2^1^1
# Propagate the merge and the prohibition to the public repository.
git push

Koordinierte Entwicklung einer neuen Funktion

Manchmal arbeiten Core-Entwickler an einem großen Feature mit mehreren Entwicklern. Als Core-Entwickler möchte ich in der Lage sein, Feature-Branches an einem gemeinsamen öffentlichen Ort zu veröffentlichen, damit ich mit anderen Entwicklern zusammenarbeiten kann.

Dies erfordert die Erstellung eines Branches auf einem Server, auf den andere Entwickler zugreifen können. Alle DVCSs unterstützen die Erstellung neuer Repositories auf Hosts, bei denen der Entwickler bereits committen kann, mit entsprechender Konfiguration des Repository-Hosts. Dies ähnelt dem Konzept des vorhandenen Sandkastens in svn, obwohl sich die Details der Repository-Initialisierung unterscheiden können.

Für Nicht-Core-Entwickler gibt es verschiedene mehr oder weniger öffentlich zugängliche Repository-Hosting-Dienste. Bazaar hat Launchpad, Mercurial hat bitbucket.org und git hat GitHub. Alle haben auch einfach zu bedienende CGI-Schnittstellen für Entwickler, die ihre eigenen Server verwalten.

  • Trunk-Branch.
  • Vom Branch auf dem Server ziehen.
  • Vom Trunk ziehen.
  • Merge in Trunk pushen.

svn

# Create branch.
svn copy svn+ssh://pythondev@svn.python.org/python/trunk svn+ssh://pythondev@svn.python.org/python/branches/NewHotness
svn checkout svn+ssh://pythondev@svn.python.org/python/branches/NewHotness
cd NewHotness
svnmerge init
svn commit -m "Initialize svnmerge."
# Pull in changes from other developers.
svn update
# Pull in trunk and merge to the branch.
svnmerge merge
svn commit -F svnmerge-commit-message.txt

Dieses Szenario ist unvollständig, da die Entscheidung für ein DVCS getroffen wurde, bevor die Arbeit abgeschlossen war.

Trennung von Issue-Abhängigkeiten

Manchmal stellt sich bei der Arbeit an einem Problem heraus, dass das bearbeitete Problem tatsächlich ein zusammengesetztes Problem aus verschiedenen kleineren Problemen ist. Die Möglichkeit, die aktuelle Arbeit zu nehmen und dann mit der Arbeit an einem separaten Problem zu beginnen, ist sehr hilfreich, um Probleme in einzelne Arbeitseinheiten zu zerlegen, anstatt sie zu einer einzigen großen Einheit zu bündeln.

  • Erstellen Sie einen Branch A (z.B. hat urllib einen Fehler).
  • Einige Codezeilen bearbeiten.
  • Erstellen Sie einen neuen Branch B, von dem Branch A abhängt (z.B. der urllib-Fehler deckt einen Socket-Fehler auf).
  • Bearbeiten Sie einige Codezeilen in Branch B.
  • Branch B committen.
  • Bearbeiten Sie einige Codezeilen in Branch A.
  • Branch A committen.
  • Aufräumen.

svn

Um die mangelnden kostengünstigen Branches von svn auszugleichen, gibt es die Option „changelist“, um eine Datei mit einer einzelnen Changelist zu verknüpfen. Dies ist nicht so leistungsfähig wie die Möglichkeit, auf Commit-Ebene zu verknüpfen. Es gibt auch keine Möglichkeit, Abhängigkeiten zwischen Changelists auszudrücken.

cp -r trunk issue0000
cd issue0000
# Edit some code.
echo "The cake is a lie!" > README
svn changelist A README
# Edit some other code.
echo "I own Python!" > LICENSE
svn changelist B LICENSE
svn ci -m "Tell it how it is." --changelist B
# Edit changelist A some more.
svn ci -m "Speak the truth." --changelist A
cd ..
rm -rf issue0000

bzr

Hier ist ein Ansatz, der bzr shelf (jetzt ein Standardbestandteil von bzr) verwendet, um einige Änderungen vorübergehend beiseite zu legen, während Sie einen Umweg machen, um die Socket-Fehler zu beheben.

bzr branch trunk bug-0000
cd bug-0000
# Edit some code. Dang, we need to fix the socket module.
bzr shelve --all
# Edit some code.
bzr commit -m "Socket module fixes"
# Detour over, now resume fixing urllib
bzr unshelve
# Edit some code

Ein anderer Ansatz verwendet das Loom-Plugin. Looms können die Arbeit an abhängigen Branches erheblich vereinfachen, da sie sich automatisch um die Stapelabhängigkeiten kümmern. Stellen Sie sich Looms als einen Stapel abhängiger Branches (in der Loom-Terminologie „Threads“ genannt) vor, mit einfachen Möglichkeiten, sich im Stapel der Threads auf- und abzubewegen, Änderungen im Stapel zu nachfolgenden Threads zu mergen, Unterschiede zwischen Threads zu erstellen usw. Gelegentlich müssen oder möchten Sie Ihre Loom-Threads in separate Branches exportieren, entweder zur Überprüfung oder zum Commit. Höhere Threads beinhalten alle Änderungen in den unteren Threads automatisch.

bzr branch trunk bug-0000
cd bug-0000
bzr loomify --base trunk
bzr create-thread fix-urllib
# Edit some code. Dang, we need to fix the socket module first.
bzr commit -m "Checkpointing my work so far"
bzr down-thread
bzr create-thread fix-socket
# Edit some code
bzr commit -m "Socket module fixes"
bzr up-thread
# Manually resolve conflicts if necessary
bzr commit -m 'Merge in socket fixes'
# Edit me some more code
bzr commit -m "Now that socket is fixed, complete the urllib fixes"
bzr record done

Für Bonuspunkte nehmen wir an, jemand anderes behebt das Socket-Modul genau so, wie Sie es gerade getan haben. Vielleicht hat diese Person sogar Ihren Fix-Socket-Thread übernommen und nur diesen in den Trunk übernommen. Sie möchten in der Lage sein, seine Änderungen in Ihren Loom zu mergen und Ihren nun redundanten Fix-Socket-Thread zu löschen.

bzr down-thread trunk
# Get all new revisions to the trunk. If you've done things
# correctly, this will succeed without conflict.
bzr pull
bzr up-thread
# See? The fix-socket thread is now identical to the trunk
bzr commit -m 'Merge in trunk changes'
bzr diff -r thread: | wc -l # returns 0
bzr combine-thread
bzr up-thread
# Resolve any conflicts
bzr commit -m 'Merge trunk'
# Now our top-thread has an up-to-date trunk and just the urllib fix.

hg

Ein Ansatz ist die Verwendung der Shelve-Erweiterung; diese Erweiterung ist nicht in Mercurial enthalten, aber sie ist leicht zu installieren. Mit Shelve können Sie Änderungen auswählen, die Sie vorübergehend beiseite legen möchten.

hg clone trunk issue0000
cd issue0000
# Edit some code (e.g. urllib).
hg shelve
# Select changes to put aside
# Edit some other code (e.g. socket).
hg commit
hg unshelve
# Complete initial fix.
hg commit
cd ../trunk
hg pull ../issue0000
hg merge
hg commit
rm -rf ../issue0000

Mehrere andere Möglichkeiten, dieses Szenario mit Mercurial anzugehen. Alexander Solovyov hat auf der Mercurial-Mailingliste einige alternative Ansätze vorgestellt.

git

cd trunk
# Edit some code in urllib.
# Discover a bug in socket, want to fix that first.
# So save away our current work.
git stash
# Edit some code, commit some changes.
git commit -a -m "Completed fix of socket."
# Restore the in-progress work on urllib.
git stash apply
# Edit me some more code, commit some more fixes.
git commit -a -m "Complete urllib fixes."
# And push both patches to the public repository.
git push

Bonuspunkte: Nehmen wir an, Sie haben sich Zeit gelassen, und jemand anderes behebt den Socket-Fehler auf die gleiche Weise wie Sie gerade, und hat ihn in den Trunk aufgenommen. In diesem Fall schlägt Ihr Push fehl, da Ihr Branch nicht aktuell ist. Wenn der Fix eine Einzeiler war, ist die Wahrscheinlichkeit sehr hoch, dass er *genau* gleich ist, Zeichen für Zeichen. Git würde das bemerken, und Sie sind fertig; Git würde sie stillschweigend mergen.

Angenommen, wir haben nicht so viel Glück

# Update your branch.
git pull git://code.python.org/public/trunk master

# git has fetched all the necessary data, but reports that the
# merge failed.  We discover the nearly-duplicated patch.
# Neither our version of the master branch nor the workspace has
# been touched.  Revert our socket patch and pull again:
git revert HEAD^
git pull git://code.python.org/public/trunk master

Wie Bazaar und Mercurial hat git Erweiterungen zur Verwaltung von Patch-Stapeln. Sie können das ursprüngliche Quilt von Andrew Morton verwenden, oder es gibt StGit („stacked git“), das die Patch-Verfolgung für große Patch-Mengen in das VCS integriert, ähnlich wie Mercurial Queues oder Bazaar Looms.

Durchführung einer Python-Veröffentlichung

Wie ändert sich PEP 101 bei Verwendung eines DVCS?

bzr

Es wird sich ändern, aber nicht wesentlich. Bei der Erstellung des Wartungs-Branches werden wir einfach an den neuen Ort pushen, anstatt einen svn cp durchzuführen. Tags sind völlig anders, da sie in svn Verzeichnis-Kopien sind, aber in bzr (und ich nehme hg an) sind sie nur symbolische Namen für Revisionen auf einem bestimmten Branch. Das Skript release.py muss geändert werden, um stattdessen bzr-Befehle zu verwenden. Es ist möglich, dass wir aufgrund der Tatsache, dass DVCS (insbesondere bzr) Cherry-Picking und Merging gut beherrscht, die Wartungs-Branches früher erstellen können. Es wäre eine nützliche Übung, zu versuchen, eine Veröffentlichung von den bzr/hg-Spiegeln aus durchzuführen.

hg

Offensichtlich müssen Details, die spezifisch für Subversion in PEP 101 und im Release-Skript sind, aktualisiert werden. Insbesondere müssen der Release-Tagging- und der Wartungs-Branch-Erstellungsprozess modifiziert werden, um die Funktionen von Mercurial zu nutzen; dies wird bestimmte Aspekte des Release-Prozesses vereinfachen und rationalisieren. Zum Beispiel wird das Taggen und erneute Taggen einer Veröffentlichung zu einer trivialen Operation, da ein Tag in Mercurial einfach ein symbolischer Name für eine gegebene Revision ist.

git

Es wird sich ändern, aber nicht wesentlich. Bei der Wartungs-Branch werden wir einfach zum neuen Speicherort pushen, anstatt ein svn cp durchzuführen. Tags sind völlig anders, da sie in svn Verzeichniskopien sind, aber in git nur symbolische Namen für Revisionen sind, ebenso wie Branches. (Der Unterschied zwischen einem Tag und einem Branch besteht darin, dass Tags sich auf einen bestimmten Commit beziehen und sich nie ändern, es sei denn, Sie verwenden git tag -f, um sie zu verschieben. Der ausgecheckte Branch hingegen wird automatisch von git commit aktualisiert.) Das release.py-Skript muss geändert werden, um stattdessen git-Befehle zu verwenden. Mit git würde ich sofort einen (lokalen) Wartungs-Branch erstellen, sobald der Release-Ingenieur ausgewählt ist. Dann würde ich "git pull" ausführen, bis mir ein Patch nicht gefiel, dann wäre es "git pull; git revert hässlicher-patch", bis es so aussah, als ob das Vernünftigste wäre, abzuzweigen und "git cherry-pick" auf die guten Patches anzuwenden.

Plattform-/Tool-Unterstützung

Betriebssysteme

DVCS Windows OS X UNIX
bzr ja (Installer) mit Tortoise ja (Installer, Fink oder MacPorts) ja (verschiedene Paketformate)
hg ja (Drittanbieter-Installer) mit Tortoise ja (Drittanbieter-Installer, Fink oder MacPorts) ja (verschiedene Paketformate)
git ja (Drittanbieter-Installer) ja (Drittanbieter-Installer, Fink oder MacPorts) ja (.deb oder .rpm)

Wie die obige Tabelle zeigt, sind alle drei DVCS auf allen drei großen Betriebssystemplattformen verfügbar. Aber sie zeigt auch, dass Bazaar das einzige DVCS ist, das Windows direkt mit einem Binär-Installer unterstützt, während Mercurial und Git erfordern, dass Sie sich für Binärdateien auf einen Drittanbieter verlassen. Sowohl bzr als auch hg haben eine Tortoise-Version, während Git dies nicht tut.

Bazaar und Mercurial haben auch den Vorteil, in reinem Python mit optionalen Erweiterungen für Leistung verfügbar zu sein.

CRLF -> LF-Unterstützung

bzr
Mein Verständnis ist, dass die Unterstützung dafür gerade entwickelt wird und bald erscheinen wird. Ich werde versuchen, Details herauszufinden.
hg
Unterstützt über die win32text-Erweiterung.
git
Ich kann dies nicht aus eigener Erfahrung sagen, aber es sieht so aus, als gäbe es durch die Konfigurationsattribute core.autocrlf und core.safecrlf eine ziemlich gute Unterstützung.

Unterstützung für nicht-sensible Dateisysteme

bzr
Sollte in Ordnung sein. Ich teile ständig Branches zwischen Linux und OS X. Ich habe Groß-/Kleinschreibung geändert (z. B. bzr mv Mailman mailman) und solange ich es unter Linux (offensichtlich) gemacht habe, war alles in Ordnung, als ich die Änderungen unter OS X eingezogen habe.
hg
Mercurial verwendet einen case-sicheren Repository-Mechanismus und erkennt Kollisionen bei der Groß-/Kleinschreibung.
git
Da OS X die Groß-/Kleinschreibung beibehält, können Sie dort auch Änderungen an der Groß-/Kleinschreibung vornehmen. Git hat kein Problem mit Umbenennungen in beide Richtungen. Die Unterstützung für dateisysteme, die nicht zwischen Groß- und Kleinschreibung unterscheiden, wird jedoch normalerweise als Beschwerde über Kollisionen auf dateisystemen verstanden, die zwischen Groß- und Kleinschreibung unterscheiden. Git tut das nicht.

Werkzeuge

In Bezug auf Code-Review-Tools wie Review Board und Rietveld unterstützt ersteres alle drei, während letzteres hg und git unterstützt, aber nicht bzr. Bazaar hat noch kein Online-Review-Board, aber es gibt mehrere Möglichkeiten, E-Mail-basierte Reviews und Trunk-Merging zu verwalten. Es gibt Bundle Buggy, Patch Queue Manager (PQM) und Launchpads Code-Reviews.

Alle drei haben eine Webseite online, die grundlegende Hosting-Unterstützung für Leute bietet, die ein Repository online stellen möchten. Bazaar hat Launchpad, Mercurial hat bitbucket.org und Git hat GitHub. Google Code bietet auch Anleitungen zur Verwendung von Git mit dem Dienst, sowohl zum Speichern eines Repositories als auch zum Spiegeln als schreibgeschütztes Spiegelbild.

Alle drei scheinen auch von Buildbot unterstützt zu werden.

Nutzung auf Subversion

DVCS SVN-Unterstützung
bzr bzr-svn (Drittanbieter)
hg mehrere Drittanbieter
git git-svn

Alle drei DVCS haben SVN-Unterstützung, obwohl Git die einzige ist, die diese Unterstützung sofort mitbringt.

Server-Unterstützung

DVCS Webseiten-Schnittstelle
bzr Loggerhead
hg hgweb
git gitweb

Alle drei DVCS unterstützen verschiedene Hooks auf Client- und Serverseite für z. B. Pre/Post-Commit-Verifizierungen.

Entwicklung

Alle drei Projekte werden aktiv entwickelt. Git scheint einem monatlichen Release-Zyklus zu folgen. Bazaar hat einen zeitlich gesteuerten monatlichen Release-Zyklus. Mercurial hat einen 4-monatigen, zeitlich gesteuerten Release-Zyklus.

Besondere Funktionen

bzr

Martin Pool fügt hinzu: „bzr hat eine stabile Python-Skripting-Schnittstelle, mit einer Unterscheidung zwischen öffentlichen und privaten Schnittstellen und einem Deprecation-Fenster für APIs, die sich ändern. Einige Plugins sind aufgeführt unter https://edge.launchpad.net/bazaar und http://bazaar-vcs.org/Documentation“.

hg

Alexander Solovyov kommentiert

Mercurial hat eine einfach zu bedienende, umfangreiche API mit Hooks für Hauptereignisse und die Möglichkeit, Befehle zu erweitern. Außerdem gibt es die mq (Mercurial Queues)-Erweiterung, die mit Mercurial vertrieben wird und die Arbeit mit Patches vereinfacht.

git

Git hat einen cvsserver-Modus, d. h., Sie können einen Baum von Git mit CVS auschecken. Sie können sogar in den Baum committen, aber Funktionen wie Merging fehlen, und Branches werden als CVS-Module behandelt, was einen erfahrenen CVS-Benutzer wahrscheinlich schockieren wird.

Tests/Eindrücke

Da ich (Brett Cannon) die Aufgabe habe, die endgültige Entscheidung zu treffen, welches/welches DVCS ich verwenden werde, und nicht meine Mitautoren, hielt ich es für fair, meine Tests und Eindrücke niederzuschreiben, während ich die verschiedenen Tools auswerte, um so transparent wie möglich zu sein.

Eintrittsbarriere

Die Zeit und der Aufwand, die benötigt werden, um einen Checkout von Pythons Repository zu erhalten, sind entscheidend. Wenn die Schwierigkeit oder Zeit zu groß ist, kann eine Person, die zu Python beitragen möchte, sehr schnell aufgeben. Das darf nicht passieren.

Ich habe das Auschecken des 2.x-Trunks gemessen, als wäre ich ein Nicht-Core-Entwickler. Die Zeitmessung erfolgte mit dem Befehl time in zsh und die Speicherplatzberechnung mit du -c -h.

DVCS San Francisco Vancouver Speicherplatz
svn 1:04 2:59 139 MB
bzr 10:45 16:04 276 MB
hg 2:30 5:24 171 MB
git 2:54 5:28 134 MB

Beim Vergleich dieser Zahlen mit SVN ist es wichtig zu erkennen, dass dies kein 1:1-Vergleich ist. SVN lädt nicht die gesamte Revisionshistorie herunter, wie es alle DVCS tun. Das bedeutet, dass SVN einen anfänglichen Checkout viel schneller als die DVCS durchführen kann, rein auf der Grundlage, dass es weniger Informationen über das Netzwerk herunterladen muss.

Performance grundlegender Informationsfunktionen

Um zu sehen, wie sich die Tools beim Ausführen eines Befehls verhielten, der eine Abfrage der Historie erforderte, wurde das Log der README-Datei zeitlich erfasst.

DVCS Zeit
bzr 4,5 s
hg 1,1 s
git 1,5 s

Eine Bemerkung während dieses Tests war, dass Git länger als die anderen drei Tools brauchte, um herauszufinden, wie das Log abgerufen werden kann, ohne einen Pager zu verwenden. Während die Pager-Verwendung im Allgemeinen ein schönes Extra ist, dauerte es einige Zeit, bis sie nicht automatisch eingeschaltet wurde (es stellte sich heraus, dass der Hauptbefehl git eine Option --no-pager hat, um die Verwendung des Pagers zu deaktivieren).

Herausfinden, welches Kommando aus der integrierten Hilfe zu verwenden ist

Ich habe versucht herauszufinden, welcher Befehl es ist, um zu sehen, von welcher URL das Repository geklont wurde. Dazu habe ich nichts weiter als die Hilfe des Tools selbst oder dessen Manpages verwendet.

Bzr war am einfachsten: bzr info. Das Ausführen von bzr help zeigte nicht viel, erwähnte aber bzr help commands. Diese Liste enthielt den Befehl mit einer Beschreibung, die sinnvoll war.

Git war am zweitbesten. Der Befehl git help zeigte nicht viel und hatte keine Möglichkeit, alle Befehle aufzulisten. Da habe ich die Manpage angesehen. Beim Lesen der verschiedenen Befehle entdeckte ich git remote. Der Befehl selbst gab nichts anderes aus als origin. Der Versuch git remote origin meldete einen Fehler und zeigte die Befehlsverwendung an. Da bemerkte ich git remote show. Das Ausführen von git remote show origin lieferte mir die gewünschten Informationen.

Für hg habe ich die gewünschten Informationen nie selbst gefunden. Es stellte sich heraus, dass ich hg paths wollte, aber das war aus der Beschreibung „show definition of symbolic path names“ nicht ersichtlich, wie sie von hg help ausgegeben wurde (es ist zu beachten, dass die Meldung dieses Problems im PEP die Mercurial-Entwickler dazu veranlasste, die Formulierung zu präzisieren, um die Verwendung des Befehls hg paths klarer zu machen).

Checkout aktualisieren

Um zu sehen, wie lange es dauert, ein veraltetes Repository zu aktualisieren, habe ich sowohl das Aktualisieren eines Repositorys mit 700 Commits Rückstand als auch mit 50 Commits Rückstand (drei Wochen veraltet bzw. eine Woche veraltet) gemessen.

DVCS 700 Commits 50 Commits
bzr 39 s 7 s
hg 17 s 3 s
git N/A 4 s

Hinweis

Git fehlt ein Wert für das Szenario *700 Commits*, da es anscheinend nicht erlaubt, ein Repository mit einer bestimmten Revision auszuchecken.

Git verdient besondere Erwähnung für seine Ausgabe von git pull. Es listet nicht nur die Delta-Änderungsinformationen für jede Datei auf, sondern färbt diese Informationen auch farblich ein.

Entscheidung

Auf der PyCon 2009 wurde die Entscheidung getroffen, sich für Mercurial zu entscheiden.

Warum Mercurial gegenüber Subversion

Während SVN dem Entwicklungsteam gut gedient hat, muss zugegeben werden, dass SVN den Bedürfnissen von Nicht-Committern nicht so gut dient wie ein DVCS. Da SVN seine Funktionen wie Versionskontrolle, Branching usw. nur für Personen mit Commit-Berechtigungen für das Repository bereitstellt, kann es für Personen ohne Commit-Berechtigungen ein Hindernis darstellen. DVCS haben jedoch keine solche Einschränkung, da jeder einen lokalen Branch von Python erstellen und seine eigenen lokalen Commits durchführen kann, ohne die Last, die mit dem Klonen des gesamten SVN-Repositorys einhergeht. Die Ermöglichung desselben Workflows wie für die Kernentwickler war der Hauptgrund für den Wechsel von SVN zu hg.

Orthogonal zu den Vorteilen, jedem die Möglichkeit zu geben, lokal an seinen eigenen Branches zu committen, sind Offline-Operationen, die schnell ablaufen. Da hg alle Daten lokal speichert, müssen keine Anfragen an einen Remote-Server gesendet werden, stattdessen wird von der lokalen Festplatte gearbeitet. Dies verbessert die Reaktionszeiten erheblich. Es ermöglicht auch die Offline-Nutzung, wenn keine Internetverbindung besteht. Dieser Vorteil ist jedoch gering und gilt eher als nebensächlicher Vorteil denn als treibender Faktor für den Wechsel von Subversion.

Warum Mercurial gegenüber anderen DVCSs

Git wurde aus drei Hauptgründen nicht gewählt (siehe den PyCon 2009 Lightning Talk, in dem Brett Cannon genau diese Gründe nennt; der Vortrag begann bei 3:45). Erstens ist die Windows-Unterstützung von Git die schwächste der drei betrachteten DVCS, was inakzeptabel ist, da Python die Entwicklung auf jeder Plattform unterstützen muss, auf der es läuft. Da Python unter Windows läuft und einige Leute auf dieser Plattform entwickeln, benötigt es solide Unterstützung. Und obwohl sich die Unterstützung von Git verbessert, ist sie zum jetzigen Zeitpunkt mit einem so großen Abstand die schwächste, dass sie als Problem betrachtet werden muss.

Zweitens, und genauso wichtig wie die erste Frage, ist, dass die Python-Kernentwickler Git mit Abstand am wenigsten von den drei DVCS-Optionen mochten. Wenn Sie die folgende Tabelle betrachten, sehen Sie die Ergebnisse einer Umfrage unter den Kernentwicklern und wie Git mit großem Abstand das unbeliebteste Versionskontrollsystem ist.

DVCS ++ gleich uninformiert
git 5 1 8 13
bzr 10 3 2 12
hg 15 1 1 10

Zuletzt, wenn alle Dinge gleich wären (was sie nicht sind, wie die beiden vorherigen Punkte zeigen), ist es vorzuziehen, ein in Python geschriebenes Werkzeug zu verwenden und zu unterstützen und nicht eines, das in C und Shell geschrieben ist. Wir sind pragmatisch genug, um ein Werkzeug nicht nur deshalb zu wählen, weil es in Python geschrieben ist, aber wir erkennen den Nutzen darin, Werkzeuge zu fördern, die dies tun, wenn es angemessen ist, wie in diesem Fall.

Was die Wahl von Mercurial gegenüber Bazaar betrifft, so war es die Popularität. Wie die Umfrage unter den Kernentwicklern zeigt, wurde hg gegenüber bzr bevorzugt. Aber auch die Community scheint hg zu bevorzugen, wie auf der PyCon nach der Bekanntgabe der Streichung von Git aus der Auswahl gezeigt wurde. Viele Leute kamen zu Brett und sagten auf verschiedene Weise, dass sie wollten, dass hg gewählt wird. Während niemand sagte, dass sie bzr nicht gewählt haben wollten, sagte auch niemand, dass sie es nicht wollten.

Basierend auf all diesen Informationen entschieden Guido und Brett, dass Mercurial das nächste Versionskontrollsystem für Python sein würde.

Migrationsplan

PEP 385 beschreibt den Übergang von SVN zu hg.


Quelle: https://github.com/python/peps/blob/main/peps/pep-0374.rst

Zuletzt geändert: 2025-02-01 08:59:27 GMT