Last active
March 16, 2020 20:17
-
-
Save bwedding/e97575519c5eb76a05b8a9db8dfac05e to your computer and use it in GitHub Desktop.
Parse a copied SVN log into a CSV file
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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