[Svnmerge] Fwd: svn commit: r18701 - trunk/contrib/client-side
David James
djames at collab.net
Fri Mar 3 17:02:27 PST 2006
---------- Forwarded message ----------
From: djames at tigris.org <djames at tigris.org>
Date: Mar 3, 2006 10:37 AM
Subject: svn commit: r18701 - trunk/contrib/client-side
To: svn at subversion.tigris.org
Author: djames
Date: Fri Mar 3 12:37:43 2006
New Revision: 18701
Modified:
trunk/contrib/client-side/svnmerge.py
Log:
Refactor svnmerge.py to cache logs, mergeprops and blockprops when
bidirectional support is enabled. This refactoring helps prepare us
for efficiently eliminating spurious property conflicts.
* contrib/client-side/svnmerge.py
(logs, mergeprops, blockprops): New variables. Contains cached
versions of the logs, merge properties, and block properties for
each URL.
(RevisionLog, VersionedProperty): New classes.
(find_changes): Remove function. Replaced with the RevisionLog and
VersionedProperty classes.
(analyze_head_revs): Use RevisionLog and VersionedProperty classes
in place of find_changes.
(main): Clear logs, mergeprops, and blockprops.
Modified: trunk/contrib/client-side/svnmerge.py
Url: http://svn.collab.net/viewcvs/svn/trunk/contrib/client-side/svnmerge.py?rev=18701&p1=trunk/contrib/client-side/svnmerge.py&p2=trunk/contrib/client-side/svnmerge.py&r1=18700&r2=18701
==============================================================================
--- trunk/contrib/client-side/svnmerge.py (original)
+++ trunk/contrib/client-side/svnmerge.py Fri Mar 3 12:37:43 2006
@@ -52,6 +52,7 @@
# - Add "svnmerge avail -R": show logs in reverse order
import sys, os, getopt, re, types, popen2, tempfile
+from bisect import bisect
NAME = "svnmerge"
if not hasattr(sys, "version_info") or sys.version_info < (2, 0):
@@ -139,6 +140,9 @@
"block_prop": NAME + "-blocked",
"commit_verbose": True,
}
+logs = {}
+mergeprops = {}
+blockprops = {}
def console_width():
"""Get the width of the console screen (if any)."""
@@ -258,6 +262,98 @@
if len(L) > 7 and L[7] == '*':
error('"%s" is not up to date; please "svn update" first' % dir)
+class RevisionLog:
+ """
+ A log of the revisions which affected a given URL between two
+ revisions.
+ """
+
+ def __init__(self, url, begin, end, find_propchanges=False):
+ """
+ Create a new RevisionLog object, which stores, in self.revs, a list
+ of the revisions which affected the specified URL between begin and
+ end. If find_propchanges is True, self.propchange_revs will contain a
+ list of the URLs which changed properties directly on the specified
+ URL. URL must be the URL for a directory in the repository.
+ """
+
+ # Look for revisions
+ revision_re = re.compile(r"^r(\d+)")
+
+ # Look for changes which contain merge tracking information
+ rlpath = url_to_rlpath(url)
+ srcdir_change_re = re.compile(r"\s*M\s+%s\s+$" % re.escape(rlpath))
+
+ # Setup the log options (--quiet, so we don't show log messages)
+ log_opts = '--quiet -r%s:%s "%s"' % (begin, end, url)
+ if find_propchanges:
+ # The --verbose flag lets us grab merge tracking information
+ # by looking at propchanges
+ log_opts = "--verbose " + log_opts
+
+ # Read the log to look for revision numbers and merge-tracking info
+ self.revs = []
+ self.propchange_revs = []
+ for line in launchsvn("log %s" % log_opts):
+ m = revision_re.match(line)
+ if m:
+ rev = int(m.groups()[0])
+ self.revs.append(rev)
+ elif srcdir_change_re.match(line):
+ self.propchange_revs.append(rev)
+
+class VersionedProperty:
+ """A read-only, cached view of a versioned property"""
+
+ def __init__(self, url, name):
+ """View the history of a versioned property at URL with name"""
+ self.url = url
+ self.name = name
+ self.revs = [0]
+ self.values = [None]
+
+ def load(self, log):
+ """
+ Load the history of property changes from the specified
+ RevisionLog object.
+ """
+
+ old_value = None
+ for rev in log.propchange_revs:
+ new_value = self.raw_get(rev)
+ if new_value != old_value:
+ self.revs.append(rev)
+ self.values.append(new_value)
+ new_value = old_value
+
+ if log.revs:
+ self.revs.append(log.revs[-1])
+ self.values.append(None)
+
+ def raw_get(self, rev=None):
+ """
+ Get the property at revision 'rev'. If rev is not specified, get
+ the property at 'head'.
+ """
+ return get_revlist_prop(self.url, self.name, rev)
+
+ def get(self, rev=None):
+ """
+ Get the property at revision 'rev'. If rev is not specified, get
+ the property at 'head'.
+ """
+ if rev is not None:
+ i = bisect(self.revs, rev) - 1
+ if self.values[i] is not None:
+ return self.values[i]
+ return self.raw_get(rev)
+
+ def keys(self):
+ """
+ Get a list of the revisions in which this property changed.
+ """
+ return self.revs[1:-1]
+
class RevisionSet:
"""
A set of revisions, held in dictionary form for easy manipulation. If we
@@ -609,44 +705,6 @@
(opts["prop"], format_merge_props(fixed), branch_dir)
sys.exit(1)
-def find_changes(url, begin, end, find_merges=False):
- """Return a list of the revisions which affect the specified URL
- between begin and end. If find_merges is True, also return a
- dictionary mapping each revision number to its associated
- merge properties. Returns a tuple (revs, merges)."""
-
- # Look for revisions
- revision_re = re.compile(r"^r(\d+)")
-
- # Look for changes which contain merge tracking information
- rlpath = url_to_rlpath(url)
- srcdir_change_re = re.compile(r"\s*M\s+%s\s+$" % re.escape(rlpath))
-
- # Setup the log options (--quiet, so we don't show log messages)
- log_opts = '--quiet -r%s:%s "%s"' % (begin, end, url)
- if find_merges:
- # The --verbose flag lets us grab merge tracking information
- # by looking at propchanges
- log_opts = "--verbose " + log_opts
-
- # Read the log to look for revision numbers and merge-tracking info
- revs = []
- merges = {}
- previous_merge_props = None
- for line in launchsvn("log %s" % log_opts):
- m = revision_re.match(line)
- if m:
- rev = int(m.groups()[0])
- revs.append(rev)
- elif srcdir_change_re.match(line):
- merge_props = get_revlist_prop(url, opts["prop"], rev)
- if merge_props != previous_merge_props:
- merges[rev] = merge_props
- previous_merge_props = merge_props
-
- return revs, merges
-
-
def analyze_revs(target_dir, url, begin=1, end=None,
find_reflected=False):
"""For the source of the merges in the head url being merged into
@@ -671,8 +729,12 @@
if long(begin) > long(end):
return RevisionSet(""), RevisionSet(""), RevisionSet("")
- revs, merges = find_changes(url, begin, end, find_reflected)
- revs = RevisionSet(",".join(map(str,revs)))
+ logs[url] = RevisionLog(url, begin, end, find_reflected)
+ mergeprops[url] = VersionedProperty(url, opts["prop"])
+ mergeprops[url].load(logs[url])
+ blockprops[url] = VersionedProperty(url, opts["block_prop"])
+ blockprops[url].load(logs[url])
+ revs = RevisionSet(",".join(map(str,logs[url].revs)))
if end == "HEAD":
# If end is not provided, we do not know which is the latest revision
@@ -684,15 +746,14 @@
reflected_revs = []
if find_reflected:
- merge_revs = merges.keys()
- merge_revs.sort()
-
+ mergeinfo = mergeprops[url]
+
report("checking for reflected changes in %d revision(s)"
- % len(merge_revs))
+ % len(mergeinfo.keys()))
old_revs = None
- for rev in merge_revs:
- new_revs = merges[rev].get(target_dir)
+ for rev in mergeinfo.keys():
+ new_revs = mergeinfo.get(rev).get(target_dir)
if new_revs != old_revs:
reflected_revs.append("%s" % rev)
old_revs = new_revs
@@ -1378,6 +1439,9 @@
# Initialize default options
opts = default_opts.copy()
+ logs.clear()
+ mergeprops.clear()
+ blockprops.clear()
optsparser = CommandOpts(global_opts, common_opts, command_table,
version="%%prog r%s\n modified: %s\n\n"
---------------------------------------------------------------------
To unsubscribe, e-mail: svn-unsubscribe at subversion.tigris.org
For additional commands, e-mail: svn-help at subversion.tigris.org
--
David James -- http://www.cs.toronto.edu/~james
More information about the Svnmerge
mailing list