summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apt-pkg/algorithms.cc20
-rw-r--r--apt-pkg/algorithms.h2
-rw-r--r--apt-pkg/cachefilter-patterns.cc4
-rw-r--r--apt-pkg/cachefilter-patterns.h19
-rw-r--r--apt-pkg/cacheiterators.h1
-rw-r--r--apt-pkg/depcache.cc55
-rw-r--r--apt-pkg/depcache.h6
-rw-r--r--apt-pkg/pkgcache.cc25
-rw-r--r--apt-pkg/upgrade.cc118
-rw-r--r--apt-private/private-install.cc17
-rw-r--r--apt-private/private-output.cc10
-rw-r--r--apt-private/private-output.h1
-rw-r--r--debian/libapt-pkg6.0.symbols4
-rw-r--r--doc/apt-patterns.7.xml6
-rwxr-xr-xtest/integration/test-phased-updates-new-depends6
-rwxr-xr-xtest/integration/test-phased-updates-upgrade51
-rwxr-xr-xtest/integration/test-ubuntu-bug-2025462-phased-dist-upgrade4
17 files changed, 227 insertions, 122 deletions
diff --git a/apt-pkg/algorithms.cc b/apt-pkg/algorithms.cc
index 3d4096a94..d841d6dd8 100644
--- a/apt-pkg/algorithms.cc
+++ b/apt-pkg/algorithms.cc
@@ -1246,6 +1246,26 @@ bool pkgProblemResolver::InstOrNewPolicyBroken(pkgCache::PkgIterator I)
return false;
}
/*}}}*/
+// ProblemResolver::KeepPhasedUpdates - Keep back phased updates /*{{{*/
+// ---------------------------------------------------------------------
+// Hold back upgrades to phased versions of already installed packages, unless
+// they are security updates
+bool pkgProblemResolver::KeepPhasedUpdates()
+{
+ for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I)
+ {
+ if (not Cache.PhasingApplied(I))
+ continue;
+
+ Cache.MarkKeep(I, false, false);
+ Cache.MarkProtected(I);
+ Protect(I);
+ }
+
+ return true;
+}
+
+ /*}}}*/
// ProblemResolver::ResolveByKeep - Resolve problems using keep /*{{{*/
// ---------------------------------------------------------------------
/* This is the work horse of the soft upgrade routine. It is very gentle
diff --git a/apt-pkg/algorithms.h b/apt-pkg/algorithms.h
index f06a38c1b..558beecd3 100644
--- a/apt-pkg/algorithms.h
+++ b/apt-pkg/algorithms.h
@@ -140,6 +140,8 @@ class APT_PUBLIC pkgProblemResolver /*{{{*/
inline void AllowBrokenPolicy(pkgCache::PkgIterator Pkg) { Flags[Pkg->ID] |= BrokenPolicyAllowed; };
#endif
+ bool KeepPhasedUpdates();
+
// Try to intelligently resolve problems by installing and removing packages
bool Resolve(bool BrokenFix = false, OpProgress * const Progress = NULL);
APT_HIDDEN bool ResolveInternal(bool const BrokenFix = false);
diff --git a/apt-pkg/cachefilter-patterns.cc b/apt-pkg/cachefilter-patterns.cc
index faf5735b8..7e6d84ff6 100644
--- a/apt-pkg/cachefilter-patterns.cc
+++ b/apt-pkg/cachefilter-patterns.cc
@@ -498,8 +498,12 @@ std::unique_ptr<APT::CacheFilter::Matcher> PatternParser::aPattern(std::unique_p
return std::make_unique<Patterns::PackageIsObsolete>();
if (node->matches("?origin", 1, 1))
return std::make_unique<Patterns::VersionIsOrigin>(aWord(node->arguments[0]));
+ if (node->matches("?phasing", 0, 0))
+ return std::make_unique<Patterns::PackageIsPhasing>(file);
if (node->matches("?section", 1, 1))
return std::make_unique<Patterns::VersionIsSection>(aWord(node->arguments[0]));
+ if (node->matches("?security", 0, 0))
+ return std::make_unique<Patterns::VersionIsSecurity>();
if (node->matches("?source-package", 1, 1))
return std::make_unique<Patterns::VersionIsSourcePackage>(aWord(node->arguments[0]));
if (node->matches("?source-version", 1, 1))
diff --git a/apt-pkg/cachefilter-patterns.h b/apt-pkg/cachefilter-patterns.h
index 2d48a1cc9..fa4605253 100644
--- a/apt-pkg/cachefilter-patterns.h
+++ b/apt-pkg/cachefilter-patterns.h
@@ -243,6 +243,16 @@ struct APT_HIDDEN PackageIsObsolete : public PackageMatcher
}
};
+struct APT_HIDDEN PackageIsPhasing : public PackageMatcher
+{
+ pkgCacheFile *Cache;
+ explicit PackageIsPhasing(pkgCacheFile *Cache) : Cache(Cache) {}
+ bool operator()(pkgCache::PkgIterator const &pkg) override
+ {
+ return (*Cache)->PhasingApplied(pkg);
+ }
+};
+
struct APT_HIDDEN PackageIsUpgradable : public PackageMatcher
{
pkgCacheFile *Cache;
@@ -405,6 +415,15 @@ struct APT_HIDDEN VersionIsSection : public VersionAnyMatcher
}
};
+struct APT_HIDDEN VersionIsSecurity : public VersionAnyMatcher
+{
+ VersionIsSecurity() {}
+ bool operator()(pkgCache::VerIterator const &Ver) override
+ {
+ return Ver.IsSecurityUpdate();
+ }
+};
+
struct APT_HIDDEN VersionIsSourcePackage : public VersionAnyMatcher
{
BaseRegexMatcher matcher;
diff --git a/apt-pkg/cacheiterators.h b/apt-pkg/cacheiterators.h
index 9273369bd..074973048 100644
--- a/apt-pkg/cacheiterators.h
+++ b/apt-pkg/cacheiterators.h
@@ -246,6 +246,7 @@ class APT_PUBLIC pkgCache::VerIterator : public Iterator<Version, VerIterator> {
bool Automatic() const;
VerFileIterator NewestFile() const;
+ bool IsSecurityUpdate() const;
#ifdef APT_COMPILING_APT
inline unsigned int PhasedUpdatePercentage() const
diff --git a/apt-pkg/depcache.cc b/apt-pkg/depcache.cc
index 322bf1bbe..c965781a4 100644
--- a/apt-pkg/depcache.cc
+++ b/apt-pkg/depcache.cc
@@ -30,11 +30,12 @@
#include <algorithm>
#include <iostream>
-#include <memory>
-#include <sstream>
#include <iterator>
#include <list>
+#include <memory>
+#include <random>
#include <set>
+#include <sstream>
#include <string>
#include <unordered_map>
#include <utility>
@@ -141,6 +142,7 @@ struct pkgDepCache::Private
{
std::unique_ptr<InRootSetFunc> inRootSetFunc;
std::unique_ptr<APT::CacheFilter::Matcher> IsAVersionedKernelPackage, IsProtectedKernelPackage;
+ std::string machineID;
};
pkgDepCache::pkgDepCache(pkgCache *const pCache, Policy *const Plcy) : group_level(0), Cache(pCache), PkgState(0), DepState(0),
iUsrSize(0), iDownloadSize(0), iInstCount(0), iDelCount(0), iKeepCount(0),
@@ -148,6 +150,7 @@ pkgDepCache::pkgDepCache(pkgCache *const pCache, Policy *const Plcy) : group_lev
{
DebugMarker = _config->FindB("Debug::pkgDepCache::Marker", false);
DebugAutoInstall = _config->FindB("Debug::pkgDepCache::AutoInstall", false);
+ d->machineID = APT::Configuration::getMachineID();
delLocalPolicy = 0;
LocalPolicy = Plcy;
if (LocalPolicy == 0)
@@ -2562,3 +2565,51 @@ bool pkgDepCache::MarkAndSweep()
return false;
}
/*}}}*/
+
+// DepCache::PhasingApplied /*{{{*/
+// Check if this version is a phased update that should be ignored, not considering whether
+// it is a security update.
+static bool IsIgnoredPhasedUpdate(std::string machineID, pkgCache::VerIterator const &Ver)
+{
+ if (_config->FindB("APT::Get::Phase-Policy", false))
+ return false;
+
+ // The order and fallbacks for the always/never checks come from update-manager and exist
+ // to preserve compatibility.
+ if (_config->FindB("APT::Get::Always-Include-Phased-Updates",
+ _config->FindB("Update-Manager::Always-Include-Phased-Updates", false)))
+ return false;
+
+ if (_config->FindB("APT::Get::Never-Include-Phased-Updates",
+ _config->FindB("Update-Manager::Never-Include-Phased-Updates", false)))
+ return true;
+
+ if (machineID.empty() // no machine-id
+ || getenv("SOURCE_DATE_EPOCH") != nullptr // reproducible build - always include
+ || APT::Configuration::isChroot())
+ return false;
+
+ std::string seedStr = std::string(Ver.SourcePkgName()) + "-" + Ver.SourceVerStr() + "-" + machineID;
+ std::seed_seq seed(seedStr.begin(), seedStr.end());
+ std::minstd_rand rand(seed);
+ std::uniform_int_distribution<unsigned int> dist(0, 100);
+
+ return dist(rand) > Ver.PhasedUpdatePercentage();
+}
+
+bool pkgDepCache::PhasingApplied(pkgCache::PkgIterator Pkg) const
+{
+ if (Pkg->CurrentVer == 0)
+ return false;
+ if ((*this)[Pkg].CandidateVer == 0)
+ return false;
+ if ((*this)[Pkg].CandidateVerIter(*Cache).PhasedUpdatePercentage() == 100)
+ return false;
+ if ((*this)[Pkg].CandidateVerIter(*Cache).IsSecurityUpdate())
+ return false;
+ if (!IsIgnoredPhasedUpdate(d->machineID, (*this)[Pkg].CandidateVerIter(*Cache)))
+ return false;
+
+ return true;
+}
+ /*}}}*/
diff --git a/apt-pkg/depcache.h b/apt-pkg/depcache.h
index be27b1d77..a850cf91a 100644
--- a/apt-pkg/depcache.h
+++ b/apt-pkg/depcache.h
@@ -381,6 +381,12 @@ class APT_PUBLIC pkgDepCache : protected pkgCache::Namespace
bool MarkAndSweep(InRootSetFunc &rootFunc);
bool MarkAndSweep();
+ /** Check if the phased update is ready.
+ *
+ * \return \b false if this is a phased update that is not yet ready for us
+ */
+ bool PhasingApplied(PkgIterator Pkg) const;
+
/** \name State Manipulators
*/
// @{
diff --git a/apt-pkg/pkgcache.cc b/apt-pkg/pkgcache.cc
index 76336b9b3..06689aa33 100644
--- a/apt-pkg/pkgcache.cc
+++ b/apt-pkg/pkgcache.cc
@@ -1017,5 +1017,30 @@ pkgCache::DescIterator pkgCache::VerIterator::TranslatedDescription() const
}
/*}}}*/
+// VerIterator::IsSecurity - check if it is a security update /*{{{*/
+// See if this version is a security update. This also checks, for installed packages,
+// if any of the previous versions is a security update
+bool pkgCache::VerIterator::IsSecurityUpdate() const
+{
+ auto Pkg = ParentPkg();
+ auto Installed = Pkg.CurrentVer();
+
+ auto OtherVer = Pkg.VersionList();
+
+ // Advance to first version < our version
+ while (OtherVer->ID != S->ID)
+ ++OtherVer;
+
+ // Iterate over all versions < our version
+ for (; !OtherVer.end() && (Installed.end() || OtherVer->ID != Installed->ID); OtherVer++)
+ {
+ for (auto PF = OtherVer.FileList(); !PF.end(); PF++)
+ if (PF.File() && PF.File().Archive() != nullptr && APT::String::Endswith(PF.File().Archive(), "-security"))
+ return true;
+ }
+ return false;
+}
+
+ /*}}}*/
pkgCache::~pkgCache() {}
diff --git a/apt-pkg/upgrade.cc b/apt-pkg/upgrade.cc
index c7e566a55..fad47838c 100644
--- a/apt-pkg/upgrade.cc
+++ b/apt-pkg/upgrade.cc
@@ -18,101 +18,6 @@
#include <apti18n.h>
/*}}}*/
-struct PhasedUpgrader
-{
- std::string machineID;
- bool isChroot;
-
- PhasedUpgrader()
- {
- machineID = APT::Configuration::getMachineID();
- }
-
- // See if this version is a security update. This also checks, for installed packages,
- // if any of the previous versions is a security update
- bool IsSecurityUpdate(pkgCache::VerIterator const &Ver)
- {
- auto Pkg = Ver.ParentPkg();
- auto Installed = Pkg.CurrentVer();
-
- auto OtherVer = Pkg.VersionList();
-
- // Advance to first version < our version
- while (OtherVer->ID != Ver->ID)
- ++OtherVer;
-
- // Iterate over all versions < our version
- for (; !OtherVer.end() && (Installed.end() || OtherVer->ID != Installed->ID); OtherVer++)
- {
- for (auto PF = OtherVer.FileList(); !PF.end(); PF++)
- if (PF.File() && PF.File().Archive() != nullptr && APT::String::Endswith(PF.File().Archive(), "-security"))
- return true;
- }
- return false;
- }
-
- // Check if this version is a phased update that should be ignored
- bool IsIgnoredPhasedUpdate(pkgCache::VerIterator const &Ver)
- {
- if (_config->FindB("APT::Get::Phase-Policy", false))
- return false;
-
- // The order and fallbacks for the always/never checks come from update-manager and exist
- // to preserve compatibility.
- if (_config->FindB("APT::Get::Always-Include-Phased-Updates",
- _config->FindB("Update-Manager::Always-Include-Phased-Updates", false)))
- return false;
-
- if (_config->FindB("APT::Get::Never-Include-Phased-Updates",
- _config->FindB("Update-Manager::Never-Include-Phased-Updates", false)))
- return true;
-
- if (machineID.empty() // no machine-id
- || getenv("SOURCE_DATE_EPOCH") != nullptr // reproducible build - always include
- || APT::Configuration::isChroot())
- return false;
-
- std::string seedStr = std::string(Ver.SourcePkgName()) + "-" + Ver.SourceVerStr() + "-" + machineID;
- std::seed_seq seed(seedStr.begin(), seedStr.end());
- std::minstd_rand rand(seed);
- std::uniform_int_distribution<unsigned int> dist(0, 100);
-
- return dist(rand) > Ver.PhasedUpdatePercentage();
- }
-
- bool ShouldKeep(pkgDepCache &Cache, pkgCache::PkgIterator Pkg)
- {
- if (Pkg->CurrentVer == 0)
- return false;
- if (Cache[Pkg].CandidateVer == 0)
- return false;
- if (Cache[Pkg].CandidateVerIter(Cache).PhasedUpdatePercentage() == 100)
- return false;
- if (IsSecurityUpdate(Cache[Pkg].CandidateVerIter(Cache)))
- return false;
- if (!IsIgnoredPhasedUpdate(Cache[Pkg].CandidateVerIter(Cache)))
- return false;
-
- return true;
- }
-
- // Hold back upgrades to phased versions of already installed packages, unless
- // they are security updates
- void HoldBackIgnoredPhasedUpdates(pkgDepCache &Cache, pkgProblemResolver *Fix)
- {
- for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I)
- {
- if (not ShouldKeep(Cache, I))
- continue;
-
- Cache.MarkKeep(I, false, false);
- Cache.MarkProtected(I);
- if (Fix != nullptr)
- Fix->Protect(I);
- }
- }
-};
-
// DistUpgrade - Distribution upgrade /*{{{*/
// ---------------------------------------------------------------------
/* This autoinstalls every package and then force installs every
@@ -132,14 +37,13 @@ static bool pkgDistUpgrade(pkgDepCache &Cache, OpProgress * const Progress)
Progress->OverallProgress(0, 100, 1, _("Calculating upgrade"));
pkgDepCache::ActionGroup group(Cache);
- PhasedUpgrader phasedUpgrader;
/* Upgrade all installed packages first without autoinst to help the resolver
in versioned or-groups to upgrade the old solver instead of installing
a new one (if the old solver is not the first one [anymore]) */
for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I)
{
- if (phasedUpgrader.ShouldKeep(Cache, I))
+ if (Cache.PhasingApplied(I))
continue;
if (I->CurrentVer != 0)
Cache.MarkInstall(I, false, 0, false);
@@ -152,7 +56,7 @@ static bool pkgDistUpgrade(pkgDepCache &Cache, OpProgress * const Progress)
for the installation */
for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I)
{
- if (phasedUpgrader.ShouldKeep(Cache, I))
+ if (Cache.PhasingApplied(I))
continue;
if (I->CurrentVer != 0)
Cache.MarkInstall(I, true, 0, false);
@@ -184,7 +88,7 @@ static bool pkgDistUpgrade(pkgDepCache &Cache, OpProgress * const Progress)
if (isEssential == false || instEssential == true)
continue;
pkgCache::PkgIterator P = G.FindPreferredPkg();
- if (phasedUpgrader.ShouldKeep(Cache, P))
+ if (Cache.PhasingApplied(P))
continue;
Cache.MarkInstall(P, true, 0, false);
}
@@ -192,7 +96,7 @@ static bool pkgDistUpgrade(pkgDepCache &Cache, OpProgress * const Progress)
else if (essential != "none")
for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I)
{
- if (phasedUpgrader.ShouldKeep(Cache, I))
+ if (Cache.PhasingApplied(I))
continue;
if ((I->Flags & pkgCache::Flag::Essential) == pkgCache::Flag::Essential)
Cache.MarkInstall(I, true, 0, false);
@@ -205,7 +109,7 @@ static bool pkgDistUpgrade(pkgDepCache &Cache, OpProgress * const Progress)
conflict resolution on them all. */
for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I)
{
- if (phasedUpgrader.ShouldKeep(Cache, I))
+ if (Cache.PhasingApplied(I))
continue;
if (I->CurrentVer != 0)
Cache.MarkInstall(I, false, 0, false);
@@ -245,7 +149,7 @@ static bool pkgDistUpgrade(pkgDepCache &Cache, OpProgress * const Progress)
for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I)
if (Cache[I].InstPolicyBroken())
FixPhasing.AllowBrokenPolicy(I);
- PhasedUpgrader().HoldBackIgnoredPhasedUpdates(Cache, &FixPhasing);
+ FixPhasing.KeepPhasedUpdates();
success = FixPhasing.ResolveByKeepInternal();
}
@@ -267,7 +171,6 @@ static bool pkgAllUpgradeNoNewPackages(pkgDepCache &Cache, OpProgress * const Pr
pkgDepCache::ActionGroup group(Cache);
pkgProblemResolver Fix(&Cache);
- PhasedUpgrader phasedUpgrader;
// Upgrade all installed packages
for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I)
{
@@ -278,7 +181,7 @@ static bool pkgAllUpgradeNoNewPackages(pkgDepCache &Cache, OpProgress * const Pr
if (I->SelectedState == pkgCache::State::Hold)
continue;
- if (phasedUpgrader.ShouldKeep(Cache, I))
+ if (Cache.PhasingApplied(I))
continue;
if (I->CurrentVer != 0 && Cache[I].InstallVer != 0)
@@ -288,7 +191,7 @@ static bool pkgAllUpgradeNoNewPackages(pkgDepCache &Cache, OpProgress * const Pr
if (Progress != NULL)
Progress->Progress(50);
- phasedUpgrader.HoldBackIgnoredPhasedUpdates(Cache, &Fix);
+ Fix.KeepPhasedUpdates();
// resolve remaining issues via keep
bool const success = Fix.ResolveByKeepInternal();
@@ -316,7 +219,6 @@ static bool pkgAllUpgradeWithNewPackages(pkgDepCache &Cache, OpProgress * const
pkgDepCache::ActionGroup group(Cache);
pkgProblemResolver Fix(&Cache);
- PhasedUpgrader phasedUpgrader;
// provide the initial set of stuff we want to upgrade by marking
// all upgradable packages for upgrade
@@ -327,7 +229,7 @@ static bool pkgAllUpgradeWithNewPackages(pkgDepCache &Cache, OpProgress * const
if (_config->FindB("APT::Ignore-Hold",false) == false)
if (I->SelectedState == pkgCache::State::Hold)
continue;
- if (phasedUpgrader.ShouldKeep(Cache, I))
+ if (Cache.PhasingApplied(I))
continue;
Cache.MarkInstall(I, false, 0, false);
@@ -353,7 +255,7 @@ static bool pkgAllUpgradeWithNewPackages(pkgDepCache &Cache, OpProgress * const
if (Progress != NULL)
Progress->Progress(60);
- phasedUpgrader.HoldBackIgnoredPhasedUpdates(Cache, &Fix);
+ Fix.KeepPhasedUpdates();
// resolve remaining issues via keep
bool const success = Fix.ResolveByKeepInternal();
diff --git a/apt-private/private-install.cc b/apt-private/private-install.cc
index 2f254ca27..dbcba8fd1 100644
--- a/apt-private/private-install.cc
+++ b/apt-private/private-install.cc
@@ -210,11 +210,26 @@ bool InstallPackages(CacheFile &Cache, APT::PackageVector &HeldBackPackages, boo
return false;
}
+ APT::PackageVector PhasingPackages;
+ APT::PackageVector NotPhasingHeldBackPackages;
+ for (auto const &Pkg : HeldBackPackages)
+ {
+ if (Cache->PhasingApplied(Pkg))
+ PhasingPackages.push_back(Pkg);
+ else
+ NotPhasingHeldBackPackages.push_back(Pkg);
+ }
+
// Show all the various warning indicators
ShowDel(c1out,Cache);
ShowNew(c1out,Cache);
if (ShwKept == true)
- ShowKept(c1out,Cache, HeldBackPackages);
+ {
+ ShowPhasing(c1out, Cache, PhasingPackages);
+ ShowKept(c1out, Cache, NotPhasingHeldBackPackages);
+ if (not PhasingPackages.empty() && not NotPhasingHeldBackPackages.empty())
+ _error->Notice("Some packages may have been kept back due to phasing.");
+ }
bool const Hold = not ShowHold(c1out,Cache);
if (_config->FindB("APT::Get::Show-Upgraded",true) == true)
ShowUpgraded(c1out,Cache);
diff --git a/apt-private/private-output.cc b/apt-private/private-output.cc
index 03fbe47e1..c73d5229b 100644
--- a/apt-private/private-output.cc
+++ b/apt-private/private-output.cc
@@ -490,6 +490,16 @@ void ShowDel(ostream &out,CacheFile &Cache)
CandidateVersion(&Cache));
}
/*}}}*/
+// ShowPhasing - Show packages kept due to phasing /*{{{*/
+void ShowPhasing(ostream &out, CacheFile &Cache, APT::PackageVector const &HeldBackPackages)
+{
+ SortedPackageUniverse Universe(Cache);
+ ShowList(out, _("The following upgrades have been deferred due to phasing:"), HeldBackPackages,
+ &AlwaysTrue,
+ &PrettyFullName,
+ CurrentToCandidateVersion(&Cache));
+}
+ /*}}}*/
// ShowKept - Show kept packages /*{{{*/
void ShowKept(ostream &out,CacheFile &Cache, APT::PackageVector const &HeldBackPackages)
{
diff --git a/apt-private/private-output.h b/apt-private/private-output.h
index d6f25e1ec..c3e73d592 100644
--- a/apt-private/private-output.h
+++ b/apt-private/private-output.h
@@ -93,6 +93,7 @@ template<class Container, class PredicateC, class DisplayP, class DisplayV> bool
void ShowNew(std::ostream &out,CacheFile &Cache);
void ShowDel(std::ostream &out,CacheFile &Cache);
void ShowKept(std::ostream &out,CacheFile &Cache, APT::PackageVector const &HeldBackPackages);
+void ShowPhasing(std::ostream &out, CacheFile &Cache, APT::PackageVector const &HeldBackPackages);
void ShowUpgraded(std::ostream &out,CacheFile &Cache);
bool ShowDowngraded(std::ostream &out,CacheFile &Cache);
bool ShowHold(std::ostream &out,CacheFile &Cache);
diff --git a/debian/libapt-pkg6.0.symbols b/debian/libapt-pkg6.0.symbols
index d297eb1c9..e12f98de7 100644
--- a/debian/libapt-pkg6.0.symbols
+++ b/debian/libapt-pkg6.0.symbols
@@ -1354,6 +1354,10 @@ libapt-pkg.so.6.0 libapt-pkg6.0 #MINVER#
(c++)"vtable for pkgDirStream@APTPKG_6.0" 0.8.0
(c++)"vtable for debDebFile::ControlExtract@APTPKG_6.0" 0.8.0
(c++)"vtable for debDebFile::MemControlExtract@APTPKG_6.0" 0.8.0
+### phased update API
+ (c++)"pkgCache::VerIterator::IsSecurityUpdate() const@APTPKG_6.0" 2.7.11
+ (c++)"pkgDepCache::PhasingApplied(pkgCache::PkgIterator) const@APTPKG_6.0" 2.7.11
+ (c++)"pkgProblemResolver::KeepPhasedUpdates()@APTPKG_6.0" 2.7.11
### gcc artifacts
(c++|optional=std)"void std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_construct<char const*>(char const*, char const*, std::forward_iterator_tag)@APTPKG_6.0" 1.7.0~alpha3~
(c++|optional=std)"typeinfo for std::_Mutex_base<(__gnu_cxx::_Lock_policy)2>@APTPKG_6.0" 1.9.11~
diff --git a/doc/apt-patterns.7.xml b/doc/apt-patterns.7.xml
index 6b3f91e96..e87fef23d 100644
--- a/doc/apt-patterns.7.xml
+++ b/doc/apt-patterns.7.xml
@@ -116,6 +116,9 @@
<varlistentry><term><code>?obsolete</code></term><term><code>~o</code></term>
<listitem><para>Selects packages that no longer exist in repositories.</para></listitem>
</varlistentry>
+ <varlistentry><term><code>?phasing</code></term>
+ <listitem><para>Selects packages that will be kept back in upgrades due to phasing.</para></listitem>
+ </varlistentry>
<varlistentry><term><code>?upgradable</code></term><term><code>~U</code></term>
<listitem><para>Selects packages that can be upgraded (have a newer candidate).</para></listitem>
</varlistentry>
@@ -159,6 +162,9 @@
<varlistentry><term><code>?priority(NAME)</code></term><term><code>~pNAME</code></term>
<listitem><para>Selects versions where the Priority string equals the given name.</para></listitem>
</varlistentry>
+ <varlistentry><term><code>?security</code></term>
+ <listitem><para>Selects packages that are a security update or succeed a security update.</para></listitem>
+ </varlistentry>
</variablelist>
</refsect1>
diff --git a/test/integration/test-phased-updates-new-depends b/test/integration/test-phased-updates-new-depends
index 8cc9314ad..5d6ac80d8 100755
--- a/test/integration/test-phased-updates-new-depends
+++ b/test/integration/test-phased-updates-new-depends
@@ -35,7 +35,7 @@ The following packages have been kept back:
testsuccessequal "Reading package lists...
Building dependency tree...
Calculating upgrade...
-The following packages have been kept back:
+The following upgrades have been deferred due to phasing:
has-new-conflicts has-new-depends has-new-recommends
0 upgraded, 0 newly installed, 0 to remove and 3 not upgraded." aptget upgrade -s -q
@@ -62,7 +62,7 @@ Conf new-recommends (2 unstable-updates [all])" aptget upgrade -s -q --with-new-
testsuccessequal "Reading package lists...
Building dependency tree...
Calculating upgrade...
-The following packages have been kept back:
+The following upgrades have been deferred due to phasing:
has-new-conflicts has-new-depends has-new-recommends
0 upgraded, 0 newly installed, 0 to remove and 3 not upgraded." aptget upgrade -s -q --with-new-pkgs
@@ -91,6 +91,6 @@ Conf new-recommends (2 unstable-updates [all])" aptget dist-upgrade -s -q -o APT
testsuccessequal "Reading package lists...
Building dependency tree...
Calculating upgrade...
-The following packages have been kept back:
+The following upgrades have been deferred due to phasing:
has-new-conflicts has-new-depends has-new-recommends
0 upgraded, 0 newly installed, 0 to remove and 3 not upgraded." aptget dist-upgrade -s -q
diff --git a/test/integration/test-phased-updates-upgrade b/test/integration/test-phased-updates-upgrade
index 2f5327f05..4f415f22e 100755
--- a/test/integration/test-phased-updates-upgrade
+++ b/test/integration/test-phased-updates-upgrade
@@ -53,8 +53,10 @@ Building dependency tree...
Calculating upgrade...
The following NEW packages will be installed:
phased-new
+The following upgrades have been deferred due to phasing:
+ phased phased-dep phased-depends-ready-dep
The following packages have been kept back:
- depends-phased-dep phased phased-dep phased-depends-ready-dep ready-dep
+ depends-phased-dep ready-dep
The following packages will be upgraded:
depends-phased-new phased-security phased-security-same
3 upgraded, 1 newly installed, 0 to remove and 5 not upgraded.
@@ -67,6 +69,32 @@ Conf depends-phased-new (3 unstable-updates [all])
Conf phased-security (3 unstable-updates [all])
Conf phased-security-same (3 unstable-security, unstable-updates [all])" aptget dist-upgrade -s -q
+testequal "Reading package lists...
+Building dependency tree...
+Calculating upgrade...
+The following NEW packages will be installed:
+ phased-new
+The following upgrades have been deferred due to phasing:
+ phased phased-dep phased-depends-ready-dep
+The following packages have been kept back:
+ depends-phased-dep ready-dep
+The following packages will be upgraded:
+ depends-phased-new phased-security phased-security-same
+3 upgraded, 1 newly installed, 0 to remove and 5 not upgraded.
+Need to get 0 B/168 B of archives.
+After this operation, 43.0 kB of additional disk space will be used.
+N: Some packages may have been kept back due to phasing.
+Do you want to continue? [Y/n] Abort." apt dist-upgrade < /dev/null
+
+testsuccessequal "Listing...
+phased-dep/unstable-updates 3 all [upgradable from: 1]
+phased-depends-ready-dep/unstable-updates 3 all [upgradable from: 1]
+phased/unstable-updates 3 all [upgradable from: 1]" apt list '?phasing'
+
+testsuccessequal "Listing...
+phased-security-same/unstable-security,unstable-updates 3 all [upgradable from: 1]
+phased-security/unstable-updates 3 all [upgradable from: 1]" apt list '?security'
+
for always in APT::Get::Always-Include-Phased-Updates Update-Manager::Always-Include-Phased-Updates; do
testsuccessequal "Reading package lists...
Building dependency tree...
@@ -131,8 +159,10 @@ Building dependency tree...
Calculating upgrade...
The following NEW packages will be installed:
phased-new
+The following upgrades have been deferred due to phasing:
+ phased phased-dep phased-depends-ready-dep
The following packages have been kept back:
- depends-phased-dep phased phased-dep phased-depends-ready-dep ready-dep
+ depends-phased-dep ready-dep
The following packages will be upgraded:
depends-phased-new phased-security phased-security-same
3 upgraded, 1 newly installed, 0 to remove and 5 not upgraded.
@@ -151,8 +181,10 @@ Building dependency tree...
Calculating upgrade...
The following NEW packages will be installed:
phased-new
+The following upgrades have been deferred due to phasing:
+ phased phased-dep phased-depends-ready-dep
The following packages have been kept back:
- depends-phased-dep phased phased-dep phased-depends-ready-dep ready-dep
+ depends-phased-dep ready-dep
The following packages will be upgraded:
depends-phased-new phased-security phased-security-same
3 upgraded, 1 newly installed, 0 to remove and 5 not upgraded.
@@ -168,9 +200,10 @@ Conf phased-security-same (3 unstable-security, unstable-updates [all])" aptget
testsuccessequal "Reading package lists...
Building dependency tree...
Calculating upgrade...
+The following upgrades have been deferred due to phasing:
+ phased phased-dep phased-depends-ready-dep
The following packages have been kept back:
- depends-phased-dep depends-phased-new phased phased-dep
- phased-depends-ready-dep ready-dep
+ depends-phased-dep depends-phased-new ready-dep
The following packages will be upgraded:
phased-security phased-security-same
2 upgraded, 0 newly installed, 0 to remove and 6 not upgraded.
@@ -185,8 +218,10 @@ Building dependency tree...
Calculating upgrade...
The following NEW packages will be installed:
phased-new
+The following upgrades have been deferred due to phasing:
+ phased phased-depends-ready-dep
The following packages have been kept back:
- phased phased-depends-ready-dep ready-dep
+ ready-dep
The following packages will be upgraded:
depends-phased-dep depends-phased-new phased-dep phased-security
phased-security-same
@@ -208,8 +243,10 @@ done
testsuccessequal "Reading package lists...
Building dependency tree...
Calculating upgrade...
+The following upgrades have been deferred due to phasing:
+ phased phased-depends-ready-dep
The following packages have been kept back:
- depends-phased-new phased phased-depends-ready-dep ready-dep
+ depends-phased-new ready-dep
The following packages will be upgraded:
depends-phased-dep phased-dep phased-security phased-security-same
4 upgraded, 0 newly installed, 0 to remove and 4 not upgraded.
diff --git a/test/integration/test-ubuntu-bug-2025462-phased-dist-upgrade b/test/integration/test-ubuntu-bug-2025462-phased-dist-upgrade
index eecadcda3..567864230 100755
--- a/test/integration/test-ubuntu-bug-2025462-phased-dist-upgrade
+++ b/test/integration/test-ubuntu-bug-2025462-phased-dist-upgrade
@@ -32,8 +32,10 @@ setupaptarchive
testsuccessequal "Reading package lists...
Building dependency tree...
Calculating upgrade...
+The following upgrades have been deferred due to phasing:
+ mutter
The following packages have been kept back:
- gnome-shell gnome-shell-common mutter
+ gnome-shell gnome-shell-common
0 upgraded, 0 newly installed, 0 to remove and 3 not upgraded." aptget dist-upgrade -s -q
testsuccessequal "Reading package lists...