<commit_before>// The offline complement to fetch-remote-git
// Based on an extract from cache/add-remote-git.js

var fs = require('graceful-fs')
var path = require('path')
var url = require('url')

var chownr = require('chownr')
var hostedFromURL = require('hosted-git-info').fromUrl
var inflight = require('inflight')
var log = require('npmlog')
var mkdir = require('mkdirp')
var realizePackageSpecifier = require('realize-package-specifier')
var fstr = require('fstream')
var rm_rf = require('rimraf')

var git = require('./utils/git.js')
var gitAux = require('./download/git-aux.js')
var npm = require('./npm.js')
var addLocal = require('./cache/add-local.js')
var tempFilename = require('./utils/temp-filename.js')

module.exports = add
function add (gitData, next, done) {
  // gitData contains fields {from, cloneURL, repoID, treeish, resolvedTreeish}.
  // 'next' is a function factory that takes a callback, perhaps 'done', and
  // returns function(er, cb) that eventually leads to a call of that callback.
  // 'done' is intended to be the penultimate callback.
  //
  done = inflight(gitData.repoID, done)
  if (!done) {
    return log.verbose('gitOffline', gitData.repoID, 'already in flight; waiting')
  }
  log.verbose('tryClone', gitData.repoID, 'not in flight; caching')

  gitAux.getGitDir(npm.cache, function (er, cs) {
    if (er) {
      log.error('gitOffline', gitData.from, 'could not get cache stat')
      return done(er)
    }

    var cachedRemote = path.join(npm.cache, gitAux.remotesDirName(), gitData.repoID)
    fs.stat(cachedRemote, function (er, s) {
      if (er || !s.isDirectory())
        return copyRemote(gitData, cachedRemote, cs, next, done)

      validateExistingRemote(gitData, cachedRemote, cs, next, done)
    })
  })
}

function copyRemote (gitData, cachedRemote, cStats, next, done) {
  log.info('copyRemote', 'copying to', cachedRemote, 'for', gitData.from)

  rm_rf(cachedRemote, { disableGlob: true }, function (rmErr) {
    if (rmErr) return done(rmErr) // No error if cachedRemote does not exist

    var remotesDirName = gitAux.remotesDirName()
    // Source:
    var clonedRemote = path.join(npm.dlTracker.path, remotesDirName, gitData.repoID)
    fstr.Reader(clonedRemote)
      .on('error', function(readErr) {
        log.error('copyRemote', 'fstream.Reader(' + clonedRemote + ')')
        done(readErr)
      })
      .pipe(fstr.Writer({ path: cachedRemote, type: 'Directory' })
        .on('error', function(copyErr) { // Writer error
          log.error('copyRemote', 'fstream.Writer(' + cachedRemote + ')')
          done(copyErr)
        })
        .on('close', function() { // Writer close
          log.verbose('copyRemote', 'cached', gitData.repoID)
          setPermissions(gitData, cachedRemote, cStats, next, done)
        })
      )
  })
}

// reuse a cached remote when possible, but nuke it if it's in an
// inconsistent state
function validateExistingRemote (gitData, cachedRemote, cStats, next, done) {
  git.whichAndExec(
    ['config', '--get', 'remote.origin.url'],
    { cwd: cachedRemote, env: gitAux.gitEnv() },
    function (er, stdout, stderr) {
      var originURL
      if (stdout) {
        originURL = stdout.trim()
        log.silly('validateExistingRemote', gitData.from, 'remote.origin.url:', originURL)
      }

      if (stderr) stderr = stderr.trim()
      if (stderr || er) {
        log.warn('gitOffline', gitData.from, 'overwriting cached repo',
                 cachedRemote, 'because of error:', stderr || er)
        return copyRemote(gitData, cachedRemote, next, done)
      } else if (gitData.cloneURL !== originURL) {
        log.warn(
          'gitOffline',
          gitData.from,
          'overwriting cached repo', cachedRemote, 'because it points to', originURL,
          'and not', gitData.cloneURL
        )
        return copyRemote(gitData, cachedRemote, cStats, next, done)
      }

      log.verbose('validateExistingRemote', from, 'is cloning existing cached remote', cachedRemote)
      cloneCachedRemote(gitData, cachedRemote, next, done)
    }
  )
}

function setPermissions (gitData, cachedRemote, cs, next, done) {
  if (process.platform === 'win32') {
    log.verbose('setPermissions', gitData.from, 'skipping chownr on Windows')
    resolveHead(gitData, cachedRemote, next, done)
  }
  else {
    chownr(cachedRemote, cs.uid, cs.gid, function (er) {
      if (er) {
        log.error(
          'setPermissions',<commit_msg>Fix issue #2: qualify 'from'<commit_after>// The offline complement to fetch-remote-git
// Based on an extract from cache/add-remote-git.js

var fs = require('graceful-fs')
var path = require('path')
var url = require('url')

var chownr = require('chownr')
var hostedFromURL = require('hosted-git-info').fromUrl
var inflight = require('inflight')
var log = require('npmlog')
var mkdir = require('mkdirp')
var realizePackageSpecifier = require('realize-package-specifier')
var fstr = require('fstream')
var rm_rf = require('rimraf')

var git = require('./utils/git.js')
var gitAux = require('./download/git-aux.js')
var npm = require('./npm.js')
var addLocal = require('./cache/add-local.js')
var tempFilename = require('./utils/temp-filename.js')

module.exports = add
function add (gitData, next, done) {
  // gitData contains fields {from, cloneURL, repoID, treeish, resolvedTreeish}.
  // 'next' is a function factory that takes a callback, perhaps 'done', and
  // returns function(er, cb) that eventually leads to a call of that callback.
  // 'done' is intended to be the penultimate callback.
  //
  done = inflight(gitData.repoID, done)
  if (!done) {
    return log.verbose('gitOffline', gitData.repoID, 'already in flight; waiting')
  }
  log.verbose('tryClone', gitData.repoID, 'not in flight; caching')

  gitAux.getGitDir(npm.cache, function (er, cs) {
    if (er) {
      log.error('gitOffline', gitData.from, 'could not get cache stat')
      return done(er)
    }

    var cachedRemote = path.join(npm.cache, gitAux.remotesDirName(), gitData.repoID)
    fs.stat(cachedRemote, function (er, s) {
      if (er || !s.isDirectory())
        return copyRemote(gitData, cachedRemote, cs, next, done)

      validateExistingRemote(gitData, cachedRemote, cs, next, done)
    })
  })
}

function copyRemote (gitData, cachedRemote, cStats, next, done) {
  log.info('copyRemote', 'copying to', cachedRemote, 'for', gitData.from)

  rm_rf(cachedRemote, { disableGlob: true }, function (rmErr) {
    if (rmErr) return done(rmErr) // No error if cachedRemote does not exist

    var remotesDirName = gitAux.remotesDirName()
    // Source:
    var clonedRemote = path.join(npm.dlTracker.path, remotesDirName, gitData.repoID)
    fstr.Reader(clonedRemote)
      .on('error', function(readErr) {
        log.error('copyRemote', 'fstream.Reader(' + clonedRemote + ')')
        done(readErr)
      })
      .pipe(fstr.Writer({ path: cachedRemote, type: 'Directory' })
        .on('error', function(copyErr) { // Writer error
          log.error('copyRemote', 'fstream.Writer(' + cachedRemote + ')')
          done(copyErr)
        })
        .on('close', function() { // Writer close
          log.verbose('copyRemote', 'cached', gitData.repoID)
          setPermissions(gitData, cachedRemote, cStats, next, done)
        })
      )
  })
}

// reuse a cached remote when possible, but nuke it if it's in an
// inconsistent state
function validateExistingRemote (gitData, cachedRemote, cStats, next, done) {
  git.whichAndExec(
    ['config', '--get', 'remote.origin.url'],
    { cwd: cachedRemote, env: gitAux.gitEnv() },
    function (er, stdout, stderr) {
      var originURL
      if (stdout) {
        originURL = stdout.trim()
        log.silly('validateExistingRemote', gitData.from, 'remote.origin.url:', originURL)
      }

      if (stderr) stderr = stderr.trim()
      if (stderr || er) {
        log.warn('gitOffline', gitData.from, 'overwriting cached repo',
                 cachedRemote, 'because of error:', stderr || er)
        return copyRemote(gitData, cachedRemote, next, done)
      } else if (gitData.cloneURL !== originURL) {
        log.warn(
          'gitOffline',
          gitData.from,
          'overwriting cached repo', cachedRemote, 'because it points to', originURL,
          'and not', gitData.cloneURL
        )
        return copyRemote(gitData, cachedRemote, cStats, next, done)
      }

      log.verbose('validateExistingRemote', gitData.from, 'is cloning existing cached remote', cachedRemote)
      cloneCachedRemote(gitData, cachedRemote, next, done)
    }
  )
}

function setPermissions (gitData, cachedRemote, cs, next, done) {
  if (process.platform === 'win32') {
    log.verbose('setPermissions', gitData.from, 'skipping chownr on Windows')
    resolveHead(gitData, cachedRemote, next, done)
  }
  else {
    chownr(cachedRemote, cs.uid, cs.gid, function (er) {
      if (er) {
        log.error(
          'setPermissions',
{'fraction_non_alphanumeric': 0.13849949534596837, 'fraction_numerical': 0.00056072670180554, 'mean_word_length': 3.2833813640730067, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2052290', 'n_tokens_mistral': 2808, 'n_tokens_neox': 2767, 'n_words': 737}