Mercurial > hg
view mercurial/logexchange.py @ 37485:0b7475ea38cf
wireproto: port heads command to wire protocol v2
After much thought and consideration, wire protocol version 2's
commands will be defined in different functions from the existing
commands. This will make it easier to implement these commands
because it won't require shoehorning things like response formatting
and argument declaration into the same APIs.
For example, wire protocol version 1 requires that commands declare
a fixed and ordered list of argument names. It isn't really possible
to insert new arguments or have optional arguments without
breaking backwards compatibility. Wire protocol version 2, however,
uses CBOR maps for passing arguments. So arguments a) can be
optional b) can be added without BC c) can be strongly typed.
This commit starts our trek towards reimplementing the wire protocol
for version 2 with the heads command. It is pretty similar to the
existing heads command. One added feature is it can be told to
operate on only public phase changesets. This is useful for
making discovery faster when a repo has tens of thousands of
draft phase heads (such as Mozilla's "try" repository).
The HTTPv2 server-side protocol has had its `getargs()` implementation
updated to reflect that arguments are a map and not a list.
Differential Revision: https://phab.mercurial-scm.org/D3179
author | Gregory Szorc <gregory.szorc@gmail.com> |
---|---|
date | Wed, 28 Mar 2018 14:55:13 -0700 |
parents | 1ccd75027abb |
children | 0e50dda7e9c1 |
line wrap: on
line source
# logexchange.py # # Copyright 2017 Augie Fackler <raf@durin42.com> # Copyright 2017 Sean Farley <sean@farley.io> # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. from __future__ import absolute_import from .node import hex from . import ( util, vfs as vfsmod, ) # directory name in .hg/ in which remotenames files will be present remotenamedir = 'logexchange' def readremotenamefile(repo, filename): """ reads a file from .hg/logexchange/ directory and yields it's content filename: the file to be read yield a tuple (node, remotepath, name) """ vfs = vfsmod.vfs(repo.vfs.join(remotenamedir)) if not vfs.exists(filename): return f = vfs(filename) lineno = 0 for line in f: line = line.strip() if not line: continue # contains the version number if lineno == 0: lineno += 1 try: node, remote, rname = line.split('\0') yield node, remote, rname except ValueError: pass f.close() def readremotenames(repo): """ read the details about the remotenames stored in .hg/logexchange/ and yields a tuple (node, remotepath, name). It does not yields information about whether an entry yielded is branch or bookmark. To get that information, call the respective functions. """ for bmentry in readremotenamefile(repo, 'bookmarks'): yield bmentry for branchentry in readremotenamefile(repo, 'branches'): yield branchentry def writeremotenamefile(repo, remotepath, names, nametype): vfs = vfsmod.vfs(repo.vfs.join(remotenamedir)) f = vfs(nametype, 'w', atomictemp=True) # write the storage version info on top of file # version '0' represents the very initial version of the storage format f.write('0\n\n') olddata = set(readremotenamefile(repo, nametype)) # re-save the data from a different remote than this one. for node, oldpath, rname in sorted(olddata): if oldpath != remotepath: f.write('%s\0%s\0%s\n' % (node, oldpath, rname)) for name, node in sorted(names.iteritems()): if nametype == "branches": for n in node: f.write('%s\0%s\0%s\n' % (n, remotepath, name)) elif nametype == "bookmarks": if node: f.write('%s\0%s\0%s\n' % (node, remotepath, name)) f.close() def saveremotenames(repo, remotepath, branches=None, bookmarks=None): """ save remotenames i.e. remotebookmarks and remotebranches in their respective files under ".hg/logexchange/" directory. """ wlock = repo.wlock() try: if bookmarks: writeremotenamefile(repo, remotepath, bookmarks, 'bookmarks') if branches: writeremotenamefile(repo, remotepath, branches, 'branches') finally: wlock.release() def activepath(repo, remote): """returns remote path""" local = None # is the remote a local peer local = remote.local() # determine the remote path from the repo, if possible; else just # use the string given to us rpath = remote if local: rpath = remote._repo.root elif not isinstance(remote, bytes): rpath = remote._url # represent the remotepath with user defined path name if exists for path, url in repo.ui.configitems('paths'): # remove auth info from user defined url url = util.removeauth(url) if url == rpath: rpath = path break return rpath def pullremotenames(localrepo, remoterepo): """ pulls bookmarks and branches information of the remote repo during a pull or clone operation. localrepo is our local repository remoterepo is the peer instance """ remotepath = activepath(localrepo, remoterepo) bookmarks = remoterepo.listkeys('bookmarks') # on a push, we don't want to keep obsolete heads since # they won't show up as heads on the next pull, so we # remove them here otherwise we would require the user # to issue a pull to refresh the storage bmap = {} repo = localrepo.unfiltered() for branch, nodes in remoterepo.branchmap().iteritems(): bmap[branch] = [] for node in nodes: if node in repo and not repo[node].obsolete(): bmap[branch].append(hex(node)) saveremotenames(localrepo, remotepath, bmap, bookmarks)