Next batch of Chora/Vcs development.
authorMichael M Slusarz <slusarz@curecanti.org>
Fri, 23 Jan 2009 07:48:18 +0000 (00:48 -0700)
committerMichael M Slusarz <slusarz@curecanti.org>
Fri, 23 Jan 2009 07:48:18 +0000 (00:48 -0700)
Big advance in this batch of commits: browsing branches works now, at
least for CVS and Git.

chora/annotate.php
chora/browse.php
chora/co.php
chora/diff.php
chora/history.php
chora/stats.php
framework/Vcs/lib/Horde/Vcs.php
framework/Vcs/lib/Horde/Vcs/Cvs.php
framework/Vcs/lib/Horde/Vcs/Git.php
framework/Vcs/lib/Horde/Vcs/Svn.php

index 7e75130..79cee55 100644 (file)
@@ -12,7 +12,7 @@
 require_once dirname(__FILE__) . '/lib/base.php';
 
 /* Spawn the file object. */
-$fl = $VC->getFileObject($where, $cache);
+$fl = $VC->getFileObject($where, array('cache' => $cache));
 Chora::checkError($fl);
 $rev_ob = $VC->getRevisionObject();
 
index aaf2090..049bf67 100644 (file)
@@ -125,12 +125,8 @@ if ($atdir) {
 }
 
 /* Showing a file. */
-// TODO - onb is j
-$onb = Util::getFormData('onb', 0);
-if ($VC->isValidRevision($onb)) {
-}
-
-$fl = $VC->getFileObject($where, $cache);
+$onb = Util::getFormData('onb');
+$fl = $VC->getFileObject($where, array('cache' => $cache, 'branch' => $onb));
 Chora::checkError($fl);
 $title = sprintf(_("Revisions for %s"), $where);
 
@@ -147,7 +143,7 @@ foreach ($fl->symrev as $sm => $rv) {
 $selAllBranches = '';
 if ($VC->hasFeature('branches')) {
     foreach (array_keys($fl->branches) as $sym) {
-        $selAllBranches .= '<option value="' . $sym . '"' . ($sym == $onb ? ' selected="selected"' : '' ) . '>' . $sym . '</option>';
+        $selAllBranches .= '<option value="' . $sym . '"' . (($sym === $onb) ? ' selected="selected"' : '' ) . '>' . $sym . '</option>';
     }
 }
 
index 0d0efd1..7515782 100644 (file)
@@ -21,7 +21,7 @@ if ($atdir) {
 $plain = Util::getFormData('p', 0);
 
 /* Create the VC_File object and populate it. */
-$file = $VC->getFileObject($where, $cache);
+$file = $VC->getFileObject($where, array('cache' => $cache));
 Chora::checkError($file);
 
 /* Get the revision number. */
index 83ef8c0..08af32f 100644 (file)
@@ -12,7 +12,7 @@
 require_once dirname(__FILE__) . '/lib/base.php';
 
 /* Spawn the repository and file objects */
-$fl = $VC->getFileObject($where, $cache);
+$fl = $VC->getFileObject($where, array('cache' => $cache));
 Chora::checkError($fl);
 $rev_ob = $VC->getRevisionObject();
 
index d1d4a04..e68d3cd 100644 (file)
@@ -18,58 +18,17 @@ if (!$VC->hasFeature('branches')) {
 }
 
 /* Spawn the file object. */
-$fl = $VC->getFileObject($where, $cache);
+$fl = $VC->getFileObject($where, array('cache' => $cache));
 Chora::checkError($fl);
 $rev_ob = $VC->getRevisionObject();
 
-/* $trunk contains an array of trunk revisions. */
-$trunk = array();
-
-/* $branches is a hash with the branch revision as the key, and value
- * being an array of revs on that branch. */
-$branches = array();
-
-/* Populate $col with a list of all the branch points. */
-foreach ($fl->branches as $rev) {
-    $branches[$rev] = array();
-}
-
-/* For every revision, figure out if it is a trunk revision, or
- * instead associated with a branch.  If trunk, add it to the $trunk
- * array.  Otherwise, add it to an array in $branches[$branch]. */
-foreach ($fl->logs as $log) {
-    $rev = $log->queryRevision();
-    $baseRev = $rev_ob->strip($rev, 1);
-    $branchFound = false;
-    foreach ($fl->branches as $branch => $name) {
-        if ($branch == $baseRev) {
-            array_unshift($branches[$branch], $rev);
-            $branchFound = true;
-        }
-    }
-    /* If its not a branch, then add it to the trunk. */
-    /* TODO: this silently drops vendor branches atm! - avsm. */
-    if (!$branchFound && $rev_ob->sizeof($rev) == 2) {
-        array_unshift($trunk, $rev);
-    }
-}
-
-foreach ($branches as $col => $rows) {
-    /* If this branch has no actual commits on it, then it's a stub
-     * branch, and we can remove it for this view. */
-    if (!sizeof($rows)) {
-        unset($branches[$col]);
-    }
-}
-
 $colset = array('#ccdeff', '#ecf', '#fec', '#efc', '#cfd', '#dcdba0');
-$colStack = array();
-$branchColours = array();
+$branch_colors = $colStack = array();
 foreach ($branches as $brrev => $brcont) {
     if (!count($colStack)) {
         $colStack = $colset;
     }
-    $branchColours[$brrev] = array_shift($colset);
+    $branch_colors[$brrev] = array_shift($colset);
 }
 
 /**
@@ -185,7 +144,7 @@ foreach ($grid as $row) {
 
         if ($VC->isValidRevision($rev) && ($rev_ob->sizeof($rev) % 2)) {
             /* This is a branch point, so put the info out. */
-            $bg = isset($branchColours[$rev]) ? $branchColours[$rev] : '#e9e9e9';
+            $bg = isset($branch_colors[$rev]) ? $branch_colors[$rev] : '#e9e9e9';
             $symname = $fl->branches[$rev];
             require CHORA_TEMPLATES . '/history/branch_cell.inc';
 
@@ -193,13 +152,13 @@ foreach ($grid as $row) {
             /* This is a continuation cell, so render it with the
              * branch colour. */
             $bgbr = $rev_ob->strip(preg_replace('|^\:|', '', $rev), 1);
-            $bg = isset($branchColours[$bgbr]) ? $branchColours[$bgbr] : '#e9e9e9';
+            $bg = isset($branch_colors[$bgbr]) ? $branch_colors[$bgbr] : '#e9e9e9';
             require CHORA_TEMPLATES . '/history/blank.inc';
 
         } elseif ($VC->isValidRevision($rev)) {
             /* This cell contains a revision, so render it. */
             $bgbr = $rev_ob->strip($rev, 1);
-            $bg = isset($branchColours[$bgbr]) ? $branchColours[$bgbr] : '#e9e9e9';
+            $bg = isset($branch_colors[$bgbr]) ? $branch_colors[$bgbr] : '#e9e9e9';
             $log = $fl->logs[$rev];
             $author = Chora::showAuthorName($log->queryAuthor());
             $date = strftime('%e %b %Y', $log->queryDate());
index f54f13f..f406e33 100644 (file)
@@ -11,7 +11,7 @@
 
 require_once dirname(__FILE__) . '/lib/base.php';
 
-$fl = $VC->getFileObject($where, $cache);
+$fl = $VC->getFileObject($where, array('cache' => $cache));
 Chora::checkError($fl);
 
 $extraLink = Chora::getFileViews();
index 627fb6a..cdc34e3 100644 (file)
@@ -105,8 +105,11 @@ class Horde_Vcs
     /**
      * Constructor.
      */
-    public function __construct()
+    public function __construct($params = array())
     {
+        $this->_sourceroot = $params['sourceroot'];
+        $this->_paths = $params['paths'];
+
         $pos = strrpos(get_class($this), '_');
         $this->_driver = substr(get_class($this), $pos + 1);
     }
@@ -293,10 +296,15 @@ class Horde_Vcs
         return $this->_cache['diff']->availableDiffTypes();
     }
 
-    public function getFileObject($filename, $cache = null, $quicklog = false)
+    /**
+     * $opts:
+     * 'cache' - (boolean)
+     * 'quicklog' - (boolean)
+     */
+    public function getFileObject($filename, $opts = array())
     {
         $class = 'Horde_Vcs_File_' . $this->_driver;
-        $vc_file = new $class($this, $filename, $cache, $quicklog);
+        $vc_file = new $class($this, $filename, $opts);
         return $vc_file->getFileObject();
     }
 
@@ -750,6 +758,10 @@ class Horde_Vcs_File
     public $branches = array();
     public $revob;
 
+    public function getFileObject()
+    {
+    }
+
     /**
      * TODO
      */
@@ -769,9 +781,19 @@ class Horde_Vcs_File
     }
 
     /**
-     * Returns the name of the current file as in the repository
+     * Returns name of the current file without the repository extensions.
+     *
+     * @return string  Filename without repository extension
+     */
+    function queryName()
+    {
+        return $this->name;
+    }
+
+    /**
+     * Returns the name of the current file as in the repository.
      *
-     * @return string  Filename (without the path)
+     * @return string  Filename (without the path).
      */
     public function queryRepositoryName()
     {
index 18a8c27..81e1794 100644 (file)
@@ -2,6 +2,13 @@
 /**
  * Horde_Vcs_cvs implementation.
  *
+ * Constructor args:
+ * <pre>
+ * 'sourceroot': The source root for this repository
+ * 'paths': Hash with the locations of all necessary binaries: 'rcsdiff',
+ *          'rlog', 'cvsps', 'cvsps_home' and the temp path: 'temp'
+ * </pre>
+ *
  * Copyright 2000-2009 The Horde Project (http://www.horde.org/)
  *
  * See the enclosed file COPYING for license information (LGPL). If you
@@ -27,24 +34,6 @@ class Horde_Vcs_Cvs extends Horde_Vcs_Rcs
     protected $_branches = true;
 
     /**
-     * Constructor.
-     *
-     * @param array $params  Any parameter the class expects.
-     *                       Current parameters:
-     * <pre>
-     * 'sourceroot': The source root for this repository
-     * 'paths': Hash with the locations of all necessary binaries: 'rcsdiff',
-     *          'rlog', 'cvsps', 'cvsps_home' and the temp path: 'temp'
-     * </pre>
-     */
-    public function __construct($params)
-    {
-        $this->_sourceroot = $params['sourceroot'];
-        $this->_paths = $params['paths'];
-        parent::__construct();
-    }
-
-    /**
      * Returns the temporary file path.
      *
      * @return string  Temporary file path.
@@ -66,12 +55,12 @@ class Horde_Vcs_Cvs extends Horde_Vcs_Rcs
     /**
      * TODO
      */
-    public function getFileObject($filename, $cache = null, $quicklog = false)
+    public function getFileObject($filename, $opts = array())
     {
         if (substr($filename, 0, 1) != '/') {
             $filename = '/' . $filename;
         }
-        return parent::getFileObject($this->sourceroot() . $filename, $cache, $quicklog);
+        return parent::getFileObject($this->sourceroot() . $filename, $opts);
     }
 
     /**
@@ -213,10 +202,10 @@ class Horde_Vcs_Checkout_Cvs extends Horde_Vcs_Checkout
      * Static function which returns a file pointing to the head of the
      * requested revision of an RCS file.
      *
-     * @param Horde_Vcs_Cvs $rep  A repository object
-     * @param string $fullname   Fully qualified pathname of the desired file
-     *                           to checkout
-     * @param string $rev        Revision number to check out
+     * @param Horde_Vcs_Cvs $rep  A repository object.
+     * @param string $fullname    Fully qualified pathname of the desired file
+     *                            to checkout.
+     * @param string $rev         Revision number to check out.
      *
      * @return resource|object  Either a PEAR_Error object, or a stream
      *                          pointer to the head of the checkout
@@ -243,9 +232,9 @@ class Horde_Vcs_Checkout_Cvs extends Horde_Vcs_Checkout
          * /path/to/filename,v  -->  standard out
          * and we check that this is the case and error otherwise
          */
-
         $co = fgets($RCS, 1024);
-        if (!preg_match('/^([\S ]+),v\s+-->\s+st(andar)?d ?out(put)?\s*$/', $co, $regs) || $regs[1].',v' != $fullname) {
+        if (!preg_match('/^([\S ]+),v\s+-->\s+st(andar)?d ?out(put)?\s*$/', $co, $regs) ||
+            ($regs[1] != $fullname)) {
             return PEAR::raiseError('Unexpected output from checkout: ' . $co);
         }
 
@@ -403,7 +392,7 @@ class Horde_Vcs_Directory_Cvs extends Horde_Vcs_Directory
                 }
             } elseif (@is_file($path) && (substr($name, -2) == ',v')) {
                 /* Spawn a new file object to represent this file. */
-                $fl = $this->_rep->getFileObject(substr($path, strlen($this->_rep->sourceroot()), -2), $cache, $quicklog);
+                $fl = $this->_rep->getFileObject(substr($path, strlen($this->_rep->sourceroot()), -2), array('cache' => $cache, 'quicklog' => $quicklog));
                 if (is_a($fl, 'PEAR_Error')) {
                     return $fl;
                 } else {
@@ -439,28 +428,37 @@ class Horde_Vcs_Directory_Cvs extends Horde_Vcs_Directory
  */
 class Horde_Vcs_File_Cvs extends Horde_Vcs_File
 {
+    /* @TODO */
+    public $filename;
+    protected $_branch = '';
+
     /**
      * Create a repository file object, and give it information about
      * what its parent directory and repository objects are.
      *
-     * @param string $fl  Full path to this file.
+     * @param TODO $rep    TODO
+     * @param string $fl   Full path to this file.
+     * @param array $opts  TODO
      */
-    public function __construct($rep, $fl, $cache = null, $quicklog = false)
+    public function __construct($rep, $fl, $opts = array())
     {
         $this->rep = $rep;
         $this->name = basename($fl);
         $this->dir = dirname($fl);
         $this->filename = $fl;
-        $this->cache = $cache;
-        $this->quicklog = $quicklog;
+        $this->cache = empty($opts['cache']) ? null : $opts['cache'];
+        $this->quicklog = !empty($opts['quicklog']);
+        if (!empty($opts['branch'])) {
+            $this->_branch = $opts['branch'];
+        }
     }
 
     public function &getFileObject()
     {
         /* Assume file is in the Attic if it doesn't exist. */
-        $filename = $this->filename;
-        if (!@is_file($filename . ',v')) {
-            $filename = dirname($filename) . '/Attic/' . basename($filename);
+        $filename = $this->filename . ',v';
+        if (!@is_file($filename)) {
+            $filename = dirname($this->filename) . '/Attic/' . basename($this->filename) . ',v';
         }
 
         /* The version of the cached data. Increment this whenever the
@@ -469,22 +467,22 @@ class Horde_Vcs_File_Cvs extends Horde_Vcs_File
         $cacheVersion = 2;
         $cacheId = $this->rep->sourceroot() . '_n' . $filename . '_f' . (int)$this->quicklog . '_v' . $cacheVersion;
 
-        $ctime = time() - filemtime($filename . ',v');
+        $ctime = time() - filemtime($filename);
         if ($this->cache &&
             $this->cache->exists($cacheId, $ctime)) {
             $fileOb = unserialize($this->cache->get($cacheId, $ctime));
             $fileOb->setRepository($this->rep);
         } else {
-            $fileOb = new Horde_Vcs_File_Cvs($this->rep, $filename . ',v', $this->cache, $this->quicklog);
-            $fileOb->setRepository($this->rep);
-            if (is_a(($result = $fileOb->getBrowseInfo()), 'PEAR_Error')) {
+            if (is_a(($result = $this->getBrowseInfo()), 'PEAR_Error')) {
                 return $result;
             }
-            $fileOb->applySort(Horde_Vcs::SORT_AGE);
+            $this->applySort(Horde_Vcs::SORT_AGE);
 
             if ($this->cache) {
-                $this->cache->set($cacheId, serialize($fileOb));
+                $this->cache->set($cacheId, serialize($this));
             }
+
+            $fileOb = $this;
         }
 
         return $fileOb;
@@ -495,9 +493,9 @@ class Horde_Vcs_File_Cvs extends Horde_Vcs_File
      *
      * @return boolean  True if file is in the Attic, and false otherwise
      */
-    function isDeleted()
+    public function isDeleted()
     {
-        return substr($this->dir, -5) == 'Attic';
+        return (substr($this->dir, -5) == 'Attic');
     }
 
     /**
@@ -506,13 +504,12 @@ class Horde_Vcs_File_Cvs extends Horde_Vcs_File
      *
      * @return string  Filename without repository extension
      */
-    function queryName()
+    public function queryName()
     {
         return preg_replace('/,v$/', '', $this->name);
     }
 
-
-    function queryPreviousRevision($rev)
+    public function queryPreviousRevision($rev)
     {
         $ob = $this->rep->getRevisionObject();
         return $ob->prev($rev);
@@ -523,7 +520,7 @@ class Horde_Vcs_File_Cvs extends Horde_Vcs_File
      *
      * @return string  HEAD revision number
      */
-    function queryHead()
+    public function queryHead()
     {
         return $this->head;
     }
@@ -534,10 +531,10 @@ class Horde_Vcs_File_Cvs extends Horde_Vcs_File
      *
      * @return boolean|object  PEAR_Error object on error, or true on success
      */
-    function getBrowseInfo()
+    public function getBrowseInfo()
     {
         /* Check that we are actually in the filesystem. */
-        $file = $this->queryFullPath();
+        $file = $this->queryFullPath() . ',v';
         if (!is_file($file)) {
             return PEAR::raiseError('File Not Found: ' . $file);
         }
@@ -555,6 +552,7 @@ class Horde_Vcs_File_Cvs extends Horde_Vcs_File
         }
 
         $accum = $revsym = $symrev = array();
+        $rev_ob = $this->rep->getRevisionObject();
         $state = 'init';
 
         foreach ($return_array as $line) {
@@ -597,9 +595,18 @@ class Horde_Vcs_File_Cvs extends Horde_Vcs_File
                     // hash.
                     $log = new Horde_Vcs_Log_Cvs($this);
                     $err = $log->processLog($accum);
-                    // TODO: error checks - avsm
-                    $this->logs[$log->queryRevision()] = $log;
-                    $this->revs[] = $log->queryRevision();
+                    $rev = $log->queryRevision();
+                    $branch = $log->queryBranch();
+                    if (empty($this->_branch) ||
+                        in_array($this->_branch, $log->queryBranch()) ||
+                        (($rev_ob->cmp($rev, $this->branches[$this->_branch]) === -1) &&
+                         (empty($branch) ||
+                          in_array('HEAD', $branch) ||
+                          (strpos($this->branches[$this->_branch], $rev) === 0)))) {
+                        $log->setBranch($branch);
+                        $this->logs[$rev] = $log;
+                        $this->revs[] = $rev;
+                    }
                     $accum = array();
                 }
                 break;
@@ -614,7 +621,7 @@ class Horde_Vcs_File_Cvs extends Horde_Vcs_File
      *
      * @return Fully qualified filename of this object
      */
-    function queryFullPath()
+    public function queryFullPath()
     {
         return $this->dir . '/' . $this->name;
     }
@@ -624,7 +631,7 @@ class Horde_Vcs_File_Cvs extends Horde_Vcs_File
      *
      * @return string  Pathname relative to the sourceroot.
      */
-    function queryModulePath()
+    public function queryModulePath()
     {
         return preg_replace('|^'. $this->rep->sourceroot() . '/?(.*),v$|', '\1', $this->queryFullPath());
     }
@@ -637,7 +644,7 @@ class Horde_Vcs_File_Cvs extends Horde_Vcs_File
      *
      * @return string  Odd-digit Branch number
      */
-    function toBranch($rev)
+    public function toBranch($rev)
     {
         /* Check if we have a valid revision number */
         $rev_ob = $this->rep->getRevisionObject();
@@ -657,6 +664,54 @@ class Horde_Vcs_File_Cvs extends Horde_Vcs_File
         return substr_replace($rev, '.', $end2, ($end - $end2 + 1));
     }
 
+    /**
+     * TODO
+     */
+    public function getBranchList()
+    {
+/*
+// $trunk contains an array of trunk revisions.
+$trunk = array();
+
+// $branches is a hash with the branch revision as the key, and value
+// being an array of revs on that branch.
+$branches = array();
+
+// Populate $col with a list of all the branch points.
+foreach ($fl->branches as $rev) {
+    $branches[$rev] = array();
+}
+
+// For every revision, figure out if it is a trunk revision, or
+// instead associated with a branch.  If trunk, add it to the $trunk
+// array.  Otherwise, add it to an array in $branches[$branch].
+foreach ($fl->logs as $log) {
+    $rev = $log->queryRevision();
+    $baseRev = $rev_ob->strip($rev, 1);
+    $branchFound = false;
+    foreach ($fl->branches as $branch => $name) {
+        if ($branch == $baseRev) {
+            array_unshift($branches[$branch], $rev);
+            $branchFound = true;
+        }
+    }
+    // If its not a branch, then add it to the trunk.
+    // TODO: this silently drops vendor branches atm! - avsm.
+    if (!$branchFound && $rev_ob->sizeof($rev) == 2) {
+        array_unshift($trunk, $rev);
+    }
+}
+
+foreach ($branches as $col => $rows) {
+    // If this branch has no actual commits on it, then it's a stub
+    // branch, and we can remove it for this view.
+    if (!sizeof($rows)) {
+        unset($branches[$col]);
+    }
+}
+*/
+    }
+
 }
 
 /**
@@ -667,6 +722,9 @@ class Horde_Vcs_File_Cvs extends Horde_Vcs_File
  */
 class Horde_Vcs_Log_Cvs extends Horde_Vcs_Log
 {
+    /* Cached branch info. */
+    protected $_branch;
+
     public function processLog($raw)
     {
         /* Initialise a simple state machine to parse the output of rlog */
@@ -725,16 +783,24 @@ class Horde_Vcs_Log_Cvs extends Horde_Vcs_Log
             array();
     }
 
+    public function setBranch($branch)
+    {
+        $this->_branch = $branch;
+    }
+
     public function queryBranch()
     {
-        $key = array_search($this->rev, $this->file->branches);
-        if ($key) {
-            return array($key);
+        if (!empty($this->_branch)) {
+            return $this->_branch;
+        }
+
+        $key = array_keys($this->file->branches, $this->rev);
+        if (!empty($key)) {
+            return $key;
         }
 
         $rev_ob = $this->file->rep->getRevisionObject();
-        $key = array_search($rev_ob->strip($this->rev, 1), $this->file->branches);
-        return $key ? array($key) : array();
+        return array_keys($this->file->branches, $rev_ob->strip($this->rev, 1));
     }
 
 }
index 6bbd14e..b86b4ca 100644 (file)
@@ -2,6 +2,12 @@
 /**
  * Horde_Vcs_Git implementation.
  *
+ * Constructor args:
+ * <pre>
+ * 'sourceroot': The source root for this repository
+ * 'paths': Hash with the locations of all necessary binaries: 'git'
+ * </pre>
+ *
  * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
  *
  * See the enclosed file COPYING for license information (LGPL). If you
@@ -28,25 +34,20 @@ class Horde_Vcs_Git extends Horde_Vcs
     protected $_branches = true;
 
     /**
-     * Constructor.
-     *
-     * @param array $params  Any parameter the class expects.
-     *                       Current parameters:
-     *                       'sourceroot': The source root for this
-     *                                     repository
-     *                       'paths': Hash with the locations of all
-     *                                necessary binaries: 'git'
+     * TODO
      */
-    public function __construct($params)
+    public function isFile($where)
     {
-        $this->_sourceroot = escapeshellcmd($params['sourceroot']);
-        $this->_paths = $params['paths'];
-        parent::__construct();
+        $command = $this->_rep->getCommand() . ' ls-tree master ' . escapeshellarg($where) . ' 2>&1';
+        $entry = array();
+        exec($command, $entry);
+        $data = explode(' ', $entry[0]);
+        return ($data[1] == 'blob');
     }
 
     public function getCommand()
     {
-        return $this->getPath('git') . ' --git-dir=' . $this->_sourceroot;
+        return $this->getPath('git') . ' --git-dir=' . escapeshellarg($this->_sourceroot);
     }
 
     public function getCheckout($file, $rev)
@@ -257,23 +258,11 @@ class Horde_Vcs_Diff_Git extends Horde_Vcs_Diff
 
     private function _getRevisionRange($rep, $file, $r1, $r2)
     {
-        $cmd = $rep->getCommand() . ' rev-list ' . $r1 . '..' . $r2 . ' -- ' . escapeshellarg($file->queryModulePath());
-        $pipe = popen($cmd, 'r');
-        if (!is_resource($pipe)) {
-            throw new Horde_Vcs_Exception('Unable to run ' . $cmd . ': ' . error_get_last());
-        }
-
+        $cmd = $rep->getCommand() . ' rev-list ' . escapeshellarg($r1) . '..' . escapeshellarg($r2) . ' -- ' . escapeshellarg($file->queryModulePath());
         $revs = array();
 
-        while (!feof($pipe)) {
-            if ($rev = trim(fgets($pipe, 4096))) {
-                $revs[] = $rev;
-            }
-        }
-
-        pclose($pipe);
-
-        return $revs;
+        exec($cmd, $revs);
+        return array_map($revs, 'trim');
     }
 }
 
@@ -298,7 +287,8 @@ class Horde_Vcs_Directory_Git extends Horde_Vcs_Directory
                               $showattic = false)
     {
         // @TODO For now, we're browsing master
-        $head = trim(shell_exec($this->_rep->getCommand() . ' rev-parse --verify master'));
+        //$head = trim(shell_exec($this->_rep->getCommand() . ' rev-parse --verify master'));
+        $head = 'HEAD';
         // @TODO can use this to see if we have a valid cache of the tree at this revision
 
         $dir = $this->queryDir();
@@ -328,7 +318,7 @@ class Horde_Vcs_Directory_Git extends Horde_Vcs_Directory
             if ($type == 'tree') {
                 $this->_dirs[] = basename($file);
             } else {
-                $this->_files[] = $this->_rep->getFileObject($file, $cache, $quicklog);
+                $this->_files[] = $this->_rep->getFileObject($file, array('cache' => $cache, 'quicklog' => $quicklog));
             }
         }
 
@@ -348,25 +338,27 @@ class Horde_Vcs_Directory_Git extends Horde_Vcs_Directory
 class Horde_Vcs_File_Git extends Horde_Vcs_File
 {
     /* @TODO */
-    protected $_branch = 'master';
+    protected $_branch;
 
     /**
      * Create a repository file object, and give it information about
      * what its parent directory and repository objects are.
      *
-     * @param string $fl  Full path to this file.
+     * @param TODO $rep    TODO
+     * @param string $fl   Full path to this file.
+     * @param array $opts  TODO
      */
-    public function __construct($rep, $fl, $cache = null, $quicklog = false)
+    public function __construct($rep, $fl, $opts = array())
     {
-        // FIXME:
-        $rep->cache = $cache;
-
         $this->rep = $rep;
         $this->fullname = $fl;
         $this->name = basename($fl);
         $this->dir = dirname($fl);
-        $this->quicklog = $quicklog;
-        $this->cache = $cache;
+        $this->cache = empty($opts['cache']) ? null : $opts['cache'];
+        $this->quicklog = !empty($opts['quicklog']);
+        if (!empty($opts['branch'])) {
+            $this->_branch = $opts['branch'];
+        }
     }
 
     /**
@@ -374,52 +366,8 @@ class Horde_Vcs_File_Git extends Horde_Vcs_File
      */
     public function getFileObject()
     {
-        $this->getBrowseInfo();
-        return $this;
-    }
-
-    /**
-     * Get the hash name for this file at a specific revision.
-     *
-     * @param string $rev  TODO
-     *
-     * @return string  Commit hash.
-     */
-    public function getHashForRevision($rev)
-    {
-        return $this->logs[$rev]->getHashForPath($this->fullname);
-    }
-
-    /**
-     * Returns name of the current file without the repository
-     * extensions (usually ,v)
-     *
-     * @return string  Filename without repository extension
-     */
-    function queryName()
-    {
-        return $this->name;
-    }
-
-    /**
-     * Populate the object with information about the revisions logs
-     * and dates of the file.
-     */
-    function getBrowseInfo()
-    {
         // Get the list of revisions that touch this path
-        // TODO: Gets all revisions
-        $cmd = $this->rep->getCommand() . ' rev-list --branches -- ' . escapeshellarg($this->fullname) . ' 2>&1';
-        $revisions = shell_exec($cmd);
-        if (substr($revisions, 5) == 'fatal') {
-            throw new Horde_Vcs_Exception($revisions);
-        }
-
-        if (!strlen($revisions)) {
-            throw new Horde_Vcs_Exception('No revisions found');
-        }
-
-        $this->revs = explode("\n", trim($revisions));
+        $this->revs = $this->_getRevList($this->_branch);
 
         foreach ($this->revs as $rev) {
             $this->logs[$rev] = Horde_Vcs_Log_Git::factory($this, $rev);
@@ -439,6 +387,34 @@ class Horde_Vcs_File_Git extends Horde_Vcs_File
             $line = explode(' ', trim($val), 2);
             $this->branches[substr($line[1], strrpos($line[1], '/') + 1)] = $line[0];
         }
+
+        return $this;
+    }
+
+    protected function _getRevList($branch)
+    {
+        $cmd = $this->rep->getCommand() . ' rev-list ' . (empty($branch) ? '--branches' : $branch) . ' -- ' . escapeshellarg($this->fullname) . ' 2>&1';
+
+        $revisions = shell_exec($cmd);
+        if (substr($revisions, 5) == 'fatal') {
+            throw new Horde_Vcs_Exception($revisions);
+        } elseif (!strlen($revisions)) {
+            throw new Horde_Vcs_Exception('No revisions found');
+        }
+
+        return explode("\n", trim($revisions));
+    }
+
+    /**
+     * Get the hash name for this file at a specific revision.
+     *
+     * @param string $rev  Revision string.
+     *
+     * @return string  Commit hash.
+     */
+    public function getHashForRevision($rev)
+    {
+        return $this->logs[$rev]->getHashForPath($this->fullname);
     }
 
     /**
@@ -453,6 +429,20 @@ class Horde_Vcs_File_Git extends Horde_Vcs_File
             : $this->dir . '/' . $this->name;
     }
 
+    /**
+     * TODO
+     */
+    public function getBranchList()
+    {
+        $revs = array();
+
+        foreach (array_keys($this->branches) as $key) {
+            $revs[$key] = $this->_getRevList($key);
+        }
+
+        return $revs;
+    }
+
 }
 
 /**
@@ -475,17 +465,17 @@ class Horde_Vcs_Log_Git extends Horde_Vcs_Log
         $cacheVersion = 1;
         $cacheId = $file->rep->sourceroot() . '_r' . $rev . '_v' . $cacheVersion;
 
-        if ($file->rep->cache &&
+        if ($file->cache &&
             // Individual revisions can be cached forever
             // return array_keys(
-            $file->rep->cache->exists($cacheId, 0)) {
-            $logOb = unserialize($file->rep->cache->get($cacheId, 0));
+            $file->cache->exists($cacheId, 0)) {
+            $logOb = unserialize($file->cache->get($cacheId, 0));
             $logOb->setRepository($file->rep);
         } else {
             $logOb = new Horde_Vcs_Log_Git($file, $rev);
 
-            if ($file->rep->cache) {
-                $file->rep->cache->set($cacheId, serialize($logOb));
+            if ($file->cache) {
+                $file->cache->set($cacheId, serialize($logOb));
             }
         }
 
@@ -624,16 +614,10 @@ class Horde_Vcs_Patchset_Git extends Horde_Vcs_Patchset
     /**
      * Populate the object with information about the patchsets that
      * this file is involved in.
-     *
-     * @return mixed  PEAR_Error object on error, or true on success.
      */
     function getPatchsets()
     {
-        $fileOb = new Horde_Vcs_File_Git($this->_rep, $this->_file);
-        if (is_a(($result = $fileOb->getBrowseInfo()), 'PEAR_Error')) {
-            return $result;
-        }
-
+        $fileOb = $this->_rep->getFileObject($this->_file);
         $this->_patchsets = array();
 
         foreach ($fileOb->logs as $rev => $log) {
index 44ce098..2bd91be 100644 (file)
@@ -1,10 +1,13 @@
 <?php
-
-require_once dirname(__FILE__) . '/Rcs.php';
-
 /**
  * Horde_Vcs_Svn implementation.
  *
+ * Constructor args:
+ * <pre>
+ * 'sourceroot': The source root for this repository
+ * 'paths': Hash with the locations of all necessary binaries: 'svn', 'diff'
+ * </pre>
+ *
  * Copyright 2000-2009 The Horde Project (http://www.horde.org/)
  *
  * See the enclosed file COPYING for license information (LGPL). If you
@@ -39,19 +42,10 @@ class Horde_Vcs_Svn extends Horde_Vcs
     /**
      * Constructor.
      *
-     * @param array $params  Any parameter the class expects.
-     *                       Current parameters:
-     * <pre>
-     * 'sourceroot': The source root for this repository
-     * 'paths': Hash with the locations of all necessary binaries: 'svn',
-     *          'diff'
-     * </pre>
+     * @param array $params  Required parameters (see above).
      */
-    public function __construct($params)
+    public function __construct($params = array())
     {
-        $this->_sourceroot = $params['sourceroot'];
-        $this->_paths = $params['paths'];
-
         if (!empty($params['username'])) {
             $this->_username = $params['username'];
         }
@@ -281,7 +275,7 @@ class Horde_Vcs_Directory_Svn extends Horde_Vcs_Directory
             } elseif (substr($line, -1) == '/') {
                 $this->_dirs[] = substr($line, 0, -1);
             } else {
-                $fl = $this->_rep->getFileObject($this->queryDir() . "/$line", $cache, $quicklog);
+                $fl = $this->_rep->getFileObject($this->queryDir() . "/$line", array('cache' => $cache, 'quicklog' => $quicklog));
                 if (is_a($fl, 'PEAR_Error')) {
                     return $fl;
                 } else {
@@ -313,16 +307,18 @@ class Horde_Vcs_File_Svn extends Horde_Vcs_File {
      * Create a repository file object, and give it information about
      * what its parent directory and repository objects are.
      *
-     * @param string $fl  Full path to this file.
+     * @param TODO $rep    TODO
+     * @param string $fl   Full path to this file.
+     * @param array $opts  TODO
      */
-    public function __construct($rep, $fl, $cache = null, $quicklog = false)
+    public function __construct($rep, $fl, $opts = array())
     {
         $this->rep = $rep;
         $this->name = basename($fl);
         $this->dir = dirname($fl);
         $this->filename = $fl;
-        $this->quicklog = $quicklog;
-        $this->cache = $cache;
+        $this->cache = empty($opts['cache']) ? null : $opts['cache'];
+        $this->quicklog = !empty($opts['quicklog']);
     }
 
     public function getFileObject()