#!/usr/bin/env node

'use strict';

const childProcess = require('child_process');
const Promise = require('bluebird');
const DEFAULT_BRANCH_NAME = 'master';

/**
 * Calls `git` with the given arguments from the CWD
 * @param {string[]} args A list of arguments
 * @param {string} [stdin] The stdin for the process
 * @returns {Promise<string>} The output from `git`
 */
function git (args, stdin) {
  return new Promise((resolve, reject) => {
    const child = childProcess.spawn('git', ['--no-pager'].concat(args));

    if (typeof stdin === 'string') {
      child.stdin.write(stdin);
      child.stdin.end();
    }

    let stdout = '';
    let stderr = '';

    child.stdout.on('data', data => stdout += data);
    child.stderr.on('data', data => stderr += data);

    child.on('close', exitCode => exitCode ? reject(stderr) : resolve(stdout));
  }).then(stdoutBuffer => stdoutBuffer.toString().replace(/\n$/, ''));
}

const commitCache = new Map();
/**
 * Gets the patch id of a single commit
 * @param {string} hash The commit hash
 * @returns {Promise<string>} The diff of the commit with the given hash
 */
function getCommitDiffId (hash) {
  if (!commitCache.has(hash)) {
    commitCache.set(hash, git(['diff', `${hash}^`, hash]).then(diff => git(['patch-id'], diff)));
  }
  return commitCache.get(hash);
}

git(['for-each-ref', 'refs/heads/', '--format=%(refname:short)'])
  .then(branchListOutput => branchListOutput.split('\n'))
  .tap(branchNames => {
    if (branchNames.indexOf(DEFAULT_BRANCH_NAME) === -1) {
      throw `fatal: no branch named '${DEFAULT_BRANCH_NAME}' found in this repo`;
    }
  }).filter(branchName =>
    // Get the common ancestor with the branch and master
    branchName !== DEFAULT_BRANCH_NAME && git(['merge-base', DEFAULT_BRANCH_NAME, branchName]).then(commonAncestorHash =>
      // Get the diff between the common ancestor and the branch tip
      git(['diff', `${commonAncestorHash}...${branchName}`]).then(diff => git(['patch-id'], diff)).then(branchPatchId =>
        // Iterate through all the commits to master since the ancestor
        git(['log', '--format=%H', `${commonAncestorHash}...${DEFAULT_BRANCH_NAME}`])
          .then(logOutput => logOutput ? logOutput.split('\n') : [])
          .map(getCommitDiffId)
          // If the patch for any commit to master since the ancestor is the same as the patch between the ancestor
          // and the branch tip, the branch can be deleted.
          .then(results => results.some(commitPatchId => commitPatchId === branchPatchId))
      )
    )
  )
  .tap(branchNamesToDelete => branchNamesToDelete.length && git(['checkout', DEFAULT_BRANCH_NAME]))
  .mapSeries(branchName => git(['branch', '-D', branchName]))
  .mapSeries(stdout => console.log(stdout))
  .catch(err => console.error(err.cause || err));

@@ -859,22 +859,16 @@
 n(stdout
-Buffer
  =%3E stdo
@@ -873,25 +873,8 @@
 dout
-Buffer.toString()
 .rep

fix misleading variable name