[svnbook commit] r3521 - * trunk/src/de/book/ch08-embedding-svn.xml
codesite-noreply at google.com
codesite-noreply at google.com
Mon May 25 09:17:24 CDT 2009
Author: jmfelderhoff at gmx.eu
Date: Mon May 25 07:17:02 2009
New Revision: 3521
Modified:
trunk/src/de/book/ch08-embedding-svn.xml
Log:
* trunk/src/de/book/ch08-embedding-svn.xml
- Fixes ticket #257 (cf. http://www.svnbook.de/report/6).
Modified: trunk/src/de/book/ch08-embedding-svn.xml
==============================================================================
--- trunk/src/de/book/ch08-embedding-svn.xml (original)
+++ trunk/src/de/book/ch08-embedding-svn.xml Mon May 25 07:17:02 2009
@@ -2049,8 +2049,12 @@
<!-- ===============================================================
-->
<sect2 id="svn.developer.usingapi.codesamples">
+<!--
<title>Code Samples</title>
+-->
+ <title>Beispielcode</title>
+<!--
<para><xref linkend="svn.developer.layerlib.repos.ex-1" />
contains a code segment (written in C) that illustrates some
of the concepts we've been discussing. It uses both the
@@ -2064,10 +2068,29 @@
handling—all Subversion errors must be explicitly
handled to avoid memory leakage (and in some cases,
application failure).</para>
+-->
+ <para><xref linkend="svn.developer.layerlib.repos.ex-1" />
+ enthält einen Codeabschnitt (in C), der einige der erörterten
+ Konzepte veranschaulicht. Er verwendet sowohl die Repository-
+ als auch die Dateisystemschnittstelle (was anhand der Präfixe
+ <literal>svn_repos_</literal> bzw.
+ <literal>svn_fs_</literal> der Funktionsnamen erkennbar ist),
+ um eine neue Revision zu erzeugen, in der ein Verzeichnis
+ hinzugefügt wird. Sie können die Verwendung des APR-Pools
+ erkennen, der zur Speicherzuteilung herumgereicht wird. Der
+ Code verdeutlicht auch eine etwas undurchsichtige
+ Angelegenheit bezüglich der Fehlerbehandlung von Subversion
+ – alle Fehler von Subversion müssen explizit behandelt
+ werden, um Speicherlöcher zu verhindern (und vereinzelt
+ Programmabstürze).</para>
<example id="svn.developer.layerlib.repos.ex-1">
+<!--
<title>Using the Repository Layer</title>
+-->
+ <title>Verwendung der Repository-Schicht</title>
+<!--
<programlisting>
/* Convert a Subversion error into a simple boolean error code.
*
@@ -2171,8 +2194,117 @@
INT_ERR(err);
}
</programlisting>
+-->
+ <programlisting>
+/* Umwandlung eines Subversion-Fehlers in einen einfachen Boole'schen
+ * Fehlerwert.
+ *
+ * NOTE: Subversion-Fehler müssen zurückgesetzt werden (mit
+ * svn_error_clear()), da sie aus dem globalen Pool zugeteilt
+ * werden, ansonsten treten Speicherlöcher auf.
+ */
+#define INT_ERR(expr) \
+ do { \
+ svn_error_t *__temperr = (expr); \
+ if (__temperr) \
+ { \
+ svn_error_clear(__temperr); \
+ return 1; \
+ } \
+ return 0; \
+ } while (0)
+
+/* Ein neues Verzeichnis im Pfad NEW_DIRECTORY des
+ * Subversion-Repositorys bei REPOS_PATH erzeugen. Sämtliche
+ * Speicherzuteilungen in Pool durchführen. Diese Funktion erzeugt
+ * eine neue Revision, damit NEW_DIRECTORY hinzugefügt werden kann. Im
+ * Erfolgsfall Null, sonst einen Wert ungleich Null zurückgeben.
+ */
+static int
+make_new_directory(const char *repos_path,
+ const char *new_directory,
+ apr_pool_t *pool)
+{
+ svn_error_t *err;
+ svn_repos_t *repos;
+ svn_fs_t *fs;
+ svn_revnum_t youngest_rev;
+ svn_fs_txn_t *txn;
+ svn_fs_root_t *txn_root;
+ const char *conflict_str;
+
+ /* Repository bei REPOS_PATH öffnen.
+ */
+ INT_ERR(svn_repos_open(&repos, repos_path, pool));
+
+ /* Zeiger auf das Dateisystemobjekt in REPOS holen.
+ */
+ fs = svn_repos_fs(repos);
+
+ /* Anfrage beim Dateisystem nach der aktuell letzten existierenden
+ * Revision.
+ */
+ INT_ERR(svn_fs_youngest_rev(&youngest_rev, fs, pool));
+
+ /* Starten einer neuen Transaktion basierend auf YOUNGEST_REV. Die
+ * Wahrscheinlichkeit einer später wegen Konflikte abgelehnten
+ * Übergabe sinkt, wenn wir stets versuchen, unsere Änderungen auf
+ * der letzten Momentaufnahme des Dateisystembaums zu machen.
+ */
+ INT_ERR(svn_repos_fs_begin_txn_for_commit2(&txn, repos, youngest_rev,
+ apr_hash_make(pool), pool));
+
+ /* Nach dem Start der Transaktion wird ein Wurzelobjekt geholt, das
+ * diese Transaktion repräsentiert.
+ */
+ INT_ERR(svn_fs_txn_root(&txn_root, txn, pool));
+
+ /* Das neue Verzeichnis unter der Transaktionswurzel im Pfad
+ * NEW_DIRECTORY anlegen.
+ */
+ INT_ERR(svn_fs_make_dir(txn_root, new_directory, pool));
+
+ /* Die Transaktion übergeben, indem eine neue Revision des
+ * Dateisystems erzeugt wird, die unseren hinzugefügten
+ * Verzeichnispfad enthält.
+ */
+ err = svn_repos_fs_commit_txn(&conflict_str, repos,
+ &youngest_rev, txn, pool);
+ if (! err)
+ {
+ /* Kein Fehler? Ausgezeichnet! Eine kurze Erfolgsnachricht
+ * ausgeben.
+ */
+ printf("Verzeichnis '%s' ist erfolgreich als neue Revision "
+ "'%ld' hinzugefügt worden.\n", new_directory, youngest_rev);
+ }
+ else if (err->apr_err == SVN_ERR_FS_CONFLICT)
+ {
+ /* Oh-ha. Die Übergabe schlug wegen eines Konfliktes fehl
+ * (jemand anderes scheint im gleichen Bereich des Dateisystems
+ * Änderungen gemacht zu haben, das wir ändern wollten). Eine
+ * Fehlermeldung ausgeben.
+ */
+ printf("Ein Konflikt trat im Pfad '%s' auf, als versucht wurde, "
+ "das Verzeichnis '%s' dem Repository bei '%s'
hinzuzufügen.\n",
+ conflict_str, new_directory, repos_path);
+ }
+ else
+ {
+ /* Ein anderer Fehler ist aufgetreten. Eine Fehlermeldung
+ * ausgeben.
+ */
+ printf("Beim Versuch, das Verzeichnis '%s' dem Repository bei "
+ "'%s' hinzuzufügen, trat ein Fehler auf.\n",
+ new_directory, repos_path);
+ }
+
+ INT_ERR(err);
+}
+</programlisting>
</example>
+<!--
<para>Note that in <xref
linkend="svn.developer.layerlib.repos.ex-1" />, the code could
just as easily have committed the transaction using
@@ -2189,16 +2321,44 @@
<function>svn_repos_fs_commit_txn()</function>. (For more
information regarding Subversion's repository hooks, see <xref
linkend="svn.reposadmin.create.hooks" />.)</para>
+-->
+ <para>Beachten Sie, dass der Code in <xref
+ linkend="svn.developer.layerlib.repos.ex-1" /> die Transaktion
+ ebenso einfach mit <function>svn_fs_commit_txn()</function>
+ hätte übergeben können. Allerdings weiß die Dateisystem-API
+ nichts über den Hook-Mechanismus der Repository-Bibliothek.
+ Falls Sie möchten, dass Ihr Subversion-Repository bei jeder
+ Transaktionsübergabe eine Nicht-Subversion-Aufgabe ausführt
+ (z.B. eine E-Mail, die alle Änderungen in dieser Transaktion
+ beschreibt, an Ihre Entwickler-Mailingliste senden), müssen
+ Sie diejenige Version dieser Funktion verwenden, die durch die
+ Bibliothek <filename>libsvn_repos</filename> umschlossen ist
+ und die Auslösung von Hooks beinhaltet – in diesem Fall
+ <function>svn_repos_fs_commit_txn()</function>. (Weitere
+ Informationen zu Subversions Repository-Hooks in <xref
+ linkend="svn.reposadmin.create.hooks" />.)</para>
+<!--
<para>Now let's switch languages. <xref
linkend="svn.developer.usingapi.otherlangs.ex-1" /> is a
sample program that uses Subversion's SWIG Python bindings to
recursively crawl the youngest repository revision, and to
print the various paths reached during the crawl.</para>
+-->
+ <para>Lassen Sie uns nun die Sprachen wechseln. <xref
+ linkend="svn.developer.usingapi.otherlangs.ex-1" /> ist ein
+ Beispielprogramm, das die SWIG-Python-Bindungen von Subversion
+ verwendet, um rekursiv die jüngste Repository-Revision zu
+ traversieren und dabei die zahlreichen Versionspfade
+ auszugeben.</para>
<example id="svn.developer.usingapi.otherlangs.ex-1">
+<!--
<title>Using the Repository layer with Python</title>
+-->
+ <title>Verwendung der Repository-Schicht mit Python</title>
+<!--
<programlisting>
#!/usr/bin/python
@@ -2264,8 +2424,77 @@
# Do the real work.
crawl_youngest(repos_path)
</programlisting>
+-->
+ <programlisting>
+#!/usr/bin/python
+
+"""Durchwandern eines Repositorys mit Ausgabe der Repository-Pfadnamen."""
+
+import sys
+import os.path
+import svn.fs, svn.core, svn.repos
+
+def crawl_filesystem_dir(root, directory):
+ """Rekursives durchwandern von DIRECTORY unterhalb von ROOT im
+ Dateisystem und eine Liste aller Pfade unterhalb von DIRECTORY
+ zurückgeben."""
+
+ # Ausgabe dieses Pfadnamens.
+ print directory + "/"
+
+ # Verzeichniseinträge für DIRECTORY holen.
+ entries = svn.fs.svn_fs_dir_entries(root, directory)
+
+ # Einträge abarbeiten.
+ names = entries.keys()
+ for name in names:
+ # Den vollen Pfadnamen des Eintrags berechnen.
+ full_path = directory + '/' + name
+
+ # Falls der Eintrag ein Verzeichnis ist, recursiv bearbeiten.
+ # Die Rekursion gibt eine Liste mit dem Eintrag und all seiner
+ # Kinder zurück, die der aktuellen Pfadliste hinzugefügt wird.
+ if svn.fs.svn_fs_is_dir(root, full_path):
+ crawl_filesystem_dir(root, full_path)
+ else:
+ # Else it's a file, so print its path here.
+ print full_path
+
+def crawl_youngest(repos_path):
+ """Öffnen des Repositorys bei REPOS_PATH, und rekursives
+ Durchwandern seiner jüngsten Revision."""
+
+ # Öffnen des Repositorys bei REPOS_PATH, und holen einer Referenz
+ # auf sein versioniertes Dateisystem.
+ repos_obj = svn.repos.svn_repos_open(repos_path)
+ fs_obj = svn.repos.svn_repos_fs(repos_obj)
+
+ # Die aktuell jüngste Revision abfragen.
+ youngest_rev = svn.fs.svn_fs_youngest_rev(fs_obj)
+
+ # Ein Wurzelobjekt öffnen, das die jüngste (HEAD) Revision
+ # repräsentiert.
+ root_obj = svn.fs.svn_fs_revision_root(fs_obj, youngest_rev)
+
+ # Rekursiv durchwandern.
+ crawl_filesystem_dir(root_obj, "")
+
+if __name__ == "__main__":
+ # Überprüfung auf korrekten Aufruf.
+ if len(sys.argv) != 2:
+ sys.stderr.write("Verwendung: %s REPOS_PATH\n"
+ % (os.path.basename(sys.argv[0])))
+ sys.exit(1)
+
+ # Den Repository-Pfad kanonisieren.
+ repos_path = svn.core.svn_path_canonicalize(sys.argv[1])
+
+ # Eigentliche Arbeit machen.
+ crawl_youngest(repos_path)
+</programlisting>
</example>
+<!--
<para>This same program in C would need to deal with APR's
memory pool system. But Python handles memory usage
automatically, and Subversion's Python bindings adhere to that
@@ -2279,7 +2508,24 @@
layer) takes care of mapping those custom datatypes into the
native datatypes of the target language. This provides a more
intuitive interface for users of that language.</para>
+-->
+ <para>Das gleiche Programm in C hätte sich noch um das
+ APR-Speicherpoolsystem kümmern müssen. Python jedoch verwaltet
+ den Speicher automatisch, und die Python-Bindungen von
+ Subversion berücksichtigen diese Konvention. In C würden Sie
+ mit angepassten Datentypen (etwa denen aus der APR-Bibliothek)
+ arbeiten, um den Hash mit Einträgen und die Liste der Pfade zu
+ repräsentieren, doch Python verfügt über Hashes
+ (<quote>Dictionarys</quote> genannt) und Listen als eingebaute
+ Datentypen und bietet eine reichhaltige Sammlung aus
+ Funktionen, um mit diesen Datentypen umzugehen. Also übernimmt
+ SWIG (mithilfe einiger Anpassungen in der
+ Sprachbindungsschicht von Subversion) die Abbildung dieser
+ speziellen Datentypen auf die spezifischen Datentypen der
+ Zielsprache. Dies bietet Benutzern dieser Sprache eine
+ intuitivere Schnittstelle.</para>
+<!--
<para>The Subversion Python bindings can be used for working
copy operations, too. In the previous section of this
chapter, we mentioned the <filename>libsvn_client</filename>
@@ -2289,10 +2535,26 @@
example of how that library can be accessed via the SWIG
Python bindings to re-create a scaled-down version of the
<command>svn status</command> command.</para>
+-->
+ <para>Auch für Operationen in der Arbeitskopie können die
+ Python-Bindungen von Subversion verwendet werden. Im
+ vorangegangenen Abschnitt dieses Kapitels erwähnten wir die
+ Schnittstelle <filename>libsvn_client</filename> und ihren
+ Zweck zur Vereinfachung der Erstellung eines
+ Subversion-Clients. <xref
+ linkend="svn.developer.usingapi.otherlangs.ex-2" /> ist ein
+ kurzes Beispiel wie auf diese Bibliothek über die
+ SWIG-Python-Bindungen zugegriffen werden kann, um eine
+ abgespeckte Version des Befehls <command>svn status</command>
+ nachzubauen.</para>
<example id="svn.developer.usingapi.otherlangs.ex-2">
+<!--
<title>A Python status crawler</title>
+-->
+ <title>Status in Python</title>
+<!--
<programlisting>
#!/usr/bin/env python
@@ -2348,8 +2610,8 @@
stream = errorcode and sys.stderr or sys.stdout
stream.write("""Usage: %s OPTIONS WC-PATH
Options:
- --help, -h : Show this usage message
- --verbose, -v : Show all statuses, even uninteresting ones
+ - -help, -h : Show this usage message
+ - -verbose, -v : Show all statuses, even uninteresting ones
""" % (os.path.basename(sys.argv[0])))
sys.exit(errorcode)
@@ -2361,9 +2623,9 @@
usage_and_exit(1)
verbose = 0
for opt, arg in opts:
- if opt in ("-h", "--help"):
+ if opt in ("-h", "- -help"):
usage_and_exit(0)
- if opt in ("-v", "--verbose"):
+ if opt in ("-v", "- -verbose"):
verbose = 1
if len(args) != 1:
usage_and_exit(2)
@@ -2378,8 +2640,97 @@
sys.stderr.write("Error (%d): %s\n" % (e.apr_err, e.message))
sys.exit(1)
</programlisting>
+-->
+ <programlisting>
+#!/usr/bin/env python
+
+"""Durchwandern eines Arbeitskopieverzeichnisses mit Ausgabe von
Statusinformation."""
+
+import sys
+import os.path
+import getopt
+import svn.core, svn.client, svn.wc
+
+def generate_status_code(status):
+ """Übersetzen eines Stauswerts in einen Ein-Zeichen-Statuscode,
+ wobei dieselbe Logik wie beim Subversion-Kommandozeilen-Client
+ verwendet wird."""
+ code_map = { svn.wc.svn_wc_status_none : ' ',
+ svn.wc.svn_wc_status_normal : ' ',
+ svn.wc.svn_wc_status_added : 'A',
+ svn.wc.svn_wc_status_missing : '!',
+ svn.wc.svn_wc_status_incomplete : '!',
+ svn.wc.svn_wc_status_deleted : 'D',
+ svn.wc.svn_wc_status_replaced : 'R',
+ svn.wc.svn_wc_status_modified : 'M',
+ svn.wc.svn_wc_status_merged : 'G',
+ svn.wc.svn_wc_status_conflicted : 'C',
+ svn.wc.svn_wc_status_obstructed : '~',
+ svn.wc.svn_wc_status_ignored : 'I',
+ svn.wc.svn_wc_status_external : 'X',
+ svn.wc.svn_wc_status_unversioned : '?',
+ }
+ return code_map.get(status, '?')
+
+def do_status(wc_path, verbose):
+ # Ein "Staffelholz" für den Client-Kontext erzeugen.
+ ctx = svn.client.svn_client_ctx_t()
+
+ def _status_callback(path, status):
+ """Eine Rückruffunktion für svn_client_status."""
+
+ # Ausgeben des Pfades, ohne den Teil, der sich mit der Wurzel
+ # des zu durchlaufenden Baums überlappt
+ text_status = generate_status_code(status.text_status)
+ prop_status = generate_status_code(status.prop_status)
+ print '%s%s %s' % (text_status, prop_status, path)
+
+ # Das Durchlaufen starten, _status_callback() als Rückruffunktion
+ # verwenden.
+ revision = svn.core.svn_opt_revision_t()
+ revision.type = svn.core.svn_opt_revision_head
+ svn.client.svn_client_status2(wc_path, revision, _status_callback,
+ svn.core.svn_depth_infinity, verbose,
+ 0, 0, 1, ctx)
+
+def usage_and_exit(errorcode):
+ """Ausgabe des Verwendungshinweises und beenden mit ERRORCODE."""
+ stream = errorcode and sys.stderr or sys.stdout
+ stream.write("""Verwendung: %s OPTIONS WC-PATH
+Options:
+ --help, -h : Diesen Hinweis anzeigen
+ --verbose, -v : Zeige alle Status, auch uninteressante
+""" % (os.path.basename(sys.argv[0])))
+ sys.exit(errorcode)
+
+if __name__ == '__main__':
+ # Parse command-line options.
+ try:
+ opts, args = getopt.getopt(sys.argv[1:], "hv", ["help", "verbose"])
+ except getopt.GetoptError:
+ usage_and_exit(1)
+ verbose = 0
+ for opt, arg in opts:
+ if opt in ("-h", "--help"):
+ usage_and_exit(0)
+ if opt in ("-v", "--verbose"):
+ verbose = 1
+ if len(args) != 1:
+ usage_and_exit(2)
+
+ # Repositorypfad kanonisieren.
+ wc_path = svn.core.svn_path_canonicalize(args[0])
+
+ # Eigentliche Arbeit machen.
+ try:
+ do_status(wc_path, verbose)
+ except svn.core.SubversionException, e:
+ sys.stderr.write("Fehler (%d): %s\n" % (e.apr_err, e.message))
+ sys.exit(1)
+</programlisting>
</example>
+<!--
<para>As was the case in <xref
linkend="svn.developer.usingapi.otherlangs.ex-1" />, this
program is pool-free and uses, for the most part, normal
@@ -2396,6 +2747,25 @@
underlying Subversion C library's assertions about such
things, which translates into rather immediate and
unceremonious program abortion.</para>
+-->
+ <para>Wie in <xref
+ linkend="svn.developer.usingapi.otherlangs.ex-1" /> verwendet
+ auch dieses Programm keine Pools und benutzt meist normale
+ Python-Datentypen. Der Aufruf von
+ <function>svn_client_ctx_t()</function> ist irreführend, da
+ die öffentliche API von Subversion keine derartige Funktion
+ hat – das passiert nur da, wo die automatische
+ Spracherzeugung von SWIG eub webig durchscheint (die Funktion
+ ist eine Art Fabrikfunktion für die Python-Version der
+ entsprechenden komplizierten C-Struktur). Beachten Sie auch,
+ dass der an dieses Programm (wie auch beim letzten) übergebene
+ Pfad durch <function>svn_path_canonicalize()</function>
+ gefiltert wird, da ein <emphasis>unterlassen</emphasis> dazu
+ führen kann, dass die Annahmen der darunter liegenden
+ C-Bibliotheken nicht mehtr zutreffen, was wiederum einen
+ ziemlich plötzlichen und ungezwungenen Programmabsturz
+ bedeutet.</para>
+
</sect2>
</sect1>
More information about the svnbook-dev
mailing list