'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 utils_js = require('./utils.js');
var _getIterator = _interopDefault(require('babel-runtime/core-js/get-iterator'));
var _Set = _interopDefault(require('babel-runtime/core-js/set'));
var path = _interopDefault(require('path'));
var _Map = _interopDefault(require('babel-runtime/core-js/map'));
var AsyncLock = _interopDefault(require('async-lock'));
var ignore = _interopDefault(require('ignore'));
var buffer = require('buffer');
var pako = _interopDefault(require('pako'));
var shasum = _interopDefault(require('shasum'));
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 stream = require('stream');

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

  _createClass(GitConfigManager, null, [{
    key: 'get',
    value: function () {
      var _ref2 = _asyncToGenerator(_regeneratorRuntime.mark(function _callee(_ref) {
        var gitdir = _ref.gitdir;
        var text;
        return _regeneratorRuntime.wrap(function _callee$(_context) {
          while (1) {
            switch (_context.prev = _context.next) {
              case 0:
                _context.next = 2;
                return utils_js.read(gitdir + '/config', { encoding: 'utf8' });

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

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

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

      return get;
    }()
  }, {
    key: 'save',
    value: function () {
      var _ref4 = _asyncToGenerator(_regeneratorRuntime.mark(function _callee2(_ref3) {
        var gitdir = _ref3.gitdir,
            config = _ref3.config;
        return _regeneratorRuntime.wrap(function _callee2$(_context2) {
          while (1) {
            switch (_context2.prev = _context2.next) {
              case 0:
                _context2.next = 2;
                return utils_js.write(gitdir + '/config', config.toString(), {
                  encoding: 'utf8'
                });

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

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

      return save;
    }()
  }]);

  return GitConfigManager;
}();

// @flow
// TODO: Add file locks.
var GitShallowManager = function () {
  function GitShallowManager() {
    _classCallCheck(this, GitShallowManager);
  }

  _createClass(GitShallowManager, null, [{
    key: 'read',
    value: function () {
      var _ref2 = _asyncToGenerator(_regeneratorRuntime.mark(function _callee(_ref) {
        var gitdir = _ref.gitdir;
        var oids, text;
        return _regeneratorRuntime.wrap(function _callee$(_context) {
          while (1) {
            switch (_context.prev = _context.next) {
              case 0:
                oids = new _Set();
                _context.next = 3;
                return utils_js.read(path.join(gitdir, 'shallow'), { encoding: 'utf8' });

              case 3:
                text = _context.sent;

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

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

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

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

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

      return read$$1;
    }()
  }, {
    key: 'write',
    value: function () {
      var _ref4 = _asyncToGenerator(_regeneratorRuntime.mark(function _callee2(_ref3) {
        var gitdir = _ref3.gitdir,
            oids = _ref3.oids;

        var text, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, oid;

        return _regeneratorRuntime.wrap(function _callee2$(_context2) {
          while (1) {
            switch (_context2.prev = _context2.next) {
              case 0:
                text = '';
                _iteratorNormalCompletion = true;
                _didIteratorError = false;
                _iteratorError = undefined;
                _context2.prev = 4;

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

                  text += oid + '\n';
                }
                _context2.next = 12;
                break;

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

              case 12:
                _context2.prev = 12;
                _context2.prev = 13;

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

              case 15:
                _context2.prev = 15;

                if (!_didIteratorError) {
                  _context2.next = 18;
                  break;
                }

                throw _iteratorError;

              case 18:
                return _context2.finish(15);

              case 19:
                return _context2.finish(12);

              case 20:
                _context2.next = 22;
                return utils_js.write(path.join(gitdir, 'shallow'), text, {
                  encoding: 'utf8'
                });

              case 22:
              case 'end':
                return _context2.stop();
            }
          }
        }, _callee2, this, [[4, 8, 12, 20], [13,, 15, 19]]);
      }));

      function write$$1(_x2) {
        return _ref4.apply(this, arguments);
      }

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

  return GitShallowManager;
}();

// @flow
// import LockManager from 'travix-lock-manager'
// import Lock from '../utils'

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

var GitIndexManager = function () {
  function GitIndexManager() {
    _classCallCheck(this, GitIndexManager);
  }

  _createClass(GitIndexManager, null, [{
    key: 'acquire',
    value: function () {
      var _ref = _asyncToGenerator(_regeneratorRuntime.mark(function _callee2(filepath, closure) {
        return _regeneratorRuntime.wrap(function _callee2$(_context2) {
          while (1) {
            switch (_context2.prev = _context2.next) {
              case 0:
                _context2.next = 2;
                return lock.acquire(filepath, _asyncToGenerator(_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 utils_js.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 utils_js.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 2:
              case 'end':
                return _context2.stop();
            }
          }
        }, _callee2, this);
      }));

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

      return acquire;
    }()
  }]);

  return GitIndexManager;
}();

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

// TODO: Implement .git/info/exclude

var GitIgnoreManager = function () {
  function GitIgnoreManager() {
    _classCallCheck(this, GitIgnoreManager);
  }

  _createClass(GitIgnoreManager, null, [{
    key: 'isIgnored',
    value: function () {
      var _ref2 = _asyncToGenerator(_regeneratorRuntime.mark(function _callee(_ref) {
        var gitdir = _ref.gitdir,
            workdir = _ref.workdir,
            pathname = _ref.pathname;

        var pairs, pieces, i, dir, 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:
                pairs = [{
                  gitignore: path.join(workdir, '.gitignore'),
                  pathname: pathname
                }];
                pieces = pathname.split('/');

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

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

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

                p = _step.value;
                _file = void 0;
                _context.prev = 12;
                _context.next = 15;
                return utils_js.read(p.gitignore, 'utf8');

              case 15:
                _file = _context.sent;
                _context.next = 22;
                break;

              case 18:
                _context.prev = 18;
                _context.t0 = _context['catch'](12);

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

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

              case 22:
                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.pathname);

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

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

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

              case 28:
                _iteratorNormalCompletion = true;
                _context.next = 9;
                break;

              case 31:
                _context.next = 37;
                break;

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

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

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

              case 40:
                _context.prev = 40;

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

                throw _iteratorError;

              case 43:
                return _context.finish(40);

              case 44:
                return _context.finish(37);

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

              case 46:
              case 'end':
                return _context.stop();
            }
          }
        }, _callee, this, [[7, 33, 37, 45], [12, 18], [38,, 40, 44]]);
      }));

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

      return isIgnored;
    }()
  }]);

  return GitIgnoreManager;
}();

// @flow
function wrapObject(_ref /*: {type: string, object: Buffer} */) {
  var type = _ref.type,
      object = _ref.object;

  var buffer$$1 = buffer.Buffer.concat([buffer.Buffer.from(type + ' '), buffer.Buffer.from(object.byteLength.toString()), buffer.Buffer.from([0]), buffer.Buffer.from(object)]);
  var oid = shasum(buffer$$1);
  return {
    oid: oid,
    file: buffer.Buffer.from(pako.deflate(buffer$$1))
  };
}

function unwrapObject(_ref2 /*: {oid: string, file: Buffer} */) {
  var oid = _ref2.oid,
      file = _ref2.file;

  var inflated = buffer.Buffer.from(pako.inflate(file));
  if (oid) {
    var sha = shasum(inflated);
    if (sha !== oid) {
      throw new Error('SHA check failed! Expected ' + oid + ', computed ' + sha);
    }
  }
  var s = inflated.indexOf(32); // first space
  var i = inflated.indexOf(0); // first null value
  var type = inflated.slice(0, s).toString('utf8'); // get type of object
  var length = inflated.slice(s + 1, i).toString('utf8'); // get type of object
  var actualLength = inflated.length - (i + 1);
  // verify length
  if (parseInt(length) !== actualLength) {
    throw new Error('Length mismatch: expected ' + length + ' bytes but got ' + actualLength + ' instead.');
  }
  return {
    type: type,
    object: buffer.Buffer.from(inflated.slice(i + 1))
  };
}

var GitObjectManager = function () {
  function GitObjectManager() {
    _classCallCheck(this, GitObjectManager);
  }

  _createClass(GitObjectManager, null, [{
    key: 'read',
    value: function () {
      var _ref4 = _asyncToGenerator(_regeneratorRuntime.mark(function _callee(_ref3 /*: {gitdir: string, oid: string} */) {
        var gitdir = _ref3.gitdir,
            oid = _ref3.oid;

        var file, text, _unwrapObject, type, object;

        return _regeneratorRuntime.wrap(function _callee$(_context) {
          while (1) {
            switch (_context.prev = _context.next) {
              case 0:
                _context.next = 2;
                return utils_js.read(gitdir + '/objects/' + oid.slice(0, 2) + '/' + oid.slice(2));

              case 2:
                file = _context.sent;

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

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

              case 6:
                text = _context.sent;

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

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

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

              case 12:
                _unwrapObject = unwrapObject({ oid: oid, file: file }), type = _unwrapObject.type, object = _unwrapObject.object;
                return _context.abrupt('return', { type: type, object: object });

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

      function read$$1(_x) {
        return _ref4.apply(this, arguments);
      }

      return read$$1;
    }()
  }, {
    key: 'hash',
    value: function () {
      var _ref6 = _asyncToGenerator(_regeneratorRuntime.mark(function _callee2(_ref5) {
        var gitdir = _ref5.gitdir,
            type = _ref5.type,
            object = _ref5.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 _ref6.apply(this, arguments);
      }

      return hash;
    }()
  }, {
    key: 'write',
    value: function () {
      var _ref8 = _asyncToGenerator(_regeneratorRuntime.mark(function _callee3(_ref7) {
        var gitdir = _ref7.gitdir,
            type = _ref7.type,
            object = _ref7.object;

        var _wrapObject, file, oid, filepath;

        return _regeneratorRuntime.wrap(function _callee3$(_context3) {
          while (1) {
            switch (_context3.prev = _context3.next) {
              case 0:
                _wrapObject = wrapObject({ type: type, object: object }), file = _wrapObject.file, oid = _wrapObject.oid;
                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 = 4;
                return utils_js.exists(filepath);

              case 4:
                if (_context3.sent) {
                  _context3.next = 7;
                  break;
                }

                _context3.next = 7;
                return utils_js.write(filepath, file);

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

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

      function write$$1(_x3) {
        return _ref8.apply(this, arguments);
      }

      return write$$1;
    }() /*: {gitdir: string, type: string, object: Buffer} */

  }]);

  return GitObjectManager;
}();

// @flow
// This is a convenience wrapper for reading and writing files in the 'refs' directory.
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(_regeneratorRuntime.mark(function _callee(_ref) {
        var gitdir = _ref.gitdir,
            remote = _ref.remote,
            refs = _ref.refs,
            symrefs = _ref.symrefs;

        var _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, _ref3, _ref4, key, value, actualRefsToWrite, _iteratorNormalCompletion2, _didIteratorError2, _iteratorError2, _iterator2, _step2, _ref5, _ref6, _key, _value, _iteratorNormalCompletion3, _didIteratorError3, _iteratorError3, _iterator3, _step3, _ref7, _ref8, _key2, _value2, branch, normalizeValue, _iteratorNormalCompletion4, _didIteratorError4, _iteratorError4, _iterator4, _step4, _ref9, _ref10, _key3, _value3;

        return _regeneratorRuntime.wrap(function _callee$(_context) {
          while (1) {
            switch (_context.prev = _context.next) {
              case 0:
                // Validate input
                _iteratorNormalCompletion = true;
                _didIteratorError = false;
                _iteratorError = undefined;
                _context.prev = 3;
                _iterator = _getIterator(refs);

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

                _ref3 = _step.value;
                _ref4 = _slicedToArray(_ref3, 2);
                
                value = _ref4[1];

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

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

              case 12:
                _iteratorNormalCompletion = true;
                _context.next = 5;
                break;

              case 15:
                _context.next = 21;
                break;

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

              case 21:
                _context.prev = 21;
                _context.prev = 22;

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

              case 24:
                _context.prev = 24;

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

                throw _iteratorError;

              case 27:
                return _context.finish(24);

              case 28:
                return _context.finish(21);

              case 29:
                // Combine refs and symrefs giving symrefs priority
                actualRefsToWrite = new _Map();
                _iteratorNormalCompletion2 = true;
                _didIteratorError2 = false;
                _iteratorError2 = undefined;
                _context.prev = 33;

                for (_iterator2 = _getIterator(refs); !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
                  _ref5 = _step2.value;
                  _ref6 = _slicedToArray(_ref5, 2);
                  _key = _ref6[0];
                  _value = _ref6[1];
                  actualRefsToWrite.set(_key, _value);
                }_context.next = 41;
                break;

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

              case 41:
                _context.prev = 41;
                _context.prev = 42;

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

              case 44:
                _context.prev = 44;

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

                throw _iteratorError2;

              case 47:
                return _context.finish(44);

              case 48:
                return _context.finish(41);

              case 49:
                _iteratorNormalCompletion3 = true;
                _didIteratorError3 = false;
                _iteratorError3 = undefined;
                _context.prev = 52;
                for (_iterator3 = _getIterator(symrefs); !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
                  _ref7 = _step3.value;
                  _ref8 = _slicedToArray(_ref7, 2);
                  _key2 = _ref8[0];
                  _value2 = _ref8[1];
                  branch = _value2.replace(/^refs\/heads\//, '');

                  actualRefsToWrite.set(_key2, 'ref: refs/remotes/' + remote + '/' + branch);
                }
                // Update files
                _context.next = 60;
                break;

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

              case 60:
                _context.prev = 60;
                _context.prev = 61;

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

              case 63:
                _context.prev = 63;

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

                throw _iteratorError3;

              case 66:
                return _context.finish(63);

              case 67:
                return _context.finish(60);

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

                _iteratorNormalCompletion4 = true;
                _didIteratorError4 = false;
                _iteratorError4 = undefined;
                _context.prev = 72;
                _iterator4 = _getIterator(actualRefsToWrite);

              case 74:
                if (_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done) {
                  _context.next = 86;
                  break;
                }

                _ref9 = _step4.value;
                _ref10 = _slicedToArray(_ref9, 2);
                _key3 = _ref10[0];
                _value3 = _ref10[1];

                // For some reason we trim these
                _key3 = _key3.replace(/^refs\/heads\//, '');
                _key3 = _key3.replace(/^refs\/tags\//, '');
                _context.next = 83;
                return utils_js.write(path.join(gitdir, 'refs', 'remotes', remote, _key3), normalizeValue(_value3), 'utf8');

              case 83:
                _iteratorNormalCompletion4 = true;
                _context.next = 74;
                break;

              case 86:
                _context.next = 92;
                break;

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

              case 92:
                _context.prev = 92;
                _context.prev = 93;

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

              case 95:
                _context.prev = 95;

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

                throw _iteratorError4;

              case 98:
                return _context.finish(95);

              case 99:
                return _context.finish(92);

              case 100:
              case 'end':
                return _context.stop();
            }
          }
        }, _callee, this, [[3, 17, 21, 29], [22,, 24, 28], [33, 37, 41, 49], [42,, 44, 48], [52, 56, 60, 68], [61,, 63, 67], [72, 88, 92, 100], [93,, 95, 99]]);
      }));

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

      return updateRemoteRefs;
    }()
  }, {
    key: 'resolve',
    value: function () {
      var _ref12 = _asyncToGenerator(_regeneratorRuntime.mark(function _callee2(_ref11) {
        var gitdir = _ref11.gitdir,
            ref = _ref11.ref,
            depth = _ref11.depth;
        var sha, text, candidates;
        return _regeneratorRuntime.wrap(function _callee2$(_context2) {
          while (1) {
            switch (_context2.prev = _context2.next) {
              case 0:
                if (!(depth !== undefined)) {
                  _context2.next = 4;
                  break;
                }

                depth--;

                if (!(depth === -1)) {
                  _context2.next = 4;
                  break;
                }

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

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

                if (!ref.startsWith('ref: ')) {
                  _context2.next = 8;
                  break;
                }

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

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

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

              case 10:
                if (!(ref === 'HEAD' || ref === 'MERGE_HEAD')) {
                  _context2.next = 16;
                  break;
                }

                _context2.next = 13;
                return utils_js.read(gitdir + '/' + ref, { encoding: 'utf8' });

              case 13:
                sha = _context2.sent;

                if (!sha) {
                  _context2.next = 16;
                  break;
                }

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

              case 16:
                if (!ref.startsWith('refs/')) {
                  _context2.next = 22;
                  break;
                }

                _context2.next = 19;
                return utils_js.read(gitdir + '/' + ref, { encoding: 'utf8' });

              case 19:
                sha = _context2.sent;

                if (!sha) {
                  _context2.next = 22;
                  break;
                }

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

              case 22:
                _context2.next = 24;
                return utils_js.read(gitdir + '/refs/heads/' + ref, { encoding: 'utf8' });

              case 24:
                sha = _context2.sent;

                if (!sha) {
                  _context2.next = 27;
                  break;
                }

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

              case 27:
                _context2.next = 29;
                return utils_js.read(gitdir + '/refs/tags/' + ref, { encoding: 'utf8' });

              case 29:
                sha = _context2.sent;

                if (!sha) {
                  _context2.next = 32;
                  break;
                }

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

              case 32:
                _context2.next = 34;
                return utils_js.read(gitdir + '/refs/remotes/' + ref, { encoding: 'utf8' });

              case 34:
                sha = _context2.sent;

                if (!sha) {
                  _context2.next = 37;
                  break;
                }

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

              case 37:
                _context2.next = 39;
                return utils_js.read(gitdir + '/packed-refs', { encoding: 'utf8' });

              case 39:
                text = _context2.sent;

                if (!(text && text.includes(ref))) {
                  _context2.next = 49;
                  break;
                }

                candidates = text.trim().split('\n').filter(function (x) {
                  return x.endsWith(ref);
                }).filter(function (x) {
                  return !x.startsWith('#');
                });

                if (!(candidates.length > 1)) {
                  _context2.next = 46;
                  break;
                }

                throw new Error('Could not resolve ambiguous reference ' + ref);

              case 46:
                if (!(candidates.length === 1)) {
                  _context2.next = 49;
                  break;
                }

                sha = candidates[0].split(' ')[0];
                return _context2.abrupt('return', GitRefManager.resolve({ gitdir: gitdir, ref: sha.trim(), depth: depth }));

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

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

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

      return resolve;
    }()
  }]);

  return GitRefManager;
}();

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

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(_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(_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(_regeneratorRuntime.mark(function _callee3(service /*: string */) {
        var _this = this;

        var headers, res, data, read$$1, 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('Bad status code from server: ' + res.statusCode);

              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$$1 = models_js.GitPktLine.reader(data);
                lineOne = read$$1();
                // skip past any flushes

                while (lineOne === null) {
                  lineOne = read$$1();
                }
                if (!(lineOne === true)) {
                  _context3.next = 18;
                  break;
                }

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

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

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

              case 20:
                lineTwo = read$$1();
                // skip past any flushes

                while (lineTwo === null) {
                  lineTwo = read$$1();
                } // In the edge case of a brand new repo, zero refs (and zero capabilities)
                // are returned.

                if (!(lineTwo === true)) {
                  _context3.next = 24;
                  break;
                }

                return _context3.abrupt('return');

              case 24:
                _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 28:
                

                line = read$$1();

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

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

              case 32:
                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 = 28;
                break;

              case 35:
                // Symrefs are thrown into the "capabilities" unfortunately.
                _iteratorNormalCompletion = true;
                _didIteratorError = false;
                _iteratorError = undefined;
                _context3.prev = 38;
                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 = 46;
                break;

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

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

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

              case 49:
                _context3.prev = 49;

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

                throw _iteratorError;

              case 52:
                return _context3.finish(49);

              case 53:
                return _context3.finish(46);

              case 54:
              case 'end':
                return _context3.stop();
            }
          }
        }, _callee3, this, [[38, 42, 46, 54], [47,, 49, 53]]);
      }));

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

      return discover;
    }()
  }, {
    key: 'push',
    value: function () {
      var _ref5 = _asyncToGenerator(_regeneratorRuntime.mark(function _callee4(stream$$1 /*: ReadableStream */) {
        var service, res;
        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:
                res = _context4.sent;
                return _context4.abrupt('return', res);

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

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

      return push;
    }()
  }, {
    key: 'pull',
    value: function () {
      var _ref6 = _asyncToGenerator(_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 _ref6.apply(this, arguments);
      }

      return pull;
    }()
  }, {
    key: 'stream',
    value: function () {
      var _ref8 = _asyncToGenerator(_regeneratorRuntime.mark(function _callee7(_ref7) {
        var _stream = _ref7.stream,
            service = _ref7.service;
        var headers, res, read$$1, 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 (!(service === 'git-receive-pack')) {
                  _context7.next = 10;
                  break;
                }

                return _context7.abrupt('return', res);

              case 10:
                // Parse the response!
                read$$1 = 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 _ref9 = _asyncToGenerator(_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$$1();

                          case 2:
                            line = _context6.sent;

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

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

                          case 8:
                            _context6.t0 = line[0];
                            _context6.next = _context6.t0 === 1 ? 11 : _context6.t0 === 2 ? 13 : _context6.t0 === 3 ? 15 : 19;
                            break;

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

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

                          case 15:
                            // 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 19:
                            // Not part of the side-band-64k protocol
                            packetlines.write(line.slice(0));

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

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

                  return function nextBit() {
                    return _ref9.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 _ref8.apply(this, arguments);
      }

      return stream$$1;
    }() /*: {
        stream: ReadableStream,
        service: string
        } */

  }]);

  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
