diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2017-04-26 15:52:15 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2017-04-26 15:53:23 +0200 |
commit | 8276cb927bafd338be237adbecf437e70042da99 (patch) | |
tree | 49218b58e1f50a65b58674177e6047f1ac9f6831 /build2/version/snapshot-git.cxx | |
parent | 2fd9d3f177429b20797897360931badedbb0f0ef (diff) |
Implement version module
Diffstat (limited to 'build2/version/snapshot-git.cxx')
-rw-r--r-- | build2/version/snapshot-git.cxx | 106 |
1 files changed, 106 insertions, 0 deletions
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 <build2/version/snapshot> + +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<string> (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 <noise> <timestamp> <timezone> + // + // For example: + // + // committer John Doe <john@example.org> 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<snapshot> (args, extract); + + if (r.empty ()) + fail << "unable to extract git commit id/date for " << src_root; + + return r; + } + } +} |