Run clang-tidy's modernize-use-override checker. This checker must have issues in version 3.8. It has way too little matches. And it adds override to destructors. Revert the changes on the destructors and change override to CM_OVERRIDE.
658 lines
19 KiB
C++
658 lines
19 KiB
C++
/*============================================================================
|
|
CMake - Cross Platform Makefile Generator
|
|
Copyright 2000-2009 Kitware, Inc.
|
|
|
|
Distributed under the OSI-approved BSD License (the "License");
|
|
see accompanying file Copyright.txt for details.
|
|
|
|
This software is distributed WITHOUT ANY WARRANTY; without even the
|
|
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
See the License for more information.
|
|
============================================================================*/
|
|
#include "cmCTestGIT.h"
|
|
|
|
#include "cmAlgorithms.h"
|
|
#include "cmCTest.h"
|
|
#include "cmSystemTools.h"
|
|
|
|
#include <cmsys/FStream.hxx>
|
|
#include <cmsys/Process.h>
|
|
#include <cmsys/RegularExpression.hxx>
|
|
|
|
#include <ctype.h>
|
|
#include <sys/types.h>
|
|
#include <time.h>
|
|
|
|
static unsigned int cmCTestGITVersion(unsigned int epic, unsigned int major,
|
|
unsigned int minor, unsigned int fix)
|
|
{
|
|
// 1.6.5.0 maps to 10605000
|
|
return fix + minor * 1000 + major * 100000 + epic * 10000000;
|
|
}
|
|
|
|
cmCTestGIT::cmCTestGIT(cmCTest* ct, std::ostream& log)
|
|
: cmCTestGlobalVC(ct, log)
|
|
{
|
|
this->PriorRev = this->Unknown;
|
|
this->CurrentGitVersion = 0;
|
|
}
|
|
|
|
cmCTestGIT::~cmCTestGIT()
|
|
{
|
|
}
|
|
|
|
class cmCTestGIT::OneLineParser : public cmCTestVC::LineParser
|
|
{
|
|
public:
|
|
OneLineParser(cmCTestGIT* git, const char* prefix, std::string& l)
|
|
: Line1(l)
|
|
{
|
|
this->SetLog(&git->Log, prefix);
|
|
}
|
|
|
|
private:
|
|
std::string& Line1;
|
|
bool ProcessLine() CM_OVERRIDE
|
|
{
|
|
// Only the first line is of interest.
|
|
this->Line1 = this->Line;
|
|
return false;
|
|
}
|
|
};
|
|
|
|
std::string cmCTestGIT::GetWorkingRevision()
|
|
{
|
|
// Run plumbing "git rev-list" to get work tree revision.
|
|
const char* git = this->CommandLineTool.c_str();
|
|
const char* git_rev_list[] = { git, "rev-list", "-n", "1", "HEAD", "--", 0 };
|
|
std::string rev;
|
|
OneLineParser out(this, "rl-out> ", rev);
|
|
OutputLogger err(this->Log, "rl-err> ");
|
|
this->RunChild(git_rev_list, &out, &err);
|
|
return rev;
|
|
}
|
|
|
|
void cmCTestGIT::NoteOldRevision()
|
|
{
|
|
this->OldRevision = this->GetWorkingRevision();
|
|
cmCTestLog(this->CTest, HANDLER_OUTPUT, " Old revision of repository is: "
|
|
<< this->OldRevision << "\n");
|
|
this->PriorRev.Rev = this->OldRevision;
|
|
}
|
|
|
|
void cmCTestGIT::NoteNewRevision()
|
|
{
|
|
this->NewRevision = this->GetWorkingRevision();
|
|
cmCTestLog(this->CTest, HANDLER_OUTPUT, " New revision of repository is: "
|
|
<< this->NewRevision << "\n");
|
|
}
|
|
|
|
std::string cmCTestGIT::FindGitDir()
|
|
{
|
|
std::string git_dir;
|
|
|
|
// Run "git rev-parse --git-dir" to locate the real .git directory.
|
|
const char* git = this->CommandLineTool.c_str();
|
|
char const* git_rev_parse[] = { git, "rev-parse", "--git-dir", 0 };
|
|
std::string git_dir_line;
|
|
OneLineParser rev_parse_out(this, "rev-parse-out> ", git_dir_line);
|
|
OutputLogger rev_parse_err(this->Log, "rev-parse-err> ");
|
|
if (this->RunChild(git_rev_parse, &rev_parse_out, &rev_parse_err)) {
|
|
git_dir = git_dir_line;
|
|
}
|
|
if (git_dir.empty()) {
|
|
git_dir = ".git";
|
|
}
|
|
|
|
// Git reports a relative path only when the .git directory is in
|
|
// the current directory.
|
|
if (git_dir[0] == '.') {
|
|
git_dir = this->SourceDirectory + "/" + git_dir;
|
|
}
|
|
#if defined(_WIN32) && !defined(__CYGWIN__)
|
|
else if (git_dir[0] == '/') {
|
|
// Cygwin Git reports a full path that Cygwin understands, but we
|
|
// are a Windows application. Run "cygpath" to get Windows path.
|
|
std::string cygpath_exe = cmSystemTools::GetFilenamePath(git);
|
|
cygpath_exe += "/cygpath.exe";
|
|
if (cmSystemTools::FileExists(cygpath_exe.c_str())) {
|
|
char const* cygpath[] = { cygpath_exe.c_str(), "-w", git_dir.c_str(),
|
|
0 };
|
|
OneLineParser cygpath_out(this, "cygpath-out> ", git_dir_line);
|
|
OutputLogger cygpath_err(this->Log, "cygpath-err> ");
|
|
if (this->RunChild(cygpath, &cygpath_out, &cygpath_err)) {
|
|
git_dir = git_dir_line;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
return git_dir;
|
|
}
|
|
|
|
std::string cmCTestGIT::FindTopDir()
|
|
{
|
|
std::string top_dir = this->SourceDirectory;
|
|
|
|
// Run "git rev-parse --show-cdup" to locate the top of the tree.
|
|
const char* git = this->CommandLineTool.c_str();
|
|
char const* git_rev_parse[] = { git, "rev-parse", "--show-cdup", 0 };
|
|
std::string cdup;
|
|
OneLineParser rev_parse_out(this, "rev-parse-out> ", cdup);
|
|
OutputLogger rev_parse_err(this->Log, "rev-parse-err> ");
|
|
if (this->RunChild(git_rev_parse, &rev_parse_out, &rev_parse_err) &&
|
|
!cdup.empty()) {
|
|
top_dir += "/";
|
|
top_dir += cdup;
|
|
top_dir = cmSystemTools::CollapseFullPath(top_dir);
|
|
}
|
|
return top_dir;
|
|
}
|
|
|
|
bool cmCTestGIT::UpdateByFetchAndReset()
|
|
{
|
|
const char* git = this->CommandLineTool.c_str();
|
|
|
|
// Use "git fetch" to get remote commits.
|
|
std::vector<char const*> git_fetch;
|
|
git_fetch.push_back(git);
|
|
git_fetch.push_back("fetch");
|
|
|
|
// Add user-specified update options.
|
|
std::string opts = this->CTest->GetCTestConfiguration("UpdateOptions");
|
|
if (opts.empty()) {
|
|
opts = this->CTest->GetCTestConfiguration("GITUpdateOptions");
|
|
}
|
|
std::vector<std::string> args = cmSystemTools::ParseArguments(opts.c_str());
|
|
for (std::vector<std::string>::const_iterator ai = args.begin();
|
|
ai != args.end(); ++ai) {
|
|
git_fetch.push_back(ai->c_str());
|
|
}
|
|
|
|
// Sentinel argument.
|
|
git_fetch.push_back(0);
|
|
|
|
// Fetch upstream refs.
|
|
OutputLogger fetch_out(this->Log, "fetch-out> ");
|
|
OutputLogger fetch_err(this->Log, "fetch-err> ");
|
|
if (!this->RunUpdateCommand(&git_fetch[0], &fetch_out, &fetch_err)) {
|
|
return false;
|
|
}
|
|
|
|
// Identify the merge head that would be used by "git pull".
|
|
std::string sha1;
|
|
{
|
|
std::string fetch_head = this->FindGitDir() + "/FETCH_HEAD";
|
|
cmsys::ifstream fin(fetch_head.c_str(), std::ios::in | std::ios::binary);
|
|
if (!fin) {
|
|
this->Log << "Unable to open " << fetch_head << "\n";
|
|
return false;
|
|
}
|
|
std::string line;
|
|
while (sha1.empty() && cmSystemTools::GetLineFromStream(fin, line)) {
|
|
this->Log << "FETCH_HEAD> " << line << "\n";
|
|
if (line.find("\tnot-for-merge\t") == line.npos) {
|
|
std::string::size_type pos = line.find('\t');
|
|
if (pos != line.npos) {
|
|
sha1 = line.substr(0, pos);
|
|
}
|
|
}
|
|
}
|
|
if (sha1.empty()) {
|
|
this->Log << "FETCH_HEAD has no upstream branch candidate!\n";
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Reset the local branch to point at that tracked from upstream.
|
|
char const* git_reset[] = { git, "reset", "--hard", sha1.c_str(), 0 };
|
|
OutputLogger reset_out(this->Log, "reset-out> ");
|
|
OutputLogger reset_err(this->Log, "reset-err> ");
|
|
return this->RunChild(&git_reset[0], &reset_out, &reset_err);
|
|
}
|
|
|
|
bool cmCTestGIT::UpdateByCustom(std::string const& custom)
|
|
{
|
|
std::vector<std::string> git_custom_command;
|
|
cmSystemTools::ExpandListArgument(custom, git_custom_command, true);
|
|
std::vector<char const*> git_custom;
|
|
for (std::vector<std::string>::const_iterator i = git_custom_command.begin();
|
|
i != git_custom_command.end(); ++i) {
|
|
git_custom.push_back(i->c_str());
|
|
}
|
|
git_custom.push_back(0);
|
|
|
|
OutputLogger custom_out(this->Log, "custom-out> ");
|
|
OutputLogger custom_err(this->Log, "custom-err> ");
|
|
return this->RunUpdateCommand(&git_custom[0], &custom_out, &custom_err);
|
|
}
|
|
|
|
bool cmCTestGIT::UpdateInternal()
|
|
{
|
|
std::string custom = this->CTest->GetCTestConfiguration("GITUpdateCustom");
|
|
if (!custom.empty()) {
|
|
return this->UpdateByCustom(custom);
|
|
}
|
|
return this->UpdateByFetchAndReset();
|
|
}
|
|
|
|
bool cmCTestGIT::UpdateImpl()
|
|
{
|
|
if (!this->UpdateInternal()) {
|
|
return false;
|
|
}
|
|
|
|
std::string top_dir = this->FindTopDir();
|
|
const char* git = this->CommandLineTool.c_str();
|
|
const char* recursive = "--recursive";
|
|
const char* sync_recursive = "--recursive";
|
|
|
|
// Git < 1.6.5 did not support submodule --recursive
|
|
if (this->GetGitVersion() < cmCTestGITVersion(1, 6, 5, 0)) {
|
|
recursive = 0;
|
|
// No need to require >= 1.6.5 if there are no submodules.
|
|
if (cmSystemTools::FileExists((top_dir + "/.gitmodules").c_str())) {
|
|
this->Log << "Git < 1.6.5 cannot update submodules recursively\n";
|
|
}
|
|
}
|
|
|
|
// Git < 1.8.1 did not support sync --recursive
|
|
if (this->GetGitVersion() < cmCTestGITVersion(1, 8, 1, 0)) {
|
|
sync_recursive = 0;
|
|
// No need to require >= 1.8.1 if there are no submodules.
|
|
if (cmSystemTools::FileExists((top_dir + "/.gitmodules").c_str())) {
|
|
this->Log << "Git < 1.8.1 cannot synchronize submodules recursively\n";
|
|
}
|
|
}
|
|
|
|
OutputLogger submodule_out(this->Log, "submodule-out> ");
|
|
OutputLogger submodule_err(this->Log, "submodule-err> ");
|
|
|
|
bool ret;
|
|
|
|
std::string init_submodules =
|
|
this->CTest->GetCTestConfiguration("GITInitSubmodules");
|
|
if (cmSystemTools::IsOn(init_submodules.c_str())) {
|
|
char const* git_submodule_init[] = { git, "submodule", "init", 0 };
|
|
ret = this->RunChild(git_submodule_init, &submodule_out, &submodule_err,
|
|
top_dir.c_str());
|
|
|
|
if (!ret) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
char const* git_submodule_sync[] = { git, "submodule", "sync",
|
|
sync_recursive, 0 };
|
|
ret = this->RunChild(git_submodule_sync, &submodule_out, &submodule_err,
|
|
top_dir.c_str());
|
|
|
|
if (!ret) {
|
|
return false;
|
|
}
|
|
|
|
char const* git_submodule[] = { git, "submodule", "update", recursive, 0 };
|
|
return this->RunChild(git_submodule, &submodule_out, &submodule_err,
|
|
top_dir.c_str());
|
|
}
|
|
|
|
unsigned int cmCTestGIT::GetGitVersion()
|
|
{
|
|
if (!this->CurrentGitVersion) {
|
|
const char* git = this->CommandLineTool.c_str();
|
|
char const* git_version[] = { git, "--version", 0 };
|
|
std::string version;
|
|
OneLineParser version_out(this, "version-out> ", version);
|
|
OutputLogger version_err(this->Log, "version-err> ");
|
|
unsigned int v[4] = { 0, 0, 0, 0 };
|
|
if (this->RunChild(git_version, &version_out, &version_err) &&
|
|
sscanf(version.c_str(), "git version %u.%u.%u.%u", &v[0], &v[1], &v[2],
|
|
&v[3]) >= 3) {
|
|
this->CurrentGitVersion = cmCTestGITVersion(v[0], v[1], v[2], v[3]);
|
|
}
|
|
}
|
|
return this->CurrentGitVersion;
|
|
}
|
|
|
|
/* Diff format:
|
|
|
|
:src-mode dst-mode src-sha1 dst-sha1 status\0
|
|
src-path\0
|
|
[dst-path\0]
|
|
|
|
The format is repeated for every file changed. The [dst-path\0]
|
|
line appears only for lines with status 'C' or 'R'. See 'git help
|
|
diff-tree' for details.
|
|
*/
|
|
class cmCTestGIT::DiffParser : public cmCTestVC::LineParser
|
|
{
|
|
public:
|
|
DiffParser(cmCTestGIT* git, const char* prefix)
|
|
: LineParser('\0', false)
|
|
, GIT(git)
|
|
, DiffField(DiffFieldNone)
|
|
{
|
|
this->SetLog(&git->Log, prefix);
|
|
}
|
|
|
|
typedef cmCTestGIT::Change Change;
|
|
std::vector<Change> Changes;
|
|
|
|
protected:
|
|
cmCTestGIT* GIT;
|
|
enum DiffFieldType
|
|
{
|
|
DiffFieldNone,
|
|
DiffFieldChange,
|
|
DiffFieldSrc,
|
|
DiffFieldDst
|
|
};
|
|
DiffFieldType DiffField;
|
|
Change CurChange;
|
|
|
|
void DiffReset()
|
|
{
|
|
this->DiffField = DiffFieldNone;
|
|
this->Changes.clear();
|
|
}
|
|
|
|
bool ProcessLine() CM_OVERRIDE
|
|
{
|
|
if (this->Line[0] == ':') {
|
|
this->DiffField = DiffFieldChange;
|
|
this->CurChange = Change();
|
|
}
|
|
if (this->DiffField == DiffFieldChange) {
|
|
// :src-mode dst-mode src-sha1 dst-sha1 status
|
|
if (this->Line[0] != ':') {
|
|
this->DiffField = DiffFieldNone;
|
|
return true;
|
|
}
|
|
const char* src_mode_first = this->Line.c_str() + 1;
|
|
const char* src_mode_last = this->ConsumeField(src_mode_first);
|
|
const char* dst_mode_first = this->ConsumeSpace(src_mode_last);
|
|
const char* dst_mode_last = this->ConsumeField(dst_mode_first);
|
|
const char* src_sha1_first = this->ConsumeSpace(dst_mode_last);
|
|
const char* src_sha1_last = this->ConsumeField(src_sha1_first);
|
|
const char* dst_sha1_first = this->ConsumeSpace(src_sha1_last);
|
|
const char* dst_sha1_last = this->ConsumeField(dst_sha1_first);
|
|
const char* status_first = this->ConsumeSpace(dst_sha1_last);
|
|
const char* status_last = this->ConsumeField(status_first);
|
|
if (status_first != status_last) {
|
|
this->CurChange.Action = *status_first;
|
|
this->DiffField = DiffFieldSrc;
|
|
} else {
|
|
this->DiffField = DiffFieldNone;
|
|
}
|
|
} else if (this->DiffField == DiffFieldSrc) {
|
|
// src-path
|
|
if (this->CurChange.Action == 'C') {
|
|
// Convert copy to addition of destination.
|
|
this->CurChange.Action = 'A';
|
|
this->DiffField = DiffFieldDst;
|
|
} else if (this->CurChange.Action == 'R') {
|
|
// Convert rename to deletion of source and addition of destination.
|
|
this->CurChange.Action = 'D';
|
|
this->CurChange.Path = this->Line;
|
|
this->Changes.push_back(this->CurChange);
|
|
|
|
this->CurChange = Change('A');
|
|
this->DiffField = DiffFieldDst;
|
|
} else {
|
|
this->CurChange.Path = this->Line;
|
|
this->Changes.push_back(this->CurChange);
|
|
this->DiffField = this->DiffFieldNone;
|
|
}
|
|
} else if (this->DiffField == DiffFieldDst) {
|
|
// dst-path
|
|
this->CurChange.Path = this->Line;
|
|
this->Changes.push_back(this->CurChange);
|
|
this->DiffField = this->DiffFieldNone;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
const char* ConsumeSpace(const char* c)
|
|
{
|
|
while (*c && isspace(*c)) {
|
|
++c;
|
|
}
|
|
return c;
|
|
}
|
|
const char* ConsumeField(const char* c)
|
|
{
|
|
while (*c && !isspace(*c)) {
|
|
++c;
|
|
}
|
|
return c;
|
|
}
|
|
};
|
|
|
|
/* Commit format:
|
|
|
|
commit ...\n
|
|
tree ...\n
|
|
parent ...\n
|
|
author ...\n
|
|
committer ...\n
|
|
\n
|
|
Log message indented by (4) spaces\n
|
|
(even blank lines have the spaces)\n
|
|
[[
|
|
\n
|
|
[Diff format]
|
|
OR
|
|
\0
|
|
]]
|
|
|
|
The header may have more fields. See 'git help diff-tree'.
|
|
*/
|
|
class cmCTestGIT::CommitParser : public cmCTestGIT::DiffParser
|
|
{
|
|
public:
|
|
CommitParser(cmCTestGIT* git, const char* prefix)
|
|
: DiffParser(git, prefix)
|
|
, Section(SectionHeader)
|
|
{
|
|
this->Separator = SectionSep[this->Section];
|
|
}
|
|
|
|
private:
|
|
typedef cmCTestGIT::Revision Revision;
|
|
enum SectionType
|
|
{
|
|
SectionHeader,
|
|
SectionBody,
|
|
SectionDiff,
|
|
SectionCount
|
|
};
|
|
static char const SectionSep[SectionCount];
|
|
SectionType Section;
|
|
Revision Rev;
|
|
|
|
struct Person
|
|
{
|
|
std::string Name;
|
|
std::string EMail;
|
|
unsigned long Time;
|
|
long TimeZone;
|
|
Person()
|
|
: Name()
|
|
, EMail()
|
|
, Time(0)
|
|
, TimeZone(0)
|
|
{
|
|
}
|
|
};
|
|
|
|
void ParsePerson(const char* str, Person& person)
|
|
{
|
|
// Person Name <person@domain.com> 1234567890 +0000
|
|
const char* c = str;
|
|
while (*c && isspace(*c)) {
|
|
++c;
|
|
}
|
|
|
|
const char* name_first = c;
|
|
while (*c && *c != '<') {
|
|
++c;
|
|
}
|
|
const char* name_last = c;
|
|
while (name_last != name_first && isspace(*(name_last - 1))) {
|
|
--name_last;
|
|
}
|
|
person.Name.assign(name_first, name_last - name_first);
|
|
|
|
const char* email_first = *c ? ++c : c;
|
|
while (*c && *c != '>') {
|
|
++c;
|
|
}
|
|
const char* email_last = *c ? c++ : c;
|
|
person.EMail.assign(email_first, email_last - email_first);
|
|
|
|
person.Time = strtoul(c, (char**)&c, 10);
|
|
person.TimeZone = strtol(c, (char**)&c, 10);
|
|
}
|
|
|
|
bool ProcessLine() CM_OVERRIDE
|
|
{
|
|
if (this->Line.empty()) {
|
|
if (this->Section == SectionBody && this->LineEnd == '\0') {
|
|
// Skip SectionDiff
|
|
this->NextSection();
|
|
}
|
|
this->NextSection();
|
|
} else {
|
|
switch (this->Section) {
|
|
case SectionHeader:
|
|
this->DoHeaderLine();
|
|
break;
|
|
case SectionBody:
|
|
this->DoBodyLine();
|
|
break;
|
|
case SectionDiff:
|
|
this->DiffParser::ProcessLine();
|
|
break;
|
|
case SectionCount:
|
|
break; // never happens
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void NextSection()
|
|
{
|
|
this->Section = SectionType((this->Section + 1) % SectionCount);
|
|
this->Separator = SectionSep[this->Section];
|
|
if (this->Section == SectionHeader) {
|
|
this->GIT->DoRevision(this->Rev, this->Changes);
|
|
this->Rev = Revision();
|
|
this->DiffReset();
|
|
}
|
|
}
|
|
|
|
void DoHeaderLine()
|
|
{
|
|
// Look for header fields that we need.
|
|
if (cmHasLiteralPrefix(this->Line.c_str(), "commit ")) {
|
|
this->Rev.Rev = this->Line.c_str() + 7;
|
|
} else if (cmHasLiteralPrefix(this->Line.c_str(), "author ")) {
|
|
Person author;
|
|
this->ParsePerson(this->Line.c_str() + 7, author);
|
|
this->Rev.Author = author.Name;
|
|
this->Rev.EMail = author.EMail;
|
|
this->Rev.Date = this->FormatDateTime(author);
|
|
} else if (cmHasLiteralPrefix(this->Line.c_str(), "committer ")) {
|
|
Person committer;
|
|
this->ParsePerson(this->Line.c_str() + 10, committer);
|
|
this->Rev.Committer = committer.Name;
|
|
this->Rev.CommitterEMail = committer.EMail;
|
|
this->Rev.CommitDate = this->FormatDateTime(committer);
|
|
}
|
|
}
|
|
|
|
void DoBodyLine()
|
|
{
|
|
// Commit log lines are indented by 4 spaces.
|
|
if (this->Line.size() >= 4) {
|
|
this->Rev.Log += this->Line.substr(4);
|
|
}
|
|
this->Rev.Log += "\n";
|
|
}
|
|
|
|
std::string FormatDateTime(Person const& person)
|
|
{
|
|
// Convert the time to a human-readable format that is also easy
|
|
// to machine-parse: "CCYY-MM-DD hh:mm:ss".
|
|
time_t seconds = static_cast<time_t>(person.Time);
|
|
struct tm* t = gmtime(&seconds);
|
|
char dt[1024];
|
|
sprintf(dt, "%04d-%02d-%02d %02d:%02d:%02d", t->tm_year + 1900,
|
|
t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
|
|
std::string out = dt;
|
|
|
|
// Add the time-zone field "+zone" or "-zone".
|
|
char tz[32];
|
|
if (person.TimeZone >= 0) {
|
|
sprintf(tz, " +%04ld", person.TimeZone);
|
|
} else {
|
|
sprintf(tz, " -%04ld", -person.TimeZone);
|
|
}
|
|
out += tz;
|
|
return out;
|
|
}
|
|
};
|
|
|
|
char const cmCTestGIT::CommitParser::SectionSep[SectionCount] = { '\n', '\n',
|
|
'\0' };
|
|
|
|
void cmCTestGIT::LoadRevisions()
|
|
{
|
|
// Use 'git rev-list ... | git diff-tree ...' to get revisions.
|
|
std::string range = this->OldRevision + ".." + this->NewRevision;
|
|
const char* git = this->CommandLineTool.c_str();
|
|
const char* git_rev_list[] = { git, "rev-list", "--reverse",
|
|
range.c_str(), "--", 0 };
|
|
const char* git_diff_tree[] = {
|
|
git, "diff-tree", "--stdin", "--always", "-z",
|
|
"-r", "--pretty=raw", "--encoding=utf-8", 0
|
|
};
|
|
this->Log << this->ComputeCommandLine(git_rev_list) << " | "
|
|
<< this->ComputeCommandLine(git_diff_tree) << "\n";
|
|
|
|
cmsysProcess* cp = cmsysProcess_New();
|
|
cmsysProcess_AddCommand(cp, git_rev_list);
|
|
cmsysProcess_AddCommand(cp, git_diff_tree);
|
|
cmsysProcess_SetWorkingDirectory(cp, this->SourceDirectory.c_str());
|
|
|
|
CommitParser out(this, "dt-out> ");
|
|
OutputLogger err(this->Log, "dt-err> ");
|
|
this->RunProcess(cp, &out, &err);
|
|
|
|
// Send one extra zero-byte to terminate the last record.
|
|
out.Process("", 1);
|
|
|
|
cmsysProcess_Delete(cp);
|
|
}
|
|
|
|
void cmCTestGIT::LoadModifications()
|
|
{
|
|
const char* git = this->CommandLineTool.c_str();
|
|
|
|
// Use 'git update-index' to refresh the index w.r.t. the work tree.
|
|
const char* git_update_index[] = { git, "update-index", "--refresh", 0 };
|
|
OutputLogger ui_out(this->Log, "ui-out> ");
|
|
OutputLogger ui_err(this->Log, "ui-err> ");
|
|
this->RunChild(git_update_index, &ui_out, &ui_err);
|
|
|
|
// Use 'git diff-index' to get modified files.
|
|
const char* git_diff_index[] = { git, "diff-index", "-z", "HEAD", "--", 0 };
|
|
DiffParser out(this, "di-out> ");
|
|
OutputLogger err(this->Log, "di-err> ");
|
|
this->RunChild(git_diff_index, &out, &err);
|
|
|
|
for (std::vector<Change>::const_iterator ci = out.Changes.begin();
|
|
ci != out.Changes.end(); ++ci) {
|
|
this->DoModification(PathModified, ci->Path);
|
|
}
|
|
}
|