diff --git a/hgext/commitcloud/__init__.py b/hgext/commitcloud/__init__.py index 4ad44efe48..281bf3c41d 100644 --- a/hgext/commitcloud/__init__.py +++ b/hgext/commitcloud/__init__.py @@ -19,6 +19,35 @@ from __future__ import absolute_import +from mercurial import ( + obsolete, + util, +) + from . import commitcloudcommands cmdtable = commitcloudcommands.cmdtable + +def reposetup(ui, repo): + def finalize(tr): + if util.safehasattr(tr, '_commitcloudskippendingobsmarkers'): + return + markers = tr.changes['obsmarkers'] + if markers: + f = tr.opener('commitcloudpendingobsmarkers', 'ab') + try: + offset = f.tell() + tr.add('commitcloudpendingobsmarkers', offset) + # offset == 0: new file - add the version header + data = b''.join(obsolete.encodemarkers(markers, offset == 0, + obsolete._fm1version)) + f.write(data) + finally: + f.close() + + class commitcloudrepo(repo.__class__): + def transaction(self, *args, **kwargs): + tr = super(commitcloudrepo, self).transaction(*args, **kwargs) + tr.addfinalize('commitcloudobsmarkers', finalize) + return tr + repo.__class__ = commitcloudrepo diff --git a/hgext/commitcloud/commitcloudcommands.py b/hgext/commitcloud/commitcloudcommands.py index 877c9db284..57af0ff267 100644 --- a/hgext/commitcloud/commitcloudcommands.py +++ b/hgext/commitcloud/commitcloudcommands.py @@ -13,6 +13,7 @@ from mercurial.i18n import _ from mercurial import ( commands, node, + obsolete, registrar, ) @@ -47,12 +48,18 @@ def cloudsync(ui, repo, **opts): if not synced: # The local repo has changed. We must send these changes to the # cloud. + obsmarkers = [] + if repo.svfs.exists('commitcloudpendingobsmarkers'): + with repo.svfs.open('commitcloudpendingobsmarkers') as f: + _version, obsmarkers = obsolete._readmarkers(f.read()) synced, cloudrefs = serv.updatereferences( lastsyncstate.version, lastsyncstate.heads, localheads, - lastsyncstate.bookmarks, localbookmarks) + lastsyncstate.bookmarks, localbookmarks, obsmarkers) if synced: lastsyncstate.update(cloudrefs.version, localheads, localbookmarks) + if obsmarkers: + repo.svfs.unlink('commitcloudpendingobsmarkers') def _applycloudchanges(ui, repo, lastsyncstate, cloudrefs): pullcmd, pullopts = _getcommandandoptions('^pull') @@ -64,6 +71,9 @@ def _applycloudchanges(ui, repo, lastsyncstate, cloudrefs): # Merge cloud bookmarks into the repo _mergebookmarks(ui, repo, cloudrefs.bookmarks, lastsyncstate.bookmarks) + # Merge obsmarkers + _mergeobsmarkers(ui, repo, cloudrefs.obsmarkers) + # We have now synced the repo to the cloud version. Store this. lastsyncstate.update(cloudrefs.version, cloudrefs.heads, cloudrefs.bookmarks) @@ -107,6 +117,11 @@ def _mergebookmarks(ui, repo, cloudbookmarks, lastsyncbookmarks): changes.append((name, None)) repo._bookmarks.applychanges(repo, tr, changes) +def _mergeobsmarkers(ui, repo, obsmarkers): + with repo.wlock(), repo.lock(), repo.transaction('commitcloud-obs') as tr: + tr._commitcloudskippendingobsmarkers = True + repo.obsstore.add(tr, obsmarkers) + def _forkname(ui, name, othernames): hostname = ui.config('commitcloud', 'hostname', socket.gethostname()) diff --git a/hgext/commitcloud/service.py b/hgext/commitcloud/service.py index 4d0143c01a..5d1a4e437f 100644 --- a/hgext/commitcloud/service.py +++ b/hgext/commitcloud/service.py @@ -5,15 +5,18 @@ from __future__ import absolute_import +import base64 import collections import json import os from mercurial import ( error, + obsolete, ) -References = collections.namedtuple('References', 'version heads bookmarks') +References = collections.namedtuple('References', + 'version heads bookmarks obsmarkers') class LocalService(object): """Local commit-cloud service implemented using files on disk. @@ -42,13 +45,18 @@ class LocalService(object): with open(filename, 'w') as f: json.dump(data, f) - def _makereferences(self, data): + def _makereferences(self, data, baseversion): """Makes a References object from JSON data""" version = data['version'] newheads = [h.encode() for h in data['heads']] newbookmarks = {n.encode('utf-8'): v.encode() for n, v in data['bookmarks'].items()} - return References(version, newheads, newbookmarks) + encobsmarkers = sum((data['obsmarkers'][str(n + 1)] + for n in range(baseversion, version)), []) + decobsmarkers = b''.join([base64.b64decode(m) for m in encobsmarkers]) + obsmarkers = obsolete._fm1readmarkers(decobsmarkers, + 0, len(decobsmarkers)) + return References(version, newheads, newbookmarks, obsmarkers) def getreferences(self, baseversion): """Gets the current references if they differ from the base version @@ -59,16 +67,16 @@ class LocalService(object): self._ui.debug( 'commitcloud local service: ' 'get_references for current version %s\n' % version) - return References(version, None, None) + return References(version, None, None, None) else: self._ui.debug( 'commitcloud local service: ' 'get_references for versions from %s to %s\n' % (baseversion, version)) - return self._makereferences(data) + return self._makereferences(data, baseversion) def updatereferences(self, version, oldheads, newheads, oldbookmarks, - newbookmarks): + newbookmarks, newobsmarkers): """Updates the references to a new version. If the update was successful, returns `(True, references)`, where @@ -80,16 +88,21 @@ class LocalService(object): """ data = self._load() if version != data['version']: - return False, self._makereferences(data) - data['version'] = data['version'] + 1 + return False, self._makereferences(data, version) + newversion = data['version'] + 1 + data['version'] = newversion data['heads'] = newheads data['bookmarks'] = newbookmarks + encodedmarkers = [base64.b64encode(m) + for m in obsolete.encodemarkers(newobsmarkers, False, + obsolete._fm1version)] + data['obsmarkers'][str(newversion)] = encodedmarkers self._ui.debug( 'commitcloud local service: ' 'update_references to %s (%s heads, %s bookmarks)\n' - % (data['version'], len(data['heads']), len(data['bookmarks']))) + % (newversion, len(data['heads']), len(data['bookmarks']))) self._save(data) - return True, References(data['version'], None, None) + return True, References(newversion, None, None, None) def get(ui): servicetype = ui.config('commitcloud', 'servicetype') diff --git a/tests/test-fb-hgext-commitcloud-sync.t b/tests/test-fb-hgext-commitcloud-sync.t index fddbedf74b..e54ee27d35 100644 --- a/tests/test-fb-hgext-commitcloud-sync.t +++ b/tests/test-fb-hgext-commitcloud-sync.t @@ -1,5 +1,6 @@ $ cat >> $HGRCPATH << EOF > [extensions] + > fbamend = > infinitepush = > commitcloud = > [ui] @@ -10,6 +11,8 @@ > hostname = testhost > [alias] > tglog = log -G --template "{node|short} '{desc}' {bookmarks}\n" + > [experimental] + > evolution = createmarkers, allowunstable > EOF $ mkcommit() { @@ -142,3 +145,40 @@ Move the bookmark also on the first client, it should be forked in the sync o d20a80d4def3 'base' $ cd .. + +Amend a commit + $ cd client1 + $ echo more >> commit1 + $ hg amend --rebase -m "commit1 amended" + rebasing 2:02f6fc2b7154 "commit2" (bookmark1) + $ hg pushbackup -q + $ hg cloudsync + $ hg tglog + o 48610b1a7ec0 'commit2' bookmark1 + | + @ a7bb357e7299 'commit1 amended' bookmark1-testhost + | + o d20a80d4def3 'base' + + $ cd .. + +Sync the amended commit to the other client + $ cd client2 + $ hg cloudsync + pulling from ssh://user@dummy/server + searching for changes + adding changesets + adding manifests + adding file changes + added 2 changesets with 1 changes to 2 files (+1 heads) + new changesets a7bb357e7299:48610b1a7ec0 + (run 'hg heads' to see heads, 'hg merge' to merge) + $ hg up -q tip + $ hg tglog + @ 48610b1a7ec0 'commit2' bookmark1 + | + o a7bb357e7299 'commit1 amended' bookmark1-testhost + | + o d20a80d4def3 'base' + + $ test ! -f .hg/store/commitcloudpendingobsmarkers