Commits

dgc committed dcc8e10

imported patch series

Comments (0)

Files changed (6)

+# HG changeset patch
+# User David Champion <dgc@uchicago.edu>
+# Date 1298190935 21600
+# Node ID eb64623a0d35c47adecb4912dde6c49663928fdb
+# Parent fa3cea089eb36a30b767fcc3f7591202f5091e5a
+implement automatic properties like svn's [auto-props]
+
+Subversion properties will be set automatically based upon entries in
+.hgrc's [hgsbversion:auto-props] values.  For example, the analogue to
+the example in svn's .subversion/config file is:
+
+[hgsubversion:auto-props]
+**.c = svn:eol-style=native
+**.cpp = svn:eol-style=native
+**.h = svn:eol-style=native
+**.dsp = svn:eol-style=CRLF
+**.dsw = svn:eol-style=CRLF
+**.sh = svn:eol-style=native;svn:executable
+**.txt = svn:eol-style=native
+**.png = svn:mime-type=image/png
+**.jpg = svn:mime-type=image/jpeg
+**/Makefile = svn:eol-style=native
+
+Filename matching uses mercurial.match, so glob:, regexp:, etc prefixes
+are supported with glob: as the implicit default.
+
+diff -r fa3cea089eb3 hgsubversion/pushmod.py
+--- a/hgsubversion/pushmod.py	Sun Feb 20 01:47:38 2011 -0600
++++ b/hgsubversion/pushmod.py	Tue Apr 17 17:31:51 2012 -0500
+@@ -1,4 +1,5 @@
+ from mercurial import util as hgutil
++from mercurial import match as hgmatch
+ 
+ import svnwrap
+ import svnexternals
+@@ -183,6 +184,14 @@
+         if file_data[tf][2] == 'add':
+             if hgutil.binary(file_data[tf][1]):
+                 props.setdefault(tf, {})['svn:mime-type'] = 'application/octet-stream'
++        for pattern, proplist in ui.configitems('hgsubversion:auto-props'):
++            if hgmatch.match(repo.root, '', [pattern])(tf):
++                for prop in proplist.split(';'):
++                    if '=' in prop:
++                        prop, value = prop.split('=', 1)
++                    else:
++                        value = None
++                    props.setdefault(tf, {})[prop] = value
+         if tf in file_data and tf != ntf:
+             file_data[ntf] = file_data[tf]
+             if tf in props:
+# HG changeset patch
+# User David Champion <dgc@uchicago.edu>
+# Date 1298188058 21600
+# Node ID a8b891f531525cd19c00610f9e5f530304991c11
+# Parent  4bbc6bf947f56a92e95a04a27b94a9f72d5482d7
+set svn:mime-type=application/octet-stream when adding binary files.
+
+Previously we only set this on copies of the file that are in branches,
+and regardless of whether adding or modifying.  When the standard trunk,
+branches, etc structure is not used, added files are never tested.
+
+diff -r 4bbc6bf947f5 -r a8b891f53152 hgsubversion/pushmod.py
+--- a/hgsubversion/pushmod.py	Sun Feb 13 20:10:52 2011 +0100
++++ b/hgsubversion/pushmod.py	Sun Feb 20 01:47:38 2011 -0600
+@@ -175,6 +175,9 @@
+ 
+     new_target_files = [svnpath(f) for f in file_data]
+     for tf, ntf in zip(file_data, new_target_files):
++        if file_data[tf][2] == 'add':
++            if hgutil.binary(file_data[tf][1]):
++                props.setdefault(tf, {})['svn:mime-type'] = 'application/octet-stream'
+         if tf in file_data and tf != ntf:
+             file_data[ntf] = file_data[tf]
+             if tf in props:
+# HG changeset patch
+# User David Champion <dgc@uchicago.edu>
+# Date 1298219040 21600
+# Node ID 93ada999960786e6d6e94643323cc70e0510eeae
+# Parent 5a8c3a4d36ad0e850733dfbfc1f3b02d8cce55cc
+add 'hg svn propget' with subversion-compatible output
+
+usage: hg svn propget [-v] [--xml] property-name files
+
+A get_prop() method is implemented in svnwrap for both subvertpy and
+swig backends.
+
+diff -r 5a8c3a4d36ad hgsubversion/svncommands.py
+--- a/hgsubversion/svncommands.py	Sun Feb 20 10:14:47 2011 -0600
++++ b/hgsubversion/svncommands.py	Sun Dec 02 16:44:51 2012 -0600
+@@ -424,6 +424,68 @@
+                         ui.write('    ' + v + '\n')
+ 
+ 
++def propget(ui, repo, args=[], **opts):
++    """get a single subversion property"""
++
++    url = repo.ui.expandpath('default')
++    if len(args) == 0:
++        ui.status('no property name or files selected\n')
++        ui.status('usage: hg svn propget propertyname filename [...]\n');
++        return 1
++    elif len(args) == 1:
++        ui.status('no files selected\n')
++        ui.status('usage: hg svn propget propertyname filename [...]\n');
++        return 2
++    else:
++        name = args[0]
++        args = args[1:]
++
++    svn = svnrepo.svnremoterepo(ui, url).svn
++    meta = repo.svnmeta()
++    hashes = meta.revmap.hashes()
++    parent = util.parentrev(ui, repo, meta, hashes)
++    r, br = hashes[parent.node()]
++    if meta.layout == 'single':
++        branchpath = ''
++    else:
++        branchpath = br and ('branches/%s/' % br) or 'trunk/'
++
++    files = []
++    for pat in args:
++        m = match.match(repo.root, repo.getcwd(), [pat])
++        files += m.files()
++
++    if opts.get('xml'):
++        ui.write('<?xml version="1.0"?>\n')
++        ui.write('<properties>\n')
++        for file in files:
++            svnfile = os.path.join(branchpath, file)
++            props = svn.get_prop(name, svnfile, r).items()
++            if props and len(props):
++                ui.write('<target\n   path="%s">\n' % file)
++                for k, v in props:
++                    ui.write('<property\n   name="%s">%s</property>\n' % (k, v))
++                ui.write('</target>\n')
++        ui.write('</properties>\n')
++
++    else:
++        for file in files:
++            svnfile = os.path.join(branchpath, file)
++            props = svn.get_prop(name, svnfile, r)
++            if props is None or len(props) == 0:
++                continue
++            k = props.keys()[0]
++            v = props[k]
++            if ui.config('ui', 'verbose'):
++                ui.write("Properties on '%s':\n" % svnfile)
++                ui.write('  ' + k + '\n')
++                ui.write('    ' + v + '\n')
++            elif len(files) > 1:
++                ui.write('%s - %s\n' % (svnfile, v))
++            else:
++                ui.write(v + '\n')
++
++
+ def info(ui, repo, **opts):
+     """show Subversion details similar to `svn info'
+     """
+@@ -562,5 +624,6 @@
+     'updateexternals': svnexternals.updateexternals,
+     'verify': verify.verify,
+     'proplist': proplist,
++    'propget': propget,
+ }
+ svn.__doc__ = _helpgen()
+diff -r 5a8c3a4d36ad hgsubversion/svnwrap/subvertpy_wrapper.py
+--- a/hgsubversion/svnwrap/subvertpy_wrapper.py	Sun Feb 20 10:14:47 2011 -0600
++++ b/hgsubversion/svnwrap/subvertpy_wrapper.py	Sun Dec 02 16:44:51 2012 -0600
+@@ -581,3 +581,22 @@
+             return self.svn_url
+         assert path[0] != '/', path
+         return '/'.join((self.svn_url, urllib.quote(path).rstrip('/'),))
++
++    def get_prop(self, name, path, revision):
++        """Get a single property by name on a given revision.
++        """
++        try:
++            # This throws ValueError, 'Unable to parse revision' :(
++            #prop = self.client.propget(name, self.path2url(path), revision)
++            proplist = self.client.proplist(self.path2url(path), revision,
++                                            client.depth_empty)
++            proplist = proplist and proplist[0][1] or {}
++        except SubversionException, e:
++            # Specified path does not exist at this revision
++            if e.args[1] == subvertpy.ERR_NODE_UNKNOWN_KIND:
++                raise IOError(errno.ENOENT,
++                              '%s cannot be found at r%d' % (path, revision))
++            raise
++        if name in proplist:
++            return {name: proplist[name]}
++        return {}
+diff -r 5a8c3a4d36ad hgsubversion/svnwrap/svn_swig_wrapper.py
+--- a/hgsubversion/svnwrap/svn_swig_wrapper.py	Sun Feb 20 10:14:47 2011 -0600
++++ b/hgsubversion/svnwrap/svn_swig_wrapper.py	Sun Dec 02 16:44:51 2012 -0600
+@@ -623,3 +623,20 @@
+             return core.svn_uri_canonicalize(self.svn_url + '/' + path)
+         except AttributeError:
+             return self.svn_url + '/' + urllib.quote(path)
++
++    def get_prop(self, name, path, revision):
++        """Get a single property's value."""
++        self.init_ra_and_client()
++        rev = optrev(revision)
++        rpath = self.path2url(path)
++        try:
++            pl = client.propget2(name, rpath, rev, rev, False,
++                                 self.client_context, self.pool)
++        except SubversionException, e:
++            # Specified path does not exist at this revision
++            if e.apr_err == core.SVN_ERR_NODE_UNKNOWN_KIND:
++                raise IOError(errno.ENOENT, e.args[0])
++            raise
++        if len(pl) == 0:
++          return {}
++        return {name: pl.values()[0]}
+# HG changeset patch
+# User David Champion <dgc@uchicago.edu>
+# Date 1298218487 21600
+# Node ID 889ed1e8384e997f3683c894446d391454d00853
+# Parent 28e9cd60a18f13d597183d2f256f71aad7f9356c
+add 'hg svn proplist' with svn-compatible output and --xml option
+
+diff -r 28e9cd60a18f hgsubversion/__init__.py
+--- a/hgsubversion/__init__.py	Sun Feb 20 02:35:35 2011 -0600
++++ b/hgsubversion/__init__.py	Sun Dec 02 16:43:49 2012 -0600
+@@ -208,6 +208,7 @@
+           ('r', 'rev', '', 'Mercurial revision'),
+           ('', 'unsafe-skip-uuid-check', False,
+            'skip repository uuid check in rebuildmeta'),
++          ('', 'xml', False, 'XML output (for properties)'),
+           ],
+          'hg svn <subcommand> ...',
+          ),
+diff -r 28e9cd60a18f hgsubversion/svncommands.py
+--- a/hgsubversion/svncommands.py	Sun Feb 20 02:35:35 2011 -0600
++++ b/hgsubversion/svncommands.py	Sun Dec 02 16:43:49 2012 -0600
+@@ -10,6 +10,7 @@
+ from mercurial import node
+ from mercurial import util as hgutil
+ from mercurial import error
++from mercurial import match
+ 
+ import maps
+ import svnwrap
+@@ -370,6 +371,58 @@
+ 
+     repo.wopener('.hgignore', 'w').write('\n'.join(ignorelines) + '\n')
+ 
++def proplist(ui, repo, args=[], **opts):
++    """list all subversion properties"""
++
++    url = repo.ui.expandpath('default')
++
++    svn = svnrepo.svnremoterepo(ui, url).svn
++    meta = repo.svnmeta()
++    hashes = meta.revmap.hashes()
++    parent = util.parentrev(ui, repo, meta, hashes)
++    r, br = hashes[parent.node()]
++    if meta.layout == 'single':
++        branchpath = ''
++    else:
++        branchpath = br and ('branches/%s/' % br) or 'trunk/'
++
++    files = []
++    for pat in args:
++        m = match.match(repo.root, repo.getcwd(), [pat])
++        files += m.files()
++
++    if len(files) == 0:
++        ui.status('no files selected\n')
++        ui.status('usage: hg svn proplist filename [...]\n')
++        return 1
++
++    if opts.get('xml'):
++        ui.write('<?xml version="1.0"?>\n')
++        ui.write('<properties>\n')
++        for file in files:
++            svnfile = os.path.join(branchpath, file)
++            props = svn.list_props(svnfile, r).items()
++            if props and len(props):
++                ui.write('<target\n   path="%s">\n' % file)
++                for k, v in props:
++                    if ui.config('ui', 'verbose'):
++                        ui.write('<property\n   name="%s">%s</property>\n' % (k, v))
++                    else:
++                        ui.write('<property\n   name="%s"/>\n' % (k))
++                ui.write('</target>\n')
++        ui.write('</properties>\n')
++
++    else:
++        for file in files:
++            svnfile = os.path.join(branchpath, file)
++            props = svn.list_props(svnfile, r).items()
++            if props and len(props):
++                ui.write("Properties on '%s':\n" % svnfile)
++                for k, v in svn.list_props(svnfile, r).items():
++                    ui.write('  ' + k + '\n')
++                    if ui.config('ui', 'verbose'):
++                        ui.write('    ' + v + '\n')
++
+ 
+ def info(ui, repo, **opts):
+     """show Subversion details similar to `svn info'
+@@ -508,5 +561,6 @@
+     'rebuildmeta': rebuildmeta,
+     'updateexternals': svnexternals.updateexternals,
+     'verify': verify.verify,
++    'proplist': proplist,
+ }
+ svn.__doc__ = _helpgen()
+# HG changeset patch
+# Parent f555406b1269089fa98f77265aeef36a5fb082d7
+add 'hg svn propset' with subversion-compatible output
+
+usage: hg svn propset [-v] [--xml] property-name files
+
+A set_prop() method is implemented in svnwrap for both subvertpy and
+swig backends.
+
+diff -r f555406b1269 hgsubversion/svncommands.py
+--- a/hgsubversion/svncommands.py	Sun Feb 20 10:24:00 2011 -0600
++++ b/hgsubversion/svncommands.py	Sun Dec 02 16:44:59 2012 -0600
+@@ -486,6 +486,58 @@
+                 ui.write(v + '\n')
+ 
+ 
++def propset(ui, repo, args=[], **opts):
++    """set a single subversion property
++    """
++
++    url = repo.ui.expandpath('default')
++    if len(args) == 0:
++        ui.status('no property name or files selected\n')
++        ui.status('usage: hg svn propset propertyname value filename [...]\n');
++        return 1
++    elif len(args) == 1:
++        ui.status('no value or files selected\n')
++        ui.status('usage: hg svn propset propertyname value filename [...]\n');
++        return 2
++    elif len(args) == 2:
++        ui.status('no files selected\n')
++        ui.status('usage: hg svn propset propertyname value filename [...]\n');
++        return 3
++
++    name = args[0]
++    value = args[1]
++    args = args[2:]
++
++    svn = svnrepo.svnremoterepo(ui, url).svn
++    meta = repo.svnmeta()
++    hashes = meta.revmap.hashes()
++    parent = util.parentrev(ui, repo, meta, hashes)
++    r, br = hashes[parent.node()]
++    if meta.layout == 'single':
++        branchpath = ''
++    else:
++        branchpath = br and ('branches/%s/' % br) or 'trunk/'
++
++    files = []
++    for pat in args:
++        m = match.match(repo.root, repo.getcwd(), [pat])
++        files += m.files()
++
++    maxrev = 0
++    for file in files:
++        svnfile = os.path.join(branchpath, file)
++        rev, date, committer = svn.set_prop(name, value, svnfile, r)
++
++        if ui.config('ui', 'verbose'):
++            ui.write("Pushed r%d [%s]: %s\n" % (rev, committer, file))
++
++        maxrev = max(maxrev, rev)
++
++    if ui.config('ui', 'verbose'):
++        ui.write("Updating to r%d\n" % maxrev)
++    return update(ui, (maxrev,), repo)
++
++
+ def info(ui, repo, **opts):
+     """show Subversion details similar to `svn info'
+     """
+@@ -625,5 +677,6 @@
+     'verify': verify.verify,
+     'proplist': proplist,
+     'propget': propget,
++    'propset': propset,
+ }
+ svn.__doc__ = _helpgen()
+diff -r f555406b1269 hgsubversion/svnwrap/subvertpy_wrapper.py
+--- a/hgsubversion/svnwrap/subvertpy_wrapper.py	Sun Feb 20 10:24:00 2011 -0600
++++ b/hgsubversion/svnwrap/subvertpy_wrapper.py	Sun Dec 02 16:44:59 2012 -0600
+@@ -600,3 +600,21 @@
+         if name in proplist:
+             return {name: proplist[name]}
+         return {}
++
++    def set_prop(self, name, value, path, revision):
++        try:
++            # Not all arguments to client.propset are documented in subvertpy.
++            # A revision is required, but you must read the source to find
++            # this syntax:
++            # propset(name, value, path, recurse, skip_checks, revision)
++            rev, date, committer = self.client.propset(name, value, self.path2url(path), True, False, revision)
++
++            # A new revision has been created upstream in Subversion, but has
++            # not been reflected in Mercurial. Return the metadata for this
++            # revision to the caller.
++            return rev, date, committer
++        except SubversionException, e:
++            if e.args[1] == subvertpy.ERR_FS_NOT_FOUND:
++                raise IOError(errno.ENOENT,
++                              '%s cannot be found at r%d' % (path, revision))
++            raise IOError(errno.EINVAL, e.args[0])
-# Placed by Bitbucket
+binary-prop
+auto-props
+proplist
+propget
+propset