mirror of
https://github.com/facebook/sapling.git
synced 2024-10-10 16:57:49 +03:00
copytrace: requesting missing move data to the server when info is missing
Summary: If some contexts are not present in the local database during a retrieve, a request is send to the server for those moves one time through a 'pull(revs)' call. When doing a rebase, a check is performed beforehand to see if the local database contains the contexts of the rebased-to branch. Test Plan: Rebasing in the middle of a branch for which the moves are not known Reviewers: #sourcecontrol, rmcelroy Differential Revision: https://phabricator.fb.com/D2634365 Tasks: 8660367
This commit is contained in:
parent
6f444dd19a
commit
d0eb449a28
@ -5,6 +5,7 @@ import filldb
|
|||||||
import copytrace
|
import copytrace
|
||||||
import bundle2
|
import bundle2
|
||||||
|
|
||||||
|
|
||||||
def extsetup(ui):
|
def extsetup(ui):
|
||||||
wrapfunction(cmdutil, 'commit', filldb.commit)
|
wrapfunction(cmdutil, 'commit', filldb.commit)
|
||||||
wrapfunction(cmdutil, 'amend', filldb.amend)
|
wrapfunction(cmdutil, 'amend', filldb.amend)
|
||||||
@ -12,6 +13,7 @@ def extsetup(ui):
|
|||||||
|
|
||||||
wrapfunction(copies, 'mergecopies', copytrace.mergecopieswithdb)
|
wrapfunction(copies, 'mergecopies', copytrace.mergecopieswithdb)
|
||||||
wrapfunction(copies, 'pathcopies', copytrace.pathcopieswithdb)
|
wrapfunction(copies, 'pathcopies', copytrace.pathcopieswithdb)
|
||||||
|
wrapfunction(rebase, 'buildstate', copytrace.buildstate)
|
||||||
|
|
||||||
wrapfunction(exchange, '_pullbundle2extraprepare',
|
wrapfunction(exchange, '_pullbundle2extraprepare',
|
||||||
bundle2._pullbundle2extraprepare)
|
bundle2._pullbundle2extraprepare)
|
||||||
|
@ -3,15 +3,56 @@
|
|||||||
# This software may be used and distributed according to the terms of the
|
# This software may be used and distributed according to the terms of the
|
||||||
# GNU General Public License version 2 or any later version.
|
# GNU General Public License version 2 or any later version.
|
||||||
|
|
||||||
from mercurial import bundle2, util, exchange
|
from mercurial import bundle2, util, exchange, hg, error
|
||||||
|
from mercurial.i18n import _
|
||||||
import dbutil
|
import dbutil
|
||||||
|
|
||||||
|
|
||||||
# Temporarily used to force to load the module, will be completed later
|
# Temporarily used to force to load the module
|
||||||
def _pullbundle2extraprepare(orig, pullop, kwargs):
|
def _pullbundle2extraprepare(orig, pullop, kwargs):
|
||||||
return orig(pullop, kwargs)
|
return orig(pullop, kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def pullmoves(repo, nodelist, source="default"):
|
||||||
|
"""
|
||||||
|
Fetch move data from the server
|
||||||
|
"""
|
||||||
|
source, branches = hg.parseurl(repo.ui.expandpath(source))
|
||||||
|
# No default server defined
|
||||||
|
try:
|
||||||
|
remote = hg.peer(repo, {}, source)
|
||||||
|
except:
|
||||||
|
return
|
||||||
|
repo.ui.status(_('pulling move data from %s\n') % util.hidepassword(source))
|
||||||
|
pullop = exchange.pulloperation(repo, remote, nodelist, False)
|
||||||
|
lock = pullop.repo.lock()
|
||||||
|
try:
|
||||||
|
pullop.trmanager = exchange.transactionmanager(repo, 'pull',
|
||||||
|
remote.url())
|
||||||
|
_pullmovesbundle2(pullop)
|
||||||
|
pullop.trmanager.close()
|
||||||
|
finally:
|
||||||
|
pullop.trmanager.release()
|
||||||
|
lock.release()
|
||||||
|
|
||||||
|
|
||||||
|
def _pullmovesbundle2(pullop):
|
||||||
|
"""
|
||||||
|
Pull move data
|
||||||
|
"""
|
||||||
|
kwargs = {}
|
||||||
|
kwargs['bundlecaps'] = exchange.caps20to10(pullop.repo)
|
||||||
|
kwargs['movedatareq'] = pullop.heads
|
||||||
|
kwargs['common'] = pullop.heads
|
||||||
|
kwargs['heads'] = pullop.heads
|
||||||
|
kwargs['cg'] = False
|
||||||
|
bundle = pullop.remote.getbundle('pull', **kwargs)
|
||||||
|
try:
|
||||||
|
op = bundle2.processbundle(pullop.repo, bundle, pullop.gettransaction)
|
||||||
|
except error.BundleValueError as exc:
|
||||||
|
raise error.Abort('missing support for %s' % exc)
|
||||||
|
|
||||||
|
|
||||||
@exchange.getbundle2partsgenerator('pull:movedata')
|
@exchange.getbundle2partsgenerator('pull:movedata')
|
||||||
def _getbundlemovedata(bundler, repo, source, bundlecaps=None, heads=None,
|
def _getbundlemovedata(bundler, repo, source, bundlecaps=None, heads=None,
|
||||||
common=None, b2caps=None, **kwargs):
|
common=None, b2caps=None, **kwargs):
|
||||||
|
@ -266,6 +266,7 @@ def _processrenames(repo, ctx, renamed, move=False):
|
|||||||
if not src in m:
|
if not src in m:
|
||||||
del renamed[src]
|
del renamed[src]
|
||||||
|
|
||||||
|
|
||||||
def _forwardrenameswithdb(a, b, match=None, move=False):
|
def _forwardrenameswithdb(a, b, match=None, move=False):
|
||||||
"""
|
"""
|
||||||
find {dst@b: src@a} renames mapping where a is an ancestor of b
|
find {dst@b: src@a} renames mapping where a is an ancestor of b
|
||||||
@ -329,3 +330,18 @@ def pathcopieswithdb(orig, x, y, match=None):
|
|||||||
return _backwardrenameswithdb(x, y)
|
return _backwardrenameswithdb(x, y)
|
||||||
return _chain(x, y, _backwardrenameswithdb(x, a),
|
return _chain(x, y, _backwardrenameswithdb(x, a),
|
||||||
_forwardrenameswithdb(a, y, match=match))
|
_forwardrenameswithdb(a, y, match=match))
|
||||||
|
|
||||||
|
|
||||||
|
def buildstate(orig, repo, dest, rebaseset, collapsef, obsoletenotrebased):
|
||||||
|
"""
|
||||||
|
wraps the command to get the set of revs that will be involved in the
|
||||||
|
rebase and checks if they are in the database
|
||||||
|
"""
|
||||||
|
if rebaseset:
|
||||||
|
rev = rebaseset.first()
|
||||||
|
rebased = repo[rev]
|
||||||
|
ca = rebased.ancestor(dest)
|
||||||
|
ctxlist = list(repo.set("only(%r, %r)" % (dest.rev(), ca.rev())))
|
||||||
|
if ctxlist:
|
||||||
|
dbutil.checkpresence(repo, ctxlist)
|
||||||
|
return orig(repo, dest, rebaseset, collapsef, obsoletenotrebased)
|
||||||
|
@ -8,7 +8,8 @@
|
|||||||
# GNU General Public License version 2 or any later version.
|
# GNU General Public License version 2 or any later version.
|
||||||
|
|
||||||
|
|
||||||
from mercurial import scmutil, util
|
from mercurial import scmutil, util, commands
|
||||||
|
import bundle2
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
|
||||||
|
|
||||||
@ -79,7 +80,6 @@ def insertdata(repo, ctx, mvdict, cpdict, remote=False):
|
|||||||
ctxhash = '0'
|
ctxhash = '0'
|
||||||
else:
|
else:
|
||||||
ctxhash = str(ctx.hex())
|
ctxhash = str(ctx.hex())
|
||||||
|
|
||||||
try:
|
try:
|
||||||
insertitem(c, ctxhash, mvdict, True)
|
insertitem(c, ctxhash, mvdict, True)
|
||||||
insertitem(c, ctxhash, cpdict, False)
|
insertitem(c, ctxhash, cpdict, False)
|
||||||
@ -90,7 +90,7 @@ def insertdata(repo, ctx, mvdict, cpdict, remote=False):
|
|||||||
_close(conn)
|
_close(conn)
|
||||||
|
|
||||||
|
|
||||||
def retrievedata(repo, ctx, move=False, remote=False):
|
def retrievedata(repo, ctx, move=False, remote=False, askserver=True):
|
||||||
"""
|
"""
|
||||||
returns the {dst:src} dictonary for moves if move = True or of copies if
|
returns the {dst:src} dictonary for moves if move = True or of copies if
|
||||||
move = False for ctx
|
move = False for ctx
|
||||||
@ -113,6 +113,14 @@ def retrievedata(repo, ctx, move=False, remote=False):
|
|||||||
all_rows = c.fetchall()
|
all_rows = c.fetchall()
|
||||||
_close(conn)
|
_close(conn)
|
||||||
ret = {}
|
ret = {}
|
||||||
|
|
||||||
|
# The local database doesn't have the data for this ctx and hasn't tried
|
||||||
|
# to retrieve it yet (askserver)
|
||||||
|
if askserver and not remote and not all_rows:
|
||||||
|
_requestdata(repo, [ctx])
|
||||||
|
return retrievedata(repo, ctx, move=move, remote=remote,
|
||||||
|
askserver=False)
|
||||||
|
|
||||||
for src, dst in all_rows:
|
for src, dst in all_rows:
|
||||||
# this ctx is registered but has no move data
|
# this ctx is registered but has no move data
|
||||||
if not dst:
|
if not dst:
|
||||||
@ -142,7 +150,7 @@ def insertrawdata(repo, dic, remote=False):
|
|||||||
_close(conn)
|
_close(conn)
|
||||||
|
|
||||||
|
|
||||||
def retrieverawdata(repo, ctxlist, remote=False):
|
def retrieverawdata(repo, ctxlist, remote=False, askserver=True):
|
||||||
"""
|
"""
|
||||||
retrieves {ctxhash: {dst: src}} for ctxhash in ctxlist for moves or copies
|
retrieves {ctxhash: {dst: src}} for ctxhash in ctxlist for moves or copies
|
||||||
"""
|
"""
|
||||||
@ -167,6 +175,17 @@ def retrieverawdata(repo, ctxlist, remote=False):
|
|||||||
ret.setdefault(ctxhash.encode('utf8'), []).append((src.encode('utf8'),
|
ret.setdefault(ctxhash.encode('utf8'), []).append((src.encode('utf8'),
|
||||||
dst.encode('utf8'), mv.encode('utf8')))
|
dst.encode('utf8'), mv.encode('utf8')))
|
||||||
|
|
||||||
|
processed = ret.keys()
|
||||||
|
missing = [f for f in ctxlist if f not in processed]
|
||||||
|
|
||||||
|
# The local database doesn't have the data for this ctx and hasn't tried
|
||||||
|
# to retrieve it yet (askserver)
|
||||||
|
if askserver and not remote and missing:
|
||||||
|
_requestdata(repo, missing)
|
||||||
|
add = retrieverawdata(repo, missing, move=move, remote=remote,
|
||||||
|
askserver=False)
|
||||||
|
ret.update(add)
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
@ -187,3 +206,33 @@ def removectx(repo, ctx, remote=False):
|
|||||||
raise util.Abort('could not delete ctx from the %s database' % dbname)
|
raise util.Abort('could not delete ctx from the %s database' % dbname)
|
||||||
|
|
||||||
_close(conn)
|
_close(conn)
|
||||||
|
|
||||||
|
|
||||||
|
def checkpresence(repo, ctxlist):
|
||||||
|
"""
|
||||||
|
checks if the ctx in ctxlist are present in the database or requests for it
|
||||||
|
"""
|
||||||
|
ctxhashs = [ctx.hex() for ctx in ctxlist]
|
||||||
|
dbname, conn, c = _connect(repo, False)
|
||||||
|
try:
|
||||||
|
c.execute('SELECT DISTINCT hash FROM Moves WHERE hash IN (%s)'
|
||||||
|
% (','.join('?' * len(ctxhashs))), ctxhashs)
|
||||||
|
except:
|
||||||
|
raise util.Abort('could not check ctx presence in the %s database'
|
||||||
|
% dbname)
|
||||||
|
processed = c.fetchall()
|
||||||
|
_close(conn)
|
||||||
|
processed = [ctx[0].encode('utf8') for ctx in processed]
|
||||||
|
missing = [repo[f].node() for f in ctxlist if f not in processed]
|
||||||
|
if missing:
|
||||||
|
_requestdata(repo, missing)
|
||||||
|
|
||||||
|
|
||||||
|
def _requestdata(repo, nodelist):
|
||||||
|
"""
|
||||||
|
Requests missing ctx data to a server
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
bundle2.pullmoves(repo, nodelist)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
@ -102,3 +102,86 @@ PULLS FROM SERVER
|
|||||||
ec660297011163dd7658d444365b6590c0dd67b3|b|d|1
|
ec660297011163dd7658d444365b6590c0dd67b3|b|d|1
|
||||||
ec660297011163dd7658d444365b6590c0dd67b3|||0
|
ec660297011163dd7658d444365b6590c0dd67b3|||0
|
||||||
$ cd ..
|
$ cd ..
|
||||||
|
$ rm -rf serverrepo
|
||||||
|
$ rm -rf clientrepo
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
!! TEST 2: pulling missing move data from repo when rebasing !!
|
||||||
|
|
||||||
|
SETUP SERVER REPO
|
||||||
|
|
||||||
|
$ hg init serverrepo
|
||||||
|
$ cd serverrepo
|
||||||
|
$ touch a b
|
||||||
|
$ hg add a b
|
||||||
|
$ hg commit -m "add a b"
|
||||||
|
$ hg mv a c
|
||||||
|
$ hg commit -m "mv a c"
|
||||||
|
$ hg mv c d
|
||||||
|
$ hg commit -m "mv c d"
|
||||||
|
$ hg mv d e
|
||||||
|
$ hg commit -m "mv d e"
|
||||||
|
$ cd ..
|
||||||
|
|
||||||
|
SETUP CLIENT REPO
|
||||||
|
|
||||||
|
$ hg clone serverrepo clientrepo
|
||||||
|
updating to branch default
|
||||||
|
2 files updated, 0 files merged, 0 files removed, 0 files unresolved
|
||||||
|
$ cd clientrepo
|
||||||
|
$ hg log -G -T 'changeset: {node}\n desc: {desc}\n'
|
||||||
|
@ changeset: 2a998f0bae7cad015586b9a9e5e8a05b4b7d281f
|
||||||
|
| desc: mv d e
|
||||||
|
o changeset: d4670020b03d62be270c7f8c22d1bf620c4c8f4a
|
||||||
|
| desc: mv c d
|
||||||
|
o changeset: a003d50a0eea20c381b92e9200e323f3c945c473
|
||||||
|
| desc: mv a c
|
||||||
|
o changeset: 2f1222a290f07a1758cc927c57cc22805d6696ed
|
||||||
|
desc: add a b
|
||||||
|
$ hg update -q 2f1222
|
||||||
|
$ hg mv b z
|
||||||
|
$ hg commit -q -m "mv b z"
|
||||||
|
$ hg log -G -T 'changeset: {node}\n desc: {desc}\n'
|
||||||
|
@ changeset: d9e9933769659048c7efa24b53b2e38a1d8205b2
|
||||||
|
| desc: mv b z
|
||||||
|
| o changeset: 2a998f0bae7cad015586b9a9e5e8a05b4b7d281f
|
||||||
|
| | desc: mv d e
|
||||||
|
| o changeset: d4670020b03d62be270c7f8c22d1bf620c4c8f4a
|
||||||
|
| | desc: mv c d
|
||||||
|
| o changeset: a003d50a0eea20c381b92e9200e323f3c945c473
|
||||||
|
|/ desc: mv a c
|
||||||
|
o changeset: 2f1222a290f07a1758cc927c57cc22805d6696ed
|
||||||
|
desc: add a b
|
||||||
|
$ sqlite3 .hg/moves.db "SELECT * FROM Moves" | sort
|
||||||
|
d9e9933769659048c7efa24b53b2e38a1d8205b2|b|z|1
|
||||||
|
d9e9933769659048c7efa24b53b2e38a1d8205b2|||0
|
||||||
|
$ hg rebase -s d9e993 -d d46700
|
||||||
|
pulling move data from $TESTTMP/serverrepo
|
||||||
|
moves for 2 changesets retrieved
|
||||||
|
rebasing 4:d9e993376965 "mv b z" (tip)
|
||||||
|
saved backup bundle to $TESTTMP/clientrepo/.hg/strip-backup/d9e993376965-0332a78c-backup.hg (glob)
|
||||||
|
$ hg log -G -T 'changeset: {node}\n desc: {desc}\n'
|
||||||
|
@ changeset: daf6369e3e011c90ecd56144609c0e8fd823e83b
|
||||||
|
| desc: mv b z
|
||||||
|
| o changeset: 2a998f0bae7cad015586b9a9e5e8a05b4b7d281f
|
||||||
|
|/ desc: mv d e
|
||||||
|
o changeset: d4670020b03d62be270c7f8c22d1bf620c4c8f4a
|
||||||
|
| desc: mv c d
|
||||||
|
o changeset: a003d50a0eea20c381b92e9200e323f3c945c473
|
||||||
|
| desc: mv a c
|
||||||
|
o changeset: 2f1222a290f07a1758cc927c57cc22805d6696ed
|
||||||
|
desc: add a b
|
||||||
|
$ sqlite3 .hg/moves.db "SELECT * FROM Moves" | sort
|
||||||
|
0|a|d|0
|
||||||
|
0|b|z|0
|
||||||
|
0|||1
|
||||||
|
a003d50a0eea20c381b92e9200e323f3c945c473|a|c|1
|
||||||
|
a003d50a0eea20c381b92e9200e323f3c945c473|||0
|
||||||
|
d4670020b03d62be270c7f8c22d1bf620c4c8f4a|c|d|1
|
||||||
|
d4670020b03d62be270c7f8c22d1bf620c4c8f4a|||0
|
||||||
|
d9e9933769659048c7efa24b53b2e38a1d8205b2|b|z|1
|
||||||
|
d9e9933769659048c7efa24b53b2e38a1d8205b2|||0
|
||||||
|
daf6369e3e011c90ecd56144609c0e8fd823e83b|b|z|1
|
||||||
|
daf6369e3e011c90ecd56144609c0e8fd823e83b|||0
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user