diff options
-rw-r--r-- | CMakeLists.txt | 2 | ||||
-rw-r--r-- | apt-pkg/solver3.cc | 117 | ||||
-rw-r--r-- | apt-pkg/solver3.h | 4 | ||||
-rw-r--r-- | doc/apt-verbatim.ent | 2 | ||||
-rw-r--r-- | doc/po/apt-doc.pot | 4 | ||||
-rw-r--r-- | po/apt-all.pot | 4 | ||||
-rwxr-xr-x | test/integration/test-apt-get-mark-auto | 30 | ||||
-rwxr-xr-x | test/integration/test-bug-549968-install-depends-of-not-installed | 2 | ||||
-rwxr-xr-x | test/integration/test-bug-960705-propagate-protected-to-satisfied-depends | 2 | ||||
-rwxr-xr-x | test/integration/test-dpkg-i-apt-install-fix-broken | 6 | ||||
-rwxr-xr-x | test/integration/test-solver3-dependencies | 65 |
11 files changed, 169 insertions, 69 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index a457110..1998ca8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -206,7 +206,7 @@ endif() # Configure some variables like package, version and architecture. set(PACKAGE ${PROJECT_NAME}) set(PACKAGE_MAIL "APT Development Team <deity@lists.debian.org>") -set(PACKAGE_VERSION "2.9.5") +set(PACKAGE_VERSION "2.9.6") string(REGEX MATCH "^[0-9.]+" PROJECT_VERSION ${PACKAGE_VERSION}) if (NOT DEFINED DPKG_DATADIR) diff --git a/apt-pkg/solver3.cc b/apt-pkg/solver3.cc index dc70adb..67aa68d 100644 --- a/apt-pkg/solver3.cc +++ b/apt-pkg/solver3.cc @@ -64,8 +64,9 @@ struct APT::Solver::CompareProviders3 /*{{{*/ // Try obsolete choices only after exhausting non-obsolete choices such that we install // packages replacing them and don't keep back upgrades depending on the replacement to // keep the obsolete package installed. - if (auto obsoleteAV = Solver.Obsolete(AV), obsoleteBV = Solver.Obsolete(BV); obsoleteAV != obsoleteBV) - return obsoleteBV; + if (upgrade) + if (auto obsoleteA = Solver.Obsolete(A), obsoleteB = Solver.Obsolete(B); obsoleteA != obsoleteB) + return obsoleteB; // Prefer MA:same packages if other architectures for it are installed if ((AV->MultiArch & pkgCache::Version::Same) == pkgCache::Version::Same || (BV->MultiArch & pkgCache::Version::Same) == pkgCache::Version::Same) @@ -154,8 +155,8 @@ class DefaultRootSetFunc2 : public pkgDepCache::DefaultRootSetFunc std::unique_ptr<APT::CacheFilter::Matcher> Kernels; public: - DefaultRootSetFunc2(pkgCache *cache) : Kernels(APT::KernelAutoRemoveHelper::GetProtectedKernelsFilter(cache)){}; - virtual ~DefaultRootSetFunc2(){}; + DefaultRootSetFunc2(pkgCache *cache) : Kernels(APT::KernelAutoRemoveHelper::GetProtectedKernelsFilter(cache)) {}; + virtual ~DefaultRootSetFunc2() {}; bool InRootSet(const pkgCache::PkgIterator &pkg) APT_OVERRIDE { return pkg.end() == false && ((*Kernels)(pkg) || DefaultRootSetFunc::InRootSet(pkg)); }; }; // FIXME: DEDUP with pkgDepCache. @@ -166,7 +167,7 @@ APT::Solver::Solver(pkgCache &cache, pkgDepCache::Policy &policy) policy(policy), pkgStates(cache.Head().PackageCount), verStates(cache.Head().VersionCount), - verObsolete(cache.Head().VersionCount) + pkgObsolete(cache.Head().PackageCount) { static_assert(sizeof(APT::Solver::State<pkgCache::PkgIterator>) == 3 * sizeof(int)); static_assert(sizeof(APT::Solver::State<pkgCache::VerIterator>) == 3 * sizeof(int)); @@ -246,23 +247,36 @@ std::string APT::Solver::WhyStr(Reason reason) return outstr; } -bool APT::Solver::Obsolete(pkgCache::VerIterator ver) +bool APT::Solver::Obsolete(pkgCache::PkgIterator pkg) { - if (verObsolete[ver->ID] != 0) - return verObsolete[ver->ID] == 2; + auto ver = policy.GetCandidateVer(pkg); + + if (ver.end() && not StrictPinning) + ver = pkg.VersionList(); + if (ver.end()) + { + std::cerr << "Obsolete: " << pkg.FullName() << " - not installable\n"; + return true; + } + if (pkgObsolete[pkg->ID] != 0) + return pkgObsolete[pkg->ID] == 2; for (auto bin = ver.Cache()->FindGrp(ver.SourcePkgName()).VersionsInSource(); not bin.end(); bin = bin.NextInSource()) - if (bin != ver && bin.ParentPkg()->Arch == ver.ParentPkg()->Arch && bin->ParentPkg != ver->ParentPkg && policy.GetCandidateVer(bin.ParentPkg()) == bin && _system->VS->CmpVersion(bin.SourceVerStr(), ver.SourceVerStr()) > 0) + if (bin != ver && bin.ParentPkg()->Arch == ver.ParentPkg()->Arch && bin->ParentPkg != ver->ParentPkg && (not StrictPinning || policy.GetCandidateVer(bin.ParentPkg()) == bin) && _system->VS->CmpVersion(bin.SourceVerStr(), ver.SourceVerStr()) > 0) { - verObsolete[ver->ID] = 2; + pkgObsolete[pkg->ID] = 2; + if (debug >= 3) + std::cerr << "Obsolete: " << ver.ParentPkg().FullName() << "=" << ver.VerStr() << " due to " << bin.ParentPkg().FullName() << "=" << bin.VerStr() << "\n"; return true; } for (auto file = ver.FileList(); !file.end(); file++) if ((file.File()->Flags & pkgCache::Flag::NotSource) == 0) { - verObsolete[ver->ID] = 1; + pkgObsolete[pkg->ID] = 1; return false; } - verObsolete[ver->ID] = 2; + if (debug >= 3) + std::cerr << "Obsolete: " << ver.ParentPkg().FullName() << "=" << ver.VerStr() << " - not installable\n"; + pkgObsolete[pkg->ID] = 2; return true; } @@ -292,7 +306,7 @@ bool APT::Solver::Install(pkgCache::PkgIterator Pkg, Reason reason, Group group) // Note decision if (unlikely(debug >= 1)) std::cerr << "[" << depth() << "] Install:" << Pkg.FullName() << " (" << WhyStr(reason) << ")\n"; - (*this)[Pkg] = {reason, depth(), Decision::MUST,}; + (*this)[Pkg] = {reason, depth(), Decision::MUST}; // Insert the work item. Work workItem{Reason(Pkg), depth(), group}; @@ -342,9 +356,9 @@ bool APT::Solver::Install(pkgCache::VerIterator Ver, Reason reason, Group group) // Note decision if (unlikely(debug >= 1)) std::cerr << "[" << depth() << "] Install:" << Ver.ParentPkg().FullName() << "=" << Ver.VerStr() << " (" << WhyStr(reason) << ")\n"; - (*this)[Ver] = {reason, depth(), Decision::MUST,}; + (*this)[Ver] = {reason, depth(), Decision::MUST}; if ((*this)[Ver.ParentPkg()].decision != Decision::MUST) - (*this)[Ver.ParentPkg()] = {Reason(Ver), depth(), Decision::MUST,}; + (*this)[Ver.ParentPkg()] = {Reason(Ver), depth(), Decision::MUST}; for (auto OV = Ver.ParentPkg().VersionList(); not OV.end(); ++OV) { @@ -381,7 +395,7 @@ bool APT::Solver::Reject(pkgCache::PkgIterator Pkg, Reason reason, Group group) // Reject the package and its versions. if (unlikely(debug >= 1)) std::cerr << "[" << depth() << "] Reject:" << Pkg.FullName() << " (" << WhyStr(reason) << ")\n"; - (*this)[Pkg] = {reason, depth(), Decision::MUSTNOT,}; + (*this)[Pkg] = {reason, depth(), Decision::MUSTNOT}; for (auto ver = Pkg.VersionList(); not ver.end(); ver++) if (not Reject(ver, Reason(Pkg), group)) return false; @@ -394,7 +408,7 @@ bool APT::Solver::Reject(pkgCache::PkgIterator Pkg, Reason reason, Group group) // \brief Do not install this version bool APT::Solver::Reject(pkgCache::VerIterator Ver, Reason reason, Group group) { - (void) group; + (void)group; if ((*this)[Ver].decision == Decision::MUSTNOT) return true; @@ -409,7 +423,7 @@ bool APT::Solver::Reject(pkgCache::VerIterator Ver, Reason reason, Group group) // Mark the package as rejected and propagate up as needed. if (unlikely(debug >= 1)) std::cerr << "[" << depth() << "] Reject:" << Ver.ParentPkg().FullName() << "=" << Ver.VerStr() << " (" << WhyStr(reason) << ")\n"; - (*this)[Ver] = {reason, depth(), Decision::MUSTNOT,}; + (*this)[Ver] = {reason, depth(), Decision::MUSTNOT}; if (auto pkg = Ver.ParentPkg(); (*this)[pkg].decision != Decision::MUSTNOT) { bool anyInstallable = false; @@ -480,7 +494,9 @@ bool APT::Solver::EnqueueOrGroup(pkgCache::DepIterator start, pkgCache::DepItera // below once we have calculated all possible solutions. if (start.ParentPkg()->CurrentVer == 0 && not policy.IsImportantDep(start)) return true; - + // Replaces and Enhances are not a real dependency. + if (start->Type == pkgCache::Dep::Replaces || start->Type == pkgCache::Dep::Enhances) + return true; if (unlikely(debug >= 3)) std::cerr << "Found dependency critical " << Ver.ParentPkg().FullName() << "=" << Ver.VerStr() << " -> " << start.TargetPkg().FullName() << "\n"; @@ -531,7 +547,7 @@ bool APT::Solver::EnqueueOrGroup(pkgCache::DepIterator start, pkgCache::DepItera { return pkgCache::VerIterator(cache, V).ParentPkg()->CurrentVer == 0; })) workItem.group = Group::SatisfyNew; if (std::any_of(workItem.solutions.begin(), workItem.solutions.end(), [this](auto V) -> auto - { return Obsolete(pkgCache::VerIterator(cache, V)); })) + { return Obsolete(pkgCache::VerIterator(cache, V).ParentPkg()); })) workItem.group = Group::SatisfyObsolete; // Try to perserve satisfied Recommends. FIXME: We should check if the Recommends was there in the installed version? if (workItem.optional && start.ParentPkg()->CurrentVer) @@ -904,10 +920,7 @@ bool APT::Solver::Solve() // \brief Apply the selections from the dep cache to the solver bool APT::Solver::FromDepCache(pkgDepCache &depcache) { - bool KeepAuto = not _config->FindB("APT::Get::AutomaticRemove"); - bool AllowRemove = _config->FindB("APT::Solver::Remove", true); - bool AllowInstall = _config->FindB("APT::Solver::Install", true); - bool AllowRemoveManual = _config->FindB("APT::Solver::RemoveManual", false); + bool AllowRemoveManual = AllowRemove && _config->FindB("APT::Solver::RemoveManual", false); DefaultRootSetFunc2 rootSet(&cache); for (auto P = cache.PkgBegin(); not P.end(); P++) @@ -916,10 +929,6 @@ bool APT::Solver::FromDepCache(pkgDepCache &depcache) continue; auto state = depcache[P]; - auto maybeInstall = state.Install() || (state.Keep() && P->CurrentVer); - auto reject = state.Delete() || (depcache[P].Keep() && not P->CurrentVer && depcache[P].Protect()); - auto isAuto = (depcache[P].Flags & pkgCache::Flag::Auto); - auto isOptional = isAuto || (AllowRemoveManual && not depcache[P].Protect()); if (P->SelectedState == pkgCache::State::Hold && not state.Protect()) { if (unlikely(debug >= 1)) @@ -927,45 +936,46 @@ bool APT::Solver::FromDepCache(pkgDepCache &depcache) if (P->CurrentVer ? not Install(P.CurrentVer(), {}, Group::HoldOrDelete) : not Reject(P, {}, Group::HoldOrDelete)) return false; } - else if (reject) + else if (state.Delete() // Normal delete request. + || (not P->CurrentVer && state.Keep() && state.Protect()) // Delete request of not installed package. + || (not P->CurrentVer && state.Keep() && not AllowInstall) // New package installs not allowed. + ) { if (unlikely(debug >= 1)) std::cerr << "Delete " << P.FullName() << "\n"; if (!Reject(P, {}, Group::HoldOrDelete)) return false; } - else if (maybeInstall && P->Flags & (pkgCache::Flag::Essential | pkgCache::Flag::Important)) - { - if (unlikely(debug >= 1)) - std::cerr << "ESSENTIAL " << P.FullName() << "\n"; - if (depcache[P].Keep() ? not Install(P, {}, Group::InstallManual) : not Install(depcache.GetCandidateVersion(P), {}, Group::InstallManual)) - return false; - } - else if (maybeInstall && not isOptional) - { - auto Upgrade = depcache.GetCandidateVersion(P) != P.CurrentVer(); - auto Group = (Upgrade ? Group::UpgradeManual : Group::InstallManual); - if (unlikely(debug >= 1)) - std::cerr << "MANUAL " << P.FullName() << "\n"; - if (depcache[P].Keep() ? not Install(P, {}, Group) : not Install(depcache.GetCandidateVersion(P), {}, Group)) - return false; - } - else if (maybeInstall && isOptional && (KeepAuto || rootSet.InRootSet(P) || not isAuto)) + else if (state.Install() || (state.Keep() && P->CurrentVer)) { + auto isEssential = P->Flags & (pkgCache::Flag::Essential | pkgCache::Flag::Important); + auto isAuto = (depcache[P].Flags & pkgCache::Flag::Auto); + auto isOptional = ((isAuto && AllowRemove) || AllowRemoveManual) && not isEssential && not depcache[P].Protect(); + auto Root = rootSet.InRootSet(P); auto Upgrade = depcache.GetCandidateVersion(P) != P.CurrentVer(); auto Group = isAuto ? (Upgrade ? Group::UpgradeAuto : Group::KeepAuto) : (Upgrade ? Group::UpgradeManual : Group::InstallManual); + + if (isAuto && not depcache[P].Protect() && not isEssential && not KeepAuto && not rootSet.InRootSet(P)) + { + if (unlikely(debug >= 1)) + std::cerr << "Ignore automatic install " << P.FullName() << " (" << (isEssential ? "E" : "") << (isAuto ? "M" : "") << (Root ? "R" : "") << ")" + << "\n"; + continue; + } if (unlikely(debug >= 1)) - std::cerr << "AUTOMATIC " << P.FullName() << (Upgrade ? " - upgrade" : "") << "\n"; + std::cerr << "Install " << P.FullName() << " (" << (isEssential ? "E" : "") << (isAuto ? "M" : "") << (Root ? "R" : "") << ")" + << "\n"; - if (not AllowRemove) + if (not isOptional) { + // Pre-empt the non-optional requests, as we don't want to queue them, we can just "unit propagate" here. if (depcache[P].Keep() ? not Install(P, {}, Group) : not Install(depcache.GetCandidateVersion(P), {}, Group)) return false; } else { - Work w{Reason(), depth(), Group, true, Upgrade}; + Work w{Reason(), depth(), Group, isOptional, Upgrade}; for (auto V = P.VersionList(); not V.end(); ++V) if (IsAllowedVersion(V)) w.solutions.push_back(V); @@ -973,13 +983,6 @@ bool APT::Solver::FromDepCache(pkgDepCache &depcache) AddWork(std::move(w)); } } - else if (P->CurrentVer == 0 && not AllowInstall) - { - if (unlikely(debug >= 1)) - std::cerr << "NOT ALLOWING INSTALL OF " << P.FullName() << "\n"; - if (not Reject(P, {}, Group::HoldOrDelete)) - return false; - } } return true; @@ -990,6 +993,8 @@ bool APT::Solver::ToDepCache(pkgDepCache &depcache) pkgDepCache::ActionGroup group(depcache); for (auto P = cache.PkgBegin(); not P.end(); P++) { + depcache[P].Marked = 0; + depcache[P].Garbage = 0; if ((*this)[P].decision == Decision::MUST) { for (auto V = P.VersionList(); not V.end(); V++) diff --git a/apt-pkg/solver3.h b/apt-pkg/solver3.h index 9a9d67a..d460c95 100644 --- a/apt-pkg/solver3.h +++ b/apt-pkg/solver3.h @@ -104,8 +104,8 @@ class Solver return verStates[V->ID]; } - std::vector<char> verObsolete; - bool Obsolete(pkgCache::VerIterator ver); + std::vector<char> pkgObsolete; + bool Obsolete(pkgCache::PkgIterator pkg); // \brief Heap of the remaining work. // diff --git a/doc/apt-verbatim.ent b/doc/apt-verbatim.ent index f849c0c..b2b38f9 100644 --- a/doc/apt-verbatim.ent +++ b/doc/apt-verbatim.ent @@ -274,7 +274,7 @@ "> <!-- this will be updated by 'prepare-release' --> -<!ENTITY apt-product-version "2.9.5"> +<!ENTITY apt-product-version "2.9.6"> <!-- (Code)names for various things used all over the place --> <!ENTITY debian-oldstable-codename "bullseye"> diff --git a/doc/po/apt-doc.pot b/doc/po/apt-doc.pot index 979992b..84ac0e8 100644 --- a/doc/po/apt-doc.pot +++ b/doc/po/apt-doc.pot @@ -5,9 +5,9 @@ #, fuzzy msgid "" msgstr "" -"Project-Id-Version: apt-doc 2.9.5\n" +"Project-Id-Version: apt-doc 2.9.6\n" "Report-Msgid-Bugs-To: APT Development Team <deity@lists.debian.org>\n" -"POT-Creation-Date: 2024-06-13 19:06+0000\n" +"POT-Creation-Date: 2024-07-02 20:27+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" diff --git a/po/apt-all.pot b/po/apt-all.pot index 51f7bf3..1f74e75 100644 --- a/po/apt-all.pot +++ b/po/apt-all.pot @@ -5,9 +5,9 @@ #, fuzzy msgid "" msgstr "" -"Project-Id-Version: apt 2.9.5\n" +"Project-Id-Version: apt 2.9.6\n" "Report-Msgid-Bugs-To: APT Development Team <deity@lists.debian.org>\n" -"POT-Creation-Date: 2024-06-13 19:06+0000\n" +"POT-Creation-Date: 2024-07-02 20:27+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" diff --git a/test/integration/test-apt-get-mark-auto b/test/integration/test-apt-get-mark-auto new file mode 100755 index 0000000..844412e --- /dev/null +++ b/test/integration/test-apt-get-mark-auto @@ -0,0 +1,30 @@ +#!/bin/sh +set -e + +TESTDIR="$(readlink -f "$(dirname "$0")")" +. "$TESTDIR/framework" +setupenvironment +configarchitecture 'amd64' + +insertpackage 'unstable' 'package' 'all' '3' + +setupaptarchive + +testsuccessequal "Reading package lists... +Building dependency tree... +The following NEW packages will be installed: + package +0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded. +Inst package (3 unstable [all]) +Conf package (3 unstable [all])" aptget install package --mark-auto -s + +# Specifically if we mark the package as automatically installed but also have auto-remove on, +# the package should still be installed... + +testsuccessequal "Reading package lists... +Building dependency tree... +The following NEW packages will be installed: + package +0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded. +Inst package (3 unstable [all]) +Conf package (3 unstable [all])" aptget install package --mark-auto --auto-remove -s diff --git a/test/integration/test-bug-549968-install-depends-of-not-installed b/test/integration/test-bug-549968-install-depends-of-not-installed index 39c86cc..f575f99 100755 --- a/test/integration/test-bug-549968-install-depends-of-not-installed +++ b/test/integration/test-bug-549968-install-depends-of-not-installed @@ -32,7 +32,7 @@ Conf coolstuff (1.0 unstable [all])" aptget install coolstuff extracoolstuff- -o testsuccessequal "Reading package lists... Building dependency tree... Package 'extracoolstuff' is not installed, so not removed -Solving dependencies...MANUAL coolstuff:i386 +Solving dependencies...Install coolstuff:i386 () [0] Install:coolstuff:i386=1.0 () Delete extracoolstuff:i386 [0] Reject:extracoolstuff:i386 () diff --git a/test/integration/test-bug-960705-propagate-protected-to-satisfied-depends b/test/integration/test-bug-960705-propagate-protected-to-satisfied-depends index da11fc8..6ffb69b 100755 --- a/test/integration/test-bug-960705-propagate-protected-to-satisfied-depends +++ b/test/integration/test-bug-960705-propagate-protected-to-satisfied-depends @@ -20,7 +20,7 @@ setupaptarchive testsuccessequal "Reading package lists... Building dependency tree... -Solving dependencies...MANUAL foobar:amd64 +Solving dependencies...Install foobar:amd64 () [0] Install:foobar:amd64=1 () [0] Install:requires-foo:amd64=1 (foobar:amd64=1) [0] Install:foo:amd64=1 (foobar:amd64=1 -> requires-foo:amd64=1) diff --git a/test/integration/test-dpkg-i-apt-install-fix-broken b/test/integration/test-dpkg-i-apt-install-fix-broken index 5c517aa..a4c3c66 100755 --- a/test/integration/test-dpkg-i-apt-install-fix-broken +++ b/test/integration/test-dpkg-i-apt-install-fix-broken @@ -14,13 +14,13 @@ setupaptarchive testfailure dpkg -i incoming/autopkgtest-*.deb testsuccessequal 'Reading package lists... Building dependency tree... -Correcting dependencies...MANUAL autopkgtest-satdep:amd64 +Correcting dependencies...Install autopkgtest-satdep:amd64 () [0] Install:autopkgtest-satdep:amd64 () [0] Install:autopkgtest-satdep:amd64=1 (autopkgtest-satdep:amd64) [0] Install:debhelper:amd64=1 (autopkgtest-satdep:amd64 -> autopkgtest-satdep:amd64=1) Done -Solving dependencies...AUTOMATIC debhelper:amd64 - upgrade -MANUAL autopkgtest-satdep:amd64 +Solving dependencies...Install debhelper:amd64 (M) +Install autopkgtest-satdep:amd64 () [0] Install:autopkgtest-satdep:amd64 () [0] Install:autopkgtest-satdep:amd64=1 (autopkgtest-satdep:amd64) [0] Install:debhelper:amd64=1 (autopkgtest-satdep:amd64 -> autopkgtest-satdep:amd64=1) diff --git a/test/integration/test-solver3-dependencies b/test/integration/test-solver3-dependencies new file mode 100755 index 0000000..330ded8 --- /dev/null +++ b/test/integration/test-solver3-dependencies @@ -0,0 +1,65 @@ +#!/bin/sh +set -e + +TESTDIR="$(readlink -f "$(dirname "$0")")" +. "$TESTDIR/framework" +setupenvironment +configarchitecture 'amd64' + +insertinstalledpackage 'replaces' 'all' '2' 'Replaces: replaced (<< 3)' +insertinstalledpackage 'replaced' 'all' '2' + +insertinstalledpackage 'enhances' 'all' '2' 'Enhances: enhanced (<< 3)' +insertinstalledpackage 'enhanced' 'all' '2' + +insertpackage 'unstable' 'replaces' 'all' '3' 'Replaces: replaced (<< 3)' +insertpackage 'unstable' 'enhances' 'all' '3' 'Enhances: enhanced (<< 3)' +insertpackage 'unstable' 'replaced' 'all' '3' +insertpackage 'unstable' 'enhanced' 'all' '3' + +setupaptarchive + +testsuccessequal "Reading package lists... +Building dependency tree... +Calculating upgrade... +The following packages will be upgraded: + enhanced enhances replaced replaces +4 upgraded, 0 newly installed, 0 to remove and 0 not upgraded. +Inst enhanced [2] (3 unstable [all]) +Inst enhances [2] (3 unstable [all]) +Inst replaced [2] (3 unstable [all]) +Inst replaces [2] (3 unstable [all]) +Conf enhanced (3 unstable [all]) +Conf enhances (3 unstable [all]) +Conf replaced (3 unstable [all]) +Conf replaces (3 unstable [all])" aptget upgrade -s + +testsuccessequal "Reading package lists... +Building dependency tree... +Calculating upgrade... +The following packages will be upgraded: + enhanced enhances replaced replaces +4 upgraded, 0 newly installed, 0 to remove and 0 not upgraded. +Inst enhanced [2] (3 unstable [all]) +Inst enhances [2] (3 unstable [all]) +Inst replaced [2] (3 unstable [all]) +Inst replaces [2] (3 unstable [all]) +Conf enhanced (3 unstable [all]) +Conf enhances (3 unstable [all]) +Conf replaced (3 unstable [all]) +Conf replaces (3 unstable [all])" apt upgrade -s + +testsuccessequal "Reading package lists... +Building dependency tree... +Calculating upgrade... +The following packages will be upgraded: + enhanced enhances replaced replaces +4 upgraded, 0 newly installed, 0 to remove and 0 not upgraded. +Inst enhanced [2] (3 unstable [all]) +Inst enhances [2] (3 unstable [all]) +Inst replaced [2] (3 unstable [all]) +Inst replaces [2] (3 unstable [all]) +Conf enhanced (3 unstable [all]) +Conf enhances (3 unstable [all]) +Conf replaced (3 unstable [all]) +Conf replaces (3 unstable [all])" apt dist-upgrade -s |