diff --git a/CHANGELOG.md b/CHANGELOG.md index 866a76d..6abc8e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Renamed CLI option `--reverse` to `--rotate` (`-r`). `--reverse` remains available as a hidden alias. +### Fixed + +- `--rotate` now also applies to `--svg` output. + ## [0.8.3] - 2026-10-29 diff --git a/src/print/svg.rs b/src/print/svg.rs index dc508dc..0cd4556 100644 --- a/src/print/svg.rs +++ b/src/print/svg.rs @@ -17,6 +17,19 @@ pub fn print_svg(graph: &GitGraph, settings: &Settings) -> Result Result Result Result Result (f32, f32) { (15.0 * (column as f32 + 1.0), 15.0 * (index as f32 + 1.0)) } + +#[cfg(test)] +mod tests { + use super::print_svg; + use git2::{Repository, Signature}; + use gleisbau::graph::Builder; + use gleisbau::print::format::CommitFormat; + use gleisbau::settings::{ + BranchOrder, BranchSettings, BranchSettingsDef, Characters, MergePatterns, Settings, + }; + use std::rc::Rc; + + /// Renders a repository with the commits "first" and then "second". + fn render(rotate: bool) -> String { + let dir = std::env::temp_dir().join(format!( + "git-graph-svg-test-{}-{rotate}", + std::process::id() + )); + let repo = Repository::init(&dir).unwrap(); + let signature = Signature::now("Test", "test@example.com").unwrap(); + let tree_id = repo.treebuilder(None).unwrap().write().unwrap(); + let tree = repo.find_tree(tree_id).unwrap(); + let first = repo + .commit(Some("HEAD"), &signature, &signature, "first", &tree, &[]) + .unwrap(); + let first = repo.find_commit(first).unwrap(); + repo.commit( + Some("HEAD"), + &signature, + &signature, + "second", + &tree, + &[&first], + ) + .unwrap(); + drop(first); + drop(tree); + + let settings = Rc::new(Settings { + reverse_commit_order: rotate, + debug: false, + compact: true, + colored: false, + include_remote: false, + format: CommitFormat::OneLine, + wrapping: None, + characters: Characters::thin(), + branch_order: BranchOrder::ShortestFirst(true), + branches: BranchSettings::from(BranchSettingsDef::simple()).unwrap(), + merge_patterns: MergePatterns::default(), + }); + let graph = Builder::new() + .with_repository(repo) + .with_settings(settings.clone()) + .build() + .unwrap(); + let svg = print_svg(&graph, &settings).unwrap(); + std::fs::remove_dir_all(&dir).unwrap(); + svg + } + + /// Returns the value of the attribute on the first element containing `marker`. + fn attr_near(svg: &str, marker: &str, attr: &str) -> f32 { + let end = svg.find(marker).unwrap(); + let start = svg[..end].rfind('<').unwrap(); + let element = &svg[start..end]; + let value = element.split(&format!("{attr}=\"")).nth(1).unwrap(); + value.split('"').next().unwrap().parse().unwrap() + } + + #[test] + fn svg_keeps_newest_commit_on_top_by_default() { + let svg = render(false); + assert!(attr_near(&svg, ">\nsecond", "y") < attr_near(&svg, ">\nfirst", "y")); + assert!(!svg.contains("matrix(1 0 0 -1")); + } + + #[test] + fn svg_rotate_puts_oldest_commit_on_top() { + let svg = render(true); + assert!(attr_near(&svg, ">\nfirst", "y") < attr_near(&svg, ">\nsecond", "y")); + // Commit dots and lines are mirrored around the same rows as the text. + assert!(svg.contains("transform=\"matrix(1 0 0 -1 0 45)\"")); + } +}