Turns out that even in its unmaintained state, tailor is the best option
for svn-to-svn copies out there, if you don't have svnadmin access to
the destination... I did a recent project where I massaged the dump from
the source (the key bit was the Lump-handling code from svndumpfilter,
but replace the broken prop-handling there), loaded it into a scratch
repo, and then tailor that into the destination.
Problem was that this (like git-svn and some others) ignores properties
entirely. Since svn:keywords and svn:eol-style (among others) change
what the content is checked out as, that wasn't good enough...
The resulting hook is kind of evil, and incomplete, but useful enough
that I thought I'd share it with other tailor users. The key bit of
evil is monkey-patching SvnWorkingDir._commit to run a list of
just-before-commit jobs -- before-commit is actually *too* soon, we need
to apply properties after all svn "add" and "mv" operations have been
done, but before the commit itself. (See force_decorate_once below.)
The "incomplete" bits include
* no support for *deletion* of properties - that would require
looking at the destination tree and seeing what props are present
only on the destination files/dirs (and handling renames within the same
commit sanely.) In my use case, the only deletion was a typo that
didn't have any impact on the files, so deleting it by hand
afterwards was acceptable.
* assumes that you're using svn on both sides - which is why this
isn't suitable as a *patch*, only as a config-file hook (though
adding a new just-before-commit-hook would be a useful patch, which
would simplify this code.)
* the svn hooks are just ad-hoc Popen calls; at very least they
should use --xml to avoid quoting issues (but I knew in advance,
from the dump, that there had never been any terribly interesting
characters in prop names or values.)
I think this gives tailor a bit of a leg up over git-svn (which is
otherwise the "runner-up" in the comparison among the tools...)
#!/usr/bin/env tailor
# -*- python -*-
"""
...
[project]
...
before-commit = before
...
"""
def svn_proplist(path):
from subprocess import Popen, PIPE
svn = Popen(["svn", "proplist", "-q", path], stdout=PIPE)
props, _ = svn.communicate()
if svn.wait() != 0:
sys.exit("proplist failed with %s" % svn.returncode)
return [prop.strip() for prop in props.split()]
def svn_propget(path, propname):
from subprocess import Popen, PIPE
svn = Popen(["svn", "propget", propname, path], stdout=PIPE)
propvalue, _ = svn.communicate()
if svn.wait() != 0:
sys.exit("propget failed with %s" % svn.returncode)
assert propvalue.endswith("\n")
propvalue = propvalue[:-1] # strip only the newline added by propget itself
# we *could* use --xml for this, but the set of values we actually have is
pretty limited
return propvalue
def svn_propset(path, propname, propvalue):
from subprocess import check_call
check_call(["svn", "propset", propname, propvalue, path])
# actually we want a list of things to do pre-commit, and then make
repository.svn.SvnWorkingDir._commit do them
def force_decorate_once(wd):
if hasattr(wd.__class__, "_orig_commit"):
return
wd.__class__._orig_commit = wd.__class__._commit
def wrapper(self, *args, **kwargs):
for work in self.work_for_this_rev:
work()
self._orig_commit(*args, **kwargs)
wd.__class__._commit = wrapper
def before(wd, changeset):
import sys, os
import functools
# the runnable-config-file hack does something weird/wrong with scoping,
but this works around it
global svn_proplist, svn_propget, svn_propset, force_decorate_once
proj = wd.repository.projectref()
repo_root = wd.repository.rootdir
srcdir = proj.source.subdir
targdir = proj.target.subdir
work_for_this_rev = []
force_decorate_once(wd)
for cs_entry in changeset.entries:
if cs_entry.action_kind != cs_entry.DELETED:
changed_src = os.path.join(repo_root, srcdir, cs_entry.name)
changed_targ = os.path.join(repo_root, targdir, cs_entry.name)
for propname in svn_proplist(changed_src):
propvalue = svn_propget(changed_src, propname)
work_for_this_rev.append(functools.partial(svn_propset,
changed_targ, propname, propvalue))
setattr(wd, "work_for_this_rev", work_for_this_rev)
return True
_______________________________________________
Tailor mailing list
[email protected]
http://lists.zooko.com/mailman/listinfo/tailor