-
-
Notifications
You must be signed in to change notification settings - Fork 321
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat:
bit revision list --svg
to create a visual graph of commits.
It's mainly a test of how well `layout-rs` performs.
- Loading branch information
Showing
7 changed files
with
207 additions
and
44 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,42 +1,140 @@ | ||
use crate::OutputFormat; | ||
use std::ffi::OsString; | ||
use std::path::PathBuf; | ||
|
||
use anyhow::{bail, Context}; | ||
use gix::traverse::commit::Sorting; | ||
pub struct Context { | ||
pub limit: Option<usize>, | ||
pub spec: OsString, | ||
pub format: OutputFormat, | ||
pub text: Format, | ||
} | ||
|
||
use crate::OutputFormat; | ||
pub enum Format { | ||
Text, | ||
Svg { path: PathBuf }, | ||
} | ||
pub const PROGRESS_RANGE: std::ops::RangeInclusive<u8> = 0..=2; | ||
|
||
pub fn list( | ||
mut repo: gix::Repository, | ||
spec: OsString, | ||
mut out: impl std::io::Write, | ||
format: OutputFormat, | ||
) -> anyhow::Result<()> { | ||
if format != OutputFormat::Human { | ||
bail!("Only human output is currently supported"); | ||
} | ||
repo.object_cache_size_if_unset(4 * 1024 * 1024); | ||
|
||
let spec = gix::path::os_str_into_bstr(&spec)?; | ||
let id = repo | ||
.rev_parse_single(spec) | ||
.context("Only single revisions are currently supported")?; | ||
let commits = id | ||
.object()? | ||
.peel_to_kind(gix::object::Kind::Commit) | ||
.context("Need commitish as starting point")? | ||
.id() | ||
.ancestors() | ||
.sorting(Sorting::ByCommitTimeNewestFirst) | ||
.all()?; | ||
for commit in commits { | ||
let commit = commit?; | ||
writeln!( | ||
out, | ||
"{} {} {}", | ||
commit.id().shorten_or_id(), | ||
commit.commit_time.expect("traversal with date"), | ||
commit.parent_ids.len() | ||
)?; | ||
pub(crate) mod function { | ||
use anyhow::{bail, Context}; | ||
use gix::traverse::commit::Sorting; | ||
use std::collections::HashMap; | ||
|
||
use gix::Progress; | ||
use layout::backends::svg::SVGWriter; | ||
use layout::core::base::Orientation; | ||
use layout::core::geometry::Point; | ||
use layout::core::style::StyleAttr; | ||
use layout::std_shapes::shapes::{Arrow, Element, ShapeKind}; | ||
|
||
use crate::repository::revision::list::Format; | ||
use crate::OutputFormat; | ||
|
||
pub fn list( | ||
mut repo: gix::Repository, | ||
mut progress: impl Progress, | ||
mut out: impl std::io::Write, | ||
super::Context { | ||
spec, | ||
format, | ||
text, | ||
limit, | ||
}: super::Context, | ||
) -> anyhow::Result<()> { | ||
if format != OutputFormat::Human { | ||
bail!("Only human output is currently supported"); | ||
} | ||
repo.object_cache_size_if_unset(4 * 1024 * 1024); | ||
|
||
let spec = gix::path::os_str_into_bstr(&spec)?; | ||
let id = repo | ||
.rev_parse_single(spec) | ||
.context("Only single revisions are currently supported")?; | ||
let commits = id | ||
.object()? | ||
.peel_to_kind(gix::object::Kind::Commit) | ||
.context("Need commitish as starting point")? | ||
.id() | ||
.ancestors() | ||
.sorting(Sorting::ByCommitTimeNewestFirst) | ||
.all()?; | ||
|
||
let mut vg = match text { | ||
Format::Svg { path } => ( | ||
layout::topo::layout::VisualGraph::new(Orientation::TopToBottom), | ||
path, | ||
HashMap::new(), | ||
) | ||
.into(), | ||
Format::Text => None, | ||
}; | ||
progress.init(None, gix::progress::count("commits")); | ||
progress.set_name("traverse"); | ||
|
||
let start = std::time::Instant::now(); | ||
for commit in commits { | ||
if gix::interrupt::is_triggered() { | ||
bail!("interrupted by user"); | ||
} | ||
let commit = commit?; | ||
match vg.as_mut() { | ||
Some((vg, _path, map)) => { | ||
let pt = Point::new(100., 30.); | ||
let source = match map.get(&commit.id) { | ||
Some(handle) => *handle, | ||
None => { | ||
let name = commit.id().shorten_or_id().to_string(); | ||
let shape = ShapeKind::new_box(name.as_str()); | ||
let style = StyleAttr::simple(); | ||
let handle = vg.add_node(Element::create(shape, style, Orientation::LeftToRight, pt)); | ||
map.insert(commit.id, handle); | ||
handle | ||
} | ||
}; | ||
|
||
for parent_id in commit.parent_ids() { | ||
let dest = match map.get(parent_id.as_ref()) { | ||
Some(handle) => *handle, | ||
None => { | ||
let name = parent_id.shorten_or_id().to_string(); | ||
let shape = ShapeKind::new_box(name.as_str()); | ||
let style = StyleAttr::simple(); | ||
let dest = vg.add_node(Element::create(shape, style, Orientation::LeftToRight, pt)); | ||
map.insert(parent_id.detach(), dest); | ||
dest | ||
} | ||
}; | ||
let arrow = Arrow::simple(""); | ||
vg.add_edge(arrow, source, dest); | ||
} | ||
} | ||
None => { | ||
writeln!( | ||
out, | ||
"{} {} {}", | ||
commit.id().shorten_or_id(), | ||
commit.commit_time.expect("traversal with date"), | ||
commit.parent_ids.len() | ||
)?; | ||
} | ||
} | ||
progress.inc(); | ||
if limit.map_or(false, |limit| limit == progress.step()) { | ||
break; | ||
} | ||
} | ||
|
||
progress.show_throughput(start); | ||
if let Some((mut vg, path, _)) = vg { | ||
let start = std::time::Instant::now(); | ||
progress.set_name("computing graph"); | ||
progress.info(format!("writing {path:?}…")); | ||
let mut svg = SVGWriter::new(); | ||
vg.do_it(false, false, false, &mut svg); | ||
std::fs::write(&path, svg.finalize().as_bytes())?; | ||
open::that(path)?; | ||
progress.show_throughput(start); | ||
} | ||
Ok(()) | ||
} | ||
Ok(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters