commitcloud: sync obsmarkers

Summary:
Add obsmarkers to the things that are synced.

For the LocalService, obsmarkers are stored base64-encoded using version 1 of
the obsmarker binary format.  This is sufficient for unit tests.

Differential Revision: D7181399

fbshipit-source-id: 61377105986de561622a160134d20fdbd54bb88f
This commit is contained in:
Mark Thomas 2018-03-14 09:03:03 -07:00 committed by Saurabh Singh
parent 6660438ea2
commit 4cea23b3a9
4 changed files with 108 additions and 11 deletions

View File

@ -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

View File

@ -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())

View File

@ -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')

View File

@ -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