You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
253 lines
6.4 KiB
253 lines
6.4 KiB
15 years ago
|
#! /usr/bin/env perl
|
||
|
|
||
|
# cvs blame inspired by Bonsai
|
||
|
# Author: Bernd Gehrmann <bernd@physik.hu-berlin.de>
|
||
|
|
||
|
=head1 NAME
|
||
|
|
||
|
cvsblame - Shows a blame-annotated representation of a CVS controlled file in Konqueror.
|
||
|
|
||
|
=head1 SYNOPSIS
|
||
|
|
||
|
cvsblame <filename>
|
||
|
|
||
|
=head1 DESCRIPTION
|
||
|
|
||
|
cvsblame opens Konqueror to display the output of cvs annotate of
|
||
|
a cvs controlled file. When the mouse is on a revision number shown
|
||
|
in the second column, a popup with the respective log message
|
||
|
appears.
|
||
|
|
||
|
In the popup, a proper mailto: link to the author of a revision
|
||
|
can be created as follows: In your home directory, make a file
|
||
|
.cvsblame. In that file, enter for each repository you are working
|
||
|
with a line like
|
||
|
|
||
|
accounts :pserver:gehrmab@cvs.kde.org:/home/kde /home/bernd/.kdeaccounts
|
||
|
|
||
|
where the accounts file contains a simple list of cvs usernames in
|
||
|
the first column and the respective mail address in the second.
|
||
|
|
||
|
=head1 BUGS
|
||
|
|
||
|
=over 4
|
||
|
|
||
|
=item Does not really work for filenames which are not in the current directory
|
||
|
|
||
|
=item Is a hack. Really.
|
||
|
|
||
|
=back
|
||
|
|
||
|
=head1 AUTHOR
|
||
|
|
||
|
Bernd Gehrmann <bernd@physik.hu-berlin.de>
|
||
|
|
||
|
=cut
|
||
|
|
||
|
$file = $ARGV[0];
|
||
|
$outputfile = `kde-config --path tmp` || './#'; # if we put the file in the cwd, then we keep a '#' at the beggining to help CVS ignore it
|
||
|
($outputfile) = split(/:/,$outputfile);
|
||
|
chomp $outputfile;
|
||
|
$outputfile .= "cvsblame.$$.html";
|
||
|
$configfile = $ENV{HOME}. "/.cvsblame";
|
||
|
$rootfile = "`pwd`/CVS/Root";
|
||
|
$cvsroot = `cat "$rootfile"`;
|
||
|
chop $cvsroot;
|
||
|
|
||
|
#
|
||
|
# Look for a username -> mail address mapping
|
||
|
#
|
||
|
|
||
|
if (open(CONFIG, $configfile)) {
|
||
|
while (<CONFIG>) {
|
||
|
if (/accounts\s*([^\s]*)\s+([^\s]*)/) {
|
||
|
if ($1 eq $cvsroot) {
|
||
|
$accountfile = $2;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
close CONFIG;
|
||
|
}
|
||
|
if ($accountfile) {
|
||
|
open(ACCOUNTS, $accountfile) || die "Account file not found: $accountfile";
|
||
|
while (<ACCOUNTS>) {
|
||
|
if (/([^\s]*)\s+([^\s].*[^\s])\s+([^\s]+)/) {
|
||
|
$mail{$1} = "$2 <$3>";
|
||
|
}
|
||
|
elsif (/([^\s]*)\s+([^\s]*)/) {
|
||
|
$mail{$1} = $2;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
#
|
||
|
# The real work, first the html header
|
||
|
#
|
||
|
|
||
|
|
||
|
open(OUTPUT, ">$outputfile");
|
||
|
print OUTPUT <<EOF;
|
||
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||
|
<html>
|
||
|
<head>
|
||
|
<title>Blame annotation for $file</title>
|
||
|
</head>
|
||
|
<script type="text/javascript">
|
||
|
function hidelog(event) {
|
||
|
var target = event.relatedTarget;
|
||
|
while (target && target.id != "popup")
|
||
|
target = target.offsetParent;
|
||
|
if (target)
|
||
|
return;
|
||
|
target = event.target;
|
||
|
while (target.id != "popup")
|
||
|
target = target.offsetParent;
|
||
|
target.style.visibility = "hidden";
|
||
|
}
|
||
|
function init() {
|
||
|
document.getElementById("popup").addEventListener("mouseout", hidelog, false);
|
||
|
}
|
||
|
function poplog(target, rev) {
|
||
|
var popup = document.getElementById("popup");
|
||
|
var x = 120, y = 0;
|
||
|
while (target && target != document)
|
||
|
{
|
||
|
x += target.offsetLeft;
|
||
|
y += target.offsetTop;
|
||
|
target = target.offsetParent;
|
||
|
}
|
||
|
popup.style.pixelLeft = x;
|
||
|
popup.style.pixelTop = y;
|
||
|
popup.innerHTML = rev;
|
||
|
popup.style.visibility = "visible";
|
||
|
return true;
|
||
|
}
|
||
|
EOF
|
||
|
|
||
|
#
|
||
|
# Translate information from cvs log in javascript stuff
|
||
|
#
|
||
|
|
||
|
$revision = "";
|
||
|
open (LOG, "cvs log \"$file\"|");
|
||
|
while (<LOG>) {
|
||
|
chop;
|
||
|
$line = $_;
|
||
|
if ($line =~ /^revision (.*)/) {
|
||
|
$revision = $1;
|
||
|
$revstr = "log$revision";
|
||
|
$revstr =~ s/\./_/g;
|
||
|
} elsif ($line =~ /date: ([^;]*);\s*author: ([^;]*);.*/) {
|
||
|
$date = $1;
|
||
|
$author = $2;
|
||
|
if ($mail{$author}) {
|
||
|
$author = "<a href=\\\"mailto:$mail{$author}\\\">$author</a>";
|
||
|
}
|
||
|
$content = "<b>$revision</b> $author <b>$date<b>";
|
||
|
} elsif ($line eq "----------------------------" && !($revision eq "")) {
|
||
|
print OUTPUT "var $revstr = \"$content\";\n";
|
||
|
} elsif ($line eq "=============================================================================" && !($revision eq "")) {
|
||
|
print OUTPUT "var $revstr = \"$content\";\n";
|
||
|
} else {
|
||
|
$line =~ s/\"/\\\"/g;
|
||
|
$line =~ s/&/&/g;
|
||
|
$line =~ s/</</g;
|
||
|
$line =~ s/>/>/g;
|
||
|
$content = "$content<br>$line";
|
||
|
}
|
||
|
}
|
||
|
close LOG;
|
||
|
|
||
|
print OUTPUT <<EOF;
|
||
|
</script>
|
||
|
<style type="text/css">
|
||
|
body {
|
||
|
background: #eeeee0;
|
||
|
}
|
||
|
#popup {
|
||
|
position: absolute;
|
||
|
background: #ffcc99;
|
||
|
border: 1px solid #80664D;
|
||
|
padding: 2px;
|
||
|
}
|
||
|
a:link {
|
||
|
text-decoration: none;
|
||
|
}
|
||
|
a:hover {
|
||
|
text-decoration: underline;
|
||
|
}
|
||
|
</style>
|
||
|
<body text="#000000" onload="init()">
|
||
|
<h1>$file</h1>
|
||
|
<table border=0 cellpadding=0 cellspacing=0 width="100%">
|
||
|
EOF
|
||
|
|
||
|
#
|
||
|
# Information from cvs annotate
|
||
|
#
|
||
|
|
||
|
$color = 1;
|
||
|
$lineno = 1;
|
||
|
$oldrevision = "";
|
||
|
$oldlineno = "";
|
||
|
$oldrevstr = "";
|
||
|
open (ANNOTATE, "cvs annotate \"$file\" 2>/dev/null|");
|
||
|
while (<ANNOTATE>) {
|
||
|
chop;
|
||
|
$line = $_;
|
||
|
$revision = substr $line, 0, 13;
|
||
|
$revision =~ s/\s//g;
|
||
|
$author = substr $line, 14, 9;
|
||
|
$author =~ s/\s//g;
|
||
|
$date = substr $line, 23, 9;
|
||
|
$content = substr $line, 35;
|
||
|
$content =~ s/\&/&/g;
|
||
|
$content =~ s/\</</g;
|
||
|
$content =~ s/\>/>/g;
|
||
|
$revstr = "log$revision";
|
||
|
$revstr =~ s/\./_/g;
|
||
|
if ($revision eq $oldrevision) {
|
||
|
if ($lineno == $oldlineno+20) {
|
||
|
$linkstr = "<span onmouseover=\"return poplog(this, $revstr)\"'>";
|
||
|
$linkendstr = "</span>";
|
||
|
$revauthor = "$author $revision";
|
||
|
$oldlineno = $lineno;
|
||
|
} else {
|
||
|
$linkstr = "";
|
||
|
$linkendstr = "";
|
||
|
$revauthor = "";
|
||
|
}
|
||
|
} else {
|
||
|
$color = ($color == 0)? 1 : 0;
|
||
|
$linkstr = "<span onmouseover=\"return poplog(this, $revstr)\"'>";
|
||
|
$linkendstr = "</span>";
|
||
|
$revauthor = "$author $revision";
|
||
|
$oldlineno = $lineno;
|
||
|
$oldrevision = $revision;
|
||
|
}
|
||
|
print OUTPUT "<tr><td nowrap style=\"background: ", ($color==1)? "#EEEEE0" : "#FFFFCC", "\"><pre>";
|
||
|
|
||
|
print OUTPUT sprintf '%-5i%s%-14s%s%s', $lineno, $linkstr, $revauthor,$linkendstr, $content;
|
||
|
print OUTPUT "</pre></td></tr>\n";
|
||
|
$lineno++;
|
||
|
}
|
||
|
close ANNOTATE;
|
||
|
|
||
|
#
|
||
|
# Finally, the html footer
|
||
|
#
|
||
|
|
||
|
print OUTPUT <<EOF;
|
||
|
</table>
|
||
|
<div id="popup" style="visibility: hidden"></div>
|
||
|
</body>
|
||
|
</html>
|
||
|
EOF
|
||
|
|
||
|
close OUTPUT;
|
||
|
|
||
|
system("kfmclient openProfile webbrowsing $outputfile");
|
||
|
|
||
|
exit 0;
|