var fs = require('fs') var proc = require('child_process') var pull = require('pull-stream') var toPull = require('stream-to-pull-stream') var paramap = require('pull-paramap') var multicb = require('multicb') var u = require('./util') var ssbRef = require('ssb-ref') exports.help = ` Usage: git ssb reconstruct ... Reconstruct pack/idx blobs referenced by git-update messages. On success, output the message ids and their pack and index blob ids. Arguments: msg ssb message id ` function reconstructBlobsForMsg(sbot, msg, cb) { var c = msg.value.content if (!c) return cb(new TypeError('Bad message: ' + JSON.stringify(msg))) var packs = Array.isArray(c.packs) && c.packs.length === 1 && c.packs var idxs = Array.isArray(c.indexes) && c.indexes.length === 1 && c.indexes var packId = packs && packs[0] && packs[0].link var idxId = idxs && idxs[0] && idxs[0].link if (!packId || !idxId) return cb(new TypeError('Message should have ' + 'exactly one pack link and idx link')) var commits = Array.isArray(c.commits) ? c.commits : [] var commitIds = commits.map(function (commit) { return commit.sha1 }) var objectIds = Array.isArray(c.object_ids) ? c.object_ids : [] var allObjectIds = commitIds.concat(objectIds) if (c.num_objects && c.num_objects !== allObjectIds.length) { return cb(new Error('Message ' + msg.key + ' ' + 'does not have information to reconstruct its blobs')) } // replace incorrectly hashed empty blob id allObjectIds = allObjectIds.map(function (id) { return id === '77e98a59e190ce51ab7ec86deb24492d1434247e' ? 'e69de29bb2d1d6434b8b29ae775ad8c2e48c5391' : id }) var tmpName = 'git-ssb-reconstruct' var child = proc.spawn('git', ['pack-objects', '-q', tmpName], { stdio: ['pipe', 'pipe', process.stderr], }) child.stdin.end(allObjectIds.join('\n')) var bufs = [] child.stdout.on('data', bufs.push.bind(bufs)) child.on('error', onError) function onError(err) { if (!cb) return console.trace(err) var _cb = cb cb = null _cb(err) } var done = multicb() child.on('exit', done()) child.stdout.on('end', done()) done(function (code) { if (code) return onError(new Error('failed to pack git objects. code: ' + code)) if (!cb) return var hash = Buffer.concat(bufs).toString('utf8').trim() if (!hash) return cb(new Error('missing hash')) var packFile = tmpName + '-' + hash + '.pack' var idxFile = tmpName + '-' + hash + '.idx' var done = multicb() pull( toPull(fs.createReadStream(packFile)), sbot.blobs.add(packId, done()) ) pull( toPull(fs.createReadStream(idxFile)), sbot.blobs.add(idxId, done()) ) done(function (err) { var done = multicb() fs.unlink(packFile, done()) fs.unlink(idxFile, done()) done()(err) done(function (err) { if (err) return cb(err) cb(null, {msg: msg, packId: packId, idxId: idxId}) }) }) }) } exports.fn = function (argv) { if (argv._.length < 1) return u.help('reconstruct') var msgIds = argv._ if (!msgIds.every(ssbRef.isMsgId)) throw 'invalid message ids: ' + JSON.stringify(argv._) u.getSbot(argv, function (err, sbot) { if (err) throw err pull( pull.values(msgIds), paramap(function (id, cb) { u.getMsg(sbot, id, function (err, msg) { if (err) throw 'failed to get message ' + id + ': ' + (err.stack||err) cb(null, msg) }) }, 8), paramap(function (msg, cb) { u.unbox(sbot, msg, function (err, msg) { if (err) throw 'failed to get decrypt message ' + id cb(null, msg) }) }, 4), pull.asyncMap(function (msg, cb) { reconstructBlobsForMsg(sbot, msg, cb) }), pull.drain(function (obj) { console.log(obj.msg.key + ' ' + obj.packId + ' ' + obj.idxId) }, function (err) { if (err) throw err sbot.close() }) ) }) }