From e849457c16f7125918ad341e98df6e0e835dcc0b Mon Sep 17 00:00:00 2001 From: Sublime Git Bot Date: Fri, 9 Jan 2026 14:55:35 +0000 Subject: [PATCH] fix(changelog): use HEAD as fallback when version tag does not exist When generating changelogs during bump operations, the code was trying to use the new version tag as the to_ref for collecting commits. However, this tag does not exist yet at changelog generation time (Step 10), since tags are only created later (Step 12). This caused a ReferenceError when running: workspace bump --execute --git-commit --git-tag Changes: - Add reference_exists() helper method to check if a Git reference exists - Modify build_git_refs() to fallback to HEAD when the target version tag does not exist yet This ensures changelog generation works correctly in the normal bump workflow where changelogs are generated before Git tags are created. --- crates/pkg/src/changelog/generator.rs | 70 +++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 5 deletions(-) diff --git a/crates/pkg/src/changelog/generator.rs b/crates/pkg/src/changelog/generator.rs index 22604c01..d05630bf 100644 --- a/crates/pkg/src/changelog/generator.rs +++ b/crates/pkg/src/changelog/generator.rs @@ -836,19 +836,74 @@ impl ChangelogGenerator { Ok(changelog) } + /// Checks if a Git reference (tag, branch, or commit) exists in the repository. + /// + /// This method attempts to resolve a Git reference using `revparse_single`. + /// If the reference can be resolved, it exists; otherwise, it does not. + /// + /// # Arguments + /// + /// * `git_ref` - The Git reference to check (tag name, branch name, or commit SHA) + /// + /// # Returns + /// + /// Returns `true` if the reference exists and can be resolved, `false` otherwise. + /// + /// # Examples + /// + /// ```rust,ignore + /// # use sublime_pkg_tools::changelog::ChangelogGenerator; + /// # use sublime_pkg_tools::config::PackageToolsConfig; + /// # use sublime_git_tools::Repo; + /// # use sublime_standard_tools::filesystem::FileSystemManager; + /// # use std::path::PathBuf; + /// # + /// # async fn example() -> Result<(), Box> { + /// # let workspace_root = PathBuf::from("."); + /// # let git_repo = Repo::open(".")?; + /// # let fs = FileSystemManager::new(); + /// # let config = PackageToolsConfig::default(); + /// # + /// let generator = ChangelogGenerator::new( + /// workspace_root, + /// git_repo, + /// fs, + /// config.changelog, + /// ).await?; + /// + /// // Check if a tag exists + /// if generator.reference_exists("v1.0.0") { + /// println!("Tag v1.0.0 exists"); + /// } + /// + /// // HEAD always exists + /// assert!(generator.reference_exists("HEAD")); + /// # Ok(()) + /// # } + /// ``` + #[must_use] + fn reference_exists(&self, git_ref: &str) -> bool { + // Use the git_repo's internal repo to check if reference exists + // This is a simple check - if revparse_single succeeds, the reference exists + self.git_repo.get_diverged_commit(git_ref).is_ok() + } + /// Builds Git references for changelog generation. /// /// Creates the from and to references based on the configured tag format. + /// When the target version tag does not exist yet (common during changelog + /// generation before tags are created), this method automatically falls back + /// to using `HEAD` as the `to_ref`. /// /// # Arguments /// - /// * `package_name` - Optional package name - /// * `previous_version` - Optional previous version - /// * `current_version` - Current version + /// * `package_name` - Optional package name for scoped packages + /// * `previous_version` - Optional previous version string + /// * `current_version` - Current version string /// /// # Returns /// - /// A tuple of (from_ref, to_ref) strings. + /// A tuple of (from_ref, to_ref) strings representing Git references. /// /// # Errors /// @@ -866,12 +921,17 @@ impl ChangelogGenerator { }; // Build to_ref (current version tag) - let to_ref = if let Some(pkg_name) = package_name { + let to_ref_tag = if let Some(pkg_name) = package_name { format.replace("{name}", pkg_name).replace("{version}", current_version) } else { format.replace("{version}", current_version) }; + // Check if the to_ref tag exists; if not, use HEAD + // This handles the case where changelog is generated before tags are created + let to_ref = + if self.reference_exists(&to_ref_tag) { to_ref_tag } else { "HEAD".to_string() }; + // Build from_ref (previous version tag or HEAD) let from_ref = if let Some(prev_version) = previous_version { if let Some(pkg_name) = package_name {