On Wed, Dec 08, 2010 at 11:59:16PM -0800, JamieEchlin wrote: > > Daniel, thank you very much for that, that's incredibly helpful. It > definitely gives me somewhere to start.
Hope, you'll be able to solve your problem. Here's a script that's a bit more clean than the previous one. I haven't tested it much at all but it appears to be able to display added, modified and deleted properties set on both dirs and files. The '###' lines represents TODO's. Daniel #!/usr/bin/env python import sys import os import getopt try: my_getopt = getopt.gnu_getop except AttributeError: my_getopt = getopt.getopt import svn.wc import svn.core EQUAL_STRING = ( "========================================================================") UNDER_STRING = ( "------------------------------------------------------------------------") def usage_and_exit(retval): if retval: out = sys.stderr else: out = sys.stdout out.write("""Usage: %s TARGET Options: --help (-h) : Show this usage message Display the difference between the BASE revision and the changes made in the working copy. This is just a very basic example script. It doesn't properly handle revisions or the problems involved in adjusting diff labels. At present it always prints one diff header for each file or dir that has property changes. If we'd check for more than property changes - we need to keep track of when we have printed a diff header. Some files can have both property and text changes. """ % (os.path.basename(sys.argv[0]))) sys.exit(retval) def print_diff_headers(path, rev): print "Index: %s" % path print EQUAL_STRING print "--- %s\t (revision %d)" % (path, rev) ### Here we'd check rev2 against some constant that represents the "working ### copy revision" - it's probably -1. print "+++ %s\t (working copy)" % path def props_changed(path, propchanges, originalprops): print "\nProperty changes on %s" % path print UNDER_STRING for (name, value) in propchanges.items(): if originalprops is not None and name in originalprops: original_value = originalprops[name] else: original_value = None if not (value or original_value) or (value == original_value): continue if not original_value: print "Added: %s" % name elif not value: print "Deleted %s" % name else: print "Modified %s" % name ### We're ignoring special handling of svn:mergeinfo for now. ### Do we have to handle utf8 conversions here? if original_value is not None: print " - %s" % original_value if value is not None: print " + %s" % value def content_changed(path, tmpfile1, tmpfile2, rev1, rev2, mimetype1, mimetype2): ### TODO: Write this one pass def has_regular_prop_changes(props): if not props: return False ### For some reason, svn_categorize_props() segfaults ### Shouldn't we only pass the regular_props to props_changed? for (name, value) in props.items(): (kind, unused_prefix_len) = svn.core.svn_property_kind(name) if kind == svn.core.svn_prop_regular_kind: return True return False class Callback(svn.wc.DiffCallbacks2): def file_changed(self, adm_access, path, tmpfile1, tmpfile2, rev1, rev2, mimetype1, mimetype2, propchanges, originalprops): if has_regular_prop_changes(propchanges): print_diff_headers(path, rev1) props_changed(path, propchanges, originalprops) return (svn.wc.notify_state_unknown, svn.wc.notify_state_unknown) def file_added(self, adm_access, path, tmpfile1, tmpfile2, rev1, rev2, mimetype1, mimetype2, propchanges, originalprops): return (svn.wc.notify_state_unknown, svn.wc.notify_state_unknown) def file_deleted(self, adm_access, path, tmpfile1, tmpfile2, mimetype1, mimetype2, originalprops): return svn.wc.notify_state_unknown def dir_added(self, adm_access, path, rev): return svn.wc.notify_state_unknown def dir_deleted(self, adm_access, path): return svn.wc.notify_state_unknown def dir_props_changed(self, adm_access, path, propchanges, originalprops): if has_regular_prop_changes(propchanges): ### How fetch the revision here? rev = 42 print_diff_headers(path, rev) props_changed(path, propchanges, originalprops) return svn.wc.notify_state_unknown def main(): diff_callbacks = Callback() depth = svn.core.svn_depth_infinity # Parse the options optlist, args = my_getopt(sys.argv[1:], "h", ['help']) for opt, arg in optlist: if opt == '--help' or opt == '-h': usage_and_exit(1) if len(args) != 1: usage_and_exit(1) if os.path.isdir(args[0]): target = '' wc_dir = args[0] else: target = os.path.basename(args[0]) wc_dir = os.path.dirname(args[0]) try: # Subversion wants all paths to be canonicalised, that involves # collapsing redundant "/./" elements, removing multiple adjacent # separator characters and removing trailing separator characters. canon_wc_dir = svn.core.svn_path_canonicalize(wc_dir) canon_target = svn.core.svn_path_canonicalize(target) # The C code that Subversion consists of, uses apr pools for allocation of # memory. The idea is that the pools in the swig bindings are # (mostly) optional and auto-managed in accordance with the lifetime # of the python objects themselves. They are needed in the places # I've used them, but later releases may not need them. pool = svn.core.Pool() # The wc, of svn_wc_adm_access_t type, is a data structure for # coordinating the access to the working copy administrative area # (the .svn folders). It is associated with the containing parent # dir. I find the adm_access approach a bit messy - you can specify # how far you wanna lock and multiple access_batons can form sets. # The 1.7 release will introduce a more intuitive way of accessing # the wc administrative area. wc = svn.wc.adm_open3(None, canon_wc_dir, True, # write_lock -1, # levels_to_lock, None) svn.wc.svn_wc_diff4(wc, canon_target, diff_callbacks, depth, False, None, pool) except svn.core.SubversionException, ex: sys.stderr.write("ERROR: %s\n" % ex.message) if __name__ == '__main__': main()