aboutsummaryrefslogtreecommitdiffstats
path: root/libbe/storage/vcs
diff options
context:
space:
mode:
authorW. Trevor King <wking@drexel.edu>2009-12-13 06:19:23 -0500
committerW. Trevor King <wking@drexel.edu>2009-12-13 06:19:23 -0500
commit4d057dab603f42ec40b911dbee6792dcf107bd14 (patch)
tree9a73459aa160e3c96f4893b132543f412ca6e97f /libbe/storage/vcs
parentdff6bd9bf89ca80e2265696a478e540476718c9c (diff)
downloadbugseverywhere-4d057dab603f42ec40b911dbee6792dcf107bd14.tar.gz
Converted libbe.storage.vcs.base to new Storage format.
Diffstat (limited to 'libbe/storage/vcs')
-rw-r--r--libbe/storage/vcs/__init__.py10
-rw-r--r--libbe/storage/vcs/base.py1105
-rw-r--r--libbe/storage/vcs/util/upgrade.py246
3 files changed, 521 insertions, 840 deletions
diff --git a/libbe/storage/vcs/__init__.py b/libbe/storage/vcs/__init__.py
new file mode 100644
index 0000000..ddfb00a
--- /dev/null
+++ b/libbe/storage/vcs/__init__.py
@@ -0,0 +1,10 @@
+# Copyright
+
+import base
+
+set_preferred_vcs = base.set_preferred_vcs
+vcs_by_name = base.vcs_by_name
+detect_vcs = base.detect_vcs
+installed_vcs = base.installed_vcs
+
+__all__ = [set_preferred_vcs, vcs_by_name, detect_vcs, installed_vcs]
diff --git a/libbe/storage/vcs/base.py b/libbe/storage/vcs/base.py
index f8b0727..8c0ecf5 100644
--- a/libbe/storage/vcs/base.py
+++ b/libbe/storage/vcs/base.py
@@ -29,20 +29,23 @@ import codecs
import os
import os.path
import re
-from socket import gethostname
import shutil
import sys
import tempfile
import libbe
-from utility import Dir, search_parent_directories
-from subproc import CommandError, invoke
-from plugin import get_plugin
+import libbe.storage.base
+import libbe.util.encoding
+from libbe.util.utility import Dir, search_parent_directories
+from libbe.util.subproc import CommandError, invoke
+from libbe.util.plugin import import_by_name
+#import libbe.storage.util.upgrade as upgrade
if libbe.TESTING == True:
import unittest
import doctest
+ import libbe.ui.util.user
# List VCS modules in order of preference.
# Don't list this module, it is implicitly last.
@@ -58,62 +61,243 @@ def set_preferred_vcs(name):
def _get_matching_vcs(matchfn):
"""Return the first module for which matchfn(VCS_instance) is true"""
for submodname in VCS_ORDER:
- module = get_plugin('libbe', submodname)
+ module = import_by_name('libbe.storage.vcs.%s' % submodname)
vcs = module.new()
if matchfn(vcs) == True:
return vcs
vcs.cleanup()
return VCS()
-
+
def vcs_by_name(vcs_name):
"""Return the module for the VCS with the given name"""
+ if vcs_name == VCS.name:
+ return new()
return _get_matching_vcs(lambda vcs: vcs.name == vcs_name)
def detect_vcs(dir):
"""Return an VCS instance for the vcs being used in this directory"""
- return _get_matching_vcs(lambda vcs: vcs.detect(dir))
+ return _get_matching_vcs(lambda vcs: vcs._detect(dir))
def installed_vcs():
"""Return an instance of an installed VCS"""
return _get_matching_vcs(lambda vcs: vcs.installed())
-
-class SettingIDnotSupported(NotImplementedError):
- pass
-
-class VCSnotRooted(Exception):
+class VCSnotRooted (libbe.storage.base.ConnectionError):
def __init__(self):
- msg = "VCS not rooted"
- Exception.__init__(self, msg)
-
-class PathNotInRoot(Exception):
- def __init__(self, path, root):
- msg = "Path '%s' not in root '%s'" % (path, root)
- Exception.__init__(self, msg)
+ msg = 'VCS not rooted'
+ libbe.storage.base.ConnectionError.__init__(self, msg)
+
+class InvalidPath (libbe.storage.base.InvalidID):
+ def __init__(self, path, root, msg=None):
+ if msg == None:
+ msg = 'Path "%s" not in root "%s"' % (path, root)
+ libbe.storage.base.InvalidID.__init__(self, msg)
self.path = path
self.root = root
-class NoSuchFile(Exception):
- def __init__(self, pathname, root="."):
+class SpacerCollision (InvalidPath):
+ def __init__(self, path, spacer):
+ msg = 'Path "%s" collides with spacer directory "%s"' % (path, spacer)
+ InvalidPath.__init__(self, path, root=None, msg=msg)
+ self.spacer = spacer
+
+class NoSuchFile (libbe.storage.base.InvalidID):
+ def __init__(self, pathname, root='.'):
path = os.path.abspath(os.path.join(root, pathname))
- Exception.__init__(self, "No such file: %s" % path)
+ libbe.storage.base.InvalidID.__init__(self, 'No such file: %s' % path)
-class EmptyCommit(Exception):
- def __init__(self):
- Exception.__init__(self, "No changes to commit")
+
+class CachedPathID (object):
+ """
+ Storage ID <-> path policy.
+ .../.be/BUGDIR/bugs/BUG/comments/COMMENT
+ ^-- root path
+
+ >>> dir = Dir()
+ >>> os.mkdir(os.path.join(dir.path, '.be'))
+ >>> os.mkdir(os.path.join(dir.path, '.be', 'abc'))
+ >>> os.mkdir(os.path.join(dir.path, '.be', 'abc', 'bugs'))
+ >>> os.mkdir(os.path.join(dir.path, '.be', 'abc', 'bugs', '123'))
+ >>> os.mkdir(os.path.join(dir.path, '.be', 'abc', 'bugs', '123', 'comments'))
+ >>> os.mkdir(os.path.join(dir.path, '.be', 'abc', 'bugs', '123', 'comments', 'def'))
+ >>> os.mkdir(os.path.join(dir.path, '.be', 'abc', 'bugs', '456'))
+ >>> file(os.path.join(dir.path, '.be', 'abc', 'values'),
+ ... 'w').close()
+ >>> file(os.path.join(dir.path, '.be', 'abc', 'bugs', '123', 'values'),
+ ... 'w').close()
+ >>> file(os.path.join(dir.path, '.be', 'abc', 'bugs', '123', 'comments', 'def', 'values'),
+ ... 'w').close()
+ >>> c = CachedPathID()
+ >>> c.root(dir.path)
+ >>> c.id(os.path.join(dir.path, '.be', 'abc', 'bugs', '123', 'comments', 'def', 'values'))
+ 'def/values'
+ >>> c.init()
+ >>> sorted(os.listdir(os.path.join(c._root, '.be')))
+ ['abc', 'id-cache']
+ >>> c.connect()
+ >>> c.path('123/values') # doctest: +ELLIPSIS
+ u'.../.be/abc/bugs/123/values'
+ >>> c.disconnect()
+ >>> c.destroy()
+ >>> sorted(os.listdir(os.path.join(c._root, '.be')))
+ ['abc']
+ >>> c.connect() # demonstrate auto init
+ >>> sorted(os.listdir(os.path.join(c._root, '.be')))
+ ['abc', 'id-cache']
+ >>> c.add_id(u'xyz', parent=None) # doctest: +ELLIPSIS
+ u'.../.be/xyz'
+ >>> c.add_id('xyz/def', parent='xyz') # doctest: +ELLIPSIS
+ u'.../.be/xyz/def'
+ >>> c.add_id('qrs', parent='123') # doctest: +ELLIPSIS
+ u'.../.be/abc/bugs/123/comments/qrs'
+ >>> c.disconnect()
+ >>> c.connect()
+ >>> c.path('qrs') # doctest: +ELLIPSIS
+ u'.../.be/abc/bugs/123/comments/qrs'
+ >>> c.remove_id('qrs')
+ >>> c.path('qrs')
+ Traceback (most recent call last):
+ ...
+ InvalidID: 'qrs'
+ >>> c.disconnect()
+ >>> c.destroy()
+ >>> dir.cleanup()
+ """
+ def __init__(self, encoding=None):
+ self.encoding = libbe.util.encoding.get_filesystem_encoding()
+ self._spacer_dirs = ['.be', 'bugs', 'comments']
+
+ def root(self, path):
+ self._root = os.path.abspath(path).rstrip(os.path.sep)
+ self._cache_path = os.path.join(
+ self._root, self._spacer_dirs[0], 'id-cache')
+
+ def init(self):
+ """
+ Create cache file for an existing .be directory.
+ File if multiple lines of the form:
+ UUID\tPATH
+ """
+ self._cache = {}
+ spaced_root = os.path.join(self._root, self._spacer_dirs[0])
+ for dirpath, dirnames, filenames in os.walk(spaced_root):
+ if dirpath == spaced_root:
+ continue
+ try:
+ id = self.id(dirpath)
+ relpath = dirpath[len(self._root)+1:]
+ if id.count('/') == 0:
+ self._cache[id] = relpath
+ except InvalidPath:
+ pass
+ self._changed = True
+ self.disconnect()
+
+ def destroy(self):
+ os.remove(self._cache_path)
+
+ def connect(self):
+ if not os.path.exists(self._cache_path):
+ try:
+ self.init()
+ except IOError:
+ raise libbe.storage.base.ConnectionError
+ self._cache = {} # key: uuid, value: path
+ self._changed = False
+ f = codecs.open(self._cache_path, 'r', self.encoding)
+ for line in f:
+ fields = line.rstrip('\n').split('\t')
+ self._cache[fields[0]] = fields[1]
+ f.close()
+
+ def disconnect(self):
+ if self._changed == True:
+ f = codecs.open(self._cache_path, 'w', self.encoding)
+ for uuid,path in self._cache.items():
+ f.write('%s\t%s\n' % (uuid, path))
+ f.close()
+ self._cache = {}
+
+ def path(self, id, relpath=False):
+ fields = id.split('/', 1)
+ uuid = fields[0]
+ if len(fields) == 1:
+ extra = []
+ else:
+ extra = fields[1:]
+ if uuid not in self._cache:
+ raise libbe.storage.base.InvalidID(uuid)
+ if relpath == True:
+ return os.path.join(self._cache[uuid], *extra)
+ return os.path.join(self._root, self._cache[uuid], *extra)
+
+ def add_id(self, id, parent=None):
+ if id.count('/') > 0:
+ # not a UUID-level path
+ assert id.startswith(parent), \
+ 'Strange ID: "%s" should start with "%s"' % (id, parent)
+ path = self.path(id)
+ elif id in self._cache:
+ # already added
+ path = self.path(id)
+ else:
+ if parent == None:
+ parent_path = ''
+ spacer = self._spacer_dirs[0]
+ else:
+ assert parent.count('/') == 0, \
+ 'Strange parent ID: "%s" should be UUID' % parent
+ parent_path = self.path(parent, relpath=True)
+ parent_spacer = parent_path.split(os.path.sep)[-2]
+ i = self._spacer_dirs.index(parent_spacer)
+ spacer = self._spacer_dirs[i+1]
+ path = os.path.join(parent_path, spacer, id)
+ self._cache[id] = path
+ self._changed = True
+ path = os.path.join(self._root, path)
+ return path
+
+ def remove_id(self, id):
+ if id.count('/') > 0:
+ return # not a UUID-level path
+ self._cache.pop(id)
+ self._changed = True
+
+ def id(self, path):
+ path = os.path.abspath(path)
+ if not path.startswith(self._root + os.path.sep):
+ raise InvalidPath('Path %s not in root %s' % (path, self._root))
+ path = path[len(self._root)+1:]
+ orig_path = path
+ if not path.startswith(self._spacer_dirs[0] + os.path.sep):
+ raise InvalidPath(path, self._spacer_dirs[0])
+ for spacer in self._spacer_dirs:
+ if not path.startswith(spacer + os.path.sep):
+ break
+ id = path[len(spacer)+1:]
+ fields = path[len(spacer)+1:].split(os.path.sep,1)
+ if len(fields) == 1:
+ break
+ path = fields[1]
+ for spacer in self._spacer_dirs:
+ if id.endswith(os.path.sep + spacer):
+ raise SpacerCollision(orig_path, spacer)
+ if os.path.sep != '/':
+ id = id.replace(os.path.sep, '/')
+ return id
def new():
return VCS()
-class VCS(object):
+class VCS (libbe.storage.base.VersionedStorage):
"""
This class implements a 'no-vcs' interface.
Support for other VCSs can be added by subclassing this class, and
overriding methods _vcs_*() with code appropriate for your VCS.
-
+
The methods _u_*() are utility methods available to the _vcs_*()
methods.
@@ -127,7 +311,7 @@ class VCS(object):
/path/to/source/.be
However, you're of in some subdirectory like
/path/to/source/GUI/testing
- and you want to comment on a bug. Setting sink_to_root=True wen
+ and you want to comment on a bug. Setting sink_to_root=True when
you initialize your BugDir will cause it to search for the '.be'
file in the ancestors of the path you passed in as 'root'.
/path/to/source/GUI/testing/.be miss
@@ -245,113 +429,110 @@ class VCS(object):
os.listdir(self.get_path("bugs")):
"""
- name = "None"
- client = "" # command-line tool for _u_invoke_client
- versioned = False
- def __init__(self, paranoid=False, encoding=sys.getdefaultencoding()):
- self.paranoid = paranoid
- self.verboseInvoke = False
- self.rootdir = None
- self._duplicateBasedir = None
- self._duplicateDirname = None
- self.encoding = encoding
- def __str__(self):
- return "<%s %s>" % (self.__class__.__name__, id(self))
- def __repr__(self):
- return str(self)
+ name = 'None'
+ client = 'false' # command-line tool for _u_invoke_client
+
+ def __init__(self, *args, **kwargs):
+ if 'encoding' not in kwargs:
+ kwargs['encoding'] = libbe.util.encoding.get_filesystem_encoding()
+ libbe.storage.base.VersionedStorage.__init__(self, *args, **kwargs)
+ self.versioned = False
+ self.verbose_invoke = False
+ self._cached_path_id = CachedPathID()
+
def _vcs_version(self):
"""
Return the VCS version string.
"""
- return "0.0"
+ return '0'
+
+ def _vcs_get_user_id(self):
+ """
+ Get the VCS's suggested user id (e.g. "John Doe <jdoe@example.com>").
+ If the VCS has not been configured with a username, return None.
+ """
+ return None
+
def _vcs_detect(self, path=None):
"""
Detect whether a directory is revision controlled with this VCS.
"""
return True
+
def _vcs_root(self, path):
"""
Get the VCS root. This is the default working directory for
future invocations. You would normally set this to the root
directory for your VCS.
"""
- if os.path.isdir(path)==False:
+ if os.path.isdir(path) == False:
path = os.path.dirname(path)
- if path == "":
- path = os.path.abspath(".")
+ if path == '':
+ path = os.path.abspath('.')
return path
- def _vcs_init(self, path):
+
+ def _vcs_init(self):
"""
- Begin versioning the tree based at path.
+ Begin versioning the tree based at self.repo.
"""
pass
- def _vcs_cleanup(self):
+
+ def _vcs_destroy(self):
"""
- Remove any cruft that _vcs_init() created outside of the
- versioned tree.
+ Remove any files used in versioning (e.g. whatever _vcs_init()
+ created).
"""
pass
- def _vcs_get_user_id(self):
- """
- Get the VCS's suggested user id (e.g. "John Doe <jdoe@example.com>").
- If the VCS has not been configured with a username, return None.
- """
- return None
- def _vcs_set_user_id(self, value):
- """
- Set the VCS's suggested user id (e.g "John Doe <jdoe@example.com>").
- This is run if the VCS has not been configured with a usename, so
- that commits will have a reasonable FROM value.
- """
- raise SettingIDnotSupported
+
def _vcs_add(self, path):
"""
Add the already created file at path to version control.
"""
pass
+
def _vcs_remove(self, path):
"""
Remove the file at path from version control. Optionally
remove the file from the filesystem as well.
"""
pass
+
def _vcs_update(self, path):
"""
Notify the versioning system of changes to the versioned file
at path.
"""
pass
- def _vcs_get_file_contents(self, path, revision=None, binary=False):
+
+ def _vcs_get_file_contents(self, path, revision=None):
"""
Get the file contents as they were in a given revision.
Revision==None specifies the current revision.
"""
- assert revision == None, \
- "The %s VCS does not support revision specifiers" % self.name
- if binary == False:
- f = codecs.open(os.path.join(self.rootdir, path), "r", self.encoding)
- else:
- f = open(os.path.join(self.rootdir, path), "rb")
+ if revision != None:
+ raise libbe.storage.base.InvalidRevision(
+ 'The %s VCS does not support revision specifiers' % self.name)
+ path = os.path.join(self.repo, path)
+ if not os.path.exists(path):
+ return libbe.util.InvalidObject
+ if os.path.isdir(path):
+ return libbe.storage.base.InvalidDirectory
+ f = open(path, 'rb')
contents = f.read()
f.close()
return contents
- def _vcs_duplicate_repo(self, directory, revision=None):
- """
- Get the repository as it was in a given revision.
- revision==None specifies the current revision.
- dir specifies a directory to create the duplicate in.
- """
- shutil.copytree(self.rootdir, directory, True)
+
def _vcs_commit(self, commitfile, allow_empty=False):
"""
Commit the current working directory, using the contents of
commitfile as the comment. Return the name of the old
revision (or None if commits are not supported).
-
+
If allow_empty == False, raise EmptyCommit if there are no
changes to commit.
"""
return None
+
def _vcs_revision_id(self, index):
"""
Return the name of the <index>th revision. Index will be an
@@ -362,11 +543,13 @@ os.listdir(self.get_path("bugs")):
specified revision does not exist.
"""
return None
+
def version(self):
- """Cache version string for efficiency."""
+ # Cache version string for efficiency.
if not hasattr(self, '_version'):
self._version = self._get_version()
return self._version
+
def _get_version(self):
try:
ret = self._vcs_version()
@@ -378,183 +561,166 @@ os.listdir(self.get_path("bugs")):
raise OSError, e
except CommandError:
return None
+
def installed(self):
if self.version() != None:
return True
return False
- def detect(self, path="."):
- """
- Detect whether a directory is revision controlled with this VCS.
- """
- return self._vcs_detect(path)
- def root(self, path):
- """
- Set the root directory to the path's VCS root. This is the
- default working directory for future invocations.
- """
- self.rootdir = self._vcs_root(path)
- def init(self, path):
- """
- Begin versioning the tree based at path.
- Also roots the vcs at path.
- """
- if os.path.isdir(path)==False:
- path = os.path.dirname(path)
- self._vcs_init(path)
- self.root(path)
- def cleanup(self):
- self._vcs_cleanup()
+
def get_user_id(self):
"""
Get the VCS's suggested user id (e.g. "John Doe <jdoe@example.com>").
- If the VCS has not been configured with a username, return the user's
- id. You can override the automatic lookup procedure by setting the
+ If the VCS has not been configured with a username, return None.
+ You can override the automatic lookup procedure by setting the
VCS.user_id attribute to a string of your choice.
"""
- if hasattr(self, "user_id"):
- if self.user_id != None:
- return self.user_id
- id = self._vcs_get_user_id()
- if id == None:
- name = self._u_get_fallback_username()
- email = self._u_get_fallback_email()
- id = self._u_create_id(name, email)
- print >> sys.stderr, "Guessing id '%s'" % id
- try:
- self.set_user_id(id)
- except SettingIDnotSupported:
- pass
- return id
- def set_user_id(self, value):
+ if not hasattr(self, 'user_id'):
+ self.user_id = self._vcs_get_user_id()
+ return self.user_id
+
+ def _detect(self, path='.'):
"""
- Set the VCS's suggested user id (e.g "John Doe <jdoe@example.com>").
- This is run if the VCS has not been configured with a usename, so
- that commits will have a reasonable FROM value.
+ Detect whether a directory is revision controlled with this VCS.
"""
- self._vcs_set_user_id(value)
- def add(self, path):
+ return self._vcs_detect(path)
+
+ def root(self):
"""
- Add the already created file at path to version control.
+ Set the root directory to the path's VCS root. This is the
+ default working directory for future invocations.
"""
- self._vcs_add(self._u_rel_path(path))
- def remove(self, path):
+ self.repo = os.path.abspath(self._vcs_root(self.repo))
+ if os.path.isdir(self.repo) == False:
+ self.repo = os.path.dirname(self.repo)
+ self.be_dir = os.path.join(
+ self.repo, self._cached_path_id._spacer_dirs[0])
+ self._cached_path_id.root(self.repo)
+
+ def _init(self):
"""
- Remove a file from both version control and the filesystem.
+ Begin versioning the tree based at self.repo.
+ Also roots the vcs at path.
"""
- self._vcs_remove(self._u_rel_path(path))
+ self.root()
+ self._vcs_init()
+ os.mkdir(self.be_dir)
+ self._vcs_add(self._u_rel_path(self.be_dir))
+ self._cached_path_id.init()
+
+ def _destroy(self):
+ self._vcs_destroy()
+ self._cached_path_id.destroy()
+ shutil.rmtree(self.be_dir)
+
+ def _connect(self):
+ self.root()
+ self._cached_path_id.connect()
+ self.check_disk_version()
+
+ def disconnect(self):
+ self._cached_path_id.disconnect()
+
+ def _add(self, id, parent=None, directory=False):
+ path = self._cached_path_id.add_id(id, parent)
+ relpath = self._u_rel_path(path)
+ reldirs = relpath.split(os.path.sep)
+ if directory == False:
+ reldirs = reldirs[:-1]
+ dir = self.repo
+ for reldir in reldirs:
+ dir = os.path.join(dir, reldir)
+ if not os.path.exists(dir):
+ os.mkdir(dir)
+ self._vcs_add(self._u_rel_path(dir))
+ elif not os.path.isdir(dir):
+ raise libbe.storage.base.InvalidDirectory
+ if directory == False:
+ if not os.path.exists(path):
+ open(path, 'w').close()
+ self._vcs_add(self._u_rel_path(path))
+
+ def _remove(self, id):
+ path = self._cached_path_id.path(id)
if os.path.exists(path):
- os.remove(path)
- def recursive_remove(self, dirname):
- """
- Remove a file/directory and all its decendents from both
- version control and the filesystem.
- """
- if not os.path.exists(dirname):
- raise NoSuchFile(dirname)
- for dirpath,dirnames,filenames in os.walk(dirname, topdown=False):
+ if os.path.isdir(path) and len(self.children(id)) > 0:
+ raise libbe.storage.base.DirectoryNotEmpty(id)
+ self._vcs_remove(self._u_rel_path(path))
+ if os.path.exists(path):
+ if os.path.isdir(path):
+ os.rmdir(path)
+ else:
+ os.remove(path)
+ self._cached_path_id.remove_id(id)
+
+ def _recursive_remove(self, id):
+ path = self._cached_path_id.path(id)
+ for dirpath,dirnames,filenames in os.walk(path, topdown=False):
filenames.extend(dirnames)
- for path in filenames:
- fullpath = os.path.join(dirpath, path)
+ for f in filenames:
+ fullpath = os.path.join(dirpath, f)
if os.path.exists(fullpath) == False:
continue
self._vcs_remove(self._u_rel_path(fullpath))
- if os.path.exists(dirname):
- shutil.rmtree(dirname)
- def update(self, path):
- """
- Notify the versioning system of changes to the versioned file
- at path.
- """
- self._vcs_update(self._u_rel_path(path))
- def get_file_contents(self, path, revision=None, allow_no_vcs=False, binary=False):
- """
- Get the file as it was in a given revision.
- Revision==None specifies the current revision.
-
- allow_no_vcs==True allows direct access to files through
- codecs.open() or open() if the vcs decides it can't handle the
- given path.
- """
- if not os.path.exists(path):
- raise NoSuchFile(path)
- if self._use_vcs(path, allow_no_vcs):
- relpath = self._u_rel_path(path)
- contents = self._vcs_get_file_contents(relpath,revision,binary=binary)
+ if os.path.exists(path):
+ shutil.rmtree(path)
+ path = self._cached_path_id.path(id, relpath=True)
+ for id,p in self._cached_path_id._cache.items():
+ if p.startswith(path):
+ self._cached_path_id.remove_id(id)
+
+ def _children(self, id=None, revision=None):
+ if id==None:
+ path = self.be_dir
else:
- if binary == True:
- f = codecs.open(path, "r", self.encoding)
- else:
- f = open(path, "rb")
- contents = f.read()
- f.close()
+ path = self._cached_path_id.path(id)
+ if os.path.isdir(path) == False:
+ return []
+ children = os.listdir(path)
+ for i,c in enumerate(children):
+ if c in self._cached_path_id._spacer_dirs:
+ children[i] = None
+ children.extend([os.path.join(c, c2) for c2 in
+ os.listdir(os.path.join(path, c))])
+ elif c == 'id-cache':
+ children[i] = None
+ for i,c in enumerate(children):
+ if c == None: continue
+ cpath = os.path.join(path, c)
+ children[i] = self._cached_path_id.id(cpath)
+ return [c for c in children if c != None]
+
+ def _get(self, id, default=libbe.util.InvalidObject, revision=None):
+ try:
+ path = self._cached_path_id.path(id)
+ except libbe.storage.base.InvalidID, e:
+ if default == libbe.util.InvalidObject:
+ raise e
+ return default
+ relpath = self._u_rel_path(path)
+ contents = self._vcs_get_file_contents(relpath,revision)
+ if contents == libbe.storage.base.InvalidDirectory:
+ raise libbe.storage.base.InvalidDirectory(id)
+ elif contents == libbe.util.InvalidObject:
+ raise libbe.storage.base.InvalidID(id)
+ elif len(contents) == 0:
+ return None
return contents
- def set_file_contents(self, path, contents, allow_no_vcs=False, binary=False):
- """
- Set the file contents under version control.
- """
- add = not os.path.exists(path)
- if binary == False:
- f = codecs.open(path, "w", self.encoding)
- else:
- f = open(path, "wb")
- f.write(contents)
- f.close()
-
- if self._use_vcs(path, allow_no_vcs):
- if add:
- self.add(path)
- else:
- self.update(path)
- def mkdir(self, path, allow_no_vcs=False, check_parents=True):
- """
- Create (if neccessary) a directory at path under version
- control.
- """
- if check_parents == True:
- parent = os.path.dirname(path)
- if not os.path.exists(parent): # recurse through parents
- self.mkdir(parent, allow_no_vcs, check_parents)
+
+ def _set(self, id, value):
+ try:
+ path = self._cached_path_id.path(id)
+ except libbe.storage.base.InvalidID, e:
+ raise e
if not os.path.exists(path):
- os.mkdir(path)
- if self._use_vcs(path, allow_no_vcs):
- self.add(path)
- else:
- assert os.path.isdir(path)
- if self._use_vcs(path, allow_no_vcs):
- #self.update(path)# Don't update directories. Changing files
- pass # underneath them should be sufficient.
-
- def duplicate_repo(self, revision=None):
- """
- Get the repository as it was in a given revision.
- revision==None specifies the current revision.
- Return the path to the arbitrary directory at the base of the new repo.
- """
- # Dirname in Basedir to protect against simlink attacks.
- if self._duplicateBasedir == None:
- self._duplicateBasedir = tempfile.mkdtemp(prefix='BEvcs')
- self._duplicateDirname = \
- os.path.join(self._duplicateBasedir, "duplicate")
- self._vcs_duplicate_repo(directory=self._duplicateDirname,
- revision=revision)
- return self._duplicateDirname
- def remove_duplicate_repo(self):
- """
- Clean up a duplicate repo created with duplicate_repo().
- """
- if self._duplicateBasedir != None:
- shutil.rmtree(self._duplicateBasedir)
- self._duplicateBasedir = None
- self._duplicateDirname = None
- def commit(self, summary, body=None, allow_empty=False):
- """
- Commit the current working directory, with a commit message
- string summary and body. Return the name of the old revision
- (or None if versioning is not supported).
-
- If allow_empty == False (the default), raise EmptyCommit if
- there are no changes to commit.
- """
+ raise libbe.storage.base.InvalidID(id)
+ if os.path.isdir(path):
+ raise libbe.storage.base.InvalidDirectory(id)
+ f = open(path, "wb")
+ f.write(value)
+ f.close()
+ self._vcs_update(self._u_rel_path(path))
+
+ def _commit(self, summary, body=None, allow_empty=False):
summary = summary.strip()+'\n'
if body is not None:
summary += '\n' + body.strip() + '\n'
@@ -564,35 +730,20 @@ os.listdir(self.get_path("bugs")):
temp_file = os.fdopen(descriptor, 'wb')
temp_file.write(summary)
temp_file.flush()
- self.precommit()
revision = self._vcs_commit(filename, allow_empty=allow_empty)
temp_file.close()
- self.postcommit()
finally:
os.remove(filename)
return revision
- def precommit(self):
- """
- Executed before all attempted commits.
- """
- pass
- def postcommit(self):
- """
- Only executed after successful commits.
- """
- pass
- def revision_id(self, index=None):
- """
- Return the name of the <index>th revision. The choice of
- which branch to follow when crossing branches/merges is not
- defined.
- Return None if index==None, revision IDs are not supported, or
- if the specified revision does not exist.
- """
+ def revision_id(self, index=None):
if index == None:
return None
- return self._vcs_revision_id(index)
+ revid = self._vcs_revision_id(index)
+ if revid == None:
+ raise libbe.storage.base.InvalidRevision(index)
+ return revid
+
def _u_any_in_string(self, list, string):
"""
Return True if any of the strings in list are in string.
@@ -602,23 +753,26 @@ os.listdir(self.get_path("bugs")):
if list_string in string:
return True
return False
+
def _u_invoke(self, *args, **kwargs):
if 'cwd' not in kwargs:
- kwargs['cwd'] = self.rootdir
+ kwargs['cwd'] = self.repo
if 'verbose' not in kwargs:
- kwargs['verbose'] = self.verboseInvoke
+ kwargs['verbose'] = self.verbose_invoke
if 'encoding' not in kwargs:
kwargs['encoding'] = self.encoding
return invoke(*args, **kwargs)
+
def _u_invoke_client(self, *args, **kwargs):
cl_args = [self.client]
cl_args.extend(args)
return self._u_invoke(cl_args, **kwargs)
+
def _u_search_parent_directories(self, path, filename):
"""
Find the file (or directory) named filename in path or in any
of path's parents.
-
+
e.g.
search_parent_directories("/a/b/c", ".be")
will return the path to the first existing file from
@@ -629,6 +783,7 @@ os.listdir(self.get_path("bugs")):
or None if none of those files exist.
"""
return search_parent_directories(path, filename)
+
def _use_vcs(self, path, allow_no_vcs):
"""
Try and decide if _vcs_add/update/mkdir/etc calls will
@@ -637,16 +792,17 @@ os.listdir(self.get_path("bugs")):
"""
use_vcs = True
exception = None
- if self.rootdir != None:
+ if self.repo != None:
if self.path_in_root(path) == False:
use_vcs = False
- exception = PathNotInRoot(path, self.rootdir)
+ exception = InvalidPath(path, self.repo)
else:
use_vcs = False
exception = VCSnotRooted
if use_vcs == False and allow_no_vcs==False:
raise exception
return use_vcs
+
def path_in_root(self, path, root=None):
"""
Return the relative path to path from root.
@@ -657,15 +813,16 @@ os.listdir(self.get_path("bugs")):
False
"""
if root == None:
- if self.rootdir == None:
+ if self.repo == None:
raise VCSnotRooted
- root = self.rootdir
+ root = self.repo
path = os.path.abspath(path)
absRoot = os.path.abspath(root)
absRootSlashedDir = os.path.join(absRoot,"")
if not path.startswith(absRootSlashedDir):
return False
return True
+
def _u_rel_path(self, path, root=None):
"""
Return the relative path to path from root.
@@ -674,18 +831,19 @@ os.listdir(self.get_path("bugs")):
'.be'
"""
if root == None:
- if self.rootdir == None:
+ if self.repo == None:
raise VCSnotRooted
- root = self.rootdir
+ root = self.repo
path = os.path.abspath(path)
absRoot = os.path.abspath(root)
absRootSlashedDir = os.path.join(absRoot,"")
if not path.startswith(absRootSlashedDir):
- raise PathNotInRoot(path, absRootSlashedDir)
+ raise InvalidPath(path, absRootSlashedDir)
assert path != absRootSlashedDir, \
"file %s == root directory %s" % (path, absRootSlashedDir)
relpath = path[len(absRootSlashedDir):]
return relpath
+
def _u_abspath(self, path, root=None):
"""
Return the absolute path from a path realtive to root.
@@ -694,60 +852,10 @@ os.listdir(self.get_path("bugs")):
'/a.b/c/.be'
"""
if root == None:
- assert self.rootdir != None, "VCS not rooted"
- root = self.rootdir
+ assert self.repo != None, "VCS not rooted"
+ root = self.repo
return os.path.abspath(os.path.join(root, path))
- def _u_create_id(self, name, email=None):
- """
- >>> vcs = new()
- >>> vcs._u_create_id("John Doe", "jdoe@example.com")
- 'John Doe <jdoe@example.com>'
- >>> vcs._u_create_id("John Doe")
- 'John Doe'
- """
- assert len(name) > 0
- if email == None or len(email) == 0:
- return name
- else:
- return "%s <%s>" % (name, email)
- def _u_parse_id(self, value):
- """
- >>> vcs = new()
- >>> vcs._u_parse_id("John Doe <jdoe@example.com>")
- ('John Doe', 'jdoe@example.com')
- >>> vcs._u_parse_id("John Doe")
- ('John Doe', None)
- >>> try:
- ... vcs._u_parse_id("John Doe <jdoe@example.com><what?>")
- ... except AssertionError:
- ... print "Invalid match"
- Invalid match
- """
- emailexp = re.compile("(.*) <([^>]*)>(.*)")
- match = emailexp.search(value)
- if match == None:
- email = None
- name = value
- else:
- assert len(match.groups()) == 3
- assert match.groups()[2] == "", match.groups()
- email = match.groups()[1]
- name = match.groups()[0]
- assert name != None
- assert len(name) > 0
- return (name, email)
- def _u_get_fallback_username(self):
- name = None
- for envariable in ["LOGNAME", "USERNAME"]:
- if os.environ.has_key(envariable):
- name = os.environ[envariable]
- break
- assert name != None
- return name
- def _u_get_fallback_email(self):
- hostname = gethostname()
- name = self._u_get_fallback_username()
- return "%s@%s" % (name, hostname)
+
def _u_parse_commitfile(self, commitfile):
"""
Split the commitfile created in self.commit() back into
@@ -763,9 +871,9 @@ os.listdir(self.get_path("bugs")):
return (summary, body)
def check_disk_version(self):
- version = self.get_version()
- if version != upgrade.BUGDIR_DISK_VERSION:
- upgrade.upgrade(self.root, version)
+ version = self.version()
+ #if version != upgrade.BUGDIR_DISK_VERSION:
+ # upgrade.upgrade(self.repo, version)
def disk_version(self, path=None, use_none_vcs=False,
for_duplicate_bugdir=False):
@@ -786,39 +894,17 @@ os.listdir(self.get_path("bugs")):
if self.sync_with_disk == False:
raise DiskAccessRequired("set version")
self.vcs.mkdir(self.get_path())
- self.vcs.set_file_contents(self.get_path("version"),
- upgrade.BUGDIR_DISK_VERSION+"\n")
+ #self.vcs.set_file_contents(self.get_path("version"),
+ # upgrade.BUGDIR_DISK_VERSION+"\n")
+
-
if libbe.TESTING == True:
- def setup_vcs_test_fixtures(testcase):
- """Set up test fixtures for VCS test case."""
- testcase.vcs = testcase.Class()
- testcase.dir = Dir()
- testcase.dirname = testcase.dir.path
-
- vcs_not_supporting_uninitialized_user_id = []
- vcs_not_supporting_set_user_id = ["None", "hg"]
- testcase.vcs_supports_uninitialized_user_id = (
- testcase.vcs.name not in vcs_not_supporting_uninitialized_user_id)
- testcase.vcs_supports_set_user_id = (
- testcase.vcs.name not in vcs_not_supporting_set_user_id)
-
- if not testcase.vcs.installed():
- testcase.fail(
- "%(name)s VCS not found" % vars(testcase.Class))
-
- if testcase.Class.name != "None":
- testcase.failIf(
- testcase.vcs.detect(testcase.dirname),
- "Detected %(name)s VCS before initialising"
- % vars(testcase.Class))
-
- testcase.vcs.init(testcase.dirname)
-
- class VCSTestCase(unittest.TestCase):
- """Test cases for base VCS class."""
+ class VCSTestCase (unittest.TestCase):
+ """
+ Test cases for base VCS class (in addition to the Storage test
+ cases).
+ """
Class = VCS
@@ -827,271 +913,102 @@ if libbe.TESTING == True:
self.dirname = None
def setUp(self):
+ """Set up test fixtures for Storage test case."""
super(VCSTestCase, self).setUp()
- setup_vcs_test_fixtures(self)
+ self.dir = Dir()
+ self.dirname = self.dir.path
+ self.s = self.Class(repo=self.dirname)
+ if self.s.installed() == True:
+ self.s.init()
+ self.s.connect()
def tearDown(self):
- self.vcs.cleanup()
- self.dir.cleanup()
super(VCSTestCase, self).tearDown()
+ if self.s.installed() == True:
+ self.s.disconnect()
+ self.s.destroy()
+ self.dir.cleanup()
- def full_path(self, rel_path):
- return os.path.join(self.dirname, rel_path)
-
+ def test_installed(self):
+ """
+ See if the VCS is installed.
+ """
+ self.failUnless(self.s.installed() == True,
+ '%(name)s VCS not found' % vars(self.Class))
- class VCS_init_TestCase(VCSTestCase):
- """Test cases for VCS.init method."""
- def test_detect_should_succeed_after_init(self):
- """Should detect VCS in directory after initialization."""
- self.failUnless(
- self.vcs.detect(self.dirname),
- "Did not detect %(name)s VCS after initialising"
+ class VCS_detection_TestCase (VCSTestCase):
+ def test_detection(self):
+ """
+ See if the VCS detects its installed repository
+ """
+ if self.s.installed():
+ self.s.disconnect()
+ self.failUnless(self.s._detect(self.dirname) == True,
+ 'Did not detected %(name)s VCS after initialising'
% vars(self.Class))
+ self.s.connect()
- def test_vcs_rootdir_in_specified_root_path(self):
+ def test_no_detection(self):
+ """
+ See if the VCS detects its installed repository
+ """
+ if self.s.installed() and self.Class.name != 'None':
+ self.s.disconnect()
+ self.s.destroy()
+ self.failUnless(self.s._detect(self.dirname) == False,
+ 'Detected %(name)s VCS before initialising'
+ % self.Class)
+ self.s.init()
+ self.s.connect()
+
+ def test_vcs_repo_in_specified_root_path(self):
"""VCS root directory should be in specified root path."""
- rp = os.path.realpath(self.vcs.rootdir)
+ rp = os.path.realpath(self.s.repo)
dp = os.path.realpath(self.dirname)
vcs_name = self.Class.name
self.failUnless(
dp == rp or rp == None,
"%(vcs_name)s VCS root in wrong dir (%(dp)s %(rp)s)" % vars())
-
class VCS_get_user_id_TestCase(VCSTestCase):
"""Test cases for VCS.get_user_id method."""
def test_gets_existing_user_id(self):
"""Should get the existing user ID."""
- if not self.vcs_supports_uninitialized_user_id:
- return
-
- user_id = self.vcs.get_user_id()
- self.failUnless(
- user_id is not None,
- "unable to get a user id")
-
-
- class VCS_set_user_id_TestCase(VCSTestCase):
- """Test cases for VCS.set_user_id method."""
-
- def setUp(self):
- super(VCS_set_user_id_TestCase, self).setUp()
-
- if self.vcs_supports_uninitialized_user_id:
- self.prev_user_id = self.vcs.get_user_id()
- else:
- self.prev_user_id = "Uninitialized identity <bogus@example.org>"
-
- if self.vcs_supports_set_user_id:
- self.test_new_user_id = "John Doe <jdoe@example.com>"
- self.vcs.set_user_id(self.test_new_user_id)
-
- def tearDown(self):
- if self.vcs_supports_set_user_id:
- self.vcs.set_user_id(self.prev_user_id)
- super(VCS_set_user_id_TestCase, self).tearDown()
-
- def test_raises_error_in_unsupported_vcs(self):
- """Should raise an error in a VCS that doesn't support it."""
- if self.vcs_supports_set_user_id:
+ user_id = self.s.get_user_id()
+ if user_id == None:
return
- self.assertRaises(
- SettingIDnotSupported,
- self.vcs.set_user_id, "foo")
-
- def test_updates_user_id_in_supporting_vcs(self):
- """Should update the user ID in an VCS that supports it."""
- if not self.vcs_supports_set_user_id:
- return
- user_id = self.vcs.get_user_id()
- self.failUnlessEqual(
- self.test_new_user_id, user_id,
- "user id not set correctly (expected %s, got %s)"
- % (self.test_new_user_id, user_id))
-
-
- def setup_vcs_revision_test_fixtures(testcase):
- """Set up revision test fixtures for VCS test case."""
- testcase.test_dirs = ['a', 'a/b', 'c']
- for path in testcase.test_dirs:
- testcase.vcs.mkdir(testcase.full_path(path))
-
- testcase.test_files = ['a/text', 'a/b/text']
-
- testcase.test_contents = {
- 'rev_1': "Lorem ipsum",
- 'uncommitted': "dolor sit amet",
- }
-
-
- class VCS_mkdir_TestCase(VCSTestCase):
- """Test cases for VCS.mkdir method."""
-
- def setUp(self):
- super(VCS_mkdir_TestCase, self).setUp()
- setup_vcs_revision_test_fixtures(self)
-
- def tearDown(self):
- for path in reversed(sorted(self.test_dirs)):
- self.vcs.recursive_remove(self.full_path(path))
- super(VCS_mkdir_TestCase, self).tearDown()
-
- def test_mkdir_creates_directory(self):
- """Should create specified directory in filesystem."""
- for path in self.test_dirs:
- full_path = self.full_path(path)
- self.failUnless(
- os.path.exists(full_path),
- "path %(full_path)s does not exist" % vars())
-
-
- class VCS_commit_TestCase(VCSTestCase):
- """Test cases for VCS.commit method."""
-
- def setUp(self):
- super(VCS_commit_TestCase, self).setUp()
- setup_vcs_revision_test_fixtures(self)
-
- def tearDown(self):
- for path in reversed(sorted(self.test_dirs)):
- self.vcs.recursive_remove(self.full_path(path))
- super(VCS_commit_TestCase, self).tearDown()
-
- def test_file_contents_as_specified(self):
- """Should set file contents as specified."""
- test_contents = self.test_contents['rev_1']
- for path in self.test_files:
- full_path = self.full_path(path)
- self.vcs.set_file_contents(full_path, test_contents)
- current_contents = self.vcs.get_file_contents(full_path)
- self.failUnlessEqual(test_contents, current_contents)
-
- def test_file_contents_as_committed(self):
- """Should have file contents as specified after commit."""
- test_contents = self.test_contents['rev_1']
- for path in self.test_files:
- full_path = self.full_path(path)
- self.vcs.set_file_contents(full_path, test_contents)
- revision = self.vcs.commit("Initial file contents.")
- current_contents = self.vcs.get_file_contents(full_path)
- self.failUnlessEqual(test_contents, current_contents)
-
- def test_file_contents_as_set_when_uncommitted(self):
- """Should set file contents as specified after commit."""
- if not self.vcs.versioned:
- return
- for path in self.test_files:
- full_path = self.full_path(path)
- self.vcs.set_file_contents(
- full_path, self.test_contents['rev_1'])
- revision = self.vcs.commit("Initial file contents.")
- self.vcs.set_file_contents(
- full_path, self.test_contents['uncommitted'])
- current_contents = self.vcs.get_file_contents(full_path)
- self.failUnlessEqual(
- self.test_contents['uncommitted'], current_contents)
-
- def test_revision_file_contents_as_committed(self):
- """Should get file contents as committed to specified revision."""
- if not self.vcs.versioned:
- return
- for path in self.test_files:
- full_path = self.full_path(path)
- self.vcs.set_file_contents(
- full_path, self.test_contents['rev_1'])
- revision = self.vcs.commit("Initial file contents.")
- self.vcs.set_file_contents(
- full_path, self.test_contents['uncommitted'])
- committed_contents = self.vcs.get_file_contents(
- full_path, revision)
- self.failUnlessEqual(
- self.test_contents['rev_1'], committed_contents)
-
- def test_revision_id_as_committed(self):
- """Check for compatibility between .commit() and .revision_id()"""
- if not self.vcs.versioned:
- self.failUnlessEqual(self.vcs.revision_id(5), None)
- return
- committed_revisions = []
- for path in self.test_files:
- full_path = self.full_path(path)
- self.vcs.set_file_contents(
- full_path, self.test_contents['rev_1'])
- revision = self.vcs.commit("Initial %s contents." % path)
- committed_revisions.append(revision)
- self.vcs.set_file_contents(
- full_path, self.test_contents['uncommitted'])
- revision = self.vcs.commit("Altered %s contents." % path)
- committed_revisions.append(revision)
- for i,revision in enumerate(committed_revisions):
- self.failUnlessEqual(self.vcs.revision_id(i), revision)
- i += -len(committed_revisions) # check negative indices
- self.failUnlessEqual(self.vcs.revision_id(i), revision)
- i = len(committed_revisions)
- self.failUnlessEqual(self.vcs.revision_id(i), None)
- self.failUnlessEqual(self.vcs.revision_id(-i-1), None)
-
- def test_revision_id_as_committed(self):
- """Check revision id before first commit"""
- if not self.vcs.versioned:
- self.failUnlessEqual(self.vcs.revision_id(5), None)
- return
- committed_revisions = []
- for path in self.test_files:
- self.failUnlessEqual(self.vcs.revision_id(0), None)
-
-
- class VCS_duplicate_repo_TestCase(VCSTestCase):
- """Test cases for VCS.duplicate_repo method."""
-
- def setUp(self):
- super(VCS_duplicate_repo_TestCase, self).setUp()
- setup_vcs_revision_test_fixtures(self)
-
- def tearDown(self):
- self.vcs.remove_duplicate_repo()
- for path in reversed(sorted(self.test_dirs)):
- self.vcs.recursive_remove(self.full_path(path))
- super(VCS_duplicate_repo_TestCase, self).tearDown()
-
- def test_revision_file_contents_as_committed(self):
- """Should match file contents as committed to specified revision.
- """
- if not self.vcs.versioned:
- return
- for path in self.test_files:
- full_path = self.full_path(path)
- self.vcs.set_file_contents(
- full_path, self.test_contents['rev_1'])
- revision = self.vcs.commit("Commit current status")
- self.vcs.set_file_contents(
- full_path, self.test_contents['uncommitted'])
- dup_repo_path = self.vcs.duplicate_repo(revision)
- dup_file_path = os.path.join(dup_repo_path, path)
- dup_file_contents = file(dup_file_path, 'rb').read()
- self.failUnlessEqual(
- self.test_contents['rev_1'], dup_file_contents)
- self.vcs.remove_duplicate_repo()
-
-
- def make_vcs_testcase_subclasses(vcs_class, namespace):
- """Make VCSTestCase subclasses for vcs_class in the namespace."""
- vcs_testcase_classes = [
- c for c in (
- ob for ob in globals().values() if isinstance(ob, type))
- if issubclass(c, VCSTestCase)]
-
- for base_class in vcs_testcase_classes:
- testcase_class_name = vcs_class.__name__ + base_class.__name__
- testcase_class_bases = (base_class,)
- testcase_class_dict = dict(base_class.__dict__)
- testcase_class_dict['Class'] = vcs_class
- testcase_class = type(
- testcase_class_name, testcase_class_bases, testcase_class_dict)
- setattr(namespace, testcase_class_name, testcase_class)
-
+ name,email = libbe.ui.util.user.parse_user_id(user_id)
+ if email != None:
+ self.failUnless('@' in email, email)
+
+ def make_vcs_testcase_subclasses(storage_class, namespace):
+ c = storage_class()
+ if c.versioned == True:
+ libbe.storage.base.make_versioned_storage_testcase_subclasses(
+ storage_class, namespace)
+ else:
+ libbe.storage.base.make_storage_testcase_subclasses(
+ storage_class, namespace)
+
+ if namespace != sys.modules[__name__]:
+ # Make VCSTestCase subclasses for vcs_class in the namespace.
+ vcs_testcase_classes = [
+ c for c in (
+ ob for ob in globals().values() if isinstance(ob, type))
+ if issubclass(c, VCSTestCase)]
+
+ for base_class in vcs_testcase_classes:
+ testcase_class_name = vcs_class.__name__ + base_class.__name__
+ testcase_class_bases = (base_class,)
+ testcase_class_dict = dict(base_class.__dict__)
+ testcase_class_dict['Class'] = vcs_class
+ testcase_class = type(
+ testcase_class_name, testcase_class_bases, testcase_class_dict)
+ setattr(namespace, testcase_class_name, testcase_class)
+
+ make_vcs_testcase_subclasses(VCS, sys.modules[__name__])
unitsuite =unittest.TestLoader().loadTestsFromModule(sys.modules[__name__])
suite = unittest.TestSuite([unitsuite, doctest.DocTestSuite()])
diff --git a/libbe/storage/vcs/util/upgrade.py b/libbe/storage/vcs/util/upgrade.py
deleted file mode 100644
index dc9d54f..0000000
--- a/libbe/storage/vcs/util/upgrade.py
+++ /dev/null
@@ -1,246 +0,0 @@
-# Copyright (C) 2009 W. Trevor King <wking@drexel.edu>
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
-"""
-Handle conversion between the various on-disk images.
-"""
-
-import os, os.path
-import sys
-
-import libbe
-import bug
-import encoding
-import mapfile
-import vcs
-if libbe.TESTING == True:
- import doctest
-
-# a list of all past versions
-BUGDIR_DISK_VERSIONS = ["Bugs Everywhere Tree 1 0",
- "Bugs Everywhere Directory v1.1",
- "Bugs Everywhere Directory v1.2",
- "Bugs Everywhere Directory v1.3"]
-
-# the current version
-BUGDIR_DISK_VERSION = BUGDIR_DISK_VERSIONS[-1]
-
-class Upgrader (object):
- "Class for converting "
- initial_version = None
- final_version = None
- def __init__(self, root):
- self.root = root
- # use the "None" VCS to ensure proper encoding/decoding and
- # simplify path construction.
- self.vcs = vcs.vcs_by_name("None")
- self.vcs.root(self.root)
- self.vcs.encoding = encoding.get_encoding()
-
- def get_path(self, *args):
- """
- Return a path relative to .root.
- """
- dir = os.path.join(self.root, ".be")
- if len(args) == 0:
- return dir
- assert args[0] in ["version", "settings", "bugs"], str(args)
- return os.path.join(dir, *args)
-
- def check_initial_version(self):
- path = self.get_path("version")
- version = self.vcs.get_file_contents(path).rstrip("\n")
- assert version == self.initial_version, version
-
- def set_version(self):
- path = self.get_path("version")
- self.vcs.set_file_contents(path, self.final_version+"\n")
-
- def upgrade(self):
- print >> sys.stderr, "upgrading bugdir from '%s' to '%s'" \
- % (self.initial_version, self.final_version)
- self.check_initial_version()
- self.set_version()
- self._upgrade()
-
- def _upgrade(self):
- raise NotImplementedError
-
-
-class Upgrade_1_0_to_1_1 (Upgrader):
- initial_version = "Bugs Everywhere Tree 1 0"
- final_version = "Bugs Everywhere Directory v1.1"
- def _upgrade_mapfile(self, path):
- contents = self.vcs.get_file_contents(path)
- old_format = False
- for line in contents.splitlines():
- if len(line.split("=")) == 2:
- old_format = True
- break
- if old_format == True:
- # translate to YAML.
- newlines = []
- for line in contents.splitlines():
- line = line.rstrip('\n')
- if len(line) == 0:
- continue
- fields = line.split("=")
- if len(fields) == 2:
- key,value = fields
- newlines.append('%s: "%s"' % (key, value.replace('"','\\"')))
- else:
- newlines.append(line)
- contents = '\n'.join(newlines)
- # load the YAML and save
- map = mapfile.parse(contents)
- mapfile.map_save(self.vcs, path, map)
-
- def _upgrade(self):
- """
- Comment value field "From" -> "Author".
- Homegrown mapfile -> YAML.
- """
- path = self.get_path("settings")
- self._upgrade_mapfile(path)
- for bug_uuid in os.listdir(self.get_path("bugs")):
- path = self.get_path("bugs", bug_uuid, "values")
- self._upgrade_mapfile(path)
- c_path = ["bugs", bug_uuid, "comments"]
- if not os.path.exists(self.get_path(*c_path)):
- continue # no comments for this bug
- for comment_uuid in os.listdir(self.get_path(*c_path)):
- path_list = c_path + [comment_uuid, "values"]
- path = self.get_path(*path_list)
- self._upgrade_mapfile(path)
- settings = mapfile.map_load(self.vcs, path)
- if "From" in settings:
- settings["Author"] = settings.pop("From")
- mapfile.map_save(self.vcs, path, settings)
-
-
-class Upgrade_1_1_to_1_2 (Upgrader):
- initial_version = "Bugs Everywhere Directory v1.1"
- final_version = "Bugs Everywhere Directory v1.2"
- def _upgrade(self):
- """
- BugDir settings field "rcs_name" -> "vcs_name".
- """
- path = self.get_path("settings")
- settings = mapfile.map_load(self.vcs, path)
- if "rcs_name" in settings:
- settings["vcs_name"] = settings.pop("rcs_name")
- mapfile.map_save(self.vcs, path, settings)
-
-class Upgrade_1_2_to_1_3 (Upgrader):
- initial_version = "Bugs Everywhere Directory v1.2"
- final_version = "Bugs Everywhere Directory v1.3"
- def __init__(self, *args, **kwargs):
- Upgrader.__init__(self, *args, **kwargs)
- self._targets = {} # key: target text,value: new target bug
- path = self.get_path('settings')
- settings = mapfile.map_load(self.vcs, path)
- if 'vcs_name' in settings:
- old_vcs = self.vcs
- self.vcs = vcs.vcs_by_name(settings['vcs_name'])
- self.vcs.root(self.root)
- self.vcs.encoding = old_vcs.encoding
-
- def _target_bug(self, target_text):
- if target_text not in self._targets:
- _bug = bug.Bug(bugdir=self, summary=target_text)
- # note: we're not a bugdir, but all Bug.save() needs is
- # .root, .vcs, and .get_path(), which we have.
- _bug.severity = 'target'
- self._targets[target_text] = _bug
- return self._targets[target_text]
-
- def _upgrade_bugdir_mapfile(self):
- path = self.get_path('settings')
- settings = mapfile.map_load(self.vcs, path)
- if 'target' in settings:
- settings['target'] = self._target_bug(settings['target']).uuid
- mapfile.map_save(self.vcs, path, settings)
-
- def _upgrade_bug_mapfile(self, bug_uuid):
- import becommands.depend
- path = self.get_path('bugs', bug_uuid, 'values')
- settings = mapfile.map_load(self.vcs, path)
- if 'target' in settings:
- target_bug = self._target_bug(settings['target'])
- _bug = bug.Bug(bugdir=self, uuid=bug_uuid, from_disk=True)
- # note: we're not a bugdir, but all Bug.load_settings()
- # needs is .root, .vcs, and .get_path(), which we have.
- becommands.depend.add_block(target_bug, _bug)
- _bug.settings.pop('target')
- _bug.save()
-
- def _upgrade(self):
- """
- Bug value field "target" -> target bugs.
- Bugdir value field "target" -> pointer to current target bug.
- """
- for bug_uuid in os.listdir(self.get_path('bugs')):
- self._upgrade_bug_mapfile(bug_uuid)
- self._upgrade_bugdir_mapfile()
- for _bug in self._targets.values():
- _bug.save()
-
-upgraders = [Upgrade_1_0_to_1_1,
- Upgrade_1_1_to_1_2,
- Upgrade_1_2_to_1_3]
-upgrade_classes = {}
-for upgrader in upgraders:
- upgrade_classes[(upgrader.initial_version,upgrader.final_version)]=upgrader
-
-def upgrade(path, current_version,
- target_version=BUGDIR_DISK_VERSION):
- """
- Call the appropriate upgrade function to convert current_version
- to target_version. If a direct conversion function does not exist,
- use consecutive conversion functions.
- """
- if current_version not in BUGDIR_DISK_VERSIONS:
- raise NotImplementedError, \
- "Cannot handle version '%s' yet." % version
- if target_version not in BUGDIR_DISK_VERSIONS:
- raise NotImplementedError, \
- "Cannot handle version '%s' yet." % version
-
- if (current_version, target_version) in upgrade_classes:
- # direct conversion
- upgrade_class = upgrade_classes[(current_version, target_version)]
- u = upgrade_class(path)
- u.upgrade()
- else:
- # consecutive single-step conversion
- i = BUGDIR_DISK_VERSIONS.index(current_version)
- while True:
- version_a = BUGDIR_DISK_VERSIONS[i]
- version_b = BUGDIR_DISK_VERSIONS[i+1]
- try:
- upgrade_class = upgrade_classes[(version_a, version_b)]
- except KeyError:
- raise NotImplementedError, \
- "Cannot convert version '%s' to '%s' yet." \
- % (version_a, version_b)
- u = upgrade_class(path)
- u.upgrade()
- if version_b == target_version:
- break
- i += 1
-
-if libbe.TESTING == True:
- suite = doctest.DocTestSuite()