From 2969a000735595b449a902110183f223e9cf0dd4 Mon Sep 17 00:00:00 2001 From: Ilya Grigoriev Date: Thu, 10 Aug 2023 17:04:54 -0700 Subject: [PATCH] cli version: Show date as part of the version next to the commit id The date comes from the commiter date of the commit. This is so that it's easy to tell at a glance how old a version is. To make it easier to get UTC date, we first obtain it as a Unix timestamp, which should always be UTC. This will make the interaction with Nix in a subsequent commit easier. --- cli/Cargo.toml | 1 + cli/build.rs | 46 ++++++++++++++++++++++++++++------- cli/tests/test_global_opts.rs | 4 ++- 3 files changed, 41 insertions(+), 10 deletions(-) diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 1444902a56..6647b8a115 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -32,6 +32,7 @@ name = "runner" [build-dependencies] cargo_metadata = { workspace = true } +chrono = { workspace = true } [dependencies] chrono = { workspace = true } diff --git a/cli/build.rs b/cli/build.rs index 2d626380f3..76feac9548 100644 --- a/cli/build.rs +++ b/cli/build.rs @@ -12,11 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. +extern crate chrono; + use std::path::Path; use std::process::Command; use std::str; use cargo_metadata::MetadataCommand; +use chrono::prelude::*; const GIT_HEAD_PATH: &str = "../.git/HEAD"; const JJ_OP_HEADS_PATH: &str = "../.jj/repo/op_heads/heads"; @@ -41,8 +44,16 @@ fn main() -> std::io::Result<()> { } println!("cargo:rerun-if-env-changed=NIX_JJ_GIT_HASH"); - if let Some(git_hash) = get_git_hash() { - println!("cargo:rustc-env=JJ_VERSION={}-{}", version, git_hash); + // TODO: timestamp can be "nix" + if let Some((git_hash, maybe_date)) = get_git_timestamp_and_hash() { + println!( + "cargo:rustc-env=JJ_VERSION={}-{}-{}", + version, + maybe_date + .map(|d| d.format("%Y%m%d").to_string()) + .unwrap_or_else(|| "dateunknown".to_string()), + git_hash + ); } else { println!("cargo:rustc-env=JJ_VERSION={}", version); } @@ -50,12 +61,27 @@ fn main() -> std::io::Result<()> { Ok(()) } -fn get_git_hash() -> Option { +/// Convert a string with a unix timestamp to a date +fn timestamp_to_date(ts_str: &str) -> Option> { + ts_str + .parse::() + .ok() + .and_then(|ts| DateTime::::from_timestamp(ts, 0)) +} + +/// Return the git hash and the committer timestamp +fn get_git_timestamp_and_hash() -> Option<(String, Option>)> { if let Some(nix_hash) = std::env::var("NIX_JJ_GIT_HASH") .ok() .filter(|s| !s.is_empty()) { - return Some(nix_hash); + return Some((nix_hash, None)); + } + + fn parse_timestamp_vbar_hash(bytes: &[u8]) -> (String, Option>) { + let s = str::from_utf8(bytes).unwrap().trim_end(); + let (ts_str, id) = s.split_once('|').unwrap(); + (id.to_owned(), timestamp_to_date(ts_str)) } if let Ok(output) = Command::new("jj") .args([ @@ -64,19 +90,21 @@ fn get_git_hash() -> Option { "log", "--no-graph", "-r=@-", - "-T=commit_id", + r#"-T=committer.timestamp().utc().format("%s") ++ "|" ++ commit_id"#, ]) .output() { if output.status.success() { - return Some(String::from_utf8(output.stdout).unwrap()); + return Some(parse_timestamp_vbar_hash(&output.stdout)); } } - if let Ok(output) = Command::new("git").args(["rev-parse", "HEAD"]).output() { + if let Ok(output) = Command::new("git") + .args(["log", "-1", "--format=%ct|%H", "HEAD"]) + .output() + { if output.status.success() { - let line = str::from_utf8(&output.stdout).unwrap(); - return Some(line.trim_end().to_owned()); + return Some(parse_timestamp_vbar_hash(&output.stdout)); } } diff --git a/cli/tests/test_global_opts.rs b/cli/tests/test_global_opts.rs index cf358b9b90..46af304529 100644 --- a/cli/tests/test_global_opts.rs +++ b/cli/tests/test_global_opts.rs @@ -48,7 +48,9 @@ fn test_version() { let sanitized = stdout.replace(|c: char| c.is_ascii_hexdigit(), "?"); let expected = [ "jj ?.??.?\n", - "jj ?.??.?-????????????????????????????????????????\n", + "jj ?.??.?-????????-????????????????????????????????????????\n", + // `dateunknown` turns into `??t?unknown` since d,a,e are hex digits. + "jj ?.??.?-??t?unknown-????????????????????????????????????????\n", ]; assert!( expected.contains(&sanitized.as_str()),