/WebSVN/diff.php
1,8 → 1,6
<?php
# vim:et:ts=3:sts=3:sw=3:fdm=marker:
 
// WebSVN - Subversion repository viewing via the web using PHP
// Copyright © 2004-2006 Tim Armes, Matt Sicker
// Copyright (C) 2004-2006 Tim Armes
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
11,12 → 9,12
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// --
//
25,334 → 23,212
// Show the differences between 2 revisions of a file.
//
 
require_once("include/setup.inc");
require_once("include/svnlook.inc");
require_once("include/utils.inc");
require_once("include/template.inc");
require_once 'include/setup.php';
require_once 'include/svnlook.php';
require_once 'include/utils.php';
require_once 'include/template.php';
 
$context = 5;
require_once 'include/diff_inc.php';
 
$vars["action"] = $lang["DIFF"];
$all = (@$_REQUEST["all"] == 1)?1:0;
$vars['action'] = $lang['DIFF'];
$all = (@$_REQUEST['all'] == 1);
$ignoreWhitespace = $config->getIgnoreWhitespacesInDiff();
 
if (array_key_exists('ignorews', $_REQUEST))
{
$ignoreWhitespace = (bool)$_REQUEST['ignorews'];
}
 
// Make sure that we have a repository
if (!isset($rep))
if (!$rep)
{
echo $lang["NOREP"];
exit;
renderTemplate404('diff','NOREP');
}
 
$svnrep = new SVNRepository($rep);
 
// If there's no revision info, go to the lastest revision for this path
$history = $svnrep->getLog($path, "", "", true);
$youngest = $history->entries[0]->rev;
$history = $svnrep->getLog($path, 'HEAD', 1, true, 2, ($path == '/') ? '' : $peg);
 
if (empty($rev))
$rev = $youngest;
if (!$history)
{
unset($vars['error']);
$history = $svnrep->getLog($path, '', '', true, 2, ($path == '/') ? '' : $peg);
}
 
$history = $svnrep->getLog($path, $rev);
$youngest = ($history && isset($history->entries[0])) ? $history->entries[0]->rev : false;
 
if ($path{0} != "/")
$ppath = "/".$path;
else
$ppath = $path;
if (empty($rev))
{
$rev = $youngest;
}
 
$history = $svnrep->getLog($path, $rev, 1, false, 2, $peg);
 
if ($path[0] != '/')
{
$ppath = '/'.$path;
}
else
{
$ppath = $path;
}
 
$prevrev = @$history->entries[1]->rev;
 
$vars["repname"] = $rep->getDisplayName();
$vars["rev"] = $rev;
$vars["path"] = $ppath;
$vars["prevrev"] = $prevrev;
$vars['path'] = str_replace('%2F', '/', rawurlencode($ppath));
$vars['safepath'] = escape($ppath);
$vars['rev1'] = $rev;
$vars['rev2'] = $prevrev;
$vars['prevrev'] = $prevrev;
 
$vars["rev1"] = $history->entries[0]->rev;
$vars["rev2"] = $prevrev;
if (isset($history->entries[0]))
{
$vars['log'] = xml_entities($history->entries[0]->msg);
$vars['date'] = $history->entries[0]->date;
$vars['age'] = datetimeFormatDuration(time() - strtotime($history->entries[0]->date));
$vars['author'] = $history->entries[0]->author;
$vars['rev'] = $vars['rev1'] = $history->entries[0]->rev;
$vars['peg'] = $peg;
}
 
createDirLinks($rep, $ppath, $rev, $showchanged);
createPathLinks($rep, $ppath, $passrev, $peg);
$passRevString = createRevAndPegString($rev, $peg);
 
$listing = array();
$passIgnoreWhitespace = '';
if ($ignoreWhitespace != $config->getIgnoreWhitespacesInDiff())
{
$passIgnoreWhitespace = '&amp;ignorews='.($ignoreWhitespace ? '1' : '0');
}
 
if ($prevrev)
if ($rev != $youngest)
{
$url = $config->getURL($rep, $path, "diff");
if (!$all)
{
$vars["showalllink"] = "<a href=\"${url}rev=$rev&amp;sc=$showchanged&amp;all=1\">${lang["SHOWENTIREFILE"]}</a>";
$vars["showcompactlink"] = "";
}
else
{
$vars["showcompactlink"] = "<a href=\"${url}rev=$rev&amp;sc=$showchanged&amp;all=0\">${lang["SHOWCOMPACT"]}</a>";
$vars["showalllink"] = "";
}
$vars['goyoungesturl'] = $config->getURL($rep, $path, 'diff').createRevAndPegString('', $peg).$passIgnoreWhitespace;
$vars['goyoungestlink'] = '<a href="'.$vars['goyoungesturl'].'"'.($youngest ? ' title="'.$lang['REV'].' '.$youngest.'"' : '').'>'.$lang['GOYOUNGEST'].'</a>';
}
 
// Get the contents of the two files
$newtname = tempnam("temp", "");
$new = $svnrep->getFileContents($history->entries[0]->path, $newtname, $history->entries[0]->rev, "", true);
$revurl = $config->getURL($rep, $path, 'diff');
 
$oldtname = tempnam("temp", "");
$old = $svnrep->getFileContents($history->entries[1]->path, $oldtname, $history->entries[1]->rev, "", true);
$ent = true;
$extension = strrchr(basename($path), ".");
if (($extension && isset($extEnscript[$extension]) && ('php' == $extEnscript[$extension])) || ($config->useEnscript))
$ent = false;
if ($rev < $youngest)
{
$history2 = $svnrep->getLog($path, $rev, $youngest, true, 2, $peg ? $peg : 'HEAD');
 
$file1cache = array();
if (isset($history2->entries[1]))
{
$nextRev = $history2->entries[1]->rev;
if ($nextRev != $youngest)
{
$vars['nextrev'] = $nextRev;
$vars['nextrevurl'] = $revurl.createRevAndPegString($nextRev, $peg).$passIgnoreWhitespace;
}
}
 
if ($all)
$context = 1; // Setting the context to 0 makes diff generate the wrong line numbers!
unset($vars['error']);
}
 
// Open a pipe to the diff command with $context lines of context
$cmd = quoteCommand($config->diff." --ignore-all-space -U $context $oldtname $newtname", false);
if ($all)
{
$ofile = fopen($oldtname, "r");
$nfile = fopen($newtname, "r");
}
if (isset($history->entries[1]))
{
$prevRev = $history->entries[1]->rev;
$prevPath = $history->entries[1]->path;
$vars['prevrev'] = $prevRev;
$vars['prevrevurl'] = $revurl.createRevAndPegString($prevRev, $peg).$passIgnoreWhitespace;
}
 
if ($diff = popen($cmd, "r"))
{
// Ignore the 3 header lines
$line = fgets($diff);
$line = fgets($diff);
$vars['revurl'] = $config->getURL($rep, $path, 'revision').$passRevString;
$vars['revlink'] = '<a href="'.$vars['revurl'].'">'.$lang['LASTMOD'].'</a>';
 
// Get the first real line
$line = fgets($diff);
$index = 0;
$listing = array();
$curoline = 1;
$curnline = 1;
while (!feof($diff))
{
// Get the first line of this range
sscanf($line, "@@ -%d", $oline);
$line = substr($line, strpos($line, "+"));
sscanf($line, "+%d", $nline);
if ($all)
{
while ($curoline < $oline || $curnline < $nline)
{
$listing[$index]["rev1diffclass"] = "diff";
$listing[$index]["rev2diffclass"] = "diff";
if ($curoline < $oline)
{
$nl = fgets($ofile);
if ($ent)
$line = replaceEntities(rtrim($nl), $rep);
else
$line = rtrim($nl);
$listing[$index]["rev1line"] = hardspace($line);
$vars['logurl'] = $config->getURL($rep, $path, 'log').$passRevString;
$vars['loglink'] = '<a href="'.$vars['logurl'].'">'.$lang['VIEWLOG'].'</a>';
 
$curoline++;
}
else
$listing[$index]["rev1line"] = "&nbsp;";
if ($curnline < $nline)
{
$nl = fgets($nfile);
$vars['filedetailurl'] = $config->getURL($rep, $path, 'file').$passRevString;
$vars['filedetaillink'] = '<a href="'.$vars['filedetailurl'].'">'.$lang['FILEDETAIL'].'</a>';
 
if ($ent)
$line = replaceEntities(rtrim($nl), $rep);
else
$line = rtrim($nl);
$listing[$index]["rev2line"] = hardspace($line);
$curnline++;
}
else
$listing[$index]["rev2line"] = "&nbsp;";
$listing[$index]["rev1lineno"] = 0;
$listing[$index]["rev2lineno"] = 0;
$vars['blameurl'] = $config->getURL($rep, $path, 'blame').$passRevString;
$vars['blamelink'] = '<a href="'.$vars['blameurl'].'">'.$lang['BLAME'].'</a>';
 
$index++;
}
}
else
{
// Output the line numbers
$listing[$index]["rev1lineno"] = "$oline";
$listing[$index]["rev2lineno"] = "$nline";
$index++;
}
$fin = false;
while (!feof($diff) && !$fin)
{
$listing[$index]["rev1lineno"] = 0;
$listing[$index]["rev2lineno"] = 0;
if ($rep->isRssEnabled())
{
$vars['rssurl'] = $config->getURL($rep, $path, 'rss').createRevAndPegString('', $peg);
$vars['rsslink'] = '<a href="'.$vars['rssurl'].'">'.$lang['RSSFEED'].'</a>';
}
 
$line = fgets($diff);
if (!strncmp($line, "@@", 2))
{
$fin = true;
}
else
{
$mod = $line{0};
// Check for binary file type before diffing.
$svnMimeType = $svnrep->getProperty($path, 'svn:mime-type', $rev);
 
if ($ent)
$line = replaceEntities(rtrim(substr($line, 1)), $rep);
else
$line = rtrim(substr($line, 1));
$listing[$index]["rev1line"] = hardspace($line);
// If no previous revision exists, bail out before diffing
if (!$rep->getIgnoreSvnMimeTypes() && preg_match('~application/*~', $svnMimeType))
{
$vars['warning'] = 'Cannot display diff of binary file. (svn:mime-type = '.$svnMimeType.')';
 
$text = hardspace($line);
if ($text == "") $text = "&nbsp;";
switch ($mod)
{
case "-":
$listing[$index]["rev1diffclass"] = "diffdeleted";
$listing[$index]["rev2diffclass"] = "diff";
$listing[$index]["rev1line"] = $text;
$listing[$index]["rev2line"] = "&nbsp;";
if ($all)
{
fgets($ofile);
$curoline++;
}
break;
}
else if (!$prevrev)
{
$vars['noprev'] = 1;
}
else
{
$diff = $config->getURL($rep, $path, 'diff').$passRevString;
 
case "+":
// Try to mark "changed" line sensibly
if (!empty($listing[$index-1]) && empty($listing[$index-1]["rev1lineno"]) && @$listing[$index-1]["rev1diffclass"] == "diffdeleted" && @$listing[$index-1]["rev2diffclass"] == "diff")
{
$i = $index - 1;
while (!empty($listing[$i-1]) && empty($listing[$i-1]["rev1lineno"]) && $listing[$i-1]["rev1diffclass"] == "diffdeleted" && $listing[$i-1]["rev2diffclass"] == "diff")
$i--;
$listing[$i]["rev1diffclass"] = "diffchanged";
$listing[$i]["rev2diffclass"] = "diffchanged";
$listing[$i]["rev2line"] = $text;
if ($all)
{
fgets($nfile);
$curnline++;
}
if ($all)
{
$vars['showcompactlink'] = '<a href="'.$diff.$passIgnoreWhitespace.'">'.$lang['SHOWCOMPACT'].'</a>';
}
else
{
$vars['showalllink'] = '<a href="'.$diff.$passIgnoreWhitespace.'&amp;all=1'.'">'.$lang['SHOWENTIREFILE'].'</a>';
}
 
// Don't increment the current index count
$index--;
}
else
{
$listing[$index]["rev1diffclass"] = "diff";
$listing[$index]["rev2diffclass"] = "diffadded";
$listing[$index]["rev1line"] = "&nbsp;";
$listing[$index]["rev2line"] = $text;
$passShowAll = ($all ? '&amp;all=1' : '');
$toggleIgnoreWhitespace = '';
 
if ($all)
{
fgets($nfile);
$curnline++;
}
}
break;
default:
$listing[$index]["rev1diffclass"] = "diff";
$listing[$index]["rev2diffclass"] = "diff";
$listing[$index]["rev1line"] = $text;
$listing[$index]["rev2line"] = $text;
if ($all)
{
fgets($ofile);
fgets($nfile);
$curoline++;
$curnline++;
}
if ($ignoreWhitespace == $config->getIgnoreWhitespacesInDiff())
{
$toggleIgnoreWhitespace = '&amp;ignorews='.($ignoreWhitespace ? '0' : '1');
}
 
break;
}
}
if (!$fin)
$index++;
}
}
// Output the rest of the files
if ($all)
{
while (!feof($ofile) || !feof($nfile))
{
$listing[$index]["rev1diffclass"] = "diff";
$listing[$index]["rev2diffclass"] = "diff";
if ($ent)
$line = replaceEntities(rtrim(fgets($ofile)), $rep);
else
$line = rtrim(fgets($ofile));
if ($ignoreWhitespace)
{
$vars['regardwhitespacelink'] = '<a href="'.$diff.$passShowAll.$toggleIgnoreWhitespace.'">'.$lang['REGARDWHITESPACE'].'</a>';
}
else
{
$vars['ignorewhitespacelink'] = '<a href="'.$diff.$passShowAll.$toggleIgnoreWhitespace.'">'.$lang['IGNOREWHITESPACE'].'</a>';
}
 
if (!feof($ofile))
$listing[$index]["rev1line"] = hardspace($line);
else
$listing[$index]["rev1line"] = "&nbsp;";
if ($ent)
$line = replaceEntities(rtrim(fgets($nfile)), $rep);
else
$line = rtrim(fgets($nfile));
// Get the contents of the two files
$newerFile = tempnamWithCheck($config->getTempDir(), '');
$newerFileHl = $newerFile.'highlight';
$normalNew = $svnrep->getFileContents($history->entries[0]->path, $newerFile, $history->entries[0]->rev, $peg, '', 'no');
$highlightedNew = $svnrep->getFileContents($history->entries[0]->path, $newerFileHl, $history->entries[0]->rev, $peg, '', 'line');
 
if (!feof($nfile))
$listing[$index]["rev2line"] = hardspace($line);
else
$listing[$index]["rev2line"] = "&nbsp;";
$listing[$index]["rev1lineno"] = 0;
$listing[$index]["rev2lineno"] = 0;
$olderFile = tempnamWithCheck($config->getTempDir(), '');
$olderFileHl = $olderFile.'highlight';
$normalOld = $svnrep->getFileContents($history->entries[0]->path, $olderFile, $history->entries[1]->rev, $peg, '', 'no');
$highlightedOld = $svnrep->getFileContents($history->entries[0]->path, $olderFileHl, $history->entries[1]->rev, $peg, '', 'line');
// TODO: Figured out why diffs across a move/rename are currently broken.
 
$index++;
}
}
pclose($diff);
}
if ($all)
{
fclose($ofile);
fclose($nfile);
}
$highlighted = ($highlightedNew && $highlightedOld);
 
// Remove our temporary files
unlink($oldtname);
unlink($newtname);
if ($highlighted)
{
$listing = do_diff($all, $ignoreWhitespace, $highlighted, $newerFile, $olderFile, $newerFileHl, $olderFileHl);
}
else
{
$listing = do_diff($all, $ignoreWhitespace, $highlighted, $newerFile, $olderFile, null, null);
}
 
// Remove our temporary files
@unlink($newerFile);
@unlink($olderFile);
@unlink($newerFileHl);
@unlink($olderFileHl);
}
else
 
if (!$rep->hasReadAccess($path, false))
{
$vars["noprev"] = 1;
$vars['error'] = $lang['NOACCESS'];
sendHeaderForbidden();
}
 
$vars["version"] = $version;
 
if (!$rep->hasReadAccess($path, false))
$vars["noaccess"] = true;
 
parseTemplate($rep->getTemplatePath()."header.tmpl", $vars, $listing);
parseTemplate($rep->getTemplatePath()."diff.tmpl", $vars, $listing);
parseTemplate($rep->getTemplatePath()."footer.tmpl", $vars, $listing);
?>
renderTemplate('diff');