diff --git a/scm/Mercurial.cpp b/scm/Mercurial.cpp index a0b76cb5637a..b911b5336198 100644 --- a/scm/Mercurial.cpp +++ b/scm/Mercurial.cpp @@ -1,6 +1,9 @@ /* Copyright 2017-present Facebook, Inc. * Licensed under the Apache License, Version 2.0 */ #include "Mercurial.h" +#include +#include +#include #include "ChildProcess.h" #include "Logging.h" #include "watchman.h" @@ -8,6 +11,8 @@ // Capability indicating support for the mercurial SCM W_CAP_REG("scm-hg") +using namespace std::chrono; + namespace watchman { std::string hgExecutablePath() { @@ -260,4 +265,32 @@ SCM::StatusResult Mercurial::getFilesChangedBetweenCommits( return result; } +time_point Mercurial::getCommitDate( + w_string_piece commitId, + w_string requestId) const { + ChildProcess proc( + {hgExecutablePath(), "log", "-r", commitId.data(), "-T", "{date}\n"}, + makeHgOptions(requestId)); + auto outputs = proc.communicate(); + auto status = proc.wait(); + if (status) { + throw std::runtime_error(to( + "failed query for hg log; command returned with status ", + status, + " out=", + outputs.first, + " err=", + outputs.second)); + } + return Mercurial::convertCommitDate(outputs.first.c_str()); +} + +time_point Mercurial::convertCommitDate(const char* commitDate) { + double date; + if (std::sscanf(commitDate, "%lf", &date) != 1) { + throw std::runtime_error(to( + "failed to parse date value `", commitDate, "` into a double")); + } + return system_clock::from_time_t(date); +} } // namespace watchman diff --git a/scm/Mercurial.h b/scm/Mercurial.h index ee3afe4d2e90..7b228da1d997 100644 --- a/scm/Mercurial.h +++ b/scm/Mercurial.h @@ -24,6 +24,12 @@ class Mercurial : public SCM { w_string_piece commitA, w_string_piece commitB, w_string requestId = nullptr) const override; + std::chrono::time_point getCommitDate( + w_string_piece commitId, + w_string requestId = nullptr) const override; + // public for testing + static std::chrono::time_point convertCommitDate( + const char* commitDate); private: // Returns options for invoking hg diff --git a/scm/SCM.h b/scm/SCM.h index 8b387dfb2691..1b7b83a4bd29 100644 --- a/scm/SCM.h +++ b/scm/SCM.h @@ -2,6 +2,7 @@ * Licensed under the Apache License, Version 2.0 */ #pragma once #include "watchman_system.h" +#include #include #include #include "watchman_string.h" @@ -62,6 +63,12 @@ class SCM { w_string_piece commitB, w_string requestId = nullptr) const = 0; + // Compute the source control date associated with the specified + // commit. + virtual std::chrono::time_point getCommitDate( + w_string_piece commitId, + w_string requestId = nullptr) const = 0; + private: w_string rootPath_; w_string scmRoot_; diff --git a/tests/MercurialTest.cpp b/tests/MercurialTest.cpp new file mode 100644 index 000000000000..3c0bb32c4a2e --- /dev/null +++ b/tests/MercurialTest.cpp @@ -0,0 +1,22 @@ +/* Copyright 2016-present Facebook, Inc. + * Licensed under the Apache License, Version 2.0. */ + +#include "scm/Mercurial.h" +#include "thirdparty/tap.h" +#include "watchman.h" + +using namespace std::chrono; + +void test_convert_date() { + auto date = watchman::Mercurial::convertCommitDate("1529420960.025200"); + auto result = duration_cast(date.time_since_epoch()).count(); + auto expected = 1529420960; + ok(result == expected, "Expected %d but observed %d", expected, result); +} + +int main(int, char**) { + plan_tests(1); + test_convert_date(); + + return exit_status(); +} diff --git a/tests/TARGETS b/tests/TARGETS index 8f783ecdfb64..e23df5d55007 100644 --- a/tests/TARGETS +++ b/tests/TARGETS @@ -202,3 +202,13 @@ t_test( "//watchman:testsupport", ], ) + +t_test( + name = "mercurialTest", + srcs = [ + "MercurialTest.cpp", + ], + deps = [ + "//watchman:watchmanlib", + ], +) diff --git a/watcher/eden.cpp b/watcher/eden.cpp index 45f401a4ec93..20cd988891b6 100644 --- a/watcher/eden.cpp +++ b/watcher/eden.cpp @@ -561,6 +561,12 @@ class EdenWrappedSCM : public SCM { return inner_->getFilesChangedBetweenCommits(commitA, commitB); } + std::chrono::time_point getCommitDate( + w_string_piece commitId, + w_string requestId = nullptr) const override { + return inner_->getCommitDate(commitId, requestId); + } + static std::unique_ptr wrap(std::unique_ptr inner) { if (!inner) { return nullptr;