From 8276cb927bafd338be237adbecf437e70042da99 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Wed, 26 Apr 2017 15:52:15 +0200 Subject: Implement version module --- build2/version/snapshot-git.cxx | 106 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 build2/version/snapshot-git.cxx (limited to 'build2/version/snapshot-git.cxx') diff --git a/build2/version/snapshot-git.cxx b/build2/version/snapshot-git.cxx new file mode 100644 index 0000000..b5db470 --- /dev/null +++ b/build2/version/snapshot-git.cxx @@ -0,0 +1,106 @@ +// file : build2/version/snapshot-git.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#include + +using namespace std; + +namespace build2 +{ + namespace version + { + snapshot + extract_snapshot_git (const dir_path& src_root) + { + snapshot r; + const char* d (src_root.string ().c_str ()); + + // First check whether the working directory is clean. There doesn't + // seem to be a way to do everything in a single invocation (the + // porcelain v2 gives us the commit id but not timestamp). + // + + // If git status --porcelain returns anything, then the working + // directory is not clean. + // + { + const char* args[] {"git", "-C", d, "status", "--porcelain", nullptr}; + + if (!run (args, [] (string& s) {return move (s);}).empty ()) + return r; + } + + // Now extract the commit id and date. + // + auto extract = [&r] (string& s) -> snapshot + { + if (s.compare (0, 5, "tree ") == 0) + { + // The 16-characters abbreviated commit id. + // + r.id.assign (s, 5, 16); + + if (r.id.size () != 16) + fail << "unable to extract git commit id from '" << s << "'"; + } + else if (s.compare (0, 10, "committer ") == 0) + try + { + // The line format is: + // + // committer + // + // For example: + // + // committer John Doe 1493117819 +0200 + // + // The timestamp is in seconds since UNIX epoch. The timezone + // appears to be always numeric (+0000 for UTC). + // + size_t p1 (s.rfind (' ')); // Can't be npos. + string tz (s, p1 + 1); + + size_t p2 (s.rfind (' ', p1 - 1)); + if (p2 == string::npos) + throw invalid_argument ("missing timestamp"); + + string ts (s, p2 + 1, p1 - p2 - 1); + r.sn = stoull (ts); + + if (tz.size () != 5) + throw invalid_argument ("invalid timezone"); + + unsigned long h (stoul (string (tz, 1, 2))); + unsigned long m (stoul (string (tz, 3, 2))); + unsigned long s (h * 3600 + m * 60); + + // The timezone indicates where the timestamp was generated so + // to convert to UTC we need to invert the sign. + // + switch (tz[0]) + { + case '+': r.sn -= s; break; + case '-': r.sn += s; break; + default: throw invalid_argument ("invalid timezone sign"); + } + } + catch (const invalid_argument& e) + { + fail << "unable to extract git commit date from '" << s << "': " << e; + } + + return (r.id.empty () || r.sn == 0) ? snapshot () : move (r); + }; + + const char* args[] { + "git", "-C", d, "cat-file", "commit", "HEAD", nullptr}; + r = run (args, extract); + + if (r.empty ()) + fail << "unable to extract git commit id/date for " << src_root; + + return r; + } + } +} -- cgit v1.1