'use strict';

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

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

var _regeneratorRuntime = _interopDefault(require('babel-runtime/regenerator'));
var _asyncToGenerator = _interopDefault(require('babel-runtime/helpers/asyncToGenerator'));
var _classCallCheck = _interopDefault(require('babel-runtime/helpers/classCallCheck'));
var _createClass = _interopDefault(require('babel-runtime/helpers/createClass'));
var models_js = require('./models.js');
var _toConsumableArray = _interopDefault(require('babel-runtime/helpers/toConsumableArray'));
var _Set = _interopDefault(require('babel-runtime/core-js/set'));
var path = _interopDefault(require('path'));
var AsyncLock = _interopDefault(require('async-lock'));
var _Map = _interopDefault(require('babel-runtime/core-js/map'));
var _getIterator = _interopDefault(require('babel-runtime/core-js/get-iterator'));
var ignore = _interopDefault(require('ignore'));
var buffer = require('buffer');
var shasum = _interopDefault(require('shasum'));
var pako = _interopDefault(require('pako'));
var _slicedToArray = _interopDefault(require('babel-runtime/helpers/slicedToArray'));
var simpleGet = _interopDefault(require('simple-get'));
var concat = _interopDefault(require('simple-concat'));
var pify = _interopDefault(require('pify'));
var utils_js = require('./utils.js');
var stream = require('stream');

/** @ignore */
var GitConfigManager = function () {
  function GitConfigManager() {
    _classCallCheck(this, GitConfigManager);
  }

  _createClass(GitConfigManager, null, [{
    key: 'get',
    value: function () {
      var _ref2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(_ref) {
        var _fs = _ref.fs,
            gitdir = _ref.gitdir;
        var fs, text;
        return _regeneratorRuntime.wrap(function _callee$(_context) {
          while (1) {
            switch (_context.prev = _context.next) {
              case 0:
                fs = new models_js.FileSystem(_fs);
                // We can improve efficiency later if needed.
                // TODO: read from full list of git config files

                _context.next = 3;
                return fs.read(gitdir + '/config', { encoding: 'utf8' });

              case 3:
                text = _context.sent;
                return _context.abrupt('return', models_js.GitConfig.from(text));

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

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

      return get;
    }()
  }, {
    key: 'save',
    value: function () {
      var _ref4 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2(_ref3) {
        var _fs = _ref3.fs,
            gitdir = _ref3.gitdir,
            config = _ref3.config;
        var fs;
        return _regeneratorRuntime.wrap(function _callee2$(_context2) {
          while (1) {
            switch (_context2.prev = _context2.next) {
              case 0:
                fs = new models_js.FileSystem(_fs);
                // We can improve efficiency later if needed.
                // TODO: handle saving to the correct global/user/repo location

                _context2.next = 3;
                return fs.write(gitdir + '/config', config.toString(), {
                  encoding: 'utf8'
                });

              case 3:
              case 'end':
                return _context2.stop();
            }
          }
        }, _callee2, this);
      }));

      function save(_x2) {
        return _ref4.apply(this, arguments);
      }

      return save;
    }()
  }]);

  return GitConfigManager;
}();

var lock = new AsyncLock();

/** @ignore */
var GitShallowManager = function () {
  function GitShallowManager() {
    _classCallCheck(this, GitShallowManager);
  }

  _createClass(GitShallowManager, null, [{
    key: 'read',
    value: function () {
      var _ref2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2(_ref) {
        var _fs = _ref.fs,
            gitdir = _ref.gitdir;
        var fs, filepath, oids;
        return _regeneratorRuntime.wrap(function _callee2$(_context2) {
          while (1) {
            switch (_context2.prev = _context2.next) {
              case 0:
                fs = new models_js.FileSystem(_fs);
                filepath = path.join(gitdir, 'shallow');
                oids = new _Set();
                _context2.next = 5;
                return lock.acquire(filepath, _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee() {
                  var text;
                  return _regeneratorRuntime.wrap(function _callee$(_context) {
                    while (1) {
                      switch (_context.prev = _context.next) {
                        case 0:
                          _context.next = 2;
                          return fs.read(filepath, { encoding: 'utf8' });

                        case 2:
                          text = _context.sent;

                          if (!(text === null)) {
                            _context.next = 5;
                            break;
                          }

                          return _context.abrupt('return');

                        case 5:
                          text.trim().split('\n').map(function (oid) {
                            return oids.add(oid);
                          });

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

              case 5:
                return _context2.abrupt('return', oids);

              case 6:
              case 'end':
                return _context2.stop();
            }
          }
        }, _callee2, this);
      }));

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

      return read;
    }()
  }, {
    key: 'write',
    value: function () {
      var _ref5 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee4(_ref4) {
        var _fs = _ref4.fs,
            gitdir = _ref4.gitdir,
            oids = _ref4.oids;
        var fs, filepath, text;
        return _regeneratorRuntime.wrap(function _callee4$(_context4) {
          while (1) {
            switch (_context4.prev = _context4.next) {
              case 0:
                fs = new models_js.FileSystem(_fs);
                filepath = path.join(gitdir, 'shallow');
                text = [].concat(_toConsumableArray(oids)).join('\n') + '\n';
                _context4.next = 5;
                return lock.acquire(filepath, _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee3() {
                  return _regeneratorRuntime.wrap(function _callee3$(_context3) {
                    while (1) {
                      switch (_context3.prev = _context3.next) {
                        case 0:
                          _context3.next = 2;
                          return fs.write(filepath, text, {
                            encoding: 'utf8'
                          });

                        case 2:
                        case 'end':
                          return _context3.stop();
                      }
                    }
                  }, _callee3, this);
                })));

              case 5:
              case 'end':
                return _context4.stop();
            }
          }
        }, _callee4, this);
      }));

      function write(_x2) {
        return _ref5.apply(this, arguments);
      }

      return write;
    }()
  }]);

  return GitShallowManager;
}();

// import Lock from '../utils'

// TODO: replace with an LRU cache?
var map /*: Map<string, GitIndex> */ = new _Map();
// const lm = new LockManager()
var lock$1 = new AsyncLock();

/** @ignore */
var GitIndexManager = function () {
  function GitIndexManager() {
    _classCallCheck(this, GitIndexManager);
  }

  _createClass(GitIndexManager, null, [{
    key: 'acquire',
    value: function () {
      var _ref2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2(_ref, closure) {
        var _fs = _ref.fs,
            filepath = _ref.filepath;
        var fs;
        return _regeneratorRuntime.wrap(function _callee2$(_context2) {
          while (1) {
            switch (_context2.prev = _context2.next) {
              case 0:
                fs = new models_js.FileSystem(_fs);
                _context2.next = 3;
                return lock$1.acquire(filepath, _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee() {
                  var index, rawIndexFile, buffer$$1;
                  return _regeneratorRuntime.wrap(function _callee$(_context) {
                    while (1) {
                      switch (_context.prev = _context.next) {
                        case 0:
                          index = map.get(filepath);

                          if (!(index === undefined)) {
                            _context.next = 7;
                            break;
                          }

                          _context.next = 4;
                          return fs.read(filepath);

                        case 4:
                          rawIndexFile = _context.sent;

                          index = models_js.GitIndex.from(rawIndexFile);
                          // cache the GitIndex object so we don't need to re-read it
                          // every time.
                          // TODO: save the stat data for the index so we know whether
                          // the cached file is stale (modified by an outside process).
                          map.set(filepath, index);
                          // await fileLock.cancel()

                        case 7:
                          _context.next = 9;
                          return closure(index);

                        case 9:
                          if (!index._dirty) {
                            _context.next = 14;
                            break;
                          }

                          // Acquire a file lock while we're writing the index file
                          // let fileLock = await Lock(filepath)
                          buffer$$1 = index.toObject();
                          _context.next = 13;
                          return fs.write(filepath, buffer$$1);

                        case 13:
                          index._dirty = false;

                        case 14:
                          // For now, discard our cached object so that external index
                          // manipulation is picked up. TODO: use lstat and compare
                          // file times to determine if our cached object should be
                          // discarded.
                          map.delete(filepath);

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

              case 3:
              case 'end':
                return _context2.stop();
            }
          }
        }, _callee2, this);
      }));

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

      return acquire;
    }()
  }]);

  return GitIndexManager;
}();

// I'm putting this in a Manager because I reckon it could benefit
// from a LOT of cacheing.

// TODO: Implement .git/info/exclude

/** @ignore */
var GitIgnoreManager = function () {
  function GitIgnoreManager() {
    _classCallCheck(this, GitIgnoreManager);
  }

  _createClass(GitIgnoreManager, null, [{
    key: 'isIgnored',
    value: function () {
      var _ref2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(_ref) {
        var _fs = _ref.fs,
            dir = _ref.dir,
            _ref$gitdir = _ref.gitdir,
            gitdir = _ref$gitdir === undefined ? path.join(dir, '.git') : _ref$gitdir,
            filepath = _ref.filepath;

        var fs, pairs, pieces, i, folder, file, ignoredStatus, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, p, _file, ign, unign, parentdir;

        return _regeneratorRuntime.wrap(function _callee$(_context) {
          while (1) {
            switch (_context.prev = _context.next) {
              case 0:
                fs = new models_js.FileSystem(_fs);
                pairs = [{
                  gitignore: path.join(dir, '.gitignore'),
                  filepath: filepath
                }];
                pieces = filepath.split('/');

                for (i = 1; i < pieces.length; i++) {
                  folder = pieces.slice(0, i).join('/');
                  file = pieces.slice(i).join('/');

                  pairs.push({
                    gitignore: path.join(dir, folder, '.gitignore'),
                    filepath: file
                  });
                }
                ignoredStatus = false;
                _iteratorNormalCompletion = true;
                _didIteratorError = false;
                _iteratorError = undefined;
                _context.prev = 8;
                _iterator = _getIterator(pairs);

              case 10:
                if (_iteratorNormalCompletion = (_step = _iterator.next()).done) {
                  _context.next = 32;
                  break;
                }

                p = _step.value;
                _file = void 0;
                _context.prev = 13;
                _context.next = 16;
                return fs.read(p.gitignore, 'utf8');

              case 16:
                _file = _context.sent;
                _context.next = 23;
                break;

              case 19:
                _context.prev = 19;
                _context.t0 = _context['catch'](13);

                if (!(_context.t0.code === 'NOENT')) {
                  _context.next = 23;
                  break;
                }

                return _context.abrupt('continue', 29);

              case 23:
                ign = ignore().add(_file);
                unign = ignore().add('**\n' + _file);
                // If the parent directory is excluded, we are done.
                // "It is not possible to re-include a file if a parent directory of that file is excluded. Git doesn’t list excluded directories for performance reasons, so any patterns on contained files have no effect, no matter where they are defined."
                // source: https://git-scm.com/docs/gitignore

                parentdir = path.dirname(p.filepath);

                if (!ign.ignores(parentdir)) {
                  _context.next = 28;
                  break;
                }

                return _context.abrupt('return', true);

              case 28:
                // If the file is currently ignored, test for UNignoring.
                if (ignoredStatus) {
                  ignoredStatus = unign.ignores(p.filepath);
                } else {
                  ignoredStatus = ign.ignores(p.filepath);
                }

              case 29:
                _iteratorNormalCompletion = true;
                _context.next = 10;
                break;

              case 32:
                _context.next = 38;
                break;

              case 34:
                _context.prev = 34;
                _context.t1 = _context['catch'](8);
                _didIteratorError = true;
                _iteratorError = _context.t1;

              case 38:
                _context.prev = 38;
                _context.prev = 39;

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

              case 41:
                _context.prev = 41;

                if (!_didIteratorError) {
                  _context.next = 44;
                  break;
                }

                throw _iteratorError;

              case 44:
                return _context.finish(41);

              case 45:
                return _context.finish(38);

              case 46:
                return _context.abrupt('return', ignoredStatus);

              case 47:
              case 'end':
                return _context.stop();
            }
          }
        }, _callee, this, [[8, 34, 38, 46], [13, 19], [39,, 41, 45]]);
      }));

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

      return isIgnored;
    }()
  }]);

  return GitIgnoreManager;
}();

var PackfileCache = new _Map();

/** @ignore */
var GitObjectManager = function () {
  function GitObjectManager() {
    _classCallCheck(this, GitObjectManager);
  }

  _createClass(GitObjectManager, null, [{
    key: 'read',
    value: function () {
      var _ref2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(_ref) {
        var _fs = _ref.fs,
            gitdir = _ref.gitdir,
            oid = _ref.oid,
            _ref$format = _ref.format,
            format = _ref$format === undefined ? 'content' : _ref$format;

        var fs, file, source, getExternalRefDelta, list, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, filename, p, idxName, idx, pack, _pack, result, text, buffer$$1, _GitObject$unwrap, type, object;

        return _regeneratorRuntime.wrap(function _callee$(_context) {
          while (1) {
            switch (_context.prev = _context.next) {
              case 0:
                fs = new models_js.FileSystem(_fs);
                // Look for it in the loose object directory.

                _context.next = 3;
                return fs.read(gitdir + '/objects/' + oid.slice(0, 2) + '/' + oid.slice(2));

              case 3:
                file = _context.sent;
                source = './objects/' + oid.slice(0, 2) + '/' + oid.slice(2);
                // Check to see if it's in a packfile.

                if (file) {
                  _context.next = 70;
                  break;
                }

                // Curry the current read method so that the packfile un-deltification
                // process can acquire external ref-deltas.
                getExternalRefDelta = function getExternalRefDelta(oid) {
                  return GitObjectManager.read({ fs: _fs, gitdir: gitdir, oid: oid });
                };
                // Iterate through all the .pack files


                _context.next = 9;
                return fs.readdir(path.join(gitdir, '/objects/pack'));

              case 9:
                list = _context.sent;

                list = list.filter(function (x) {
                  return x.endsWith('.pack');
                });
                _iteratorNormalCompletion = true;
                _didIteratorError = false;
                _iteratorError = undefined;
                _context.prev = 14;
                _iterator = _getIterator(list);

              case 16:
                if (_iteratorNormalCompletion = (_step = _iterator.next()).done) {
                  _context.next = 56;
                  break;
                }

                filename = _step.value;

                // Try to get the packfile from the in-memory cache
                p = PackfileCache.get(filename);

                if (p) {
                  _context.next = 41;
                  break;
                }

                // If not there, load it from a .idx file
                idxName = filename.replace(/pack$/, 'idx');
                _context.next = 23;
                return fs.exists(gitdir + '/objects/pack/' + idxName);

              case 23:
                if (!_context.sent) {
                  _context.next = 32;
                  break;
                }

                _context.next = 26;
                return fs.read(gitdir + '/objects/pack/' + idxName);

              case 26:
                idx = _context.sent;
                _context.next = 29;
                return models_js.GitPackIndex.fromIdx({ idx: idx, getExternalRefDelta: getExternalRefDelta });

              case 29:
                p = _context.sent;
                _context.next = 40;
                break;

              case 32:
                _context.next = 34;
                return fs.read(gitdir + '/objects/pack/' + filename);

              case 34:
                pack = _context.sent;
                _context.next = 37;
                return models_js.GitPackIndex.fromPack({ pack: pack, getExternalRefDelta: getExternalRefDelta });

              case 37:
                p = _context.sent;
                _context.next = 40;
                return fs.write(gitdir + '/objects/pack/' + idxName, p.toBuffer());

              case 40:
                PackfileCache.set(filename, p);

              case 41:
                if (!p.hashes.includes(oid)) {
                  _context.next = 53;
                  break;
                }

                if (p.pack) {
                  _context.next = 48;
                  break;
                }

                _context.next = 45;
                return fs.read(gitdir + '/objects/pack/' + filename);

              case 45:
                _pack = _context.sent;
                _context.next = 48;
                return p.load({ pack: _pack });

              case 48:
                _context.next = 50;
                return p.read({ oid: oid, getExternalRefDelta: getExternalRefDelta });

              case 50:
                result = _context.sent;

                result.source = './objects/pack/' + filename;
                return _context.abrupt('return', result);

              case 53:
                _iteratorNormalCompletion = true;
                _context.next = 16;
                break;

              case 56:
                _context.next = 62;
                break;

              case 58:
                _context.prev = 58;
                _context.t0 = _context['catch'](14);
                _didIteratorError = true;
                _iteratorError = _context.t0;

              case 62:
                _context.prev = 62;
                _context.prev = 63;

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

              case 65:
                _context.prev = 65;

                if (!_didIteratorError) {
                  _context.next = 68;
                  break;
                }

                throw _iteratorError;

              case 68:
                return _context.finish(65);

              case 69:
                return _context.finish(62);

              case 70:
                if (file) {
                  _context.next = 76;
                  break;
                }

                _context.next = 73;
                return fs.read(gitdir + '/shallow', { encoding: 'utf8' });

              case 73:
                text = _context.sent;

                if (!(text !== null && text.includes(oid))) {
                  _context.next = 76;
                  break;
                }

                throw new Error('Failed to read git object with oid ' + oid + ' because it is a shallow commit');

              case 76:
                if (file) {
                  _context.next = 78;
                  break;
                }

                throw new Error('Failed to read git object with oid ' + oid);

              case 78:
                if (!(format === 'deflated')) {
                  _context.next = 80;
                  break;
                }

                return _context.abrupt('return', { format: 'deflated', object: file, source: source });

              case 80:
                buffer$$1 = buffer.Buffer.from(pako.inflate(file));

                if (!(format === 'wrapped')) {
                  _context.next = 83;
                  break;
                }

                return _context.abrupt('return', { format: 'wrapped', object: buffer$$1, source: source });

              case 83:
                _GitObject$unwrap = models_js.GitObject.unwrap({ oid: oid, buffer: buffer$$1 }), type = _GitObject$unwrap.type, object = _GitObject$unwrap.object;

                if (!(format === 'content')) {
                  _context.next = 86;
                  break;
                }

                return _context.abrupt('return', { type: type, format: 'content', object: object, source: source });

              case 86:
              case 'end':
                return _context.stop();
            }
          }
        }, _callee, this, [[14, 58, 62, 70], [63,, 65, 69]]);
      }));

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

      return read;
    }()
  }, {
    key: 'hash',
    value: function () {
      var _ref4 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2(_ref3) {
        var gitdir = _ref3.gitdir,
            type = _ref3.type,
            object = _ref3.object;
        var buffer$$1, oid;
        return _regeneratorRuntime.wrap(function _callee2$(_context2) {
          while (1) {
            switch (_context2.prev = _context2.next) {
              case 0:
                buffer$$1 = buffer.Buffer.concat([buffer.Buffer.from(type + ' '), buffer.Buffer.from(object.byteLength.toString()), buffer.Buffer.from([0]), buffer.Buffer.from(object)]);
                oid = shasum(buffer$$1);
                return _context2.abrupt('return', oid);

              case 3:
              case 'end':
                return _context2.stop();
            }
          }
        }, _callee2, this);
      }));

      function hash(_x2) {
        return _ref4.apply(this, arguments);
      }

      return hash;
    }()
  }, {
    key: 'write',
    value: function () {
      var _ref6 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee3(_ref5) {
        var _fs = _ref5.fs,
            gitdir = _ref5.gitdir,
            type = _ref5.type,
            object = _ref5.object;

        var fs, _GitObject$wrap, buffer$$1, oid, file, filepath;

        return _regeneratorRuntime.wrap(function _callee3$(_context3) {
          while (1) {
            switch (_context3.prev = _context3.next) {
              case 0:
                fs = new models_js.FileSystem(_fs);
                _GitObject$wrap = models_js.GitObject.wrap({ type: type, object: object }), buffer$$1 = _GitObject$wrap.buffer, oid = _GitObject$wrap.oid;
                file = buffer.Buffer.from(pako.deflate(buffer$$1));
                filepath = gitdir + '/objects/' + oid.slice(0, 2) + '/' + oid.slice(2);
                // Don't overwrite existing git objects - this helps avoid EPERM errors.
                // Although I don't know how we'd fix corrupted objects then. Perhaps delete them
                // on read?

                _context3.next = 6;
                return fs.exists(filepath);

              case 6:
                if (_context3.sent) {
                  _context3.next = 9;
                  break;
                }

                _context3.next = 9;
                return fs.write(filepath, file);

              case 9:
                return _context3.abrupt('return', oid);

              case 10:
              case 'end':
                return _context3.stop();
            }
          }
        }, _callee3, this);
      }));

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

      return write;
    }()
  }]);

  return GitObjectManager;
}();

// @see https://git-scm.com/docs/git-rev-parse.html#_specifying_revisions
var refpaths = function refpaths(ref) {
  return ['' + ref, 'refs/' + ref, 'refs/tags/' + ref, 'refs/heads/' + ref, 'refs/remotes/' + ref, 'refs/remotes/' + ref + '/HEAD'];
};

/** @ignore */
var GitRefManager = function () {
  function GitRefManager() {
    _classCallCheck(this, GitRefManager);
  }

  _createClass(GitRefManager, null, [{
    key: 'updateRemoteRefs',

    /* ::
    updateRemoteRefs : ({
      gitdir: string,
      remote: string,
      refs: Map<string, string>,
      symrefs: Map<string, string>
    }) => Promise<void>
    */
    value: function () {
      var _ref2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(_ref) {
        var _fs = _ref.fs,
            gitdir = _ref.gitdir,
            remote = _ref.remote,
            refs = _ref.refs,
            symrefs = _ref.symrefs,
            tags = _ref.tags,
            refspecs = _ref.refspecs;

        var fs, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, value, config, refspec, actualRefsToWrite, _iteratorNormalCompletion2, _didIteratorError2, _iteratorError2, _iterator2, _step2, serverRef, filename, oid, refTranslations, _iteratorNormalCompletion3, _didIteratorError3, _iteratorError3, _iterator3, _step3, _ref3, _ref4, _serverRef, translatedRef, _value, symrefTranslations, _iteratorNormalCompletion4, _didIteratorError4, _iteratorError4, _iterator4, _step4, _ref5, _ref6, _serverRef2, _translatedRef, _value2, symtarget, normalizeValue, _iteratorNormalCompletion5, _didIteratorError5, _iteratorError5, _iterator5, _step5, _ref7, _ref8, key, _value3;

        return _regeneratorRuntime.wrap(function _callee$(_context) {
          while (1) {
            switch (_context.prev = _context.next) {
              case 0:
                fs = new models_js.FileSystem(_fs);
                // Validate input

                _iteratorNormalCompletion = true;
                _didIteratorError = false;
                _iteratorError = undefined;
                _context.prev = 4;
                _iterator = _getIterator(refs.values());

              case 6:
                if (_iteratorNormalCompletion = (_step = _iterator.next()).done) {
                  _context.next = 13;
                  break;
                }

                value = _step.value;

                if (value.match(/[0-9a-f]{40}/)) {
                  _context.next = 10;
                  break;
                }

                throw new Error('Unexpected ref contents: \'' + value + '\'');

              case 10:
                _iteratorNormalCompletion = true;
                _context.next = 6;
                break;

              case 13:
                _context.next = 19;
                break;

              case 15:
                _context.prev = 15;
                _context.t0 = _context['catch'](4);
                _didIteratorError = true;
                _iteratorError = _context.t0;

              case 19:
                _context.prev = 19;
                _context.prev = 20;

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

              case 22:
                _context.prev = 22;

                if (!_didIteratorError) {
                  _context.next = 25;
                  break;
                }

                throw _iteratorError;

              case 25:
                return _context.finish(22);

              case 26:
                return _context.finish(19);

              case 27:
                _context.next = 29;
                return GitConfigManager.get({ fs: fs, gitdir: gitdir });

              case 29:
                config = _context.sent;

                if (refspecs) {
                  _context.next = 36;
                  break;
                }

                _context.next = 33;
                return config.getall('remote.' + remote + '.fetch');

              case 33:
                refspecs = _context.sent;

                if (!(refspecs.length === 0)) {
                  _context.next = 36;
                  break;
                }

                throw new Error('Could not find a fetch refspec fot remote \'' + remote + '\'.\nMake sure the config file has an entry like the following:\n[remote "' + remote + '"]\nfetch = +refs/heads/*:refs/remotes/origin/*');

              case 36:
                refspec = models_js.GitRefSpecSet.from(refspecs);
                actualRefsToWrite = new _Map();
                // Add all tags if the fetch tags argument is true.

                if (!tags) {
                  _context.next = 70;
                  break;
                }

                _iteratorNormalCompletion2 = true;
                _didIteratorError2 = false;
                _iteratorError2 = undefined;
                _context.prev = 42;
                _iterator2 = _getIterator(refs.keys());

              case 44:
                if (_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done) {
                  _context.next = 56;
                  break;
                }

                serverRef = _step2.value;

                if (!(serverRef.startsWith('refs/tags') && !serverRef.endsWith('^{}'))) {
                  _context.next = 53;
                  break;
                }

                filename = path.join(gitdir, serverRef);
                // Git's behavior is to only fetch tags that do not conflict with tags already present.

                _context.next = 50;
                return fs.exists(filename);

              case 50:
                if (_context.sent) {
                  _context.next = 53;
                  break;
                }

                // If there is a dereferenced an annotated tag value available, prefer that.
                oid = refs.get(serverRef + '^{}') || refs.get(serverRef);

                actualRefsToWrite.set(serverRef, oid);

              case 53:
                _iteratorNormalCompletion2 = true;
                _context.next = 44;
                break;

              case 56:
                _context.next = 62;
                break;

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

              case 62:
                _context.prev = 62;
                _context.prev = 63;

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

              case 65:
                _context.prev = 65;

                if (!_didIteratorError2) {
                  _context.next = 68;
                  break;
                }

                throw _iteratorError2;

              case 68:
                return _context.finish(65);

              case 69:
                return _context.finish(62);

              case 70:
                // Combine refs and symrefs giving symrefs priority
                refTranslations = refspec.translate([].concat(_toConsumableArray(refs.keys())));
                _iteratorNormalCompletion3 = true;
                _didIteratorError3 = false;
                _iteratorError3 = undefined;
                _context.prev = 74;

                for (_iterator3 = _getIterator(refTranslations); !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
                  _ref3 = _step3.value;
                  _ref4 = _slicedToArray(_ref3, 2);
                  _serverRef = _ref4[0];
                  translatedRef = _ref4[1];
                  _value = refs.get(_serverRef);

                  actualRefsToWrite.set(translatedRef, _value);
                }
                _context.next = 82;
                break;

              case 78:
                _context.prev = 78;
                _context.t2 = _context['catch'](74);
                _didIteratorError3 = true;
                _iteratorError3 = _context.t2;

              case 82:
                _context.prev = 82;
                _context.prev = 83;

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

              case 85:
                _context.prev = 85;

                if (!_didIteratorError3) {
                  _context.next = 88;
                  break;
                }

                throw _iteratorError3;

              case 88:
                return _context.finish(85);

              case 89:
                return _context.finish(82);

              case 90:
                symrefTranslations = refspec.translate([].concat(_toConsumableArray(symrefs.keys())));
                _iteratorNormalCompletion4 = true;
                _didIteratorError4 = false;
                _iteratorError4 = undefined;
                _context.prev = 94;

                for (_iterator4 = _getIterator(symrefTranslations); !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
                  _ref5 = _step4.value;
                  _ref6 = _slicedToArray(_ref5, 2);
                  _serverRef2 = _ref6[0];
                  _translatedRef = _ref6[1];
                  _value2 = symrefs.get(_serverRef2);
                  symtarget = refspec.translateOne(_value2);

                  if (symtarget) {
                    actualRefsToWrite.set(_translatedRef, 'ref: ' + symtarget);
                  }
                }
                // Update files
                // TODO: For large repos with a history of thousands of pull requests
                // (i.e. gitlab-ce) it would be vastly more efficient to write them
                // to .git/packed-refs.
                // The trick is to make sure we a) don't write a packed ref that is
                // already shadowed by a loose ref and b) don't loose any refs already
                // in packed-refs. Doing this efficiently may be difficult. A
                // solution that might work is
                // a) load the current packed-refs file
                // b) add actualRefsToWrite, overriding the existing values if present
                // c) enumerate all the loose refs currently in .git/refs/remotes/${remote}
                // d) overwrite their value with the new value.
                // Examples of refs we need to avoid writing in loose format for efficieny's sake
                // are .git/refs/remotes/origin/refs/remotes/remote_mirror_3059
                // and .git/refs/remotes/origin/refs/merge-requests
                _context.next = 102;
                break;

              case 98:
                _context.prev = 98;
                _context.t3 = _context['catch'](94);
                _didIteratorError4 = true;
                _iteratorError4 = _context.t3;

              case 102:
                _context.prev = 102;
                _context.prev = 103;

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

              case 105:
                _context.prev = 105;

                if (!_didIteratorError4) {
                  _context.next = 108;
                  break;
                }

                throw _iteratorError4;

              case 108:
                return _context.finish(105);

              case 109:
                return _context.finish(102);

              case 110:
                normalizeValue = function normalizeValue(value) {
                  return value.trim() + '\n';
                };

                _iteratorNormalCompletion5 = true;
                _didIteratorError5 = false;
                _iteratorError5 = undefined;
                _context.prev = 114;
                _iterator5 = _getIterator(actualRefsToWrite);

              case 116:
                if (_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done) {
                  _context.next = 126;
                  break;
                }

                _ref7 = _step5.value;
                _ref8 = _slicedToArray(_ref7, 2);
                key = _ref8[0];
                _value3 = _ref8[1];
                _context.next = 123;
                return fs.write(path.join(gitdir, key), normalizeValue(_value3), 'utf8');

              case 123:
                _iteratorNormalCompletion5 = true;
                _context.next = 116;
                break;

              case 126:
                _context.next = 132;
                break;

              case 128:
                _context.prev = 128;
                _context.t4 = _context['catch'](114);
                _didIteratorError5 = true;
                _iteratorError5 = _context.t4;

              case 132:
                _context.prev = 132;
                _context.prev = 133;

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

              case 135:
                _context.prev = 135;

                if (!_didIteratorError5) {
                  _context.next = 138;
                  break;
                }

                throw _iteratorError5;

              case 138:
                return _context.finish(135);

              case 139:
                return _context.finish(132);

              case 140:
              case 'end':
                return _context.stop();
            }
          }
        }, _callee, this, [[4, 15, 19, 27], [20,, 22, 26], [42, 58, 62, 70], [63,, 65, 69], [74, 78, 82, 90], [83,, 85, 89], [94, 98, 102, 110], [103,, 105, 109], [114, 128, 132, 140], [133,, 135, 139]]);
      }));

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

      return updateRemoteRefs;
    }()
    // TODO: make this less crude?

  }, {
    key: 'writeRef',
    value: function () {
      var _ref10 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2(_ref9) {
        var _fs = _ref9.fs,
            gitdir = _ref9.gitdir,
            ref = _ref9.ref,
            value = _ref9.value;
        var fs, normalizeValue;
        return _regeneratorRuntime.wrap(function _callee2$(_context2) {
          while (1) {
            switch (_context2.prev = _context2.next) {
              case 0:
                fs = new models_js.FileSystem(_fs);
                // Validate input

                if (value.match(/[0-9a-f]{40}/)) {
                  _context2.next = 3;
                  break;
                }

                throw new Error('Unexpected ref contents: \'' + value + '\'');

              case 3:
                normalizeValue = function normalizeValue(value) {
                  return value.trim() + '\n';
                };

                _context2.next = 6;
                return fs.write(path.join(gitdir, ref), normalizeValue(value), 'utf8');

              case 6:
              case 'end':
                return _context2.stop();
            }
          }
        }, _callee2, this);
      }));

      function writeRef(_x2) {
        return _ref10.apply(this, arguments);
      }

      return writeRef;
    }()
  }, {
    key: 'resolve',
    value: function () {
      var _ref12 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee3(_ref11) {
        var _fs = _ref11.fs,
            gitdir = _ref11.gitdir,
            ref = _ref11.ref,
            depth = _ref11.depth;

        var fs, sha, packedMap, allpaths, _iteratorNormalCompletion6, _didIteratorError6, _iteratorError6, _iterator6, _step6, _ref13;

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

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

                depth--;

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

                return _context3.abrupt('return', ref);

              case 5:
                sha = void 0;
                // Is it a ref pointer?

                if (!ref.startsWith('ref: ')) {
                  _context3.next = 9;
                  break;
                }

                ref = ref.slice('ref: '.length);
                return _context3.abrupt('return', GitRefManager.resolve({ fs: fs, gitdir: gitdir, ref: ref, depth: depth }));

              case 9:
                if (!(ref.length === 40 && /[0-9a-f]{40}/.test(ref))) {
                  _context3.next = 11;
                  break;
                }

                return _context3.abrupt('return', ref);

              case 11:
                _context3.next = 13;
                return GitRefManager.packedRefs({ fs: fs, gitdir: gitdir });

              case 13:
                packedMap = _context3.sent;

                // Look in all the proper paths, in this order
                allpaths = refpaths(ref);
                _iteratorNormalCompletion6 = true;
                _didIteratorError6 = false;
                _iteratorError6 = undefined;
                _context3.prev = 18;
                _iterator6 = _getIterator(allpaths);

              case 20:
                if (_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done) {
                  _context3.next = 33;
                  break;
                }

                _ref13 = _step6.value;
                _context3.next = 24;
                return fs.read(gitdir + '/' + _ref13, { encoding: 'utf8' });

              case 24:
                _context3.t0 = _context3.sent;

                if (_context3.t0) {
                  _context3.next = 27;
                  break;
                }

                _context3.t0 = packedMap.get(_ref13);

              case 27:
                sha = _context3.t0;

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

                return _context3.abrupt('return', GitRefManager.resolve({ fs: fs, gitdir: gitdir, ref: sha.trim(), depth: depth }));

              case 30:
                _iteratorNormalCompletion6 = true;
                _context3.next = 20;
                break;

              case 33:
                _context3.next = 39;
                break;

              case 35:
                _context3.prev = 35;
                _context3.t1 = _context3['catch'](18);
                _didIteratorError6 = true;
                _iteratorError6 = _context3.t1;

              case 39:
                _context3.prev = 39;
                _context3.prev = 40;

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

              case 42:
                _context3.prev = 42;

                if (!_didIteratorError6) {
                  _context3.next = 45;
                  break;
                }

                throw _iteratorError6;

              case 45:
                return _context3.finish(42);

              case 46:
                return _context3.finish(39);

              case 47:
                throw new Error('Could not resolve reference ' + ref);

              case 48:
              case 'end':
                return _context3.stop();
            }
          }
        }, _callee3, this, [[18, 35, 39, 47], [40,, 42, 46]]);
      }));

      function resolve(_x3) {
        return _ref12.apply(this, arguments);
      }

      return resolve;
    }()
  }, {
    key: 'expand',
    value: function () {
      var _ref15 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee4(_ref14) {
        var _fs = _ref14.fs,
            gitdir = _ref14.gitdir,
            ref = _ref14.ref;

        var fs, packedMap, allpaths, _iteratorNormalCompletion7, _didIteratorError7, _iteratorError7, _iterator7, _step7, _ref16;

        return _regeneratorRuntime.wrap(function _callee4$(_context4) {
          while (1) {
            switch (_context4.prev = _context4.next) {
              case 0:
                fs = new models_js.FileSystem(_fs);
                // Is it a complete and valid SHA?

                if (!(ref.length === 40 && /[0-9a-f]{40}/.test(ref))) {
                  _context4.next = 3;
                  break;
                }

                return _context4.abrupt('return', ref);

              case 3:
                _context4.next = 5;
                return GitRefManager.packedRefs({ fs: fs, gitdir: gitdir });

              case 5:
                packedMap = _context4.sent;

                // Look in all the proper paths, in this order
                allpaths = refpaths(ref);
                _iteratorNormalCompletion7 = true;
                _didIteratorError7 = false;
                _iteratorError7 = undefined;
                _context4.prev = 10;
                _iterator7 = _getIterator(allpaths);

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

                _ref16 = _step7.value;
                _context4.next = 16;
                return fs.exists(gitdir + '/' + _ref16);

              case 16:
                if (!_context4.sent) {
                  _context4.next = 18;
                  break;
                }

                return _context4.abrupt('return', _ref16);

              case 18:
                if (!packedMap.has(_ref16)) {
                  _context4.next = 20;
                  break;
                }

                return _context4.abrupt('return', _ref16);

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

              case 23:
                _context4.next = 29;
                break;

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

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

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

              case 32:
                _context4.prev = 32;

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

                throw _iteratorError7;

              case 35:
                return _context4.finish(32);

              case 36:
                return _context4.finish(29);

              case 37:
                throw new Error('Could not expand ref ' + ref);

              case 38:
              case 'end':
                return _context4.stop();
            }
          }
        }, _callee4, this, [[10, 25, 29, 37], [30,, 32, 36]]);
      }));

      function expand(_x4) {
        return _ref15.apply(this, arguments);
      }

      return expand;
    }()
  }, {
    key: 'resolveAgainstMap',
    value: function resolveAgainstMap(_ref17) {
      var ref = _ref17.ref,
          depth = _ref17.depth,
          map = _ref17.map;

      if (depth !== undefined) {
        depth--;
        if (depth === -1) {
          return ref;
        }
      }
      // Is it a ref pointer?
      if (ref.startsWith('ref: ')) {
        ref = ref.slice('ref: '.length);
        return GitRefManager.resolveAgainstMap({ ref: ref, depth: depth, map: map });
      }
      // Is it a complete and valid SHA?
      if (ref.length === 40 && /[0-9a-f]{40}/.test(ref)) {
        return ref;
      }
      // Look in all the proper paths, in this order
      var allpaths = refpaths(ref);
      var _iteratorNormalCompletion8 = true;
      var _didIteratorError8 = false;
      var _iteratorError8 = undefined;

      try {
        for (var _iterator8 = _getIterator(allpaths), _step8; !(_iteratorNormalCompletion8 = (_step8 = _iterator8.next()).done); _iteratorNormalCompletion8 = true) {
          var _ref18 = _step8.value;

          var sha = map.get(_ref18);
          if (sha) {
            return GitRefManager.resolveAgainstMap({ ref: sha.trim(), depth: depth, map: map });
          }
        }
        // Do we give up?
      } catch (err) {
        _didIteratorError8 = true;
        _iteratorError8 = err;
      } finally {
        try {
          if (!_iteratorNormalCompletion8 && _iterator8.return) {
            _iterator8.return();
          }
        } finally {
          if (_didIteratorError8) {
            throw _iteratorError8;
          }
        }
      }

      throw new Error('Could not resolve reference ' + ref);
    }
  }, {
    key: 'packedRefs',
    value: function () {
      var _ref20 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee5(_ref19) {
        var _fs = _ref19.fs,
            gitdir = _ref19.gitdir;

        var refs, fs, text, lines, key, _iteratorNormalCompletion9, _didIteratorError9, _iteratorError9, _iterator9, _step9, line, i, value, _value4;

        return _regeneratorRuntime.wrap(function _callee5$(_context5) {
          while (1) {
            switch (_context5.prev = _context5.next) {
              case 0:
                refs = new _Map();
                fs = new models_js.FileSystem(_fs);
                _context5.next = 4;
                return fs.read(gitdir + '/packed-refs', { encoding: 'utf8' });

              case 4:
                text = _context5.sent;

                if (text) {
                  _context5.next = 7;
                  break;
                }

                return _context5.abrupt('return', refs);

              case 7:
                lines = text.trim().split('\n').filter(function (line) {
                  return !/^\s*#/.test(line);
                });
                key = null;
                _iteratorNormalCompletion9 = true;
                _didIteratorError9 = false;
                _iteratorError9 = undefined;
                _context5.prev = 12;

                for (_iterator9 = _getIterator(lines); !(_iteratorNormalCompletion9 = (_step9 = _iterator9.next()).done); _iteratorNormalCompletion9 = true) {
                  line = _step9.value;
                  i = line.indexOf(' ');

                  if (line.startsWith('^')) {
                    // This is a oid for the commit associated with the annotated tag immediately preceding this line.
                    // Trim off the '^'
                    value = line.slice(1, i);
                    // The tagname^{} syntax is based on the output of `git show-ref --tags -d`

                    refs.set(key + '^{}', value);
                  } else {
                    // This is an oid followed by the ref name
                    _value4 = line.slice(0, i);

                    key = line.slice(i + 1);
                    refs.set(key, _value4);
                  }
                }
                _context5.next = 20;
                break;

              case 16:
                _context5.prev = 16;
                _context5.t0 = _context5['catch'](12);
                _didIteratorError9 = true;
                _iteratorError9 = _context5.t0;

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

                if (!_iteratorNormalCompletion9 && _iterator9.return) {
                  _iterator9.return();
                }

              case 23:
                _context5.prev = 23;

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

                throw _iteratorError9;

              case 26:
                return _context5.finish(23);

              case 27:
                return _context5.finish(20);

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

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

      function packedRefs(_x5) {
        return _ref20.apply(this, arguments);
      }

      return packedRefs;
    }()
    // List all the refs that match the `filepath` prefix

  }, {
    key: 'listRefs',
    value: function () {
      var _ref22 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee6(_ref21) {
        var _fs = _ref21.fs,
            gitdir = _ref21.gitdir,
            filepath = _ref21.filepath;

        var fs, packedMap, files, _iteratorNormalCompletion10, _didIteratorError10, _iteratorError10, _iterator10, _step10, key;

        return _regeneratorRuntime.wrap(function _callee6$(_context6) {
          while (1) {
            switch (_context6.prev = _context6.next) {
              case 0:
                fs = new models_js.FileSystem(_fs);
                packedMap = GitRefManager.packedRefs({ fs: fs, gitdir: gitdir });
                files = null;
                _context6.prev = 3;
                _context6.next = 6;
                return fs.readdirDeep(gitdir + '/' + filepath);

              case 6:
                files = _context6.sent;

                files = files.map(function (x) {
                  return x.replace(gitdir + '/' + filepath + '/', '');
                });
                _context6.next = 13;
                break;

              case 10:
                _context6.prev = 10;
                _context6.t0 = _context6['catch'](3);

                files = [];

              case 13:
                _iteratorNormalCompletion10 = true;
                _didIteratorError10 = false;
                _iteratorError10 = undefined;
                _context6.prev = 16;
                _context6.t1 = _getIterator;
                _context6.next = 20;
                return packedMap;

              case 20:
                _context6.t2 = _context6.sent.keys();
                _iterator10 = (0, _context6.t1)(_context6.t2);

              case 22:
                if (_iteratorNormalCompletion10 = (_step10 = _iterator10.next()).done) {
                  _context6.next = 28;
                  break;
                }

                key = _step10.value;

                // filter by prefix
                if (key.startsWith(filepath)) {
                  // remove prefix
                  key = key.replace(filepath + '/', '');
                  // Don't include duplicates; the loose files have precedence anyway
                  if (!files.includes(key)) {
                    files.push(key);
                  }
                }

              case 25:
                _iteratorNormalCompletion10 = true;
                _context6.next = 22;
                break;

              case 28:
                _context6.next = 34;
                break;

              case 30:
                _context6.prev = 30;
                _context6.t3 = _context6['catch'](16);
                _didIteratorError10 = true;
                _iteratorError10 = _context6.t3;

              case 34:
                _context6.prev = 34;
                _context6.prev = 35;

                if (!_iteratorNormalCompletion10 && _iterator10.return) {
                  _iterator10.return();
                }

              case 37:
                _context6.prev = 37;

                if (!_didIteratorError10) {
                  _context6.next = 40;
                  break;
                }

                throw _iteratorError10;

              case 40:
                return _context6.finish(37);

              case 41:
                return _context6.finish(34);

              case 42:
                return _context6.abrupt('return', files);

              case 43:
              case 'end':
                return _context6.stop();
            }
          }
        }, _callee6, this, [[3, 10], [16, 30, 34, 42], [35,, 37, 41]]);
      }));

      function listRefs(_x6) {
        return _ref22.apply(this, arguments);
      }

      return listRefs;
    }()
  }, {
    key: 'listBranches',
    value: function () {
      var _ref24 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee7(_ref23) {
        var _fs = _ref23.fs,
            gitdir = _ref23.gitdir,
            remote = _ref23.remote;
        var fs;
        return _regeneratorRuntime.wrap(function _callee7$(_context7) {
          while (1) {
            switch (_context7.prev = _context7.next) {
              case 0:
                fs = new models_js.FileSystem(_fs);

                if (!remote) {
                  _context7.next = 5;
                  break;
                }

                return _context7.abrupt('return', GitRefManager.listRefs({
                  fs: fs,
                  gitdir: gitdir,
                  filepath: 'refs/remotes/' + remote
                }));

              case 5:
                return _context7.abrupt('return', GitRefManager.listRefs({ fs: fs, gitdir: gitdir, filepath: 'refs/heads' }));

              case 6:
              case 'end':
                return _context7.stop();
            }
          }
        }, _callee7, this);
      }));

      function listBranches(_x7) {
        return _ref24.apply(this, arguments);
      }

      return listBranches;
    }()
  }, {
    key: 'listTags',
    value: function () {
      var _ref26 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee8(_ref25) {
        var _fs = _ref25.fs,
            gitdir = _ref25.gitdir;
        var fs, tags;
        return _regeneratorRuntime.wrap(function _callee8$(_context8) {
          while (1) {
            switch (_context8.prev = _context8.next) {
              case 0:
                fs = new models_js.FileSystem(_fs);
                _context8.next = 3;
                return GitRefManager.listRefs({
                  fs: fs,
                  gitdir: gitdir,
                  filepath: 'refs/tags'
                });

              case 3:
                tags = _context8.sent;

                tags = tags.filter(function (x) {
                  return !x.endsWith('^{}');
                });
                return _context8.abrupt('return', tags);

              case 6:
              case 'end':
                return _context8.stop();
            }
          }
        }, _callee8, this);
      }));

      function listTags(_x8) {
        return _ref26.apply(this, arguments);
      }

      return listTags;
    }()
  }]);

  return GitRefManager;
}();

function basicAuth(auth) {
  return 'Basic ' + buffer.Buffer.from(auth.username + ':' + auth.password).toString('base64');
}

/** @ignore */

var GitRemoteHTTP = function () {
  /*::
  GIT_URL : string
  refs : Map<string, string>
  symrefs : Map<string, string>
  capabilities : Set<string>
  auth : { username : string, password : string }
  */
  function GitRemoteHTTP(url /*: string */) {
    _classCallCheck(this, GitRemoteHTTP);

    // Auto-append the (necessary) .git if it's missing.
    if (!url.endsWith('.git')) url = url += '.git';
    this.GIT_URL = url;
  }

  _createClass(GitRemoteHTTP, [{
    key: 'preparePull',
    value: function () {
      var _ref = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee() {
        return _regeneratorRuntime.wrap(function _callee$(_context) {
          while (1) {
            switch (_context.prev = _context.next) {
              case 0:
                _context.next = 2;
                return this.discover('git-upload-pack');

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

      function preparePull() {
        return _ref.apply(this, arguments);
      }

      return preparePull;
    }()
  }, {
    key: 'preparePush',
    value: function () {
      var _ref2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2() {
        return _regeneratorRuntime.wrap(function _callee2$(_context2) {
          while (1) {
            switch (_context2.prev = _context2.next) {
              case 0:
                _context2.next = 2;
                return this.discover('git-receive-pack');

              case 2:
              case 'end':
                return _context2.stop();
            }
          }
        }, _callee2, this);
      }));

      function preparePush() {
        return _ref2.apply(this, arguments);
      }

      return preparePush;
    }()
  }, {
    key: 'discover',
    value: function () {
      var _ref3 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee3(service /*: string */) {
        var _this = this;

        var headers, res, data, read, lineOne, lineTwo, _lineTwo$toString$tri, _lineTwo$toString$tri2, firstRef, capabilities, _firstRef$split, _firstRef$split2, ref, name, line, _line$toString$trim$s, _line$toString$trim$s2, _ref4, _name, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, cap, m;

        return _regeneratorRuntime.wrap(function _callee3$(_context3) {
          while (1) {
            switch (_context3.prev = _context3.next) {
              case 0:
                this.capabilities = new _Set();
                this.refs = new _Map();
                this.symrefs = new _Map();
                headers = {};
                // headers['Accept'] = `application/x-${service}-advertisement`

                if (this.auth) {
                  headers['Authorization'] = basicAuth(this.auth);
                }
                _context3.next = 7;
                return pify(simpleGet)({
                  method: 'GET',
                  url: this.GIT_URL + '/info/refs?service=' + service,
                  headers: headers
                });

              case 7:
                res = _context3.sent;

                if (!(res.statusCode !== 200)) {
                  _context3.next = 10;
                  break;
                }

                throw new Error('HTTP Error: ' + res.statusCode + ' ' + res.statusMessage);

              case 10:
                _context3.next = 12;
                return pify(concat)(res);

              case 12:
                data = _context3.sent;

                // There is probably a better way to do this, but for now
                // let's just throw the result parser inline here.
                read = models_js.GitPktLine.reader(data);
                _context3.next = 16;
                return read();

              case 16:
                lineOne = _context3.sent;

              case 17:
                if (!(lineOne === null)) {
                  _context3.next = 23;
                  break;
                }

                _context3.next = 20;
                return read();

              case 20:
                lineOne = _context3.sent;
                _context3.next = 17;
                break;

              case 23:
                if (!(lineOne === true)) {
                  _context3.next = 25;
                  break;
                }

                throw new Error('Bad response from git server.');

              case 25:
                if (!(lineOne.toString('utf8') !== '# service=' + service + '\n')) {
                  _context3.next = 27;
                  break;
                }

                throw new Error('Expected \'# service=' + service + '\\n\' but got \'' + lineOne.toString('utf8') + '\'');

              case 27:
                _context3.next = 29;
                return read();

              case 29:
                lineTwo = _context3.sent;

              case 30:
                if (!(lineTwo === null)) {
                  _context3.next = 36;
                  break;
                }

                _context3.next = 33;
                return read();

              case 33:
                lineTwo = _context3.sent;
                _context3.next = 30;
                break;

              case 36:
                if (!(lineTwo === true)) {
                  _context3.next = 38;
                  break;
                }

                return _context3.abrupt('return');

              case 38:
                _lineTwo$toString$tri = lineTwo.toString('utf8').trim().split('\0'), _lineTwo$toString$tri2 = _slicedToArray(_lineTwo$toString$tri, 2), firstRef = _lineTwo$toString$tri2[0], capabilities = _lineTwo$toString$tri2[1];

                capabilities.split(' ').map(function (x) {
                  return _this.capabilities.add(x);
                });
                _firstRef$split = firstRef.split(' '), _firstRef$split2 = _slicedToArray(_firstRef$split, 2), ref = _firstRef$split2[0], name = _firstRef$split2[1];

                this.refs.set(name, ref);

              case 42:

                _context3.next = 45;
                return read();

              case 45:
                line = _context3.sent;

                if (!(line === true)) {
                  _context3.next = 48;
                  break;
                }

                return _context3.abrupt('break', 51);

              case 48:
                if (line !== null) {
                  _line$toString$trim$s = line.toString('utf8').trim().split(' '), _line$toString$trim$s2 = _slicedToArray(_line$toString$trim$s, 2), _ref4 = _line$toString$trim$s2[0], _name = _line$toString$trim$s2[1];

                  this.refs.set(_name, _ref4);
                }
                _context3.next = 42;
                break;

              case 51:
                // Symrefs are thrown into the "capabilities" unfortunately.
                _iteratorNormalCompletion = true;
                _didIteratorError = false;
                _iteratorError = undefined;
                _context3.prev = 54;
                for (_iterator = _getIterator(this.capabilities); !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
                  cap = _step.value;

                  if (cap.startsWith('symref=')) {
                    m = cap.match(/symref=([^:]+):(.*)/);

                    if (m.length === 3) {
                      this.symrefs.set(m[1], m[2]);
                    }
                  }
                }
                _context3.next = 62;
                break;

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

              case 62:
                _context3.prev = 62;
                _context3.prev = 63;

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

              case 65:
                _context3.prev = 65;

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

                throw _iteratorError;

              case 68:
                return _context3.finish(65);

              case 69:
                return _context3.finish(62);

              case 70:
              case 'end':
                return _context3.stop();
            }
          }
        }, _callee3, this, [[54, 58, 62, 70], [63,, 65, 69]]);
      }));

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

      return discover;
    }()
  }, {
    key: 'push',
    value: function () {
      var _ref5 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee4(stream$$1 /*: ReadableStream */) {
        var service, _ref6, packetlines, packfile, result, response, read, line, lines, _iteratorNormalCompletion2, _didIteratorError2, _iteratorError2, _iterator2, _step2, _line, status, refAndMessage;

        return _regeneratorRuntime.wrap(function _callee4$(_context4) {
          while (1) {
            switch (_context4.prev = _context4.next) {
              case 0:
                service = 'git-receive-pack';
                _context4.next = 3;
                return this.stream({
                  stream: stream$$1,
                  service: service
                });

              case 3:
                _ref6 = _context4.sent;
                packetlines = _ref6.packetlines;
                packfile = _ref6.packfile;
                _context4.next = 8;
                return pify(concat)(packfile);

              case 8:
                packfile = _context4.sent;
                _context4.next = 11;
                return pify(concat)(packetlines);

              case 11:
                packetlines = _context4.sent;
                result = {};
                // Parse the response!
                // I'm combining the side-band-64k and regular streams
                // because Github returns the first line in the sideband while
                // git-http-server returns it without the sideband.

                response = '';
                read = models_js.GitPktLine.reader(packfile);
                _context4.next = 17;
                return read();

              case 17:
                line = _context4.sent;

              case 18:
                if (!(line !== null && line !== true)) {
                  _context4.next = 25;
                  break;
                }

                response += line.toString('utf8') + '\n';
                _context4.next = 22;
                return read();

              case 22:
                line = _context4.sent;
                _context4.next = 18;
                break;

              case 25:
                response += packetlines.toString('utf8');

                lines = response.toString('utf8').split('\n');
                // We're expecting "unpack {unpack-result}"

                line = lines.shift();

                if (line.startsWith('unpack ')) {
                  _context4.next = 30;
                  break;
                }

                throw new Error('Unparsable response from server! Expected \'unpack ok\' or \'unpack [error message]\' but got \'' + line + '\'');

              case 30:
                if (line === 'unpack ok') {
                  result.ok = ['unpack'];
                } else {
                  result.errors = [line.trim()];
                }
                _iteratorNormalCompletion2 = true;
                _didIteratorError2 = false;
                _iteratorError2 = undefined;
                _context4.prev = 34;
                for (_iterator2 = _getIterator(lines); !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
                  _line = _step2.value;
                  status = _line.slice(0, 2);
                  refAndMessage = _line.slice(3);

                  if (status === 'ok') {
                    result.ok = result.ok || [];
                    result.ok.push(refAndMessage);
                  } else if (status === 'ng') {
                    result.errors = result.errors || [];
                    result.errors.push(refAndMessage);
                  }
                }
                _context4.next = 42;
                break;

              case 38:
                _context4.prev = 38;
                _context4.t0 = _context4['catch'](34);
                _didIteratorError2 = true;
                _iteratorError2 = _context4.t0;

              case 42:
                _context4.prev = 42;
                _context4.prev = 43;

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

              case 45:
                _context4.prev = 45;

                if (!_didIteratorError2) {
                  _context4.next = 48;
                  break;
                }

                throw _iteratorError2;

              case 48:
                return _context4.finish(45);

              case 49:
                return _context4.finish(42);

              case 50:
                utils_js.log(result);
                return _context4.abrupt('return', result);

              case 52:
              case 'end':
                return _context4.stop();
            }
          }
        }, _callee4, this, [[34, 38, 42, 50], [43,, 45, 49]]);
      }));

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

      return push;
    }()
  }, {
    key: 'pull',
    value: function () {
      var _ref7 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee5(stream$$1 /*: ReadableStream */) {
        var service, res;
        return _regeneratorRuntime.wrap(function _callee5$(_context5) {
          while (1) {
            switch (_context5.prev = _context5.next) {
              case 0:
                service = 'git-upload-pack';
                _context5.next = 3;
                return this.stream({ stream: stream$$1, service: service });

              case 3:
                res = _context5.sent;
                return _context5.abrupt('return', res);

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

      function pull(_x3) {
        return _ref7.apply(this, arguments);
      }

      return pull;
    }()
  }, {
    key: 'stream',
    value: function () {
      var _ref9 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee7(_ref8) {
        var _stream = _ref8.stream,
            service = _ref8.service;
        var headers, res, read, packetlines, packfile, progress, nextBit;
        return _regeneratorRuntime.wrap(function _callee7$(_context7) {
          while (1) {
            switch (_context7.prev = _context7.next) {
              case 0:
                headers = {};

                headers['content-type'] = 'application/x-' + service + '-request';
                headers['accept'] = 'application/x-' + service + '-result';
                headers['user-agent'] = 'git/' + utils_js.pkg.name + '@' + utils_js.pkg.version;
                if (this.auth) {
                  headers['authorization'] = basicAuth(this.auth);
                }
                _context7.next = 7;
                return pify(simpleGet)({
                  method: 'POST',
                  url: this.GIT_URL + '/' + service,
                  body: _stream,
                  headers: headers
                });

              case 7:
                res = _context7.sent;

                if (!(res.statusCode !== 200)) {
                  _context7.next = 10;
                  break;
                }

                throw new Error('HTTP Error: ' + res.statusCode + ' ' + res.statusMessage);

              case 10:
                // Parse the response!
                read = models_js.GitPktLine.streamReader(res);
                // And now for the ridiculous side-band-64k protocol

                packetlines = new stream.PassThrough();
                packfile = new stream.PassThrough();
                progress = new stream.PassThrough();
                // TODO: Use a proper through stream?

                nextBit = function () {
                  var _ref10 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee6() {
                    var line, error;
                    return _regeneratorRuntime.wrap(function _callee6$(_context6) {
                      while (1) {
                        switch (_context6.prev = _context6.next) {
                          case 0:
                            _context6.next = 2;
                            return read();

                          case 2:
                            line = _context6.sent;

                            if (!(line === null)) {
                              _context6.next = 5;
                              break;
                            }

                            return _context6.abrupt('return', nextBit());

                          case 5:
                            if (!(line === true)) {
                              _context6.next = 10;
                              break;
                            }

                            packetlines.end();
                            progress.end();
                            packfile.end();
                            return _context6.abrupt('return');

                          case 10:
                            _context6.t0 = line[0];
                            _context6.next = _context6.t0 === 1 ? 13 : _context6.t0 === 2 ? 15 : _context6.t0 === 3 ? 17 : 21;
                            break;

                          case 13:
                            // pack data
                            packfile.write(line.slice(1));
                            return _context6.abrupt('break', 22);

                          case 15:
                            // progress message
                            progress.write(line.slice(1));
                            return _context6.abrupt('break', 22);

                          case 17:
                            // fatal error message just before stream aborts
                            error = line.slice(1);

                            progress.write(error);
                            packfile.destroy(new Error(error.toString('utf8')));
                            return _context6.abrupt('return');

                          case 21:
                            // Not part of the side-band-64k protocol
                            packetlines.write(line.slice(0));

                          case 22:
                            // Careful not to blow up the stack.
                            // I think Promises in a tail-call position should be OK.
                            nextBit();

                          case 23:
                          case 'end':
                            return _context6.stop();
                        }
                      }
                    }, _callee6, this);
                  }));

                  return function nextBit() {
                    return _ref10.apply(this, arguments);
                  };
                }();

                nextBit();
                return _context7.abrupt('return', {
                  packetlines: packetlines,
                  packfile: packfile,
                  progress: progress
                });

              case 17:
              case 'end':
                return _context7.stop();
            }
          }
        }, _callee7, this);
      }));

      function stream$$1(_x4) {
        return _ref9.apply(this, arguments);
      }

      return stream$$1;
    }()
  }]);

  return GitRemoteHTTP;
}();

exports.GitConfigManager = GitConfigManager;
exports.GitShallowManager = GitShallowManager;
exports.GitIndexManager = GitIndexManager;
exports.GitIgnoreManager = GitIgnoreManager;
exports.GitObjectManager = GitObjectManager;
exports.GitRefManager = GitRefManager;
exports.GitRemoteHTTP = GitRemoteHTTP;
//# sourceMappingURL=managers.js.map
