Mercurial > hgstorage
changeset 5:0be8ec7b9717
add merging of conflicts
| author | Radomir Dopieralski <sheep-devel@sheep.art.pl> |
|---|---|
| date | Fri, 13 May 2011 23:28:54 +0200 |
| parents | 52f6805311fd |
| children | 161239cbd12b |
| files | hgstorage.py test_repo.py |
| diffstat | 2 files changed, 76 insertions(+), 27 deletions(-) [+] |
line wrap: on
line diff
--- a/hgstorage.py Fri May 13 22:14:46 2011 +0200 +++ b/hgstorage.py Fri May 13 23:28:54 2011 +0200 @@ -13,6 +13,8 @@ import mercurial.ui import mercurial.revlog import mercurial.context +import mercurial.util +import mercurial.simplemerge class Storage(object): @@ -72,7 +74,7 @@ class Session(object): def __init__(self, storage): self.storage = storage - self.update() + self._update() def __contains__(self, key): filename = self.storage._key_to_file(key) @@ -81,47 +83,69 @@ def __getitem__(self, key): return Item(self, key) - def update(self): + def _update(self): + """Reset the tip of the repository.""" + self.tip = self.storage.repo.changectx('tip') + def _get_parents(self, filename, parent_rev): + """Calculate the parents of the commit.""" + + if parent_rev is None: + return ('tip', None) + try: + filetip = self.tip[filename] + except mercurial.revlog.LookupError: + if parent_rev != -1: + raise IndexError("no such parent revision %r" % parent_rev) + return ('tip', None) + + last_rev = filetip.filerev() + if parent_rev > last_rev: + raise IndexError("no such parent revision %r" % parent_rev) + if parent_rev == last_rev: + return ('tip', None) + + return (parent_rev, last_rev) + + def _merge(self, filename, parent, other, data): + """Merge conflicting changes.""" + + filetip = self.tip[filename] + parent_data = filetip.filectx(parent).data() + other_data = filetip.filectx(other).data() + if (mercurial.util.binary(data) or + mercurial.util.binary(parent_data) or + mercurial.util.binary(other_data)): + raise ValueError("conflict when saving binary data") + m3 = mercurial.simplemerge.Merge3Text(parent_data, other_data, data) + return ''.join(m3.merge_lines(start_marker='<<<<<<<', + mid_marker='=======', + end_marker='>>>>>>>', + base_marker='|||||||')) + def save(self, key, data, user=None, message=None, parent_rev=None, encoding=None, force=False): """Save a new revision of the item.""" if encoding: data = data.encode(encoding) - is_text = True - else: - is_text = False filename = self.storage._key_to_file(key) - if parent_rev is None: - parents = ('tip', None) - else: - try: - filetip = self.tip[filename] - last_rev = filetip.filerev() - except mercurial.revlog.LookupError: - last_rev = -1 - if force: - parents = (last_rev, parent_rev) - elif parent_rev == last_rev: - parents = ('tip', None) - elif is_text: - # XXX merge the data - parents = (last_rev, parent_rev) - else: - raise ValueError("conflict when saving binary data") + parent, other = self._get_parents(filename, parent_rev) + + if other is not None and not force: + data = self._merge(filename, parent, other, data) def filectxfn(repo, memctx, path): return mercurial.context.memfilectx(path, data, False, False, None) ctx = mercurial.context.memctx( - repo=self.storage.repo, parents=parents, - text=message or "Commit Message", files=[filename], + repo=self.storage.repo, parents=(parent, other), + text=message or "saved", files=[filename], filectxfn=filectxfn, user=user or "user") self.storage.repo.commitctx(ctx) - self.update() + self._update() def filectx(self, key, rev=None): filename = self.storage._key_to_file(key)
--- a/test_repo.py Fri May 13 22:14:46 2011 +0200 +++ b/test_repo.py Fri May 13 23:28:54 2011 +0200 @@ -65,9 +65,34 @@ def test_double_save(self, storage): s = storage.session() - s.save('title', 'text1', 'user1', 'message1', -1, encoding='utf8') - s.save('title', 'text2', 'user2', 'message2', 0, encoding='utf8') + s.save('title', u'text1', 'user1', 'message1', -1, encoding='utf8') + s.save('title', u'text2', 'user2', 'message2', 0, encoding='utf8') data = s.load('title') assert 'text2' == data data = s.load('title', 0) assert 'text1' == data + + def test_save_merge(self, storage): + s = storage.session() + s.save('title', u'a\ntext\nb', 'user1', 'message1', -1, encoding='utf8') + data = s.load('title') + assert 'a\ntext\nb' == data + s.save('title', u'a\ntext\nbb', 'user1', 'message1', 0, encoding='utf8') + data = s.load('title') + assert 'a\ntext\nbb' == data + s.save('title', u'aa\ntext\nb', 'user2', 'message2', 0, encoding='utf8') + data = s.load('title') + assert 'aa\ntext\nbb' == data + + def test_save_conflict(self, storage): + s = storage.session() + s.save('title', u'a\ntext\nb', 'user1', 'message1', -1, encoding='utf8') + data = s.load('title') + assert 'a\ntext\nb' == data + s.save('title', u'a\ntext\nbb', 'user1', 'message1', 0, encoding='utf8') + data = s.load('title') + assert 'a\ntext\nbb' == data + s.save('title', u'a\ntext\na', 'user2', 'message2', 0, encoding='utf8') + data = s.load('title') + assert 'a\ntext\n<<<<<<<\nbb|||||||\nb=======\na>>>>>>>\n' == data +
