From 4511c1165b55c7df3770752b4b86e60bde9f0035 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ale=C5=A1=20Mat=C4=9Bj?= Date: Thu, 25 Jul 2024 08:15:30 +0200 Subject: [PATCH 1/6] Make `get_package_types()` from libdnf5::transaction::CompsGroup public So it can be used in to_replay() function. --- include/libdnf5/transaction/comps_group.hpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/include/libdnf5/transaction/comps_group.hpp b/include/libdnf5/transaction/comps_group.hpp index 4824ad451..c2e08c174 100644 --- a/include/libdnf5/transaction/comps_group.hpp +++ b/include/libdnf5/transaction/comps_group.hpp @@ -48,6 +48,11 @@ class LIBDNF_API CompsGroup : public TransactionItem { // @replaces libdnf:transaction/CompsGroupItem.hpp:method:CompsGroupItem.toStr() std::string to_string() const; + /// Get types of the packages to be installed with the group (related xml elements: ``) + /// + // @replaces libdnf:transaction/CompsGroupItem.hpp:method:CompsGroupItem.getPackageTypes() + libdnf5::comps::PackageType get_package_types() const noexcept; + CompsGroup(const CompsGroup & src); CompsGroup & operator=(const CompsGroup & src); CompsGroup(CompsGroup && src) noexcept; @@ -92,11 +97,6 @@ class LIBDNF_API CompsGroup : public TransactionItem { // @replaces libdnf:transaction/CompsGroupItem.hpp:method:CompsGroupItem.setTranslatedName(const std::string & value) LIBDNF_LOCAL void set_translated_name(const std::string & value); - /// Get types of the packages to be installed with the group (related xml elements: ``) - /// - // @replaces libdnf:transaction/CompsGroupItem.hpp:method:CompsGroupItem.getPackageTypes() - LIBDNF_LOCAL libdnf5::comps::PackageType get_package_types() const noexcept; - /// Set types of the packages to be installed with the group (related xml elements: ``) /// // @replaces libdnf:transaction/CompsGroupItem.hpp:method:CompsGroupItem.setPackageTypes(libdnf::CompsPackageType value) From 6a5277be46685ae37862d8f1a22aca9ae9321cdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ale=C5=A1=20Mat=C4=9Bj?= Date: Thu, 25 Jul 2024 06:43:17 +0200 Subject: [PATCH 2/6] Extract private API `to_replay()` for `libdnf5::transaction::Transaction` It will be used later for transaction redo. --- libdnf5/transaction/transaction.cpp | 41 +----------------------- libdnf5/transaction/transaction_sr.cpp | 43 ++++++++++++++++++++++++++ libdnf5/transaction/transaction_sr.hpp | 3 ++ 3 files changed, 47 insertions(+), 40 deletions(-) diff --git a/libdnf5/transaction/transaction.cpp b/libdnf5/transaction/transaction.cpp index 3a0f6f427..43917cbd1 100644 --- a/libdnf5/transaction/transaction.cpp +++ b/libdnf5/transaction/transaction.cpp @@ -326,46 +326,7 @@ void Transaction::finish(TransactionState state) { } std::string Transaction::serialize() { - TransactionReplay transaction_replay; - - for (const auto & pkg : get_packages()) { - PackageReplay package_replay; - - // Use to_nevra_string in order to have nevra wihtout epoch if it is 0 - package_replay.nevra = rpm::to_nevra_string(pkg); - package_replay.action = pkg.get_action(); - package_replay.reason = pkg.get_reason(); - package_replay.repo_id = pkg.get_repoid(); - //TODO(amatej): Add the group_id for reason change? - - transaction_replay.packages.push_back(package_replay); - } - - for (const auto & group : get_comps_groups()) { - GroupReplay group_replay; - - group_replay.group_id = group.to_string(); - group_replay.action = group.get_action(); - group_replay.reason = group.get_reason(); - group_replay.repo_id = group.get_repoid(); - group_replay.package_types = group.get_package_types(); - - transaction_replay.groups.push_back(group_replay); - } - - for (const auto & environment : get_comps_environments()) { - EnvironmentReplay environment_replay; - - environment_replay.environment_id = environment.to_string(); - environment_replay.action = environment.get_action(); - environment_replay.repo_id = environment.get_repoid(); - - transaction_replay.environments.push_back(environment_replay); - } - - ////TODO(amatej): potentially add modules - - return json_serialize(transaction_replay); + return json_serialize(to_replay(*this)); } diff --git a/libdnf5/transaction/transaction_sr.cpp b/libdnf5/transaction/transaction_sr.cpp index bba285c11..d809344bd 100644 --- a/libdnf5/transaction/transaction_sr.cpp +++ b/libdnf5/transaction/transaction_sr.cpp @@ -340,4 +340,47 @@ std::string json_serialize(const TransactionReplay & transaction_replay) { return json; } +TransactionReplay to_replay(libdnf5::transaction::Transaction & trans) { + TransactionReplay transaction_replay; + + for (const auto & pkg : trans.get_packages()) { + PackageReplay package_replay; + + // Use to_nevra_string in order to have nevra wihtout epoch if it is 0 + package_replay.nevra = rpm::to_nevra_string(pkg); + package_replay.action = pkg.get_action(); + package_replay.reason = pkg.get_reason(); + package_replay.repo_id = pkg.get_repoid(); + //TODO(amatej): Add the group_id for reason change? + + transaction_replay.packages.push_back(package_replay); + } + + for (const auto & group : trans.get_comps_groups()) { + GroupReplay group_replay; + + group_replay.group_id = group.to_string(); + group_replay.action = group.get_action(); + group_replay.reason = group.get_reason(); + group_replay.repo_id = group.get_repoid(); + group_replay.package_types = group.get_package_types(); + + transaction_replay.groups.push_back(group_replay); + } + + for (const auto & environment : trans.get_comps_environments()) { + EnvironmentReplay environment_replay; + + environment_replay.environment_id = environment.to_string(); + environment_replay.action = environment.get_action(); + environment_replay.repo_id = environment.get_repoid(); + + transaction_replay.environments.push_back(environment_replay); + } + + ////TODO(amatej): potentially add modules + + return transaction_replay; +} + } // namespace libdnf5::transaction diff --git a/libdnf5/transaction/transaction_sr.hpp b/libdnf5/transaction/transaction_sr.hpp index 3807a4ed9..8acf2341a 100644 --- a/libdnf5/transaction/transaction_sr.hpp +++ b/libdnf5/transaction/transaction_sr.hpp @@ -20,6 +20,8 @@ along with libdnf. If not, see . #ifndef LIBDNF5_TRANSACTION_TRANSACTION_REPLAY_HPP #define LIBDNF5_TRANSACTION_TRANSACTION_REPLAY_HPP +#include "libdnf5/transaction/transaction.hpp" + #include #include #include @@ -75,6 +77,7 @@ struct TransactionReplay { TransactionReplay parse_transaction_replay(const std::string & json_serialized_transaction); std::string json_serialize(const TransactionReplay & transaction_replay); +TransactionReplay to_replay(libdnf5::transaction::Transaction & trans); } // namespace libdnf5::transaction From dbcdad359f485f1115b1661a739c4e226d7ac245 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ale=C5=A1=20Mat=C4=9Bj?= Date: Thu, 25 Jul 2024 08:40:08 +0200 Subject: [PATCH 3/6] Add `history redo` command It adds a new goal API to redo a history transaction. --- dnf5/commands/history/history.cpp | 2 +- dnf5/commands/history/history_redo.cpp | 44 ++++++++++++++++++++++++-- dnf5/commands/history/history_redo.hpp | 5 +++ include/libdnf5/base/goal.hpp | 10 ++++++ libdnf5/base/goal.cpp | 16 ++++++++++ 5 files changed, 74 insertions(+), 3 deletions(-) diff --git a/dnf5/commands/history/history.cpp b/dnf5/commands/history/history.cpp index 791c516ce..3d86d0367 100644 --- a/dnf5/commands/history/history.cpp +++ b/dnf5/commands/history/history.cpp @@ -57,7 +57,7 @@ void HistoryCommand::register_subcommands() { software_management_commands_group->set_header("Software Management Commands:"); cmd.register_group(software_management_commands_group); register_subcommand(std::make_unique(get_context()), software_management_commands_group); - // register_subcommand(std::make_unique(get_context()), software_management_commands_group); + register_subcommand(std::make_unique(get_context()), software_management_commands_group); register_subcommand(std::make_unique(get_context()), software_management_commands_group); register_subcommand(std::make_unique(get_context()), software_management_commands_group); } diff --git a/dnf5/commands/history/history_redo.cpp b/dnf5/commands/history/history_redo.cpp index a329e0b40..618d9dab3 100644 --- a/dnf5/commands/history/history_redo.cpp +++ b/dnf5/commands/history/history_redo.cpp @@ -19,14 +19,54 @@ along with libdnf. If not, see . #include "history_redo.hpp" +#include "commands/history/transaction_id.hpp" +#include "dnf5/shared_options.hpp" + +#include + namespace dnf5 { using namespace libdnf5::cli; void HistoryRedoCommand::set_argument_parser() { - get_argument_parser_command()->set_description("Repeat transactions"); + get_argument_parser_command()->set_description("Repeat all actions from the specified transaction"); + transaction_specs = std::make_unique(*this, 1); + auto & ctx = get_context(); + transaction_specs->get_arg()->set_complete_hook_func(create_history_id_autocomplete(ctx)); + auto skip_unavailable = std::make_unique(*this); } -void HistoryRedoCommand::run() {} +void HistoryRedoCommand::configure() { + auto & context = get_context(); + context.set_load_system_repo(true); + context.set_load_available_repos(Context::LoadAvailableRepos::ENABLED); +} + +void HistoryRedoCommand::run() { + auto ts_specs = transaction_specs->get_value(); + libdnf5::transaction::TransactionHistory history(get_context().get_base()); + std::vector target_trans; + + target_trans = list_transactions_from_specs(history, ts_specs); + + if (target_trans.size() < 1) { + throw libdnf5::cli::CommandExitError(1, M_("No matching transaction ID found, exactly one required.")); + } + + if (target_trans.size() > 1) { + throw libdnf5::cli::CommandExitError(1, M_("Matched more than one transaction ID, exactly one required.")); + } + + auto goal = get_context().get_goal(); + // To enable removal of dependency packages not present in the selected transaction + // it requires allow_erasing. This will inform the user that additional removes + // are required and the transaction won't proceed without --ignore-extras. + goal->set_allow_erasing(true); + + auto settings = libdnf5::GoalJobSettings(); + settings.set_ignore_extras(true); + settings.set_ignore_installed(true); + goal->add_redo_transaction(target_trans[0], settings); +} } // namespace dnf5 diff --git a/dnf5/commands/history/history_redo.hpp b/dnf5/commands/history/history_redo.hpp index 3b54a0c36..42569ca94 100644 --- a/dnf5/commands/history/history_redo.hpp +++ b/dnf5/commands/history/history_redo.hpp @@ -21,6 +21,8 @@ along with libdnf. If not, see . #ifndef DNF5_COMMANDS_HISTORY_HISTORY_REDO_HPP #define DNF5_COMMANDS_HISTORY_HISTORY_REDO_HPP +#include "commands/history/arguments.hpp" + #include @@ -31,7 +33,10 @@ class HistoryRedoCommand : public Command { public: explicit HistoryRedoCommand(Context & context) : Command(context, "redo") {} void set_argument_parser() override; + void configure() override; void run() override; + + std::unique_ptr transaction_specs{nullptr}; }; diff --git a/include/libdnf5/base/goal.hpp b/include/libdnf5/base/goal.hpp index b625b174c..560cbb3c0 100644 --- a/include/libdnf5/base/goal.hpp +++ b/include/libdnf5/base/goal.hpp @@ -384,6 +384,16 @@ class LIBDNF_API Goal { const std::vector & transactions, const libdnf5::GoalJobSettings & settings = libdnf5::GoalJobSettings()); + /// @warning This method is experimental/unstable and should not be relied on. It may be removed without warning + /// Add redo request of history transaction to the goal. + /// Can be called only once per Goal. + /// + /// @param transaction A history transaction to be repeated. + /// @param settings A structure to override default goal settings. + void add_redo_transaction( + const libdnf5::transaction::Transaction & transaction, + const libdnf5::GoalJobSettings & settings = libdnf5::GoalJobSettings()); + /// When true it allows to remove installed packages to resolve dependency problems void set_allow_erasing(bool value); diff --git a/libdnf5/base/goal.cpp b/libdnf5/base/goal.cpp index 6d2afb189..c99dcc520 100644 --- a/libdnf5/base/goal.cpp +++ b/libdnf5/base/goal.cpp @@ -159,6 +159,7 @@ class Goal::Impl { GoalProblem add_reason_change_specs_to_goal(base::Transaction & transaction); GoalProblem resolve_reverted_transactions(base::Transaction & transaction); + GoalProblem resolve_redo_transaction(base::Transaction & transaction); std::pair add_install_to_goal( base::Transaction & transaction, GoalAction action, const std::string & spec, GoalJobSettings & settings); @@ -271,6 +272,7 @@ class Goal::Impl { std::unique_ptr> serialized_transaction; std::unique_ptr, GoalJobSettings>> revert_transactions; + std::unique_ptr> redo_transaction; }; Goal::Goal(const BaseWeakPtr & base) : p_impl(new Impl(base)) {} @@ -2950,6 +2952,13 @@ GoalProblem Goal::Impl::resolve_reverted_transactions(base::Transaction & transa return ret; } +GoalProblem Goal::Impl::resolve_redo_transaction(base::Transaction & transaction) { + if (!redo_transaction) { + return GoalProblem::NO_PROBLEM; + } + auto & [trans, settings] = *redo_transaction; + return add_replay_to_goal(transaction, transaction::to_replay(trans), settings); +} void Goal::Impl::add_paths_to_goal() { if (rpm_filepaths.empty()) { @@ -3093,6 +3102,7 @@ base::Transaction Goal::resolve() { // Both serialized and reverted transactions use TransactionReplay. ret |= p_impl->add_serialized_transaction_to_goal(transaction); ret |= p_impl->resolve_reverted_transactions(transaction); + ret |= p_impl->resolve_redo_transaction(transaction); p_impl->add_paths_to_goal(); @@ -3261,6 +3271,12 @@ void Goal::add_revert_transactions( std::make_unique, GoalJobSettings>>(transactions, settings); } +void Goal::add_redo_transaction( + const libdnf5::transaction::Transaction & transaction, const libdnf5::GoalJobSettings & settings) { + libdnf_user_assert(!p_impl->redo_transaction, "Redo transactions cannot be set multiple times."); + p_impl->redo_transaction = + std::make_unique>(transaction, settings); +} void Goal::reset() { p_impl->module_specs.clear(); From 7456326599d55ed5e0b5211f0a7c311098153368 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ale=C5=A1=20Mat=C4=9Bj?= Date: Thu, 25 Jul 2024 11:15:35 +0200 Subject: [PATCH 4/6] Add `override_reasons` to `GoalJobSettings` and use it in `redo` If a package is already installed in the requested version but with a different reason override the reason. This is useful when redoing an interrupted transaction. The longest part of a transaction is running rpm changes, saving reasons is done after rpm finishes. If a transaction is interrupted in the middle of the rpm part some rpms are changed but reasons are not updated at all. To handle this enforce the reasons in the selected transaction. --- dnf5/commands/history/history_redo.cpp | 6 ++++++ include/libdnf5/base/goal_elements.hpp | 13 +++++++++++-- libdnf5/base/goal.cpp | 17 +++++++++++++++-- libdnf5/base/goal_elements.cpp | 11 +++++++++++ 4 files changed, 43 insertions(+), 4 deletions(-) diff --git a/dnf5/commands/history/history_redo.cpp b/dnf5/commands/history/history_redo.cpp index 618d9dab3..e46ea10f1 100644 --- a/dnf5/commands/history/history_redo.cpp +++ b/dnf5/commands/history/history_redo.cpp @@ -66,6 +66,12 @@ void HistoryRedoCommand::run() { auto settings = libdnf5::GoalJobSettings(); settings.set_ignore_extras(true); settings.set_ignore_installed(true); + // If a package is already installed in the requested version but with a different reason override the reason. + // This is useful when redoing an interrupted transaction. + // The longest part of a transaction is running rpm changes, saving reasons is done after rpm finishes. If a transaction + // is interrupted in the middle of the rpm part some rpms are changed but reasons are not updated at all. To handle this + // enforce the reasons in the selected transaction. + settings.set_override_reasons(true); goal->add_redo_transaction(target_trans[0], settings); } diff --git a/include/libdnf5/base/goal_elements.hpp b/include/libdnf5/base/goal_elements.hpp index 36c4e04fb..096fee615 100644 --- a/include/libdnf5/base/goal_elements.hpp +++ b/include/libdnf5/base/goal_elements.hpp @@ -349,20 +349,29 @@ struct LIBDNF_API GoalJobSettings : public ResolveSpecSettings { void set_to_repo_ids(std::vector to_repo_ids); std::vector get_to_repo_ids() const; - /// If set to true, after resolving serialized or reverted transactions don't check for + /// If set to true, after resolving serialized, reverted or redo transactions don't check for /// extra packages pulled into the transaction. /// /// Default: false void set_ignore_extras(bool ignore_extras); bool get_ignore_extras() const; - /// If set to true, after resolving serialized or reverted transactions don't check for + /// If set to true, after resolving serialized, reverted or redo transactions don't check for /// installed packages matching those in the transactions. /// /// Default: false void set_ignore_installed(bool ignore_installed); bool get_ignore_installed() const; + /// If set to true, after resolving serialized, reverted or redo transactions override reasons + /// of already installed packages. + /// This option only has an effect if ignore_installed is set otherwise the transaction fails + /// when it contains already installed packages. + /// + /// Default: false + void set_override_reasons(bool override_reasons); + bool get_override_reasons() const; + private: friend class Goal; diff --git a/libdnf5/base/goal.cpp b/libdnf5/base/goal.cpp index c99dcc520..5a3067d7c 100644 --- a/libdnf5/base/goal.cpp +++ b/libdnf5/base/goal.cpp @@ -859,8 +859,21 @@ GoalProblem Goal::Impl::add_replay_to_goal( if (!query_na.empty() && is_installonly.empty()) { query_nevra.filter_installed(); - auto problem = query_nevra.empty() ? GoalProblem::INSTALLED_IN_DIFFERENT_VERSION - : GoalProblem::ALREADY_INSTALLED; + auto problem = GoalProblem::INSTALLED_IN_DIFFERENT_VERSION; + + if (!query_nevra.empty()) { + problem = GoalProblem::ALREADY_INSTALLED; + if (settings.get_override_reasons()) { + if ((*query_nevra.begin()).get_reason() != package_replay.reason) { + rpm_reason_change_specs.push_back(std::make_tuple( + package_replay.reason, + package_replay.nevra, + package_replay.group_id, + settings_per_package)); + } + } + } + auto log_level = libdnf5::Logger::Level::WARNING; if (!settings.get_ignore_installed()) { log_level = libdnf5::Logger::Level::ERROR; diff --git a/libdnf5/base/goal_elements.cpp b/libdnf5/base/goal_elements.cpp index d8e0be8b8..c59c5257b 100644 --- a/libdnf5/base/goal_elements.cpp +++ b/libdnf5/base/goal_elements.cpp @@ -177,6 +177,10 @@ class GoalJobSettings::Impl { /// Used by history undo, system upgrade, ... bool ignore_installed{false}; + /// For replaying transactions override reasons for already installed packages. + /// Used by history redo. + bool override_reasons{false}; + GoalUsedSetting used_skip_broken{GoalUsedSetting::UNUSED}; GoalUsedSetting used_skip_unavailable{GoalUsedSetting::UNUSED}; GoalUsedSetting used_best{GoalUsedSetting::UNUSED}; @@ -518,4 +522,11 @@ bool GoalJobSettings::get_ignore_installed() const { return p_impl->ignore_installed; } +void GoalJobSettings::set_override_reasons(bool override_reasons) { + p_impl->override_reasons = override_reasons; +} +bool GoalJobSettings::get_override_reasons() const { + return p_impl->override_reasons; +} + } // namespace libdnf5 From b09a0336dec386eb39761dc0dcb647889c71c3c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ale=C5=A1=20Mat=C4=9Bj?= Date: Thu, 25 Jul 2024 11:18:50 +0200 Subject: [PATCH 5/6] Add `redo` to and install history documentation --- dnf5.spec | 3 +-- doc/CMakeLists.txt | 3 +-- doc/commands/history.8.rst | 13 +++++++++---- doc/commands/index.rst | 3 +-- doc/conf.py.in | 2 +- doc/dnf5.8.rst | 12 ++++-------- 6 files changed, 17 insertions(+), 19 deletions(-) diff --git a/dnf5.spec b/dnf5.spec index 3d9eae939..5b4d73e43 100644 --- a/dnf5.spec +++ b/dnf5.spec @@ -296,8 +296,7 @@ It supports RPM packages, modulemd modules, and comps groups & environments. %{_mandir}/man8/dnf*-download.8.* %{_mandir}/man8/dnf*-environment.8.* %{_mandir}/man8/dnf*-group.8.* -# TODO(jkolarik): history is not ready yet -# %%{_mandir}/man8/dnf*-history.8.* +%{_mandir}/man8/dnf*-history.8.* %{_mandir}/man8/dnf*-info.8.* %{_mandir}/man8/dnf*-install.8.* %{_mandir}/man8/dnf*-leaves.8.* diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index 27052df06..de64b81aa 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -65,8 +65,7 @@ if(WITH_MAN) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/man/dnf5-download.8 DESTINATION share/man/man8) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/man/dnf5-environment.8 DESTINATION share/man/man8) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/man/dnf5-group.8 DESTINATION share/man/man8) - # TODO(jkolarik): history is not ready yet - # install(FILES ${CMAKE_CURRENT_BINARY_DIR}/man/dnf5-history.8 DESTINATION share/man/man8) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/man/dnf5-history.8 DESTINATION share/man/man8) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/man/dnf5-info.8 DESTINATION share/man/man8) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/man/dnf5-install.8 DESTINATION share/man/man8) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/man/dnf5-leaves.8 DESTINATION share/man/man8) diff --git a/doc/commands/history.8.rst b/doc/commands/history.8.rst index 9120b0fb9..15782c71b 100644 --- a/doc/commands/history.8.rst +++ b/doc/commands/history.8.rst @@ -16,8 +16,6 @@ You should have received a copy of the GNU General Public License along with libdnf. If not, see . -.. - # TODO(jkolarik): Command not ready yet in upstream ... .. _history_command_ref-label: @@ -54,6 +52,9 @@ Subcommands ``redo`` | Repeat the specified transaction. + | Automatically uses ``--ignore-extras`` and ``--ignore-installed``. + | Unlike the rest of history commands it overrides reasons for transaction packages that are already installed. + | This command is useful to finish interrupted transactons. ``rollback`` | Undo all transactions performed after the specified transaction. @@ -69,12 +70,16 @@ Options for ``list`` and ``info`` | Reverse the order of transactions in the output. -Options for ``undo`` -==================== +Options for ``undo``, ``rollback`` and ``redo`` +=============================================== ``--skip-unavailable`` | Allow skipping packages actions that are not possible perform. + +Options for ``undo`` and ``rollback`` +===================================== + ``--ignore-extras`` | Don't consider extra packages pulled into the transaction as errors. | They will still be reported as warnings. diff --git a/doc/commands/index.rst b/doc/commands/index.rst index 18154c957..38f548228 100644 --- a/doc/commands/index.rst +++ b/doc/commands/index.rst @@ -16,6 +16,7 @@ DNF5 Commands download.8 environment.8 group.8 + history.8 info.8 install.8 leaves.8 @@ -37,5 +38,3 @@ DNF5 Commands versionlock.8 .. - # TODO(jkolarik): history not ready yet - history.8 diff --git a/doc/conf.py.in b/doc/conf.py.in index f2d86d80b..593923fc9 100644 --- a/doc/conf.py.in +++ b/doc/conf.py.in @@ -122,7 +122,7 @@ man_pages = [ ('commands/download.8', 'dnf5-download', 'Download Command', AUTHORS, 8), ('commands/environment.8', 'dnf5-environment', 'Environment Command', AUTHORS, 8), ('commands/group.8', 'dnf5-group', 'Group Command', AUTHORS, 8), - # TODO(jkolarik): ('commands/history.8', 'dnf5-history', 'History Command', AUTHORS, 8), + ('commands/history.8', 'dnf5-history', 'History Command', AUTHORS, 8), ('commands/info.8', 'dnf5-info', 'Info Command', AUTHORS, 8), ('commands/install.8', 'dnf5-install', 'Install Command', AUTHORS, 8), ('commands/leaves.8', 'dnf5-leaves', 'Leaves Command', AUTHORS, 8), diff --git a/doc/dnf5.8.rst b/doc/dnf5.8.rst index d49f38ecf..19edb7f66 100644 --- a/doc/dnf5.8.rst +++ b/doc/dnf5.8.rst @@ -79,6 +79,9 @@ For more details see the separate man page for the specific command, f.e. ``man :ref:`group ` | Manage comps groups. +:ref:`history ` + | Manage transaction history. + :ref:`info ` | Provide detailed information about installed or available packages. @@ -136,11 +139,6 @@ For more details see the separate man page for the specific command, f.e. ``man :ref:`versionlock ` | Protect packages from updates to newer versions. -.. - # TODO(jkolarik): History command is not ready yet - :ref:`history ` - | Manage transaction history. - Plugin commands --------------- @@ -417,6 +415,7 @@ Commands: | :manpage:`dnf5-download(8)`, :ref:`Download command ` | :manpage:`dnf5-environment(8)`, :ref:`Environment command ` | :manpage:`dnf5-group(8)`, :ref:`Group command ` + | :manpage:`dnf5-history(8)`, :ref:`History command, ` | :manpage:`dnf5-info(8)`, :ref:`Info command ` | :manpage:`dnf5-install(8)`, :ref:`Install command ` | :manpage:`dnf5-leaves(8)`, :ref:`Leaves command ` @@ -436,9 +435,6 @@ Commands: | :manpage:`dnf5-upgrade(8)`, :ref:`Upgrade command ` | :manpage:`dnf5-versionlock(8)`, :ref:`Versionlock command ` -.. - # TODO(jkolarik): History command is not ready yet - | :manpage:`dnf5-history(8)`, :ref:`History command, ` Application Plugins: | :manpage:`dnf5-automatic(8)`, :ref:`Automatic command ` From 4887aa88444bdedba4a5d05c5c17075a80bc090a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ale=C5=A1=20Mat=C4=9Bj?= Date: Thu, 25 Jul 2024 12:40:14 +0200 Subject: [PATCH 6/6] replay: Allow up/down-grade if NA is already installed This makes a difference if `--ignore-installed` is set and the package is installed in a different version -> it upgrades/downgrades the package to the requested version. This matches dnf4 behavior. --- libdnf5/base/goal.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libdnf5/base/goal.cpp b/libdnf5/base/goal.cpp index 5a3067d7c..5cda9b8d1 100644 --- a/libdnf5/base/goal.cpp +++ b/libdnf5/base/goal.cpp @@ -888,7 +888,9 @@ GoalProblem Goal::Impl::add_replay_to_goal( package_replay.nevra, query_to_vec_of_nevra_str(query_na), log_level); - continue; + if (problem == GoalProblem::ALREADY_INSTALLED) { + continue; + } } } }