Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ BeforeAll {
'export', 'fetch', 'find', 'format-feature-baseline', 'format-manifest', 'hash', 'help', 'install', 'integrate',
'license-report', 'list', 'new', 'owns', 'portsdiff', 'remove', 'search', 'update', 'upgrade', 'use', 'version',
'x-add-version', 'x-check-support', 'x-init-registry', 'x-package-info', 'x-regenerate', 'x-set-installed',
'x-test-features', 'x-update-baseline', 'x-update-registry', 'x-vsinstances'
'x-test-features', 'x-update-baseline', 'x-update-registry', 'x-vsinstances', 'x-baseline-diff'
)
CommonParameterList = @()
CommandOptionList = @{
Expand Down
12 changes: 12 additions & 0 deletions include/vcpkg/base/message-data.inc.h
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,10 @@ DECLARE_MESSAGE(BaselineConflict,
"",
"Specifying vcpkg-configuration.default-registry in a manifest file conflicts with built-in "
"baseline.\nPlease remove one of these conflicting settings.")
DECLARE_MESSAGE(BaselineDiffNoChange,
(),
"commit is a git commit here",
"No package that would have been installed was updated between the two commits")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"No package that would have been installed was updated between the two commits")
"No changes to installable packages between baselines")

I'm not sure if installable conveys the meaning but I'd prefer a terse description.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe installed? Because the packages listed in the vcpkg.json are normally installed.

DECLARE_MESSAGE(BaselineGitShowFailed,
(msg::commit_sha),
"",
Expand Down Expand Up @@ -599,6 +603,11 @@ DECLARE_MESSAGE(CmdAddVersionOptOverwriteVersion, (), "", "Overwrites git-tree o
DECLARE_MESSAGE(CmdAddVersionOptSkipFormatChk, (), "", "Skips the formatting check of vcpkg.json files")
DECLARE_MESSAGE(CmdAddVersionOptSkipVersionFormatChk, (), "", "Skips the version format check")
DECLARE_MESSAGE(CmdAddVersionOptVerbose, (), "", "Prints success messages rather than only errors")
DECLARE_MESSAGE(CmdBaselineDiffSynopsis,
(),
"",
"Computes the set of packages installed for different baselines and compares the versions that "
"would be installed")
Comment thread
autoantwort marked this conversation as resolved.
Outdated
DECLARE_MESSAGE(CmdBootstrapStandaloneSynopsis, (), "", "Bootstraps a vcpkg root from only a vcpkg binary")
DECLARE_MESSAGE(CmdBuildExternalExample1,
(),
Expand Down Expand Up @@ -1066,6 +1075,7 @@ DECLARE_MESSAGE(DependencyWillFail,
"'cascade' is a keyword and should not be translated",
"Dependency {feature_spec} will not build => cascade")
DECLARE_MESSAGE(DetectCompilerHash, (msg::triplet), "", "Detecting compiler hash for triplet {triplet}...")
DECLARE_MESSAGE(DirectDependencies, (), "", "Direct dependencies")
DECLARE_MESSAGE(DirectoriesRelativeToThePackageDirectoryHere,
(),
"",
Expand Down Expand Up @@ -2180,6 +2190,7 @@ DECLARE_MESSAGE(MissingDependency,
(msg::spec, msg::package_name),
"",
"Package {spec} is installed, but dependency {package_name} is not.")
DECLARE_MESSAGE(MissingManifestFile, (), "", "Could not find manifest file (vcpkg.json file)")
Comment thread
autoantwort marked this conversation as resolved.
Outdated
DECLARE_MESSAGE(MissingOption, (msg::option), "", "This command requires --{option}")
DECLARE_MESSAGE(MissingOrInvalidIdentifer, (), "", "missing or invalid identifier")
DECLARE_MESSAGE(MissingPortSuggestPullRequest,
Expand Down Expand Up @@ -2870,6 +2881,7 @@ DECLARE_MESSAGE(ToUpdatePackages,
"To update these packages and all dependencies, run\n{command_name} upgrade'")
DECLARE_MESSAGE(TrailingCommaInArray, (), "", "Trailing comma in array")
DECLARE_MESSAGE(TrailingCommaInObj, (), "", "Trailing comma in an object")
DECLARE_MESSAGE(TransitiveDependencies, (), "", "Transitive dependencies")
DECLARE_MESSAGE(TripletLabel, (), "", "Triplet:")
DECLARE_MESSAGE(TripletFileNotFound, (msg::triplet), "", "Triplet file {triplet}.cmake not found")
DECLARE_MESSAGE(TwoFeatureFlagsSpecified,
Expand Down
14 changes: 14 additions & 0 deletions include/vcpkg/commands.baseline-diff.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#pragma once

#include <vcpkg/fwd/triplet.h>
#include <vcpkg/fwd/vcpkgcmdarguments.h>
#include <vcpkg/fwd/vcpkgpaths.h>

namespace vcpkg
Comment thread
autoantwort marked this conversation as resolved.
{
Comment thread
autoantwort marked this conversation as resolved.
extern const CommandMetadata CommandBaselineDiffMetadata;
void command_baseline_diff_and_exit(const VcpkgCmdArguments& args,
const VcpkgPaths& paths,
vcpkg::Triplet default_triplet,
vcpkg::Triplet host_triplet);
}
6 changes: 6 additions & 0 deletions locales/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,8 @@
"AzcopyFailedToPutBlob": "azcopy failed to upload a file to {url} with exit code {exit_code} and http code {value}.",
"_AzcopyFailedToPutBlob.comment": "azcopy is the name of a program. {value} is an HTTP status code. An example of {exit_code} is 127. An example of {url} is https://github.com/microsoft/vcpkg.",
"BaselineConflict": "Specifying vcpkg-configuration.default-registry in a manifest file conflicts with built-in baseline.\nPlease remove one of these conflicting settings.",
"BaselineDiffNoChange": "No package that would have been installed was updated between the two commits",
Comment thread
autoantwort marked this conversation as resolved.
Outdated
"_BaselineDiffNoChange.comment": "commit is a git commit here",
Comment thread
autoantwort marked this conversation as resolved.
Outdated
"BaselineGitShowFailed": "while checking out baseline from commit '{commit_sha}', failed to `git show` versions/baseline.json. This may be fixed by fetching commits with `git fetch`.",
"_BaselineGitShowFailed.comment": "An example of {commit_sha} is 7cfad47ae9f68b183983090afd6337cd60fd4949.",
"BaselineMissing": "{package_name} is not assigned a version",
Expand Down Expand Up @@ -346,6 +348,7 @@
"CmdAddVersionOptSkipVersionFormatChk": "Skips the version format check",
"CmdAddVersionOptVerbose": "Prints success messages rather than only errors",
"CmdAddVersionSynopsis": "Adds a version to the version database",
"CmdBaselineDiffSynopsis": "Computes the set of packages installed for different baselines and compares the versions that would be installed",
"CmdBootstrapStandaloneSynopsis": "Bootstraps a vcpkg root from only a vcpkg binary",
"CmdBuildExample1": "vcpkg build <port spec>",
"_CmdBuildExample1.comment": "This is a command line, only the <>s part should be localized",
Expand Down Expand Up @@ -613,6 +616,7 @@
"_DependencyWillFail.comment": "'cascade' is a keyword and should not be translated An example of {feature_spec} is zlib[featurea,featureb].",
"DetectCompilerHash": "Detecting compiler hash for triplet {triplet}...",
"_DetectCompilerHash.comment": "An example of {triplet} is x64-windows.",
"DirectDependencies": "Direct dependencies",
"DirectoriesRelativeToThePackageDirectoryHere": "the directories are relative to ${{CURRENT_PACKAGES_DIR}} here",
"DllsRelativeToThePackageDirectoryHere": "the DLLs are relative to ${{CURRENT_PACKAGES_DIR}} here",
"DocumentedFieldsSuggestUpdate": "If these are documented fields that should be recognized try updating the vcpkg tool.",
Expand Down Expand Up @@ -1183,6 +1187,7 @@
"MissingClosingParen": "missing closing )",
"MissingDependency": "Package {spec} is installed, but dependency {package_name} is not.",
"_MissingDependency.comment": "An example of {spec} is zlib:x64-windows. An example of {package_name} is zlib.",
"MissingManifestFile": "Could not find manifest file (vcpkg.json file)",
"MissingOption": "This command requires --{option}",
"_MissingOption.comment": "An example of {option} is editable.",
"MissingOrInvalidIdentifer": "missing or invalid identifier",
Expand Down Expand Up @@ -1492,6 +1497,7 @@
"_TotalInstallTimeSuccess.comment": "An example of {elapsed} is 3.532 min.",
"TrailingCommaInArray": "Trailing comma in array",
"TrailingCommaInObj": "Trailing comma in an object",
"TransitiveDependencies": "Transitive dependencies",
"TripletFileNotFound": "Triplet file {triplet}.cmake not found",
"_TripletFileNotFound.comment": "An example of {triplet} is x64-windows.",
"TripletLabel": "Triplet:",
Expand Down
183 changes: 183 additions & 0 deletions src/vcpkg/commands.baseline-diff.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
#include <vcpkg/base/fwd/message_sinks.h>

#include <vcpkg/base/contractual-constants.h>
#include <vcpkg/base/files.h>
#include <vcpkg/base/messages.h>
#include <vcpkg/base/parse.h>
#include <vcpkg/base/path.h>
#include <vcpkg/base/strings.h>
#include <vcpkg/base/util.h>

#include <vcpkg/binarycaching.h>
#include <vcpkg/cmakevars.h>
#include <vcpkg/commands.baseline-diff.h>
#include <vcpkg/commands.install.h>
#include <vcpkg/configuration.h>
#include <vcpkg/dependencies.h>
#include <vcpkg/installeddatabase.h>
#include <vcpkg/installedpaths.h>
#include <vcpkg/paragraphs.h>
#include <vcpkg/portfileprovider.h>
#include <vcpkg/vcpkgcmdarguments.h>
#include <vcpkg/vcpkgpaths.h>

#include <map>
#include <memory>
#include <string>
#include <vector>

using namespace vcpkg;

namespace
{
constexpr CommandSwitch switches[] = {
{SwitchXNoDefaultFeatures, msgHelpTxtOptManifestNoDefault},
};

static constexpr CommandMultiSetting multisettings[] = {
{SwitchXFeature, msgHelpTxtOptManifestFeature},
};

bool print_lines(LocalizedString&& header, std::vector<std::string>&& lines)
{
if (!lines.empty())
{
Util::sort_unique_erase(lines);
msg::println(header.append_raw(':'));
Comment thread
autoantwort marked this conversation as resolved.
Outdated
for (const auto& line : lines)
{
msg::write_unlocalized_text(Color::none, line);
msg::write_unlocalized_text(Color::none, "\n");
}
msg::write_unlocalized_text(Color::none, "\n");
return true;
}
return false;
}

void check_for_valid_sha(StringView sha)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this use check_commit_exists (from git.h) instead?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see we can't do that in the general case because this kinda just blindly overwrites the baseline with a SHA and we don't necessarily have a git repo present for that.

However that makes this kinda broken because filesystem registries' baselines are not SHAs. We should only be checking that the input is a SHA when we know that registry has that constraint.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code is registry.h/cpp could really benefit from some comments. What is a LockFile and LockFile::lockdata maps what to what?

{
if (sha.size() < 7 || !Util::all_of(sha, ParserBase::is_hex_digit_lower))
{
Checks::msg_exit_with_error(VCPKG_LINE_INFO, msgInvalidCommitId, msg::commit_sha = sha);
}
Comment thread
autoantwort marked this conversation as resolved.
Outdated
}
Comment thread
autoantwort marked this conversation as resolved.
Outdated
Comment thread
autoantwort marked this conversation as resolved.
Outdated

} // unnamed namespace

namespace vcpkg
{
constexpr CommandMetadata CommandBaselineDiffMetadata{
"x-baseline-diff",
msgCmdBaselineDiffSynopsis,
{"vcpkg x-baseline-diff <old_commit_sha> <newer_commit_sha>",
"vcpkg x-baseline-diff $(git rev-parse 2026.02.27) $(git rev-parse 2026.03.18)"},

Copilot AI Apr 8, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per docs/command-guidelines.md, new commands should provide localized example messages (CmdBaselineDiffExample1, etc.) rather than only raw string literals in the examples list. This ensures examples appear localized/consistent in vcpkg help. Consider adding the example message keys to the message map and referencing them here (similar to other commands like x-add-version).

Suggested change
{"vcpkg x-baseline-diff <old_commit_sha> <newer_commit_sha>",
"vcpkg x-baseline-diff $(git rev-parse 2026.02.27) $(git rev-parse 2026.03.18)"},
{msgCmdBaselineDiffExample1, msgCmdBaselineDiffExample2},

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I must say that i am always very confused if such examples gets translated. Also only old and new would be translated here (The people that uses git also understand git without translations...).

@BillyONeal Billy O'Neal (BillyONeal) Apr 15, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general if there was "English" in the example we made it be translated, and if there was not we did not. So for instance, remove has one that is translated (vcpkg remove <package name>... because "package name" is translatable) and two that are not:

{msgCmdRemoveExample1, "vcpkg remove zlib zlib:x64-windows curl boost", "vcpkg remove --outdated"},

So I believe in this case the one that says <old_commit_sha> should get translated but the one with rev-parse should not. (However I'm not sure we should show the rev-parse example at all as that syntax makes shell assumptions)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should just accept the tags here rather than needing the user to say rev-parse? (I fixed a similar bug in #1935 )

Undocumented,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need a docs page for this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AutocompletePriority::Public,
2,
2,
{switches, {}, multisettings},
nullptr,
};
Comment thread
autoantwort marked this conversation as resolved.

void command_baseline_diff_and_exit(const VcpkgCmdArguments& args,
const VcpkgPaths& paths,
Triplet default_triplet,
Triplet host_triplet)
Comment thread
autoantwort marked this conversation as resolved.
{
const auto* manifest = paths.get_manifest();
if (manifest == nullptr)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I agree with this design: Because the user is explicitly passing in multiple baseline SHAs, it seems to really be more of a classic mode command as other registries aren't accounted for.

It seems like what you actually wanted is effectively this same diff but before/after what x-update-baseline does?

If this stays manifest only, it seems like one of the SHAs should be the one already in the manifest?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like what you actually wanted is effectively this same diff but before/after what x-update-baseline does?

Yes. I wanted to know what x-update-baseline does mean to me in terms of port versions, or what it would mean.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this stays manifest only, it seems like one of the SHAs should be the one already in the manifest?

At least if there is no second one. You are right, this would be more developer friendly.

{
Checks::msg_exit_with_message(VCPKG_LINE_INFO, msgMissingManifestFile);
}
auto options = args.parse_arguments(CommandBaselineDiffMetadata);
check_for_valid_sha(options.command_arguments.at(0));
check_for_valid_sha(options.command_arguments.at(1));
Comment thread
autoantwort marked this conversation as resolved.
Outdated

auto& fs = paths.get_filesystem();
InstalledDatabaseLock installed_lock{fs, paths.installed(), args.wait_for_lock, args.ignore_lock_failures};
auto var_provider_storage = CMakeVars::make_triplet_cmake_var_provider(paths, installed_lock);
auto& var_provider = *var_provider_storage;

auto configuration = paths.get_configuration();
bool is_default_builtin = configuration.instantiate_registry_set(paths)->is_default_builtin_registry();
auto manifest_scf = parse_manifest_scf_or_exit(*manifest, paths, is_default_builtin);

const auto& manifest_core = *manifest_scf->core_paragraph;
PackageSpec toplevel{manifest_core.name, default_triplet};
auto features = get_manifest_features(options, manifest_core, var_provider, toplevel, host_triplet);

auto dependencies = get_manifest_dependencies(*manifest_scf, features);

ActionPlan plan[2];
for (int i = 0; i < 2; ++i)
Comment thread
autoantwort marked this conversation as resolved.
Outdated
{
if (auto default_reg = configuration.config.default_reg.get())
{
default_reg->baseline = options.command_arguments.at(i);
Comment thread
autoantwort marked this conversation as resolved.
Outdated
}
else
{
RegistryConfig synthesized_registry;
synthesized_registry.kind = JsonIdBuiltin.to_string();
synthesized_registry.baseline = options.command_arguments.at(i);
Comment thread
autoantwort marked this conversation as resolved.
Outdated
configuration.config.default_reg.emplace(synthesized_registry);
}

auto registry_set = configuration.instantiate_registry_set(paths);
auto verprovider = make_versioned_portfile_provider(*registry_set);
auto baseprovider = make_baseline_provider(*registry_set);

auto extended_overlay_port_directories = paths.overlay_ports;

auto oprovider = make_manifest_provider(fs,
extended_overlay_port_directories,
manifest->path,
std::make_unique<SourceControlFile>(manifest_scf->clone()));
PackagesDirAssigner packages_dir_assigner{paths.packages()};
Comment thread
autoantwort marked this conversation as resolved.
Outdated
Comment thread
autoantwort marked this conversation as resolved.
const CreateInstallPlanOptions create_options{
nullptr, host_triplet, UnsupportedPortAction::Warn, UseHeadVersion::No, Editable::No};
plan[i] = create_versioned_install_plan(*verprovider,
*baseprovider,
*oprovider,
var_provider,
dependencies,
manifest_core.overrides,
toplevel,
packages_dir_assigner,
create_options)
.value_or_exit(VCPKG_LINE_INFO);
Comment thread
BillyONeal marked this conversation as resolved.
}

std::map<PackageSpec, std::string> versions;
for (const auto& action : plan[0].install_actions)
{
versions[action.spec] = action.version.to_string();
}
std::vector<std::string> user_requested;
std::vector<std::string> transitive;
for (const auto& action : plan[1].install_actions)
{
auto oldIter = versions.find(action.spec);
auto newVersion = action.version.to_string();
auto& vec = action.request_type == RequestType::USER_REQUESTED ? user_requested : transitive;
if (oldIter == versions.end())
{
vec.push_back(fmt::format("{}: new: {}", action.spec.name(), newVersion));
}
else if (oldIter->second != newVersion)
{
vec.push_back(fmt::format("{}: {} -> {}", action.spec.name(), oldIter->second, newVersion));
Comment thread
BillyONeal marked this conversation as resolved.
Comment thread
autoantwort marked this conversation as resolved.
}
}
Comment thread
BillyONeal marked this conversation as resolved.
Comment thread
autoantwort marked this conversation as resolved.
bool any_changes = false;
Comment thread
autoantwort marked this conversation as resolved.
any_changes |= print_lines(msg::format(msgDirectDependencies), std::move(user_requested));
any_changes |= print_lines(msg::format(msgTransitiveDependencies), std::move(transitive));
Comment thread
autoantwort marked this conversation as resolved.
Outdated
if (!any_changes)
{
msg::println(msgBaselineDiffNoChange);
}

Checks::exit_success(VCPKG_LINE_INFO);
}
} // namespace vcpkg
2 changes: 2 additions & 0 deletions src/vcpkg/commands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <vcpkg/commands.add-version.h>
#include <vcpkg/commands.add.h>
#include <vcpkg/commands.autocomplete.h>
#include <vcpkg/commands.baseline-diff.h>
#include <vcpkg/commands.bootstrap-standalone.h>
#include <vcpkg/commands.build-external.h>
#include <vcpkg/commands.build.h>
Expand Down Expand Up @@ -127,6 +128,7 @@ namespace vcpkg
{CommandInstallMetadata, command_install_and_exit},
{CommandRemoveMetadata, command_remove_and_exit},
{CommandTestFeaturesMetadata, command_test_features_and_exit},
{CommandBaselineDiffMetadata, command_baseline_diff_and_exit},
{CommandSetInstalledMetadata, command_set_installed_and_exit},
{CommandUpgradeMetadata, command_upgrade_and_exit},
{CommandZPrintConfigMetadata, command_z_print_config_and_exit},
Expand Down
Loading