diff --git a/setup/cli/modules/unpack.php b/setup/cli/modules/unpack.php
index fca8f0ac2405686d72c624786ccefa6a1cac4ebd..dc184d00b14572ce36a1b6867c6426e3f1277e72 100644
--- a/setup/cli/modules/unpack.php
+++ b/setup/cli/modules/unpack.php
@@ -21,6 +21,9 @@ class Unpacker extends Module {
              code in that folder. The folder will be automatically created if
              it doesn't already exist."
         ),
+        'verbose' => array('-v','--verbose', 'default'=>false, 'nargs'=>0,
+            'action'=>'store_true', 'help'=>
+            "Move verbose logging to stdout"),
     );
 
     var $arguments = array(
@@ -45,9 +48,9 @@ class Unpacker extends Module {
         # Read the main.inc.php script
         $main_inc_php = $this->destination . '/main.inc.php';
         $lines = explode("\n", file_get_contents($main_inc_php));
-        # Try and use ROOT_PATH
+        # Try and use ROOT_DIR
         if (strpos($include_path, $this->destination) === 0)
-            $include_path = "ROOT_PATH . '" .
+            $include_path = "ROOT_DIR . '" .
                 str_replace($this->destination, '', $include_path) . "'";
         else
             $include_path = "'$include_path'";
@@ -78,14 +81,37 @@ class Unpacker extends Module {
         return false;
     }
 
-    function unpackage($folder, $destination, $recurse=true, $exclude=false) {
+    /**
+     * Copy from source to desination, perhaps recursing up to n folders.
+     * Exclusions are also permitted. If any files match an MD5 sum, they
+     * will be excluded from the copy operation.
+     *
+     * Parameters:
+     * folder - (string) source folder root
+     * destination - (string) destination folder root
+     * recurse - (int) recuse up to this many folders. Use 0 or false to
+     *      disable recursion, and -1 to recurse infinite folders.
+     * exclude - (string | array<string>) patterns that will be matched
+     *      using the PHP `fnmatch` function. If any file or folder matches,
+     *      it will be excluded from the copy procedure. Omit or use false
+     *      to disable exclusions
+     */
+    function unpackage($folder, $destination, $recurse=0, $exclude=false) {
+        $verbose = $this->getOption('verbose');
+        if (substr($destination, -1) !== '/')
+            $destination .= '/';
         foreach (glob($folder, GLOB_BRACE|GLOB_NOSORT) as $file) {
             if ($this->exclude($exclude, $file))
                 continue;
             if (is_file($file)) {
                 if (!is_dir($destination))
                     mkdir($destination, 0751, true);
-                copy($file, $destination . '/' . basename($file));
+                $target = $destination . basename($file);
+                if (is_file($target) && md5_file($target) == md5_file($file))
+                    continue;
+                if ($verbose)
+                    $this->stdout->write($target."\n");
+                copy($file, $target);
             }
         }
         if ($recurse) {
@@ -94,7 +120,7 @@ class Unpacker extends Module {
                     continue;
                 $this->unpackage(
                     dirname($folder).'/'.basename($dir).'/'.basename($folder),
-                    $destination.'/'.basename($dir),
+                    $destination.basename($dir),
                     $recurse - 1, $exclude);
             }
         }