[Zope-CVS] CVS: Products/ZopeVersionControl - IVersionControl.py:1.2 Repository.py:1.2 Utility.py:1.2 Version.py:1.5 VersionHistory.py:1.3 VersionSupport.py:1.2 ZopeRepository.py:1.2 ZopeVersion.py:1.2 ZopeVersionHistory.py:1.2 __init__.py:1.2 notes.py:NONE
Shane Hathaway
shane@cvs.zope.org
Thu, 9 May 2002 13:44:11 -0400
Update of /cvs-repository/Products/ZopeVersionControl
In directory cvs.zope.org:/tmp/cvs-serv16533
Modified Files:
IVersionControl.py Repository.py Utility.py Version.py
VersionHistory.py VersionSupport.py ZopeRepository.py
ZopeVersion.py ZopeVersionHistory.py __init__.py
Removed Files:
notes.py
Log Message:
Brought in line with Brian's current work
=== Products/ZopeVersionControl/IVersionControl.py 1.1.1.1 => 1.2 ===
=== Products/ZopeVersionControl/Repository.py 1.1 => 1.2 === (453/553 lines abridged)
+#
+# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE
+#
+##############################################################################
+
__version__='$Revision$'[11:-2]
-from Globals import DTMLFile, InitializeClass
-from OFS.ObjectManager import ObjectManager
-from VersionHistory import VersionHistory
-import OFS, AccessControl, Acquisition
-
-
-class Repository(
- OFS.ObjectManager.ObjectManager,
- AccessControl.Role.RoleManager,
- OFS.SimpleItem.Item,
- ):
- """ """
- __non_versionable__ = 1
-
- security = AccessControl.ClassSecurityInfo()
-
- meta_type = 'Repository'
-
- manage_options=(
- ( {'label': 'Contents', 'action':'manage_main',
- 'help': ('Zope Help', 'Repository-Manage.stx')},
- {'label': 'Properties', 'action':'manage_properties_form',
- 'help': ('Zope Help', 'Repository-Properties.stx')},
- ) +
- AccessControl.Role.RoleManager.manage_options +
- OFS.SimpleItem.Item.manage_options
- )
-
- security.declareProtected('View management screens',
- 'manage_main', 'manage_properties_form'
- )
-
- manage_main = DTMLFile('dtml/RepositoryManageMain', globals())
- manage_properties_form = DTMLFile('dtml/RepositoryProperties', globals())
- manage_main._setName('manage_main')
- manage = manage_main
[-=- -=- -=- 453 lines omitted -=- -=- -=-]
+ else:
+ if history.hasVersionId(selector):
+ version = history.getVersionById(selector)
+ sticky = ('V', selector)
+
+ elif self._labels.has_key(selector):
+ version = history.getVersionByLabel(selector)
+ sticky = ('L', selector)
+
+ elif self._branches.has_key(selector):
+ version = history.getLatestVersion(selector)
+ sticky = ('B', selector)
+ else:
+ try: date = DateTime(selector)
+ except:
+ raise VersionControlError(
+ 'Invalid version selector: %s' % selector
+ )
+ else:
+ timestamp = date.timeTime()
+ sticky = ('D', timestamp)
+ version = history.getVersionByDate('mainline', timestamp)
+
+ object = version.copyState()
+
+ info = VersionInfo(history_id, version.getId(), VersionInfo.CHECKED_IN)
+ if sticky is not None:
+ info.sticky = sticky
+ object.__vc_info__ = info
+ return object
+
+ security.declareProtected(use_vc_permission, 'getVersionIds')
+ def getVersionIds(self, object):
+ info = self.getVersionInfo(object)
+ history = self.getVersionHistory(info.history_id)
+ return history.getVersionIds()
+
+ security.declareProtected(use_vc_permission, 'getLabelsForResource')
+ def getLabelsForResource(self, object):
+ info = self.getVersionInfo(object)
+ history = self.getVersionHistory(info.history_id)
+ return history.getLabels()
+
+ security.declareProtected(use_vc_permission, 'getLogEntries')
+ def getLogEntries(self, object):
+ info = self.getVersionInfo(object)
+ history = self.getVersionHistory(info.history_id)
+ return history.getLogEntries()
+
+InitializeClass(Repository)
=== Products/ZopeVersionControl/Utility.py 1.1 => 1.2 ===
+##############################################################################
+#
+# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE
+#
+##############################################################################
from Globals import InitializeClass, Persistent
-import ExtensionClass, AccessControl
-import string
+from AccessControl import ClassSecurityInfo
+from ZODB.referencesf import referencesf
+from ZODB.TimeStamp import TimeStamp
+from App.Common import package_home
+import os, time, AccessControl
+from DateTime import DateTime
+from types import FloatType
-class VersionInfo(Persistent):
- """ """
-
- security = AccessControl.ClassSecurityInfo()
-
- def __init__(self, object):
- pass
-
- security.declarePublic('getRepositoryId')
- def getRepositoryId(self):
- return self._repository_id
-
- security.declarePrivate('setRepositoryId')
- def setRepositoryId(self, id):
- self._repository_id = id
-
- security.declarePublic('getVersionHistoryId')
- def getVersionHistoryId(self):
- return self._history_id
-
- security.declarePrivate('setVersionHistoryId')
- def setVersionHistoryId(self, id):
- self._history_id = id
-
- security.declarePublic('getSourceVersionId')
- def getSourceVersionId(self):
- return self._version_id
-
- security.declarePrivate('setSourceVersionId')
- def setSourceVersionId(self, id):
- self._version_id = id
-
- security.declarePublic('getVersionPath')
- def getVersionPath(self):
- return self._version_path
+_dtmldir = os.path.join( package_home( globals() ), 'dtml' )
- security.declarePrivate('setVersionPath')
- def setVersionPath(self, path):
- self._version_path = path
-
- security.declarePublic('getResourceStatus')
- def getResourceStatus(self):
- return getattr(self, '_status', 'checked-in')
-
- security.declarePrivate('setResourceStatus')
- def setResourceStatus(self, status):
- if not status in ('checked-in', 'checked-out'):
- raise ValueError, status
- self._status = status
+use_vc_permission = 'Use version control'
+class VersionInfo(Persistent):
+ """A VersionInfo object contains bookkeeping information for version
+ controlled objects. The bookkeeping information can be read (but
+ not changed) by restricted code."""
+
+ def __init__(self, history_id, version_id, status):
+ self.timestamp = time.time()
+ self.history_id = history_id
+ self.version_id = version_id
+ self.status = status
+ self.user_id = _findUserId()
+
+ sticky = None
+
+ CHECKED_OUT = 0
+ CHECKED_IN = 1
+
+ def branchName(self):
+ if self.sticky is not None and self.sticky[0] == 'B':
+ return self.sticky[1]
+ return 'mainline'
- security.declarePrivate('clone')
- def clone(self):
- info = VersionInfo(None)
+ def clone(self, clear_sticky=0):
+ info = VersionInfo(self.history_id, self.version_id, self.status)
dict = info.__dict__
for name, value in self.__dict__.items():
dict[name] = value
+ if clear_sticky:
+ if dict.has_key('sticky'):
+ del dict['sticky']
+ info.user_id = _findUserId()
+ info.timestamp = time.time()
return info
-
-class ObjectMetadata(Persistent):
- """ """
-
- security = AccessControl.ClassSecurityInfo()
-
- def __init__(self, object):
- # Save some basic information about the object, so that we
- # don't have to wake it up for simple things like listing
- # the available version histories in the repository.
- self._ob_meta_type = getattr_ex(object, 'meta_type', None)
- self._ob_icon = getattr_ex(object, 'icon', None)
- path = object.getPhysicalPath()
- self._ob_path = string.join(path, '/')
- self._ob_id = find_id(object)
-
- security.declarePublic('getItemIcon')
- def getItemIcon(self):
- """Return the icon of the underlying version controlled resource."""
- return self._ob_icon
-
- security.declarePublic('getItemType')
- def getItemType(self):
- """Return the type name of the version controlled resource."""
- return self._ob_meta_type
-
- security.declarePublic('getItemPath')
- def getItemPath(self):
- """Return the original path of the resource that intially was
- used to create the version history in the repository."""
- return self._ob_path
-
- security.declarePublic('getItemId')
- def getItemId(self):
- """Return the original id of the version controlled resource."""
- return self._ob_id
+InitializeClass(VersionInfo)
-InitializeClass(ObjectMetadata)
+class ReadOnlyJar:
+ """A read-only ZODB connection-like object that prevents changes."""
+ def __init__(self, base):
+ self.__base__ = base
-from DateTime.DateTime import DateTime
-import time
+ _invalidating = []
-class AuditRecord(Persistent):
- """ """
+ def __getattr__(self, name):
+ return getattr(self.__base__, name)
- security = AccessControl.ClassSecurityInfo()
+ def commit(*args, **kw):
+ raise VersionWriteError(
+ 'Old versions of objects cannot be modified.'
+ )
- def __init__(self, action, username, path, message):
- self._version_id = None
- self._action = action
- self._path = path
- self._username = username
- self._message = message
- self._time = time.time()
-
- security.declarePublic('getVersionId')
- def getVersionId(self):
- return self._version_id
-
- security.declarePublic('setVersionId')
- def setVersionId(self, version_id):
- self._version_id = version_id
-
- security.declarePublic('getAction')
- def getAction(self):
- return self._action
-
- security.declarePublic('getTargetPath')
- def getTargetPath(self):
- return self._path
-
- security.declarePublic('getUserName')
- def getUserName(self):
- return self._username
-
- security.declarePublic('getMessage')
- def getMessage(self):
- return self._message
-
- security.declarePublic('getDateTime')
- def getDateTime(self):
- return DateTime(self._time)
-
-
-
-InitializeClass(AuditRecord)
-
-
-class CheckoutRecord(Persistent):
- """ """
-
- security = AccessControl.ClassSecurityInfo()
-
- def __init__(self, activity_id, version_id, path, username):
- self._activity_id = activity_id
- self._version_id = version_id
- self._username = username
- self._time = time.time()
- self._path = path
-
- security.declarePublic('getActivityId')
- def getActivityId(self):
- return self._activity_id
-
- security.declarePublic('getVersionId')
- def getVersionId(self):
- return self._version_id
-
- security.declarePublic('getTargetPath')
- def getTargetPath(self):
- return self._path
+ def abort(*args, **kw):
+ pass
- security.declarePublic('getUserName')
- def getUserName(self):
- return self._username
- security.declarePublic('getMessage')
- def getMessage(self):
- return self._message
- security.declarePublic('getDateTime')
- def getDateTime(self):
- return DateTime(self._time)
+class VersionControlError(Exception):
+ pass
-InitializeClass(CheckoutRecord)
+def _findUserId():
+ user = AccessControl.getSecurityManager().getUser()
+ return user.getUserName()
+
+def _findPath(object):
+ path = object.getPhysicalPath()
+ return '/'.join(path)
+
+def _findModificationTime(object):
+ """Find the last modification time for a version-controlled object.
+ The modification time reflects the latest modification time of
+ the object or any of its persistent subobjects that are not
+ themselves version-controlled objects. Note that this will
+ return None if the object has no modification time."""
+
+ mtime = getattr(object, '_p_mtime', None)
+ if mtime is None:
+ return None
+
+ latest = mtime
+ conn = object._p_jar
+ load = conn._storage.load
+ version = conn._version
+ refs = referencesf
+
+ oids=[object._p_oid]
+ done_oids={}
+ done=done_oids.has_key
+ first = 1
+
+ while oids:
+ oid=oids[0]
+ del oids[0]
+ if done(oid):
+ continue
+ done_oids[oid]=1
+ try: p, serial = load(oid, version)
+ except: pass # invalid reference!
+ else:
+ if first is not None:
+ first = None
+ else:
+ if p.find('U\x0b__vc_info__') == -1:
+ mtime = TimeStamp(serial).timeTime()
+ if mtime > latest:
+ latest = mtime
+ refs(p, oids)
-_marker = []
+ return latest
-def getattr_ex(object, name, default=_marker):
- data = getattr(object, name, default)
- if data is _marker and default is _marker:
- raise AttributeError, name
- if callable(data):
- return data()
- return data
-
-def find_id(object):
- name=getattr(object, 'id', None)
- if callable(name):
- return name()
- if name is not None:
- return name
- if hasattr(object, '__name__'):
- return object.__name__
- raise AttributeError, 'This object has no id'
=== Products/ZopeVersionControl/Version.py 1.4 => 1.5 ===
+#
+# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE
+#
+##############################################################################
+
__version__='$Revision$'[11:-2]
-from Globals import DTMLFile, InitializeClass
-from Globals import PersistentMapping
-from OFS.ObjectManager import ObjectManager
-import OFS, AccessControl, Acquisition
-from Utility import ObjectMetadata
-from Utility import AuditRecord
-import copy
-
-class Version(
- Acquisition.Implicit,
- OFS.SimpleItem.Item,
- AccessControl.Role.RoleManager
- ):
- """A "version resource", or simply "version", is a resource that
- contains a copy of a particular state (content and dead properties)
- of a version-controlled resource. A version is created by
- "checking in" a checked-out resource. The server allocates a
- distinct new URL for each new version, and this URL will never be
- used to identify any resource other than that version. The content
- and dead properties of a version never change.
-
- A "version name" is a string chosen by the server to distinguish
- one version of a version history from the other versions of that
- version history. Versions from different version histories may
- have the same version name."""
-
- security = AccessControl.ClassSecurityInfo()
- __non_versionable__ = 1
-
- meta_type = 'Version'
-
- manage_options=(
- ( {'label': 'Information', 'action':'manage_main',
- 'help': ('Zope Help', 'Version-Manage.stx')},
- {'label': 'Activity Log', 'action':'manage_activity_form',
- 'help': ('Zope Help', 'Version-ActivityLog.stx')},
- {'label': 'Properties', 'action':'manage_properties_form',
- 'help': ('Zope Help', 'Version-Properties.stx')},
- ) +
- AccessControl.Role.RoleManager.manage_options +
- OFS.SimpleItem.Item.manage_options
- )
-
- icon='misc_/ZopeVersionControl/Version.gif'
-
- security.declareProtected('View management screens',
- 'manage_main',
- 'manage_properties_form',
- 'manage_activity_form'
- )
-
- manage_main = DTMLFile('dtml/VersionManageMain', globals())
- manage_main._setName('manage_main')
- manage = manage_main
-
- manage_properties_form = DTMLFile('dtml/VersionProperties', globals())
- manage_activity_form = DTMLFile('dtml/VersionActivityLog', globals())
-
- security.declareProtected('Manage version resources', 'manage_edit')
- def manage_edit(self, REQUEST=None):
- """Change object properties."""
- if REQUEST is not None:
- message="Saved changes."
- return self.manage_properties_form(
- self, REQUEST, manage_tabs_message=message
-
- )
-
- def __init__(self, id, object, parent):
- self.id = id
- self.meta = ObjectMetadata(object)
- self.saveState(object, parent)
- # xxx - fix this
- self.records = []
-# self.co_state = PersistentMapping()
-
- def addMessageEntry(self, action, username, path, message):
- record = AuditRecord(action, username, path, message)
- record.setVersionId(self.id)
- records = self.records
- records.append(record)
- self.records = records
-
-
- def getMessageEntries(self):
- return self.records
-
- def getReadOnlyObject(self):
- """Return the object corresponding to this version."""
- object = Acquisition.aq_base(self.data)
- object._p_jar = ReadOnlyJar(object._p_jar)
- return object
-
- def saveState(self, object, parent):
- baseobj = Acquisition.aq_base(object)
- # xxx - fix this!
-# obcopy = object._getCopy(object)
- try: obcopy = object._getCopy(parent)
- except:
- # This may happen for the intial version!
- obcopy = object._getCopy(Acquisition.aq_parent(parent))
- obcopy.__non_versionable__ = 1
- self.data = obcopy
- return
+from Acquisition import Implicit, aq_parent, aq_inner
+from Globals import InitializeClass, Persistent
+from AccessControl import ClassSecurityInfo
+from Utility import VersionControlError
+from BTrees.OOBTree import OOBTree
+import tempfile, time
+
+class Version(Implicit, Persistent):
+ """A Version is a resource that contains a copy of a particular state
+ (content and dead properties) of a version-controlled resource. A
+ version is created by checking in a checked-out resource. The state
+ of a version of a version-controlled resource never changes."""
+
+ def __init__(self, version_id, object):
+ self.id = version_id
+ self.date_created = time.time()
+ self._data = None
+
+ # These attributes are set by the createVersion method of the version
+ # history at the time the version is created. The branch is the name
+ # of the branch on which the version was created. The prev attribute
+ # is the version id of the predecessor to this version. The next attr
+ # is a sequence of version ids of the successors to this version.
+ branch = 'mainline'
+ prev = None
+ next = ()
- def copyState(self):
- # xxx - fix this!
- object = self.data._getCopy(self.data)
- if hasattr(object, '__non_versionable__'):
- del object.__non_versionable__
- return object
+ security = ClassSecurityInfo()
- def getVersionId(self):
- """ """
+ security.declarePublic('getId')
+ def getId(self):
return self.id
-InitializeClass(Version)
-
-
-
-
-def historicalRevision(self, serial):
- state=self._p_jar.oldstate(self, serial)
- rev=self.__class__.__basicnew__()
- rev._p_jar=HystoryJar(self._p_jar)
- rev._p_oid=self._p_oid
- rev._p_serial=serial
- rev.__setstate__(state)
- rev._p_changed=0
- return rev
+ security.declarePrivate('saveState')
+ def saveState(self, object):
+ """Save the state of object as the state for this version of
+ a version-controlled resource."""
+ self._data = self.stateCopy(object, self)
+ security.declarePrivate('copyState')
+ def copyState(self):
+ """Return an independent deep copy of the state of the version."""
+ return self.stateCopy(self._data, self)
-class ReadOnlyJar:
- """A read-only ZODB connection-like object that prevents changes."""
-
- def __init__(self, base):
- self.__base__=base
-
- _invalidating = []
-
- def __getattr__(self, name):
- return getattr(self.__base__, name)
-
- def commit(*args, **kw):
- raise VersionWriteError(
- 'Old versions of objects cannot be modified.'
- )
+ security.declarePrivate('stateCopy')
+ def stateCopy(self, object, container):
+ """Get a deep copy of the state of an object, breaking any database
+ identity references."""
+ # TODO: this should probably use Utility._findModificationTime,
+ # though we should gauge the impact on performance.
+ if getattr(object, '_p_changed', 0):
+ get_transaction().commit(1)
+ file = tempfile.TemporaryFile()
+ object._p_jar.exportFile(object._p_oid, file)
+ file.seek(0)
+ result = container._p_jar.importFile(file)
+ file.close()
+ return result
- def abort(*args, **kw):
- pass
-class VersionWriteError(Exception):
- pass
+InitializeClass(Version)
=== Products/ZopeVersionControl/VersionHistory.py 1.2 => 1.3 ===
+#
+# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE
+#
+##############################################################################
+
__version__='$Revision$'[11:-2]
-from Globals import DTMLFile, InitializeClass
-from Globals import PersistentMapping
-from OFS.ObjectManager import ObjectManager
-import OFS, AccessControl, Acquisition
-from Utility import ObjectMetadata
-from Utility import CheckoutRecord
-from Version import Version
-import copy
-
-
-class VersionHistory(
- OFS.ObjectManager.ObjectManager,
- AccessControl.Role.RoleManager,
- OFS.SimpleItem.Item,
- ):
- """A version history resource, or simply version history, is a
- resource that contains all the versions of a particular version
- controlled resource."""
- __non_versionable__ = 1
-
- security = AccessControl.ClassSecurityInfo()
-
- meta_type = 'Version History'
-
- manage_options=(
- ( {'label': 'Contents', 'action':'manage_main',
- 'help': ('Zope Help', 'VersionHistory-Manage.stx')},
- {'label': 'Activity Log', 'action':'manage_activity_form',
- 'help': ('Zope Help', 'VersionHistory-ActivityLog.stx')},
- {'label': 'Properties', 'action':'manage_properties_form',
- 'help': ('Zope Help', 'VersionHistory-Properties.stx')},
- ) +
- AccessControl.Role.RoleManager.manage_options +
- OFS.SimpleItem.Item.manage_options
- )
-
- icon='misc_/ZopeVersionControl/VersionHistory.gif'
-
- security.declareProtected('View management screens',
- 'manage_main',
- 'manage_properties_form',
- 'manage_activity_form'
- )
-
- manage_main = DTMLFile('dtml/VersionHistoryManageMain', globals())
- manage_main._setName('manage_main')
- manage = manage_main
-
- manage_properties_form = DTMLFile('dtml/VersionHistoryProperties',
- globals())
- manage_activity_form = DTMLFile('dtml/VersionHistoryActivityLog',
- globals())
-
-
- def __init__(self, id, object):
- self.id = id
- self.meta = ObjectMetadata(object)
- self.co_state = PersistentMapping()
-
- security.declareProtected('Manage version histories', 'manage_edit')
- def manage_edit(self, REQUEST=None):
- """Change object properties."""
- if REQUEST is not None:
- message="Saved changes."
- return self.manage_properties_form(
- self, REQUEST, manage_tabs_message=message
+from Globals import InitializeClass, Persistent
+from AccessControl import ClassSecurityInfo
+from EventLog import EventLog, LogEntry
+from Utility import VersionControlError
+from ZopeVersion import ZopeVersion
+from BTrees.IOBTree import IOBTree
+from BTrees.IIBTree import IIBTree
+from BTrees.OOBTree import OOBTree
+from Acquisition import Implicit
+import sys, time
+
+
+class VersionHistory(Implicit, Persistent):
+ """A version history maintains the information about the changes
+ to a particular version-controlled resource over time."""
+
+ def __init__(self, history_id, object):
+ # The _versions mapping maps version ids to version objects. All
+ # of the actual version data is looked up there. The _labels
+ # mapping maps labels to specific version ids. The _branches map
+ # manages BranchInfo objects that maintain branch information.
+ self._eventLog = EventLog()
+ self._versions = OOBTree()
+ self._branches = OOBTree()
+ self._labels = OOBTree()
+ mainline = self.createBranch('mainline', None)
+ self.id = history_id
+
+ security = ClassSecurityInfo()
+
+ security.declarePublic('getId')
+ def getId(self):
+ return self.id
+
+ security.declarePrivate('addLogEntry')
+ def addLogEntry(self, version_id, action, path=None, message=''):
+ """Add a new log entry associated with this version history."""
+ entry = LogEntry(version_id, action, path, message)
+ self._eventLog.addEntry(entry)
+
+ security.declarePrivate('getLogEntries')
+ def getLogEntries(self):
+ """Return a sequence of the log entries for this version history."""
+ return self._eventLog.getEntries()
+
+ security.declarePrivate('getLabels')
+ def getLabels(self):
+ return self._labels.keys()
+
+ security.declarePrivate('labelVersion')
+ def labelVersion(self, version_id, label, force=0):
+ """Associate a particular version in a version history with the
+ given label, removing any existing association with that label
+ if force is true, or raising an error if force is false and
+ an association with the given label already exists."""
+ current = self._labels.get(label)
+ if current is not None:
+ if current == version_id:
+ return
+ if not force:
+ raise VersionControlError(
+ 'The label %s is already associated with a version.' % (
+ label
+ ))
+ del self._labels[label]
+ self._labels[label] = version_id
+
+ security.declarePrivate('createBranch')
+ def createBranch(self, branch_id, version_id):
+ """Create a new branch associated with the given branch_id. The
+ new branch is rooted at the version named by version_id."""
+ if self._branches.has_key(branch_id):
+ raise VersionControlError(
+ 'Activity already exists: %s' % branch_id
)
+ branch = BranchInfo(branch_id, version_id)
+ self._branches[branch_id] = branch
+ return branch
+
+ security.declarePrivate('createVersion')
+ def createVersion(self, object, branch_id):
+ """Create a new version in the line of descent named by the given
+ branch_id, returning the newly created version object."""
+ branch = self._branches.get(branch_id)
+ if branch is None:
+ branch = self.createBranch(branch_id, None)
+ if branch.name != 'mainline':
+ version_id = '%s.%d' % (branch.name, len(branch) + 1)
+ else:
+ version_id = '%d' % (len(branch) + 1)
+ version = ZopeVersion(version_id, object)
+ version._p_jar = self._p_jar
+ version.saveState(object)
+
+ # Update the predecessor, successor and branch relationships.
+ # This is something of a hedge against the future. Versions will
+ # always know enough to reconstruct their lineage without the help
+ # of optimized data structures, which will make it easier to change
+ # internals in the future if we need to.
+ latest = branch.latest()
+ if latest is not None:
+ last = self._versions[latest]
+ last.next = last.next + (version_id,)
+ version.prev = latest
+
+ # If the branch is not the mainline, store the branch name in the
+ # version. Versions have 'mainline' as the default class attribute
+ # which is the common case and saves a minor bit of storage space.
+ if branch.name != 'mainline':
+ version.branch = branch.name
+
+ branch.append(version)
+ self._versions[version_id] = version
+ return version.__of__(self)
+
+ security.declarePrivate('hasVersionId')
+ def hasVersionId(self, version_id):
+ """Return true if history contains a version with the given id."""
+ return self._versions.has_key(version_id)
+
+ security.declarePrivate('isLatestVersion')
+ def isLatestVersion(self, version_id, branch_id):
+ """Return true if version id is the latest in its branch."""
+ branch = self._branches[branch_id]
+ return version_id == branch.latest()
+
+ security.declarePrivate('getLatestVersion')
+ def getLatestVersion(self, branch_id):
+ """Return the latest version object within the given branch, or
+ None if the branch contains no versions."""
+ branch = self._branches[branch_id]
+ version = self._versions[branch.latest()]
+ return version.__of__(self)
+
+ security.declarePrivate('findBranchId')
+ def findBranchId(self, version_id):
+ """Given a version id, return the id of the branch of the version.
+ Note that we cheat, since we can find this out from the id."""
+ parts = version_id.split('.')
+ if len(parts) > 1:
+ return parts[-2]
+ return 'mainline'
+
+ security.declarePrivate('getVersionById')
+ def getVersionById(self, version_id):
+ """Return the version object named by the given version id, or
+ raise a VersionControlError if the version is not found."""
+ version = self._versions.get(version_id)
+ if version is None:
+ raise VersionControlError(
+ 'Unknown version id: %s' % version_id
+ )
+ return version.__of__(self)
- def getDescription(self):
- return getattr(self, '_description', '')
-
- def setDescription(self, description):
- self._description = description
+ security.declarePrivate('getVersionByLabel')
+ def getVersionByLabel(self, label):
+ """Return the version associated with the given label, or None
+ if no version matches the given label."""
+ version_id = self._labels.get(label)
+ version = self._versions.get(version_id)
+ if version is None:
+ return None
+ return version.__of__(self)
+
+ security.declarePrivate('getVersionByDate')
+ def getVersionByDate(self, branch_id, timestamp):
+ """Return the last version committed in the given branch on or
+ before the given time value. The timestamp should be a float
+ (time.time format) value in UTC."""
+ branch = self._branches[branch_id]
+ tvalue = int(timestamp / 60.0)
+ while 1:
+ # Try to find a version with a commit date <= the given time
+ # using the timestamp index in the branch information.
+ if branch.m_order:
+ try:
+ match = branch.m_date.maxKey(tvalue)
+ match = branch.m_order[branch.m_date[match]]
+ return self._versions[match].__of__(self)
+ except ValueError:
+ pass
+
+ # If we've run out of lineage without finding a version with
+ # a commit date <= the given timestamp, we return None. It is
+ # up to the caller to decide what to do in this situation.
+ if branch.root is None:
+ return None
+
+ # If the branch has a root (a version in another branch), then
+ # we check the root and do it again with the ancestor branch.
+ rootver = self._versions[branch.root]
+ if int(rootver.date_created / 60.0) < tvalue:
+ return rootver.__of__(self)
+ branch = self._branches[rootver.branch]
+
+ security.declarePrivate('getVersionIds')
+ def getVersionIds(self, branch_id=None):
+ """Return a sequence of version ids for the versions in this
+ version history. If a branch_id is given, only version ids
+ from that branch will be returned. Note that the sequence
+ of ids returned does not include the id of the branch root."""
+ if branch_id is not None:
+ return self._branches[branch_id].versionIds()
+ return self._versions.keys()
- def nextVersionId(self, object):
- # xxx - fix this
- next_id = getattr(self, '_next_id', 0)
- self._next_id = next_id + 1
- return'1.%d' % self._next_id
-
- def lastVersionId(self):
- ids = self.objectIds()
- ids.sort()
- return ids[-1]
-
- def createVersion(self, object):
- ver_id = self.nextVersionId(object)
- version = Version(ver_id, object, self)
- self._setObject(ver_id, version)
- return self.getVersion(ver_id)
-
- def getVersionIds(self):
- """ """
- return self.objectIds()
-
- def getVersions(self):
- """Return a sequence containing the versions of the resource."""
- # make this a tricky sequence! :)
- return self.objectValues()
-
- def getVersion(self, id):
- """ """
- return self._getOb(id, None)
-
- def delVersion(self, id):
- """ """
- self._delObject(id)
-
-
- def registerCheckout(self, activity, version_id, path, username):
- """ """
- if self.co_state.has_key(activity):
- raise ValueError, 'Already checked out in activity!'
-
- co_record = CheckoutRecord(activity, version_id, path, username)
- self.co_state[activity] = co_record
-
- def unregisterCheckout(self, activity):
- """ """
- if self.co_state.has_key(activity):
- del self.co_state[activity]
- return
-
- def getCheckoutRecords(self):
- """ """
- return self.co_state.values()
-
- def getCurrentCheckout(self, activity):
- return self.co_state.get(activity)
-
- def getAllMessageEntries(self):
- """ """
- entries = {}
- for item in self.getVersions():
- for message in item.getMessageEntries():
- entries[message.getDateTime()] = message
- keys = entries.keys()
- keys.sort()
- keys.reverse()
- result = []
- for key in keys:
- result.append(entries[key])
- return result
+InitializeClass(VersionHistory)
-InitializeClass(VersionHistory)
+class BranchInfo(Implicit, Persistent):
+ """A utility class to hold branch (line-of-descent) information. It
+ maintains the name of the branch, the version id of the root of
+ the branch and indices to allow for efficient lookups."""
+
+ def __init__(self, name, root):
+ # m_order maintains a newest-first mapping of int -> version id.
+ # m_date maintains a mapping of a packed date (int # of minutes
+ # since the epoch) to a lookup key in m_order. The two structures
+ # are separate because we only support minute precision for date
+ # lookups (and multiple versions could be added in a minute).
+ self.date_created = time.time()
+ self.m_order = IOBTree()
+ self.m_date = IIBTree()
+ self.name = name
+ self.root = root
+
+ def append(self, version):
+ """Append a version to the branch information. Note that this
+ does not store the actual version, but metadata about the
+ version to support ordering and date lookups."""
+ if len(self.m_order):
+ key = self.m_order.minKey() - 1
+ else: key = sys.maxint
+ self.m_order[key] = version.id
+ timestamp = int(version.date_created / 60.0)
+ self.m_date[timestamp] = key
+
+ def versionIds(self):
+ """Return a newest-first sequence of version ids in the branch."""
+ return self.m_order.values()
+
+ def latest(self):
+ """Return the version id of the latest version in the branch."""
+ mapping = self.m_order
+ if not len(mapping):
+ return self.root
+ return mapping[mapping.keys()[0]]
+ def __len__(self):
+ return len(self.m_order)
+InitializeClass(BranchInfo)
=== Products/ZopeVersionControl/VersionSupport.py 1.1.1.1 => 1.2 ===
=== Products/ZopeVersionControl/ZopeRepository.py 1.1.1.1 => 1.2 ===
=== Products/ZopeVersionControl/ZopeVersion.py 1.1.1.1 => 1.2 ===
=== Products/ZopeVersionControl/ZopeVersionHistory.py 1.1.1.1 => 1.2 ===
=== Products/ZopeVersionControl/__init__.py 1.1 => 1.2 ===
-
-__version__='$Revision$'[11:-2]
-
-import Repository, OFS, App
-
-def initialize(context):
-
- context.registerClass(
- instance_class = Repository.Repository,
- meta_type = 'Repository',
- permission = 'Add Repositories',
- constructors = Repository.constructors,
- icon = 'www/Repository.gif'
- )
-
- context.registerHelp()
- context.registerHelpTitle('Zope Help')
-
- registerIcon('VersionHistory.gif')
- registerIcon('Version.gif')
-
-
-def registerIcon(filename):
- setattr(OFS.misc_.misc_.ZopeVersionControl, filename,
- App.ImageFile.ImageFile('www/%s' % filename, globals())
- )
-
+##############################################################################
+#
+# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE
+#
+##############################################################################
+
+__version__='$Revision$'[11:-2]
+
+import ZopeRepository, OFS, App, Globals
+
+
+def initialize(context):
+
+ context.registerClass(
+ instance_class = ZopeRepository.ZopeRepository,
+ meta_type = 'Repository',
+ permission = 'Add Repositories',
+ constructors = ZopeRepository.constructors,
+ icon = 'www/Repository.gif'
+ )
+
+ context.registerHelp()
+ context.registerHelpTitle('Zope Help')
+
+ registerIcon('VersionHistory.gif')
+ registerIcon('Version.gif')
+
+
+ # Hackery - don't try this at home, kids. :) This is temporary for
+ # testing purposes only.
+ from VersionSupport import VersionSupport
+ import OFS.SimpleItem, App.Management
+
+ method = App.Management.Tabs.filtered_manage_options
+ def filtered_manage_options(self, REQUEST=None, method = method,
+ options = VersionSupport.manage_options):
+ result = method(self, REQUEST)
+ for item in result:
+ if item.get('label') == 'Version Control':
+ return result
+ for option in options:
+ result.append(option)
+ return result
+ App.Management.Tabs.filtered_manage_options = filtered_manage_options
+
+ for _class in (OFS.SimpleItem.Item, OFS.SimpleItem.Item_w__name__):
+ dict = _class.__dict__
+ for name, value in VersionSupport.__dict__.items():
+ if name != 'manage_options':
+ dict[name] = value
+ Globals.InitializeClass(_class)
+
+
+def registerIcon(filename):
+ setattr(OFS.misc_.misc_.ZopeVersionControl, filename,
+ App.ImageFile.ImageFile('www/%s' % filename, globals())
+ )
=== Removed File Products/ZopeVersionControl/notes.py ===