diff -r 6d43b39fbaa0 -r 03ff17a4bf53 hgext/infinitepush/infinitepushcommands.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hgext/infinitepush/infinitepushcommands.py Fri Feb 09 13:39:15 2018 +0530 @@ -0,0 +1,102 @@ +# Copyright 2016 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. +""" +config:: + + [infinitepush] + # limit number of files in the node metadata. This is to make sure we don't + # waste too much space on huge codemod commits. + metadatafilelimit = 100 +""" + +from __future__ import absolute_import + +import json + +from mercurial.node import bin +from mercurial.i18n import _ + +from mercurial import ( + copies as copiesmod, + encoding, + error, + hg, + patch, + registrar, + scmutil, + util, +) + +from . import ( + backupcommands, + common, +) + +downloadbundle = common.downloadbundle + +cmdtable = backupcommands.cmdtable +command = registrar.command(cmdtable) + +@command('debugfillinfinitepushmetadata', + [('', 'node', [], 'node to fill metadata for')]) +def debugfillinfinitepushmetadata(ui, repo, **opts): + '''Special command that fills infinitepush metadata for a node + ''' + + nodes = opts['node'] + if not nodes: + raise error.Abort(_('nodes are not specified')) + + filelimit = ui.configint('infinitepush', 'metadatafilelimit', 100) + nodesmetadata = {} + for node in nodes: + index = repo.bundlestore.index + if not bool(index.getbundle(node)): + raise error.Abort(_('node %s is not found') % node) + + if node not in repo: + newbundlefile = downloadbundle(repo, bin(node)) + bundlepath = "bundle:%s+%s" % (repo.root, newbundlefile) + bundlerepo = hg.repository(ui, bundlepath) + repo = bundlerepo + + p1 = repo[node].p1().node() + diffopts = patch.diffallopts(ui, {}) + match = scmutil.matchall(repo) + chunks = patch.diff(repo, p1, node, match, None, diffopts, relroot='') + difflines = util.iterlines(chunks) + + states = 'modified added removed deleted unknown ignored clean'.split() + status = repo.status(p1, node) + status = zip(states, status) + + filestatus = {} + for state, files in status: + for f in files: + filestatus[f] = state + + diffstat = patch.diffstatdata(difflines) + changed_files = {} + copies = copiesmod.pathcopies(repo[p1], repo[node]) + for filename, adds, removes, isbinary in diffstat[:filelimit]: + # use special encoding that allows non-utf8 filenames + filename = encoding.jsonescape(filename, paranoid=True) + changed_files[filename] = { + 'adds': adds, 'removes': removes, 'isbinary': isbinary, + 'status': filestatus.get(filename, 'unknown') + } + if filename in copies: + changed_files[filename]['copies'] = copies[filename] + + output = {} + output['changed_files'] = changed_files + if len(diffstat) > filelimit: + output['changed_files_truncated'] = True + nodesmetadata[node] = output + + with index: + for node, metadata in nodesmetadata.iteritems(): + dumped = json.dumps(metadata, sort_keys=True) + index.saveoptionaljsonmetadata(node, dumped)