<!--
Copyright (C) 2017 xaizek <xaizek@posteo.net>
This file is part of uncov.
uncov is free software: you can redistribute it and/or modify
it under the terms of version 3 of the GNU Affero General Public License as
published by the Free Software Foundation.
uncov 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
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with uncov. If not, see <http://www.gnu.org/licenses/>.
-->
<%pre>
#include <cstdlib>
#include <boost/optional.hpp>
#include <tnt/httperror.h>
#include "utils/Text.hpp"
#include "utils/strings.hpp"
#include "BuildHistory.hpp"
#include "ColorCane.hpp"
#include "FilePrinter.hpp"
#include "Repository.hpp"
#include "Settings.hpp"
#include "colors.hpp"
#include "listings.hpp"
extern Repository *globalRepo;
extern BuildHistory *globalBH;
extern Settings *globalSettings;
static int toNum(const std::string &s) {
char *end;
return std::strtol(s.c_str(), &end, 10);
}
</%pre>
<%cpp>
const std::string buildIdStr = request.getArg("buildId");
const int buildId = [&request, &buildIdStr]() {
try {
return std::stoi(buildIdStr);
} catch (const std::exception &) {
return 0;
}
}();
const std::string filePath = request.getArg("filePath");
</%cpp>
<%application>
// srchilite::SourceHighlight doesn't seem to be thread safe (in particular
// it has grammar parsing with global state) and needs to be globally
// locked
FilePrinter printer(*globalSettings);
</%application>
<html>
<head>
<title>Diff of <$ filePath $> in build #<$ buildIdStr $> | uncov</title>
<%include>web/header.ecpp</%include>
</head>
<body>
% if (boost::optional<Build> build = globalBH->getBuild(buildId)) {
% if (boost::optional<Build> prevBuild =
% globalBH->getBuild(globalBH->getPreviousBuildId(buildId))) {
% if (boost::optional<File &> file = build->getFile(filePath)) {
% boost::optional<File &> prevFile = prevBuild->getFile(filePath);
% std::vector<std::string> fileInfo = describeFile(globalBH, *build, *file,
% DoSpacing{});
% std::vector<std::string> buildInfo = describeBuild(globalBH, *build,
% !DoExtraAlign{},
% DoSpacing{});
<div class="sidenav">
<a href="/builds">All builds</a>
<a class="branch" href="/branches/<$ buildInfo[5] $>">Current branch</a>
<a href="/builds/<$ buildIdStr $>">Current build</a>
<br/>
<a class="action" href="/changes/<$ buildIdStr $>">Changed files</a>
</div>
<div class="rightbar">
<div class="bargroup">
<span class="barelem">Build <$$ buildInfo[0] $></span>
<span class="barelem"><$$ buildInfo[1] $></br>
<small><$$ buildInfo[2] $></small></span>
<span class="barelem"><$$ buildInfo[3] $></br>
<small><$$ buildInfo[4] $></small></span>
<span class="barelem">
<div class="tooltip">
<span class="tooltipobj">
<a class="branch" href="/branches/<$ buildInfo[5] $>">
<$$ buildInfo[5] $>
</a>
</span>
<span class="tooltiptext">
<$$ buildInfo[6] $>
</span>
</div>
</span>
</div>
<div class="bargroup">
<span class="barelem">File</span>
<span class="barelem"><$$ fileInfo[1] $></br>
<small><$$ fileInfo[2] $></small></span>
<span class="barelem"><$$ fileInfo[3] $></br>
<small><$$ fileInfo[4] $></small></span>
</div>
<%include>web/footer.ecpp</%include>
</div>
<h3 class="title">
Changes of a file between two builds
</h3>
<h4>
Path: <a class="path" href="/builds/<$ buildIdStr $>"><root></a>
% std::string pathPrefix;
% for (const std::string &entry : split(fileInfo[0], '/')) {
% pathPrefix += '/' + entry;
% const std::string slash = pathPrefix == "/" + fileInfo[0] ? "" : "/";
/ <a class="path"
href="/builds/<$ buildIdStr $><$ pathPrefix + slash $>"><$ entry $></a>
% }
</h4>
% const std::vector<int> &oldCov = prevFile ? prevFile->getCoverage()
% : std::vector<int>{};
% const std::vector<int> &newCov = file ? file->getCoverage()
% : std::vector<int>{};
% Text oldVersion(prevFile
% ? globalRepo->readFile(prevBuild->getRef(), filePath)
% : std::string());
% Text newVersion(file ? globalRepo->readFile(build->getRef(), filePath)
% : std::string());
% FileComparator comparator(oldVersion.asLines(), oldCov,
% newVersion.asLines(), newCov,
% CompareStrategy::State, *globalSettings);
<pre>
% ColorCane cc = printer.printDiff(filePath,
% oldVersion.asStream(), oldCov,
% newVersion.asStream(), newCov,
% comparator);
% std::string oldId = std::to_string(prevBuild->getId());
% std::string newId = std::to_string(build->getId());
% for (const ColorCanePiece &piece : cc) {
% if (piece.hi == ColorGroup::OldLineNo ||
% piece.hi == ColorGroup::NewLineNo) {
% const std::string & id = (piece.hi == ColorGroup::OldLineNo)
% ? oldId : newId;
% if (int n = toNum(piece.text)) {
% reply.out() << "<a href='" << "/builds/" << id << '/'
% << fileInfo[0] << "#l" << std::to_string(n) << "'>"
% << piece
% << "</a>";
% } else {
% reply.out() << "<span class='blank'>" << piece << "</span>";
% }
% } else {
% reply.out() << piece;
% }
% }
</pre>
% } else {
% log_warn("Can't find requested file " + filePath + " in build #"
% + buildIdStr);
<h1>No such file</h1>
% }
% } else {
% log_warn("Can't build preceding build #" + buildIdStr);
<h1>No previous build</h1>
% }
% } else {
% log_warn("Can't find requested build #" + buildIdStr);
<h1>No such build</h1>
% }
</body>
</html>
Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"
Clone this repository using HTTP(S):
git clone https://code.reversed.top/user/xaizek/uncov
Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@code.reversed.top/user/xaizek/uncov
You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a
pull request:
... clone the repository ...
... make some changes and some commits ...
git push origin master