sapling/infinitepush/bundleparts.py
Durham Goode 0ea3abac80 infinitepush: send treepack parts with the client push
This makes the client side pushes also include tree packs in the push.

Also adds a test to see that the tree parts round trip between two tree-only
clients.

This was reverted in D921 because clients couldn't push old non-tree commits to
infinitepush anymore. I've fixed the issue and added a test to this commit.

Differential Revision: https://phab.mercurial-scm.org/D1017
2017-11-01 17:10:05 -07:00

115 lines
3.7 KiB
Python

# Copyright 2017 Facebook, Inc.
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
from .common import (
encodebookmarks,
isremotebooksenabled,
)
from mercurial import (
bundle2,
changegroup,
error,
extensions,
revsetlang,
)
from mercurial.i18n import _
scratchbranchparttype = 'b2x:infinitepush'
scratchbookmarksparttype = 'b2x:infinitepushscratchbookmarks'
def getscratchbranchparts(repo, peer, outgoing, confignonforwardmove,
ui, bookmark, create):
if not outgoing.missing:
raise error.Abort(_('no commits to push'))
if scratchbranchparttype not in bundle2.bundle2caps(peer):
raise error.Abort(_('no server support for %r') % scratchbranchparttype)
_validaterevset(repo, revsetlang.formatspec('%ln', outgoing.missing),
bookmark)
supportedversions = changegroup.supportedoutgoingversions(repo)
# Explicitly avoid using '01' changegroup version in infinitepush to
# support general delta
supportedversions.discard('01')
cgversion = min(supportedversions)
_handlelfs(repo, outgoing.missing)
cg = changegroup.makestream(repo, outgoing, cgversion, 'push')
params = {}
params['cgversion'] = cgversion
if bookmark:
params['bookmark'] = bookmark
# 'prevbooknode' is necessary for pushkey reply part
params['bookprevnode'] = ''
if bookmark in repo:
params['bookprevnode'] = repo[bookmark].hex()
if create:
params['create'] = '1'
if confignonforwardmove:
params['force'] = '1'
# Do not send pushback bundle2 part with bookmarks if remotenames extension
# is enabled. It will be handled manually in `_push()`
if not isremotebooksenabled(ui):
params['pushbackbookmarks'] = '1'
parts = []
# .upper() marks this as a mandatory part: server will abort if there's no
# handler
parts.append(bundle2.bundlepart(
scratchbranchparttype.upper(),
advisoryparams=params.iteritems(),
data=cg))
try:
treemod = extensions.find('treemanifest')
mfnodes = []
for node in outgoing.missing:
mfnodes.append(('', repo[node].manifestnode()))
# Only include the tree parts if they all exist
if not repo.manifestlog.datastore.getmissing(mfnodes):
parts.append(treemod.createtreepackpart(
repo, outgoing, treemod.TREEGROUP_PARTTYPE2))
except KeyError:
pass
return parts
def getscratchbookmarkspart(peer, bookmarks):
if scratchbookmarksparttype not in bundle2.bundle2caps(peer):
raise error.Abort(
_('no server support for %r') % scratchbookmarksparttype)
return bundle2.bundlepart(
scratchbookmarksparttype.upper(),
data=encodebookmarks(bookmarks))
def _validaterevset(repo, revset, bookmark):
"""Abort if the revs to be pushed aren't valid for a scratch branch."""
if not repo.revs(revset):
raise error.Abort(_('nothing to push'))
if bookmark:
# Allow bundle with many heads only if no bookmark is specified
heads = repo.revs('heads(%r)', revset)
if len(heads) > 1:
raise error.Abort(
_('cannot push more than one head to a scratch branch'))
def _handlelfs(repo, missing):
'''Special case if lfs is enabled
If lfs is enabled then we need to call prepush hook
to make sure large files are uploaded to lfs
'''
try:
lfsmod = extensions.find('lfs')
lfsmod.wrapper.uploadblobsfromrevs(repo, missing)
except KeyError:
# Ignore if lfs extension is not enabled
return