[svn.haxx.se] · SVN Dev · SVN Users · SVN Org · TSVN Dev · TSVN Users · Subclipse Dev · Subclipse Users · this month's index

Re: usage of svn_wc_diff

From: Daniel Näslund <daniel_at_longitudo.com>
Date: Thu, 9 Dec 2010 22:25:19 +0100

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()
Received on 2010-12-09 22:26:08 CET

This is an archived mail posted to the Subversion Users mailing list.

This site is subject to the Apache Privacy Policy and the Apache Public Forum Archive Policy.