From 75bce7a963baddab5341817b2192df9293ef2851 Mon Sep 17 00:00:00 2001
From: Tim Schaub <tim.schaub@gmail.com>
Date: Sat, 6 May 2017 07:58:47 -0600
Subject: [PATCH 1/2] Make code prettier

---
 bin/gh-pages          |  84 +++++++-------
 lib/git.js            |  88 +++++++--------
 lib/index.js          | 255 ++++++++++++++++++++++--------------------
 lib/util.js           |  22 ++--
 package.json          |   3 +-
 test/helper.js        |   2 -
 test/lib/util.spec.js |   6 -
 7 files changed, 233 insertions(+), 227 deletions(-)

diff --git a/bin/gh-pages b/bin/gh-pages
index 5277cb58..4128a646 100755
--- a/bin/gh-pages
+++ b/bin/gh-pages
@@ -6,48 +6,56 @@ var path = require('path');
 
 program
   .version(require('../package').version)
-  .option('-d, --dist <dist>',
-      'base directory for all source files')
-  .option('-s, --src <src>',
-      'pattern used to select which files should be published', '**/*')
-  .option('-r, --repo <repo>',
-      'URL of the repository you\'ll be pushing to')
+  .option('-d, --dist <dist>', 'base directory for all source files')
+  .option(
+    '-s, --src <src>',
+    'pattern used to select which files should be published',
+    '**/*'
+  )
+  .option('-r, --repo <repo>', "URL of the repository you'll be pushing to")
   .option('-x, --silent', 'Do not output the repository url')
-  .option('-b, --branch <branch>',
-      'name of the branch you\'ll be pushing to', 'gh-pages')
-  .option('-o, --remote <name>',
-      'The name of the remote', 'origin')
-  .option('-m, --message <message>',
-      'commit message', 'Updates')
-  .option('-g, --tag <tag>',
-      'add tag to commit')
+  .option(
+    '-b, --branch <branch>',
+    "name of the branch you'll be pushing to",
+    'gh-pages'
+  )
+  .option('-o, --remote <name>', 'The name of the remote', 'origin')
+  .option('-m, --message <message>', 'commit message', 'Updates')
+  .option('-g, --tag <tag>', 'add tag to commit')
   .option('-t, --dotfiles', 'Include dotfiles')
   .option('-a, --add', 'Only add, and never remove existing files.')
-  .option('-v, --remove <pattern>',
-      'Remove files that match the given pattern ' +
-      '(ignored if used together with --add).', '.')
+  .option(
+    '-v, --remove <pattern>',
+    'Remove files that match the given pattern ' +
+      '(ignored if used together with --add).',
+    '.'
+  )
   .option('-n, --no-push', 'Commit only (with no push)')
   .parse(process.argv);
 
-ghpages.publish(path.join(process.cwd(), program.dist), {
-  repo: program.repo,
-  silent: !!program.silent,
-  branch: program.branch,
-  src: program.src,
-  message: program.message,
-  tag: program.tag,
-  dotfiles: !!program.dotfiles,
-  add: !!program.add,
-  only: program.remove,
-  remote: program.remote,
-  push: !program.noPush,
-  logger: function(message) {
-    process.stderr.write(message + '\n');
+ghpages.publish(
+  path.join(process.cwd(), program.dist),
+  {
+    repo: program.repo,
+    silent: !!program.silent,
+    branch: program.branch,
+    src: program.src,
+    message: program.message,
+    tag: program.tag,
+    dotfiles: !!program.dotfiles,
+    add: !!program.add,
+    only: program.remove,
+    remote: program.remote,
+    push: !program.noPush,
+    logger: function(message) {
+      process.stderr.write(message + '\n');
+    }
+  },
+  function(err) {
+    if (err) {
+      process.stderr.write(err.message + '\n');
+      return process.exit(1);
+    }
+    process.stderr.write('Published\n');
   }
-}, function(err) {
-  if (err) {
-    process.stderr.write(err.message + '\n');
-    return process.exit(1);
-  }
-  process.stderr.write('Published\n');
-});
+);
diff --git a/lib/git.js b/lib/git.js
index 28857597..6cf5aa90 100644
--- a/lib/git.js
+++ b/lib/git.js
@@ -7,7 +7,6 @@ var fs = require('q-io/fs');
 
 var git = 'git';
 
-
 /**
  * @constructor
  * @param {number} code Error code.
@@ -23,7 +22,6 @@ function ProcessError(code, message) {
 }
 util.inherits(ProcessError, Error);
 
-
 /**
  * Util function for handling spawned processes as promises.
  * @param {string} exe Executable.
@@ -52,7 +50,6 @@ function spawn(exe, args, cwd) {
   return deferred.promise;
 }
 
-
 /**
  * Execute a git command.
  * @param {Array.<string>} args Arguments (e.g. ['remote', 'update']).
@@ -65,7 +62,6 @@ exports = module.exports = function(args, cwd) {
   return spawn(git, args, cwd);
 };
 
-
 /**
  * Set the Git executable to be used by exported methods (defaults to 'git').
  * @param {string} exe Git executable (full path if not already on path).
@@ -74,7 +70,6 @@ exports.exe = function(exe) {
   git = exe;
 };
 
-
 /**
  * Initialize repository.
  * @param {string} cwd Repository directory.
@@ -84,7 +79,6 @@ exports.init = function init(cwd) {
   return spawn(git, ['init'], cwd);
 };
 
-
 /**
  * Clone a repo into the given dir if it doesn't already exist.
  * @param {string} repo Repository URL.
@@ -99,7 +93,16 @@ exports.clone = function clone(repo, dir, branch, options) {
       return Q.resolve();
     } else {
       return fs.makeTree(path.dirname(path.resolve(dir))).then(function() {
-        var args = ['clone', repo, dir, '--branch', branch, '--single-branch', '--origin', options.remote];
+        var args = [
+          'clone',
+          repo,
+          dir,
+          '--branch',
+          branch,
+          '--single-branch',
+          '--origin',
+          options.remote
+        ];
         if (options.depth) {
           args.push('--depth', options.depth);
         }
@@ -112,16 +115,14 @@ exports.clone = function clone(repo, dir, branch, options) {
   });
 };
 
-
 /**
  * Clean up unversioned files.
  * @param {string} cwd Repository directory.
  * @return {Promise} A promise.
  */
-var clean = exports.clean = function clean(cwd) {
+var clean = (exports.clean = function clean(cwd) {
   return spawn(git, ['clean', '-f', '-d'], cwd);
-};
-
+});
 
 /**
  * Hard reset to remote/branch
@@ -130,10 +131,9 @@ var clean = exports.clean = function clean(cwd) {
  * @param {string} cwd Repository directory.
  * @return {Promise} A promise.
  */
-var reset = exports.reset = function reset(remote, branch, cwd) {
+var reset = (exports.reset = function reset(remote, branch, cwd) {
   return spawn(git, ['reset', '--hard', remote + '/' + branch], cwd);
-};
-
+});
 
 /**
  * Fetch from a remote.
@@ -145,7 +145,6 @@ exports.fetch = function fetch(remote, cwd) {
   return spawn(git, ['fetch', remote], cwd);
 };
 
-
 /**
  * Checkout a branch (create an orphan if it doesn't exist on the remote).
  * @param {string} remote Remote alias.
@@ -155,28 +154,29 @@ exports.fetch = function fetch(remote, cwd) {
  */
 exports.checkout = function checkout(remote, branch, cwd) {
   var treeish = remote + '/' + branch;
-  return spawn(git, ['ls-remote', '--exit-code', '.', treeish], cwd)
-      .then(function() {
-        // branch exists on remote, hard reset
-        return spawn(git, ['checkout', branch], cwd)
-            .then(function() {
-              return clean(cwd);
-            })
-            .then(function() {
-              return reset(remote, branch, cwd);
-            });
-      }, function(error) {
-        if (error instanceof ProcessError && error.code === 2) {
-          // branch doesn't exist, create an orphan
-          return spawn(git, ['checkout', '--orphan', branch], cwd);
-        } else {
-          // unhandled error
-          return Q.reject(error);
-        }
-      });
+  return spawn(git, ['ls-remote', '--exit-code', '.', treeish], cwd).then(
+    function() {
+      // branch exists on remote, hard reset
+      return spawn(git, ['checkout', branch], cwd)
+        .then(function() {
+          return clean(cwd);
+        })
+        .then(function() {
+          return reset(remote, branch, cwd);
+        });
+    },
+    function(error) {
+      if (error instanceof ProcessError && error.code === 2) {
+        // branch doesn't exist, create an orphan
+        return spawn(git, ['checkout', '--orphan', branch], cwd);
+      } else {
+        // unhandled error
+        return Q.reject(error);
+      }
+    }
+  );
 };
 
-
 /**
  * Remove all unversioned files.
  * @param {string} files Files argument.
@@ -187,7 +187,6 @@ exports.rm = function rm(files, cwd) {
   return spawn(git, ['rm', '--ignore-unmatch', '-r', '-f', files], cwd);
 };
 
-
 /**
  * Add files.
  * @param {string} files Files argument.
@@ -198,7 +197,6 @@ exports.add = function add(files, cwd) {
   return spawn(git, ['add', files], cwd);
 };
 
-
 /**
  * Commit.
  * @param {string} message Commit message.
@@ -207,16 +205,15 @@ exports.add = function add(files, cwd) {
  */
 exports.commit = function commit(message, cwd) {
   return spawn(git, ['diff-index', '--quiet', 'HEAD', '.'], cwd)
-      .then(function() {
-        // nothing to commit
-        return Q.resolve();
-      })
-      .fail(function() {
-        return spawn(git, ['commit', '-m', message], cwd);
-      });
+    .then(function() {
+      // nothing to commit
+      return Q.resolve();
+    })
+    .fail(function() {
+      return spawn(git, ['commit', '-m', message], cwd);
+    });
 };
 
-
 /**
  * Add tag
  * @param {string} name Name of tag.
@@ -227,7 +224,6 @@ exports.tag = function tag(name, cwd) {
   return spawn(git, ['tag', name], cwd);
 };
 
-
 /**
  * Push a branch.
  * @param {string} remote Remote alias.
diff --git a/lib/index.js b/lib/index.js
index cafc02e1..c8262bc7 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -16,23 +16,31 @@ function getCacheDir() {
 function getRemoteUrl(dir, remote) {
   var repo;
   return git(['config', '--get', 'remote.' + remote + '.url'], dir)
-      .progress(function(chunk) {
-        repo = String(chunk).split(/[\n\r]/).shift();
-      })
-      .then(function() {
-        if (repo) {
-          return Q.resolve(repo);
-        } else {
-          return Q.reject(new Error(
-              'Failed to get repo URL from options or current directory.'));
-        }
-      })
-      .fail(function(err) {
-        return Q.reject(new Error(
-            'Failed to get remote.' + remote + '.url (task must either be ' +
-            'run in a git repository with a configured ' + remote + ' remote ' +
-            'or must be configured with the "repo" option).'));
-      });
+    .progress(function(chunk) {
+      repo = String(chunk).split(/[\n\r]/).shift();
+    })
+    .then(function() {
+      if (repo) {
+        return Q.resolve(repo);
+      } else {
+        return Q.reject(
+          new Error('Failed to get repo URL from options or current directory.')
+        );
+      }
+    })
+    .fail(function(err) {
+      return Q.reject(
+        new Error(
+          'Failed to get remote.' +
+            remote +
+            '.url (task must either be ' +
+            'run in a git repository with a configured ' +
+            remote +
+            ' remote ' +
+            'or must be configured with the "repo" option).'
+        )
+      );
+    });
 }
 
 function getRepo(options) {
@@ -43,7 +51,6 @@ function getRepo(options) {
   }
 }
 
-
 /**
  * Push a git branch to a remote (pushes gh-pages by default).
  * @param {string} basePath The base path.
@@ -113,12 +120,14 @@ exports.publish = function publish(basePath, config, callback) {
     return;
   }
 
-  var files = globby.sync(options.src, {
-    cwd: basePath,
-    dot: options.dotfiles
-  }).filter(function(file) {
-    return !fs.statSync(path.join(basePath, file)).isDirectory();
-  });
+  var files = globby
+    .sync(options.src, {
+      cwd: basePath,
+      dot: options.dotfiles
+    })
+    .filter(function(file) {
+      return !fs.statSync(path.join(basePath, file)).isDirectory();
+    });
 
   if (!Array.isArray(files) || files.length === 0) {
     done(new Error('Files must be provided in the "src" property.'));
@@ -131,112 +140,118 @@ exports.publish = function publish(basePath, config, callback) {
 
   var repoUrl;
   getRepo(options)
-      .then(function(repo) {
-        repoUrl = repo;
-        log('Cloning ' + repo + ' into ' + options.clone);
-        return git.clone(repo, options.clone, options.branch, options);
-      })
-      .then(function() {
-        return getRemoteUrl(options.clone, options.remote)
-            .then(function(url) {
-              if (url !== repoUrl) {
-                var message = 'Remote url mismatch.  Got "' + url + '" ' +
-                    'but expected "' + repoUrl + '" in ' + options.clone +
-                    '.  If you have changed your "repo" option, try ' +
-                    'running the "clean" task first.';
-                return Q.reject(new Error(message));
-              } else {
-                return Q.resolve();
-              }
-            });
-      })
-      .then(function() {
-        // only required if someone mucks with the checkout between builds
-        log('Cleaning');
-        return git.clean(options.clone);
-      })
-      .then(function() {
-        log('Fetching ' + options.remote);
-        return git.fetch(options.remote, options.clone);
-      })
-      .then(function() {
-        log('Checking out ' + options.remote + '/' +
-            options.branch);
-        return git.checkout(options.remote, options.branch,
-            options.clone);
-      })
-      .then(function() {
-        if (!options.add) {
-          log('Removing files');
-          return git.rm(only.join(' '), options.clone);
-        } else {
-          return Q.resolve();
-        }
-      })
-      .then(function() {
-        log('Copying files');
-        return copy(files, basePath, options.clone);
-      })
-      .then(function() {
-        log('Adding all');
-        return git.add('.', options.clone);
-      })
-      .then(function() {
-        if (options.user) {
-          return git(['config', 'user.email', options.user.email],
-              options.clone)
-              .then(function() {
-                return git(['config', 'user.name', options.user.name],
-                    options.clone);
-              });
-        } else {
-          return Q.resolve();
-        }
-      })
-      .then(function() {
-        log('Committing');
-        return git.commit(options.message, options.clone);
-      })
-      .then(function() {
-        if (options.tag) {
-          log('Tagging');
-          var deferred = Q.defer();
-          git.tag(options.tag, options.clone)
-            .then(function() {
-              return deferred.resolve();
-            })
-            .fail(function(error) {
-              // tagging failed probably because this tag alredy exists
-              log('Tagging failed, continuing');
-              options.logger(error);
-              return deferred.resolve();
-            });
-          return deferred.promise;
+    .then(function(repo) {
+      repoUrl = repo;
+      log('Cloning ' + repo + ' into ' + options.clone);
+      return git.clone(repo, options.clone, options.branch, options);
+    })
+    .then(function() {
+      return getRemoteUrl(options.clone, options.remote).then(function(url) {
+        if (url !== repoUrl) {
+          var message =
+            'Remote url mismatch.  Got "' +
+            url +
+            '" ' +
+            'but expected "' +
+            repoUrl +
+            '" in ' +
+            options.clone +
+            '.  If you have changed your "repo" option, try ' +
+            'running the "clean" task first.';
+          return Q.reject(new Error(message));
         } else {
           return Q.resolve();
         }
-      })
-      .then(function() {
-        if (options.push) {
-          log('Pushing');
-          return git.push(options.remote, options.branch,
-              options.clone);
-        } else {
-          return Q.resolve();
-        }
-      })
-      .then(function() {
+      });
+    })
+    .then(function() {
+      // only required if someone mucks with the checkout between builds
+      log('Cleaning');
+      return git.clean(options.clone);
+    })
+    .then(function() {
+      log('Fetching ' + options.remote);
+      return git.fetch(options.remote, options.clone);
+    })
+    .then(function() {
+      log('Checking out ' + options.remote + '/' + options.branch);
+      return git.checkout(options.remote, options.branch, options.clone);
+    })
+    .then(function() {
+      if (!options.add) {
+        log('Removing files');
+        return git.rm(only.join(' '), options.clone);
+      } else {
+        return Q.resolve();
+      }
+    })
+    .then(function() {
+      log('Copying files');
+      return copy(files, basePath, options.clone);
+    })
+    .then(function() {
+      log('Adding all');
+      return git.add('.', options.clone);
+    })
+    .then(function() {
+      if (options.user) {
+        return git(
+          ['config', 'user.email', options.user.email],
+          options.clone
+        ).then(function() {
+          return git(['config', 'user.name', options.user.name], options.clone);
+        });
+      } else {
+        return Q.resolve();
+      }
+    })
+    .then(function() {
+      log('Committing');
+      return git.commit(options.message, options.clone);
+    })
+    .then(function() {
+      if (options.tag) {
+        log('Tagging');
+        var deferred = Q.defer();
+        git
+          .tag(options.tag, options.clone)
+          .then(function() {
+            return deferred.resolve();
+          })
+          .fail(function(error) {
+            // tagging failed probably because this tag alredy exists
+            log('Tagging failed, continuing');
+            options.logger(error);
+            return deferred.resolve();
+          });
+        return deferred.promise;
+      } else {
+        return Q.resolve();
+      }
+    })
+    .then(function() {
+      if (options.push) {
+        log('Pushing');
+        return git.push(options.remote, options.branch, options.clone);
+      } else {
+        return Q.resolve();
+      }
+    })
+    .then(
+      function() {
         done();
-      }, function(error) {
+      },
+      function(error) {
         if (options.silent) {
           error = new Error(
-              'Unspecified error (run without silent option for detail)');
+            'Unspecified error (run without silent option for detail)'
+          );
         }
         done(error);
-      });
+      }
+    );
 };
 
-
 /**
  * Clean the cache directory.
  */
diff --git a/lib/util.js b/lib/util.js
index f7180458..ed919edc 100644
--- a/lib/util.js
+++ b/lib/util.js
@@ -4,13 +4,12 @@ var async = require('async');
 var fs = require('graceful-fs');
 var Q = require('q');
 
-
 /**
  * Generate a list of unique directory paths given a list of file paths.
  * @param {Array.<string>} files List of file paths.
  * @return {Array.<string>} List of directory paths.
  */
-var uniqueDirs = exports.uniqueDirs = function(files) {
+var uniqueDirs = (exports.uniqueDirs = function(files) {
   var dirs = {};
   files.forEach(function(filepath) {
     var parts = path.dirname(filepath).split(path.sep);
@@ -22,8 +21,7 @@ var uniqueDirs = exports.uniqueDirs = function(files) {
     }
   });
   return Object.keys(dirs);
-};
-
+});
 
 /**
  * Sort function for paths.  Sorter paths come first.  Paths of equal length are
@@ -32,7 +30,7 @@ var uniqueDirs = exports.uniqueDirs = function(files) {
  * @param {string} b Second path.
  * @return {number} Comparison.
  */
-var byShortPath = exports.byShortPath = function(a, b) {
+var byShortPath = (exports.byShortPath = function(a, b) {
   var aParts = a.split(path.sep);
   var bParts = b.split(path.sep);
   var aLength = aParts.length;
@@ -57,25 +55,23 @@ var byShortPath = exports.byShortPath = function(a, b) {
     }
   }
   return cmp;
-};
-
+});
 
 /**
  * Generate a list of directories to create given a list of file paths.
  * @param {Array.<string>} files List of file paths.
  * @return {Array.<string>} List of directory paths ordered by path length.
  */
-var dirsToCreate = exports.dirsToCreate = function(files) {
+var dirsToCreate = (exports.dirsToCreate = function(files) {
   return uniqueDirs(files).sort(byShortPath);
-};
-
+});
 
 /**
  * Copy a file.
  * @param {Object} obj Object with src and dest properties.
  * @param {function(Error)} callback Callback
  */
-var copyFile = exports.copyFile = function(obj, callback) {
+var copyFile = (exports.copyFile = function(obj, callback) {
   var called = false;
   function done(err) {
     if (!called) {
@@ -98,8 +94,7 @@ var copyFile = exports.copyFile = function(obj, callback) {
   });
 
   read.pipe(write);
-};
-
+});
 
 /**
  * Make directory, ignoring errors if directory already exists.
@@ -123,7 +118,6 @@ function makeDir(path, callback) {
   });
 }
 
-
 /**
  * Copy a list of files.
  * @param {Array.<string>} files Files to copy.
diff --git a/package.json b/package.json
index 0884d511..a9f9e1db 100644
--- a/package.json
+++ b/package.json
@@ -22,6 +22,7 @@
   },
   "main": "lib/index.js",
   "scripts": {
+    "fix-lint": "eslint --fix lib test bin/gh-pages",
     "pretest": "eslint lib test bin/gh-pages",
     "test": "mocha --recursive test"
   },
@@ -37,7 +38,7 @@
   "devDependencies": {
     "chai": "^3.5.0",
     "eslint": "^3.10.2",
-    "eslint-config-tschaub": "^6.0.0",
+    "eslint-config-tschaub": "^7.0.0",
     "mocha": "^3.1.2"
   },
   "bin": {
diff --git a/test/helper.js b/test/helper.js
index de6adf3d..d5011ceb 100644
--- a/test/helper.js
+++ b/test/helper.js
@@ -1,10 +1,8 @@
 var chai = require('chai');
 
-
 /** @type {boolean} */
 chai.config.includeStack = true;
 
-
 /**
  * Chai's assert function configured to include stacks on failure.
  * @type {function}
diff --git a/test/lib/util.spec.js b/test/lib/util.spec.js
index f541c376..aa609e39 100644
--- a/test/lib/util.spec.js
+++ b/test/lib/util.spec.js
@@ -6,7 +6,6 @@ var assert = require('../helper').assert;
 var util = require('../../lib/util');
 
 describe('util', function() {
-
   var files;
   beforeEach(function() {
     files = [
@@ -49,7 +48,6 @@ describe('util', function() {
   });
 
   describe('uniqueDirs', function() {
-
     it('gets a list of unique directory paths', function() {
       // not comparing order here, so we sort both
       var got = util.uniqueDirs(files).sort();
@@ -69,11 +67,9 @@ describe('util', function() {
 
       assert.deepEqual(got, expected);
     });
-
   });
 
   describe('dirsToCreate', function() {
-
     it('gets a sorted list of directories to create', function() {
       var got = util.dirsToCreate(files);
 
@@ -92,7 +88,5 @@ describe('util', function() {
 
       assert.deepEqual(got, expected);
     });
-
   });
-
 });

From 0a77b03eaf66087ed1468002e4b644e86e9d079c Mon Sep 17 00:00:00 2001
From: Tim Schaub <tim.schaub@gmail.com>
Date: Sat, 6 May 2017 08:33:02 -0600
Subject: [PATCH 2/2] Remove q and q-io

---
 lib/git.js   | 64 +++++++++++++++++++-----------------------
 lib/index.js | 79 +++++++++++++++++-----------------------------------
 lib/util.js  | 45 ++++++++++++++----------------
 package.json |  3 +-
 4 files changed, 77 insertions(+), 114 deletions(-)

diff --git a/lib/git.js b/lib/git.js
index 6cf5aa90..78f9695a 100644
--- a/lib/git.js
+++ b/lib/git.js
@@ -1,10 +1,8 @@
 var cp = require('child_process');
+var fs = require('fs-extra');
 var path = require('path');
 var util = require('util');
 
-var Q = require('q');
-var fs = require('q-io/fs');
-
 var git = 'git';
 
 /**
@@ -30,33 +28,30 @@ util.inherits(ProcessError, Error);
  * @return {Promise} A promise.
  */
 function spawn(exe, args, cwd) {
-  var deferred = Q.defer();
-  var child = cp.spawn(exe, args, {cwd: cwd || process.cwd()});
-  var buffer = [];
-  child.stderr.on('data', function(chunk) {
-    buffer.push(chunk.toString());
-  });
-  child.stdout.on('data', function(chunk) {
-    deferred.notify(chunk);
-  });
-  child.on('close', function(code) {
-    if (code) {
-      var msg = buffer.join('') || 'Process failed: ' + code;
-      deferred.reject(new ProcessError(code, msg));
-    } else {
-      deferred.resolve(code);
-    }
+  return new Promise(function(resolve, reject) {
+    var child = cp.spawn(exe, args, {cwd: cwd || process.cwd()});
+    var buffer = [];
+    child.stderr.on('data', function(chunk) {
+      buffer.push(chunk.toString());
+    });
+    child.on('close', function(code) {
+      var output = buffer.join('');
+      if (code) {
+        var msg = output || 'Process failed: ' + code;
+        reject(new ProcessError(code, msg));
+      } else {
+        resolve(output);
+      }
+    });
   });
-  return deferred.promise;
 }
 
 /**
  * Execute a git command.
  * @param {Array.<string>} args Arguments (e.g. ['remote', 'update']).
  * @param {string} cwd Repository directory.
- * @return {Promise} A promise.  The promise will be resolved with the exit code
- *     or rejected with an error.  To get stdout, use a progress listener (e.g.
- *     `promise.progress(function(chunk) {console.log(String(chunk);}))`).
+ * @return {Promise} A promise.  The promise will be resolved with stdout output
+ *     or rejected with an error.
  */
 exports = module.exports = function(args, cwd) {
   return spawn(git, args, cwd);
@@ -90,9 +85,9 @@ exports.init = function init(cwd) {
 exports.clone = function clone(repo, dir, branch, options) {
   return fs.exists(dir).then(function(exists) {
     if (exists) {
-      return Q.resolve();
+      return Promise.resolve();
     } else {
-      return fs.makeTree(path.dirname(path.resolve(dir))).then(function() {
+      return fs.mkdirp(path.dirname(path.resolve(dir))).then(function() {
         var args = [
           'clone',
           repo,
@@ -106,7 +101,7 @@ exports.clone = function clone(repo, dir, branch, options) {
         if (options.depth) {
           args.push('--depth', options.depth);
         }
-        return spawn(git, args).fail(function(err) {
+        return spawn(git, args).catch(function(err) {
           // try again without banch options
           return spawn(git, ['clone', repo, dir, '--origin', options.remote]);
         });
@@ -171,7 +166,7 @@ exports.checkout = function checkout(remote, branch, cwd) {
         return spawn(git, ['checkout', '--orphan', branch], cwd);
       } else {
         // unhandled error
-        return Q.reject(error);
+        throw error;
       }
     }
   );
@@ -204,14 +199,13 @@ exports.add = function add(files, cwd) {
  * @return {Promise} A promise.
  */
 exports.commit = function commit(message, cwd) {
-  return spawn(git, ['diff-index', '--quiet', 'HEAD', '.'], cwd)
-    .then(function() {
-      // nothing to commit
-      return Q.resolve();
-    })
-    .fail(function() {
-      return spawn(git, ['commit', '-m', message], cwd);
-    });
+  return spawn(
+    git,
+    ['diff-index', '--quiet', 'HEAD', '.'],
+    cwd
+  ).catch(function() {
+    return spawn(git, ['commit', '-m', message], cwd);
+  });
 };
 
 /**
diff --git a/lib/index.js b/lib/index.js
index c8262bc7..66652467 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -1,13 +1,8 @@
-var path = require('path');
-var fs = require('fs');
-
-var Q = require('q');
-var rimraf = require('rimraf');
-var globby = require('globby');
-
-var git = require('./git');
-
 var copy = require('./util').copy;
+var fs = require('fs-extra');
+var git = require('./git');
+var globby = require('globby');
+var path = require('path');
 
 function getCacheDir() {
   return path.relative(process.cwd(), path.resolve(__dirname, '../.cache'));
@@ -16,36 +11,32 @@ function getCacheDir() {
 function getRemoteUrl(dir, remote) {
   var repo;
   return git(['config', '--get', 'remote.' + remote + '.url'], dir)
-    .progress(function(chunk) {
-      repo = String(chunk).split(/[\n\r]/).shift();
-    })
-    .then(function() {
+    .then(function(output) {
+      repo = output.split(/[\n\r]/).shift();
       if (repo) {
-        return Q.resolve(repo);
+        return repo;
       } else {
-        return Q.reject(
-          new Error('Failed to get repo URL from options or current directory.')
+        throw new Error(
+          'Failed to get repo URL from options or current directory.'
         );
       }
     })
-    .fail(function(err) {
-      return Q.reject(
-        new Error(
-          'Failed to get remote.' +
-            remote +
-            '.url (task must either be ' +
-            'run in a git repository with a configured ' +
-            remote +
-            ' remote ' +
-            'or must be configured with the "repo" option).'
-        )
+    .catch(function(err) {
+      throw new Error(
+        'Failed to get remote.' +
+          remote +
+          '.url (task must either be ' +
+          'run in a git repository with a configured ' +
+          remote +
+          ' remote ' +
+          'or must be configured with the "repo" option).'
       );
     });
 }
 
 function getRepo(options) {
   if (options.repo) {
-    return Q.resolve(options.repo);
+    return Promise.resolve(options.repo);
   } else {
     return getRemoteUrl(process.cwd(), options.remote);
   }
@@ -158,9 +149,7 @@ exports.publish = function publish(basePath, config, callback) {
             options.clone +
             '.  If you have changed your "repo" option, try ' +
             'running the "clean" task first.';
-          return Q.reject(new Error(message));
-        } else {
-          return Q.resolve();
+          throw new Error(message);
         }
       });
     })
@@ -181,8 +170,6 @@ exports.publish = function publish(basePath, config, callback) {
       if (!options.add) {
         log('Removing files');
         return git.rm(only.join(' '), options.clone);
-      } else {
-        return Q.resolve();
       }
     })
     .then(function() {
@@ -201,8 +188,6 @@ exports.publish = function publish(basePath, config, callback) {
         ).then(function() {
           return git(['config', 'user.name', options.user.name], options.clone);
         });
-      } else {
-        return Q.resolve();
       }
     })
     .then(function() {
@@ -212,29 +197,17 @@ exports.publish = function publish(basePath, config, callback) {
     .then(function() {
       if (options.tag) {
         log('Tagging');
-        var deferred = Q.defer();
-        git
-          .tag(options.tag, options.clone)
-          .then(function() {
-            return deferred.resolve();
-          })
-          .fail(function(error) {
-            // tagging failed probably because this tag alredy exists
-            log('Tagging failed, continuing');
-            options.logger(error);
-            return deferred.resolve();
-          });
-        return deferred.promise;
-      } else {
-        return Q.resolve();
+        return git.tag(options.tag, options.clone).catch(function(error) {
+          // tagging failed probably because this tag alredy exists
+          log('Tagging failed, continuing');
+          options.logger(error);
+        });
       }
     })
     .then(function() {
       if (options.push) {
         log('Pushing');
         return git.push(options.remote, options.branch, options.clone);
-      } else {
-        return Q.resolve();
       }
     })
     .then(
@@ -256,5 +229,5 @@ exports.publish = function publish(basePath, config, callback) {
  * Clean the cache directory.
  */
 exports.clean = function clean() {
-  rimraf.sync(getCacheDir());
+  fs.removeSync(getCacheDir());
 };
diff --git a/lib/util.js b/lib/util.js
index ed919edc..cb468545 100644
--- a/lib/util.js
+++ b/lib/util.js
@@ -2,7 +2,6 @@ var path = require('path');
 
 var async = require('async');
 var fs = require('graceful-fs');
-var Q = require('q');
 
 /**
  * Generate a list of unique directory paths given a list of file paths.
@@ -126,33 +125,31 @@ function makeDir(path, callback) {
  * @return {Promise} A promise.
  */
 exports.copy = function(files, base, dest) {
-  var deferred = Q.defer();
-
-  var pairs = [];
-  var destFiles = [];
-  files.forEach(function(file) {
-    var src = path.resolve(base, file);
-    var relative = path.relative(base, src);
-    var target = path.join(dest, relative);
-    pairs.push({
-      src: src,
-      dest: target
+  return new Promise(function(resolve, reject) {
+    var pairs = [];
+    var destFiles = [];
+    files.forEach(function(file) {
+      var src = path.resolve(base, file);
+      var relative = path.relative(base, src);
+      var target = path.join(dest, relative);
+      pairs.push({
+        src: src,
+        dest: target
+      });
+      destFiles.push(target);
     });
-    destFiles.push(target);
-  });
 
-  async.eachSeries(dirsToCreate(destFiles), makeDir, function(err) {
-    if (err) {
-      return deferred.reject(err);
-    }
-    async.each(pairs, copyFile, function(err) {
+    async.eachSeries(dirsToCreate(destFiles), makeDir, function(err) {
       if (err) {
-        return deferred.reject(err);
-      } else {
-        return deferred.resolve();
+        return reject(err);
       }
+      async.each(pairs, copyFile, function(err) {
+        if (err) {
+          return reject(err);
+        } else {
+          return resolve();
+        }
+      });
     });
   });
-
-  return deferred.promise;
 };
diff --git a/package.json b/package.json
index a9f9e1db..a76179b7 100644
--- a/package.json
+++ b/package.json
@@ -29,10 +29,9 @@
   "dependencies": {
     "async": "2.1.4",
     "commander": "2.9.0",
+    "fs-extra": "^3.0.1",
     "globby": "^6.1.0",
     "graceful-fs": "4.1.11",
-    "q": "1.4.1",
-    "q-io": "1.13.2",
     "rimraf": "^2.5.4"
   },
   "devDependencies": {