From 94390f3d7545ecf51f81ded92118e0771abc206d 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 8d2b41c514..03ccfa7c41 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -44,6 +44,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 5639de0bf5..3766f2d9a9 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()),