Skip to content

Instantly share code, notes, and snippets.

@bwedding
Last active March 16, 2020 20:17
Show Gist options
  • Save bwedding/e97575519c5eb76a05b8a9db8dfac05e to your computer and use it in GitHub Desktop.
Save bwedding/e97575519c5eb76a05b8a9db8dfac05e to your computer and use it in GitHub Desktop.
Parse a copied SVN log into a CSV file
// SVNLogToCSV.cpp : This program will take an SVN log file, as captured in the repository documentation
// and convert it to a CSV file for deeper analysis
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <algorithm>
const std::string Revision = "Revision:";
const std::string Author = "Author:";
const std::string Date = "Date:";
const std::string Message = "Message:";
const std::string Separator = "----";
// CSV Header. This must change if the format changes
std::string Header()
{
return "Revision,Author,Date,Message\n";
}
// Stores all information for one SVN commit
struct Commit
{
std::string Revision;
std::string Author;
std::string Date;
std::string Message;
std::string ToCSV()
{
std::string csv;
std::replace(Date.begin(), Date.end(), ',', ' ');
std::replace(Message.begin(), Message.end(), ',', ' ');
csv = Revision + "," + Author + "," + Date + "," + Message + "\n";
return csv;
}
void Clear()
{
Revision = "";
Author = "";
Date = "";
Message = "";
}
};
enum LineType
{
TRevision,
TAuthor,
TDate,
TMessage,
TSeparator,
TNone
};
LineType GetLineType(std::string& line)
{
if (line.find(Revision) != std::string::npos)
{
return LineType::TRevision;
}
if (line.find(Author) != std::string::npos)
{
return LineType::TAuthor;
}
if (line.find(Date) != std::string::npos)
{
return LineType::TDate;
}
if (line.find(Message) != std::string::npos)
{
return LineType::TMessage;
}
if (line.find(Separator) != std::string::npos)
{
return LineType::TSeparator;
}
return LineType::TNone;
}
// Trims leading spaces from string
static inline std::string& ltrim(std::string& s)
{
s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int c) {return !std::isspace(c); }));
return s;
}
std::string GetRevision(std::string line)
{
std::string revision;
std::size_t found = line.find(Revision);
if (found != std::string::npos)
{
int l = Revision.length();
revision = line.substr(found + Revision.length());
revision = ltrim(revision);
}
return revision;
}
std::string GetAuthor(std::string line)
{
std::string author;
std::size_t found = line.find(Author);
if (found != std::string::npos)
{
author = line.substr(found + Author.length());
author = ltrim(author);
}
return author;
}
std::string GetDate(std::string line)
{
std::string date;
std::size_t found = line.find(Date);
if (found != std::string::npos)
{
int l = Date.length();
date = line.substr(found + Date.length());
date = ltrim(date);
}
return date;
}
std::string GetMessage(std::string line)
{
std::string msg;
std::size_t found = line.find(Message);
if (found != std::string::npos)
{
msg = line.substr(found + Message.length());
msg = ltrim(msg);
}
std::size_t sep = line.find(Separator);
if (sep != std::string::npos)
{
msg = "\n";
}
else
{
if (found == std::string::npos)
msg = line + " ";
}
return msg;
}
// Read entire file into a vector of Commits
void GetAllCommits(std::ifstream& myfile, std::vector<Commit>& commits)
{
std::string line;
Commit commit;
while (std::getline(myfile, line))
{
LineType typeFound = GetLineType(line);
switch (typeFound)
{
case TRevision:
commit.Revision = GetRevision(line);
break;
case TAuthor:
commit.Author = GetAuthor(line);
break;
case TDate:
commit.Date = GetDate(line);
break;
case TMessage:
case TNone:
commit.Message += GetMessage(line);
break;
case TSeparator:
commits.push_back(commit);
commit.Clear();
break;
default:
break;
}
}
}
// Change the file extension from whatever it is to CSV
// Note: this does not handle a filename missing an extension
std::string ChangeToCSV(std::string fname)
{
size_t lastindex = fname.find_last_of(".");
std::string rawName = fname.substr(0, lastindex);
return rawName + ".csv";
}
int main(int argc, char* argv[])
{
std::vector<Commit> commits;
// Get filename
if (argc <= 1)
{
std::cout << "Usage: Please enter the filename to process\nPress enter to exit and try again.\n";
std::getchar();
return 0;
}
std::string fname(argv[1]);
// Open Log File
std::ifstream myfile(fname);
if (myfile.is_open())
{
GetAllCommits(myfile, commits);
myfile.close();
}
else
{
std::cout << "Unable to open file\nPress a key to exit.";
std::getchar();
return -1;
}
auto csv = ChangeToCSV(fname);
std::ofstream outFile(ChangeToCSV(fname));
if (!outFile.is_open())
{
std::cout << "Unable to open output file\nPress a key to exit.";
std::getchar();
return -1;
}
outFile << Header();
// For all commits
for (auto& commit : commits)
{
outFile << commit.ToCSV();
}
outFile.close();
std::cout << csv << " is ready!\nPress a key to exit.";
std::getchar();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment