Versionsverwaltung mit Mercurial (Teil 3)
von Philipp Burch
Rückblick und Ausblick
Teil 1 behandelte die grundlegenden Funktionen von Mercurial, während Teil 2 auf die Arbeit mit mehreren Repositories einging. In diesem Teil werden nun noch weitere nützliche Möglichkeiten vorgestellt. Diese sind für die tägliche Arbeit nicht zwingend erforderlich, können aber doch sehr hilfreich sein.
Lokaler Server: hg serve
In den vorhergehenden Abschnitten wurde oft der "Server" mit einem Repository erwähnt. Es gibt mehrere Möglichkeiten, zu einem solchen Server zu kommen: Öffentliche Hoster (siehe "Anbieter von Mercurial-Repositories"), ein eigener Server mit einer Installation von Mercurial, oder aber der eigene PC. Die letzte Variante soll hier kurz vorgestellt werden.
Mercurial selbst enthält einen eigenen kleinen Webserver, welcher über den Befehl
test$ hg serve listening at http://localhost6.localdomain6:8000/ (bound to *:8000)
gestartet werden kann. Anschliessend ist sofort Lesezugriff auf das Repo über die Adresse http://localhost:8000 möglich. Einerseits wird dann ein Durchforsten mittels Webbrowser geboten, andererseits kann ein Repo so auch direkt über eine Netzwerkverbindung geklont werden, sofern es die Firewall des Rechners zulässt. Lokal würde das dann etwa so aussehen:
~$ hg clone http://localhost:8000 httpclone requesting all changes adding changesets adding manifests adding file changes added 18 changesets with 12 changes to 9 files updating to branch default 3 files updated, 0 files merged, 0 files removed, 0 files unresolved Desktop$ cd httpclone/ httpclone$ ls datei_von_klon.txt datei_von_test.txt neuer_name.txt
Von einem anderen Rechner müsste dann "localhost" durch die IP-Adresse des Zielrechners ersetzt werden. Schreibzugriff auf das Repository ist ebenfalls möglich, jedoch nicht einfach so:
httpclone$ echo "Datei über den Server" > server.txt httpclone$ hg add server.txt httpclone$ hg commit -m 'Serverdatei erstellt.' httpclone$ hg push pushing to http://localhost:8000/ searching for changes remote: ssl required
Zur Aktivierung von Push-Zugriff durch "Fremde" muss die Konfiguration des Server-Repos angepasst werden. Konkret müssen in der hgrc-Datei die Einträge web.allow_push=* und web.push_ssl=false vorhanden sein. Dazu muss erst der Server beendet und nach der Dateianpassung neu gestartet werden:
^Cinterrupted! test$ cat >> .hg/hgrc [web] allow_push = * push_ssl = false test$ hg serve listening at http://localhost6.localdomain6:8000/ (bound to *:8000)
Und schon funktioniert es wie gewünscht:
httpclone$ hg push pushing to http://localhost:8000/ searching for changes remote: adding changesets remote: adding manifests remote: adding file changes remote: added 1 changesets with 1 changes to 1 files
Abbildung 1: hg serve in Aktion
So schön diese Funktion auch ist, so sollte sie doch mit Bedacht eingesetzt werden. Aus Sicherheitsgründen ist es nicht ratsam, einen öffentlichen Server über längere Zeit auf diesem Wege zu betreiben.
Extensions
Wie so viele andere Programme auch, bietet Mercurial eine Schnittstelle für Erweiterungen. Die Aktivierung einer Erweiterung ist denkbar einfach, es reicht, den entsprechenden Eintrag in der hgrc vorzunehmen. Das Format ist:
[extensions] erweiterung = pfad_zur_erweiterung
Der Pfad zur Erweiterung sollte dann auf die entsprechende Python-Datei verweisen. Glücklicherweise ist es jedoch für viele Erweiterungen nicht notwendig, überhaupt einen Pfad anzugeben, weil sie bereits zum normalen Funktionsumfang von Mercurial gehören und einfach nicht aktiviert sind. Mehr dazu weiss
test$ hg help extensions
Die hier behandelten Erweiterungen gehören alle bereits zu Mercurial.
fetch
Eine sehr nützliche Erweiterung ist fetch, sie wurde bereits in einem früheren Abschnitt erwähnt. Die Aktivierung ist einfach, sie lautet
[extensions] fetch =
fetch ist eine Kombination aus den Befehlen pull, update, merge und commit und dient zum schnellen Herunterladen von Änderungen aus einem anderen Repo. Dazu ein Beispiel:
test$ echo "Daten für fetch" > fetch.txt test$ hg add fetch.txt test$ hg commit -m 'Datei für fetch-Beispiel erzeugt.' test$ cd ../klon/ klon$ cat >> .hg/hgrc [extensions] fetch = klon$ hg fetch ../test/ pulling from ../test/ searching for changes adding changesets adding manifests adding file changes added 2 changesets with 2 changes to 2 files (+1 heads) updating to 21:2e3522b70f10 3 files updated, 0 files merged, 0 files removed, 0 files unresolved merging with 19:1e0a0f95ed00 1 files updated, 0 files merged, 0 files removed, 0 files unresolved new changeset 22:0d5630651b3d merges remote changes with local klon$ hg log -r 21:tip changeset: 21:2e3522b70f10 user: phip date: Mon Feb 06 18:56:53 2012 +0100 summary: Datei für fetch-Beispiel erzeugt. changeset: 22:0d5630651b3d tag: tip parent: 21:2e3522b70f10 parent: 19:1e0a0f95ed00 user: phip date: Mon Feb 06 19:02:37 2012 +0100 summary: Automated merge with file:///.../test
Wer lieber eine andere commit-message angeben würde, falls ein merge erforderlich ist, sollte sich die Optionen -m und -l ansehen.
graphlog
Einige Abschnitte weiter oben wurde von "linearer Historie", "Kopfversion" und ähnlichen Begriffen gesprochen. Diese Bezeichnungen sind nicht zufällig gewählt, sondern leiten sich aus der Repräsentation im DAG, dem directed acyclic graph ab. Dieser Graph zeigt den Zusammenhang einzelner Revisionen eines Repos. Wer sich im Abschnitt hg serve das Repository im Webbrowser etwas genauer angesehen hat, ist vielleicht auf das Register Graph gestossen. Dieses zeigt eben diesen DAG des Repos. TortoiseHg bietet diese Darstellung standardmässig in der Hg Workbench an. Als weitere Option kann der Graph mittels der graphlog-Erweiterung angezeigt werden:
test$ cat >> .hg/hgrc [extensions] graphlog = test$ hg glog -r 13:tip @ changeset: 19:2e3522b70f10 | tag: tip | user: phip | date: Mon Feb 06 18:56:53 2012 +0100 | summary: Datei für fetch-Beispiel erzeugt. | o changeset: 18:9e6e0452d739 | user: phip | date: Mon Feb 06 06:40:18 2012 +0100 | summary: Serverdatei erstellt. | o changeset: 17:58aa83679302 | user: phip | date: Sun Feb 05 19:39:32 2012 +0100 | summary: datei_von_klon.txt erweitert. | o changeset: 16:9f0bc6cc9581 |\ parent: 14:aa3e7b2d29ae | | parent: 15:ed475c1ebd7a | | user: phip | | date: Fri Feb 03 20:23:31 2012 +0100 | | summary: Merge. | | | o changeset: 15:ed475c1ebd7a | | parent: 13:7f552d0520d2 | | user: phip | | date: Fri Feb 03 20:21:38 2012 +0100 | | summary: Zeile ergänzt. | | o | changeset: 14:aa3e7b2d29ae |/ user: phip | date: Fri Feb 03 20:22:58 2012 +0100 | summary: Datei erstellt. | o changeset: 13:7f552d0520d2 |\ parent: 11:e5088b70e741 | | parent: 12:bce19940449e | | user: phip | | date: Fri Feb 03 20:03:42 2012 +0100 | | summary: Änderungen zusammengeführt. | |
Hier ist sehr schön erkennbar, was die oben erwähnten Begriffe bedeuten: Zwischen Changeset 17 und 19 ist eine gerade Linie vorhanden, der Graph (und damit die Versionshistorie) ist linear. Von Changeset 14 bis 16 waren zwei Versionen des Repos vorhanden (Zur Erinnerung: Die eine war im "klon"), welche in Changeset 16 wieder zusammengefasst wurden.
Changeset 19 ist die Kopfversion des Repos, doch die Bedeutung davon lässt sich noch etwas besser veranschaulichen, wenn ein zweiter "head" existiert:
test$ hg update -r 18 0 files updated, 0 files merged, 1 files removed, 0 files unresolved test$ hg rm neuer_name.txt test$ hg commit -m "Datei 'neuer_name.txt' gelöscht." created new head test$ hg glog -r 17:tip @ changeset: 20:f7ab5cdd71e4 | tag: tip | parent: 18:9e6e0452d739 | user: phip | date: Mon Feb 06 19:21:27 2012 +0100 | summary: Datei 'neuer_name.txt' gelöscht. | | o changeset: 19:2e3522b70f10 |/ user: phip | date: Mon Feb 06 18:56:53 2012 +0100 | summary: Datei für fetch-Beispiel erzeugt. | o changeset: 18:9e6e0452d739 | user: phip | date: Mon Feb 06 06:40:18 2012 +0100 | summary: Serverdatei erstellt. | o changeset: 17:58aa83679302 | user: phip | date: Sun Feb 05 19:39:32 2012 +0100 | summary: datei_von_klon.txt erweitert. |
Offensichtlich wurde mit Changeset 19 ein neuer Ast (branch) erzeugt, dessen Kopfversion eben die 19 ist. Würde dieser mit hg merge wieder integriert, ergäbe sich das gleiche Bild wie etwas weiter oben, es wären zwei Linien parallel vorhanden.
Dateien ignorieren: .hgignore
Während der Entwicklung werden gerade durch integrierte Werkzeuge sehr häufig sehr viele Dateien erzeugt, darunter auch viele mit binären Formaten. Dabei handelt es sich teilweise um Zwischendateien (z.B. Objektdateien des Compilers) oder gecachte Daten (wie die Intellisense-Datenbanken von Visual Studio). Normalerweise ist es nicht wünschenswert, diese ebenfalls unter die Versionsverwaltung zu stellen, da sie einerseits nur eine Redundanz darstellen (sie können in jeder Revision aus den vorhandenen Quelldateien erstellt werden) und das Repo unnötig aufblähen. Es wäre nun wohl möglich, nur die relevanten Dateien mit hg add hinzuzufügen und den Rest einfach wegzulassen, doch dies ist mühsam und müllt die Ausgabe von hg status zu:
test$ for i in {1..10}; do echo abc > "$i.dat"; done; test$ hg status ? 1.dat ? 10.dat ? 2.dat ? 3.dat ? 4.dat ? 5.dat ? 6.dat ? 7.dat ? 8.dat ? 9.dat
Mittels Einträgen in der Datei ".hgignore" können Dateien anhand einem Muster im Namen ausgeschlossen werden. Zuerst ein passendes Beispiel für den gezeigten Fall:
test$ cat > .hgignore syntax: glob *.dat test$ hg status ? .hgignore
Einträge können in zwei Syntaxvarianten erfasst werden: glob und regexp. Die regexp-Syntax verwendet die Einträge als reguläre Ausdrücke um zu entscheiden, ob eine Datei ausgeschlossen werden soll. Auf die Funktionsweise davon soll hier nicht weiter eingegangen werden, dafür gibt es bessere Ressourcen, wie beispielsweise diese Kolumne zu regulären Ausdrücken.
glob ist dieseble Syntax, wie sie von den meisten Shells verwendet wird. Der am häufigsten verwendete Platzhalter dürfte der Stern (*) sein, welcher auf eine beliebige Zeichenfolge passt. Daneben gibt es das Fragezeichen (?), welches auf genau ein Zeichen passt und die Liste in eckigen Klammern, welche auf ein beliebiges Zeichen aus der Liste passt. Mehr dazu weiss Wikipedia.
Ein wichtiger Punkt muss hier beachtet werden: Die Muster werden jeweils auf den kompletten Pfad einer Datei (relativ zum Ursprungsverzeichnis des Repos) angewendet. Dies führt dazu, dass durch oben genanntes Beispiel sämtliche Dateien mit der Endung .dat ausgeschlossen werden, auch in beliebigen Unterverzeichnissen.
Der Aufruf von hg status oben zeigt, dass die .hgignore-Datei eine gewöhnliche Datei innerhalb des Repos ist, welche ebenfalls unter Versionsverwaltung gestellt werden kann (und sollte). Dadurch werden die Dateien bei allen Benutzern eines Repos ausgeschlossen.
Versionen benennen: hg tag
Ein nützliches Feature bei grösseren Projekten ist die Möglichkeit, einzelne Revisionen zu benennen. Dadurch lassen sie sich direkt über ihren symbolischen Namen ansprechen und man braucht sich keine Hashes oder Revisionsnummern zu merken. Der Befehl zum Vergeben eines neuen Tags ist hg tag:
test$ hg tag "Getaggte Version."
Dabei ist zu beachten, dass Tags im Normalfall in einer gewöhnlichen Datei mit dem Namen .hgtags abgelegt werden. Daher müssen Änderungen an Tags ebenfalls mit einem Commit in das Repo übernommen werden. Dies wird von hg tag automatisch erledigt. Der Tag bezieht sich dann jedoch auf die vorhergehende Revision, sollte also nicht zusammen mit anderen Änderungen übernommen werden.
Im Revisionslog werden Tags bei den entsprechenden Revisionen aufgeführt:
test$ hg log -r 20:tip changeset: 20:f7ab5cdd71e4 tag: Getaggte Version. parent: 18:9e6e0452d739 user: phip date: Mon Feb 06 19:21:27 2012 +0100 summary: Datei 'neuer_name.txt' gelöscht. changeset: 21:611b9c316c17 tag: tip user: phip date: Wed Feb 08 06:12:29 2012 +0100 summary: Added tag Getaggte Version. for changeset f7ab5cdd71e4
Mittels hg tags können alle momentan vergebenen Tags angezeigt werden:
test$ hg tags tip 21:611b9c316c17 Getaggte Version. 20:f7ab5cdd71e4
Der Tag "tip" ist immer vorhanden und verweist auf die jeweils aktuellste (oberste) Version.
Verschiedene Entwicklungslinien: Branches
Als Branches (Äste) bezeichnet man verschiedene parallel geführte Entwicklungslinien in einem Projekt. Dazu erst ein Anwendungsbeispiel: Angenommen, wir haben ein Projekt auf einer stabilen Version, z.B. 1.0. Für dieses Programm sollen natürlich weiterhin Updates mit Bugfixes und Sicherheitsaktualisierungen angeboten werden. Dazu ist es notwendig, jederzeit auf Version 1.0 Zugriff zu haben, damit die Updates auch wirklich auf die ausgelieferten Versionen passen. Parallel dazu soll Version 2.0 der Software entwickelt werden. Diese Entwicklung darf jedoch nicht mit den anderen Updates in Konflikt geraten.
Eine Möglichkeit für diesen Zweck wäre, das Repo an Version 1.0 zu klonen und im Klon an V2.0 zu arbeiten, während das Original für die Updates verwendet wird. Dies ist absolut möglich und kann so gemacht werden. Doch Mercurial hat für genau diese Anwendung auch eine integrierte Möglichkeit: Branches.
Zuerst markieren wir Version 1.0 für einfacheres Wiederfinden:
test$ hg tag "V1.0" test$ hg glog -r 21: @ changeset: 22:b35cd09efa5e | tag: tip | user: phip | date: Wed Feb 08 06:46:42 2012 +0100 | summary: Added tag V1.0 for changeset 611b9c316c17 | o changeset: 21:611b9c316c17 | tag: V1.0 | user: phip | date: Wed Feb 08 06:12:29 2012 +0100 | summary: Added tag Getaggte Version. for changeset f7ab5cdd71e4 |
Anschliessend wird ein Sicherheitsupdate erstellt:
test$ echo "Sicherheitsupdate" > update1.txt test$ hg add update1.txt test$ hg commit -m 'Sicherheitsupdate' test$ hg glog -r 21: @ changeset: 23:a05934ef00e9 | tag: tip | user: phip | date: Wed Feb 08 06:49:06 2012 +0100 | summary: Sicherheitsupdate | o changeset: 22:b35cd09efa5e | user: phip | date: Wed Feb 08 06:46:42 2012 +0100 | summary: Added tag V1.0 for changeset 611b9c316c17 | o changeset: 21:611b9c316c17 | tag: V1.0 | user: phip | date: Wed Feb 08 06:12:29 2012 +0100 | summary: Added tag Getaggte Version. for changeset f7ab5cdd71e4 |
Nun beginnt die Entwicklung an V2.0. Dafür wird zuerst auf V1.0 "zurückaktualisiert" um von dort aus weiterarbeiten zu können. Anschliessend wird mit hg branch die Benennung des neuen Asts geändert:
test$ hg update V1.0 1 files updated, 0 files merged, 1 files removed, 0 files unresolved test$ hg branch v2_staging marked working directory as branch v2_staging
Ab jetzt kann an Version 2.0 gearbeitet werden:
test$ echo "Latest and greatest" > v2.txt test$ hg add v2.txt test$ hg commit -m 'Entwicklung an V2.0 begonnen' test$ hg glog -r 21: @ changeset: 24:ddbe83ca5422 | branch: v2_staging | tag: tip | parent: 21:611b9c316c17 | user: phip | date: Wed Feb 08 06:54:21 2012 +0100 | summary: Entwicklung an V2.0 begonnen | | o changeset: 23:a05934ef00e9 | | user: phip | | date: Wed Feb 08 06:49:06 2012 +0100 | | summary: Sicherheitsupdate | | | o changeset: 22:b35cd09efa5e |/ user: phip | date: Wed Feb 08 06:46:42 2012 +0100 | summary: Added tag V1.0 for changeset 611b9c316c17 | o changeset: 21:611b9c316c17 | tag: V1.0 | user: phip | date: Wed Feb 08 06:12:29 2012 +0100 | summary: Added tag Getaggte Version. for changeset f7ab5cdd71e4 |
Sollte es nun vorkommen, dass V1.0 ein weitere Update benötigt, kann man bequem auf den originalen Branch "default" zurückkehren:
test$ hg update default 2 files updated, 0 files merged, 1 files removed, 0 files unresolved test$ ls datei_von_klon.txt datei_von_test.txt server.txt update1.txt
Ab sofort landen weitere Commits wieder im originalen Branch, es können also (in unserem Beispiel) weitere Updates erstellt werden:
test$ echo "Bugfix" > update2.txt test$ hg add update2.txt test$ hg commit -m 'Bug gefixt.' test$ hg glog -r 21: @ changeset: 25:3bb76cc26015 | tag: tip | parent: 23:a05934ef00e9 | user: phip | date: Wed Feb 08 17:55:50 2012 +0100 | summary: Bug gefixt. | | o changeset: 24:ddbe83ca5422 | | branch: v2_staging | | parent: 21:611b9c316c17 | | user: phip | | date: Wed Feb 08 06:54:21 2012 +0100 | | summary: Entwicklung an V2.0 begonnen | | o | changeset: 23:a05934ef00e9 | | user: phip | | date: Wed Feb 08 06:49:06 2012 +0100 | | summary: Sicherheitsupdate | | o | changeset: 22:b35cd09efa5e |/ user: phip | date: Wed Feb 08 06:46:42 2012 +0100 | summary: Added tag V1.0 for changeset 611b9c316c17 | o changeset: 21:611b9c316c17 | tag: V1.0 | user: phip | date: Wed Feb 08 06:12:29 2012 +0100 | summary: Added tag Getaggte Version. for changeset f7ab5cdd71e4 |
Alle vorhandenen (und offenen) Branches können jederzeit mittels hg branches angezeigt werden:
test$ hg branches default 25:3bb76cc26015 v2_staging 24:ddbe83ca5422
Die Branches von Mercurial könnten noch wesentlich mehr, doch das bisher Vermittelte sollte zumindest als Startpunkt ausreichen. Nach etwas Eingewöhnungszeit sollten die grundlegenden Prinzipien schnell klar werden.
Nützliche Funktionen
Die bis hier erklärten Funktionen sollten bereits relativ effizientes Arbeiten mit Mercurial ermöglichen. Doch trotz des nun schon etwas umfangreicheren Tutorials wurde dabei eigentlich nur die Oberfläche etwas angekratzt.
Im folgenden sind einige weitere nützliche Befehle aufgeführt, jedoch ohne tiefer gehende Erklärung und ohne Beispiele.
hg archive
Diese Funktion erzeugt ein (komprimiertes) Dateiarchiv aus einer bestimmten Revision des Repos. Dabei werden alle versionierten Dateien dieser Revision verpackt, jedoch ohne die Metadaten, also ohne den .hg-Ordner.
hg backout
backout erzeugt ein Changeset, welches die Änderung eines früheren Commits rückgängig macht. Damit können keine Daten aus der Historie entfernt werden, doch es ist beispielsweise möglich, ein früheres fehlerhaftes Update wieder zu entfernen.
hg bisect
bisecting ist ein eher fortgeschrittenes Verfahren zum Finden von Bugs in einer Software. Dabei müssen zwei Changesets angegeben werden: Eines, welches den Bug noch nicht aufweist (good) und eines, welches den Bug klar reproduzierbar zeigt (bad). Mittels hg bisect wird dann eine Revision ausgewählt, die getestet werden sollte. Diese kann anschliessend ebenfalls entweder als good oder bad markiert werden. Aus diesen Informationen ist Mercurial in der Lage, den Ursprung eines Fehlers auf das verantwortliche Changeset einzuschränken, ohne dass jeder einzelne Commit überprüft werden muss.
hg cat
Dieser Befehl dient wie cat zum unformatierten Ausgeben des Inhalts einer Datei. Jedoch kann angegeben werden, welche Revision der Datei ausgegeben werden soll. Damit ist es auch ohne hg update oder hg revert möglich, den Inhalt einer mittlerweile entfernten Datei auszugeben.
hg grep
hg grep ist das Mercurial-Pendant zum Unix-Befehl grep, der zum Durchsuchen einer Datei mittels regulären Ausdrücken dient. Auch hier ist der wesentliche Unterschied, dass die Datei in einer bestimmten Version durchsucht werden kann.
hg locate
Mittels hg locate kann eine Datei im Repo gesucht werden, deren Dateiname auf ein Muster passt. Dies ist ebenfalls für jede beliebige Revision möglich.
hg recover
Sollte bei einer Transaktion etwas schiefgehen (beispielsweise ein Abbruch der Netzwerkverbindung bei pull), dann wird das Repo möglicherweise in einem unsauberen Zustand hinterlassen. In diesem Fall kann mittels hg recover wieder ein stabiler Zustand hergestellt werden. Mercurial gibt normalerweise eine entsprechende Meldung aus, falls dieser Befehl verwendet werden sollte.
hg rollback
Es kommt vor, dass man beispielsweise einen Commit durchführt und vergisst, einige Dateien mittels hg add zum Repo hinzuzufügen. In diesem Fall kann es sinnvoll sein, mit hg rollback den Commit rückgängig zu machen, die Dateien zu hg add-en und erneut zu commiten. Trotzdem ist Vorsicht geboten, denn es kann nur die letzte Transaktion rückgängig gemacht werden und es gibt kein Gegenstück zu rollback. Wenn also eine Transaktion zurückgenommen wurde, kann das Zurücknehmen nicht mehr rückgängig gemacht werden.
Siehe auch hg help rollback
hg root
Dieser Befehl zeigt das Root-Verzeichnis des aktuellen Repositories an. Dies ist jener Ordner, in welchem sich der .hg-Ordner befindet.
hg showconfig
hg showconfig zeigt die kumulierte Liste aller aktiven Konfigurationsoptionen an. Damit kann beispielsweise geprüft werden, ob eine globale Einstellung durch eine lokale überschrieben wurde oder ähnliches. Die Ausgabe erfolgt im Punktformat, also z.B. "ui.username=xyz" für den Wert "xyz" des Attributs "username" im Abschnitt "ui".
Mercurial im Netz
Eine gute Anlaufstelle ist immer die Website des Entwicklers.
Zusätzlich gibt es ein gutes Online-Buch zu Mercurial: Mercurial: The Definitive Guide
Ein Tutorial in Englisch gibt es bei Hg Init. (Übrigens sehr unterhaltsam zum Lesen.)
Wie immer bei Themen zu Softwareprojekten fördert die Suchmaschine seiner Wahl natürlich auch zu Mercurial eine fast unbegrenzte Fülle von Informationen.
Anbieter von Mercurial-Repositories
Wie weiter oben erwähnt, bietet Mercurial mit hg serve einen internen Webserver. Dieser eignet sich gut zum Verteilen von Changesets unter Kameraden, doch er hat nichts auf einem öffentlich zugänglichen Server verloren. Dafür gibt es bessere Varianten.
Die einfachste Möglichkeit ist jedoch, ein Repo bei einem entsprechenden Anbieter hosten zu lassen. Dafür gibt es einige, sowohl kostenlos wie auch kostenpflichtig. Eine unvollständige Liste:
Schlusswort
Das Tutorial endet hier. Natürlich gäbe es noch viel, viel, viel mehr zur Mercurial zu sagen, doch dafür soll an dieser Stelle nochmals auf andere Ressourcen wie das HgBook verwiesen werden.
Ich hoffe, ich konnte einen Eindruck vermitteln, wozu Mercurial verwendet werden kann und wie es das Leben eines Entwicklers erleichtert.
Nun bleibt mir nur noch, dem Leser viel Erfolg beim Quecksilbern zu wünschen.
Philipp Burch
Ihre Meinung
Falls Sie Fragen zu diesem Tutorial haben oder Ihre Erfahrung mit anderen Nutzern austauschen möchten, dann teilen Sie uns diese bitte in einem der unten vorhandenen Themen oder über einen neuen Beitrag mit. Hierzu können sie einfach einen Beitrag in einem zum Thema passenden Forum anlegen, welcher automatisch mit dieser Seite verknüpft wird.