Imported Upstream version 0.7.27
[platform/upstream/libsolv.git] / src / rules.c
index b5f3e3e..7d57cfd 100644 (file)
@@ -183,7 +183,7 @@ solver_unifyrules(Solver *solv)
            binr++;
          else
            {
-             dp = solv->pool->whatprovidesdata + r->d;
+             dp = pool->whatprovidesdata + r->d;
              while (*dp++)
                lits++;
            }
@@ -498,6 +498,16 @@ add_package_link(Solver *solv, Solvable *s, Map *m, Queue *workq)
 
 #ifdef ENABLE_COMPLEX_DEPS
 
+#ifdef SUSE
+static inline int
+suse_isptf(Pool *pool, Solvable *s)
+{
+  if (!strncmp("ptf-", pool_id2str(pool, s->name), 4))
+    return 1;
+  return 0;
+}
+#endif
+
 static void
 add_complex_deprules(Solver *solv, Id p, Id dep, int type, int dontfix, Queue *workq, Map *m)
 {
@@ -511,6 +521,10 @@ add_complex_deprules(Solver *solv, Id p, Id dep, int type, int dontfix, Queue *w
   /* CNF expansion for requires, DNF + INVERT expansion for conflicts */
   if (type == SOLVER_RULE_PKG_CONFLICTS)
     flags |= CPLXDEPS_TODNF | CPLXDEPS_EXPAND | CPLXDEPS_INVERT;
+#ifdef SUSE
+  if (type == SOLVER_RULE_PKG_REQUIRES && suse_isptf(pool, pool->solvables + p))
+    flags |= CPLXDEPS_NAME;    /* do not match provides */
+#endif
 
   i = pool_normalize_complex_dep(pool, dep, &bq, flags);
   /* handle special cases */
@@ -599,7 +613,7 @@ add_complex_deprules(Solver *solv, Id p, Id dep, int type, int dontfix, Queue *w
        }
       else
        {
-         Id *qele;
+         Id *qele, d;
          int qcnt;
 
          qele = bq.elements + i;
@@ -639,7 +653,21 @@ add_complex_deprules(Solver *solv, Id p, Id dep, int type, int dontfix, Queue *w
              break;
          if (j < qcnt)
            continue;
-         addpkgrule(solv, qele[0], 0, pool_ids2whatprovides(pool, qele + 1, qcnt - 1), type, dep);
+         d = pool_ids2whatprovides(pool, qele + 1, qcnt - 1);
+         if (solv->ruleinfoq && qele[0] != p)
+           {
+             int oldcount = solv->ruleinfoq->count;
+             addpkgrule(solv, qele[0], 0, d, type, dep);
+             /* fixup from element of ruleinfo */
+             if (solv->ruleinfoq->count > oldcount && solv->ruleinfoq->elements[oldcount + 1] != p)
+               {
+                 if (solv->ruleinfoq->elements[oldcount + 2])
+                   solv->ruleinfoq->elements[oldcount + 2] = solv->ruleinfoq->elements[oldcount + 1];
+                 solv->ruleinfoq->elements[oldcount + 1] = p;
+               }
+           }
+         else
+           addpkgrule(solv, qele[0], 0, d, type, dep);
          if (m)
            for (j = 0; j < qcnt; j++)
              if (qele[j] > 0 && !MAPTST(m, qele[j]))
@@ -651,6 +679,34 @@ add_complex_deprules(Solver *solv, Id p, Id dep, int type, int dontfix, Queue *w
 
 #endif
 
+#ifdef ENABLE_CONDA
+void
+add_conda_constrains_rule(Solver *solv, Id n, Id dep, int dontfix)
+{
+  Pool *pool = solv->pool;
+  Reldep *rd;
+  Id p, pp, pdep;
+  if (!ISRELDEP(dep))
+    return;
+  rd = GETRELDEP(pool, dep);
+  pdep = pool_whatprovides(pool, dep);
+  FOR_PROVIDES(p, pp, rd->name)
+    {
+      Id p2;
+      if (p == n)
+       continue;
+      if (dontfix && pool->solvables[p].repo == solv->installed)
+       continue;
+      while ((p2 = pool->whatprovidesdata[pdep]) != 0 && p2 < p)
+       pdep++;
+      if (p == p2)
+       pdep++;
+      else
+        addpkgrule(solv, -n, -p, 0, SOLVER_RULE_PKG_CONSTRAINS, dep);
+    }
+}
+#endif
+
 /*-------------------------------------------------------------------
  *
  * add dependency rules for solvable
@@ -679,8 +735,8 @@ solver_addpkgrulesforsolvable(Solver *solv, Solvable *s, Map *m)
 
   Queue workq; /* list of solvables we still have to work on */
   Id workqbuf[64];
-  Queue prereqq;       /* list of pre-req ids to ignore */
-  Id prereqbuf[16];
+  Queue depq;  /* list of pre-req ids to ignore */
+  Id depqbuf[16];
 
   int i;
   int dontfix;         /* ignore dependency errors for installed solvables */
@@ -696,7 +752,7 @@ solver_addpkgrulesforsolvable(Solver *solv, Solvable *s, Map *m)
   queue_init_buffer(&workq, workqbuf, sizeof(workqbuf)/sizeof(*workqbuf));
   queue_push(&workq, s - pool->solvables);     /* push solvable Id to work queue */
 
-  queue_init_buffer(&prereqq, prereqbuf, sizeof(prereqbuf)/sizeof(*prereqbuf));
+  queue_init_buffer(&depq, depqbuf, sizeof(depqbuf)/sizeof(*depqbuf));
 
   /* loop until there's no more work left */
   while (workq.count)
@@ -756,20 +812,18 @@ solver_addpkgrulesforsolvable(Solver *solv, Solvable *s, Map *m)
                {
                  if (installed && s->repo == installed)
                    {
-                     if (prereqq.count)
-                       queue_empty(&prereqq);
-                     solvable_lookup_idarray(s, SOLVABLE_PREREQ_IGNOREINST, &prereqq);
-                     filterpre = prereqq.count;
+                     solvable_lookup_idarray(s, SOLVABLE_PREREQ_IGNOREINST, &depq);
+                     filterpre = depq.count;
                    }
                  continue;
                }
              if (filterpre)
                {
-                 /* check if this id is filtered. assumes that prereqq.count is small */
-                 for (i = 0; i < prereqq.count; i++)
-                   if (req == prereqq.elements[i])
+                 /* check if this id is filtered. assumes that depq.count is small */
+                 for (i = 0; i < depq.count; i++)
+                   if (req == depq.elements[i])
                      break;
-                 if (i < prereqq.count)
+                 if (i < depq.count)
                    {
                      POOL_DEBUG(SOLV_DEBUG_RULE_CREATION, "package %s: ignoring filtered pre-req dependency %s\n", pool_solvable2str(pool, s), pool_dep2str(pool, req));
                      continue;
@@ -882,6 +936,15 @@ solver_addpkgrulesforsolvable(Solver *solv, Solvable *s, Map *m)
            }
        }
 
+#ifdef ENABLE_CONDA
+      if (pool->disttype == DISTTYPE_CONDA)
+       {
+         solvable_lookup_idarray(s, SOLVABLE_CONSTRAINS, &depq);
+         for (i = 0; i < depq.count; i++)
+           add_conda_constrains_rule(solv, n, depq.elements[i], dontfix);
+       }
+#endif
+
       /* that's all we check for src packages */
       if (s->arch == ARCH_SRC || s->arch == ARCH_NOSRC)
        continue;
@@ -1031,16 +1094,17 @@ solver_addpkgrulesforsolvable(Solver *solv, Solvable *s, Map *m)
            }
        }
 
-      if (m && pool->implicitobsoleteusescolors && (s->arch > pool->lastarch || pool->id2arch[s->arch] != 1))
+      if (m && pool->implicitobsoleteusescolors && pool_arch2score(pool, s->arch) > 1)
        {
-         int a = pool->id2arch[s->arch];
+         unsigned int pa, a = pool_arch2score(pool, s->arch);
          /* check lock-step candidates */
          FOR_PROVIDES(p, pp, s->name)
            {
              Solvable *ps = pool->solvables + p;
              if (s->name != ps->name || s->evr != ps->evr || MAPTST(m, p))
                continue;
-             if (ps->arch > pool->lastarch || pool->id2arch[ps->arch] == 1 || pool->id2arch[ps->arch] >= a)
+             pa = pool_arch2score(pool, ps->arch);
+             if (!pa || pa == 1 || pa >= a)
                continue;
              queue_push(&workq, p);
            }
@@ -1058,7 +1122,7 @@ solver_addpkgrulesforsolvable(Solver *solv, Solvable *s, Map *m)
              if (pool_is_complex_dep(pool, rec))
                {
                  pool_add_pos_literals_complex_dep(pool, rec, &workq, m, 0);
-                   continue;
+                 continue;
                }
 #endif
              FOR_PROVIDES(p, pp, rec)
@@ -1075,7 +1139,7 @@ solver_addpkgrulesforsolvable(Solver *solv, Solvable *s, Map *m)
              if (pool_is_complex_dep(pool, sug))
                {
                  pool_add_pos_literals_complex_dep(pool, sug, &workq, m, 0);
-                   continue;
+                 continue;
                }
 #endif
              FOR_PROVIDES(p, pp, sug)
@@ -1084,7 +1148,7 @@ solver_addpkgrulesforsolvable(Solver *solv, Solvable *s, Map *m)
            }
        }
     }
-  queue_free(&prereqq);
+  queue_free(&depq);
   queue_free(&workq);
 }
 
@@ -1327,6 +1391,31 @@ solver_addfeaturerule(Solver *solv, Solvable *s)
     }
 }
 
+/* check if multiversion solvable s2 has an obsoletes for installed solvable s */
+static int
+is_multiversion_obsoleteed(Pool *pool, Solvable *s, Solvable *s2)
+{
+  Id *wp, obs, *obsp;
+
+  if (pool->obsoleteusescolors && !pool_colormatch(pool, s, s2))
+    return 0;
+  obsp = s2->repo->idarraydata + s2->obsoletes;
+  if (!pool->obsoleteusesprovides)
+    {
+      while ((obs = *obsp++) != 0)
+        if (pool_match_nevr(pool, s, obs))
+         return 1;
+    }
+  else
+    {
+      while ((obs = *obsp++) != 0)
+        for (wp = pool_whatprovides_ptr(pool, obs); *wp; wp++)
+         if (pool->solvables + *wp == s)
+           return 1;
+    }
+  return 0;
+}
+
 /*-------------------------------------------------------------------
  *
  * add rule for update
@@ -1347,6 +1436,14 @@ solver_addupdaterule(Solver *solv, Solvable *s)
   int dupinvolved = 0;
 
   p = s - pool->solvables;
+
+  if (pool->considered && pool_disabled_solvable(pool, s))
+    {
+      /* disabled installed solvables must stay installed */
+      solver_addrule(solv, p, 0, 0);
+      return;
+    }
+
   /* Orphan detection. We cheat by looking at the feature rule, which
    * we already calculated */
   r = solv->rules + solv->featurerules + (p - solv->installed->start);
@@ -1388,9 +1485,8 @@ solver_addupdaterule(Solver *solv, Solvable *s)
              if (MAPTST(&solv->multiversion, qs.elements[i]))
                {
                  Solvable *ps = pool->solvables + qs.elements[i];
-                 /* if keepexplicitobsoletes is set and the name is different,
-                  * we assume that there is an obsoletes. XXX: not 100% correct */
-                 if (solv->keepexplicitobsoletes && ps->name != s->name)
+                 /* check if there is an explicit obsoletes */
+                 if (solv->keepexplicitobsoletes && ps->obsoletes && is_multiversion_obsoleteed(pool, s, ps))
                    {
                      qs.elements[j++] = qs.elements[i];
                      continue;
@@ -1462,12 +1558,12 @@ disableupdaterule(Solver *solv, Id p)
   r = solv->rules + solv->featurerules + (p - solv->installed->start);
   if (r->p && r->d >= 0)
     solver_disablerule(solv, r);
-  if (solv->bestrules_pkg)
+  if (solv->bestrules_info)
     {
       int i, ni;
       ni = solv->bestrules_end - solv->bestrules;
-      for (i = 0; i < ni; i++)
-       if (solv->bestrules_pkg[i] == p)
+      for (i = solv->bestrules_up - solv->bestrules; i < ni; i++)
+       if (solv->bestrules_info[i] == p)
          solver_disablerule(solv, solv->rules + solv->bestrules + i);
     }
 }
@@ -1505,12 +1601,12 @@ reenableupdaterule(Solver *solv, Id p)
            }
        }
     }
-  if (solv->bestrules_pkg)
+  if (solv->bestrules_info)
     {
       int i, ni;
       ni = solv->bestrules_end - solv->bestrules;
-      for (i = 0; i < ni; i++)
-       if (solv->bestrules_pkg[i] == p)
+      for (i = solv->bestrules_up - solv->bestrules; i < ni; i++)
+       if (solv->bestrules_info[i] == p)
          solver_enablerule(solv, solv->rules + solv->bestrules + i);
     }
 }
@@ -1531,7 +1627,8 @@ solver_addinfarchrules(Solver *solv, Map *addedmap)
   Pool *pool = solv->pool;
   Repo *installed = pool->installed;
   int first, i, j;
-  Id p, pp, a, aa, bestarch;
+  Id p, pp, aa;
+  unsigned int a, bestscore;
   Solvable *s, *ps, *bests;
   Queue badq, allowedarchs;
   Queue lsq;
@@ -1546,7 +1643,7 @@ solver_addinfarchrules(Solver *solv, Map *addedmap)
        continue;
       s = pool->solvables + i;
       first = i;
-      bestarch = 0;
+      bestscore = 0;
       bests = 0;
       queue_empty(&allowedarchs);
       FOR_PROVIDES(p, pp, s->name)
@@ -1558,8 +1655,7 @@ solver_addinfarchrules(Solver *solv, Map *addedmap)
            first = 0;
          if (first)
            break;
-         a = ps->arch;
-         a = (a <= pool->lastarch) ? pool->id2arch[a] : 0;
+         a = pool_arch2score(pool, ps->arch);
          if (a != 1 && installed && ps->repo == installed)
            {
              if (solv->dupinvolvedmap_all || (solv->dupinvolvedmap.size && MAPTST(&solv->dupinvolvedmap, p)))
@@ -1567,20 +1663,33 @@ solver_addinfarchrules(Solver *solv, Map *addedmap)
              queue_pushunique(&allowedarchs, ps->arch);        /* also ok to keep this architecture */
              continue;         /* but ignore installed solvables when calculating the best arch */
            }
-         if (a && a != 1 && (!bestarch || a < bestarch))
+         if (a && a != 1 && (!bestscore || a < bestscore))
            {
-             bestarch = a;
+             bestscore = a;
              bests = ps;
            }
        }
       if (first)
-       continue;
+       continue;               /* not the first in the group */
+
+      if (!bestscore && allowedarchs.count > 1 && pool->implicitobsoleteusescolors)
+       {
+         for (j = 0; j < allowedarchs.count; j++)
+           {
+             a = pool_arch2score(pool, allowedarchs.elements[j]);
+             if (a && a != 1 && (!bestscore || a < bestscore))
+               bestscore = a;
+           }
+       }
+
+      if (!bestscore)
+       continue;               /* did not find a score for this group */
 
       /* speed up common case where installed package already has best arch */
       if (allowedarchs.count == 1 && bests && allowedarchs.elements[0] == bests->arch)
        allowedarchs.count--;   /* installed arch is best */
 
-      if (allowedarchs.count && pool->implicitobsoleteusescolors && installed && bestarch)
+      if (allowedarchs.count && pool->implicitobsoleteusescolors && installed)
        {
          /* need an extra pass for lockstep checking: we only allow to keep an inferior arch
           * if the corresponding installed package is not lock-stepped */
@@ -1593,25 +1702,23 @@ solver_addinfarchrules(Solver *solv, Map *addedmap)
                continue;
              if (solv->dupinvolvedmap_all || (solv->dupinvolvedmap.size && MAPTST(&solv->dupinvolvedmap, p)))
                continue;
-             a = ps->arch;
-             a = (a <= pool->lastarch) ? pool->id2arch[a] : 0;
+             a = pool_arch2score(pool, ps->arch);
              if (!a)
                {
                  queue_pushunique(&allowedarchs, ps->arch);    /* strange arch, allow */
                  continue;
                }
-             if (a == 1 || ((a ^ bestarch) & 0xffff0000) == 0)
+             if (a == 1 || ((a ^ bestscore) & 0xffff0000) == 0)
                continue;
              /* have installed package with inferior arch, check if lock-stepped */
              FOR_PROVIDES(p2, pp2, s->name)
                {
                  Solvable *s2 = pool->solvables + p2;
-                 Id a2;
+                 unsigned int a2;
                  if (p2 == p || s2->name != s->name || s2->evr != pool->solvables[p].evr || s2->arch == pool->solvables[p].arch)
                    continue;
-                 a2 = s2->arch;
-                 a2 = (a2 <= pool->lastarch) ? pool->id2arch[a2] : 0;
-                 if (a2 && (a2 == 1 || ((a2 ^ bestarch) & 0xffff0000) == 0))
+                 a2 = pool_arch2score(pool, s2->arch);
+                 if (a2 && (a2 == 1 || ((a2 ^ bestscore) & 0xffff0000) == 0))
                    break;
                }
              if (!p2)
@@ -1626,9 +1733,8 @@ solver_addinfarchrules(Solver *solv, Map *addedmap)
          ps = pool->solvables + p;
          if (ps->name != s->name || !MAPTST(addedmap, p))
            continue;
-         a = ps->arch;
-         a = (a <= pool->lastarch) ? pool->id2arch[a] : 0;
-         if (a != 1 && bestarch && ((a ^ bestarch) & 0xffff0000) != 0)
+         a = pool_arch2score(pool, ps->arch);
+         if (a != 1 && ((a ^ bestscore) & 0xffff0000) != 0)
            {
              if (installed && ps->repo == installed)
                {
@@ -1638,11 +1744,12 @@ solver_addinfarchrules(Solver *solv, Map *addedmap)
                }
              for (j = 0; j < allowedarchs.count; j++)
                {
+                 unsigned int aas;
                  aa = allowedarchs.elements[j];
                  if (ps->arch == aa)
                    break;
-                 aa = (aa <= pool->lastarch) ? pool->id2arch[aa] : 0;
-                 if (aa && ((a ^ aa) & 0xffff0000) == 0)
+                 aas = pool_arch2score(pool, aa);
+                 if (aas && ((a ^ aas) & 0xffff0000) == 0)
                    break;      /* compatible */
                }
              if (j == allowedarchs.count)
@@ -1654,7 +1761,7 @@ solver_addinfarchrules(Solver *solv, Map *addedmap)
       for (j = 0; j < badq.count; j++)
        {
          p = badq.elements[j];
-         /* lock-step */
+         /* special lock-step handling */
          if (pool->implicitobsoleteusescolors)
            {
              Id p2;
@@ -1665,9 +1772,8 @@ solver_addinfarchrules(Solver *solv, Map *addedmap)
                  Solvable *s2 = pool->solvables + p2;
                  if (p2 == p || s2->name != s->name || s2->evr != pool->solvables[p].evr || s2->arch == pool->solvables[p].arch)
                    continue;
-                 a = s2->arch;
-                 a = (a <= pool->lastarch) ? pool->id2arch[a] : 0;
-                 if (a && (a == 1 || ((a ^ bestarch) & 0xffff000) == 0))
+                 a = pool_arch2score(pool, s2->arch);
+                 if (a && (a == 1 || ((a ^ bestscore) & 0xffff000) == 0))
                    {
                      queue_push(&lsq, p2);
                      if (installed && s2->repo == installed)
@@ -1676,11 +1782,15 @@ solver_addinfarchrules(Solver *solv, Map *addedmap)
                }
              if (installed && pool->solvables[p].repo == installed && !haveinstalled)
                continue;       /* installed package not in lock-step */
+             if (lsq.count < 2)
+               solver_addrule(solv, -p, lsq.count ? lsq.elements[0] : 0, 0);
+             else
+               solver_addrule(solv, -p, 0, pool_queuetowhatprovides(pool, &lsq));
            }
-         if (lsq.count < 2)
-           solver_addrule(solv, -p, lsq.count ? lsq.elements[0] : 0, 0);
          else
-           solver_addrule(solv, -p, 0, pool_queuetowhatprovides(pool, &lsq));
+           {
+             solver_addrule(solv, -p, 0, 0);
+           }
        }
     }
   queue_free(&lsq);
@@ -1835,8 +1945,8 @@ solver_addtodupmaps(Solver *solv, Id p, Id how, int targeted)
 void
 solver_createdupmaps(Solver *solv)
 {
-  Queue *job = &solv->job;
   Pool *pool = solv->pool;
+  Queue *job = &solv->job;
   Repo *installed = solv->installed;
   Id select, how, what, p, pp;
   Solvable *s;
@@ -1922,6 +2032,21 @@ solver_createdupmaps(Solver *solv)
     }
   if (solv->dupinvolvedmap.size)
     MAPCLR(&solv->dupinvolvedmap, SYSTEMSOLVABLE);
+  /* set update for all involved installed packages. We need to do
+   * this before creating the update rules */
+  if (solv->dupinvolvedmap_all)
+    solv->updatemap_all = 1;
+  else if (installed && !solv->updatemap_all && solv->dupinvolvedmap.size)
+    {
+      FOR_REPO_SOLVABLES(installed, p, s)
+       {
+         if (!MAPTST(&solv->dupinvolvedmap, p))
+           continue;
+         if (!solv->updatemap.size)
+           map_grow(&solv->updatemap, installed->end - installed->start);
+         MAPSET(&solv->updatemap, p - installed->start);
+       }
+    }
 }
 
 void
@@ -1943,8 +2068,6 @@ solver_addduprules(Solver *solv, Map *addedmap)
   Rule *r;
 
   solv->duprules = solv->nrules;
-  if (solv->dupinvolvedmap_all)
-    solv->updatemap_all = 1;
   for (i = 1; i < pool->nsolvables; i++)
     {
       if (i == SYSTEMSOLVABLE || !MAPTST(addedmap, i))
@@ -1964,12 +2087,8 @@ solver_addduprules(Solver *solv, Map *addedmap)
            continue;
          if (installed && ps->repo == installed)
            {
-             if (!solv->updatemap_all)
-               {
-                 if (!solv->updatemap.size)
-                   map_grow(&solv->updatemap, installed->end - installed->start);
-                 MAPSET(&solv->updatemap, p - installed->start);
-               }
+             if (pool->considered && pool_disabled_solvable(pool, ps))
+               continue;               /* always keep disabled installed packages */
              if (!MAPTST(&solv->dupmap, p))
                {
                  Id ip, ipp;
@@ -2058,6 +2177,180 @@ reenableduprule(Solver *solv, Id name)
     }
 }
 
+/***********************************************************************
+ ***
+ ***  Black rule part
+ ***/
+
+static inline void
+disableblackrule(Solver *solv, Id p)
+{
+  Rule *r;
+  int i;
+  for (i = solv->blackrules, r = solv->rules + i; i < solv->blackrules_end; i++, r++)
+    if (r->p == -p)
+      solver_disablerule(solv, r);
+}
+
+static inline void
+reenableblackrule(Solver *solv, Id p)
+{
+  Pool *pool = solv->pool;
+  Rule *r;
+  int i;
+  for (i = solv->blackrules, r = solv->rules + i; i < solv->blackrules_end; i++, r++)
+    if (r->p == -p)
+      {
+       solver_enablerule(solv, r);
+       IF_POOLDEBUG (SOLV_DEBUG_SOLUTIONS)
+         {
+           POOL_DEBUG(SOLV_DEBUG_SOLUTIONS, "@@@ re-enabling ");
+           solver_printruleclass(solv, SOLV_DEBUG_SOLUTIONS, r);
+         }
+      }
+}
+
+void
+solver_addblackrules(Solver *solv)
+{
+  Pool *pool = solv->pool;
+  Repo *installed = solv->installed;
+  Id how, select, what, p, pp;
+  Queue *job = &solv->job;
+  Map updatemap;
+  int i;
+
+  map_init(&updatemap, 0);
+  solv->blackrules = solv->nrules;
+  if (installed)
+    {
+      for (i = 0; i < job->count; i += 2)
+       {
+         how = job->elements[i];
+         select = job->elements[i] & SOLVER_SELECTMASK;
+         what = job->elements[i + 1];
+         switch (how & SOLVER_JOBMASK)
+           {
+           case SOLVER_BLACKLIST:
+             FOR_JOB_SELECT(p, pp, select, what)
+               {
+                 Solvable *s = pool->solvables + p;
+                 if (s->repo != installed)
+                   continue;
+                 if (!updatemap.size)
+                   map_grow(&updatemap, pool->ss.nstrings);
+                 if (s->name > 0 && s->name < pool->ss.nstrings)
+                   MAPSET(&updatemap, s->name);
+               }
+           }
+       }
+    }
+  for (i = 0; i < job->count; i += 2)
+    {
+      how = job->elements[i];
+      select = job->elements[i] & SOLVER_SELECTMASK;
+      what = job->elements[i + 1];
+      switch (how & SOLVER_JOBMASK)
+       {
+       case SOLVER_BLACKLIST:
+         FOR_JOB_SELECT(p, pp, select, what)
+           {
+             Solvable *s = pool->solvables + p;
+             if (s->repo == installed)
+               continue;
+             if (updatemap.size && s->name > 0 && s->name < pool->ss.nstrings && MAPTST(&updatemap, s->name))
+               continue;       /* installed package with same name is already blacklisted */
+             solver_addrule(solv, -p, 0, 0);
+           }
+         break;
+       }
+    }
+  map_free(&updatemap);
+  solv->blackrules_end = solv->nrules;
+}
+
+/***********************************************************************
+ ***
+ ***  Strict repo prio rule part
+ ***/
+
+/* add rules to exclude solvables provided by lower
+ * precedence repositories */
+void solver_addstrictrepopriorules(struct s_Solver *solv, Map *addedmap)
+{
+  Pool *pool = solv->pool;
+  Solvable *s;
+  Id p, p2, pp2;
+  Map priomap;
+  int max_prio;
+
+  map_init_clone(&priomap, addedmap);
+  solv->strictrepopriorules = solv->nrules;
+
+  FOR_POOL_SOLVABLES(p)
+  {
+    if (!MAPTST(&priomap, p))
+      continue;
+
+    s = pool->solvables + p;
+    max_prio = s->repo->priority;
+    FOR_PROVIDES(p2, pp2, s->name)
+      {
+       Solvable *s2 = pool->solvables + p2;
+       if (s->name != s2->name)
+         continue;
+       if (s2->repo->priority > max_prio)
+         max_prio = s2->repo->priority;
+      }
+         
+    FOR_PROVIDES(p2, pp2, s->name)
+      {
+       Solvable *s2 = pool->solvables + p2;
+       if (s->name != s2->name || !MAPTST(&priomap, p2))
+         continue;
+       MAPCLR(&priomap, p2);
+       if (pool->installed && s2->repo == pool->installed)
+         continue;
+       if (s2->repo->priority < max_prio)
+         solver_addrule(solv, -p2, 0, 0);
+      }
+  }
+  solv->strictrepopriorules_end = solv->nrules;
+  map_free(&priomap);
+}
+
+static inline void
+disablerepopriorule(Solver *solv, Id name)
+{
+  Pool *pool = solv->pool;
+  Rule *r;
+  int i;
+  for (i = solv->strictrepopriorules, r = solv->rules + i; i < solv->strictrepopriorules_end; i++, r++)
+    {
+      if (r->p < 0 && r->d >= 0 && pool->solvables[-r->p].name == name)
+       solver_disablerule(solv, r);
+    }
+}
+
+static inline void
+reenablerepopriorule(Solver *solv, Id name)
+{
+  Pool *pool = solv->pool;
+  Rule *r;
+  int i;
+  for (i = solv->strictrepopriorules, r = solv->rules + i; i < solv->strictrepopriorules_end; i++, r++)
+    {
+      if (r->p < 0 && r->d < 0 && pool->solvables[-r->p].name == name)
+       {
+         solver_enablerule(solv, r);
+         IF_POOLDEBUG (SOLV_DEBUG_SOLUTIONS)
+           {
+             POOL_DEBUG(SOLV_DEBUG_SOLUTIONS, "@@@ re-enabling ");
+             solver_printruleclass(solv, SOLV_DEBUG_SOLUTIONS, r);
+           }
+       }
+    }
+}
 
 /***********************************************************************
  ***
@@ -2068,9 +2361,36 @@ reenableduprule(Solver *solv, Id name)
  ***
  ***/
 
-#define DISABLE_UPDATE 1
-#define DISABLE_INFARCH        2
-#define DISABLE_DUP    3
+#define DISABLE_UPDATE  1
+#define DISABLE_INFARCH         2
+#define DISABLE_DUP     3
+#define DISABLE_BLACK   4
+#define DISABLE_REPOPRIO 5
+
+/* check if installed package p is in lock-step with another installed package */
+static int
+installed_is_in_lockstep(Solver *solv, Id p)
+{
+  Pool *pool = solv->pool;
+  Repo *installed = solv->installed;
+  int rid;
+  Id pp, l;
+  Rule *r;
+
+  if (!installed)
+    return 0;
+  for (rid = solv->infarchrules, r = solv->rules + rid; rid < solv->infarchrules_end; rid++, r++)
+    {
+      if (r->p >= 0)
+       continue;
+      if (pool->solvables[-r->p].repo != installed)
+       continue;
+      FOR_RULELITERALS(l, pp, r)
+       if (l == p)
+         return 1;
+    }
+  return 0;
+}
 
 static void
 jobtodisablelist(Solver *solv, Id how, Id what, Queue *q)
@@ -2121,7 +2441,13 @@ jobtodisablelist(Solver *solv, Id how, Id what, Queue *q)
       if ((set & SOLVER_SETARCH) != 0 && solv->infarchrules != solv->infarchrules_end)
        {
          if (select == SOLVER_SOLVABLE)
-           queue_push2(q, DISABLE_INFARCH, pool->solvables[what].name);
+           {
+             for (i = solv->infarchrules; i < solv->infarchrules_end; i++)
+               if (solv->rules[i].p == -what)
+                 break;
+             if (i < solv->infarchrules_end)
+               queue_push2(q, DISABLE_INFARCH, pool->solvables[what].name);
+           }
          else
            {
              int qcnt = q->count;
@@ -2135,8 +2461,12 @@ jobtodisablelist(Solver *solv, Id how, Id what, Queue *q)
                    if (q->elements[i + 1] == s->name)
                      break;
                  if (i < q->count)
-                   continue;
-                 queue_push2(q, DISABLE_INFARCH, s->name);
+                   continue;           /* already have that DISABLE_INFARCH element */
+                 for (i = solv->infarchrules; i < solv->infarchrules_end; i++)
+                   if (solv->rules[i].p == -p)
+                     break;
+                 if (i < solv->infarchrules_end)
+                   queue_push2(q, DISABLE_INFARCH, s->name);
                }
            }
        }
@@ -2160,17 +2490,61 @@ jobtodisablelist(Solver *solv, Id how, Id what, Queue *q)
                }
            }
        }
+      if ((set & SOLVER_SETREPO) != 0 && solv->strictrepopriorules != solv->strictrepopriorules_end)
+       {
+         if (select == SOLVER_SOLVABLE)
+           queue_push2(q, DISABLE_REPOPRIO, pool->solvables[what].name);
+         else
+           {
+             int qcnt = q->count;
+             FOR_JOB_SELECT(p, pp, select, what)
+               {
+                 s = pool->solvables + p;
+                 /* unify names */
+                 for (i = qcnt; i < q->count; i += 2)
+                   if (q->elements[i + 1] == s->name)
+                     break;
+                 if (i < q->count)
+                   continue;
+                 queue_push2(q, DISABLE_REPOPRIO, s->name);
+               }
+           }
+       }
+      if ((set & SOLVER_SETEVR) != 0 && solv->blackrules != solv->blackrules_end)
+        {
+         if (select == SOLVER_SOLVABLE)
+           queue_push2(q, DISABLE_BLACK, what);
+         else
+           {
+             FOR_JOB_SELECT(p, pp, select, what)
+               queue_push2(q, DISABLE_BLACK, p);
+           }
+        }
       if (!installed || installed->end == installed->start)
        return;
       /* now the hard part: disable some update rules */
 
+      /* if the job asks for a single solvable to stay, disable the update rule */
+      if (select == SOLVER_SOLVABLE && pool->solvables[what].repo == installed && solv->bestrules_info)
+        if ((set & (SOLVER_SETEVR | SOLVER_SETARCH | SOLVER_SETVENDOR)) == (SOLVER_SETEVR | SOLVER_SETARCH | SOLVER_SETVENDOR))
+         {
+           int ni = solv->bestrules_end - solv->bestrules;
+           for (i = solv->bestrules_up - solv->bestrules; i < ni; i++)
+             if (solv->bestrules_info[i] == what)
+               {
+                 queue_push2(q, DISABLE_UPDATE, what);         /* will also disable the best rule */
+                 break;
+               }
+           return;
+         }
+
       /* first check if we have installed or multiversion packages in the job */
       FOR_JOB_SELECT(p, pp, select, what)
        {
          if (pool->solvables[p].repo == installed)
            return;
          if (solv->multiversion.size && MAPTST(&solv->multiversion, p) && !solv->keepexplicitobsoletes)
-           return;
+           return;             /* will not obsolete anything, so just return */
        }
       omap.size = 0;
       qstart = q->count;
@@ -2239,6 +2613,20 @@ jobtodisablelist(Solver *solv, Id how, Id what, Queue *q)
     case SOLVER_ERASE:
       if (!installed)
        break;
+      set = how & SOLVER_SETMASK;
+      if (!(set & (SOLVER_NOAUTOSET | SOLVER_SETARCH)) && pool->implicitobsoleteusescolors && solv->infarchrules != solv->infarchrules_end)
+       {
+         if (select == SOLVER_SOLVABLE)
+           set |= SOLVER_SETARCH;
+         else if ((select == SOLVER_SOLVABLE_NAME || select == SOLVER_SOLVABLE_PROVIDES) && ISRELDEP(what))
+           {
+             Reldep *rd = GETRELDEP(pool, what);
+             if (rd->flags <= 7 && ISRELDEP(rd->name))
+               rd = GETRELDEP(pool, rd->name);
+             if (rd->flags == REL_ARCH)
+               set |= SOLVER_SETARCH;
+           }
+       }
       if (select == SOLVER_SOLVABLE_ALL || (select == SOLVER_SOLVABLE_REPO && what == installed->repoid))
        {
          FOR_REPO_SOLVABLES(installed, p, s)
@@ -2248,12 +2636,43 @@ jobtodisablelist(Solver *solv, Id how, Id what, Queue *q)
        if (pool->solvables[p].repo == installed)
          {
            queue_push2(q, DISABLE_UPDATE, p);
+           if ((set & SOLVER_SETARCH) != 0 && pool->implicitobsoleteusescolors && solv->infarchrules != solv->infarchrules_end)
+             if (installed_is_in_lockstep(solv, p))
+               queue_push2(q, DISABLE_INFARCH, pool->solvables[p].name);               /* allow to break the lock-step */
 #ifdef ENABLE_LINKED_PKGS
            if (solv->instbuddy && solv->instbuddy[p - installed->start] > 1)
              queue_push2(q, DISABLE_UPDATE, solv->instbuddy[p - installed->start]);
 #endif
          }
       return;
+
+    case SOLVER_LOCK:
+      if (!installed)
+       break;
+      qstart = q->count;
+      if (select == SOLVER_SOLVABLE_ALL || (select == SOLVER_SOLVABLE_REPO && what == installed->repoid))
+       {
+         FOR_REPO_SOLVABLES(installed, p, s)
+           {
+             for (i = qstart; i < q->count; i += 2)
+               if (q->elements[i] == DISABLE_DUP && q->elements[i + 1] == pool->solvables[p].name)
+                 break;
+             if (i == q->count)
+               queue_push2(q, DISABLE_DUP, pool->solvables[p].name);
+           }
+       }
+      FOR_JOB_SELECT(p, pp, select, what)
+       {
+         if (pool->solvables[p].repo != installed)
+           continue;
+         for (i = qstart; i < q->count; i += 2)
+           if (q->elements[i] == DISABLE_DUP && q->elements[i + 1] == pool->solvables[p].name)
+             break;
+         if (i == q->count)
+           queue_push2(q, DISABLE_DUP, pool->solvables[p].name);
+       }
+      break;
+
     default:
       return;
     }
@@ -2305,6 +2724,12 @@ solver_disablepolicyrules(Solver *solv)
        case DISABLE_DUP:
          disableduprule(solv, arg);
          break;
+       case DISABLE_BLACK:
+         disableblackrule(solv, arg);
+         break;
+       case DISABLE_REPOPRIO:
+         disablerepopriorule(solv, arg);
+         break;
        default:
          break;
        }
@@ -2408,6 +2833,12 @@ solver_reenablepolicyrules(Solver *solv, int jobidx)
        case DISABLE_DUP:
          reenableduprule(solv, arg);
          break;
+       case DISABLE_BLACK:
+         reenableblackrule(solv, arg);
+         break;
+       case DISABLE_REPOPRIO:
+         reenablerepopriorule(solv, arg);
+         break;
        }
     }
   queue_free(&q);
@@ -2488,7 +2919,8 @@ addpkgruleinfo(Solver *solv, Id p, Id p2, Id d, int type, Id dep)
          if (*odp)
            return;
        }
-      if (p < 0 && pool->whatprovidesdata[d] < 0 && type == SOLVER_RULE_PKG_CONFLICTS)
+      /* set p2 for multiversion conflicts */
+      if (p < 0 && pool->whatprovidesdata[d] < 0 && pool->whatprovidesdata[d + 1] >= 0 && type == SOLVER_RULE_PKG_CONFLICTS)
        p2 = pool->whatprovidesdata[d];
     }
   else
@@ -2646,7 +3078,7 @@ solver_ruleinfo(Solver *solv, Id rid, Id *fromp, Id *top, Id *depp)
          qp = rq.elements[i + 1];
          qo = rq.elements[i + 2];
          qd = rq.elements[i + 3];
-         if (type == SOLVER_RULE_PKG || type > qt)
+         if (type == SOLVER_RULE_PKG || qt == SOLVER_RULE_PKG_SAME_NAME || type > qt)
            {
              type = qt;
              if (fromp)
@@ -2655,6 +3087,8 @@ solver_ruleinfo(Solver *solv, Id rid, Id *fromp, Id *top, Id *depp)
                *top = qo;
              if (depp)
                *depp = qd;
+             if (qt == SOLVER_RULE_PKG_SAME_NAME)
+               break;                  /* prefer SOLVER_RULE_PKG_SAME_NAME */
            }
        }
       queue_free(&rq);
@@ -2714,8 +3148,12 @@ solver_ruleinfo(Solver *solv, Id rid, Id *fromp, Id *top, Id *depp)
     }
   if (rid >= solv->bestrules && rid < solv->bestrules_end)
     {
-      if (fromp && solv->bestrules_pkg[rid - solv->bestrules] > 0)
-       *fromp = solv->bestrules_pkg[rid - solv->bestrules];
+      /* > 0: the package we are updating */
+      if (fromp && solv->bestrules_info[rid - solv->bestrules] > 0)
+       *fromp = solv->bestrules_info[rid - solv->bestrules];
+      /* < 0: the job rule */
+      if (top && solv->bestrules_info[rid - solv->bestrules] < 0)
+       *top = -solv->bestrules_info[rid - solv->bestrules];
       return SOLVER_RULE_BEST;
     }
   if (rid >= solv->yumobsrules && rid < solv->yumobsrules_end)
@@ -2734,14 +3172,32 @@ solver_ruleinfo(Solver *solv, Id rid, Id *fromp, Id *top, Id *depp)
        *depp = solv->yumobsrules_info[rid - solv->yumobsrules];
       return SOLVER_RULE_YUMOBS;
     }
+  if (rid >= solv->blackrules && rid < solv->blackrules_end)
+    {
+      if (fromp)
+       *fromp = -r->p;
+      return SOLVER_RULE_BLACK;
+    }
+  if (rid >= solv->strictrepopriorules && rid < solv->strictrepopriorules_end)
+    {
+      if (fromp)
+       *fromp = -r->p;
+      return SOLVER_RULE_STRICT_REPO_PRIORITY;
+    }
   if (rid >= solv->choicerules && rid < solv->choicerules_end)
     {
+      if (solv->choicerules_info && fromp)
+       *fromp = solv->choicerules_info[rid - solv->choicerules];
       return SOLVER_RULE_CHOICE;
     }
-  if (rid >= solv->learntrules)
+  if (rid >= solv->recommendsrules && rid < solv->recommendsrules_end)
     {
-      return SOLVER_RULE_LEARNT;
+      if (solv->recommendsrules_info && fromp)
+       *fromp = solv->recommendsrules_info[rid - solv->recommendsrules];
+      return SOLVER_RULE_RECOMMENDS;
     }
+  if (rid >= solv->learntrules)
+    return SOLVER_RULE_LEARNT;
   return SOLVER_RULE_UNKNOWN;
 }
 
@@ -2766,8 +3222,14 @@ solver_ruleclass(Solver *solv, Id rid)
     return SOLVER_RULE_BEST;
   if (rid >= solv->yumobsrules && rid < solv->yumobsrules_end)
     return SOLVER_RULE_YUMOBS;
+  if (rid >= solv->blackrules && rid < solv->blackrules_end)
+    return SOLVER_RULE_BLACK;
   if (rid >= solv->choicerules && rid < solv->choicerules_end)
     return SOLVER_RULE_CHOICE;
+  if (rid >= solv->recommendsrules && rid < solv->recommendsrules_end)
+    return SOLVER_RULE_RECOMMENDS;
+  if (rid >= solv->strictrepopriorules && rid < solv->strictrepopriorules_end)
+    return SOLVER_RULE_STRICT_REPO_PRIORITY;
   if (rid >= solv->learntrules && rid < solv->nrules)
     return SOLVER_RULE_LEARNT;
   return SOLVER_RULE_UNKNOWN;
@@ -2818,10 +3280,10 @@ solver_rule2job(Solver *solv, Id rid, Id *whatp)
 Id
 solver_rule2solvable(Solver *solv, Id rid)
 {
-  if (rid >= solv->updaterules && rid < solv->updaterules_end)
-    return rid - solv->updaterules;
-  if (rid >= solv->featurerules && rid < solv->featurerules_end)
-    return rid - solv->featurerules;
+  if (rid >= solv->updaterules && rid < solv->updaterules_end && solv->installed)
+    return solv->installed->start + (rid - solv->updaterules);
+  if (rid >= solv->featurerules && rid < solv->featurerules_end && solv->installed)
+    return solv->installed->start + (rid - solv->featurerules);
   return 0;
 }
 
@@ -2829,7 +3291,9 @@ Id
 solver_rule2pkgrule(Solver *solv, Id rid)
 {
   if (rid >= solv->choicerules && rid < solv->choicerules_end)
-    return solv->choicerules_ref[rid - solv->choicerules];
+    return solv->choicerules_info[rid - solv->choicerules];
+  if (rid >= solv->recommendsrules && rid < solv->recommendsrules_end)
+    return solv->recommendsrules_info[rid - solv->recommendsrules];
   return 0;
 }
 
@@ -2875,6 +3339,12 @@ solver_rule2rules(Solver *solv, Id rid, Queue *q, int recursive)
 
 
 /* check if the newest versions of pi still provides the dependency we're looking for */
+/* pi: installed package
+ * r: rule for the dependency
+ * m: map with all positive elements of r
+ * return 0: at least one provider
+ * return 1: the newest versions do not provide the dependency
+ */
 static int
 solver_choicerulecheck(Solver *solv, Id pi, Rule *r, Map *m, Queue *q)
 {
@@ -2923,20 +3393,46 @@ solver_choicerulecheck(Solver *solv, Id pi, Rule *r, Map *m, Queue *q)
   return 1;    /* none of the new packages provided it */
 }
 
-static inline void
-queue_removeelement(Queue *q, Id el)
+static Id
+choicerule_find_installed(Pool *pool, Id p)
 {
-  int i, j;
-  for (i = 0; i < q->count; i++)
-    if (q->elements[i] == el)
-      break;
-  if (i < q->count)
+  Solvable *s = pool->solvables + p;
+  Id p2, pp2;
+
+  if (!s->repo)
+    return 0;
+  if (s->repo == pool->installed)
+    return p;
+  FOR_PROVIDES(p2, pp2, s->name)
     {
-      for (j = i++; i < q->count; i++)
-       if (q->elements[i] != el)
-         q->elements[j++] = q->elements[i];
-      queue_truncate(q, j);
+      Solvable *s2 = pool->solvables + p2;
+      if (s2->repo != pool->installed)
+       continue;
+      if (!pool->implicitobsoleteusesprovides && s->name != s2->name)
+       continue;
+      if (pool->implicitobsoleteusescolors && !pool_colormatch(pool, s, s2))
+       continue;
+      return p2;
+    }
+  if (s->obsoletes)
+    {
+      Id obs, *obsp = s->repo->idarraydata + s->obsoletes;
+      while ((obs = *obsp++) != 0)
+       {
+         FOR_PROVIDES(p2, pp2, obs)
+           {
+             Solvable *s2 = pool->solvables + p2;
+             if (s2->repo != pool->installed)
+               continue;
+             if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, pool->solvables + p2, obs))
+               continue;
+             if (pool->obsoleteusescolors && !pool_colormatch(pool, s, s2))
+               continue;
+             return p2;
+           }
+       }
     }
+  return 0;
 }
 
 void
@@ -2945,14 +3441,15 @@ solver_addchoicerules(Solver *solv)
   Pool *pool = solv->pool;
   Map m, mneg;
   Rule *r;
-  Queue q, qi, qcheck;
-  int i, j, rid, havechoice;
-  Id p, d, pp;
-  Id p2, pp2;
-  Solvable *s, *s2;
+  Queue q, qi, qcheck, infoq;
+  int i, j, rid, havechoice, negcnt;
+  Id p, d, pp, p2;
+  Solvable *s;
   Id lastaddedp, lastaddedd;
   int lastaddedcnt;
   unsigned int now;
+  int isinstalled;
+  int dodowngradecheck = solv->allowdowngrade;
 
   solv->choicerules = solv->nrules;
   if (!pool->installed)
@@ -2961,10 +3458,12 @@ solver_addchoicerules(Solver *solv)
       return;
     }
   now = solv_timems(0);
-  solv->choicerules_ref = solv_calloc(solv->pkgrules_end, sizeof(Id));
+  if ((solv->dupinvolvedmap_all || solv->dupinvolvedmap.size) && solv->dup_allowdowngrade)
+    dodowngradecheck = 1;
   queue_init(&q);
   queue_init(&qi);
   queue_init(&qcheck);
+  queue_init(&infoq);
   map_init(&m, pool->nsolvables);
   map_init(&mneg, pool->nsolvables);
   /* set up negative assertion map from infarch and dup rules */
@@ -2983,116 +3482,80 @@ solver_addchoicerules(Solver *solv)
       if (r->p >= 0 || ((r->d == 0 || r->d == -1) && r->w2 <= 0))
        continue;       /* only look at requires rules */
       /* solver_printrule(solv, SOLV_DEBUG_RESULT, r); */
-      queue_empty(&q);
       queue_empty(&qi);
       havechoice = 0;
+      isinstalled = 0;
       FOR_RULELITERALS(p, pp, r)
        {
          if (p < 0)
-           continue;
+           {
+             Solvable *s = pool->solvables - p;
+             p2 = s->repo == pool->installed ? -p : 0;
+             if (p2)
+               {
+                 if (!(solv->updatemap_all || (solv->updatemap.size && MAPTST(&solv->updatemap, p2 - solv->installed->start))))
+                   isinstalled = 1;
+               }
+             continue;
+           }
          s = pool->solvables + p;
          if (!s->repo)
            continue;
          if (s->repo == pool->installed)
            {
-             queue_push(&q, p);
+             queue_push2(&qi, p, p);
              continue;
            }
-         /* check if this package is "blocked" by a installed package */
-         s2 = 0;
-         FOR_PROVIDES(p2, pp2, s->name)
-           {
-             s2 = pool->solvables + p2;
-             if (s2->repo != pool->installed)
-               continue;
-             if (!pool->implicitobsoleteusesprovides && s->name != s2->name)
-               continue;
-             if (pool->implicitobsoleteusescolors && !pool_colormatch(pool, s, s2))
-               continue;
-             break;
-           }
+         /* find an installed package p2 that we can update/downgrade to p */
+         p2 = choicerule_find_installed(pool, p);
          if (p2)
            {
-             /* found installed package p2 that we can update to p */
              if (MAPTST(&mneg, p))
                continue;
-             if (policy_is_illegal(solv, s2, s, 0))
+             if (policy_is_illegal(solv, pool->solvables + p2, s, 0))
                continue;
-#if 0
-             if (solver_choicerulecheck(solv, p2, r, &m))
-               continue;
-             queue_push(&qi, p2);
-#else
              queue_push2(&qi, p2, p);
-#endif
-             queue_push(&q, p);
              continue;
            }
-         if (s->obsoletes)
-           {
-             Id obs, *obsp = s->repo->idarraydata + s->obsoletes;
-             s2 = 0;
-             while ((obs = *obsp++) != 0)
-               {
-                 FOR_PROVIDES(p2, pp2, obs)
-                   {
-                     s2 = pool->solvables + p2;
-                     if (s2->repo != pool->installed)
-                       continue;
-                     if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, pool->solvables + p2, obs))
-                       continue;
-                     if (pool->obsoleteusescolors && !pool_colormatch(pool, s, s2))
-                       continue;
-                     break;
-                   }
-                 if (p2)
-                   break;
-               }
-             if (obs)
-               {
-                 /* found installed package p2 that we can update to p */
-                 if (MAPTST(&mneg, p))
-                   continue;
-                 if (policy_is_illegal(solv, s2, s, 0))
-                   continue;
-#if 0
-                 if (solver_choicerulecheck(solv, p2, r, &m))
-                   continue;
-                 queue_push(&qi, p2);
-#else
-                 queue_push2(&qi, p2, p);
-#endif
-                 queue_push(&q, p);
-                 continue;
-               }
-           }
          /* package p is independent of the installed ones */
          havechoice = 1;
        }
-      if (!havechoice || !q.count || !qi.count)
+#if 0
+      printf("havechoice: %d qcount %d qicount %d\n", havechoice, q.count, qi.count);
+#endif
+      if (!havechoice || !qi.count)
        continue;       /* no choice */
 
       FOR_RULELITERALS(p, pp, r)
         if (p > 0)
          MAPSET(&m, p);
 
-      /* do extra checking */
-      for (i = j = 0; i < qi.count; i += 2)
+      if (!isinstalled)
        {
-         p2 = qi.elements[i];
-         if (!p2)
-           continue;
-         if (solver_choicerulecheck(solv, p2, r, &m, &qcheck))
+         /* do extra checking for packages related to installed packages */
+         for (i = j = 0; i < qi.count; i += 2)
            {
-             /* oops, remove element p from q */
-             queue_removeelement(&q, qi.elements[i + 1]);
-             continue;
+             int isdowngrade = 0;
+             p2 = qi.elements[i];
+             if (dodowngradecheck)
+               {
+                 p = qi.elements[i + 1];
+                 if (pool->solvables[p2].name == pool->solvables[p].name)
+                   if (pool_evrcmp(pool, pool->solvables[p2].evr, pool->solvables[p].evr, EVRCMP_COMPARE) > 0)
+                     isdowngrade = 1;
+               }
+             if (isdowngrade || solv->updatemap_all || (solv->updatemap.size && MAPTST(&solv->updatemap, p2 - solv->installed->start)))
+               {
+                 if (solver_choicerulecheck(solv, p2, r, &m, &qcheck))
+                   continue;
+               }
+             qi.elements[j++] = p2;
+             qi.elements[j++] = qi.elements[i + 1];
            }
-         qi.elements[j++] = p2;
+         queue_truncate(&qi, j);
        }
-      queue_truncate(&qi, j);
 
-      if (!q.count || !qi.count)
+      if (!qi.count)
        {
          FOR_RULELITERALS(p, pp, r)
            if (p > 0)
@@ -3100,6 +3563,15 @@ solver_addchoicerules(Solver *solv)
          continue;
        }
 
+      queue_empty(&q);
+      /* split q from qi */
+      for (i = j = 0; i < qi.count; i += 2)
+       {
+         queue_push(&q, qi.elements[i + 1]);
+         qi.elements[j++] = qi.elements[i];
+       }
+      queue_truncate(&qi, j);
+
 
       /* now check the update rules of the installed package.
        * if all packages of the update rules are contained in
@@ -3119,14 +3591,18 @@ solver_addchoicerules(Solver *solv)
              break;
          if (p)
            break;
+         /* speed improvement: only check each package once */
          for (j = i + 1; j < qi.count; j++)
            if (qi.elements[i] == qi.elements[j])
              qi.elements[j] = 0;
        }
       /* empty map again */
+      negcnt = 0;
       FOR_RULELITERALS(p, pp, r)
         if (p > 0)
          MAPCLR(&m, p);
+       else
+         negcnt++;
       if (i == qi.count)
        {
 #if 0
@@ -3135,7 +3611,14 @@ solver_addchoicerules(Solver *solv)
 #endif
          continue;
        }
-
+      /* add neg elements to the front */
+      if (negcnt > 1)
+       {
+         i = 0;
+         FOR_RULELITERALS(p, pp, r)
+          if (p < 0 && p != r->p)
+            queue_insert(&q, i++, p);
+       }
       /* don't add identical rules */
       if (lastaddedp == r->p && lastaddedcnt == q.count)
        {
@@ -3153,7 +3636,7 @@ solver_addchoicerules(Solver *solv)
 
       solver_addrule(solv, r->p, 0, d);
       queue_push(&solv->weakruleq, solv->nrules - 1);
-      solv->choicerules_ref[solv->nrules - 1 - solv->choicerules] = rid;
+      queue_push(&infoq, rid);
 #if 0
       printf("OLD ");
       solver_printrule(solv, SOLV_DEBUG_RESULT, solv->rules + rid);
@@ -3161,20 +3644,21 @@ solver_addchoicerules(Solver *solv)
       solver_printrule(solv, SOLV_DEBUG_RESULT, solv->rules + solv->nrules - 1);
 #endif
     }
+  if (infoq.count)
+    solv->choicerules_info = solv_memdup2(infoq.elements, infoq.count, sizeof(Id));
   queue_free(&q);
   queue_free(&qi);
   queue_free(&qcheck);
+  queue_free(&infoq);
   map_free(&m);
   map_free(&mneg);
   solv->choicerules_end = solv->nrules;
-  /* shrink choicerules_ref */
-  solv->choicerules_ref = solv_realloc2(solv->choicerules_ref, solv->choicerules_end - solv->choicerules, sizeof(Id));
   POOL_DEBUG(SOLV_DEBUG_STATS, "choice rule creation took %d ms\n", solv_timems(now));
 }
 
-/* called when a choice rule is disabled by analyze_unsolvable. We also
- * have to disable all other choice rules so that the best packages get
- * picked */
+/* called when a choice rule needs to be disabled by analyze_unsolvable.
+ * We also have to disable all other choice rules so that the best packages
+ * get picked */
 void
 solver_disablechoicerules(Solver *solv, Rule *r)
 {
@@ -3183,7 +3667,8 @@ solver_disablechoicerules(Solver *solv, Rule *r)
   Map m;
   Rule *or;
 
-  or = solv->rules + solv->choicerules_ref[(r - solv->rules) - solv->choicerules];
+  solver_disablerule(solv, r);
+  or = solv->rules + solv->choicerules_info[(r - solv->rules) - solv->choicerules];
   map_init(&m, pool->nsolvables);
   FOR_RULELITERALS(p, pp, or)
     if (p > 0)
@@ -3196,13 +3681,14 @@ solver_disablechoicerules(Solver *solv, Rule *r)
       r = solv->rules + rid;
       if (r->d < 0)
        continue;
-      or = solv->rules + solv->choicerules_ref[(r - solv->rules) - solv->choicerules];
+      or = solv->rules + solv->choicerules_info[rid - solv->choicerules];
       FOR_RULELITERALS(p, pp, or)
         if (p > 0 && MAPTST(&m, p))
          break;
       if (p)
        solver_disablerule(solv, r);
     }
+  map_free(&m);
 }
 
 static void
@@ -3236,8 +3722,34 @@ prune_to_dup_packages(Solver *solv, Id p, Queue *q)
   queue_truncate(q, j);
 }
 
+static void
+prune_best_update(Solver *solv, Id p, Queue *q)
+{
+  if (solv->update_targets && solv->update_targets->elements[p - solv->installed->start])
+    prune_to_update_targets(solv, solv->update_targets->elements + solv->update_targets->elements[p - solv->installed->start], q);
+  if (solv->dupinvolvedmap.size && MAPTST(&solv->dupinvolvedmap, p))
+    prune_to_dup_packages(solv, p, q);
+  /* select best packages, just look at prio and version */
+  policy_filter_unwanted(solv, q, POLICY_MODE_RECOMMEND);
+}
+
+static void
+prune_disabled(Pool *pool, Queue *q)
+{
+  int i, j;
+  for (i = j = 0; i < q->count; i++)
+    {
+      Id p = q->elements[i];
+      Solvable *s = pool->solvables + p;
+      if (s->repo && s->repo != pool->installed && !MAPTST(pool->considered, p))
+       continue;
+      q->elements[j++] = p;
+    }
+  queue_truncate(q, j);
+}
+
 void
-solver_addbestrules(Solver *solv, int havebestinstalljobs)
+solver_addbestrules(Solver *solv, int havebestinstalljobs, int haslockjob)
 {
   Pool *pool = solv->pool;
   Id p;
@@ -3245,48 +3757,99 @@ solver_addbestrules(Solver *solv, int havebestinstalljobs)
   Repo *installed = solv->installed;
   Queue q, q2;
   Rule *r;
-  Queue r2pkg;
+  Queue infoq;
   int i, oldcnt;
+  Map *lockedmap = 0;
 
   solv->bestrules = solv->nrules;
   queue_init(&q);
   queue_init(&q2);
-  queue_init(&r2pkg);
+  queue_init(&infoq);
 
+  if (haslockjob)
+    {
+      int i;
+      lockedmap = solv_calloc(1, sizeof(Map));
+      map_init(lockedmap, pool->nsolvables);
+      for (i = 0, r = solv->rules + solv->jobrules; i < solv->ruletojob.count; i++, r++)
+       {
+         if (r->w2 || (solv->job.elements[solv->ruletojob.elements[i]] & SOLVER_JOBMASK) != SOLVER_LOCK)
+           continue;
+         p = r->p > 0 ? r->p : -r->p;
+         MAPSET(lockedmap, p);
+       }
+    }
   if (havebestinstalljobs)
     {
       for (i = 0; i < solv->job.count; i += 2)
        {
-         if ((solv->job.elements[i] & (SOLVER_JOBMASK | SOLVER_FORCEBEST)) == (SOLVER_INSTALL | SOLVER_FORCEBEST))
+         Id how = solv->job.elements[i];
+         if ((how & (SOLVER_JOBMASK | SOLVER_FORCEBEST)) == (SOLVER_INSTALL | SOLVER_FORCEBEST))
            {
-             int j;
+             int j, k;
              Id p2, pp2;
              for (j = 0; j < solv->ruletojob.count; j++)
-               if (solv->ruletojob.elements[j] == i)
-                 break;
-             if (j == solv->ruletojob.count)
-               continue;
-             r = solv->rules + solv->jobrules + j;
-             queue_empty(&q);
-             FOR_RULELITERALS(p2, pp2, r)
-               if (p2 > 0)
-                 queue_push(&q, p2);
-             if (!q.count)
-               continue;       /* orphaned */
-             /* select best packages, just look at prio and version */
-             oldcnt = q.count;
-             policy_filter_unwanted(solv, &q, POLICY_MODE_RECOMMEND);
-             if (q.count == oldcnt)
-               continue;       /* nothing filtered */
-             p2 = queue_shift(&q);
-             if (q.count < 2)
-               solver_addrule(solv, p2, q.count ? q.elements[0] : 0, 0);
-             else
-               solver_addrule(solv, p2, 0, pool_queuetowhatprovides(pool, &q));
-             queue_push(&r2pkg, -(solv->jobrules + j));
+               {
+                 if (solv->ruletojob.elements[j] != i)
+                   continue;
+                 r = solv->rules + solv->jobrules + j;
+                 queue_empty(&q);
+                 queue_empty(&q2);
+                 FOR_RULELITERALS(p2, pp2, r)
+                   {
+                     if (p2 > 0)
+                       queue_push(&q, p2);
+                     else if (p2 < 0)
+                       queue_push(&q2, p2);
+                   }
+                 if (pool->considered && pool->whatprovideswithdisabled)
+                   prune_disabled(pool, &q);
+                 if (!q.count)
+                   continue;   /* orphaned */
+                 /* select best packages, just look at prio and version */
+                 oldcnt = q.count;
+                 policy_filter_unwanted(solv, &q, POLICY_MODE_RECOMMEND);
+                 if (q.count == oldcnt)
+                   continue;   /* nothing filtered */
+                 if (lockedmap)
+                   {
+                     queue_insertn(&q, 0, q2.count, q2.elements);
+                     queue_empty(&q2);
+                     FOR_RULELITERALS(p2, pp2, r)
+                       {
+                         if (p2 <= 0)
+                           continue;
+                         if (installed && pool->solvables[p2].repo == installed)
+                           {
+                             if (MAPTST(lockedmap, p2))
+                               queue_pushunique(&q, p2);               /* we always want that package */
+                           }
+                         else if (MAPTST(lockedmap, p2))
+                           continue;
+                         queue_push(&q2, p2);
+                       }
+                     if (pool->considered && pool->whatprovideswithdisabled)
+                       prune_disabled(pool, &q2);
+                     policy_filter_unwanted(solv, &q2, POLICY_MODE_RECOMMEND);
+                     for (k = 0; k < q2.count; k++)
+                       queue_pushunique(&q, q2.elements[k]);
+                     queue_empty(&q2);
+                   }
+                 if (q2.count)
+                   queue_insertn(&q, 0, q2.count, q2.elements);
+                 p2 = queue_shift(&q);
+                 if (q.count < 2)
+                   solver_addrule(solv, p2, q.count ? q.elements[0] : 0, 0);
+                 else
+                   solver_addrule(solv, p2, 0, pool_queuetowhatprovides(pool, &q));
+                 if ((how & SOLVER_WEAK) != 0)
+                   queue_push(&solv->weakruleq, solv->nrules - 1);
+                 queue_push(&infoq, -(solv->jobrules + j));
+               }
            }
        }
     }
+  solv->bestrules_up = solv->nrules;
 
   if (installed && (solv->bestupdatemap_all || solv->bestupdatemap.size))
     {
@@ -3326,14 +3889,38 @@ solver_addbestrules(Solver *solv, int havebestinstalljobs)
                if (p2 > 0)
                  queue_push(&q, p2);
            }
-         if (solv->update_targets && solv->update_targets->elements[p - installed->start])
-           prune_to_update_targets(solv, solv->update_targets->elements + solv->update_targets->elements[p - installed->start], &q);
-         if (solv->dupinvolvedmap.size && MAPTST(&solv->dupinvolvedmap, p))
-           prune_to_dup_packages(solv, p, &q);
-         /* select best packages, just look at prio and version */
-         policy_filter_unwanted(solv, &q, POLICY_MODE_RECOMMEND);
+         if (lockedmap)
+           {
+             queue_empty(&q2);
+             queue_insertn(&q2, 0, q.count, q.elements);
+           }
+         prune_best_update(solv, p, &q);
          if (!q.count)
            continue;   /* orphaned */
+         if (lockedmap)
+           {
+             int j;
+             /* always ok to keep installed locked packages */
+             if (MAPTST(lockedmap, p))
+               queue_pushunique(&q2, p);
+             for (j = 0; j < q2.count; j++)
+               {
+                 Id p2 = q2.elements[j];
+                 if (pool->solvables[p2].repo == installed && MAPTST(lockedmap, p2))
+                   queue_pushunique(&q, p2);
+               }
+             /* filter out locked packages */
+             for (i = j = 0; j < q2.count; j++)
+               {
+                 Id p2 = q2.elements[j];
+                 if (pool->solvables[p2].repo == installed || !MAPTST(lockedmap, p2))
+                   q2.elements[i++] = p2;
+               }
+             queue_truncate(&q2, i);
+             prune_best_update(solv, p, &q2);
+             for (j = 0; j < q2.count; j++)
+               queue_pushunique(&q, q2.elements[j]);
+           }
          if (solv->bestobeypolicy)
            {
              /* also filter the best of the feature rule packages and add them */
@@ -3345,13 +3932,20 @@ solver_addbestrules(Solver *solv, int havebestinstalljobs)
                  FOR_RULELITERALS(p2, pp2, r)
                    if (p2 > 0)
                      queue_push(&q2, p2);
-                 if (solv->update_targets && solv->update_targets->elements[p - installed->start])
-                   prune_to_update_targets(solv, solv->update_targets->elements + solv->update_targets->elements[p - installed->start], &q2);
-                 if (solv->dupinvolvedmap.size && MAPTST(&solv->dupinvolvedmap, p))
-                   prune_to_dup_packages(solv, p, &q2);
-                 policy_filter_unwanted(solv, &q2, POLICY_MODE_RECOMMEND);
+                 prune_best_update(solv, p, &q2);
                  for (j = 0; j < q2.count; j++)
                    queue_pushunique(&q, q2.elements[j]);
+                 if (lockedmap)
+                   {
+                     queue_empty(&q2);
+                     FOR_RULELITERALS(p2, pp2, r)
+                       if (p2 > 0)
+                         if (pool->solvables[p2].repo == installed || !MAPTST(lockedmap, p2))
+                           queue_push(&q2, p2);
+                     prune_best_update(solv, p, &q2);
+                     for (j = 0; j < q2.count; j++)
+                       queue_pushunique(&q, q2.elements[j]);
+                   }
                }
            }
          if (solv->allowuninstall || solv->allowuninstall_all || (solv->allowuninstallmap.size && MAPTST(&solv->allowuninstallmap, p - installed->start)))
@@ -3371,7 +3965,7 @@ solver_addbestrules(Solver *solv, int havebestinstalljobs)
                    solver_addrule(solv, -p2, d, 0);
                  else
                    solver_addrule(solv, -p2, 0, -d);
-                 queue_push(&r2pkg, p);
+                 queue_push(&infoq, p);
                }
              for (i = 0; i < q.count; i++)
                MAPCLR(&m, q.elements[i]);
@@ -3382,22 +3976,29 @@ solver_addbestrules(Solver *solv, int havebestinstalljobs)
            solver_addrule(solv, p2, q.count ? q.elements[0] : 0, 0);
          else
            solver_addrule(solv, p2, 0, pool_queuetowhatprovides(pool, &q));
-         queue_push(&r2pkg, p);
+         queue_push(&infoq, p);
        }
       map_free(&m);
     }
-  if (r2pkg.count)
-    solv->bestrules_pkg = solv_memdup2(r2pkg.elements, r2pkg.count, sizeof(Id));
+  if (infoq.count)
+    solv->bestrules_info = solv_memdup2(infoq.elements, infoq.count, sizeof(Id));
   solv->bestrules_end = solv->nrules;
   queue_free(&q);
   queue_free(&q2);
-  queue_free(&r2pkg);
+  queue_free(&infoq);
+  if (lockedmap)
+    {
+      map_free(lockedmap);
+      solv_free(lockedmap);
+    }
 }
 
 
 
 
 /* yumobs rule handling */
+/* note that we use pool->obsoleteusescolors || pool->implicitobsoleteusescolors
+ * like in policy_findupdatepackages */
 
 static void
 find_obsolete_group(Solver *solv, Id obs, Queue *q)
@@ -3422,9 +4023,9 @@ find_obsolete_group(Solver *solv, Id obs, Queue *q)
          Id obs2, *obsp2;
          if (!os->obsoletes)
            continue;
-         if (pool->obsoleteusescolors && !pool_colormatch(pool, s2, os))
+         if ((pool->obsoleteusescolors || pool->implicitobsoleteusescolors) && !pool_colormatch(pool, s2, os))
            continue;
-         obsp2 = os->repo->idarraydata + os->obsoletes; 
+         obsp2 = os->repo->idarraydata + os->obsoletes;
          while ((obs2 = *obsp2++) != 0)
            if (obs2 == obs)
              break;
@@ -3440,9 +4041,9 @@ find_obsolete_group(Solver *solv, Id obs, Queue *q)
            continue;
          if (!os->obsoletes)
            continue;
-         if (pool->obsoleteusescolors && !pool_colormatch(pool, s2, os))
+         if ((pool->obsoleteusescolors || pool->implicitobsoleteusescolors) && !pool_colormatch(pool, s2, os))
            continue;
-         obsp2 = os->repo->idarraydata + os->obsoletes; 
+         obsp2 = os->repo->idarraydata + os->obsoletes;
          while ((obs2 = *obsp2++) != 0)
            if (obs2 == obs)
              break;
@@ -3452,7 +4053,7 @@ find_obsolete_group(Solver *solv, Id obs, Queue *q)
     }
   /* find names so that we can build groups */
   queue_init_clone(&qn, q);
-  prune_to_best_version(solv->pool, &qn);
+  prune_to_best_version(pool, &qn);
 #if 0
 {
   for (i = 0; i < qn.count; i++)
@@ -3506,7 +4107,7 @@ solver_addyumobsrules(Solver *solv)
   Repo *installed = solv->installed;
   Id p, op, *opp;
   Solvable *s;
-  Queue qo, qq, yumobsinfoq;
+  Queue qo, qq, infoq;
   int i, j, k;
   unsigned int now;
 
@@ -3539,7 +4140,7 @@ printf("checking yumobs for %s\n", pool_solvable2str(pool, s));
                    continue;
                  if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, pool->solvables + p2, obs))
                    continue;
-                 if (pool->obsoleteusescolors && !pool_colormatch(pool, s, s2))
+                 if ((pool->obsoleteusescolors || pool->implicitobsoleteusescolors) && !pool_colormatch(pool, s, s2))
                    continue;
                  queue_pushunique(&qo, obs);
                  break;
@@ -3552,7 +4153,7 @@ printf("checking yumobs for %s\n", pool_solvable2str(pool, s));
       queue_free(&qo);
       return;
     }
-  queue_init(&yumobsinfoq);
+  queue_init(&infoq);
   queue_init(&qq);
   for (i = 0; i < qo.count; i++)
     {
@@ -3570,10 +4171,10 @@ for (j = 0; j < qq.count; j++)
   else
     printf("%s\n", pool_solvid2str(pool, qq.elements[j]));
 #endif
-  
+
       if (!qq.count)
        continue;
-      /* at least two goups, build rules */
+      /* at least two groups, build rules */
       group = 0;
       for (j = 0; j < qq.count; j++)
        {
@@ -3599,22 +4200,70 @@ for (j = 0; j < qq.count; j++)
                    solver_addrule(solv, -p, qq.elements[groupstart], 0);
                  else
                    solver_addrule(solv, -p, 0, pool_ids2whatprovides(pool, qq.elements + groupstart, k - groupstart));
-                 queue_push(&yumobsinfoq, qo.elements[i]);
+                 queue_push(&infoq, qo.elements[i]);
                }
              groupstart = k + 1;
              groupk++;
            }
        }
     }
-  if (yumobsinfoq.count)
-    solv->yumobsrules_info = solv_memdup2(yumobsinfoq.elements, yumobsinfoq.count, sizeof(Id));
-  queue_free(&yumobsinfoq);
+  if (infoq.count)
+    solv->yumobsrules_info = solv_memdup2(infoq.elements, infoq.count, sizeof(Id));
+  queue_free(&infoq);
   queue_free(&qq);
   queue_free(&qo);
   solv->yumobsrules_end = solv->nrules;
   POOL_DEBUG(SOLV_DEBUG_STATS, "yumobs rule creation took %d ms\n", solv_timems(now));
 }
 
+/* recommendsrules are a copy of some recommends package rule but
+ * with some disfavored literals removed */
+void
+solver_addrecommendsrules(Solver *solv)
+{
+  Pool *pool = solv->pool;
+  int i, havedis, havepos;
+  Id p, pp;
+  Queue q, infoq;
+
+  solv->recommendsrules = solv->nrules;
+  queue_init(&q);
+  queue_init(&infoq);
+  for (i = 0; i < solv->recommendsruleq->count; i++)
+    {
+      int rid = solv->recommendsruleq->elements[i];
+      Rule *r = solv->rules + rid;
+      queue_empty(&q);
+      havedis = havepos = 0;
+      FOR_RULELITERALS(p, pp, r)
+       {
+         if (p > 0 && solv->favormap[p] < 0)
+           havedis = 1;
+         else
+           {
+             if (p > 0)
+               havepos = 1;
+             queue_push(&q, p);
+           }
+       }
+      if (!havedis)
+       continue;
+      solver_disablerule(solv, r);
+      if (!havepos || q.count < 2)
+       continue;
+      if (q.count == 2)
+       solver_addrule(solv, q.elements[0], q.elements[1], 0);
+      else
+       solver_addrule(solv, q.elements[0], 0, pool_ids2whatprovides(pool, q.elements + 1, q.count - 1));
+      queue_push(&infoq, rid);
+    }
+  if (infoq.count)
+    solv->recommendsrules_info = solv_memdup2(infoq.elements, infoq.count, sizeof(Id));
+  queue_free(&infoq);
+  queue_free(&q);
+  solv->recommendsrules_end = solv->nrules;
+}
+
 void
 solver_breakorphans(Solver *solv)
 {
@@ -3678,7 +4327,7 @@ solver_check_brokenorphanrules(Solver *solv, Queue *dq)
   Pool *pool = solv->pool;
   int i;
   Id l, pp;
-  
+
   queue_empty(dq);
   if (!solv->brokenorphanrules)
     return;
@@ -3707,3 +4356,122 @@ solver_check_brokenorphanrules(Solver *solv, Queue *dq)
     }
 }
 
+const char *
+solver_ruleinfo2str(Solver *solv, SolverRuleinfo type, Id source, Id target, Id dep)
+{
+  Pool *pool = solv->pool;
+  char *s;
+  Solvable *ss;
+  switch (type)
+    {
+    case SOLVER_RULE_DISTUPGRADE:
+      return pool_tmpjoin(pool, pool_solvid2str(pool, source), " does not belong to a distupgrade repository", 0);
+    case SOLVER_RULE_INFARCH:
+      return pool_tmpjoin(pool, pool_solvid2str(pool, source), " has inferior architecture", 0);
+    case SOLVER_RULE_UPDATE:
+      return pool_tmpjoin(pool, pool_solvid2str(pool, source), " needs to stay installed or be updated", 0);
+    case SOLVER_RULE_FEATURE:
+      return pool_tmpjoin(pool, pool_solvid2str(pool, source), " needs to stay installed or be updated/downgraded", 0);
+    case SOLVER_RULE_JOB:
+      return pool_tmpjoin(pool, "job ", pool_job2str(pool, target, dep, 0), 0);
+    case SOLVER_RULE_JOB_UNSUPPORTED:
+      return pool_tmpjoin(pool, "unsupported job ", pool_job2str(pool, target, dep, 0), 0);
+    case SOLVER_RULE_JOB_NOTHING_PROVIDES_DEP:
+      return pool_tmpjoin(pool, "nothing provides requested ", pool_dep2str(pool, dep), 0);
+    case SOLVER_RULE_JOB_UNKNOWN_PACKAGE:
+      return pool_tmpjoin(pool, "requested package ", pool_dep2str(pool, dep), " does not exist");
+    case SOLVER_RULE_JOB_PROVIDED_BY_SYSTEM:
+      return pool_tmpjoin(pool, "requested ", pool_dep2str(pool, dep), " is provided by the system");
+    case SOLVER_RULE_BEST:
+      if (source > 0)
+        return pool_tmpjoin(pool, "install best update candidate for ", pool_solvid2str(pool, source), 0);
+      if (target > 0)
+       {
+         target = solver_rule2job(solv, target, &dep);
+         return pool_tmpjoin(pool, "best package for job ", pool_job2str(pool, target, dep, 0), 0);
+       }
+     return "best rule";
+    case SOLVER_RULE_PKG:
+      return "bad pkg rule type";
+    case SOLVER_RULE_PKG_NOT_INSTALLABLE:
+      ss = pool->solvables + source;
+      if (pool_disabled_solvable(pool, ss))
+        return pool_tmpjoin(pool, pool_solvid2str(pool, source), " is disabled", 0);
+      if (ss->arch && ss->arch != ARCH_SRC && ss->arch != ARCH_NOSRC &&
+          pool->id2arch && pool_arch2score(pool, ss->arch) == 0)
+        return pool_tmpjoin(pool, pool_solvid2str(pool, source), " does not have a compatible architecture", 0);
+      return pool_tmpjoin(pool, pool_solvid2str(pool, source), " is not installable", 0);
+    case SOLVER_RULE_PKG_NOTHING_PROVIDES_DEP:
+      s = pool_tmpjoin(pool, "nothing provides ", pool_dep2str(pool, dep), 0);
+      return pool_tmpappend(pool, s, " needed by ", pool_solvid2str(pool, source));
+    case SOLVER_RULE_PKG_SAME_NAME:
+      s = pool_tmpjoin(pool, "cannot install both ", pool_solvid2str(pool, source), 0);
+      return pool_tmpappend(pool, s, " and ", pool_solvid2str(pool, target));
+    case SOLVER_RULE_PKG_CONFLICTS:
+      s = pool_tmpappend(pool, pool_solvid2str(pool, source), " conflicts with ", pool_dep2str(pool, dep));
+      if (target)
+        s = pool_tmpappend(pool, s, " provided by ", pool_solvid2str(pool, target));
+      return s;
+    case SOLVER_RULE_PKG_SELF_CONFLICT:
+      s = pool_tmpjoin(pool, pool_solvid2str(pool, source), " conflicts with ", 0);
+      return pool_tmpappend(pool, s, pool_dep2str(pool, dep), " provided by itself");
+    case SOLVER_RULE_PKG_OBSOLETES:
+      s = pool_tmpappend(pool, pool_solvid2str(pool, source), " obsoletes ", pool_dep2str(pool, dep));
+      if (target)
+       s = pool_tmpappend(pool, s, " provided by ", pool_solvid2str(pool, target));
+      return s;
+    case SOLVER_RULE_PKG_INSTALLED_OBSOLETES:
+      s = pool_tmpjoin(pool, "installed ", pool_solvid2str(pool, source), 0);
+      s = pool_tmpappend(pool, s, " obsoletes ", pool_dep2str(pool, dep));
+      if (target)
+       s = pool_tmpappend(pool, s, " provided by ", pool_solvid2str(pool, target));
+      return s;
+    case SOLVER_RULE_PKG_IMPLICIT_OBSOLETES:
+      s = pool_tmpappend(pool, pool_solvid2str(pool, source), " implicitly obsoletes ", pool_dep2str(pool, dep));
+      if (target)
+       s = pool_tmpappend(pool, s, " provided by ", pool_solvid2str(pool, target));
+      return s;
+    case SOLVER_RULE_PKG_REQUIRES:
+      return pool_tmpjoin(pool, pool_solvid2str(pool, source), " requires ", pool_dep2str(pool, dep));
+    case SOLVER_RULE_PKG_RECOMMENDS:
+      return pool_tmpjoin(pool, pool_solvid2str(pool, source), " recommends ", pool_dep2str(pool, dep));
+    case SOLVER_RULE_PKG_CONSTRAINS:
+      s = pool_tmpappend(pool, pool_solvid2str(pool, source), " has constraint ", pool_dep2str(pool, dep));
+      return pool_tmpappend(pool, s, " conflicting with ", pool_solvid2str(pool, target));
+    case SOLVER_RULE_PKG_SUPPLEMENTS:
+      s = pool_tmpjoin(pool, pool_solvid2str(pool, source), " supplements ", pool_dep2str(pool, dep));
+      if (target)
+       s = pool_tmpappend(pool, s, " provided by ", pool_solvid2str(pool, target));
+      return s;
+    case SOLVER_RULE_YUMOBS:
+      s = pool_tmpjoin(pool, "both ", pool_solvid2str(pool, source), " and ");
+      s = pool_tmpjoin(pool, s, pool_solvid2str(pool, target), " obsolete ");
+      return pool_tmpappend(pool, s, pool_dep2str(pool, dep), 0);
+    case SOLVER_RULE_BLACK:
+      return pool_tmpjoin(pool, pool_solvid2str(pool, source), " can only be installed by a direct request", 0);
+    case SOLVER_RULE_STRICT_REPO_PRIORITY:
+      return pool_tmpjoin(pool, pool_solvid2str(pool, source), " is excluded by strict repo priority", 0);
+    case SOLVER_RULE_LEARNT:
+      return "learnt rule";
+    case SOLVER_RULE_CHOICE:
+      if (source > 0)
+       {
+         const char *s2;
+          type = solver_ruleinfo(solv, source, &source, &target, &dep);
+         s2 = solver_ruleinfo2str(solv, type, source, target, dep);
+         return pool_tmpjoin(pool, s2, " (limited version)", 0);
+       }
+      return "choice rule";
+    case SOLVER_RULE_RECOMMENDS:
+      if (source > 0)
+       {
+         const char *s2;
+          type = solver_ruleinfo(solv, source, &source, &target, &dep);
+         s2 = solver_ruleinfo2str(solv, type, source, target, dep);
+         return pool_tmpjoin(pool, s2, " (limited version)", 0);
+       }
+      return "recommends rule";
+    default:
+      return "bad rule type";
+    }
+}