1. Michael Gatto
  2. VersionControl_Hg

Commits

Michael Gatto  committed 51e6100

* Clone command is now at initial operational capacity.

  • Participants
  • Parent commits 7d08b63
  • Branches default

Comments (0)

Files changed (3)

File Trunk/Tests/Functional/test_Clone.php

View file
+<?php
+
+//unimplemented: --uncompressed
+
+include_once '../../VersionControl/Hg.php';
+
+$old_repository = 'H:\Development\_Webroot\Trunk\Tests\Fixtures\Test_Repository' ;
+$new_repository = 'H:\Development\_Webroot\Trunk\Tests\Fixtures\Clone_of_Repository';
+
+$hg = new VersionControl_Hg();
+
+/* Rely on Init.php to do work? */
+$cloned_repository_object_1 =
+    $hg->clone($old_repository)->to($new_repository)->run();
+
+var_dump($cloned_repository_object_1);
+die('end of test');
+
+
+
+
+$cloned_repository_object_2 =
+    $hg->clone($old_repository)->revision(3)->to($new_repository)->run();
+
+$cloned_repository_object_3 =
+    $hg->clone($old_repository)->revision('cdf5643ade')->to($new_repository)->run();
+
+//default is to create a working copy as well; only('repository') is the same
+//as hg update null and kills the working copy.
+// -U option
+$cloned_repository_object_4 =
+    //What? do we have a only('working-copy') as well?
+    $hg->clone($old_repository)->to($new_repository)->only('repository')->run();
+    //makes little sense for a function to have a single argument...
+    $hg->clone($old_repository)->to($new_repository)->without('working-copy')->run();
+    //no_working_copy() is too verbose, and we don't want/like underscored function names
+    $hg->clone($old_repository)->to($new_repository)->no_working_copy()->run();
+    //no_update does not clearly state its function
+    $hg->clone($old_repository)->to($new_repository)->no_update()->run();
+    //probably better; 'sparse' is already a term is some VCs implementations
+    $hg->clone($old_repository)->to($new_repository)->sparse()->run();
+    //even more semantic, but sounds kind of ominous
+    $hg->clone($old_repository)->to($new_repository)->emptied()->run();
+
+
+// -b options
+$cloned_repository_object_5 =
+    $hg->clone($old_repository)->branch('new_feature')->to($new_repository)->run();
+
+    //keep() = keeps the working copy.
+
+echo $cloned_repository_object;
+var_dump($cloned_repository_object);

File Trunk/VersionControl/Hg/Command/Clone.php

View file
+<?php
+/**
+ * Contains the definition of the VersionControl_Hg_Repository_Command_Clone
+ * class
+ *
+ * PHP version 5
+ *
+ * @category   VersionControl
+ * @package    Hg
+ * @subpackage Command
+ * @author     Michael Gatto <mgatto@lisantra.com>
+ * @copyright  2011 Lisantra Technologies, LLC
+ * @license    http://www.opensource.org/licenses/mit-license.html  MIT License
+ * @link       http://pear.php.net/package/VersionControl_Hg
+ */
+
+/**
+ * Provides the required interface for all commands
+ */
+require_once 'Interface.php';
+
+/**
+ * Provides base functionality common to all commands
+ */
+require_once 'Abstract.php';
+
+/**
+ * Provides Exceptions for commands (VersionControl_Hg_Command_Exception)
+ */
+require_once 'Exception.php';
+
+/**
+ * Clone a repository to a destination
+ *
+ * Usage:
+ * <code>
+ * $hg = new VersionControl_Hg('/path/to/repo');
+ * $hg->clone('http://url/to/repo')->to('/path/to/clone')->run();
+ * </code>
+ *
+ * NOTES
+ * Should return the object representing the cloned repository as
+ * type: VersionControl_Hg_Container_Repository.
+ *
+ * Should check if new location for cloned repo exists or not, and/or is
+ * empty same as Init.php does.
+ *
+ * PHP version 5
+ *
+ * @category   VersionControl
+ * @package    Hg
+ * @subpackage Command
+ * @author     Michael Gatto <mgatto@lisantra.com>
+ * @copyright  2011 Lisantra Technologies, LLC
+ * @license    http://www.opensource.org/licenses/mit-license.html  MIT License
+ * @link       http://pear.php.net/package/VersionControl_Hg
+ */
+class VersionControl_Hg_Command_Clone
+    extends VersionControl_Hg_Command_Abstract
+        implements VersionControl_Hg_Command_Interface
+{
+   /**
+     * The name of the mercurial command implemented here
+     *
+     * @var string
+     */
+    protected $command = 'clone';
+
+    /**
+     * Required options for this specific command. These may not be required
+     * by Mercurial itself, but are required for the proper functioning of
+     * this package.
+     *
+     * @var mixed
+     */
+    protected $required_options = array(
+        'noninteractive' => null,
+        //'repository' => null,
+        'files' => null,
+    );
+
+    /**
+     * Permissable options.
+     *
+     * The actual option must be the key, while 'null' is a value here to
+     * accommodate the current implementation of setting options.
+     *
+     * @var mixed
+     */
+    protected $allowed_options = array(
+        /* --pull is used for safety since HG automatically uses hardlinks for
+         repo data, although on some fs's, its not safe (eg. AFS) */
+        'pull' => null,
+        'sparse' => null,
+        'rev' => null,
+        'branch' => null,
+    );
+
+    /**
+     * The path in which Mercurial will create the new repository
+     *
+     * @var string
+     */
+    protected $cloned_path;
+
+    /**
+     * Constructor
+     *
+     * This may be empty if
+     *
+     * @param mixed $params is one or more parameters to modify the command
+     *
+     * @return void
+     */
+    public function __construct($params = null, VersionControl_Hg $hg)
+    {
+        $this->hg = $hg;
+
+        /* check if a repository has been designated already or not */
+        $cloned_path = $this->hg->repository->getPath();
+
+        if ( empty($cloned_path) ) {
+            /* are the argument(s) correctly formed? */
+            if ( (array_key_exists(0, $params)) && (! empty($params[0])) ) {
+                /* if its an array, check for the 'repository' key */
+                if ( (is_array($params[0])) && (! array_key_exists('repository', $params[0])) ) {
+                    throw new VersionControl_Hg_Command_Exception(
+                        VersionControl_Hg_Command_Exception::BAD_ARGUMENT,
+                        "The repository must be defined either at
+                         instantiation, as a string path arugment to clone()
+                         or as the 'repository' key in an array of options."
+                    );
+
+                    /* should always be called so we have a full array of valid options */
+                    $this->setOptions($params);
+                } elseif ( is_scalar($params[0])) {
+                    /* if scalar, we have to assume its a path */
+                    /* This is a psuedo-hack because init has no arugment prefix;
+                     * our current inmplementation of 'files' doesn't give one = cool! */
+                    $this->repository($params[0]);
+                }
+            }
+        } else {
+            /* should always be called so we have a full array of valid options */
+            $this->setOptions($params);
+        }
+    }
+
+    /**
+     * Execute the command and return the results.
+     *
+     * @param mixed $params The options passed to the Log command
+     *
+     * @return string
+     */
+    public function execute(array $params = null)
+    {
+        /* Validate */
+        $files = $this->getOption('files');
+        /* the destination of the cloned repo must be index 1 */
+        $cloned_path = $files[1];
+
+        if (! $this->directory_exists($cloned_path) ) {
+            /* kill any umasks. */
+            //@TODO remove since it causes problems on multithreaded servers
+            //umask(0);
+
+            //refuse bad fs names
+            //$except = array('\\', '/', ':', '*', '?', '"', '<', '>', '|');
+
+            //Please note that when specifying the recursive option the function returns false anyway if the directory already exists.
+            //Octal permissions are ignored on Windows
+            if (! mkdir($cloned_path, 0755, true) ) {
+                throw new VersionControl_Hg_Command_Exception(
+                    VersionControl_Hg_Command_Exception::BAD_ARGUMENT,
+                    "PHP encountered an error while trying to create the
+                     directory '{$cloned_path}'. "
+                );
+            }
+
+            /* This works around some umask issues */
+            chmod($cloned_path, 0755);
+        } //clearstatcache()
+
+        if ( ! is_writable($cloned_path) ) {
+            throw new VersionControl_Hg_Command_Exception(
+                VersionControl_Hg_Command_Exception::BAD_ARGUMENT,
+                "The directory '{$cloned_path}' is not writable, but must be. "
+            );
+        }
+
+        if ( ! $this->directory_is_empty($cloned_path) ) {
+            throw new VersionControl_Hg_Command_Exception(
+                VersionControl_Hg_Command_Exception::BAD_ARGUMENT,
+                "The directory '{$cloned_path}' is not empty, but must be. "
+            );
+        }
+
+        /* take care of options passed into run() as such:
+         * $hg->clone('/path/')->run('verbose'));
+         */
+        if ( ! empty($params) ) {
+            $this->setOptions($params);
+        }
+
+        /* --noninteractive is required since issuing the command is
+         * unattended by nature of using this package.
+        */
+        $this->addOptions(
+            array(
+                'noninteractive' => null,
+            )
+        );
+
+        /* Despite its being so not variable, we need to set the command string
+         * only after manually setting options and other command-specific data */
+        $this->setCommandString();
+
+        /* no var assignment, since 2nd param holds output */
+        exec($this->command_string, $this->output, $this->status);
+
+        if ( $this->status !== 0 ) {
+            throw new VersionControl_Hg_Command_Exception(
+                VersionControl_Hg_Command_Exception::COMMANDLINE_ERROR
+            );
+        }
+        $repository = VersionControl_Hg_Container_Repository::getInstance();
+        $repository->setPath($cloned_path);
+
+        return $this->hg->repository;
+    }
+
+    /**
+     * Tells Mercurial to use the pull functionality to copy metadata.
+     */
+    public function pull() {
+        $this->addOption('pull', null);
+
+        /* For the fluent API */
+        return $this;
+    }
+
+    /**
+     * Tells Mercurial to not create a working copy.
+     */
+    public function sparse() {
+        $this->addOption('noupdate', null);
+
+        /* For the fluent API */
+        return $this;
+    }
+
+    /**
+     * Tells Mercurial to not create a working copy.
+     */
+    public function to($cloned_path) {
+        $files = $this->getOption('files');
+
+        /* the repository to clone MUST be the first files item */
+        $files[1] = $cloned_path;
+        $this->addOption('files', $files);
+
+        /* For the fluent API */
+        return $this;
+    }
+
+    /**
+     * Tells Mercurial to not create a working copy.
+     */
+    public function repository($path) {
+        $files = $this->getOption('files');
+
+        /* the repository to clone MUST be the first files item */
+        $files[0] = $path;
+        $this->addOption('files', $files);
+
+        /* For the fluent API */
+        return $this;
+    }
+
+    /**
+     * Confirm that the path exists
+     *
+     * @param string $cloned_path The path the repository should be created at
+     *
+     * @return boolean
+     */
+    protected function directory_exists($path)
+    {
+        $directory_exists = true;
+
+        if (! is_dir($path) ) {
+            //So, is it a file? Do we care?
+
+            $directory_exists = false;
+        }
+
+        /* for the fluent API */
+        return $directory_exists;
+    }
+
+    /**
+     * Confirm that the path exists
+     *
+     * @param string $cloned_path The path the repository should be created at
+     *
+     * @return boolean
+     */
+    protected function directory_is_empty($path)
+    {
+        $directory_is_empty = true;
+
+        $files_in_dir = scandir($path);
+
+        /* ccount for . and .. */
+        if ( count($files_in_dir) > 2 ) {
+            $directory_is_empty = false;
+        }
+
+        /* for the fluent API */
+        return $directory_is_empty;
+    }
+
+    /**
+     * Specifies the revision to restrict the clone operation to
+      *
+     * Usage:
+     * <code>$hg->clone()->revision(7)->run();</code>
+     * or
+     * <code>$hg->clone()->revision('cde1256adc443a3')->run();</code>
+     * or
+     * <code>$hg->clone(array('revision' => 7 ))->to('/path/to)->run();</code>
+     *
+     * @param string $revision is the optional revision to archive
+     *
+     * @return void
+     */
+    public function revision($revision = 'tip')
+    {
+        /* Technically, this shouldn't occur since 'tip' is default */
+        if ( empty($revision)) {
+            throw new VersionControl_Hg_Command_Exception(
+                VersionControl_Hg_Command_Exception::BAD_ARGUMENT
+            );
+        }
+
+        $this->addOption('rev', $revision);
+
+        /* for the fluent API */
+        return $this;
+    }
+
+}

File Trunk/VersionControl/Hg/CommandProxy.php

View file
      * @var array
      */
     protected $allowed_commands = array(
-        'version', 'archive', 'status', 'log', 'init',
+        'version', 'archive', 'status', 'log', 'init', 'clone',
     );
 
     /**