Skip to main content

grit_lib/
commit_pretty.rs

1//! Human-oriented commit one-line formats shared by porcelain commands.
2
3use crate::objects::ObjectId;
4
5/// Abbreviate `oid` to at most `abbrev_len` hex characters (minimum 4, maximum 40).
6///
7/// # Parameters
8///
9/// - `oid` — full commit object id.
10/// - `abbrev_len` — desired abbreviation length (clamped to 4..=40 and to the hex length).
11#[must_use]
12pub fn abbrev_hex(oid: &ObjectId, abbrev_len: usize) -> String {
13    let hex = oid.to_hex();
14    let n = abbrev_len.clamp(4, 40).min(hex.len());
15    hex[..n].to_owned()
16}
17
18fn parse_tz_offset_seconds(offset: &str) -> i64 {
19    if offset.len() < 5 {
20        return 0;
21    }
22    let sign = if offset.starts_with('-') { -1i64 } else { 1i64 };
23    let hours: i64 = offset[1..3].parse().unwrap_or(0);
24    let minutes: i64 = offset[3..5].parse().unwrap_or(0);
25    sign * (hours * 3600 + minutes * 60)
26}
27
28/// Format the author/committer date as `YYYY-MM-DD` in the commit's local timezone.
29///
30/// Matches Git's `DATE_SHORT` mode used by `--pretty=reference` (e.g. `2005-04-07`).
31#[must_use]
32pub fn format_short_date_from_ident(ident: &str) -> String {
33    let parts: Vec<&str> = ident.rsplitn(3, ' ').collect();
34    if parts.len() < 2 {
35        return ident.to_owned();
36    }
37    let ts_str = parts[1];
38    let offset_str = parts[0];
39    let Ok(ts) = ts_str.parse::<i64>() else {
40        return ident.to_owned();
41    };
42    let offset_secs = parse_tz_offset_seconds(offset_str);
43    let Ok(dt) = time::OffsetDateTime::from_unix_timestamp(ts + offset_secs) else {
44        return ident.to_owned();
45    };
46    let format = time::format_description::parse("[year]-[month]-[day]");
47    let Ok(fmt) = format else {
48        return ident.to_owned();
49    };
50    dt.format(&fmt).unwrap_or_else(|_| ident.to_owned())
51}
52
53/// One-line `reference` format: `abbrev (subject, YYYY-MM-DD)`.
54///
55/// Matches upstream `git show -s --pretty=reference` / sequencer `refer_to_commit` output.
56///
57/// # Parameters
58///
59/// - `subject_first_line` — first line of the commit message (no trailing newline).
60/// - `committer_ident` — raw `committer` header line (`Name <email> epoch tz`).
61/// - `abbrev_len` — abbreviation length for the hash (typically 7).
62#[must_use]
63pub fn format_reference_line(
64    oid: &ObjectId,
65    subject_first_line: &str,
66    committer_ident: &str,
67    abbrev_len: usize,
68) -> String {
69    let abbrev = abbrev_hex(oid, abbrev_len);
70    let date = format_short_date_from_ident(committer_ident);
71    format!("{abbrev} ({subject_first_line}, {date})")
72}