Today I got tired of going through a set of subdirectories that all are svn:externals and pasting the urls into an EXTERNALS.txt file. So I wrote a script to do it instead, using pysvn.
It preserves comments in an EXTERNAL.txt file (and the file name if it’s called something else), but stacks the comments in the beginning. It also will store the revision number you used if there has been modifications, so do an svn up first. I realize there is a better way to check that then the one I use, and I just realized it does not actually add the EXTERNALS.txt to the repository, but those improvements are left as an exercise to the reader.
)
Here ya go:
import os
import tempfile
import pysvn
externals_comment = """# If you want to change externals, modify the relevant line in this
# file. Afterwards, run:
# svn propset svn:externals -F EXTERNALS.txt .
"""
client = pysvn.Client()
tempdir = tempfile.mkdtemp()
repo_revisions = {}
externals = []
filenames = os.listdir('.')
for filename in filenames:
if os.path.isdir(filename):
info = client.info(filename)
if info is None:
continue
repo = info.repos
if not repo in repo_revisions.keys():
# Get the last revision for the used repository:
repoinfo = client.info2(info.url, recurse=False)
repo_revisions[repo] = repoinfo[0][1].rev
if repo_revisions[repo] != info.revision:
# Check if they differ:
diff = client.diff(tempdir, info.url, info.revision,
info.url, repo_revisions[repo])
if diff:
use_revision = info.revision.number
else:
use_revision = None
externals.append((filename, use_revision, info.url))
# OK, so lets see if there is an EXTERNALS.txt file:
externals_filename = None
for filename in ('svn:externals', 'externals.txt', 'EXTERNALS.txt'):
if os.path.exists(filename):
externals_filename = filename
if externals_filename is not None:
# Suck in the comments:
text = open(externals_filename).readlines()
text = [x for x in text if x.strip().startswith('#') or not x.strip()]
externals_comment = ''.join(text)
else:
externals_filename = 'EXTERNALS.txt'
external_text = externals_comment
for dir, revision, url in externals:
if revision is not None:
external_text += '%s -r%s %sn' % (dir, revision, url)
else:
external_text += '%s %sn' % (dir, url)
open(externals_filename, 'w').write(external_text)
client.propset('svn:externals', external_text, '.')

February 27, 2008 at 23:09
You might also consider checking out externalator (http://pypi.python.org/pypi/externalator/0.7.0), a tool developed by one of my TOPP cohorts which gives you a command line tool for managing svn bundles.
February 27, 2008 at 23:29
Sure. Could you maybe have a description on pypi that tells me a little about it and hwat it does? “svn bundle manager” is not exactly very exhaustive.
March 7, 2008 at 6:51
Er, yeah, I guess there’s not much info there. I’ll pass your request on to the author.
Even the README is a bit incomplete, but it does have some simple usage information: http://trac.openplans.org/openplans/browser/standalone/externalator/trunk/README.txt.
The short story is that it’s a command line utility that can be used inside an svn bundle checkout, to examine, add, remove, or freeze (i.e. peg to the current revision) the bundle’s externals. It negates the need for an EXTERNALS.txt file altogether.
March 7, 2008 at 6:52
urgh… here’s the link above w/o the trailing dot:
http://trac.openplans.org/openplans/browser/standalone/externalator/trunk/README.txt
March 8, 2008 at 11:58
Hmm, that’s interesting. That’s one part of the functionality I need for a release manager.
http://regebro.wordpress.com/2008/02/15/my-release-software-requirements/
March 8, 2008 at 12:02
And on another note, I notice that this, just as bundleman, handles svn not by PySVN or similar, but by calling svn commands as processes and massaging the output.
Is there a good reason for that?