Diffstat info added to single patchset, diff, and browsefile screens.
Currently, only git supports. CVS uses a different way to generate this
info (see queryChangedLines()), but should eventually be changed to the
same mechanism - this diff information should not live directly in the
log object, but rather in the file objects inside of the master log
object.
$rev = $lg->queryRevision();
$branch_info = $lg->queryBranch();
+ $added = $deleted = null;
+ $fileinfo = $lg->queryFiles($where);
+ if ($fileinfo && isset($fileinfo['added'])) {
+ $added = $fileinfo['added'];
+ $deleted = $fileinfo['deleted'];
+ }
+
+ // TODO: Remove in favor of getting info from queryFiles()
+ $changedlines = $lg->queryChangedLines();
+
$textUrl = Chora::url('co', $where, array('r' => $rev));
$commitDate = Chora::formatDate($lg->queryDate());
$readableDate = Chora::readableTime($lg->queryDate(), true);
foreach ($VC->getRevisionRange($fl, $r1, $r2) as $val) {
$clog = $fl->queryLogs($val);
if (!is_null($clog)) {
- $log_messages[] = array(
+ $fileinfo = $clog->queryFiles($where);
+ $stats = ($fileinfo && isset($fileinfo['added']))
+ ? array('added' => $fileinfo['added'], 'deleted' => $fileinfo['deleted'])
+ : array();
+
+ $log_messages[] = array_merge(array(
'rev' => $val,
'msg' => Chora::formatLogMessage($clog->queryLog()),
'author' => Chora::showAuthorName($clog->queryAuthor(), true),
'branchinfo' => $clog->queryBranch(),
'date' => Chora::formatDate($clog->queryDate()),
'tags' => Chora::getTags($clog, $where),
- );
+ ), $stats);
}
}
$file['to'] = Horde::link(Chora::url('co', $member['file'], array('r' => $member['to'])), $member['to']) . htmlspecialchars($VC->abbrev($member['to'])) . '</a>';
}
+ if (isset($member['added'])) {
+ $file['added'] = $member['added'];
+ $file['deleted'] = $member['deleted'];
+ }
+
$files[] = $file;
}
</li>
<?php endif; ?>
<?php if (!empty($val['tags'])): ?><li class="tags"><?php echo _("Tags:") ?> <?php echo implode(', ', $val['tags']) ?></li><?php endif; ?>
+ <?php if (isset($val['added'])): ?><li><?php echo _("Statistics:") ?> <span class="diffadd">+<?php echo htmlspecialchars($val['added']) ?></span> <?php echo _("lines") ?>, <span class="diffdel">-<?php echo htmlspecialchars($val['deleted']) ?></span> <?php echo _("lines") ?></li><?php endif; ?>
</ul>
<a href="<?php echo Chora::url('co', $where, array('r' => $val['rev'])) ?>" title="<?php echo htmlspecialchars($val['rev']) ?>"><?php echo htmlspecialchars($VC->abbrev($val['rev'])) ?></a>: <?php echo $val['msg'] ?>
<div class="diffclear"></div>
<?php foreach (array_diff($branch_info, array($onb)) as $val): ?>
<span class="branch"><?php echo Horde::link(Chora::url('browsefile', $where, array('onb' => $val))) . htmlspecialchars($val) ?></a></span>
<?php endforeach; ?>
-<?php if (!empty($lg->lines)): ?>
- <small>(<?php printf('%s lines', $lg->lines) ?>)</small>
+<?php if (!empty($changedlines)): ?>
+ <small>(<?php printf('%s lines', htmlspecialchars($changedlines)) ?>)</small>
+<?php endif; ?>
+<?php if (!is_null($added)): ?>
+ <small>(<span class="diffadd">+<?php echo $added ?></span>, <span class="diffdel">-<?php echo $deleted ?></span> <?php echo _("lines") ?>)</small>
<?php endif; ?>
</td>
<td class="ago" sortval="<?php echo (int)$lg->queryDate() ?>"><a title="<?php echo $readableDate ?>"><?php echo $commitDate ?></a></td>
<ul class="singlepsfiles">
<?php foreach ($files as $file): ?>
- <li><?php echo $file['file'] . ': ' . $file['from'] . ' -> ' . $file['to'] . ' ' . $file['diff'] ?></li>
+ <li><?php echo $file['file'] . (isset($file['added']) ? (' (<span class="diffadd">+' . $file['added'] . '</span>, <span class="diffdel">-' . $file['deleted'] . '</span>) ') : '') . ': ' . $file['from'] . ' -> ' . $file['to'] . ' ' . $file['diff'] ?></li>
<?php endforeach; ?>
</ul>
padding-bottom: 4px;
}
+/* Diff stat information. */
+.diffadd {
+ color: blue;
+}
+.diffdel {
+ color: red;
+}
+
/* Checkout. */
div.checkout {
padding: 3px;
*/
public function queryLogs($rev = null)
{
- return is_null($rev) ? $this->_logs : (isset($this->_logs[$rev]) ? $this->_logs[$rev] : null);
+ return is_null($rev)
+ ? $this->_logs
+ : (isset($this->_logs[$rev]) ? $this->_logs[$rev] : null);
}
/**
{
protected $_rep;
protected $_file;
+ protected $_files;
protected $_rev;
protected $_author;
protected $_tags = array();
return $symBranches;
}
+ /**
+ * TODO
+ */
+ public function queryFiles($file = null)
+ {
+ return is_null($file)
+ ? $this->_files
+ : (isset($this->_files[$file]) ? $this->_files[$file] : null);
+ }
+
}
/**
protected $_parent = null;
/**
- * @var array
- */
- protected $_files = array();
-
- /**
* Constructor.
*
* @throws Horde_Vcs_Exception
{
parent::__construct($rep, $fl, $rev);
+ /* Get diff statistics. */
+ $stats = array();
+ $cmd = $rep->getCommand() . ' diff-tree --numstat ' . escapeshellarg($rev);
+ exec($cmd, $output);
+
+ reset($output);
+ // Skip the first entry (it is the revision number)
+ next($output);
+ while (list(,$v) = each($output)) {
+ $tmp = explode("\t", $v);
+ $stats[$tmp[2]] = array_slice($tmp, 0, 2);
+ }
+
// @TODO use Commit, CommitDate, and Merge properties
- $cmd = $rep->getCommand() . ' whatchanged --no-color --pretty=format:"Rev:%H%nParents:%P%nAuthor:%an <%ae>%nAuthorDate:%at%nRefs:%d%n%n%s%n%b" --no-abbrev -n 1 ' . $rev;
+ $cmd = $rep->getCommand() . ' whatchanged --no-color --pretty=format:"Rev:%H%nParents:%P%nAuthor:%an <%ae>%nAuthorDate:%at%nRefs:%d%n%n%s%n%b" --no-abbrev -n 1 ' . escapeshellarg($rev);
$pipe = popen($cmd, 'r');
if (!is_resource($pipe)) {
throw new Horde_Vcs_Exception('Unable to run ' . $cmd . ': ' . error_get_last());
// http://www.kernel.org/pub/software/scm/git/docs/git-diff-tree.html
while ($line) {
preg_match('/:(\d+) (\d+) (\w+) (\w+) (.+)\t(.+)(\t(.+))?/', $line, $matches);
- $this->_files[$matches[6]] = array(
+
+ $statinfo = isset($stats[$matches[6]])
+ ? array('added' => $stats[$matches[6]][0], 'deleted' => $stats[$matches[6]][1])
+ : array();
+
+ $this->_files[$matches[6]] = array_merge(array(
'srcMode' => $matches[1],
'dstMode' => $matches[2],
'srcSha1' => $matches[3],
'status' => $matches[5],
'srcPath' => $matches[6],
'dstPath' => isset($matches[7]) ? $matches[7] : ''
- );
+ ), $statinfo);
$line = fgets($pipe);
}
/**
* TODO
*/
- public function queryFiles()
- {
- return $this->_files;
- }
-
- /**
- * TODO
- */
public function queryParent()
{
return $this->_parent;
$from = $log->queryParent();
}
- $this->_patchsets[$rev]['members'][] = array(
+ $statinfo = isset($file['added'])
+ ? array('added' => $file['added'], 'deleted' => $file['deleted'])
+ : array();
+
+ $this->_patchsets[$rev]['members'][] = array_merge(array(
'file' => $file['srcPath'],
'from' => $from,
'status' => $status,
'to' => $to,
- );
+ ), $statinfo);
}
}
}