1
1
mirror of https://github.com/orhun/git-cliff.git synced 2024-09-11 15:05:30 +03:00
This commit is contained in:
DaniPopes 2024-06-24 20:37:17 +02:00 committed by GitHub
commit 6fb2295134
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 106 additions and 125 deletions

View File

@ -499,7 +499,7 @@ impl<'a> Changelog<'a> {
} }
/// Generates the changelog and writes it to the given output. /// Generates the changelog and writes it to the given output.
pub fn generate<W: Write>(&self, out: &mut W) -> Result<()> { pub fn generate<W: Write + ?Sized>(&self, out: &mut W) -> Result<()> {
debug!("Generating changelog..."); debug!("Generating changelog...");
let postprocessors = self let postprocessors = self
.config .config
@ -567,7 +567,7 @@ impl<'a> Changelog<'a> {
} }
/// Generates a changelog and prepends it to the given changelog. /// Generates a changelog and prepends it to the given changelog.
pub fn prepend<W: Write>( pub fn prepend<W: Write + ?Sized>(
&self, &self,
mut changelog: String, mut changelog: String,
out: &mut W, out: &mut W,
@ -582,7 +582,7 @@ impl<'a> Changelog<'a> {
} }
/// Prints the changelog context to the given output. /// Prints the changelog context to the given output.
pub fn write_context<W: Write>(&self, out: &mut W) -> Result<()> { pub fn write_context<W: Write + ?Sized>(&self, out: &mut W) -> Result<()> {
let output = Releases { let output = Releases {
releases: &self.releases, releases: &self.releases,
} }

View File

@ -55,14 +55,14 @@ impl Repository {
/// Sorts the commits by their time. /// Sorts the commits by their time.
pub fn commits( pub fn commits(
&self, &self,
range: Option<String>, range: Option<&str>,
include_path: Option<Vec<Pattern>>, include_path: Option<&[Pattern]>,
exclude_path: Option<Vec<Pattern>>, exclude_path: Option<&[Pattern]>,
) -> Result<Vec<Commit>> { ) -> Result<Vec<Commit>> {
let mut revwalk = self.inner.revwalk()?; let mut revwalk = self.inner.revwalk()?;
revwalk.set_sorting(Sort::TOPOLOGICAL)?; revwalk.set_sorting(Sort::TOPOLOGICAL)?;
if let Some(range) = range { if let Some(range) = range {
revwalk.push_range(&range)?; revwalk.push_range(range)?;
} else { } else {
revwalk.push_head()?; revwalk.push_head()?;
} }
@ -72,31 +72,31 @@ impl Repository {
.collect(); .collect();
if include_path.is_some() || exclude_path.is_some() { if include_path.is_some() || exclude_path.is_some() {
commits.retain(|commit| { commits.retain(|commit| {
if let Ok(prev_commit) = commit.parent(0) { let Ok(prev_commit) = commit.parent(0) else {
if let Ok(diff) = self.inner.diff_tree_to_tree( return false;
commit.tree().ok().as_ref(), };
prev_commit.tree().ok().as_ref(), let Ok(diff) = self.inner.diff_tree_to_tree(
None, commit.tree().ok().as_ref(),
) { prev_commit.tree().ok().as_ref(),
return diff None,
.deltas() ) else {
.filter_map(|delta| delta.new_file().path()) return false;
.any(|new_file_path| { };
if let Some(include_path) = &include_path { diff.deltas()
include_path .filter_map(|delta| delta.new_file().path())
.iter() .any(|new_file_path| {
.any(|glob| glob.matches_path(new_file_path)) if let Some(include_path) = include_path {
} else if let Some(exclude_path) = &exclude_path { return include_path
!exclude_path .iter()
.iter() .any(|glob| glob.matches_path(new_file_path));
.any(|glob| glob.matches_path(new_file_path)) }
} else { if let Some(exclude_path) = exclude_path {
false return !exclude_path
} .iter()
}); .any(|glob| glob.matches_path(new_file_path));
} }
} unreachable!()
false })
}); });
} }
Ok(commits) Ok(commits)

View File

@ -44,11 +44,8 @@ use std::fs::{
self, self,
File, File,
}; };
use std::io::{ use std::io;
self, use std::path::Path;
Write,
};
use std::path::PathBuf;
use std::time::{ use std::time::{
SystemTime, SystemTime,
UNIX_EPOCH, UNIX_EPOCH,
@ -88,31 +85,28 @@ fn process_repository<'a>(
let mut tags = repository.tags(&config.git.tag_pattern, args.topo_order)?; let mut tags = repository.tags(&config.git.tag_pattern, args.topo_order)?;
let skip_regex = config.git.skip_tags.as_ref(); let skip_regex = config.git.skip_tags.as_ref();
let ignore_regex = config.git.ignore_tags.as_ref(); let ignore_regex = config.git.ignore_tags.as_ref();
tags = tags tags.retain(|_, tag| {
.into_iter() let name = &tag.name;
.filter(|(_, tag)| {
let name = &tag.name;
// Keep skip tags to drop commits in the later stage. // Keep skip tags to drop commits in the later stage.
let skip = skip_regex.map(|r| r.is_match(name)).unwrap_or_default(); let skip = skip_regex.is_some_and(|r| r.is_match(name));
if skip {
return true;
}
let ignore = ignore_regex let ignore = ignore_regex.is_some_and(|r| {
.map(|r| { if r.as_str().trim().is_empty() {
if r.as_str().trim().is_empty() { return false;
return false; }
}
let ignore_tag = r.is_match(name); let ignore_tag = r.is_match(name);
if ignore_tag { if ignore_tag {
trace!("Ignoring release: {}", name) trace!("Ignoring release: {}", name)
} }
ignore_tag ignore_tag
}) });
.unwrap_or_default(); !ignore
});
skip || !ignore
})
.collect();
if !config.remote.github.is_set() { if !config.remote.github.is_set() {
match repository.upstream_remote() { match repository.upstream_remote() {
@ -213,14 +207,12 @@ fn process_repository<'a>(
} }
} }
let mut commits = repository.commits( let mut commits = repository.commits(
commit_range, commit_range.as_deref(),
args.include_path.clone(), args.include_path.as_deref(),
args.exclude_path.clone(), args.exclude_path.as_deref(),
)?; )?;
if let Some(commit_limit_value) = config.git.limit_commits { if let Some(commit_limit_value) = config.git.limit_commits {
commits = commits commits.truncate(commit_limit_value);
.drain(..commits.len().min(commit_limit_value))
.collect();
} }
// Update tags. // Update tags.
@ -239,25 +231,18 @@ fn process_repository<'a>(
// Process releases. // Process releases.
let mut releases = vec![Release::default()]; let mut releases = vec![Release::default()];
let mut release_index = 0;
let mut previous_release = Release::default(); let mut previous_release = Release::default();
let mut first_processed_tag = None; let mut first_processed_tag = None;
for git_commit in commits.iter().rev() { for git_commit in commits.iter().rev() {
let release = releases.last_mut().unwrap();
let commit = Commit::from(git_commit); let commit = Commit::from(git_commit);
let commit_id = commit.id.to_string(); let commit_id = commit.id.to_string();
if args.sort == Sort::Newest { release.commits.push(commit);
releases[release_index].commits.insert(0, commit);
} else {
releases[release_index].commits.push(commit);
}
if let Some(tag) = tags.get(&commit_id) { if let Some(tag) = tags.get(&commit_id) {
let tag_name = &tag.name; release.version = Some(tag.name.to_string());
release.message = tag.message.clone();
releases[release_index].version = Some(tag_name.clone()); release.commit_id = Some(commit_id);
releases[release_index].message = tag.message.clone(); release.timestamp = if args.tag.as_deref() == Some(tag.name.as_str()) {
releases[release_index].commit_id = Some(commit_id);
releases[release_index].timestamp = if args.tag == Some(tag_name.clone())
{
SystemTime::now() SystemTime::now()
.duration_since(UNIX_EPOCH)? .duration_since(UNIX_EPOCH)?
.as_secs() .as_secs()
@ -269,27 +254,32 @@ fn process_repository<'a>(
first_processed_tag = Some(tag); first_processed_tag = Some(tag);
} }
previous_release.previous = None; previous_release.previous = None;
releases[release_index].previous = Some(Box::new(previous_release)); release.previous = Some(Box::new(previous_release));
previous_release = releases[release_index].clone(); previous_release = release.clone();
releases.push(Release::default()); releases.push(Release::default());
release_index += 1;
} }
} }
if release_index > 0 { debug_assert!(!releases.is_empty());
if releases.len() > 1 {
previous_release.previous = None; previous_release.previous = None;
releases[release_index].previous = Some(Box::new(previous_release)); releases.last_mut().unwrap().previous = Some(Box::new(previous_release));
}
if args.sort == Sort::Newest {
for release in &mut releases {
release.commits.reverse();
}
} }
// Add custom commit messages to the latest release. // Add custom commit messages to the latest release.
if let Some(custom_commits) = &args.with_commit { if let Some(custom_commits) = &args.with_commit {
if let Some(latest_release) = releases.iter_mut().last() { releases
custom_commits.iter().for_each(|message| { .last_mut()
latest_release .unwrap()
.commits .commits
.push(Commit::from(message.to_string())) .extend(custom_commits.iter().cloned().map(Commit::from));
});
}
} }
// Set custom message for the latest release. // Set custom message for the latest release.
@ -300,12 +290,11 @@ fn process_repository<'a>(
} }
// Set the previous release if the first release does not have one set. // Set the previous release if the first release does not have one set.
if !releases.is_empty() && if releases[0]
releases .previous
.first() .as_ref()
.and_then(|r| r.previous.as_ref()) .and_then(|p| p.version.as_ref())
.and_then(|p| p.version.as_ref()) .is_none()
.is_none()
{ {
// Get the previous tag of the first processed tag in the release loop. // Get the previous tag of the first processed tag in the release loop.
let first_tag = first_processed_tag let first_tag = first_processed_tag
@ -547,6 +536,15 @@ pub fn run(mut args: Opt) -> Result<()> {
let mut changelog = Changelog::new(releases, &config)?; let mut changelog = Changelog::new(releases, &config)?;
// Print the result. // Print the result.
let mut out: Box<dyn io::Write> = if let Some(path) = &args.output {
if path == Path::new("-") {
Box::new(io::stdout())
} else {
Box::new(io::BufWriter::new(File::create(path)?))
}
} else {
Box::new(io::stdout())
};
if args.bump || args.bumped_version { if args.bump || args.bumped_version {
let next_version = if let Some(next_version) = changelog.bump_version()? { let next_version = if let Some(next_version) = changelog.bump_version()? {
next_version next_version
@ -559,40 +557,23 @@ pub fn run(mut args: Opt) -> Result<()> {
return Ok(()); return Ok(());
}; };
if args.bumped_version { if args.bumped_version {
if let Some(path) = args.output { writeln!(out, "{next_version}")?;
let mut output = File::create(path)?;
output.write_all(next_version.as_bytes())?;
} else {
println!("{next_version}");
}
return Ok(()); return Ok(());
} }
} }
if args.context { if args.context {
return if let Some(path) = args.output { changelog.write_context(&mut out)?;
let mut output = File::create(path)?; return Ok(());
changelog.write_context(&mut output)
} else {
changelog.write_context(&mut io::stdout())
};
} }
if let Some(ref path) = args.prepend {
changelog.prepend(fs::read_to_string(path)?, &mut File::create(path)?)?; if let Some(path) = &args.prepend {
let mut out = io::BufWriter::new(File::create(path)?);
changelog.prepend(fs::read_to_string(path)?, &mut out)?;
} }
if let Some(path) = args.output { if args.output.is_some() || args.prepend.is_none() {
let mut output: Box<dyn Write> = if path == PathBuf::from("-") { changelog.generate(&mut out)?;
Box::new(io::stdout())
} else {
Box::new(File::create(path)?)
};
if args.context {
changelog.write_context(&mut output)
} else {
changelog.generate(&mut output)
}
} else if args.prepend.is_none() {
changelog.generate(&mut io::stdout())
} else {
Ok(())
} }
Ok(())
} }