Here's a revision of my original suggestion that handles deep branch
directories nicely. Skip down to 'we will expand arguments'; not much above
that has changed.
#!/usr/bin/python
# This is a wrapper for 'svn', the Subversion command-line client,
# that implements a shorthand for referring to files in repository
# trees parallel to the tree in a working copy.
#
# Two directories are "parallel" if it makes sense to compare
# corresponding files in them. A trunk and a branch or tag taken from
# that trunk are parallel, as are two branches from the same trunk.
#
# Parallel directories often have a common ancestor in the repository;
# when this is so, comparison can recognize renamings and copies. But
# directories with no common ancestor can sometimes be parallel, too;
# for example, if you repeatedly import external releases of some
# tree, successive releases can often be usefully treated as parallel
# even though they have no common ancestor in the repository. In this
# case, saying that A and B are parallel simply means that for most
# any relative path R, it makes sense to compare A/R and B/R.
#
# The Subversion book recommends organizing a repository by having a
# 'trunk' directory for the main line of development, and sibling
# 'branches' and 'tags' directories whose contents are trees parallel
# to 'trunk'. This makes it easy to do things in your Subversion
# repository that resemble CVS's 'tag' and 'branch' operations.
#
# However, this organization is clumsy to work with. Given a working
# copy based on a branch, say, you will often want to compare its
# contents with the corresponding files or directories on the trunk;
# similarly for trunk-to-branch comparisons. Without some further
# structure, you must type out the full URL of the parallel directory
# in the repository:
#
# $ svn info
# ...
# URL: http://svn.red-bean.com/repos/project/branch/crazy-idea/doc
# ...
# $ cd doc
# $ svn diff --old . --new http://svn.red-bean.com/repos/project/trunk\
# /doc
#
# (I can't even fit the command on one line in this documentation, and
# I'm not using unrealistic filenames or hostnames.)
#
# There is a lot of redundancy in this command. In the common case:
#
# - We make intra-repository comparisons. You shouldn't need to
# re-type the host name and repository path (here,
# 'http://svn.red-bean.com/repos').
#
# - We make intra-project comparisons. You shouldn't need to re-type
# the project name (here, 'project').
#
# - We compare parallel directories within parallel trees. You
# shouldn't need to re-type the subdirectory (here, 'doc'), since
# it's apparent from your working directory.
#
# Omitting these pieces from the diff command above, we get:
#
# $ svn diff --old . --new trunk
#
# But we still need some indication that 'trunk' is not a local
# relative filename; the presence of the leading URL scheme 'http:'
# used to do this, but that's gone now. For the sake of argument,
# we'll put a '+' in its place:
#
# $ svn diff --old . --new +trunk
#
# (You could argue that --old and --new are redundant, too, but those
# resolve ambiguities in the meaning of the 'svn diff' command
# unrelated to the repository structure. We'll leave those in place
# for now.)
#
# To compare a trunk-based working copy with a particular branch, one
# should be able to type something like:
#
# $ svn diff --old . --new +branch/crazy-idea
#
# There are few more rights we expect:
#
# - You shouldn't need to explain to Subversion the relative positions
# of your 'trunk', 'tags', and 'branches' directories. These were
# established when you first arranged your repository, and are
# essentially static.
#
# - You shouldn't have to distribute some sort of abbreviation list to
# your users to make all this work. You should be able to put the
# needed information in the repository, and always have the current
# data available automatically. (That is, config file tweaking is
# out.)
#
# This wrapper implements the above syntax, based on guidance from
# properties set on directories in the repository. If you use 'svnp'
# instead of 'svn', we will expand arguments of the form
# '+KIND[/SUBDIR]' (where KIND contains no slashes) according the
# following rules, and pass the resulting argument list on to 'svn':
#
# - First, assume that our current working directory is based on the
# repository path BASE.
#
# - Find the lowest parent of BASE that has a property named
# 'svnp:parallel-root'. (The value of this property must be '.';
# don't worry about that for now.) This marks the root of a tree
# which has parallel trees elsewhere. Call this directory PROOT.
# Since it's a parent of BASE, there's some RELBASE such that
# PROOT/RELBASE is BASE. PROOT may be BASE itself, in which case
# RELBASE is '.'.
#
# - Find the lowest parent of RELBASE that has a property named
# 'svnp:parallel:KIND'. Its value should be an absolute repository
# path; call that KINDROOT.
#
# - Replace the '+KIND[/SUBDIR]' argument with the string
# 'KINDROOT/SUBDIR/RELBASE'. (Use '.' for SUBDIR if it's absent, or
# just omit that path element altogether.)
#
# So, the root of every parallel tree needs to be marked with
# 'svnp:parallel-root', and at some parent directory common to all
# your parallel trees you need a bunch of 'svnp:parallel:KIND'
# properties.
#
# Note that, if you place the 'svnp:parallel-root' property at the top
# of your trunk directory, any branch or tag you create by copying the
# trunk off to someplace in a 'branches' or 'tags' directory will
# automatically have its 'svnp:parallel-root' properties set properly.
#
# In our example, the following directories would need the following
# properties:
#
# /project:
# svnp:parallel:trunk = /project/trunk
# svnp:parallel:branch = /project/branches
# /project/branch/crazy-idea:
# svnp:parallel-root = .
# /project/trunk/crazy-idea:
# svnp:parallel-root = .
#
# So, to expand '+trunk' in a working copy based on
# /project/branch/crazy-idea/doc:
#
# - We take BASE to be '/project/branch/crazy-idea/doc', KIND to be
# 'trunk', and SUBDIR (since it's missing) to be '.'.
#
# - We walk up and find the 'svn:parallel-root' property on
# '/project/branch/crazy-idea'; that's our PROOT, and our RELBASE is
# 'doc'.
#
# - We walk up further and find the 'svn:parallel:trunk' property on
# '/project'; its value is '/project/trunk', so that's our KINDROOT.
#
# - The path we substitute for '+trunk' is 'KINDROOT/SUBDIR/RELBASE',
# or '/project/trunk/./doc'. This is the directory corresponding to
# '/project/branch/crazy-idea/doc' on the trunk.
#
# One thing to notice here is that this makes no assumptions about the
# arrangement of the tree above the marked parallel roots, other than
# that one can find an svnp:parallel:KIND property somewhere up there.
# You could have a branch named /project/branches/releases/1.3 ---
# where the branch name itself contains slashes --- and everything
# works fine.
Received on Fri Nov 11 01:26:54 2005