--- a/review/api.py Sat Jun 19 15:43:22 2010 -0400
+++ b/review/api.py Sat Jun 19 17:39:57 2010 -0400
@@ -79,6 +79,10 @@
"""Raised when trying to specify an item with an identifier which does not match any items."""
pass
+class WrongEditItemType(Exception):
+ """Raised when calling edit_comment with a signoff, or vice versa."""
+ pass
+
def _split_path_dammit(p):
"""Take a file path (from the current platform) and split it. Really.
@@ -261,7 +265,7 @@
def __getitem__(self, rev):
"""Return a ReviewChangeset for the given revision."""
node = hex(self.target[str(rev)].node())
- return ReviewChangeset(self.ui, self.repo, self.target, node)
+ return ReviewChangeset(self.ui, self.repo, self.target, node, self)
def reviewed_changesets(self):
@@ -298,6 +302,28 @@
else:
items[0]._delete(self.ui, self.repo)
+ def edit_comment(self, identifier, message=None, filename=None, lines=None, style=None):
+ olds = self.get_items(identifier)
+
+ if len(olds) == 0:
+ raise UnknownIdentifier
+ elif len(olds) > 1:
+ raise AmbiguousIdentifier
+
+ old = olds[0]
+ if old.itemtype != 'comment':
+ raise WrongEditItemType()
+
+ filename = filename if filename is not None else old.filename
+ if filename and filename not in self.target[old.node].files():
+ raise FileNotInChangeset(filename)
+
+ old.hgdate = util.makedate()
+ old.filename = filename
+ old.lines = lines if lines is not None else old.lines
+ old.message = message if message is not None else old.message
+ old.style = style if style is not None else old.style
+ old._rename(self.ui, self.repo, old.identifier)
class ReviewChangeset(object):
"""The review data about one changeset in the target repository.
@@ -322,7 +348,7 @@
and full_diffs methods. See the docs of those methods for more info.
"""
- def __init__(self, ui, repo, target, node):
+ def __init__(self, ui, repo, target, node, datastore):
"""Initialize a ReviewChangeset.
You shouldn't need to create these directly -- use a ReviewDatastore
@@ -336,6 +362,7 @@
self.target = target
self.ui = ui
self.node = node
+ self.datastore = datastore
if '%s/.exists' % self.node in self.repo['tip']:
_match = lambda p: lambda fn: fn.startswith(p)
@@ -381,6 +408,7 @@
def signoffs_for_current_user(self):
return self.signoffs_for_user(self.ui.username())
+
def add_signoff(self, message, opinion='', force=False, style=''):
"""Add (and commit) a signoff for the given revision.
@@ -649,10 +677,12 @@
class _ReviewObject(object):
"""A base object for some kind of review data (a signoff or comment)."""
- def __init__(self, container, commit_message, delete_message=None):
+ def __init__(self, container, commit_message, delete_message, rename_message):
self.container = container
self.commit_message = commit_message
self.delete_message = delete_message
+ self.rename_message = rename_message
+
def _commit(self, ui, repo):
"""Write and commit this object to the given repo."""
@@ -683,6 +713,34 @@
cmdutil.commit(ui, repo, _commitfunc, [objectpath],
{ 'message': self.delete_message % self.node, 'addremove': True, })
+ def _rename(self, ui, repo, identifier):
+ """Commit this object in the given repo and mark it as a rename of identifier."""
+
+ data = self._render_data()
+ newidentifier = util.sha1(data).hexdigest()
+ newpath = os.path.join(repo.root, self.node, self.container, newidentifier)
+
+ oldpath = os.path.join(repo.root, self.node, self.container, identifier)
+
+ if oldpath == newpath:
+ # Nothing has changed. This is probably from a "touch" edit made
+ # within the same second as the previous modification time.
+ return
+
+ wlock = repo.wlock(False)
+ try:
+ cmdutil.copy(ui, repo, [oldpath, newpath], {'force': True}, rename=True)
+ finally:
+ wlock.release()
+
+ with open(newpath, 'w') as objectfile:
+ objectfile.write(data)
+
+ cmdutil.commit(ui, repo, _commitfunc, [oldpath, newpath],
+ { 'message': self.rename_message % self.node })
+
+ self.identifier = newidentifier
+
@property
def local_datetime(self):
@@ -705,6 +763,7 @@
comment.message
comment.style
comment.identifier
+ comment.itemtype
Each item is a string, except for lines, hgdate, and local_datetime.
@@ -731,10 +790,10 @@
tip_comments = tip_review.comments
"""
- super(ReviewComment, self).__init__(
- container='comments', commit_message=messages.COMMIT_COMMENT,
- delete_message=messages.DELETE_COMMENT
- )
+ super(ReviewComment, self).__init__(container='comments',
+ commit_message=messages.COMMIT_COMMENT,
+ delete_message=messages.DELETE_COMMENT,
+ rename_message=messages.RENAME_COMMENT)
self.author = author
self.hgdate = hgdate
self.node = node
@@ -743,6 +802,7 @@
self.message = message
self.style = style
self.identifier = identifier
+ self.itemtype = 'comment'
def _render_data(self):
"""Render the data of this comment into a string for writing to disk.
@@ -752,7 +812,7 @@
"""
rendered_date = util.datestr(self.hgdate)
- lines = ','.join(self.lines)
+ lines = ','.join(map(str, self.lines))
return files.COMMENT_FILE_TEMPLATE % ( self.author, rendered_date,
self.node, self.filename, lines, self.style, self.message )
@@ -765,6 +825,7 @@
self.node,
self.filename,
self.lines,
+ self.style,
self.message,
'\n',
]))
@@ -785,6 +846,7 @@
signoff.message
signoff.style
signoff.identifier
+ signoff.itemtype
Each item is a string, except for hgdate and local_datetime.
@@ -809,10 +871,10 @@
tip_signoffs = tip_review.signoffs
"""
- super(ReviewSignoff, self).__init__(
- container='signoffs', commit_message=messages.COMMIT_SIGNOFF,
+ super(ReviewSignoff, self).__init__(container='signoffs',
+ commit_message=messages.COMMIT_SIGNOFF,
delete_message=messages.DELETE_SIGNOFF,
- )
+ rename_message=messages.RENAME_SIGNOFF)
self.author = author
self.hgdate = hgdate
self.node = node
@@ -820,6 +882,7 @@
self.message = message
self.style = style
self.identifier = identifier
+ self.itemtype = 'signoff'
def _render_data(self):
"""Render the data of this signoff into a string for writing to disk.