513 args[arg][b'default'] = meta['default']() |
515 args[arg][b'default'] = meta['default']() |
514 |
516 |
515 if meta['validvalues']: |
517 if meta['validvalues']: |
516 args[arg][b'validvalues'] = meta['validvalues'] |
518 args[arg][b'validvalues'] = meta['validvalues'] |
517 |
519 |
|
520 # TODO this type of check should be defined in a per-command callback. |
|
521 if (command == b'rawstorefiledata' |
|
522 and not streamclone.allowservergeneration(repo)): |
|
523 continue |
|
524 |
518 caps['commands'][command] = { |
525 caps['commands'][command] = { |
519 'args': args, |
526 'args': args, |
520 'permissions': [entry.permission], |
527 'permissions': [entry.permission], |
521 } |
528 } |
522 |
529 |
1367 # TODO handle ui output redirection |
1374 # TODO handle ui output redirection |
1368 yield repo.pushkey(encoding.tolocal(namespace), |
1375 yield repo.pushkey(encoding.tolocal(namespace), |
1369 encoding.tolocal(key), |
1376 encoding.tolocal(key), |
1370 encoding.tolocal(old), |
1377 encoding.tolocal(old), |
1371 encoding.tolocal(new)) |
1378 encoding.tolocal(new)) |
|
1379 |
|
1380 |
|
1381 @wireprotocommand( |
|
1382 'rawstorefiledata', |
|
1383 args={ |
|
1384 'files': { |
|
1385 'type': 'list', |
|
1386 'example': [b'changelog', b'manifestlog'], |
|
1387 }, |
|
1388 'pathfilter': { |
|
1389 'type': 'list', |
|
1390 'default': lambda: None, |
|
1391 'example': {b'include': [b'path:tests']}, |
|
1392 }, |
|
1393 }, |
|
1394 permission='pull') |
|
1395 def rawstorefiledata(repo, proto, files, pathfilter): |
|
1396 if not streamclone.allowservergeneration(repo): |
|
1397 raise error.WireprotoCommandError(b'stream clone is disabled') |
|
1398 |
|
1399 # TODO support dynamically advertising what store files "sets" are |
|
1400 # available. For now, we support changelog, manifestlog, and files. |
|
1401 files = set(files) |
|
1402 allowedfiles = {b'changelog', b'manifestlog'} |
|
1403 |
|
1404 unsupported = files - allowedfiles |
|
1405 if unsupported: |
|
1406 raise error.WireprotoCommandError(b'unknown file type: %s', |
|
1407 (b', '.join(sorted(unsupported)),)) |
|
1408 |
|
1409 with repo.lock(): |
|
1410 topfiles = list(repo.store.topfiles()) |
|
1411 |
|
1412 sendfiles = [] |
|
1413 totalsize = 0 |
|
1414 |
|
1415 # TODO this is a bunch of storage layer interface abstractions because |
|
1416 # it assumes revlogs. |
|
1417 for name, encodedname, size in topfiles: |
|
1418 if b'changelog' in files and name.startswith(b'00changelog'): |
|
1419 pass |
|
1420 elif b'manifestlog' in files and name.startswith(b'00manifest'): |
|
1421 pass |
|
1422 else: |
|
1423 continue |
|
1424 |
|
1425 sendfiles.append((b'store', name, size)) |
|
1426 totalsize += size |
|
1427 |
|
1428 yield { |
|
1429 b'filecount': len(sendfiles), |
|
1430 b'totalsize': totalsize, |
|
1431 } |
|
1432 |
|
1433 for location, name, size in sendfiles: |
|
1434 yield { |
|
1435 b'location': location, |
|
1436 b'path': name, |
|
1437 b'size': size, |
|
1438 } |
|
1439 |
|
1440 # We have to use a closure for this to ensure the context manager is |
|
1441 # closed only after sending the final chunk. |
|
1442 def getfiledata(): |
|
1443 with repo.svfs(name, 'rb', auditpath=False) as fh: |
|
1444 for chunk in util.filechunkiter(fh, limit=size): |
|
1445 yield chunk |
|
1446 |
|
1447 yield wireprototypes.indefinitebytestringresponse( |
|
1448 getfiledata()) |