'use strict';

Object.defineProperty(exports, '__esModule', { value: true });

function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }

var _getIterator = _interopDefault(require('babel-runtime/core-js/get-iterator'));
var _Set = _interopDefault(require('babel-runtime/core-js/set'));
var _regeneratorRuntime = _interopDefault(require('babel-runtime/regenerator'));
var _toConsumableArray = _interopDefault(require('babel-runtime/helpers/toConsumableArray'));
var _asyncToGenerator = _interopDefault(require('babel-runtime/helpers/asyncToGenerator'));
var buffer = require('buffer');
var stream = require('stream');
var pad = _interopDefault(require('pad'));
var pako = _interopDefault(require('pako'));
var crypto = _interopDefault(require('crypto'));
var models_js = require('./models.js');
var managers_js = require('./managers.js');
var _Map = _interopDefault(require('babel-runtime/core-js/map'));
var _Promise = _interopDefault(require('babel-runtime/core-js/promise'));
var _Number$isNaN = _interopDefault(require('babel-runtime/core-js/number/is-nan'));
var through2 = _interopDefault(require('through2'));
var listpack = _interopDefault(require('git-list-pack'));
var peek = _interopDefault(require('buffer-peek-stream'));
var applyDelta = _interopDefault(require('git-apply-delta'));
var marky = _interopDefault(require('marky'));
var utils_js = require('./utils.js');

/**
 * Read and/or write to the git config file(s)
 * @param {GitRepo} repo - A {@link Git} object matching `{gitdir, fs}`
 * @param {Object} args - Arguments object
 * @param {string} args.path -  The key of the git config entry.
 * @param {string} [args.value] - A value to store at that path.
 * @returns {Promise<any>} - Resolves with the config value
 *
 * If no `value` is provided, it does a read.
 * If a `value` is provided, it does a write.
 *
 * @example
 * let repo = new Git({fs, dir: '.'})
 *
 * // Write config value
 * await config(repo, {
 *   path: 'user.name',
 *   value: 'Mr. Test'
 * })
 *
 * // Read config value
 * let value = await config(repo, {
 *   path: 'user.name'
 * })
 */
var config = function () {
  var _ref2 = _asyncToGenerator(_regeneratorRuntime.mark(function _callee(_ref, args) {
    var gitdir = _ref.gitdir,
        _fs = _ref.fs;

    var fs, path, value, config, _value;

    return _regeneratorRuntime.wrap(function _callee$(_context) {
      while (1) {
        switch (_context.prev = _context.next) {
          case 0:
            fs = new models_js.FileSystem(_fs);
            path = args.path, value = args.value;
            _context.next = 4;
            return managers_js.GitConfigManager.get({ fs: fs, gitdir: gitdir });

          case 4:
            config = _context.sent;

            if (!(value === undefined && !args.hasOwnProperty('value'))) {
              _context.next = 12;
              break;
            }

            _context.next = 8;
            return config.get(path);

          case 8:
            _value = _context.sent;
            return _context.abrupt('return', _value);

          case 12:
            _context.next = 14;
            return config.set(path, value);

          case 14:
            _context.next = 16;
            return managers_js.GitConfigManager.save({ fs: fs, gitdir: gitdir, config: config });

          case 16:
          case 'end':
            return _context.stop();
        }
      }
    }, _callee, this);
  }));

  return function config(_x, _x2) {
    return _ref2.apply(this, arguments);
  };
}();

var types = {
  commit: 16,
  tree: 32,
  blob: 48,
  tag: 64

  /**
   * Push a branch
   *
   * @param {GitRepo} repo - A {@link Git} object matching `{gitdir, fs}`
   * @param {Object} args - Arguments object
   * @param {integer} [args.depth=0] - Determines how much of the git repository's history to retrieve. If not specified it defaults to 0 which means the entire repo history.
   * @param {string} [args.ref=undefined] - Which branch to push. By default this is the currently checked out branch of the repository.
   * @param {string} [args.authUsername=undefined] - The username to use with Basic Auth
   * @param {string} [args.authPassword=undefined] - The password to use with Basic Auth
   * @param {string} [args.url=undefined] - The URL of the remote git server. The default is the value set in the git config for that remote.
   * @param {string} [args.remote='origin'] - If URL is not specified, determines which remote to use.
   * @returns {Promise<void>} - Resolves successfully when push completes
   *
   * @example
   * let repo = new Git({fs, dir: '.'})
   * await push(repo, {
   *   remote: 'origin',
   *   ref: 'master',
   *   authUsername: process.env.GITHUB_TOKEN,
   *   authPassword: process.env.GITHUB_TOKEN
   * })
   */
};var push = function () {
  var _ref3 = _asyncToGenerator(_regeneratorRuntime.mark(function _callee(_ref, _ref2) {
    var gitdir = _ref.gitdir,
        _fs = _ref.fs;
    var ref = _ref2.ref,
        remote = _ref2.remote,
        url = _ref2.url,
        authUsername = _ref2.authUsername,
        authPassword = _ref2.authPassword;
    var fs, fullRef, oid, httpRemote, commits, objects, packstream, oldoid, response;
    return _regeneratorRuntime.wrap(function _callee$(_context) {
      while (1) {
        switch (_context.prev = _context.next) {
          case 0:
            fs = new models_js.FileSystem(_fs);
            // TODO: Figure out how pushing tags works. (This only works for branches.)

            remote = remote || 'origin';

            if (!(url === undefined)) {
              _context.next = 6;
              break;
            }

            _context.next = 5;
            return config({ fs: fs, gitdir: gitdir }, { path: 'remote.' + remote + '.url' });

          case 5:
            url = _context.sent;

          case 6:
            fullRef = ref.startsWith('refs/') ? ref : 'refs/heads/' + ref;
            _context.next = 9;
            return managers_js.GitRefManager.resolve({ fs: fs, gitdir: gitdir, ref: ref });

          case 9:
            oid = _context.sent;
            httpRemote = new managers_js.GitRemoteHTTP(url);

            if (authUsername !== undefined && authPassword !== undefined) {
              httpRemote.auth = {
                username: authUsername,
                password: authPassword
              };
            }
            _context.next = 14;
            return httpRemote.preparePush();

          case 14:
            _context.next = 16;
            return listCommits({ fs: fs, gitdir: gitdir }, {
              start: [oid],
              finish: httpRemote.refs.values()
            });

          case 16:
            commits = _context.sent;
            _context.next = 19;
            return listObjects({ fs: fs, gitdir: gitdir }, { oids: commits });

          case 19:
            objects = _context.sent;
            packstream = new stream.PassThrough();
            oldoid = httpRemote.refs.get(fullRef) || '0000000000000000000000000000000000000000';

            packstream.write(models_js.GitPktLine.encode(oldoid + ' ' + oid + ' ' + fullRef + '\0 report-status\n'));
            packstream.write(models_js.GitPktLine.flush());
            pack({ fs: fs, gitdir: gitdir }, {
              oids: [].concat(_toConsumableArray(objects)),
              outputStream: packstream
            });
            _context.next = 27;
            return httpRemote.push(packstream);

          case 27:
            response = _context.sent;
            return _context.abrupt('return', response);

          case 29:
          case 'end':
            return _context.stop();
        }
      }
    }, _callee, this);
  }));

  return function push(_x, _x2) {
    return _ref3.apply(this, arguments);
  };
}();

/**
 * @ignore
 */
var listCommits = function () {
  var _ref6 = _asyncToGenerator(_regeneratorRuntime.mark(function _callee3(_ref4, _ref5) {
    var gitdir = _ref4.gitdir,
        _fs = _ref4.fs;

    // Because git commits are named by their hash, there is no
    // way to construct a cycle. Therefore we won't worry about
    // setting a default recursion limit.
    var walk = function () {
      var _ref8 = _asyncToGenerator(_regeneratorRuntime.mark(function _callee2(oid) {
        var _ref9, type, object, commit, parents, _iteratorNormalCompletion3, _didIteratorError3, _iteratorError3, _iterator3, _step3;

        return _regeneratorRuntime.wrap(function _callee2$(_context2) {
          while (1) {
            switch (_context2.prev = _context2.next) {
              case 0:
                visited.add(oid);
                _context2.next = 3;
                return managers_js.GitObjectManager.read({ fs: fs, gitdir: gitdir, oid: oid });

              case 3:
                _ref9 = _context2.sent;
                type = _ref9.type;
                object = _ref9.object;

                if (!(type !== 'commit')) {
                  _context2.next = 8;
                  break;
                }

                throw new Error('Expected type commit but type is ' + type);

              case 8:
                commit = models_js.GitCommit.from(object);
                parents = commit.headers().parent;
                _iteratorNormalCompletion3 = true;
                _didIteratorError3 = false;
                _iteratorError3 = undefined;
                _context2.prev = 13;
                _iterator3 = _getIterator(parents);

              case 15:
                if (_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done) {
                  _context2.next = 23;
                  break;
                }

                oid = _step3.value;

                if (!(!finishingSet.has(oid) && !visited.has(oid))) {
                  _context2.next = 20;
                  break;
                }

                _context2.next = 20;
                return walk(oid);

              case 20:
                _iteratorNormalCompletion3 = true;
                _context2.next = 15;
                break;

              case 23:
                _context2.next = 29;
                break;

              case 25:
                _context2.prev = 25;
                _context2.t0 = _context2['catch'](13);
                _didIteratorError3 = true;
                _iteratorError3 = _context2.t0;

              case 29:
                _context2.prev = 29;
                _context2.prev = 30;

                if (!_iteratorNormalCompletion3 && _iterator3.return) {
                  _iterator3.return();
                }

              case 32:
                _context2.prev = 32;

                if (!_didIteratorError3) {
                  _context2.next = 35;
                  break;
                }

                throw _iteratorError3;

              case 35:
                return _context2.finish(32);

              case 36:
                return _context2.finish(29);

              case 37:
              case 'end':
                return _context2.stop();
            }
          }
        }, _callee2, this, [[13, 25, 29, 37], [30,, 32, 36]]);
      }));

      return function walk(_x5) {
        return _ref8.apply(this, arguments);
      };
    }();

    // Let's go walking!


    var start = _ref5.start,
        finish = _ref5.finish;

    var fs, startingSet, finishingSet, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, ref, _iteratorNormalCompletion2, _didIteratorError2, _iteratorError2, _iterator2, _step2, _ref7, _oid, visited, _iteratorNormalCompletion4, _didIteratorError4, _iteratorError4, _iterator4, _step4, oid;

    return _regeneratorRuntime.wrap(function _callee3$(_context3) {
      while (1) {
        switch (_context3.prev = _context3.next) {
          case 0:
            fs = new models_js.FileSystem(_fs);
            startingSet = new _Set();
            finishingSet = new _Set();
            _iteratorNormalCompletion = true;
            _didIteratorError = false;
            _iteratorError = undefined;
            _context3.prev = 6;
            _iterator = _getIterator(start);

          case 8:
            if (_iteratorNormalCompletion = (_step = _iterator.next()).done) {
              _context3.next = 18;
              break;
            }

            ref = _step.value;
            _context3.t0 = startingSet;
            _context3.next = 13;
            return managers_js.GitRefManager.resolve({ fs: fs, gitdir: gitdir, ref: ref });

          case 13:
            _context3.t1 = _context3.sent;

            _context3.t0.add.call(_context3.t0, _context3.t1);

          case 15:
            _iteratorNormalCompletion = true;
            _context3.next = 8;
            break;

          case 18:
            _context3.next = 24;
            break;

          case 20:
            _context3.prev = 20;
            _context3.t2 = _context3['catch'](6);
            _didIteratorError = true;
            _iteratorError = _context3.t2;

          case 24:
            _context3.prev = 24;
            _context3.prev = 25;

            if (!_iteratorNormalCompletion && _iterator.return) {
              _iterator.return();
            }

          case 27:
            _context3.prev = 27;

            if (!_didIteratorError) {
              _context3.next = 30;
              break;
            }

            throw _iteratorError;

          case 30:
            return _context3.finish(27);

          case 31:
            return _context3.finish(24);

          case 32:
            _iteratorNormalCompletion2 = true;
            _didIteratorError2 = false;
            _iteratorError2 = undefined;
            _context3.prev = 35;
            _iterator2 = _getIterator(finish);

          case 37:
            if (_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done) {
              _context3.next = 51;
              break;
            }

            _ref7 = _step2.value;
            _context3.prev = 39;
            _context3.next = 42;
            return managers_js.GitRefManager.resolve({ fs: fs, gitdir: gitdir, ref: _ref7 });

          case 42:
            _oid = _context3.sent;

            finishingSet.add(_oid);
            _context3.next = 48;
            break;

          case 46:
            _context3.prev = 46;
            _context3.t3 = _context3['catch'](39);

          case 48:
            _iteratorNormalCompletion2 = true;
            _context3.next = 37;
            break;

          case 51:
            _context3.next = 57;
            break;

          case 53:
            _context3.prev = 53;
            _context3.t4 = _context3['catch'](35);
            _didIteratorError2 = true;
            _iteratorError2 = _context3.t4;

          case 57:
            _context3.prev = 57;
            _context3.prev = 58;

            if (!_iteratorNormalCompletion2 && _iterator2.return) {
              _iterator2.return();
            }

          case 60:
            _context3.prev = 60;

            if (!_didIteratorError2) {
              _context3.next = 63;
              break;
            }

            throw _iteratorError2;

          case 63:
            return _context3.finish(60);

          case 64:
            return _context3.finish(57);

          case 65:
            visited /*: Set<string> */ = new _Set();
            _iteratorNormalCompletion4 = true;
            _didIteratorError4 = false;
            _iteratorError4 = undefined;
            _context3.prev = 69;
            _iterator4 = _getIterator(startingSet);

          case 71:
            if (_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done) {
              _context3.next = 78;
              break;
            }

            oid = _step4.value;
            _context3.next = 75;
            return walk(oid);

          case 75:
            _iteratorNormalCompletion4 = true;
            _context3.next = 71;
            break;

          case 78:
            _context3.next = 84;
            break;

          case 80:
            _context3.prev = 80;
            _context3.t5 = _context3['catch'](69);
            _didIteratorError4 = true;
            _iteratorError4 = _context3.t5;

          case 84:
            _context3.prev = 84;
            _context3.prev = 85;

            if (!_iteratorNormalCompletion4 && _iterator4.return) {
              _iterator4.return();
            }

          case 87:
            _context3.prev = 87;

            if (!_didIteratorError4) {
              _context3.next = 90;
              break;
            }

            throw _iteratorError4;

          case 90:
            return _context3.finish(87);

          case 91:
            return _context3.finish(84);

          case 92:
            return _context3.abrupt('return', visited);

          case 93:
          case 'end':
            return _context3.stop();
        }
      }
    }, _callee3, this, [[6, 20, 24, 32], [25,, 27, 31], [35, 53, 57, 65], [39, 46], [58,, 60, 64], [69, 80, 84, 92], [85,, 87, 91]]);
  }));

  return function listCommits(_x3, _x4) {
    return _ref6.apply(this, arguments);
  };
}();

/**
 * @ignore
 */
var listObjects = function () {
  var _ref12 = _asyncToGenerator(_regeneratorRuntime.mark(function _callee5(_ref10, _ref11 /*: { oids: Set<string> } */
  ) {
    var gitdir = _ref10.gitdir,
        _fs = _ref10.fs;

    // We don't do the purest simplest recursion, because we can
    // avoid reading Blob objects entirely since the Tree objects
    // tell us which oids are Blobs and which are Trees.
    var walk = function () {
      var _ref13 = _asyncToGenerator(_regeneratorRuntime.mark(function _callee4(oid) {
        var _ref14, type, object, commit, tree, _tree, _iteratorNormalCompletion5, _didIteratorError5, _iteratorError5, _iterator5, _step5, entry;

        return _regeneratorRuntime.wrap(function _callee4$(_context4) {
          while (1) {
            switch (_context4.prev = _context4.next) {
              case 0:
                visited.add(oid);
                _context4.next = 3;
                return managers_js.GitObjectManager.read({ fs: fs, gitdir: gitdir, oid: oid });

              case 3:
                _ref14 = _context4.sent;
                type = _ref14.type;
                object = _ref14.object;

                if (!(type === 'commit')) {
                  _context4.next = 13;
                  break;
                }

                commit = models_js.GitCommit.from(object);
                tree = commit.headers().tree;
                _context4.next = 11;
                return walk(tree);

              case 11:
                _context4.next = 43;
                break;

              case 13:
                if (!(type === 'tree')) {
                  _context4.next = 43;
                  break;
                }

                _tree = models_js.GitTree.from(object);
                _iteratorNormalCompletion5 = true;
                _didIteratorError5 = false;
                _iteratorError5 = undefined;
                _context4.prev = 18;
                _iterator5 = _getIterator( /*: TreeEntry */_tree);

              case 20:
                if (_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done) {
                  _context4.next = 29;
                  break;
                }

                entry = _step5.value;

                visited.add(entry.oid);
                // only recurse for trees

                if (!(entry.type === 'tree')) {
                  _context4.next = 26;
                  break;
                }

                _context4.next = 26;
                return walk(entry.oid);

              case 26:
                _iteratorNormalCompletion5 = true;
                _context4.next = 20;
                break;

              case 29:
                _context4.next = 35;
                break;

              case 31:
                _context4.prev = 31;
                _context4.t0 = _context4['catch'](18);
                _didIteratorError5 = true;
                _iteratorError5 = _context4.t0;

              case 35:
                _context4.prev = 35;
                _context4.prev = 36;

                if (!_iteratorNormalCompletion5 && _iterator5.return) {
                  _iterator5.return();
                }

              case 38:
                _context4.prev = 38;

                if (!_didIteratorError5) {
                  _context4.next = 41;
                  break;
                }

                throw _iteratorError5;

              case 41:
                return _context4.finish(38);

              case 42:
                return _context4.finish(35);

              case 43:
              case 'end':
                return _context4.stop();
            }
          }
        }, _callee4, this, [[18, 31, 35, 43], [36,, 38, 42]]);
      }));

      return function walk(_x8) {
        return _ref13.apply(this, arguments);
      };
    }();

    // Let's go walking!


    var oids = _ref11.oids;

    var fs, visited, _iteratorNormalCompletion6, _didIteratorError6, _iteratorError6, _iterator6, _step6, oid;

    return _regeneratorRuntime.wrap(function _callee5$(_context5) {
      while (1) {
        switch (_context5.prev = _context5.next) {
          case 0:
            fs = new models_js.FileSystem(_fs);
            visited /*: Set<string> */ = new _Set();
            _iteratorNormalCompletion6 = true;
            _didIteratorError6 = false;
            _iteratorError6 = undefined;
            _context5.prev = 5;
            _iterator6 = _getIterator(oids);

          case 7:
            if (_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done) {
              _context5.next = 14;
              break;
            }

            oid = _step6.value;
            _context5.next = 11;
            return walk(oid);

          case 11:
            _iteratorNormalCompletion6 = true;
            _context5.next = 7;
            break;

          case 14:
            _context5.next = 20;
            break;

          case 16:
            _context5.prev = 16;
            _context5.t0 = _context5['catch'](5);
            _didIteratorError6 = true;
            _iteratorError6 = _context5.t0;

          case 20:
            _context5.prev = 20;
            _context5.prev = 21;

            if (!_iteratorNormalCompletion6 && _iterator6.return) {
              _iterator6.return();
            }

          case 23:
            _context5.prev = 23;

            if (!_didIteratorError6) {
              _context5.next = 26;
              break;
            }

            throw _iteratorError6;

          case 26:
            return _context5.finish(23);

          case 27:
            return _context5.finish(20);

          case 28:
            return _context5.abrupt('return', visited);

          case 29:
          case 'end':
            return _context5.stop();
        }
      }
    }, _callee5, this, [[5, 16, 20, 28], [21,, 23, 27]]);
  }));

  return function listObjects(_x6, _x7) {
    return _ref12.apply(this, arguments);
  };
}();

/**
 * @ignore
 */
var pack = function () {
  var _ref17 = _asyncToGenerator(_regeneratorRuntime.mark(function _callee6(_ref15, _ref16) {
    var gitdir = _ref15.gitdir,
        _fs = _ref15.fs;
    var oids = _ref16.oids,
        outputStream = _ref16.outputStream;

    var fs, hash, write, writeObject, _iteratorNormalCompletion7, _didIteratorError7, _iteratorError7, _iterator7, _step7, oid, _ref19, type, object, digest;

    return _regeneratorRuntime.wrap(function _callee6$(_context6) {
      while (1) {
        switch (_context6.prev = _context6.next) {
          case 0:
            writeObject = function writeObject(_ref18) {
              var stype = _ref18.stype,
                  object = _ref18.object;

              var lastFour = void 0,
                  multibyte = void 0,
                  length = void 0;
              // Object type is encoded in bits 654
              var type = types[stype];
              if (type === undefined) throw new Error('Unrecognized type: ' + stype);
              // The length encoding get complicated.
              length = object.length;
              // Whether the next byte is part of the variable-length encoded number
              // is encoded in bit 7
              multibyte = length > 15 ? 128 : 0;
              // Last four bits of length is encoded in bits 3210
              lastFour = length & 15;
              // Discard those bits
              length = length >>> 4;
              // The first byte is then (1-bit multibyte?), (3-bit type), (4-bit least sig 4-bits of length)
              var byte = (multibyte | type | lastFour).toString(16);
              write(byte, 'hex');
              // Now we keep chopping away at length 7-bits at a time until its zero,
              // writing out the bytes in what amounts to little-endian order.
              while (multibyte) {
                multibyte = length > 127 ? 128 : 0;
                byte = multibyte | length & 127;
                write(pad(2, byte.toString(16), '0'), 'hex');
                length = length >>> 7;
              }
              // Lastly, we can compress and write the object.
              write(buffer.Buffer.from(pako.deflate(object)));
            };

            write = function write(chunk, enc) {
              outputStream.write(chunk, enc);
              hash.update(chunk, enc);
            };

            fs = new models_js.FileSystem(_fs);
            hash = crypto.createHash('sha1');


            write('PACK');
            write('00000002', 'hex');
            // Write a 4 byte (32-bit) int
            write(pad(8, oids.length.toString(16), '0'), 'hex');
            _iteratorNormalCompletion7 = true;
            _didIteratorError7 = false;
            _iteratorError7 = undefined;
            _context6.prev = 10;
            _iterator7 = _getIterator(oids);

          case 12:
            if (_iteratorNormalCompletion7 = (_step7 = _iterator7.next()).done) {
              _context6.next = 23;
              break;
            }

            oid = _step7.value;
            _context6.next = 16;
            return managers_js.GitObjectManager.read({ fs: fs, gitdir: gitdir, oid: oid });

          case 16:
            _ref19 = _context6.sent;
            type = _ref19.type;
            object = _ref19.object;

            writeObject({ write: write, object: object, stype: type });

          case 20:
            _iteratorNormalCompletion7 = true;
            _context6.next = 12;
            break;

          case 23:
            _context6.next = 29;
            break;

          case 25:
            _context6.prev = 25;
            _context6.t0 = _context6['catch'](10);
            _didIteratorError7 = true;
            _iteratorError7 = _context6.t0;

          case 29:
            _context6.prev = 29;
            _context6.prev = 30;

            if (!_iteratorNormalCompletion7 && _iterator7.return) {
              _iterator7.return();
            }

          case 32:
            _context6.prev = 32;

            if (!_didIteratorError7) {
              _context6.next = 35;
              break;
            }

            throw _iteratorError7;

          case 35:
            return _context6.finish(32);

          case 36:
            return _context6.finish(29);

          case 37:
            // Write SHA1 checksum
            digest = hash.digest();

            outputStream.end(digest);
            return _context6.abrupt('return', outputStream);

          case 40:
          case 'end':
            return _context6.stop();
        }
      }
    }, _callee6, this, [[10, 25, 29, 37], [30,, 32, 36]]);
  }));

  return function pack(_x9, _x10) {
    return _ref17.apply(this, arguments);
  };
}();

var fetchPackfile = function () {
  var _ref6 = _asyncToGenerator(_regeneratorRuntime.mark(function _callee3(_ref4, _ref5) {
    var _this = this;

    var gitdir = _ref4.gitdir,
        _fs = _ref4.fs;
    var ref = _ref5.ref,
        remote = _ref5.remote,
        url = _ref5.url,
        authUsername = _ref5.authUsername,
        authPassword = _ref5.authPassword,
        _ref5$depth = _ref5.depth,
        depth = _ref5$depth === undefined ? null : _ref5$depth,
        _ref5$since = _ref5.since,
        since = _ref5$since === undefined ? null : _ref5$since,
        _ref5$exclude = _ref5.exclude,
        exclude = _ref5$exclude === undefined ? [] : _ref5$exclude,
        _ref5$relative = _ref5.relative,
        relative = _ref5$relative === undefined ? false : _ref5$relative;

    var fs, remoteHTTP, want, capabilities, packstream, oids, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, oid, _iteratorNormalCompletion2, _didIteratorError2, _iteratorError2, _iterator2, _step2, x, have, response;

    return _regeneratorRuntime.wrap(function _callee3$(_context3) {
      while (1) {
        switch (_context3.prev = _context3.next) {
          case 0:
            fs = new models_js.FileSystem(_fs);

            if (!(depth !== null)) {
              _context3.next = 5;
              break;
            }

            if (!_Number$isNaN(parseInt(depth))) {
              _context3.next = 4;
              break;
            }

            throw new Error('Invalid value for depth argument: ' + depth);

          case 4:
            depth = parseInt(depth);

          case 5:
            remote = remote || 'origin';

            if (!(url === undefined)) {
              _context3.next = 10;
              break;
            }

            _context3.next = 9;
            return config({ fs: fs, gitdir: gitdir }, {
              path: 'remote.' + remote + '.url'
            });

          case 9:
            url = _context3.sent;

          case 10:
            remoteHTTP = new managers_js.GitRemoteHTTP(url);

            if (authUsername !== undefined && authPassword !== undefined) {
              remoteHTTP.auth = {
                username: authUsername,
                password: authPassword
              };
            }
            _context3.next = 14;
            return remoteHTTP.preparePull();

          case 14:
            if (!(depth !== null && !remoteHTTP.capabilities.has('shallow'))) {
              _context3.next = 16;
              break;
            }

            throw new Error('Remote does not support shallow fetches');

          case 16:
            if (!(since !== null && !remoteHTTP.capabilities.has('deepen-since'))) {
              _context3.next = 18;
              break;
            }

            throw new Error('Remote does not support shallow fetches by date');

          case 18:
            if (!(exclude.length > 0 && !remoteHTTP.capabilities.has('deepen-not'))) {
              _context3.next = 20;
              break;
            }

            throw new Error('Remote does not support shallow fetches excluding commits reachable by refs');

          case 20:
            if (!(relative === true && !remoteHTTP.capabilities.has('deepen-relative'))) {
              _context3.next = 22;
              break;
            }

            throw new Error('Remote does not support shallow fetches relative to the current shallow depth');

          case 22:
            _context3.next = 24;
            return managers_js.GitRefManager.updateRemoteRefs({
              fs: fs,
              gitdir: gitdir,
              remote: remote,
              refs: remoteHTTP.refs,
              symrefs: remoteHTTP.symrefs
            });

          case 24:
            _context3.next = 26;
            return managers_js.GitRefManager.resolve({
              fs: fs,
              gitdir: gitdir,
              ref: 'refs/remotes/' + remote + '/' + ref
            });

          case 26:
            want = _context3.sent;

            // Note: I removed "ofs-delta" from the capabilities list and now
            // Github uses all ref-deltas when I fetch packfiles instead of all ofs-deltas. Nice!
            capabilities = 'multi_ack_detailed no-done side-band-64k thin-pack agent=git/' + utils_js.pkg.name + '@' + utils_js.pkg.version + (relative ? ' deepen-relative' : '');
            packstream = new stream.PassThrough();

            packstream.write(models_js.GitPktLine.encode('want ' + want + ' ' + capabilities + '\n'));
            _context3.next = 32;
            return managers_js.GitShallowManager.read({ fs: fs, gitdir: gitdir });

          case 32:
            oids = _context3.sent;

            if (!(oids.size > 0 && remoteHTTP.capabilities.has('shallow'))) {
              _context3.next = 53;
              break;
            }

            _iteratorNormalCompletion = true;
            _didIteratorError = false;
            _iteratorError = undefined;
            _context3.prev = 37;

            for (_iterator = _getIterator(oids); !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
              oid = _step.value;

              packstream.write(models_js.GitPktLine.encode('shallow ' + oid + '\n'));
            }
            _context3.next = 45;
            break;

          case 41:
            _context3.prev = 41;
            _context3.t0 = _context3['catch'](37);
            _didIteratorError = true;
            _iteratorError = _context3.t0;

          case 45:
            _context3.prev = 45;
            _context3.prev = 46;

            if (!_iteratorNormalCompletion && _iterator.return) {
              _iterator.return();
            }

          case 48:
            _context3.prev = 48;

            if (!_didIteratorError) {
              _context3.next = 51;
              break;
            }

            throw _iteratorError;

          case 51:
            return _context3.finish(48);

          case 52:
            return _context3.finish(45);

          case 53:
            if (depth !== null) {
              packstream.write(models_js.GitPktLine.encode('deepen ' + depth + '\n'));
            }
            if (since !== null) {
              packstream.write(models_js.GitPktLine.encode('deepen-since ' + Math.floor(since.valueOf() / 1000) + '\n'));
            }
            _iteratorNormalCompletion2 = true;
            _didIteratorError2 = false;
            _iteratorError2 = undefined;
            _context3.prev = 58;
            for (_iterator2 = _getIterator(exclude); !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
              x = _step2.value;

              packstream.write(models_js.GitPktLine.encode('deepen-not ' + x + '\n'));
            }
            _context3.next = 66;
            break;

          case 62:
            _context3.prev = 62;
            _context3.t1 = _context3['catch'](58);
            _didIteratorError2 = true;
            _iteratorError2 = _context3.t1;

          case 66:
            _context3.prev = 66;
            _context3.prev = 67;

            if (!_iteratorNormalCompletion2 && _iterator2.return) {
              _iterator2.return();
            }

          case 69:
            _context3.prev = 69;

            if (!_didIteratorError2) {
              _context3.next = 72;
              break;
            }

            throw _iteratorError2;

          case 72:
            return _context3.finish(69);

          case 73:
            return _context3.finish(66);

          case 74:
            packstream.write(models_js.GitPktLine.flush());
            have = null;
            _context3.prev = 76;
            _context3.next = 79;
            return managers_js.GitRefManager.resolve({ fs: fs, gitdir: gitdir, ref: ref });

          case 79:
            have = _context3.sent;
            _context3.next = 84;
            break;

          case 82:
            _context3.prev = 82;
            _context3.t2 = _context3['catch'](76);

          case 84:
            if (have) {
              packstream.write(models_js.GitPktLine.encode('have ' + have + '\n'));
              packstream.write(models_js.GitPktLine.flush());
            }
            packstream.end(models_js.GitPktLine.encode('done\n'));
            _context3.next = 88;
            return remoteHTTP.pull(packstream);

          case 88:
            response = _context3.sent;

            response.packetlines.pipe(through2(function () {
              var _ref7 = _asyncToGenerator(_regeneratorRuntime.mark(function _callee2(data, enc, next) {
                var line, _oid, _oid2;

                return _regeneratorRuntime.wrap(function _callee2$(_context2) {
                  while (1) {
                    switch (_context2.prev = _context2.next) {
                      case 0:
                        line = data.toString('utf8');

                        if (!line.startsWith('shallow')) {
                          _context2.next = 10;
                          break;
                        }

                        _oid = line.slice(-41).trim();

                        if (!(_oid.length !== 40)) {
                          _context2.next = 5;
                          break;
                        }

                        throw new Error('non-40 character \'shallow\' oid: ' + _oid);

                      case 5:
                        oids.add(_oid);
                        _context2.next = 8;
                        return managers_js.GitShallowManager.write({ fs: fs, gitdir: gitdir, oids: oids });

                      case 8:
                        _context2.next = 17;
                        break;

                      case 10:
                        if (!line.startsWith('unshallow')) {
                          _context2.next = 17;
                          break;
                        }

                        _oid2 = line.slice(-41).trim();

                        if (!(_oid2.length !== 40)) {
                          _context2.next = 14;
                          break;
                        }

                        throw new Error('non-40 character \'shallow\' oid: ' + _oid2);

                      case 14:
                        oids.delete(_oid2);
                        _context2.next = 17;
                        return managers_js.GitShallowManager.write({ fs: fs, gitdir: gitdir, oids: oids });

                      case 17:
                        next(null, data);

                      case 18:
                      case 'end':
                        return _context2.stop();
                    }
                  }
                }, _callee2, _this);
              }));

              return function (_x5, _x6, _x7) {
                return _ref7.apply(this, arguments);
              };
            }()));
            return _context3.abrupt('return', response);

          case 91:
          case 'end':
            return _context3.stop();
        }
      }
    }, _callee3, this, [[37, 41, 45, 53], [46,, 48, 52], [58, 62, 66, 74], [67,, 69, 73], [76, 82]]);
  }));

  return function fetchPackfile(_x3, _x4) {
    return _ref6.apply(this, arguments);
  };
}();

/**
 * Fetch commits
 *
 * @param {GitRepo} repo - A {@link Git} object matching `{workdir, gitdir, fs}`
 * @param {Object} args - Arguments object
 * @param {string} [args.url=undefined] - The URL of the remote git server. The default is the value set in the git config for that remote.
 * @param {string} [args.remote='origin'] - If `url` is not specified, determines which remote to use.
 * @param {string} [args.ref=undefined] - Which branch to fetch from. By default this is the currently checked out branch.
 * @param {string} [args.authUsername=undefined] - The username to use with Basic Auth
 * @param {string} [args.authPassword=undefined] - The password to use with Basic Auth
 * @param {integer} [args.depth=undefined] - Determines how much of the git repository's history to retrieve.
 * @param {Date} [args.since=undefined] - Only fetch commits created after the given date. Mutually exclusive with `depth`.
 * @param {string[]} [args.exclude=[]] - A list of branches or tags. Instructs the remote server not to send us any commits reachable from these refs.
 * @param {boolean} [args.relative=false] - Changes the meaning of `depth` to be measured from the current shallow depth rather than from the branch tip.
 * @param {Function} [args.onprogress=undefined] - Callback to receive [ProgressEvent](https://developer.mozilla.org/en-US/docs/Web/API/ProgressEvent)s for the operation.
 * @returns {Promise<void>} - Resolves successfully when clone completes
 *
 * @example
 * let repo = new Git({fs, dir: '.'})
 * await fetch(repo, {
 *   url: 'https://cors-buster-jfpactjnem.now.sh/github.com/wmhilton/isomorphic-git',
 *   depth: 1
 * })
 */
var fetch = function () {
  var _ref3 = _asyncToGenerator(_regeneratorRuntime.mark(function _callee(_ref, _ref2) {
    var gitdir = _ref.gitdir,
        fs = _ref.fs;
    var _ref2$ref = _ref2.ref,
        ref = _ref2$ref === undefined ? 'HEAD' : _ref2$ref,
        remote = _ref2.remote,
        url = _ref2.url,
        authUsername = _ref2.authUsername,
        authPassword = _ref2.authPassword,
        depth = _ref2.depth,
        since = _ref2.since,
        exclude = _ref2.exclude,
        relative = _ref2.relative,
        onprogress = _ref2.onprogress;
    var response;
    return _regeneratorRuntime.wrap(function _callee$(_context) {
      while (1) {
        switch (_context.prev = _context.next) {
          case 0:
            _context.next = 2;
            return fetchPackfile({
              gitdir: gitdir,
              fs: fs
            }, {
              ref: ref,
              remote: remote,
              url: url,
              authUsername: authUsername,
              authPassword: authPassword,
              depth: depth,
              since: since,
              exclude: exclude,
              relative: relative
            });

          case 2:
            response = _context.sent;
            _context.next = 5;
            return unpack({ fs: fs, gitdir: gitdir }, { inputStream: response.packfile, onprogress: onprogress });

          case 5:
          case 'end':
            return _context.stop();
        }
      }
    }, _callee, this);
  }));

  return function fetch(_x, _x2) {
    return _ref3.apply(this, arguments);
  };
}();

var types$1 = {
  1: 'commit',
  2: 'tree',
  3: 'blob',
  4: 'tag',
  6: 'ofs-delta',
  7: 'ref-delta'
};

function parseVarInt(buffer$$1 /*: Buffer */) {
  var n = 0;
  for (var i = 0; i < buffer$$1.byteLength; i++) {
    n = (buffer$$1[i] & 127) + (n << 7);
    if ((buffer$$1[i] & 128) === 0) {
      if (i !== buffer$$1.byteLength - 1) throw new Error('Invalid varint buffer');
      return n;
    }
  }
  throw new Error('Invalid varint buffer');
}

/**
 * @ignore
 * @param {GitRepo} repo - A {@link Git} object matching `{gitdir, fs}`
 * @param {Object} args - Arguments object
 * @param {ReadableStream} args.inputStream
 * @param {Function} args.onprogress
 */
var unpack = function () {
  var _ref10 = _asyncToGenerator(_regeneratorRuntime.mark(function _callee5(_ref8, _ref9) {
    var gitdir = _ref8.gitdir,
        _fs = _ref8.fs;
    var inputStream = _ref9.inputStream,
        onprogress = _ref9.onprogress;
    var fs;
    return _regeneratorRuntime.wrap(function _callee5$(_context5) {
      while (1) {
        switch (_context5.prev = _context5.next) {
          case 0:
            fs = new models_js.FileSystem(_fs);
            return _context5.abrupt('return', new _Promise(function (resolve, reject) {
              var _this2 = this;

              // Read header
              peek(inputStream, 12, function (err, data, inputStream) {
                if (err) return reject(err);
                var iden = data.slice(0, 4).toString('utf8');
                if (iden !== 'PACK') {
                  throw new Error('Packfile started with \'' + iden + '\'. Expected \'PACK\'');
                }
                var ver = data.slice(4, 8).toString('hex');
                if (ver !== '00000002') {
                  throw new Error('Unknown packfile version \'' + ver + '\'. Expected 00000002.');
                }
                // Read a 4 byte (32-bit) int
                var numObjects = data.readInt32BE(8);
                if (onprogress !== undefined) {
                  onprogress({ loaded: 0, total: numObjects, lengthComputable: true });
                }
                if (numObjects === 0) return resolve();
                // And on our merry way
                var totalTime = 0;
                var totalApplyDeltaTime = 0;
                var totalWriteFileTime = 0;
                var totalReadFileTime = 0;
                var offsetMap = new _Map();
                inputStream.pipe(listpack()).pipe(through2.obj(function () {
                  var _ref12 = _asyncToGenerator(_regeneratorRuntime.mark(function _callee4(_ref11, enc, next) {
                    var data = _ref11.data,
                        type = _ref11.type,
                        reference = _ref11.reference,
                        offset = _ref11.offset,
                        num = _ref11.num;

                    var oid, _ref13, object, _type, result, newoid, absoluteOffset, referenceOid, _ref14, _type2, _object, _result, _oid3, _oid4, perfentry;

                    return _regeneratorRuntime.wrap(function _callee4$(_context4) {
                      while (1) {
                        switch (_context4.prev = _context4.next) {
                          case 0:
                            type = types$1[type];
                            marky.mark(type + ' #' + num + ' ' + data.length + 'B');

                            if (!(type === 'ref-delta')) {
                              _context4.next = 28;
                              break;
                            }

                            oid = buffer.Buffer.from(reference).toString('hex');
                            _context4.prev = 4;

                            marky.mark('readFile');
                            _context4.next = 8;
                            return managers_js.GitObjectManager.read({
                              fs: fs,
                              gitdir: gitdir,
                              oid: oid
                            });

                          case 8:
                            _ref13 = _context4.sent;
                            object = _ref13.object;
                            _type = _ref13.type;

                            totalReadFileTime += marky.stop('readFile').duration;
                            marky.mark('applyDelta');
                            result = applyDelta(data, object);

                            totalApplyDeltaTime += marky.stop('applyDelta').duration;
                            marky.mark('writeFile');
                            _context4.next = 18;
                            return managers_js.GitObjectManager.write({
                              fs: fs,
                              gitdir: gitdir,
                              type: _type,
                              object: result
                            });

                          case 18:
                            newoid = _context4.sent;

                            totalWriteFileTime += marky.stop('writeFile').duration;
                            // console.log(`${type} ${newoid} ref-delta ${oid}`)
                            offsetMap.set(offset, newoid);
                            _context4.next = 26;
                            break;

                          case 23:
                            _context4.prev = 23;
                            _context4.t0 = _context4['catch'](4);
                            throw new Error('Could not find object ' + reference + ' ' + oid + ' that is referenced by a ref-delta object in packfile at byte offset ' + offset + '.');

                          case 26:
                            _context4.next = 49;
                            break;

                          case 28:
                            if (!(type === 'ofs-delta')) {
                              _context4.next = 43;
                              break;
                            }

                            // Note: this might be not working because offsets might not be
                            // guaranteed to be on object boundaries? In which case we'd need
                            // to write the packfile to disk first, I think.
                            // For now I've "solved" it by simply not advertising ofs-delta as a capability
                            // during the HTTP request, so Github will only send ref-deltas not ofs-deltas.
                            absoluteOffset = offset - parseVarInt(reference);
                            referenceOid = offsetMap.get(absoluteOffset);
                            // console.log(`${offset} ofs-delta ${absoluteOffset} ${referenceOid}`)

                            _context4.next = 33;
                            return managers_js.GitObjectManager.read({
                              fs: fs,
                              gitdir: gitdir,
                              oid: referenceOid
                            });

                          case 33:
                            _ref14 = _context4.sent;
                            _type2 = _ref14.type;
                            _object = _ref14.object;
                            _result = applyDelta(data, _object);
                            _context4.next = 39;
                            return managers_js.GitObjectManager.write({
                              fs: fs,
                              gitdir: gitdir,
                              type: _type2,
                              object: _result
                            });

                          case 39:
                            _oid3 = _context4.sent;

                            // console.log(`${offset} ${type} ${oid} ofs-delta ${referenceOid}`)
                            offsetMap.set(offset, _oid3);
                            _context4.next = 49;
                            break;

                          case 43:
                            marky.mark('writeFile');
                            _context4.next = 46;
                            return managers_js.GitObjectManager.write({
                              fs: fs,
                              gitdir: gitdir,
                              type: type,
                              object: data
                            });

                          case 46:
                            _oid4 = _context4.sent;

                            totalWriteFileTime += marky.stop('writeFile').duration;
                            // console.log(`${offset} ${type} ${oid}`)
                            offsetMap.set(offset, _oid4);

                          case 49:
                            if (onprogress !== undefined) {
                              onprogress({
                                loaded: numObjects - num,
                                total: numObjects,
                                lengthComputable: true
                              });
                            }
                            perfentry = marky.stop(type + ' #' + num + ' ' + data.length + 'B');

                            totalTime += perfentry.duration;

                            if (!(num === 0)) {
                              _context4.next = 58;
                              break;
                            }

                            console.log('Total time unpacking objects: ' + totalTime);
                            console.log('Total time applying deltas: ' + totalApplyDeltaTime);
                            console.log('Total time reading files: ' + totalReadFileTime);
                            console.log('Total time writing files: ' + totalWriteFileTime);
                            return _context4.abrupt('return', resolve());

                          case 58:
                            next(null);

                          case 59:
                          case 'end':
                            return _context4.stop();
                        }
                      }
                    }, _callee4, _this2, [[4, 23]]);
                  }));

                  return function (_x10, _x11, _x12) {
                    return _ref12.apply(this, arguments);
                  };
                }())).on('error', reject).on('finish', resolve);
              });
            }));

          case 2:
          case 'end':
            return _context5.stop();
        }
      }
    }, _callee5, this);
  }));

  return function unpack(_x8, _x9) {
    return _ref10.apply(this, arguments);
  };
}();

exports.listCommits = listCommits;
exports.listObjects = listObjects;
exports.pack = pack;
exports.unpack = unpack;
//# sourceMappingURL=internal-apis.js.map
