[yum/f17] update to latest HEAD.

Zdeněk Pavlas zpavlas at fedoraproject.org
Tue Aug 28 14:17:10 UTC 2012


commit f0ffad9f33b4f9021362c255338675f993bea49d
Author: Zdeněk Pavlas <zpavlas at redhat.com>
Date:   Tue Aug 28 16:16:00 2012 +0200

    update to latest HEAD.

 yum-HEAD.patch              | 2538 ++++++++++++++++++++++++++++++++++++-------
 yum-completion-helper.patch |   11 +
 yum.spec                    |    9 +-
 3 files changed, 2155 insertions(+), 403 deletions(-)
---
diff --git a/yum-HEAD.patch b/yum-HEAD.patch
index 3e9193b..3efb022 100644
--- a/yum-HEAD.patch
+++ b/yum-HEAD.patch
@@ -96,7 +96,7 @@ index 2f6154e..2e5a052 100644
 diff --git a/cli.py b/cli.py
 old mode 100644
 new mode 100755
-index 6056d38..597efd6
+index 6056d38..c7d89a1
 --- a/cli.py
 +++ b/cli.py
 @@ -25,7 +25,7 @@ import sys
@@ -253,6 +253,18 @@ index 6056d38..597efd6
          except ValueError, e:
              self.logger.critical(_('Options Error: %s'), e)
              sys.exit(1)
+@@ -290,9 +324,9 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+                                    self.term.MODE['normal'])
+                 print _("  Installed: %s-%s at %s") %(name, ver,
+                                                    sm_ui_time(pkg.installtime))
+-                print _("  Built    : %s at %s") % (pkg.packager,
++                print _("  Built    : %s at %s") % (to_unicode(pkg.packager),
+                                                     sm_ui_time(pkg.buildtime))
+-                print _("  Committed: %s at %s") % (pkg.committer,
++                print _("  Committed: %s at %s") % (to_unicode(pkg.committer),
+                                                     sm_ui_date(pkg.committime))
+             sys.exit(0)
+ 
 @@ -318,9 +352,11 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
          time.sleep(sleeptime)
          
@@ -494,7 +506,7 @@ index 6056d38..597efd6
          # get the list of available packages
          # iterate over the user's list
          # add packages to Transaction holding class if they match.
-@@ -710,11 +822,26 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -710,11 +822,36 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
          for arg in userlist:
              if (arg.endswith('.rpm') and (yum.misc.re_remote_url(arg) or
                                            os.path.exists(arg))):
@@ -509,12 +521,22 @@ index 6056d38..597efd6
 +                elif basecmd == 'install-n':
 +                    txmbrs = self.install(name=arg)
 +                elif basecmd == 'install-na':
-+                    n,a = arg.split('.')
++                    try:
++                        n,a = arg.rsplit('.', 1)
++                    except:
++                        self.verbose_logger.warning(_('Bad %s argument %s.'),
++                                                    basecmd, arg)
++                        continue
 +                    txmbrs = self.install(name=n, arch=a)
 +                elif basecmd == 'install-nevra':
-+                    nevr,a = arg.rsplit('.', 2)
-+                    n,ev,r = nevr.rsplit('-', 3)
-+                    e,v    = ev.split(':', 2)
++                    try:
++                        nevr,a = arg.rsplit('.', 1)
++                        n,ev,r = nevr.rsplit('-', 2)
++                        e,v    = ev.split(':', 1)
++                    except:
++                        self.verbose_logger.warning(_('Bad %s argument %s.'),
++                                                    basecmd, arg)
++                        continue
 +                    txmbrs = self.install(name=n,
 +                                          epoch=e, version=v, release=r, arch=a)
 +                else:
@@ -523,7 +545,7 @@ index 6056d38..597efd6
              except yum.Errors.InstallError:
                  self.verbose_logger.log(yum.logginglevels.INFO_2,
                                          _('No package %s%s%s available.'),
-@@ -723,6 +850,7 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -723,6 +860,7 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
                  self._maybeYouMeant(arg)
              else:
                  done = True
@@ -531,7 +553,7 @@ index 6056d38..597efd6
          if len(self.tsInfo) > oldcount:
              change = len(self.tsInfo) - oldcount
              return 2, [P_('%d package to install', '%d packages to install', change) % change]
-@@ -732,9 +860,27 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -732,9 +870,27 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
          return 0, [_('Nothing to do')]
          
      def updatePkgs(self, userlist, quiet=0, update_to=False):
@@ -562,7 +584,7 @@ index 6056d38..597efd6
          # if there is no userlist, then do global update below
          # this is probably 90% of the calls
          # if there is a userlist then it's for updating pkgs, not obsoleting
-@@ -745,21 +891,18 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -745,21 +901,18 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
  
          else:
              # go through the userlist - look for items that are local rpms. If we find them
@@ -593,7 +615,7 @@ index 6056d38..597efd6
  
          if len(self.tsInfo) > oldcount:
              change = len(self.tsInfo) - oldcount
-@@ -770,9 +913,24 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -770,9 +923,24 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
      #  Note that we aren't in __init__ yet for a couple of reasons, but we 
      # probably will get there for 3.2.28.
      def distroSyncPkgs(self, userlist):
@@ -621,7 +643,7 @@ index 6056d38..597efd6
  
          level = 'diff'
          if userlist and userlist[0] in ('full', 'diff', 'different'):
-@@ -831,6 +989,7 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -831,6 +999,7 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
                          continue
  
                      nayi = napkg.yumdb_info
@@ -629,14 +651,16 @@ index 6056d38..597efd6
                      for apkg in self.pkgSack.searchPkgTuple(napkg.pkgtup):
                          if ('checksum_type' in nayi and
                              'checksum_data' in nayi and
-@@ -866,10 +1025,19 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -865,15 +1034,54 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+         else:
              return 0, [_('No Packages marked for Distribution Synchronization')]
  
-     def erasePkgs(self, userlist):
+-    def erasePkgs(self, userlist):
 -        """take user commands and populate a transaction wrapper with packages
 -           to be erased/removed"""
 -        
 -        oldcount = len(self.tsInfo)
++    def erasePkgs(self, userlist, pos=False, basecmd='remove'):
 +        """Take user commands and populate a transaction wrapper with
 +        packages to be erased.
 +
@@ -653,7 +677,42 @@ index 6056d38..597efd6
  
          all_rms = []
          for arg in userlist:
-@@ -884,12 +1052,24 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+-            rms = self.remove(pattern=arg)
++            if pos:
++                rms = self.remove(po=arg)
++                if rms:
++                    all_rms.extend(rms)
++                continue
++
++            if False: pass
++            elif basecmd in ('erase-n', 'remove-n'):
++                rms = self.remove(name=arg)
++            elif basecmd in ('erase-na', 'remove-na'):
++                try:
++                    n,a = arg.rsplit('.', 1)
++                except:
++                    self.verbose_logger.warning(_('Bad %s argument %s.'),
++                                                basecmd, arg)
++                    continue
++                rms = self.remove(name=n, arch=a)
++            elif basecmd in ('erase-nevra', 'remove-nevra'):
++                try:
++                    nevr,a = arg.rsplit('.', 1)
++                    n,ev,r = nevr.rsplit('-', 2)
++                    e,v    = ev.split(':', 1)
++                except:
++                    self.verbose_logger.warning(_('Bad %s argument %s.'),
++                                                basecmd, arg)
++                    continue
++                rms = self.remove(name=n, epoch=e, version=v, release=r, arch=a)
++            else:
++                assert basecmd in ('erase', 'remove'), basecmd
++                rms = self.remove(pattern=arg)
++
+             if not rms:
+                 self._checkMaybeYouMeant(arg, always_output=False, rpmdb_only=True)
+             all_rms.extend(rms)
+@@ -884,12 +1092,24 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
              return 0, [_('No Packages marked for removal')]
      
      def downgradePkgs(self, userlist):
@@ -681,7 +740,7 @@ index 6056d38..597efd6
          for arg in userlist:
              if (arg.endswith('.rpm') and (yum.misc.re_remote_url(arg) or
                                            os.path.exists(arg))):
-@@ -905,26 +1085,44 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -905,26 +1125,44 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
                                          self.term.MODE['bold'], arg,
                                          self.term.MODE['normal'])
                  self._maybeYouMeant(arg)
@@ -730,7 +789,7 @@ index 6056d38..597efd6
              except yum.Errors.ReinstallRemoveError:
                  self._checkMaybeYouMeant(arg, always_output=False)
              except yum.Errors.ReinstallInstallError, e:
-@@ -940,15 +1138,31 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -940,15 +1178,31 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
              except yum.Errors.ReinstallError, e:
                  assert False, "Shouldn't happen, but just in case"
                  self.verbose_logger.log(yum.logginglevels.INFO_2, e)
@@ -765,7 +824,7 @@ index 6056d38..597efd6
          # read in each package into a YumLocalPackage Object
          # append it to self.localPackages
          # check if it can be installed or updated based on nevra versus rpmdb
-@@ -972,20 +1186,25 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -972,20 +1226,25 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
          return 0, [_('Nothing to do')]
  
      def returnPkgLists(self, extcmds, installed_available=False):
@@ -805,7 +864,7 @@ index 6056d38..597efd6
          special = ['available', 'installed', 'all', 'extras', 'updates', 'recent',
                     'obsoletes']
          
-@@ -1017,8 +1236,25 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -1017,8 +1276,25 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
          return ypl
  
      def search(self, args):
@@ -833,7 +892,7 @@ index 6056d38..597efd6
          
          # call the yum module search function with lists of tags to search
          # and what to search for
-@@ -1108,9 +1344,20 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -1108,9 +1384,20 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
          return 0, matching
  
      def deplist(self, args):
@@ -842,13 +901,13 @@ index 6056d38..597efd6
 +        """Print out a formatted list of dependencies for a list of
 +        packages.  This is a cli wrapper method for
 +        :class:`yum.YumBase.findDeps`.
-+
+ 
 +        :param args: a list of names or wildcards specifying packages
 +           that should have their dependenices printed
 +        :return: (exit_code, [ errors ])
 +
 +        exit_code is::
- 
++
 +            0 = we're done, exit
 +            1 = we've errored, exit with error string
 +            2 = we've got work yet to do, onto the next stage
@@ -856,7 +915,7 @@ index 6056d38..597efd6
          pkgs = []
          for arg in args:
              if (arg.endswith('.rpm') and (yum.misc.re_remote_url(arg) or
-@@ -1131,10 +1378,19 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -1131,10 +1418,19 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
          return 0, []
  
      def provides(self, args):
@@ -880,7 +939,7 @@ index 6056d38..597efd6
          old_sdup = self.conf.showdupesfromrepos
          # For output, as searchPackageProvides() is always in showdups mode
          self.conf.showdupesfromrepos = True
-@@ -1147,6 +1403,8 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -1147,6 +1443,8 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
              paths = set(sys.path + os.environ['PATH'].split(':'))
              nargs = []
              for arg in args:
@@ -889,7 +948,7 @@ index 6056d38..597efd6
                  if yum.misc.re_filename(arg) or yum.misc.re_glob(arg):
                      continue
                  for path in paths:
-@@ -1163,20 +1421,77 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -1163,20 +1461,77 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
          return 0, []
      
      def resolveDepCli(self, args):
@@ -972,13 +1031,14 @@ index 6056d38..597efd6
          hdrcode = pkgcode = xmlcode = dbcode = expccode = 0
          pkgresults = hdrresults = xmlresults = dbresults = expcresults = []
          msg = self.fmtKeyValFill(_('Cleaning repos: '), 
-@@ -1228,7 +1543,19 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -1228,130 +1583,257 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
          return code, []
  
      def returnGroupLists(self, userlist):
 +        """Print out a list of groups that match the given names or
 +        wildcards.
  
+-        uservisible=1
 +        :param extcmds: a list of names or wildcards specifying
 +           groups to list
 +        :return: (exit_code, [ errors ])
@@ -989,22 +1049,207 @@ index 6056d38..597efd6
 +            1 = we've errored, exit with error string
 +            2 = we've got work yet to do, onto the next stage        
 +        """
-         uservisible=1
++        return self._returnGroupLists(userlist)
++
++    def _returnGroupLists(self, userlist, summary=False):
++        # What data are we showing...
++        wts_map = {'hidden' : 'hidden',
++                   'language' : 'lang',
++                   'languages' : 'lang',
++                   'lang' : 'lang',
++                   'langs' : 'lang',
++                   'environment' : 'env',
++                   'environments' : 'env',
++                   'env' : 'env',
++                   'envs' : 'env',
++                   'package' : 'pkg',
++                   'packages' : 'pkg',
++                   'pkg' : 'pkg',
++                   'pkgs' : 'pkg',
++                   'available' : 'avail',
++                   'avail' : 'avail',
++                   'installed' : 'inst',
++                   'inst' : 'inst',
++                   'id' : 'id',
++                   'ids' : 'id',
++                   }
++        verb = self.verbose_logger.isEnabledFor(yum.logginglevels.DEBUG_3)
++        wts = {'hidden' : False,
++               'lang'   : None,
++               'env'    : None,
++               'pkg'    : None,
++               'inst'   : None,
++               'avail'  : None,
++               'id'     : verb}
              
-         if len(userlist) > 0:
-@@ -1254,7 +1581,7 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
-                 msg += ' (%s)' % group.groupid
-             if group.langonly:
-                 msg += ' [%s]' % group.langonly
--            self.verbose_logger.log(yum.logginglevels.INFO_2, '%s', msg)
-+            self.verbose_logger.info('%s', msg)
+-        if len(userlist) > 0:
+-            if userlist[0] == 'hidden':
+-                uservisible=0
+-                userlist.pop(0)
++        ouserlist = userlist[:]
++        while userlist:
++            arg = userlist[0]
++            val = True
++            if arg.startswith('no'):
++                arg = arg[2:]
++                val = False
++            if arg not in wts_map:
++                break
++            wts[wts_map[arg]] = val
++            userlist.pop(0)
+         if not userlist:
+             userlist = None # Match everything...
  
-         done = False
+-        installed, available = self.doGroupLists(uservisible=uservisible,
+-                                                 patterns=userlist)
+-        
+-        if not installed and not available:
+-            self.logger.error(_('Warning: No groups match: %s'),
+-                              ", ".join(userlist))
+-            return 0, []
++        if wts['inst'] is None and wts['avail'] is None:
++            wts['inst']  = True
++            wts['avail'] = True
+ 
+-        def _out_grp(sect, group):
+-            if not done:
+-                self.verbose_logger.log(yum.logginglevels.INFO_2, sect)
+-            msg = '   %s' % group.ui_name
+-            if self.verbose_logger.isEnabledFor(yum.logginglevels.DEBUG_3):
+-                msg += ' (%s)' % group.groupid
+-            if group.langonly:
+-                msg += ' [%s]' % group.langonly
+-            self.verbose_logger.log(yum.logginglevels.INFO_2, '%s', msg)
++        if wts['lang'] is None and wts['pkg'] is None and wts['env'] is None:
++            wts['env'] = True
++            wts['pkg'] = True
+ 
+-        done = False
+-        for group in installed:
+-            if group.langonly: continue
+-            _out_grp(_('Installed Groups:'), group)
+-            done = True
++        uv  = not wts['hidden']
++        dGL = self.doGroupLists(patterns=userlist,
++                                uservisible=uv, return_evgrps=True)
+ 
+-        done = False
+-        for group in installed:
+-            if not group.langonly: continue
+-            _out_grp(_('Installed Language Groups:'), group)
+-            done = True
++        installed, available, ievgrps, evgrps = dGL
+ 
+-        done = False
+-        for group in available:
+-            if group.langonly: continue
+-            _out_grp(_('Available Groups:'), group)
+-            done = True
++        if not wts['env']:
++            ievgrps = []
++            evgrps  = []
+ 
+-        done = False
+-        for group in available:
+-            if not group.langonly: continue
+-            _out_grp(_('Available Language Groups:'), group)
+-            done = True
++        if not wts['inst']:
++            installed = []
++            ievgrps   = []
++        if not wts['avail']:
++            available = []
++            evgrps    = []
++        
++        done = []
++        def _out_grp(sect, groups):
++            if not groups:
++                return
+ 
+-        return 0, [_('Done')]
++            done.append(sect)
++            if summary:
++                self.verbose_logger.log(yum.logginglevels.INFO_2,
++                                        "%s %u", sect, len(groups))
++                return
+ 
+-    def returnGroupSummary(self, userlist):
++            self.verbose_logger.log(yum.logginglevels.INFO_2, sect)
+ 
+-        uservisible=1
+-            
+-        if len(userlist) > 0:
+-            if userlist[0] == 'hidden':
+-                uservisible=0
+-                userlist.pop(0)
+-        if not userlist:
+-            userlist = None # Match everything...
++            for group in groups:
++                msg = '   %s' % group.ui_name
++                if wts['id']:
++                    msg += ' (%s)' % group.compsid
++                if group.langonly:
++                    msg += ' [%s]' % group.langonly
++                self.verbose_logger.info('%s', msg)
+ 
+-        installed, available = self.doGroupLists(uservisible=uservisible,
+-                                                 patterns=userlist)
+-        
+-        def _out_grp(sect, num):
+-            if not num:
+-                return
+-            self.verbose_logger.log(yum.logginglevels.INFO_2, '%s %u', sect,num)
+-        done = 0
++        _out_grp(_('Installed Environment Groups:'), ievgrps)
++        _out_grp(_('Available Environment Groups:'), evgrps)
++
++        groups = []
          for group in installed:
-@@ -1283,7 +1610,20 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
-         return 0, [_('Done')]
+             if group.langonly: continue
+-            done += 1
+-        _out_grp(_('Installed Groups:'), done)
++            if not wts['pkg']: continue
++            groups.append(group)
++        _out_grp(_('Installed Groups:'), groups)
+ 
+-        done = 0
++        groups = []
+         for group in installed:
+             if not group.langonly: continue
+-            done += 1
+-        _out_grp(_('Installed Language Groups:'), done)
++            if not wts['lang']: continue
++            groups.append(group)
++        _out_grp(_('Installed Language Groups:'), groups)
+ 
+-        done = False
++        groups = []
+         for group in available:
+             if group.langonly: continue
+-            done += 1
+-        _out_grp(_('Available Groups:'), done)
++            if not wts['pkg']: continue
++            groups.append(group)
++        _out_grp(_('Available Groups:'), groups)
+ 
+-        done = False
++        groups = []
+         for group in available:
+             if not group.langonly: continue
+-            done += 1
+-        _out_grp(_('Available Language Groups:'), done)
++            if not wts['lang']: continue
++            groups.append(group)
++        _out_grp(_('Available Language Groups:'), groups)
++
++        if not done:
++            self.logger.error(_('Warning: No Environments/Groups match: %s'),
++                              ", ".join(ouserlist))
++            return 0, []
  
-     def returnGroupSummary(self, userlist):
+         return 0, [_('Done')]
++
++    def returnGroupSummary(self, userlist):
 +        """Print a summary of the groups that match the given names or
 +        wildcards.
 +
@@ -1012,18 +1257,14 @@ index 6056d38..597efd6
 +           groups to summarise. If *userlist* is an empty list, all
 +           installed and available packages will be summarised
 +        :return: (exit_code, [ errors ])
- 
++
 +        exit_code is::
 +
 +            0 = we're done, exit
 +            1 = we've errored, exit with error string
 +            2 = we've got work yet to do, onto the next stage
 +        """
-         uservisible=1
-             
-         if len(userlist) > 0:
-@@ -1327,7 +1667,19 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
-         return 0, [_('Done')]
++        return self._returnGroupLists(userlist, summary=True)
      
      def returnGroupInfo(self, userlist):
 -        """returns complete information on a list of groups"""
@@ -1042,8 +1283,31 @@ index 6056d38..597efd6
 +        """
          for strng in userlist:
              group_matched = False
-             for group in self.comps.return_groups(strng):
-@@ -1339,9 +1691,19 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+-            for group in self.comps.return_groups(strng):
+-                self.displayPkgsInGroups(group)
+-                group_matched = True
++
++            pkg_grp = True
++            grp_grp = True
++            if strng.startswith('@^'):
++                strng = strng[2:]
++                pkg_grp = False
++            elif strng.startswith('@'):
++                strng = strng[1:]
++                grp_grp = False
++
++            if grp_grp:
++                for evgroup in self.comps.return_environments(strng):
++                    self.displayGrpsInEnvironments(evgroup)
++                    group_matched = True
++            if pkg_grp:
++                for group in self.comps.return_groups(strng):
++                    self.displayPkgsInGroups(group)
++                    group_matched = True
+ 
+             if not group_matched:
+-                self.logger.error(_('Warning: Group %s does not exist.'), strng)
++                self.logger.error(_('Warning: Group/Environment %s does not exist.'), strng)
          
          return 0, []
          
@@ -1066,8 +1330,38 @@ index 6056d38..597efd6
          pkgs_used = []
          
          for group_string in grouplist:
-@@ -1351,7 +1713,7 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
++
++            grp_grp = True
++            pkg_grp = True
++            if group_string.startswith('@^'):
++                pkg_grp = False
++                group_string = group_string[2:]
++            elif group_string.startswith('@'):
++                grp_grp = False
++                group_string = group_string[1:]
++
+             group_matched = False
+-            for group in self.comps.return_groups(group_string):
++            groups = []
++            if grp_grp:
++                groups = self.comps.return_environments(group_string)
++            for group in groups:
+                 group_matched = True
  
++                try:
++                    txmbrs = self.selectEnvironment(group.environmentid,
++                                                    upgrade=upgrade)
++                except yum.Errors.GroupsError:
++                    self.logger.critical(_('Warning: Environment %s does not exist.'), group_string)
++                    continue
++                else:
++                    pkgs_used.extend(txmbrs)
++
++            groups = []
++            if pkg_grp:
++                groups = self.comps.return_groups(group_string)
++            for group in groups:
++                group_matched = True
              
                  try:
 -                    txmbrs = self.selectGroup(group.groupid)
@@ -1075,7 +1369,7 @@ index 6056d38..597efd6
                  except yum.Errors.GroupsError:
                      self.logger.critical(_('Warning: Group %s does not exist.'), group_string)
                      continue
-@@ -1368,8 +1730,18 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -1368,17 +1850,61 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
              return 2, [P_('%d package to Install', '%d packages to Install', len(pkgs_used)) % len(pkgs_used)]
  
      def removeGroups(self, grouplist):
@@ -1094,8 +1388,58 @@ index 6056d38..597efd6
 +        """
          pkgs_used = []
          for group_string in grouplist:
-             try:
-@@ -1389,7 +1761,7 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+-            try:
+-                txmbrs = self.groupRemove(group_string)
+-            except yum.Errors.GroupsError:
+-                self.logger.critical(_('No group named %s exists'), group_string)
+-                continue
+-            else:
+-                pkgs_used.extend(txmbrs)
++
++            grp_grp = True
++            pkg_grp = True
++            if group_string.startswith('@^'):
++                pkg_grp = False
++                group_string = group_string[2:]
++            elif group_string.startswith('@'):
++                grp_grp = False
++                group_string = group_string[1:]
++
++            groups = []
++            if grp_grp:
++                if self.conf.group_command == 'objects':
++                    groups = self.igroups.return_environments(group_string)
++                else:
++                    groups = self.comps.return_environments(group_string)
++                if not groups:
++                    self.logger.critical(_('No Environment named %s exists'), group_string)
++            for group in groups:
++                try:
++                    txmbrs = self.environmentRemove(group.environmentid)
++                except yum.Errors.GroupsError:
++                    continue
++                else:
++                    pkgs_used.extend(txmbrs)
++
++            groups = []
++            if pkg_grp:
++                if self.conf.group_command == 'objects':
++                    groups = self.igroups.return_groups(group_string)
++                else:
++                    groups = self.comps.return_groups(group_string)
++                if not groups:
++                    self.logger.critical(_('No group named %s exists'), group_string)
++            for group in groups:
++                try:
++                    txmbrs = self.groupRemove(group.groupid)
++                except yum.Errors.GroupsError:
++                    continue
++                else:
++                    pkgs_used.extend(txmbrs)
+                 
+         if not pkgs_used:
+             return 0, [_('No packages to remove from groups')]
+@@ -1389,7 +1915,7 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
  
      def _promptWanted(self):
          # shortcut for the always-off/always-on options
@@ -1104,7 +1448,7 @@ index 6056d38..597efd6
              return False
          if self.conf.alwaysprompt:
              return True
-@@ -1400,7 +1772,6 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -1400,7 +1926,6 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
          #  package wasn't explictly given on the command line
          for txmbr in self.tsInfo.getMembers():
              if txmbr.isDep or \
@@ -1112,7 +1456,7 @@ index 6056d38..597efd6
                     txmbr.name not in self.extcmds:
                  return True
          
-@@ -1408,11 +1779,11 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -1408,11 +1933,11 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
          return False
  
      def usage(self):
@@ -1126,7 +1470,7 @@ index 6056d38..597efd6
          sys.stdout.write(self.optparser.get_usage())
      
      def _installable(self, pkg, ematch=False):
-@@ -1468,9 +1839,9 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -1468,9 +1993,9 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
          return False
  
  class YumOptionParser(OptionParser):
@@ -1138,7 +1482,7 @@ index 6056d38..597efd6
  
      def __init__(self,base, **kwargs):
          # check if this is called with a utils=True/False parameter
-@@ -1488,13 +1859,23 @@ class YumOptionParser(OptionParser):
+@@ -1488,13 +2013,23 @@ class YumOptionParser(OptionParser):
          self._addYumBasicOptions()
  
      def error(self, msg):
@@ -1164,7 +1508,7 @@ index 6056d38..597efd6
          try:
              args = _filtercmdline(
                          ('--noplugins','--version','-q', '-v', "--quiet", "--verbose"), 
-@@ -1521,7 +1902,15 @@ class YumOptionParser(OptionParser):
+@@ -1521,7 +2056,15 @@ class YumOptionParser(OptionParser):
          return ret
          
      def setupYumConfig(self, args=None):
@@ -1181,7 +1525,13 @@ index 6056d38..597efd6
          if not args:
              (opts, cmds) = self.parse_args()
          else:
-@@ -1536,13 +1925,14 @@ class YumOptionParser(OptionParser):
+@@ -1533,16 +2076,20 @@ class YumOptionParser(OptionParser):
+         try:
+             # config file is parsed and moving us forward
+             # set some things in it.
++
++            if opts.tolerant or self.base.conf.tolerant: # Make it slower capt.
++                self.base.conf.recheck_installed_requires = True
                  
              # Handle remaining options
              if opts.assumeyes:
@@ -1201,7 +1551,7 @@ index 6056d38..597efd6
                  self.base.conf.cache = 1
  
              if opts.obsoletes:
-@@ -1610,10 +2000,6 @@ class YumOptionParser(OptionParser):
+@@ -1610,10 +2157,6 @@ class YumOptionParser(OptionParser):
                      self.base.usage()
                      sys.exit(1)
  
@@ -1212,7 +1562,7 @@ index 6056d38..597efd6
              # Disable all gpg key checking, if requested.
              if opts.nogpgcheck:
                  #  Altering the normal configs. doesn't work too well, esp. with
-@@ -1640,6 +2026,14 @@ class YumOptionParser(OptionParser):
+@@ -1640,6 +2183,14 @@ class YumOptionParser(OptionParser):
          sys.exit(1)
  
      def getRoot(self,opts):
@@ -1227,7 +1577,7 @@ index 6056d38..597efd6
          self._checkAbsInstallRoot(opts)
          # If the conf file is inside the  installroot - use that.
          # otherwise look for it in the normal root
-@@ -1713,6 +2107,10 @@ class YumOptionParser(OptionParser):
+@@ -1713,6 +2264,10 @@ class YumOptionParser(OptionParser):
                          help=_("verbose operation"))
          group.add_option("-y", "--assumeyes", dest="assumeyes",
                  action="store_true", help=_("answer yes for all questions"))
@@ -1240,10 +1590,10 @@ index 6056d38..597efd6
          group.add_option("--installroot", help=_("set install root"), 
 diff --git a/completion-helper.py b/completion-helper.py
 new file mode 100755
-index 0000000..52a5463
+index 0000000..0e4b96b
 --- /dev/null
 +++ b/completion-helper.py
-@@ -0,0 +1,91 @@
+@@ -0,0 +1,90 @@
 +#!/usr/bin/python -t
 +# -*- coding: utf-8 -*-
 +#
@@ -1316,7 +1666,6 @@ index 0000000..52a5463
 +
 +def main(args):
 +    base = cli.YumBaseCli()
-+    base.setCacheDir = lambda *x: True # use the system cachedir
 +    base.yum_cli_commands.clear()
 +    base.registerCommand(GroupsCompletionCommand())
 +    base.registerCommand(ListCompletionCommand())
@@ -1934,7 +2283,7 @@ index 0000000..d2a0ed1
 +if __name__ == "__main__":
 +    generateAll(os.getcwd(), os.getcwd())
 diff --git a/docs/yum.8 b/docs/yum.8
-index 1a8202a..604377b 100644
+index 1a8202a..15a8345 100644
 --- a/docs/yum.8
 +++ b/docs/yum.8
 @@ -52,6 +52,7 @@ gnome\-packagekit application\&.
@@ -1956,8 +2305,17 @@ index 1a8202a..604377b 100644
  .br
  .I \fR * check
  .br 
-@@ -91,9 +94,14 @@ glob and any matches are then installed\&. If the name starts with an
- command\&. If the name starts with a - character, then a search is done within
+@@ -86,14 +89,20 @@ Is used to install the latest version of a package or
+ group of packages while ensuring that all dependencies are
+ satisfied\&.  (See \fBSpecifying package names\fP for more information) 
+ If no package matches the given package name(s), they are assumed to be a shell 
+-glob and any matches are then installed\&. If the name starts with an 
+-@ character the rest of the name is used as though passed to the groupinstall
+-command\&. If the name starts with a - character, then a search is done within
++glob and any matches are then installed\&. If the name starts with @^ then it
++is treated as an environment group (group install @^foo), an @ character and
++it's treated as a group (plain group install)\&. If the name starts with
++a - character, then a search is done within
  the transaction and any matches are removed. If the name is a file, then install works
  like localinstall\&. If the name doesn't match a package, then package
 -"provides" are searched (Eg. "_sqlitecache.so()(64bit)") as are
@@ -1972,7 +2330,7 @@ index 1a8202a..604377b 100644
  .IP 
  .IP "\fBupdate\fP"
  If run without any packages, update will update every currently
-@@ -111,7 +119,7 @@ changes, for example: upgrading from somelinux 8.0 to somelinux 9.
+@@ -111,7 +120,7 @@ changes, for example: upgrading from somelinux 8.0 to somelinux 9.
  
  Note that "\fBupdate\fP" works on installed packages first, and only if there
  are no matches does it look for available packages. The difference is most
@@ -1981,7 +2339,36 @@ index 1a8202a..604377b 100644
  "\fBupdate\fP foo" if foo-1-2 is installed. You can use the "\fBupdate-to\fP"
  if you'd prefer that nothing happen in the above case.
  .IP 
-@@ -224,10 +232,37 @@ to only remove packages which aren't required by something else.
+@@ -158,6 +167,11 @@ the "install" command\&.(See \fBSpecifying package names\fP for more information
+ 
+ Note that "yum" is included in the protected_packages configuration, by default.
+ So you can't accidentally remove yum itself.
++
++Because remove does a lot of work to make it as easy as possible to use, there
++are also a few specific remove commands "\fBremove-n\fP", "\fBremove-na\fP"
++and "\fBremove-nevra\fP". These only work on package names, and do not process
++wildcards etc.
+ .IP 
+ .IP "\fBlist\fP"
+ Is used to list various information about available
+@@ -209,10 +223,12 @@ installed.
+ "\fBgroup list\fP" is used to list the available groups from all \fByum\fP repos. Groups are marked
+ as "installed" if all mandatory packages are installed, or if a group doesn't
+ have any mandatory packages then it is installed if any of the optional or
+-default package are installed.
+-The optional "hidden" argument will also list groups marked as not being
+-"user visible". If you pass the \-v option, to enable verbose mode, then the
+-groupids are displayed.
++default package are installed (when not in group_command=objects mode).
++You can pass optional arguments to the list/summary commands: installed,
++available, environment, language, packages, hidden and ids (or any of those
++prefixed by "no" to turn them off again).
++If you pass the \-v option, to enable verbose mode, then the groupids are
++displayed by default (but "yum group list ids" is often easier to read).
+ 
+ "\fBgroup remove\fP" is used to remove all of the packages in a group, unlike "groupinstall" this
+ will remove everything regardless of group_package_types. It is worth pointing
+@@ -224,10 +240,37 @@ to only remove packages which aren't required by something else.
  
  "\fBgroup info\fP" is used to give the description and package list of a group (and which type
  those packages are marked as). Note that you can use the yum-filter-data and
@@ -2020,7 +2407,7 @@ index 1a8202a..604377b 100644
  .IP
  .IP "\fBshell\fP"
  Is used to enter the 'yum shell', when a filename is specified the contents of
-@@ -235,12 +270,13 @@ that file is executed in yum shell mode. See \fIyum-shell(8)\fP for more info
+@@ -235,12 +278,13 @@ that file is executed in yum shell mode. See \fIyum-shell(8)\fP for more info
  .IP
  .IP "\fBresolvedep\fP"
  Is used to list packages providing the specified dependencies, at most one
@@ -2036,7 +2423,7 @@ index 1a8202a..604377b 100644
  reasons only.
  .IP
  .IP "\fBlocalupdate\fP"
-@@ -248,7 +284,7 @@ Is used to update the system by specifying local rpm files. Only the specified
+@@ -248,7 +292,7 @@ Is used to update the system by specifying local rpm files. Only the specified
  rpm files of which an older version is already installed will be installed,
  the remaining specified packages will be ignored.
  If required the enabled repositories will be used to resolve dependencies. Note
@@ -2045,7 +2432,7 @@ index 1a8202a..604377b 100644
  legacy reasons only.
  .IP
  .IP "\fBreinstall\fP"
-@@ -260,7 +296,7 @@ on groups, files, provides and filelists just like the "install" command\&.
+@@ -260,7 +304,7 @@ on groups, files, provides and filelists just like the "install" command\&.
  Will try and downgrade a package from the version currently installed to the
  previously highest version (or the specified version).
  The depsolver will not necessarily work, but if you specify all the packages it
@@ -2054,7 +2441,7 @@ index 1a8202a..604377b 100644
  work for "installonly" packages, like Kernels. downgrade operates
  on groups, files, provides, filelists and rpm files just like the "install" command\&.
  .IP
-@@ -294,8 +330,8 @@ package counts/etc. will be zeroed out).
+@@ -294,8 +338,8 @@ package counts/etc. will be zeroed out).
  .IP "\fBversion\fP"
  Produces a "version" of the rpmdb, and of the enabled repositories if "all" is
  given as the first argument. You can also specify version groups in the
@@ -2065,7 +2452,7 @@ index 1a8202a..604377b 100644
  packages (in sorted order), and the checksum_type/checksum_data entries from
  the yumdb. Note that this rpmdb version is now also used significantly within
  yum (esp. in yum history).
-@@ -321,26 +357,33 @@ and so takes sub-commands:
+@@ -321,26 +365,33 @@ and so takes sub-commands:
  .IP "\fBhistory\fP"
  The history command allows the user to view what has happened in past
  transactions (assuming the history_record config. option is set). You can use
@@ -2105,7 +2492,7 @@ index 1a8202a..604377b 100644
  A (if it is not still installed), and "rollback 1" will try to remove packages
  B and C. Note that after a "rollback 1" you will have a fourth transaction,
  although the ending rpmdb version (see: yum version) should be the same in
-@@ -349,6 +392,12 @@ transactions 1 and 4.
+@@ -349,6 +400,12 @@ transactions 1 and 4.
  The addon-info command takes a transaction ID, and the packages-list command
  takes a package (with wildcards).
  
@@ -2118,7 +2505,7 @@ index 1a8202a..604377b 100644
  In "history list" you can change the behaviour of the 2nd column via. the
  configuration option history_list_view.
  
-@@ -371,6 +420,15 @@ end of the package column in the packages-list command).
+@@ -371,6 +428,15 @@ end of the package column in the packages-list command).
  .I \fBs\fR - The transaction completed fine, but --skip-broken was enabled and had to skip some packages.
  .br
  
@@ -2134,7 +2521,7 @@ index 1a8202a..604377b 100644
  .IP
  .IP "\fBcheck\fP"
  Checks the local rpmdb and produces information on any problems it finds. You
-@@ -401,6 +459,11 @@ Assume yes; assume that the answer to any question which would be asked
+@@ -401,6 +467,11 @@ Assume yes; assume that the answer to any question which would be asked
  is yes\&.
  .br
  Configuration Option: \fBassumeyes\fP
@@ -2146,7 +2533,7 @@ index 1a8202a..604377b 100644
  .IP "\fB\-c, \-\-config=[config file]\fP" 
  Specifies the config file location - can take HTTP and FTP URLs and local file
  paths\&.
-@@ -420,7 +483,7 @@ Sets the error level to [number] Practical range 0 \- 10. 0 means print only cri
+@@ -420,7 +491,7 @@ Sets the error level to [number] Practical range 0 \- 10. 0 means print only cri
  .br
  Configuration Option: \fBerrorlevel\fP
  .IP "\fB\-\-rpmverbosity=[name]\fP" 
@@ -2155,6 +2542,16 @@ index 1a8202a..604377b 100644
  options are: 'critical', 'emergency', 'error', 'warn' and 'debug'.
  .br
  Configuration Option: \fBrpmverbosity\fP
+@@ -506,7 +577,8 @@ option will corrupt your cache (and you can use $releasever in your cachedir
+ configuration to stop this).
+ .PP 
+ .IP "\fB\-t, \-\-tolerant\fP"
+-This option currently does nothing.
++This option makes yum go slower, checking for things that shouldn't be possible
++making it more tolerant of external errors.
+ .br
+ .IP "\fB\-\-setopt=option=value\fP"
+ Set any config option in yum config or repo files. For options in the global 
 diff --git a/docs/yum.conf.5 b/docs/yum.conf.5
 index 515aa73..b456074 100644
 --- a/docs/yum.conf.5
@@ -3044,7 +3441,7 @@ index f1e06e8..b21c594 100644
  complete -F _yum -o filenames yum yummain.py
  
 diff --git a/output.py b/output.py
-index b6aa277..b3f2c75 100755
+index b6aa277..caac21a 100755
 --- a/output.py
 +++ b/output.py
 @@ -1,6 +1,6 @@
@@ -3055,6 +3452,15 @@ index b6aa277..b3f2c75 100755
  
  # This program is free software; you can redistribute it and/or modify
  # it under the terms of the GNU General Public License as published by
+@@ -29,7 +29,7 @@ import re # For YumTerm
+ 
+ from weakref import proxy as weakref
+ 
+-from urlgrabber.progress import TextMeter
++from urlgrabber.progress import TextMeter, TextMultiFileMeter
+ import urlgrabber.progress
+ from urlgrabber.grabber import URLGrabError
+ from yum.misc import prco_tuple_to_string
 @@ -47,6 +47,21 @@ import yum.history
  
  from yum.i18n import utf8_width, utf8_width_fill, utf8_text_fill
@@ -3077,7 +3483,7 @@ index b6aa277..b3f2c75 100755
  def _term_width():
      """ Simple terminal width, limit to 20 chars. and make 0 == 80. """
      if not hasattr(urlgrabber.progress, 'terminal_width_cached'):
-@@ -60,17 +75,21 @@ def _term_width():
+@@ -60,17 +75,26 @@ def _term_width():
  
  
  class YumTextMeter(TextMeter):
@@ -3098,13 +3504,18 @@ index b6aa277..b3f2c75 100755
          checkSignals()
          TextMeter.update(self, amount_read, now)
  
++class YumTextMultiFileMeter(TextMultiFileMeter):
++    def update_meter(self, meter, now):
++        checkSignals()
++        TextMultiFileMeter.update_meter(self, meter, now)
++
  class YumTerm:
 -    """some terminal "UI" helpers based on curses"""
 +    """A class to provide some terminal "UI" helpers based on curses."""
  
      # From initial search for "terminfo and python" got:
      # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/475116
-@@ -145,6 +164,17 @@ class YumTerm:
+@@ -145,6 +169,17 @@ class YumTerm:
          self.BG_COLOR = self.__ansi_forced_BG_COLOR
  
      def reinit(self, term_stream=None, color='auto'):
@@ -3122,7 +3533,7 @@ index b6aa277..b3f2c75 100755
          self.__enabled = True
          if not hasattr(urlgrabber.progress, 'terminal_width_cached'):
              self.columns = 80
-@@ -255,6 +285,37 @@ class YumTerm:
+@@ -255,6 +290,37 @@ class YumTerm:
          return re.sub(r'\$<\d+>[/*]?', '', cap)
  
      def sub(self, haystack, beg, end, needles, escape=None, ignore_case=False):
@@ -3160,7 +3571,7 @@ index b6aa277..b3f2c75 100755
          if not self.__enabled:
              return haystack
  
-@@ -269,27 +330,106 @@ class YumTerm:
+@@ -269,27 +335,106 @@ class YumTerm:
              haystack = re.sub(pat, render, haystack)
          return haystack
      def sub_norm(self, haystack, beg, needles, **kwds):
@@ -3271,7 +3682,7 @@ index b6aa277..b3f2c75 100755
  
      def __init__(self):
          self.logger = logging.getLogger("yum.cli")
-@@ -304,6 +444,12 @@ class YumOutput:
+@@ -304,6 +449,12 @@ class YumOutput:
  
      
      def printtime(self):
@@ -3284,7 +3695,7 @@ index b6aa277..b3f2c75 100755
          months = [_('Jan'), _('Feb'), _('Mar'), _('Apr'), _('May'), _('Jun'),
                    _('Jul'), _('Aug'), _('Sep'), _('Oct'), _('Nov'), _('Dec')]
          now = time.localtime(time.time())
-@@ -312,14 +458,27 @@ class YumOutput:
+@@ -312,14 +463,27 @@ class YumOutput:
          return ret
           
      def failureReport(self, errobj):
@@ -3314,7 +3725,7 @@ index b6aa277..b3f2c75 100755
          progressbar(current, total, name)
  
      def _highlight(self, highlight):
-@@ -368,9 +527,29 @@ class YumOutput:
+@@ -368,9 +532,29 @@ class YumOutput:
  
      def calcColumns(self, data, columns=None, remainder_column=0,
                      total_width=None, indent=''):
@@ -3347,7 +3758,7 @@ index b6aa277..b3f2c75 100755
          if total_width is None:
              total_width = self.term.columns
  
-@@ -473,10 +652,20 @@ class YumOutput:
+@@ -473,10 +657,20 @@ class YumOutput:
          return (val, width, hibeg, hiend)
  
      def fmtColumns(self, columns, msg=u'', end=u'', text_width=utf8_width):
@@ -3372,7 +3783,7 @@ index b6aa277..b3f2c75 100755
          total_width = len(msg)
          data = []
          for col_data in columns[:-1]:
-@@ -513,8 +702,18 @@ class YumOutput:
+@@ -513,8 +707,18 @@ class YumOutput:
  
      def simpleList(self, pkg, ui_overflow=False, indent='', highlight=False,
                     columns=None):
@@ -3393,7 +3804,7 @@ index b6aa277..b3f2c75 100755
          if columns is None:
              columns = (-40, -22, -16) # Old default
          ver = pkg.printVer()
-@@ -526,9 +725,19 @@ class YumOutput:
+@@ -526,9 +730,19 @@ class YumOutput:
  
      def simpleEnvraList(self, pkg, ui_overflow=False,
                          indent='', highlight=False, columns=None):
@@ -3416,7 +3827,7 @@ index b6aa277..b3f2c75 100755
          if columns is None:
              columns = (-63, -16) # Old default
          envra = '%s%s' % (indent, str(pkg))
-@@ -538,7 +747,13 @@ class YumOutput:
+@@ -538,7 +752,13 @@ class YumOutput:
          print self.fmtColumns(columns, text_width=len)
  
      def fmtKeyValFill(self, key, val):
@@ -3431,7 +3842,7 @@ index b6aa277..b3f2c75 100755
          val = to_str(val)
          keylen = utf8_width(key)
          cols = self.term.columns
-@@ -553,6 +768,15 @@ class YumOutput:
+@@ -553,6 +773,15 @@ class YumOutput:
          return ret
      
      def fmtSection(self, name, fill='='):
@@ -3447,7 +3858,7 @@ index b6aa277..b3f2c75 100755
          name = to_str(name)
          cols = self.term.columns - 2
          name_len = utf8_width(name)
-@@ -577,6 +801,12 @@ class YumOutput:
+@@ -577,6 +806,12 @@ class YumOutput:
          return to_unicode(s)
  
      def infoOutput(self, pkg, highlight=False):
@@ -3460,7 +3871,7 @@ index b6aa277..b3f2c75 100755
          (hibeg, hiend) = self._highlight(highlight)
          print _("Name        : %s%s%s") % (hibeg, to_unicode(pkg.name), hiend)
          print _("Arch        : %s") % to_unicode(pkg.arch)
-@@ -617,9 +847,22 @@ class YumOutput:
+@@ -617,9 +852,22 @@ class YumOutput:
          print ""
      
      def updatesObsoletesList(self, uotup, changetype, columns=None):
@@ -3486,7 +3897,7 @@ index b6aa277..b3f2c75 100755
          (changePkg, instPkg) = uotup
  
          if columns is not None:
-@@ -640,12 +883,45 @@ class YumOutput:
+@@ -640,12 +888,45 @@ class YumOutput:
  
      def listPkgs(self, lst, description, outputType, highlight_na={},
                   columns=None, highlight_modes={}):
@@ -3538,7 +3949,7 @@ index b6aa277..b3f2c75 100755
          if outputType in ['list', 'info']:
              thingslisted = 0
              if len(lst) > 0:
-@@ -654,7 +930,8 @@ class YumOutput:
+@@ -654,7 +935,8 @@ class YumOutput:
                  for pkg in sorted(lst):
                      key = (pkg.name, pkg.arch)
                      highlight = False
@@ -3548,7 +3959,7 @@ index b6aa277..b3f2c75 100755
                      elif key not in highlight_na:
                          highlight = highlight_modes.get('not in', 'normal')
                      elif pkg.verEQ(highlight_na[key]):
-@@ -679,8 +956,11 @@ class YumOutput:
+@@ -679,8 +961,11 @@ class YumOutput:
      
          
      def userconfirm(self):
@@ -3561,7 +3972,7 @@ index b6aa277..b3f2c75 100755
          yui = (to_unicode(_('y')), to_unicode(_('yes')))
          nui = (to_unicode(_('n')), to_unicode(_('no')))
          aui = (yui[0], yui[1], nui[0], nui[1])
-@@ -739,27 +1019,58 @@ class YumOutput:
+@@ -739,27 +1024,58 @@ class YumOutput:
          return ret
  
      def _calcDataPkgColumns(self, data, pkg_names, pkg_names2pkgs,
@@ -3624,7 +4035,7 @@ index b6aa277..b3f2c75 100755
                      continue
                  for (apkg, ipkg) in sorted(pkg_names2pkgs[item],
                                             key=lambda x: x[1] or x[0]):
-@@ -770,18 +1081,38 @@ class YumOutput:
+@@ -770,18 +1086,38 @@ class YumOutput:
                      else:
                          highlight = False
                      self.simpleEnvraList(ipkg or apkg, ui_overflow=True,
@@ -3640,8 +4051,10 @@ index b6aa277..b3f2c75 100755
          print _('\nGroup: %s') % group.ui_name
  
          verb = self.verbose_logger.isEnabledFor(logginglevels.DEBUG_3)
-         if verb:
-             print _(' Group-Id: %s') % to_unicode(group.groupid)
+-        if verb:
+-            print _(' Group-Id: %s') % to_unicode(group.groupid)
++        if True:
++            print _(' Group-Id: %s') % to_unicode(group.compsid)
 +
 +        igroup_data = self._groupInstalledData(group)
 +        igrp_only   = set()
@@ -3665,7 +4078,7 @@ index b6aa277..b3f2c75 100755
          if group.ui_description:
              print _(' Description: %s') % to_unicode(group.ui_description)
          if group.langonly:
-@@ -795,7 +1126,8 @@ class YumOutput:
+@@ -795,7 +1131,8 @@ class YumOutput:
          if verb:
              data = {'envra' : {}, 'rid' : {}}
              for (section_name, pkg_names) in sections:
@@ -3675,7 +4088,7 @@ index b6aa277..b3f2c75 100755
              data = [data['envra'], data['rid']]
              columns = self.calcColumns(data)
              columns = (-columns[0], -columns[1])
-@@ -804,11 +1136,20 @@ class YumOutput:
+@@ -804,11 +1141,77 @@ class YumOutput:
              if len(pkg_names) > 0:
                  print section_name
                  self._displayPkgsFromNames(pkg_names, verb, pkg_names2pkgs,
@@ -3687,6 +4100,63 @@ index b6aa277..b3f2c75 100755
 +            self._displayPkgsFromNames(igrp_only, verb, pkg_names2pkgs,
 +                                       columns=columns,
 +                                       igroup_data=igroup_data)
++
++    def displayGrpsInEnvironments(self, evgroup):
++        """Output information about the groups in a given evgroup
++
++        :param group: an Environment object to output information about
++        """
++        print _('\nEnvironment Group: %s') % evgroup.ui_name
++        print _(' Environment-Id: %s') % to_unicode(evgroup.compsid)
++
++        igroup_data = self._groupInstalledEnvData(evgroup)
++        igrp_only   = set()
++        for grp_name in igroup_data:
++            if igroup_data[grp_name] == 'installed':
++                igrp_only.add(grp_name)
++        igrp_only.difference_update(evgroup.allgroups)
++        all_grps = evgroup.allgroups + list(igrp_only)
++
++        if evgroup.ui_description:
++            print _(' Description: %s') % to_unicode(evgroup.ui_description)
++
++        def _get_igrp_data(item, indent):
++            if not igroup_data:
++                return indent
++
++            assert item in igroup_data
++            if item not in igroup_data or igroup_data[item] == 'available':
++                indent += '+' # Group up/in will install i
++            elif igroup_data[item] == 'installed':
++                indent += '=' # Installed via. group
++            elif igroup_data[item] == 'blacklisted-installed':
++                if False: # Not sure it's worth listing these...
++                    return None # On the other hand, there's mark-packages
++                indent += ' ' # Installed, not via. group
++            else:
++                assert igroup_data[item] == 'blacklisted-available'
++                if False: # Not sure it's worth listing these...
++                    return None
++                indent += '-' # Not installed, and won't be
++            return indent
++
++        sections = ((_(' Mandatory Groups:'), evgroup.groups),
++                    (_(' Optional Groups:'),  evgroup.options))
++
++        for (section_name, grp_names) in sections:
++            if len(grp_names) > 0:
++                print section_name
++                for grp_name in sorted(grp_names):
++                    pindent = _get_igrp_data(grp_name, "   ")
++                    if pindent is None:
++                        continue
++
++                    print "%s%s" % (pindent, grp_name)
++
++        if igrp_only:
++            print _(' Installed Groups:')
++            for grp_name in sorted(igrp_only):
++                print "%s%s", "  ", grp_name
  
      def depListOutput(self, results):
 -        """take a list of findDeps results and 'pretty print' the output"""
@@ -3699,7 +4169,7 @@ index b6aa277..b3f2c75 100755
          verb = self.verbose_logger.isEnabledFor(logginglevels.DEBUG_3)
          for pkg in sorted(results):
              print _("package: %s") % pkg.compactPrint()
-@@ -832,7 +1173,18 @@ class YumOutput:
+@@ -832,7 +1235,18 @@ class YumOutput:
                      print "   provider: %s" % po.compactPrint()
  
      def format_number(self, number, SI=0, space=' '):
@@ -3719,7 +4189,7 @@ index b6aa277..b3f2c75 100755
          symbols = [ ' ', # (none)
                      'k', # kilo
                      'M', # mega
-@@ -870,16 +1222,31 @@ class YumOutput:
+@@ -870,16 +1284,31 @@ class YumOutput:
  
      @staticmethod
      def format_time(seconds, use_hours=0):
@@ -3757,7 +4227,7 @@ index b6aa277..b3f2c75 100755
          if self.conf.showdupesfromrepos:
              msg = '%s : ' % po
          else:
-@@ -923,7 +1290,15 @@ class YumOutput:
+@@ -923,7 +1352,15 @@ class YumOutput:
                  item = self._enc(item)
                  can_overflow = False
              else:
@@ -3774,7 +4244,7 @@ index b6aa277..b3f2c75 100755
  
              if matchfor:
                  item = self._sub_highlight(item, highlight, matchfor,
-@@ -935,14 +1310,34 @@ class YumOutput:
+@@ -935,14 +1372,34 @@ class YumOutput:
          print '\n\n'
  
      def matchcallback_verbose(self, po, values, matchfor=None):
@@ -3810,7 +4280,7 @@ index b6aa277..b3f2c75 100755
          for pkg in packages:
              # Just to be on the safe side, if for some reason getting
              # the package size fails, log the error and don't report download
-@@ -971,18 +1366,18 @@ class YumOutput:
+@@ -971,18 +1428,18 @@ class YumOutput:
  
          if (not error):
              if locsize:
@@ -3837,7 +4307,7 @@ index b6aa277..b3f2c75 100755
          totsize = 0
          error = False
          for pkg in packages:
-@@ -997,13 +1392,19 @@ class YumOutput:
+@@ -997,13 +1454,19 @@ class YumOutput:
                  self.logger.error(_('There was an error calculating installed size'))
                  break
          if (not error):
@@ -3862,7 +4332,7 @@ index b6aa277..b3f2c75 100755
          self.tsInfo.makelists(True, True)
          pkglist_lines = []
          data  = {'n' : {}, 'v' : {}, 'r' : {}}
-@@ -1032,8 +1433,7 @@ class YumOutput:
+@@ -1032,8 +1495,7 @@ class YumOutput:
              for (d, v) in (("n",len(n)), ("v",len(evr)), ("r",len(repoid))):
                  data[d].setdefault(v, 0)
                  data[d][v] += 1
@@ -3872,7 +4342,7 @@ index b6aa277..b3f2c75 100755
              return a_wid
  
          for (action, pkglist) in [(_('Installing'), self.tsInfo.installed),
-@@ -1102,19 +1502,72 @@ class YumOutput:
+@@ -1102,19 +1564,72 @@ class YumOutput:
  Transaction Summary
  %s
  """) % ('=' * self.term.columns))
@@ -3955,7 +4425,7 @@ index b6aa277..b3f2c75 100755
          out = ''
          
          self.tsInfo.makelists()
-@@ -1179,9 +1632,9 @@ Transaction Summary
+@@ -1179,17 +1694,19 @@ Transaction Summary
          return out
  
      def setupProgressCallbacks(self):
@@ -3968,7 +4438,33 @@ index b6aa277..b3f2c75 100755
          # if we're below 2 on the debug level we don't need to be outputting
          # progress bars - this is hacky - I'm open to other options
          # One of these is a download
-@@ -1216,10 +1669,12 @@ Transaction Summary
+         if self.conf.debuglevel < 2 or not sys.stdout.isatty():
+             progressbar = None
++            multi_progressbar = None
+             callback = None
+         else:
+             progressbar = YumTextMeter(fo=sys.stdout)
++            multi_progressbar = YumTextMultiFileMeter(fo=sys.stdout)
+             callback = CacheProgressCallback()
+ 
+         # setup our failure report for failover
+@@ -1200,13 +1717,14 @@ Transaction Summary
+         interrupt_callback = self.interrupt_callback
+         if hasattr(self, 'prerepoconf'):
+             self.prerepoconf.progressbar = progressbar
++            self.prerepoconf.multi_progressbar = multi_progressbar
+             self.prerepoconf.callback = callback
+             self.prerepoconf.failure_callback = failure_callback
+             self.prerepoconf.interrupt_callback = interrupt_callback
+         else:
+             #  Just in case some API user decides to do self.repos before
+             # calling us.
+-            self.repos.setProgressBar(progressbar)
++            self.repos.setProgressBar(progressbar, multi_progressbar)
+             self.repos.callback = callback
+             self.repos.setFailureCallback(failure_callback)
+             self.repos.setInterruptCallback(interrupt_callback)
+@@ -1216,10 +1734,12 @@ Transaction Summary
          self.dsCallback = dscb
      
      def setupProgessCallbacks(self):
@@ -3982,27 +4478,27 @@ index b6aa277..b3f2c75 100755
          confirm_func = self._cli_confirm_gpg_key_import
          gpg_import_func = self.getKeyForRepo
          gpgca_import_func = self.getCAKeyForRepo
-@@ -1233,14 +1688,12 @@ Transaction Summary
+@@ -1233,14 +1753,12 @@ Transaction Summary
              self.repos.gpgca_import_func = gpgca_import_func
  
      def interrupt_callback(self, cbobj):
 -        '''Handle CTRL-C's during downloads
+-
+-        If a CTRL-C occurs a URLGrabError will be raised to push the download
+-        onto the next mirror.  
+-        
+-        If two CTRL-C's occur in quick succession then yum will exit.
 +        '''Handle CTRL-C's during downloads.  If a CTRL-C occurs a
 +        URLGrabError will be raised to push the download onto the next
 +        mirror.  If two CTRL-C's occur in quick succession then yum
 +        will exit.
  
--        If a CTRL-C occurs a URLGrabError will be raised to push the download
--        onto the next mirror.  
--        
--        If two CTRL-C's occur in quick succession then yum will exit.
--
 -        @param cbobj: urlgrabber callback obj
 +        :param cbobj: :class:`urlgrabber.grabber.CallbackObject`
          '''
          delta_exit_chk = 2.0      # Delta between C-c's so we treat as exit
          delta_exit_str = _("two") # Human readable version of above
-@@ -1269,6 +1722,14 @@ to exit.
+@@ -1269,6 +1787,14 @@ to exit.
  
      def download_callback_total_cb(self, remote_pkgs, remote_size,
                                     download_start_timestamp):
@@ -4017,7 +4513,7 @@ index b6aa277..b3f2c75 100755
          if len(remote_pkgs) <= 1:
              return
          if not hasattr(urlgrabber.progress, 'TerminalLine'):
-@@ -1434,8 +1895,17 @@ to exit.
+@@ -1434,8 +1960,17 @@ to exit.
          return tids, printall
  
      def historyListCmd(self, extcmds):
@@ -4027,16 +4523,16 @@ index b6aa277..b3f2c75 100755
 +
 +        :param extcmds: list of extra command line arguments
 +        :return: (exit_code, [errors])
- 
-+        exit_code is::
 +
++        exit_code is::
+ 
 +            0 = we're done, exit
 +            1 = we've errored, exit with error string
 +        """
          tids, printall = self._history_list_transactions(extcmds)
          if tids is None:
              return 1, ['Failed history list']
-@@ -1564,6 +2034,16 @@ to exit.
+@@ -1564,6 +2099,16 @@ to exit.
          return old[0]
  
      def historyInfoCmd(self, extcmds):
@@ -4053,7 +4549,7 @@ index b6aa277..b3f2c75 100755
          def str2int(x):
              try:
                  return int(x)
-@@ -1656,6 +2136,9 @@ to exit.
+@@ -1656,6 +2201,9 @@ to exit.
      def _hpkg2from_repo(self, hpkg):
          """ Given a pkg, find the ipkg.ui_from_repo ... if none, then
              get an apkg. ... and put a ? in there. """
@@ -4063,7 +4559,7 @@ index b6aa277..b3f2c75 100755
          ipkgs = self.rpmdb.searchPkgTuple(hpkg.pkgtup)
          if not ipkgs:
              apkgs = self.pkgSack.searchPkgTuple(hpkg.pkgtup)
-@@ -1672,13 +2155,12 @@ to exit.
+@@ -1672,13 +2220,12 @@ to exit.
                                   'o' : _('Updated'), 'n' : _('Downgraded')}
          _pkg_states_available = {'i' : _('Installed'), 'e' : _('Not installed'),
                                   'o' : _('Older'), 'n' : _('Newer')}
@@ -4080,7 +4576,7 @@ index b6aa277..b3f2c75 100755
              prefix = " " * prefix_len
              if was_installed:
                  _pkg_states = _pkg_states_installed
-@@ -1702,9 +2184,11 @@ to exit.
+@@ -1702,9 +2249,11 @@ to exit.
              else:
                  (hibeg, hiend) = self._highlight('normal')
              state = utf8_width_fill(state, _pkg_states['maxlen'])
@@ -4094,7 +4590,7 @@ index b6aa277..b3f2c75 100755
  
          if type(old.tid) == type([]):
              print _("Transaction ID :"), "%u..%u" % (old.tid[0], old.tid[-1])
-@@ -1778,8 +2262,8 @@ to exit.
+@@ -1778,8 +2327,8 @@ to exit.
              default_addons = set(['config-main', 'config-repos', 'saved_tx'])
              non_default = set(addon_info).difference(default_addons)
              if len(non_default) > 0:
@@ -4105,7 +4601,7 @@ index b6aa277..b3f2c75 100755
  
          if old.trans_with:
              # This is _possible_, but not common
-@@ -1794,7 +2278,9 @@ to exit.
+@@ -1794,7 +2343,9 @@ to exit.
              print _("Packages Skipped:")
              pkg_max_len = max((len(str(hpkg)) for hpkg in old.trans_skip))
          for hpkg in old.trans_skip:
@@ -4116,7 +4612,7 @@ index b6aa277..b3f2c75 100755
  
          if old.rpmdb_problems:
              print _("Rpmdb Problems:")
-@@ -1833,6 +2319,13 @@ to exit.
+@@ -1833,6 +2384,13 @@ to exit.
                                'Updated'      : _('Updated'),
                                }
      def historyInfoCmdPkgsAltered(self, old, pats=[]):
@@ -4130,7 +4626,7 @@ index b6aa277..b3f2c75 100755
          last = None
          #  Note that these don't use _simple_pkg() because we are showing what
          # happened to them in the transaction ... not the difference between the
-@@ -1886,6 +2379,10 @@ to exit.
+@@ -1886,6 +2444,10 @@ to exit.
                                          self._hpkg2from_repo(hpkg))
  
      def historySummaryCmd(self, extcmds):
@@ -4141,7 +4637,7 @@ index b6aa277..b3f2c75 100755
          tids, printall = self._history_list_transactions(extcmds)
          if tids is None:
              return 1, ['Failed history info']
-@@ -1946,6 +2443,10 @@ to exit.
+@@ -1946,6 +2508,10 @@ to exit.
                               utf8_width_fill(uiacts, 16, 16), count)
  
      def historyAddonInfoCmd(self, extcmds):
@@ -4152,7 +4648,7 @@ index b6aa277..b3f2c75 100755
          tid = None
          if len(extcmds) > 1:
              tid = extcmds[1]
-@@ -1983,16 +2484,19 @@ to exit.
+@@ -1983,16 +2549,19 @@ to exit.
          
          for item in extcmds[2:]:
              if item in addon_info:
@@ -4178,7 +4674,7 @@ index b6aa277..b3f2c75 100755
          tids = self.history.search(extcmds)
          limit = None
          if extcmds and not tids:
-@@ -2078,9 +2582,94 @@ to exit.
+@@ -2078,9 +2647,94 @@ to exit.
              if lastdbv.end_rpmdbversion != rpmdbv:
                  self._rpmdb_warn_checks()
  
@@ -4274,7 +4770,7 @@ index b6aa277..b3f2c75 100755
      
      def __init__(self, ayum=None):
          """requires yum-cli log and errorlog functions as arguments"""
-@@ -2089,6 +2678,25 @@ class DepSolveProgressCallBack:
+@@ -2089,6 +2743,25 @@ class DepSolveProgressCallBack:
          self.ayum = ayum
  
      def pkgAdded(self, pkgtup, mode):
@@ -4300,7 +4796,7 @@ index b6aa277..b3f2c75 100755
          modedict = { 'i': _('installed'),
                       'u': _('an update'),
                       'e': _('erased'),
-@@ -2104,43 +2712,85 @@ class DepSolveProgressCallBack:
+@@ -2104,43 +2777,85 @@ class DepSolveProgressCallBack:
              modeterm)
          
      def start(self):
@@ -4388,7 +4884,7 @@ index b6aa277..b3f2c75 100755
          needname, needflags, needversion = reqTup
  
          yb = self.ayum
-@@ -2225,46 +2875,106 @@ class DepSolveProgressCallBack:
+@@ -2225,46 +2940,106 @@ class DepSolveProgressCallBack:
          return msg
      
      def procConflict(self, name, confname):
@@ -4500,7 +4996,7 @@ index b6aa277..b3f2c75 100755
  
  def _pkgname_ui(ayum, pkgname, ts_states=None):
      """ Get more information on a simple pkgname, if we can. We need to search
-@@ -2316,10 +3026,7 @@ def _pkgname_ui(ayum, pkgname, ts_states=None):
+@@ -2316,10 +3091,7 @@ def _pkgname_ui(ayum, pkgname, ts_states=None):
      return pkgname
  
  class YumCliRPMCallBack(RPMBaseCallback):
@@ -4512,7 +5008,7 @@ index b6aa277..b3f2c75 100755
  
      width = property(lambda x: _term_width())
  
-@@ -2337,21 +3044,34 @@ class YumCliRPMCallBack(RPMBaseCallback):
+@@ -2337,21 +3109,34 @@ class YumCliRPMCallBack(RPMBaseCallback):
      #  Installing things have pkg objects passed to the events, so only need to
      # lookup for erased/obsoleted.
      def pkgname_ui(self, pkgname, ts_states=('e', 'od', 'ud', None)):
@@ -4557,7 +5053,7 @@ index b6aa277..b3f2c75 100755
          
          if type(package) not in types.StringTypes:
              pkgname = str(package)
-@@ -2363,9 +3083,25 @@ class YumCliRPMCallBack(RPMBaseCallback):
+@@ -2363,9 +3148,25 @@ class YumCliRPMCallBack(RPMBaseCallback):
              percent = 0
          else:
              percent = (te_current*100L)/te_total
@@ -4584,7 +5080,7 @@ index b6aa277..b3f2c75 100755
                                                pkgname=pkgname, wid1=wid1)
              msg = fmt % (utf8_width_fill(process, wid1, wid1),
                           utf8_width_fill(pkgname, wid2, wid2))
-@@ -2377,6 +3113,11 @@ class YumCliRPMCallBack(RPMBaseCallback):
+@@ -2377,6 +3178,11 @@ class YumCliRPMCallBack(RPMBaseCallback):
                  print " "
  
      def scriptout(self, package, msgs):
@@ -4596,7 +5092,7 @@ index b6aa277..b3f2c75 100755
          if msgs:
              sys.stdout.write(to_unicode(msgs))
              sys.stdout.flush()
-@@ -2429,8 +3170,30 @@ class YumCliRPMCallBack(RPMBaseCallback):
+@@ -2429,8 +3235,30 @@ class YumCliRPMCallBack(RPMBaseCallback):
              wid2 = pnl
          return fmt, wid1, wid2
  
@@ -148637,10 +149133,10 @@ index 97a9923..55e6f14 100644
  
      @staticmethod
 diff --git a/test/simpleupdatetests.py b/test/simpleupdatetests.py
-index 6177fb1..2c8bcb3 100644
+index 6177fb1..9e22a6b 100644
 --- a/test/simpleupdatetests.py
 +++ b/test/simpleupdatetests.py
-@@ -990,3 +990,72 @@ class SimpleUpdateTests(OperationsTests):
+@@ -990,3 +990,93 @@ class SimpleUpdateTests(OperationsTests):
          # Nothing to do...
          self.assert_(res==0, msg)
  
@@ -148713,6 +149209,27 @@ index 6177fb1..2c8bcb3 100644
 +        # Ideal:
 +        # self.assertResult((foo11, bar12, cbar11))
 +        self.assertResult((foo12, bar12, cbar11))
++
++    def testInstallOtherArch1(self):
++        #  Installing something with dep. on newer version should upgrade older
++        # version with other arch.
++        # Eg. BZ 791326
++        pax1 = FakePackage('inst', '1', '1', '0', 'x86_64')
++        pai1 = FakePackage('inst', '1', '1', '0', 'i686')
++        pax2 = FakePackage('inst', '2', '1', '0', 'x86_64')
++        pax2.addProvides('foo-x', 'EQ', ('0', '2', '1'))
++        pai2 = FakePackage('inst', '2', '1', '0', 'i686')
++        pai2.addProvides('foo-i', 'EQ', ('0', '2', '1'))
++
++        pa2 = FakePackage('bar', '1', '1', '0', 'i386')
++        pa2.addRequires('foo-i', 'EQ', ('0', '2', '1'))
++
++        res, msg = self.runOperation(['install', 'bar'],
++                                     [pax1],
++                                     [pax1, pai1, pax2, pai2, pa2])
++        self.assert_(res=='ok', msg)
++        self.assertResult((pax2, pai2, pa2))
++
 diff --git a/test/testbase.py b/test/testbase.py
 index d0f22be..608da70 100644
 --- a/test/testbase.py
@@ -150797,8 +151314,35 @@ index abd203f..65c62a9 100644
  * Wed Apr 20 2011 James Antill <james at fedoraproject.org>
  - 3.4.1
  - umask bug fix.
+diff --git a/yum/Errors.py b/yum/Errors.py
+index c1af4ad..e3e3956 100644
+--- a/yum/Errors.py
++++ b/yum/Errors.py
+@@ -79,9 +79,20 @@ class RepoError(YumBaseError):
+ class DuplicateRepoError(RepoError):
+     pass
+ 
++# Have our own custom .value with all the mirror errors.
+ class NoMoreMirrorsRepoError(RepoError):
+-    pass
+-    
++    def __init__(self, value=None, errors=None):
++        Exception.__init__(self)
++        self._value = value
++        self.errors = errors
++
++    @property
++    def value(self):
++        ret = self._value
++        for url, msg in self.errors or []:
++            ret += '\n%s: %s' % (url, msg)
++        return ret
++
+ class ConfigError(YumBaseError):
+     pass
+     
 diff --git a/yum/__init__.py b/yum/__init__.py
-index 99039e0..4650639 100644
+index 99039e0..198dc6d 100644
 --- a/yum/__init__.py
 +++ b/yum/__init__.py
 @@ -46,8 +46,13 @@ import operator
@@ -150851,7 +151395,7 @@ index 99039e0..4650639 100644
      def __init__(self):
          self.fn = '/etc/yum/yum.conf'
          self.root = '/'
-@@ -125,10 +133,12 @@ class _YumPreBaseConf:
+@@ -125,12 +133,15 @@ class _YumPreBaseConf:
  
  
  class _YumPreRepoConf:
@@ -150867,8 +151411,11 @@ index 99039e0..4650639 100644
 +    """
      def __init__(self):
          self.progressbar = None
++        self.multi_progressbar = None
          self.callback = None
-@@ -164,11 +174,11 @@ class _YumCostExclude:
+         self.failure_callback = None
+         self.interrupt_callback = None
+@@ -164,11 +175,11 @@ class _YumCostExclude:
          return False
  
  class YumBase(depsolve.Depsolve):
@@ -150885,7 +151432,7 @@ index 99039e0..4650639 100644
      def __init__(self):
          depsolve.Depsolve.__init__(self)
          self._conf = None
-@@ -177,6 +187,7 @@ class YumBase(depsolve.Depsolve):
+@@ -177,6 +188,7 @@ class YumBase(depsolve.Depsolve):
          self._up = None
          self._comps = None
          self._history = None
@@ -150893,7 +151440,7 @@ index 99039e0..4650639 100644
          self._pkgSack = None
          self._lockfile = None
          self._tags = None
-@@ -204,6 +215,7 @@ class YumBase(depsolve.Depsolve):
+@@ -204,6 +216,7 @@ class YumBase(depsolve.Depsolve):
  
          self.run_with_package_names = set()
          self._cleanup = []
@@ -150901,7 +151448,7 @@ index 99039e0..4650639 100644
  
      def __del__(self):
          self.close()
-@@ -213,10 +225,15 @@ class YumBase(depsolve.Depsolve):
+@@ -213,10 +226,15 @@ class YumBase(depsolve.Depsolve):
          for cb in self._cleanup: cb()
  
      def close(self):
@@ -150917,7 +151464,7 @@ index 99039e0..4650639 100644
          if self._repos:
              self._repos.close()
  
-@@ -225,15 +242,33 @@ class YumBase(depsolve.Depsolve):
+@@ -225,15 +243,33 @@ class YumBase(depsolve.Depsolve):
          return transactioninfo.TransactionData()
  
      def doGenericSetup(self, cache=0):
@@ -150953,7 +151500,7 @@ index 99039e0..4650639 100644
          warnings.warn(_('doConfigSetup() will go away in a future version of Yum.\n'),
                  Errors.YumFutureDeprecationWarning, stacklevel=2)
  
-@@ -297,15 +332,17 @@ class YumBase(depsolve.Depsolve):
+@@ -297,15 +333,17 @@ class YumBase(depsolve.Depsolve):
              # Try the old default
              fn = '/etc/yum.conf'
  
@@ -150974,7 +151521,7 @@ index 99039e0..4650639 100644
  
          if debuglevel != None:
              startupconf.debuglevel = debuglevel
-@@ -336,6 +373,12 @@ class YumBase(depsolve.Depsolve):
+@@ -336,6 +374,12 @@ class YumBase(depsolve.Depsolve):
  
          self._conf = config.readMainConfig(startupconf)
  
@@ -150987,7 +151534,7 @@ index 99039e0..4650639 100644
          #  We don't want people accessing/altering preconf after it becomes
          # worthless. So we delete it, and thus. it'll raise AttributeError
          del self.preconf
-@@ -367,22 +410,36 @@ class YumBase(depsolve.Depsolve):
+@@ -367,22 +411,36 @@ class YumBase(depsolve.Depsolve):
      def doLoggingSetup(self, debuglevel, errorlevel,
                         syslog_ident=None, syslog_facility=None,
                         syslog_device='/dev/log'):
@@ -151031,7 +151578,7 @@ index 99039e0..4650639 100644
          if repo_age is None:
              repo_age = os.stat(repofn)[8]
          
-@@ -391,8 +448,7 @@ class YumBase(depsolve.Depsolve):
+@@ -391,8 +449,7 @@ class YumBase(depsolve.Depsolve):
          try:
              parser.readfp(confpp_obj)
          except ParsingError, e:
@@ -151041,7 +151588,7 @@ index 99039e0..4650639 100644
  
          # Check sections in the .repo file that was just slurped up
          for section in parser.sections():
-@@ -429,7 +485,15 @@ class YumBase(depsolve.Depsolve):
+@@ -429,7 +486,15 @@ class YumBase(depsolve.Depsolve):
  
                  thisrepo.base_persistdir = self.conf._repos_persistdir
  
@@ -151058,7 +151605,7 @@ index 99039e0..4650639 100644
              if thisrepo.id in self.repo_setopts:
                  for opt in self.repo_setopts[thisrepo.id].items:
                      if not hasattr(thisrepo, opt):
-@@ -440,6 +504,20 @@ class YumBase(depsolve.Depsolve):
+@@ -440,6 +505,20 @@ class YumBase(depsolve.Depsolve):
              if validate and not validate(thisrepo):
                  continue
                      
@@ -151079,7 +151626,7 @@ index 99039e0..4650639 100644
              # Got our list of repo objects, add them to the repos
              # collection
              try:
-@@ -448,8 +526,11 @@ class YumBase(depsolve.Depsolve):
+@@ -448,8 +527,11 @@ class YumBase(depsolve.Depsolve):
                  self.logger.warning(e)
          
      def getReposFromConfig(self):
@@ -151093,7 +151640,7 @@ index 99039e0..4650639 100644
          # Read .repo files from directories specified by the reposdir option
          # (typically /etc/yum/repos.d)
          repo_config_age = self.conf.config_file_age
-@@ -466,18 +547,22 @@ class YumBase(depsolve.Depsolve):
+@@ -466,18 +548,22 @@ class YumBase(depsolve.Depsolve):
  
              if os.path.isdir(reposdir):
                  for repofn in sorted(glob.glob('%s/*.repo' % reposdir)):
@@ -151121,7 +151668,7 @@ index 99039e0..4650639 100644
          repo = yumRepo.YumRepository(section)
          try:
              repo.populate(parser, section, self.conf)
-@@ -493,38 +578,40 @@ class YumBase(depsolve.Depsolve):
+@@ -493,38 +579,40 @@ class YumBase(depsolve.Depsolve):
          repo.name = to_unicode(repo.name)
  
          # Set attributes not from the config file
@@ -151184,7 +151731,7 @@ index 99039e0..4650639 100644
          if isinstance(self.plugins, plugins.YumPlugins):
              raise RuntimeError(_("plugins already initialised"))
  
-@@ -533,6 +620,8 @@ class YumBase(depsolve.Depsolve):
+@@ -533,6 +621,8 @@ class YumBase(depsolve.Depsolve):
  
      
      def doRpmDBSetup(self):
@@ -151193,7 +151740,7 @@ index 99039e0..4650639 100644
          warnings.warn(_('doRpmDBSetup() will go away in a future version of Yum.\n'),
                  Errors.YumFutureDeprecationWarning, stacklevel=2)
  
-@@ -552,7 +641,8 @@ class YumBase(depsolve.Depsolve):
+@@ -552,7 +642,8 @@ class YumBase(depsolve.Depsolve):
          return self._rpmdb
  
      def closeRpmDB(self):
@@ -151203,7 +151750,7 @@ index 99039e0..4650639 100644
          if self._rpmdb is not None:
              self._rpmdb.ts = None
              self._rpmdb.dropCachedData()
-@@ -567,6 +657,12 @@ class YumBase(depsolve.Depsolve):
+@@ -567,6 +658,12 @@ class YumBase(depsolve.Depsolve):
          self._ts = None
  
      def doRepoSetup(self, thisrepo=None):
@@ -151216,7 +151763,17 @@ index 99039e0..4650639 100644
          warnings.warn(_('doRepoSetup() will go away in a future version of Yum.\n'),
                  Errors.YumFutureDeprecationWarning, stacklevel=2)
  
-@@ -630,6 +726,14 @@ class YumBase(depsolve.Depsolve):
+@@ -588,7 +685,8 @@ class YumBase(depsolve.Depsolve):
+             prerepoconf = self.prerepoconf
+             del self.prerepoconf
+ 
+-            self.repos.setProgressBar(prerepoconf.progressbar)
++            self.repos.setProgressBar(prerepoconf.progressbar,
++                                      prerepoconf.multi_progressbar)
+             self.repos.callback = prerepoconf.callback
+             self.repos.setFailureCallback(prerepoconf.failure_callback)
+             self.repos.setInterruptCallback(prerepoconf.interrupt_callback)
+@@ -630,6 +728,14 @@ class YumBase(depsolve.Depsolve):
          self._repos = RepoStorage(self)
      
      def doSackSetup(self, archlist=None, thisrepo=None):
@@ -151231,7 +151788,7 @@ index 99039e0..4650639 100644
          warnings.warn(_('doSackSetup() will go away in a future version of Yum.\n'),
                  Errors.YumFutureDeprecationWarning, stacklevel=2)
  
-@@ -711,6 +815,9 @@ class YumBase(depsolve.Depsolve):
+@@ -711,6 +817,9 @@ class YumBase(depsolve.Depsolve):
              
             
      def doUpdateSetup(self):
@@ -151241,7 +151798,7 @@ index 99039e0..4650639 100644
          warnings.warn(_('doUpdateSetup() will go away in a future version of Yum.\n'),
                  Errors.YumFutureDeprecationWarning, stacklevel=2)
  
-@@ -765,6 +872,8 @@ class YumBase(depsolve.Depsolve):
+@@ -765,6 +874,8 @@ class YumBase(depsolve.Depsolve):
          return self._up
      
      def doGroupSetup(self):
@@ -151250,7 +151807,7 @@ index 99039e0..4650639 100644
          warnings.warn(_('doGroupSetup() will go away in a future version of Yum.\n'),
                  Errors.YumFutureDeprecationWarning, stacklevel=2)
  
-@@ -829,7 +938,7 @@ class YumBase(depsolve.Depsolve):
+@@ -829,7 +940,7 @@ class YumBase(depsolve.Depsolve):
              try:
                  self._comps.add(groupfile)
              except (Errors.GroupsError,Errors.CompsException), e:
@@ -151259,7 +151816,7 @@ index 99039e0..4650639 100644
                  self.logger.critical(msg)
              else:
                  repo.groups_added = True
-@@ -837,7 +946,10 @@ class YumBase(depsolve.Depsolve):
+@@ -837,7 +948,10 @@ class YumBase(depsolve.Depsolve):
          if self._comps.compscount == 0:
              raise Errors.GroupsError, _('No Groups Available in any repository')
  
@@ -151271,7 +151828,7 @@ index 99039e0..4650639 100644
          self.verbose_logger.debug('group time: %0.3f' % (time.time() - group_st))                
          return self._comps
  
-@@ -868,7 +980,7 @@ class YumBase(depsolve.Depsolve):
+@@ -868,7 +982,7 @@ class YumBase(depsolve.Depsolve):
                      # feed it into _tags.add()
                      self._tags.add(repo.id, tag_sqlite)
                  except (Errors.RepoError, Errors.PkgTagsError), e:
@@ -151280,7 +151837,7 @@ index 99039e0..4650639 100644
                      self.logger.critical(msg)
                      
                  
-@@ -881,9 +993,18 @@ class YumBase(depsolve.Depsolve):
+@@ -881,9 +995,18 @@ class YumBase(depsolve.Depsolve):
          if self._history is None:
              pdb_path = self.conf.persistdir + "/history"
              self._history = yum.history.YumHistory(root=self.conf.installroot,
@@ -151300,7 +151857,7 @@ index 99039e0..4650639 100644
      # properties so they auto-create themselves with defaults
      repos = property(fget=lambda self: self._getRepos(),
                       fset=lambda self, value: setattr(self, "_repos", value),
-@@ -921,6 +1042,11 @@ class YumBase(depsolve.Depsolve):
+@@ -921,6 +1044,11 @@ class YumBase(depsolve.Depsolve):
                         fdel=lambda self: setattr(self, "_history", None),
                         doc="Yum History Object")
  
@@ -151312,7 +151869,7 @@ index 99039e0..4650639 100644
      pkgtags = property(fget=lambda self: self._getTags(),
                         fset=lambda self, value: setattr(self, "_tags",value),
                         fdel=lambda self: setattr(self, "_tags", None),
-@@ -928,9 +1054,10 @@ class YumBase(depsolve.Depsolve):
+@@ -928,9 +1056,10 @@ class YumBase(depsolve.Depsolve):
      
      
      def doSackFilelistPopulate(self):
@@ -151326,7 +151883,7 @@ index 99039e0..4650639 100644
          necessary = False
          
          # I can't think of a nice way of doing this, we have to have the sack here
-@@ -951,8 +1078,12 @@ class YumBase(depsolve.Depsolve):
+@@ -951,8 +1080,12 @@ class YumBase(depsolve.Depsolve):
              self.repos.populateSack(mdtype='filelists')
             
      def yumUtilsMsg(self, func, prog):
@@ -151341,7 +151898,7 @@ index 99039e0..4650639 100644
          if self.rpmdb.contains(name="yum-utils"):
              return
  
-@@ -964,8 +1095,17 @@ class YumBase(depsolve.Depsolve):
+@@ -964,8 +1097,17 @@ class YumBase(depsolve.Depsolve):
               (hibeg, prog, hiend))
  
      def buildTransaction(self, unfinished_transactions_check=True):
@@ -151361,7 +151918,7 @@ index 99039e0..4650639 100644
          if (unfinished_transactions_check and
              misc.find_unfinished_transactions(yumlibpath=self.conf.persistdir)):
              msg = _('There are unfinished transactions remaining. You might ' \
-@@ -1004,7 +1144,7 @@ class YumBase(depsolve.Depsolve):
+@@ -1004,7 +1146,7 @@ class YumBase(depsolve.Depsolve):
              # If transaction was changed by postresolve plugins then we should run skipbroken again
              (rescode, restring) = self._doSkipBroken(rescode, restring, clear_skipped=False )
  
@@ -151370,7 +151927,45 @@ index 99039e0..4650639 100644
              self.tsInfo.pkgSack.dropCachedData()
  
          # FIXME: This is horrible, see below and yummain. Maybe create a real
-@@ -1242,13 +1382,15 @@ class YumBase(depsolve.Depsolve):
+@@ -1044,6 +1186,37 @@ class YumBase(depsolve.Depsolve):
+                 if first.verEQ(other):
+                     continue
+                 msg = _('Protected multilib versions: %s != %s')
++                if not xrestring:
++                    #  People are confused about protected mutilib ... so give
++                    # them a nicer message.
++                    bigmsg = _("""\
++ Multilib version problems found. This often means that the root
++cause is something else and multilib version checking is just
++pointing it that there is a problem. Eg.:
++
++  1. You have an upgrade for %(name)s which is missing some
++     dependency that another package requires. Yum is trying to
++     solve this by installing an older version of %(name)s of the
++     different architecture. If you exclude the bad architecture
++     yum will tell you what the root cause is (which package
++     requires what).
++
++  2. You have multiple architectures of %(name)s installed, but
++     yum can only see an upgrade for one of those arcitectures.
++     If you don't want/need both architectures anymore then you
++     can remove the one with the missing update and everything
++     will work.
++
++  3. You have duplicate versions of %(name)s installed already.
++     You can use "yum check" to get yum show these errors.
++
++...you can also use --setopt=protected_multilib=false to remove
++this checking, however this is almost never the correct thing to
++do as something else is very likely to go wrong (often causing
++much more problems).
++
++""") % {'name' : pkgname}
++                    msg = bigmsg + msg
+                 xrestring.append(msg % (first, other))
+         if xrestring:
+             rescode = 1
+@@ -1242,13 +1415,15 @@ class YumBase(depsolve.Depsolve):
          if None in pkgtup:
              return None
          return pkgtup
@@ -151390,7 +151985,7 @@ index 99039e0..4650639 100644
          if pkgtup is None:
              return
          self._not_found_i[pkgtup] = YumNotFoundPackage(pkgtup)
-@@ -1454,8 +1596,14 @@ class YumBase(depsolve.Depsolve):
+@@ -1454,8 +1629,14 @@ class YumBase(depsolve.Depsolve):
          return probs
  
      def runTransaction(self, cb):
@@ -151406,7 +152001,7 @@ index 99039e0..4650639 100644
          self.plugins.run('pretrans')
  
          #  We may want to put this other places, eventually, but for now it's
-@@ -1516,10 +1664,23 @@ class YumBase(depsolve.Depsolve):
+@@ -1516,10 +1697,23 @@ class YumBase(depsolve.Depsolve):
                  pass
          self._ts_save_file = None
          
@@ -151430,7 +152025,7 @@ index 99039e0..4650639 100644
          
          # make resultobject - just a plain yumgenericholder object
          resultobject = misc.GenericHolder()
-@@ -1567,13 +1728,24 @@ class YumBase(depsolve.Depsolve):
+@@ -1567,13 +1761,24 @@ class YumBase(depsolve.Depsolve):
          self.plugins.run('posttrans')
          # sync up what just happened versus what is in the rpmdb
          if not self.ts.isTsFlagSet(rpm.RPMTRANS_FLAG_TEST):
@@ -151460,7 +152055,7 @@ index 99039e0..4650639 100644
          # check to see that the rpmdb and the tsInfo roughly matches
          # push package object metadata outside of rpmdb into yumdb
          # delete old yumdb metadata entries
-@@ -1584,9 +1756,16 @@ class YumBase(depsolve.Depsolve):
+@@ -1584,9 +1789,16 @@ class YumBase(depsolve.Depsolve):
          #    that there is not also an install of this pkg in the tsInfo (reinstall)
          # for any kind of install add from_repo to the yumdb, and the cmdline
          # and the install reason
@@ -151477,7 +152072,7 @@ index 99039e0..4650639 100644
          for txmbr in self.tsInfo:
              if txmbr.output_state in TS_INSTALL_STATES:
                  if not self.rpmdb.contains(po=txmbr.po):
-@@ -1596,7 +1775,9 @@ class YumBase(depsolve.Depsolve):
+@@ -1596,7 +1808,9 @@ class YumBase(depsolve.Depsolve):
                                             ' but is not!' % txmbr.po))
                      # Note: Get Panu to do te.Failed() so we don't have to
                      txmbr.output_state = TS_FAILED
@@ -151487,7 +152082,7 @@ index 99039e0..4650639 100644
                  po = self.getInstalledPackageObject(txmbr.pkgtup)
                  rpo = txmbr.po
                  po.yumdb_info.from_repo = rpo.repoid
-@@ -1630,6 +1811,10 @@ class YumBase(depsolve.Depsolve):
+@@ -1630,6 +1844,10 @@ class YumBase(depsolve.Depsolve):
                      if md:
                          po.yumdb_info.from_repo_timestamp = str(md.timestamp)
  
@@ -151498,7 +152093,7 @@ index 99039e0..4650639 100644
                  loginuid = misc.getloginuid()
                  if txmbr.updates or txmbr.downgrades or txmbr.reinstall:
                      if txmbr.updates:
-@@ -1640,11 +1825,16 @@ class YumBase(depsolve.Depsolve):
+@@ -1640,11 +1858,16 @@ class YumBase(depsolve.Depsolve):
                          opo = po
                      if 'installed_by' in opo.yumdb_info:
                          po.yumdb_info.installed_by = opo.yumdb_info.installed_by
@@ -151515,7 +152110,7 @@ index 99039e0..4650639 100644
          # Remove old ones after installing new ones, so we can copy values.
          for txmbr in self.tsInfo:
              if txmbr.output_state in TS_INSTALL_STATES:
-@@ -1662,10 +1852,13 @@ class YumBase(depsolve.Depsolve):
+@@ -1662,10 +1885,13 @@ class YumBase(depsolve.Depsolve):
                                                 ' but is not!' % txmbr.po))
                          # Note: Get Panu to do te.Failed() so we don't have to
                          txmbr.output_state = TS_FAILED
@@ -151529,7 +152124,7 @@ index 99039e0..4650639 100644
                  self.verbose_logger.log(logginglevels.DEBUG_2, 'What is this? %s' % txmbr.po)
  
          self.plugins.run('postverifytrans')
-@@ -1680,10 +1873,11 @@ class YumBase(depsolve.Depsolve):
+@@ -1680,10 +1906,11 @@ class YumBase(depsolve.Depsolve):
          self.verbose_logger.debug('VerifyTransaction time: %0.3f' % (time.time() - vt_st))
  
      def costExcludePackages(self):
@@ -151545,7 +152140,7 @@ index 99039e0..4650639 100644
          # if all the repo.costs are equal then don't bother running things
          costs = {}
          for r in self.repos.listEnabled():
-@@ -1705,10 +1899,12 @@ class YumBase(depsolve.Depsolve):
+@@ -1705,10 +1932,12 @@ class YumBase(depsolve.Depsolve):
              done = True
  
      def excludePackages(self, repo=None):
@@ -151561,7 +152156,7 @@ index 99039e0..4650639 100644
          if "all" in self.conf.disable_excludes:
              return
          
-@@ -1735,9 +1931,11 @@ class YumBase(depsolve.Depsolve):
+@@ -1735,9 +1964,11 @@ class YumBase(depsolve.Depsolve):
              self.pkgSack.addPackageExcluder(repoid, exid,'exclude.match', match)
  
      def includePackages(self, repo):
@@ -151576,7 +152171,7 @@ index 99039e0..4650639 100644
          includelist = repo.getIncludePkgList()
          
          if len(includelist) == 0:
-@@ -1757,8 +1955,11 @@ class YumBase(depsolve.Depsolve):
+@@ -1757,8 +1988,11 @@ class YumBase(depsolve.Depsolve):
          self.pkgSack.addPackageExcluder(repo.id, exid, 'exclude.marked')
          
      def doLock(self, lockfile = YUM_PID_FILE):
@@ -151590,7 +152185,7 @@ index 99039e0..4650639 100644
          if self.conf.uid != 0:
              #  If we are a user, assume we are using the root cache ... so don't
              # bother locking.
-@@ -1774,38 +1975,26 @@ class YumBase(depsolve.Depsolve):
+@@ -1774,38 +2008,26 @@ class YumBase(depsolve.Depsolve):
          
          mypid=str(os.getpid())    
          while not self._lock(lockfile, mypid, 0644):
@@ -151644,7 +152239,7 @@ index 99039e0..4650639 100644
          # if we're not root then we don't lock - just return nicely
          #  Note that we can get here from __del__, so if we haven't created
          # YumBase.conf we don't want to do so here as creating stuff inside
-@@ -1830,31 +2019,69 @@ class YumBase(depsolve.Depsolve):
+@@ -1830,31 +2052,69 @@ class YumBase(depsolve.Depsolve):
          self._unlock(lockfile)
          self._lockfile = None
          
@@ -151724,7 +152319,7 @@ index 99039e0..4650639 100644
          failed = False
  
          if type(fo) is types.InstanceType:
-@@ -1894,9 +2121,16 @@ class YumBase(depsolve.Depsolve):
+@@ -1894,9 +2154,16 @@ class YumBase(depsolve.Depsolve):
          
          
      def verifyChecksum(self, fo, checksumType, csum):
@@ -151744,7 +152339,7 @@ index 99039e0..4650639 100644
          try:
              filesum = misc.checksum(checksumType, fo)
          except Errors.MiscError, e:
-@@ -1908,6 +2142,17 @@ class YumBase(depsolve.Depsolve):
+@@ -1908,6 +2175,17 @@ class YumBase(depsolve.Depsolve):
          return 0
  
      def downloadPkgs(self, pkglist, callback=None, callback_total=None):
@@ -151762,7 +152357,7 @@ index 99039e0..4650639 100644
          def mediasort(apo, bpo):
              # FIXME: we should probably also use the mediaid; else we
              # could conceivably ping-pong between different disc1's
-@@ -1979,8 +2224,9 @@ class YumBase(depsolve.Depsolve):
+@@ -1979,8 +2257,9 @@ class YumBase(depsolve.Depsolve):
              urlgrabber.progress.text_meter_total_size(remote_size)
          beg_download = time.time()
          i = 0
@@ -151773,7 +152368,7 @@ index 99039e0..4650639 100644
          for po in remote_pkgs:
              #  Recheck if the file is there, works around a couple of weird
              # edge cases.
-@@ -1992,52 +2238,47 @@ class YumBase(depsolve.Depsolve):
+@@ -1992,52 +2271,47 @@ class YumBase(depsolve.Depsolve):
                      remote_size -= po.size
                      if hasattr(urlgrabber.progress, 'text_meter_total_size'):
                          urlgrabber.progress.text_meter_total_size(remote_size,
@@ -151852,7 +152447,7 @@ index 99039e0..4650639 100644
          if hasattr(urlgrabber.progress, 'text_meter_total_size'):
              urlgrabber.progress.text_meter_total_size(0)
          if callback_total is not None and not errors:
-@@ -2052,7 +2293,22 @@ class YumBase(depsolve.Depsolve):
+@@ -2052,7 +2326,22 @@ class YumBase(depsolve.Depsolve):
          return errors
  
      def verifyHeader(self, fo, po, raiseError):
@@ -151876,7 +152471,7 @@ index 99039e0..4650639 100644
          if type(fo) is types.InstanceType:
              fo = fo.filename
              
-@@ -2076,9 +2332,12 @@ class YumBase(depsolve.Depsolve):
+@@ -2076,9 +2365,12 @@ class YumBase(depsolve.Depsolve):
          return 1
          
      def downloadHeader(self, po):
@@ -151891,7 +152486,7 @@ index 99039e0..4650639 100644
          if hasattr(po, 'pkgtype') and po.pkgtype == 'local':
              return
                  
-@@ -2122,15 +2381,17 @@ class YumBase(depsolve.Depsolve):
+@@ -2122,15 +2414,17 @@ class YumBase(depsolve.Depsolve):
              return
  
      def sigCheckPkg(self, po):
@@ -151917,7 +152512,7 @@ index 99039e0..4650639 100644
          if self._override_sigchecks:
              check = False
              hasgpgkey = 0
-@@ -2181,6 +2442,9 @@ class YumBase(depsolve.Depsolve):
+@@ -2181,6 +2475,9 @@ class YumBase(depsolve.Depsolve):
          return result, msg
  
      def cleanUsedHeadersPackages(self):
@@ -151927,7 +152522,7 @@ index 99039e0..4650639 100644
          filelist = []
          for txmbr in self.tsInfo:
              if txmbr.po.state not in TS_INSTALL_STATES:
-@@ -2218,27 +2482,42 @@ class YumBase(depsolve.Depsolve):
+@@ -2218,27 +2515,42 @@ class YumBase(depsolve.Depsolve):
                      _('%s removed'), fn)
          
      def cleanHeaders(self):
@@ -151972,7 +152567,7 @@ index 99039e0..4650639 100644
          cachedir = self.conf.persistdir + "/rpmdb-indexes/"
          if not os.path.exists(cachedir):
              filelist = []
-@@ -2272,8 +2551,29 @@ class YumBase(depsolve.Depsolve):
+@@ -2272,8 +2584,29 @@ class YumBase(depsolve.Depsolve):
  
      def doPackageLists(self, pkgnarrow='all', patterns=None, showdups=None,
                         ignore_case=False):
@@ -152004,7 +152599,7 @@ index 99039e0..4650639 100644
          if showdups is None:
              showdups = self.conf.showdupesfromrepos
          ygh = misc.GenericHolder(iter=pkgnarrow)
-@@ -2323,10 +2623,22 @@ class YumBase(depsolve.Depsolve):
+@@ -2323,10 +2656,22 @@ class YumBase(depsolve.Depsolve):
                      key = (pkg.name, pkg.arch)
                      if pkg.pkgtup in dinst:
                          reinstall_available.append(pkg)
@@ -152030,7 +152625,7 @@ index 99039e0..4650639 100644
  
          # produce the updates list of tuples
          elif pkgnarrow == 'updates':
-@@ -2461,14 +2773,13 @@ class YumBase(depsolve.Depsolve):
+@@ -2461,14 +2806,13 @@ class YumBase(depsolve.Depsolve):
  
          
      def findDeps(self, pkgs):
@@ -152050,7 +152645,7 @@ index 99039e0..4650639 100644
          results = {}
  
          for pkg in pkgs:
-@@ -2495,10 +2806,22 @@ class YumBase(depsolve.Depsolve):
+@@ -2495,10 +2839,22 @@ class YumBase(depsolve.Depsolve):
      # pre 3.2.10 API used to always showdups, so that's the default atm.
      def searchGenerator(self, fields, criteria, showdups=True, keys=False, 
                                               searchtags=True, searchrpmdb=True):
@@ -152077,7 +152672,7 @@ index 99039e0..4650639 100644
          sql_fields = []
          for f in fields:
              sql_fields.append(RPM_TO_SQLITE.get(f, f))
-@@ -2661,6 +2984,14 @@ class YumBase(depsolve.Depsolve):
+@@ -2661,6 +3017,14 @@ class YumBase(depsolve.Depsolve):
                      yield (po, vs)
  
      def searchPackageTags(self, criteria):
@@ -152092,7 +152687,7 @@ index 99039e0..4650639 100644
          results = {} # name = [(criteria, taglist)]
          for c in criteria:
              c = c.lower()
-@@ -2677,11 +3008,16 @@ class YumBase(depsolve.Depsolve):
+@@ -2677,11 +3041,16 @@ class YumBase(depsolve.Depsolve):
          return results
          
      def searchPackages(self, fields, criteria, callback=None):
@@ -152114,7 +152709,7 @@ index 99039e0..4650639 100644
          warnings.warn(_('searchPackages() will go away in a future version of Yum.\
                        Use searchGenerator() instead. \n'),
                  Errors.YumFutureDeprecationWarning, stacklevel=2)           
-@@ -2700,13 +3036,23 @@ class YumBase(depsolve.Depsolve):
+@@ -2700,13 +3069,23 @@ class YumBase(depsolve.Depsolve):
      
      def searchPackageProvides(self, args, callback=None,
                                callback_has_matchfor=False):
@@ -152142,7 +152737,7 @@ index 99039e0..4650639 100644
              else:
                  isglob = True
                  canBeFile = misc.re_filename(arg)
-@@ -2723,7 +3069,7 @@ class YumBase(depsolve.Depsolve):
+@@ -2723,7 +3102,7 @@ class YumBase(depsolve.Depsolve):
                  where = self.returnPackagesByDep(arg)
              else:
                  usedDepString = False
@@ -152151,10 +152746,16 @@ index 99039e0..4650639 100644
              self.verbose_logger.log(logginglevels.DEBUG_1,
                 P_('Searching %d package', 'Searching %d packages', len(where)), len(where))
              
-@@ -2817,25 +3163,93 @@ class YumBase(depsolve.Depsolve):
+@@ -2817,25 +3196,160 @@ class YumBase(depsolve.Depsolve):
              
          return matches
  
+-    def doGroupLists(self, uservisible=0, patterns=None, ignore_case=True):
+-        """returns two lists of groups, installed groups and available groups
+-           optional 'uservisible' bool to tell it whether or not to return
+-           only groups marked as uservisible"""
+-        
+-        
 +    def _groupInstalledData(self, group):
 +        """ Return a dict of
 +             pkg_name =>
@@ -152189,30 +152790,81 @@ index 99039e0..4650639 100644
 +
 +        return ret
 +
++    def _groupInstalledEnvData(self, evgroup):
++        """ Return a dict of
++             grp_name =>
++             (installed, available,
++             backlisted-installed, blacklisted-available). """
++        ret = {}
++        if not evgroup or self.conf.group_command != 'objects':
++            return ret
++
++        grp_names = {}
++        if evgroup.environmentid in self.igroups.groups:
++            grp_names = self.igroups.environments[evgroup.environmentid]
++            grp_names = grp_names.grp_names
++
++        for grp_name in set(evgroup.allgroups + list(grp_names)):
++            igrp = self.igroups.groups.get(grp_name)
++            if grp_name not in grp_names and not igrp:
++                ret[grp_name] = 'available'
++                continue
++
++            if not igrp:
++                ret[grp_name] = 'blacklisted-available'
++                continue
++
++            if igrp.environment == evgroup.environmentid:
++                ret[grp_name] = 'installed'
++                break
++            else:
++                ret[grp_name] = 'blacklisted-installed'
++
++        return ret
++
 +    def _groupReturnGroups(self, patterns=None, ignore_case=True):
 +        igrps = None
++        ievgrps = None
 +        if patterns is None:
 +            grps = self.comps.groups
 +            if self.conf.group_command == 'objects':
 +                igrps = self.igroups.groups.values()
-+            return igrps, grps
++            evgrps = self.comps.environments
++            if False and self.conf.group_command == 'objects':
++                # FIXME: Environment groups.
++                ievgrps = self.igroups.environments.values()
++            return igrps, grps, ievgrps, evgrps
++
++        gpats = []
++        epats = []
++        for pat in patterns:
++            if pat.startswith('@^'):
++                epats.append(pat[2:])
++            elif pat.startswith('@'):
++                gpats.append(pat[1:])
++            else:
++                epats.append(pat)
++                gpats.append(pat)
++
++        epats = ",".join(epats)
++        gpats = ",".join(gpats)
 +
-+        pats = ",".join(patterns)
 +        cs   = not ignore_case
-+        grps = self.comps.return_groups(pats, case_sensitive=cs)
++        grps = self.comps.return_groups(gpats, case_sensitive=cs)
 +        #  Because we want name matches too, and we don't store group names
 +        # we need to add the groupid's we've found:
 +        if self.conf.group_command == 'objects':
-+            pats += "," + ",".join([grp.groupid for grp in grps])
-+            igrps = self.igroups.return_groups(pats, case_sensitive=cs)
-+        return igrps, grps
++            gpats = gpats + "," + ",".join([grp.groupid for grp in grps])
++            igrps = self.igroups.return_groups(gpats, case_sensitive=cs)
 +
-     def doGroupLists(self, uservisible=0, patterns=None, ignore_case=True):
--        """returns two lists of groups, installed groups and available groups
--           optional 'uservisible' bool to tell it whether or not to return
--           only groups marked as uservisible"""
--        
--        
++        evgrps = self.comps.return_environments(epats, case_sensitive=cs)
++        if self.conf.group_command == 'objects':
++            epats = epats+ "," + ",".join([grp.environmentid for grp in evgrps])
++            ievgrps = self.igroups.return_environments(epats, case_sensitive=cs)
++        return igrps, grps, ievgrps, evgrps
++
++    def doGroupLists(self, uservisible=0, patterns=None, ignore_case=True,
++                     return_evgrps=False):
 +        """Return two lists of groups: installed groups and available
 +        groups.
 +
@@ -152223,9 +152875,13 @@ index 99039e0..4650639 100644
 +           lists.  If not given, all groups will be included
 +        :param ignore_case: whether to ignore case when determining
 +           whether group names match the strings in *patterns*
++        :param return_evgrps: whether to return environment groups as well as
++           package groups
 +        """
          installed = []
          available = []
++        einstalled = []
++        eavailable = []
  
          if self.comps.compscount == 0:
              raise Errors.GroupsError, _('No group data available for configured repositories')
@@ -152235,7 +152891,8 @@ index 99039e0..4650639 100644
 -        else:
 -            grps = self.comps.return_groups(",".join(patterns),
 -                                            case_sensitive=not ignore_case)
-+        igrps, grps = self._groupReturnGroups(patterns, ignore_case)
++        igrps, grps, ievgrps, evgrps = self._groupReturnGroups(patterns,
++                                                               ignore_case)
 +
 +        if igrps is not None:
 +            digrps = {}
@@ -152243,6 +152900,12 @@ index 99039e0..4650639 100644
 +                digrps[igrp.gid] = igrp
 +            igrps = digrps
 +
++        if ievgrps is not None:
++            digrps = {}
++            for ievgrp in ievgrps:
++                digrps[ievgrp.evgid] = ievgrp
++            ievgrps = digrps
++
          for grp in grps:
 -            if grp.installed:
 +            if igrps is None:
@@ -152256,23 +152919,53 @@ index 99039e0..4650639 100644
                  if uservisible:
                      if grp.user_visible:
                          installed.append(grp)
-@@ -2848,12 +3262,29 @@ class YumBase(depsolve.Depsolve):
+@@ -2847,34 +3361,98 @@ class YumBase(depsolve.Depsolve):
+                         available.append(grp)
                  else:
                      available.append(grp)
++
++        for evgrp in evgrps:
++            if ievgrps is None:
++                evgrp_installed = evgrp.installed
++            else:
++                evgrp_installed = evgrp.environmentid in ievgrps
++                if evgrp_installed:
++                    del ievgrps[evgrp.environmentid]
++
++            if evgrp_installed:
++                einstalled.append(evgrp)
++            else:
++                eavailable.append(evgrp)
              
 +        if igrps is None:
-+            return sorted(installed), sorted(available)
++            igrps = {}
++        if ievgrps is None:
++            ievgrps = {}
 +
 +        for igrp in igrps.values():
 +            #  These are installed groups that aren't in comps anymore. so we
 +            # create fake comps groups for them.
 +            grp = comps.Group()
++            grp.groupid = igrp.gid
 +            grp.installed = True
 +            grp.name = grp.groupid
 +            for pkg_name in igrp.pkg_names:
 +                grp.mandatory_packages[pkg_name] = 1
 +            installed.append(grp)
 +
++        for ievgrp in ievgrps.values():
++            #  These are installed evgroups that aren't in comps anymore. so we
++            # create fake comps evgroups for them.
++            evgrp = comps.Environment()
++            grp.environmentid = ievgrp.evgid
++            evgrp.installed = True
++            evgrp.name = evgrp.environmentid
++            evgrp._groups = list(ievgrp.groups)
++            einstalled.append(evgrp)
++
++        if return_evgrps:
++            return (sorted(installed), sorted(available),
++                    sorted(einstalled), sorted(eavailable))
          return sorted(installed), sorted(available)
      
 -    
@@ -152287,26 +152980,41 @@ index 99039e0..4650639 100644
 +           transaction set by this function
 +        """
          txmbrs_used = []
-         
-         thesegroups = self.comps.return_groups(grpid)
-@@ -2861,20 +3292,28 @@ class YumBase(depsolve.Depsolve):
+-        
+-        thesegroups = self.comps.return_groups(grpid)
++
++        if self.conf.group_command == 'objects':
++            thesegroups = self.igroups.return_groups(grpid)
++        else:
++            thesegroups = self.comps.return_groups(grpid)
+         if not thesegroups:
              raise Errors.GroupsError, _("No Group named %s exists") % to_unicode(grpid)
  
          for thisgroup in thesegroups:
 +            igroup_data = self._groupInstalledData(thisgroup)
 +
              thisgroup.toremove = True
-             pkgs = thisgroup.packages
-             for pkg in thisgroup.packages:
+-            pkgs = thisgroup.packages
+-            for pkg in thisgroup.packages:
++
++            if self.conf.group_command == 'objects':
++                pkgs = thisgroup.pkg_names
++                gid  = thisgroup.gid
++            else:
++                pkgs = thisgroup.packages
++                gid  = thisgroup.groupid
++
++            for pkg in pkgs:
 +                if pkg in igroup_data and igroup_data[pkg] != 'installed':
 +                    continue
 +
                  txmbrs = self.remove(name=pkg, silence_warnings=True)
                  txmbrs_used.extend(txmbrs)
                  for txmbr in txmbrs:
-                     txmbr.groups.append(thisgroup.groupid)
+-                    txmbr.groups.append(thisgroup.groupid)
++                    txmbr.groups.append(gid)
 +            if igroup_data:
-+                self.igroups.del_group(thisgroup.groupid)
++                self.igroups.del_group(gid)
              
          return txmbrs_used
  
@@ -152320,7 +153028,7 @@ index 99039e0..4650639 100644
          thesegroups = self.comps.return_groups(grpid)
          if not thesegroups:
              raise Errors.GroupsError, _("No Group named %s exists") % to_unicode(grpid)
-@@ -2898,13 +3337,18 @@ class YumBase(depsolve.Depsolve):
+@@ -2898,13 +3476,58 @@ class YumBase(depsolve.Depsolve):
                              self.tsInfo.remove(txmbr.po.pkgtup)
          
          
@@ -152331,8 +153039,48 @@ index 99039e0..4650639 100644
 -           Optionally take:
 -           group_package_types=List - overrides self.conf.group_package_types
 -           enable_group_conditionals=Bool - overrides self.conf.enable_group_conditionals
++    def environmentRemove(self, evgrpid):
++        """Mark all the packages in the given group to be removed.
++
++        :param evgrpid: the name of the environment containing the groups to
++           mark for removal
++        :return: a list of transaction members added to the
++           transaction set by this function
++        """
++        txmbrs_used = []
++
++        if self.conf.group_command == 'objects':
++            thesegroups = self.igroups.return_environments(evgrpid)
++        else:
++            thesegroups = self.comps.return_environments(evgrpid)
++        if not thesegroups:
++            raise Errors.GroupsError, _("No Environment named %s exists") % to_unicode(evgrpid)
++
++        for thisgroup in thesegroups:
++            igroup_data = self._groupInstalledEnvData(thisgroup)
++
++            if self.conf.group_command == 'objects':
++                grps  = thisgroup.grp_names
++                evgid = thisgroup.evgid
++            else:
++                grps  = thisgroup.allgroups
++                evgid = thisgroup.environmentid
++
++            for grp in grps:
++                if grp in igroup_data and igroup_data[grp] != 'installed':
++                    continue
++
++                txmbrs = self.groupRemove(grp)
++                txmbrs_used.extend(txmbrs)
++                for txmbr in txmbrs:
++                    txmbr.environments.append(evgid)
++            if igroup_data:
++                self.igroups.del_environment(evgid)
++
++        return txmbrs_used
++
 +    def selectGroup(self, grpid, group_package_types=[],
-+                    enable_group_conditionals=None, upgrade=False):
++                    enable_group_conditionals=None, upgrade=False, ievgrp=None):
 +        """Mark all the packages in the given group to be installed.
 +
 +        :param grpid: the name of the group containing the packages to
@@ -152346,7 +153094,17 @@ index 99039e0..4650639 100644
          """
  
          if not self.comps.has_group(grpid):
-@@ -2934,12 +3378,47 @@ class YumBase(depsolve.Depsolve):
+@@ -2920,6 +3543,9 @@ class YumBase(depsolve.Depsolve):
+         if group_package_types:
+             package_types = group_package_types
+ 
++        if self.conf.group_command == 'compat':
++            upgrade = False
++
+         for thisgroup in thesegroups:
+             if thisgroup.selected:
+                 continue
+@@ -2934,12 +3560,49 @@ class YumBase(depsolve.Depsolve):
              if 'optional' in package_types:
                  pkgs.extend(thisgroup.optional_packages)
  
@@ -152356,7 +153114,8 @@ index 99039e0..4650639 100644
 +                if thisgroup.groupid in self.igroups.groups:
 +                    igrp = self.igroups.groups[thisgroup.groupid]
 +                else:
-+                    self.igroups.add_group(thisgroup.groupid,thisgroup.packages)
++                    self.igroups.add_group(thisgroup.groupid,
++                                           thisgroup.packages, ievgrp)
 +            pkgs.extend(list(igroup_data.keys()))
 +
              old_txmbrs = len(txmbrs_used)
@@ -152384,7 +153143,8 @@ index 99039e0..4650639 100644
 +                    if (upgrade and
 +                        (self.conf.group_command == 'simple' or
 +                         (igroup_data and igroup_data[pkg] == 'installed'))):
-+                        txmbrs = self.update(name = pkg)
++                        txmbrs = self.update(name = pkg,
++                                             pkg_warning_level='debug2')
 +                    elif igroup_data and igroup_data[pkg] == 'installed':
 +                        pass # Don't upgrade on install.
 +                    else:
@@ -152395,15 +153155,28 @@ index 99039e0..4650639 100644
                  except Errors.InstallError, e:
                      self.verbose_logger.debug(_('No package named %s available to be installed'),
                          pkg)
-@@ -2953,6 +3432,7 @@ class YumBase(depsolve.Depsolve):
+@@ -2953,7 +3616,9 @@ class YumBase(depsolve.Depsolve):
                  group_conditionals = enable_group_conditionals
  
              count_cond_test = 0
+-            if group_conditionals:
 +            # FIXME: What do we do about group conditionals when group==objects
-             if group_conditionals:
++            #        or group upgrade for group_command=simple?
++            if not upgrade and group_conditionals:
                  for condreq, cond in thisgroup.conditional_packages.iteritems():
                      if self.isPackageInstalled(cond):
-@@ -2997,10 +3477,14 @@ class YumBase(depsolve.Depsolve):
+                         try:
+@@ -2990,17 +3655,22 @@ class YumBase(depsolve.Depsolve):
+                         if cond not in self.tsInfo.conditionals:
+                             self.tsInfo.conditionals[cond] = []
+                         self.tsInfo.conditionals[cond].extend(pkgs)
+-            if len(txmbrs_used) == old_txmbrs:
+-                self.logger.critical(_('Warning: Group %s does not have any packages.'), thisgroup.groupid)
++
++            if not upgrade and len(txmbrs_used) == old_txmbrs:
++                self.logger.critical(_('Warning: Group %s does not have any packages to install.'), thisgroup.groupid)
+                 if count_cond_test:
+                     self.logger.critical(_('Group %s does have %u conditional packages, which may get installed.'), count_cond_test)
          return txmbrs_used
  
      def deselectGroup(self, grpid, force=False):
@@ -152422,9 +153195,100 @@ index 99039e0..4650639 100644
          
          if not self.comps.has_group(grpid):
              raise Errors.GroupsError, _("No Group named %s exists") % to_unicode(grpid)
-@@ -3035,12 +3519,21 @@ class YumBase(depsolve.Depsolve):
+@@ -3008,7 +3678,8 @@ class YumBase(depsolve.Depsolve):
+         thesegroups = self.comps.return_groups(grpid)
+         if not thesegroups:
+             raise Errors.GroupsError, _("No Group named %s exists") % to_unicode(grpid)
+-        
++
++        # FIXME: Do something with groups as objects, and env. groups.
+         for thisgroup in thesegroups:
+             thisgroup.selected = False
+             
+@@ -3034,13 +3705,102 @@ class YumBase(depsolve.Depsolve):
+                         for pkg in self.tsInfo.conditionals.get(txmbr.name, []):
                              self.tsInfo.remove(pkg.pkgtup)
          
++    def selectEnvironment(self, evgrpid, group_package_types=[],
++                          enable_group_conditionals=None, upgrade=False):
++        """Mark all the groups in the given environment group to be installed.
++
++        :param evgrpid: the name of the env. group containing the groups to
++           mark for installation
++        :param group_package_types: a list of the types of groups to
++           work with.  This overrides self.conf.group_package_types
++        :param enable_group_conditionals: overrides
++           self.conf.enable_group_conditionals
++        :return: a list of transaction members added to the
++           transaction set by this function
++        """
++        evgrps = self.comps.return_environments(evgrpid)
++        if not evgrps:
++            raise Errors.GroupsError, _("No Environment named %s exists") % to_unicode(evgrpid)
++
++        ret = []
++        for evgrp in evgrps:
++
++            ievgrp = None
++            if self.conf.group_command == 'compat':
++                grps = ",".join(sorted(evgrp.groups))
++            elif self.conf.group_command == 'simple':
++                if not upgrade:
++                    grps = ",".join(sorted(evgrp.groups))
++                else: # Only upgrade the installed groups...
++                    grps = []
++                    for grpid in evgrp.groups:
++                        grp = self.comps.return_group(grpid)
++                        if grp is None:
++                            continue
++                        if not grp.installed:
++                            continue
++                        grps.append(grpid)
++                    grps = ",".join(sorted(grps))
++            elif self.conf.group_command == 'objects':
++                igroup_data = self._groupInstalledEnvData(evgrp)
++ 
++                grps = []
++                for grpid in evgrp.groups:
++                    if (grpid not in igroup_data or
++                        igroup_data[grpid].startswith('blacklisted')):
++                        msg = _('Skipping group %s from environment %s'),
++                        self.verbose_logger.log(logginglevels.DEBUG_2,
++                                                msg, grpid, evgrp.environmentid)
++                        continue
++                    grps.append(grpid)
++                if evgrp.environmentid in self.igroups.environments:
++                    ievgrp = self.igroups.environments[evgrp.environmentid]
++                else:
++                    self.igroups.add_environment(evgrp.environmentid,
++                                                 evgrp.allgroups)
++                grps = ",".join(sorted(grps))
++
++            txs = self.selectGroup(grps,
++                                   group_package_types,
++                                   enable_group_conditionals, upgrade,
++                                   ievgrp=ievgrp)
++            ret.extend(txs)
++        return ret
++
++    def deselectEnvironment(self, evgrpid, force=False):
++        """Unmark the groups in the given environment group from being
++        installed.
++
++        :param evgrpid: the name of the environment group containing the
++           groups to unmark from installation
++        :param force: if True, force remove all the packages in the
++           given groups from the transaction
++        """
++        evgrps = self.comps.return_environments(evgrpid)
++        if not thesegroups:
++            raise Errors.GroupsError, _("No Environment named %s exists") % to_unicode(evgrpid)
++
++        for evgrp in evgrps:
++            grps = ",".join(sorted(evgrp.groups))
++            self.deselectGroup(grps, force)
++            # FIXME: env. needs to be marked not-to-be-installed, etc.
++
      def getPackageObject(self, pkgtup, allow_missing=False):
 -        """retrieves a packageObject from a pkgtuple - if we need
 -           to pick and choose which one is best we better call out
@@ -152450,7 +153314,7 @@ index 99039e0..4650639 100644
          # look it up in the self.localPackages first:
          for po in self.localPackages:
              if po.pkgtup == pkgtup:
-@@ -3049,7 +3542,7 @@ class YumBase(depsolve.Depsolve):
+@@ -3049,7 +3809,7 @@ class YumBase(depsolve.Depsolve):
          pkgs = self.pkgSack.searchPkgTuple(pkgtup)
  
          if len(pkgs) == 0:
@@ -152459,7 +153323,7 @@ index 99039e0..4650639 100644
              if allow_missing: #  This can happen due to excludes after .up has
                  return None   # happened.
              raise Errors.DepError, _('Package tuple %s could not be found in packagesack') % str(pkgtup)
-@@ -3065,13 +3558,21 @@ class YumBase(depsolve.Depsolve):
+@@ -3065,13 +3825,21 @@ class YumBase(depsolve.Depsolve):
          return result
  
      def getInstalledPackageObject(self, pkgtup):
@@ -152486,7 +153350,7 @@ index 99039e0..4650639 100644
              raise Errors.RpmDBError, _('Package tuple %s could not be found in rpmdb') % str(pkgtup)
  
          # Dito. FIXME from getPackageObject() for len() > 1 ... :)
-@@ -3079,9 +3580,11 @@ class YumBase(depsolve.Depsolve):
+@@ -3079,9 +3847,11 @@ class YumBase(depsolve.Depsolve):
          return po
          
      def gpgKeyCheck(self):
@@ -152500,7 +153364,7 @@ index 99039e0..4650639 100644
          gpgkeyschecked = self.conf.cachedir + '/.gpgkeyschecked.yum'
          if os.path.exists(gpgkeyschecked):
              return 1
-@@ -3106,9 +3609,13 @@ class YumBase(depsolve.Depsolve):
+@@ -3106,9 +3876,13 @@ class YumBase(depsolve.Depsolve):
              return 1
  
      def returnPackagesByDep(self, depstring):
@@ -152516,7 +153380,7 @@ index 99039e0..4650639 100644
          if not depstring:
              return []
  
-@@ -3135,9 +3642,16 @@ class YumBase(depsolve.Depsolve):
+@@ -3135,9 +3909,16 @@ class YumBase(depsolve.Depsolve):
          return self.pkgSack.getProvides(depname, depflags, depver).keys()
  
      def returnPackageByDep(self, depstring):
@@ -152536,7 +153400,7 @@ index 99039e0..4650639 100644
          # we get all sorts of randomness here
          errstring = depstring
          if type(depstring) not in types.StringTypes:
-@@ -3149,16 +3663,22 @@ class YumBase(depsolve.Depsolve):
+@@ -3149,16 +3930,22 @@ class YumBase(depsolve.Depsolve):
              raise Errors.YumBaseError, _('No Package found for %s') % errstring
          
          ps = ListPackageSack(pkglist)
@@ -152563,7 +153427,7 @@ index 99039e0..4650639 100644
          if not depstring:
              return []
  
-@@ -3184,12 +3704,47 @@ class YumBase(depsolve.Depsolve):
+@@ -3184,12 +3971,47 @@ class YumBase(depsolve.Depsolve):
  
          return self.rpmdb.getProvides(depname, depflags, depver).keys()
  
@@ -152613,7 +153477,7 @@ index 99039e0..4650639 100644
          
          
          if len(pkglist) == 0:
-@@ -3198,14 +3753,23 @@ class YumBase(depsolve.Depsolve):
+@@ -3198,14 +4020,23 @@ class YumBase(depsolve.Depsolve):
          if len(pkglist) == 1:
              return pkglist[0]
  
@@ -152643,7 +153507,7 @@ index 99039e0..4650639 100644
          returnlist = []
          compatArchList = self.arch.get_arch_list(arch)
          multiLib = []
-@@ -3222,9 +3786,9 @@ class YumBase(depsolve.Depsolve):
+@@ -3222,9 +4053,9 @@ class YumBase(depsolve.Depsolve):
                  singleLib.append(po)
                  
          # we now have three lists.  find the best package(s) of each
@@ -152656,7 +153520,7 @@ index 99039e0..4650639 100644
  
          if single_name and multi and single and multi.name != single.name:
              # Sinlge _must_ match multi, if we want a single package name
-@@ -3238,7 +3802,7 @@ class YumBase(depsolve.Depsolve):
+@@ -3238,7 +4069,7 @@ class YumBase(depsolve.Depsolve):
          # if there's a noarch and it's newer than the multilib, we want
          # just the noarch.  otherwise, we want multi + single
          elif multi:
@@ -152665,7 +153529,7 @@ index 99039e0..4650639 100644
              if best.arch == "noarch":
                  returnlist.append(no)
              else:
-@@ -3246,7 +3810,7 @@ class YumBase(depsolve.Depsolve):
+@@ -3246,7 +4077,7 @@ class YumBase(depsolve.Depsolve):
                  if single: returnlist.append(single)
          # similar for the non-multilib case
          elif single:
@@ -152674,7 +153538,7 @@ index 99039e0..4650639 100644
              if best.arch == "noarch":
                  returnlist.append(no)
              else:
-@@ -3350,23 +3914,27 @@ class YumBase(depsolve.Depsolve):
+@@ -3350,28 +4181,58 @@ class YumBase(depsolve.Depsolve):
              done = True
  
              slow = next_func(slow)
@@ -152689,6 +153553,20 @@ index 99039e0..4650639 100644
          assert pattern[0] == '@'
          group_string = pattern[1:]
          tx_return = []
++
++        if group_string and group_string[0] == '^':
++            group_string = group_string[1:]
++            # Actually dealing with "environment groups".
++            for env_grp in self.comps.return_environments(group_string):
++                try:
++                    txmbrs = self.selectEnvironment(env_grp.environmentid,
++                                                    upgrade=upgrade)
++                    tx_return.extend(txmbrs)
++                except yum.Errors.GroupsError:
++                    self.logger.critical(_('Warning: Environment Group %s does not exist.'), group_string)
++                    continue
++            return tx_return
++
          for group in self.comps.return_groups(group_string):
              try:
 -                txmbrs = self.selectGroup(group.groupid)
@@ -152707,16 +153585,46 @@ index 99039e0..4650639 100644
      def _at_groupremove(self, pattern):
          " Do groupremove via. leading @ on the cmd line, for remove."
          assert pattern[0] == '@'
-@@ -3398,7 +3966,7 @@ class YumBase(depsolve.Depsolve):
+         group_string = pattern[1:]
+         tx_return = []
++
++        if group_string and group_string[0] == '^':
++            group_string = group_string[1:]
++            # Actually dealing with "environment groups".
++            try:
++                txmbrs = self.environmentRemove(group_string)
++            except yum.Errors.GroupsError:
++                self.logger.critical(_('Warning: Environment Group %s does not exist.'), group_string)
++            else:
++                tx_return.extend(txmbrs)
++            return tx_return
++
+         try:
+             txmbrs = self.groupRemove(group_string)
+         except yum.Errors.GroupsError:
+@@ -3387,6 +4248,8 @@ class YumBase(depsolve.Depsolve):
+         assert pattern[0] == '@'
+         grpid = pattern[1:]
+ 
++        # FIXME: **** environment groups and groups as objects... ****
++
+         thesegroups = self.comps.return_groups(grpid)
+         if not thesegroups:
+             raise Errors.GroupsError, _("No Group named %s exists") % to_unicode(grpid)
+@@ -3398,7 +4261,11 @@ class YumBase(depsolve.Depsolve):
      def _minus_deselect(self, pattern):
          """ Remove things from the transaction, like kickstart. """
          assert pattern[0] == '-'
 -        pat = pattern[1:]
 +        pat = pattern[1:].strip()
++
++        if pat and pat.startswith('@^'):
++            pat = pat[2:]
++            return self.deselectEnvironment(pat)
  
          if pat and pat[0] == '@':
              pat = pat[1:]
-@@ -3437,14 +4005,61 @@ class YumBase(depsolve.Depsolve):
+@@ -3437,14 +4304,61 @@ class YumBase(depsolve.Depsolve):
              if flag not in self.tsInfo.probFilterFlags:
                  self.tsInfo.probFilterFlags.append(flag)
  
@@ -152784,7 +153692,7 @@ index 99039e0..4650639 100644
          pkgs = []
          was_pattern = False
          if po:
-@@ -3477,20 +4092,12 @@ class YumBase(depsolve.Depsolve):
+@@ -3477,20 +4391,12 @@ class YumBase(depsolve.Depsolve):
                      self.verbose_logger.debug(_('Checking for virtual provide or file-provide for %s'), 
                          arg)
  
@@ -152811,7 +153719,7 @@ index 99039e0..4650639 100644
              else:
                  nevra_dict = self._nevra_kwarg_parse(kwargs)
  
-@@ -3577,8 +4184,8 @@ class YumBase(depsolve.Depsolve):
+@@ -3577,8 +4483,8 @@ class YumBase(depsolve.Depsolve):
                      continue
              
              # make sure this shouldn't be passed to update:
@@ -152822,7 +153730,7 @@ index 99039e0..4650639 100644
                  txmbrs = self.update(po=po)
                  tx_return.extend(txmbrs)
                  continue
-@@ -3587,7 +4194,7 @@ class YumBase(depsolve.Depsolve):
+@@ -3587,7 +4493,7 @@ class YumBase(depsolve.Depsolve):
              # something else in the repo. Unless there is a obsoletion loop,
              # at which point ignore everything.
              obsoleting_pkg = None
@@ -152831,7 +153739,7 @@ index 99039e0..4650639 100644
                  obsoleting_pkg = self._test_loop(po, self._pkg2obspkg)
              if obsoleting_pkg is not None:
                  # this is not a definitive check but it'll make sure we don't
-@@ -3600,23 +4207,23 @@ class YumBase(depsolve.Depsolve):
+@@ -3600,23 +4506,23 @@ class YumBase(depsolve.Depsolve):
                      already_obs = pkgs[0]
  
                  if already_obs:
@@ -152862,7 +153770,7 @@ index 99039e0..4650639 100644
                      continue
  
              # make sure we don't have a name.arch of this already installed
-@@ -3630,7 +4237,7 @@ class YumBase(depsolve.Depsolve):
+@@ -3630,7 +4536,7 @@ class YumBase(depsolve.Depsolve):
                          found = True
                          break
                  if not found:
@@ -152871,7 +153779,7 @@ index 99039e0..4650639 100644
                      txmbrs = self.update(po=po)
                      tx_return.extend(txmbrs)
                      continue
-@@ -3719,14 +4326,33 @@ class YumBase(depsolve.Depsolve):
+@@ -3719,19 +4625,47 @@ class YumBase(depsolve.Depsolve):
          return txmbr
  
      def update(self, po=None, requiringPo=None, update_to=False, **kwargs):
@@ -152912,7 +153820,21 @@ index 99039e0..4650639 100644
          # check for args - if no po nor kwargs, do them all
          # if po, do it, ignore all else
          # if no po do kwargs
-@@ -3765,7 +4391,12 @@ class YumBase(depsolve.Depsolve):
+         # uninstalled pkgs called for update get returned with errors in a list, maybe?
+ 
++        pkg_warn = kwargs.get('pkg_warning_level', 'flibble')
++        def _dbg2(*args, **kwargs):
++            self.verbose_logger.log(logginglevels.DEBUG_2, *args, **kwargs)
++        level2func = {'debug2' : _dbg2,
++                      'warning' : self.verbose_logger.warning}
++        if pkg_warn not in level2func:
++            pkg_warn = 'warning'
++        pkg_warn = level2func[pkg_warn]
++
+         tx_return = []
+         if not po and not kwargs: # update everything (the easy case)
+             self.verbose_logger.log(logginglevels.DEBUG_2, _('Updating Everything'))
+@@ -3765,7 +4699,14 @@ class YumBase(depsolve.Depsolve):
                      if new is None:
                          continue
                      tx_return.extend(self.update(po=new))
@@ -152920,13 +153842,15 @@ index 99039e0..4650639 100644
 +
 +            # Upgrade the installed groups, as part of generic "yum upgrade"
 +            if self.conf.group_command == 'objects':
++                for ievgrp in self.igroups.environments:
++                    tx_return.extend(self._at_groupupgrade('@^' + ievgrp))
 +                for igrp in self.igroups.groups:
-+                    tx_return.extend(self._at_groupupgrade(igrp))
++                    tx_return.extend(self._at_groupupgrade('@'  + igrp))
 +
              return tx_return
  
          # complications
-@@ -3787,7 +4418,7 @@ class YumBase(depsolve.Depsolve):
+@@ -3787,7 +4728,7 @@ class YumBase(depsolve.Depsolve):
                  return self._minus_deselect(kwargs['pattern'])
  
              if kwargs['pattern'] and kwargs['pattern'][0] == '@':
@@ -152935,7 +153859,16 @@ index 99039e0..4650639 100644
  
              arg = kwargs['pattern']
              if not update_to:
-@@ -3920,6 +4551,18 @@ class YumBase(depsolve.Depsolve):
+@@ -3843,7 +4784,7 @@ class YumBase(depsolve.Depsolve):
+                     availpkgs = self._compare_providers(availpkgs, requiringPo)
+                     availpkgs = map(lambda x: x[0], availpkgs)
+                 elif not availpkgs:
+-                    self.logger.warning(_("No package matched to upgrade: %s"), self._ui_nevra_dict(nevra_dict))
++                    pkg_warn(_("No package matched to upgrade: %s"), self._ui_nevra_dict(nevra_dict))
+        
+         # for any thing specified
+         # get the list of available pkgs matching it (or take the po)
+@@ -3920,6 +4861,18 @@ class YumBase(depsolve.Depsolve):
                      tx_return.append(txmbr)
                          
          for available_pkg in availpkgs:
@@ -152954,7 +153887,7 @@ index 99039e0..4650639 100644
              #  Make sure we're not installing a package which is obsoleted by
              # something else in the repo. Unless there is a obsoletion loop,
              # at which point ignore everything.
-@@ -3985,11 +4628,18 @@ class YumBase(depsolve.Depsolve):
+@@ -3985,11 +4938,18 @@ class YumBase(depsolve.Depsolve):
          return tx_return
          
      def remove(self, po=None, **kwargs):
@@ -152978,7 +153911,7 @@ index 99039e0..4650639 100644
          if not po and not kwargs:
              raise Errors.RemoveError, 'Nothing specified to remove'
          
-@@ -4055,17 +4705,19 @@ class YumBase(depsolve.Depsolve):
+@@ -4055,17 +5015,19 @@ class YumBase(depsolve.Depsolve):
          return tx_return
  
      def installLocal(self, pkg, po=None, updateonly=False):
@@ -153008,7 +153941,7 @@ index 99039e0..4650639 100644
          # read in the package into a YumLocalPackage Object
          # append it to self.localPackages
          # check if it can be installed or updated based on nevra versus rpmdb
-@@ -4183,16 +4835,15 @@ class YumBase(depsolve.Depsolve):
+@@ -4183,16 +5145,15 @@ class YumBase(depsolve.Depsolve):
          return tx_return
  
      def reinstallLocal(self, pkg, po=None):
@@ -153033,7 +153966,7 @@ index 99039e0..4650639 100644
          if not po:
              try:
                  po = YumUrlPackage(self, ts=self.rpmdb.readOnlyTS(), url=pkg,
-@@ -4215,9 +4866,19 @@ class YumBase(depsolve.Depsolve):
+@@ -4215,9 +5176,19 @@ class YumBase(depsolve.Depsolve):
          return self.reinstall(po=po)
  
      def reinstall(self, po=None, **kwargs):
@@ -153056,7 +153989,7 @@ index 99039e0..4650639 100644
          self._add_prob_flags(rpm.RPMPROB_FILTER_REPLACEPKG,
                               rpm.RPMPROB_FILTER_REPLACENEWFILES,
                               rpm.RPMPROB_FILTER_REPLACEOLDFILES)
-@@ -4259,16 +4920,15 @@ class YumBase(depsolve.Depsolve):
+@@ -4259,16 +5230,15 @@ class YumBase(depsolve.Depsolve):
          return tx_mbrs
          
      def downgradeLocal(self, pkg, po=None):
@@ -153081,7 +154014,7 @@ index 99039e0..4650639 100644
          if not po:
              try:
                  po = YumUrlPackage(self, ts=self.rpmdb.readOnlyTS(), url=pkg,
-@@ -4309,13 +4969,19 @@ class YumBase(depsolve.Depsolve):
+@@ -4309,13 +5279,19 @@ class YumBase(depsolve.Depsolve):
          return False
          
      def downgrade(self, po=None, **kwargs):
@@ -153108,7 +154041,7 @@ index 99039e0..4650639 100644
          if not po and not kwargs:
              raise Errors.DowngradeError, 'Nothing specified to downgrade'
  
-@@ -4421,6 +5087,9 @@ class YumBase(depsolve.Depsolve):
+@@ -4421,6 +5397,9 @@ class YumBase(depsolve.Depsolve):
                  warned_nas.add(na)
                  continue
  
@@ -153118,7 +154051,7 @@ index 99039e0..4650639 100644
              if pkg.verGE(lipkg):
                  if na not in warned_nas:
                      msg = _('Only Upgrade available on package: %s') % pkg
-@@ -4457,7 +5126,7 @@ class YumBase(depsolve.Depsolve):
+@@ -4457,7 +5436,7 @@ class YumBase(depsolve.Depsolve):
          if e and v and r:
              evr = '%s:%s-%s' % (e, v, r)
          elif v and r:
@@ -153127,7 +154060,7 @@ index 99039e0..4650639 100644
          elif e and v:
              evr = '%s:%s' % (e, v)
          elif v: # e and r etc. is just too weird to print
-@@ -4500,12 +5169,24 @@ class YumBase(depsolve.Depsolve):
+@@ -4500,12 +5479,24 @@ class YumBase(depsolve.Depsolve):
  
          return returndict
  
@@ -153155,7 +154088,7 @@ index 99039e0..4650639 100644
          old_conf_obs = self.conf.obsoletes
          self.conf.obsoletes = False
          done = False
-@@ -4515,19 +5196,46 @@ class YumBase(depsolve.Depsolve):
+@@ -4515,19 +5506,46 @@ class YumBase(depsolve.Depsolve):
                      done = True
          for pkg in transaction.trans_data:
              if pkg.state == 'Downgrade':
@@ -153202,7 +154135,7 @@ index 99039e0..4650639 100644
                  if self.install(pkgtup=pkg.pkgtup):
                      done = True
          for pkg in transaction.trans_data:
-@@ -4538,8 +5246,14 @@ class YumBase(depsolve.Depsolve):
+@@ -4538,8 +5556,14 @@ class YumBase(depsolve.Depsolve):
          return done
  
      def history_undo(self, transaction):
@@ -153219,7 +154152,7 @@ index 99039e0..4650639 100644
          # NOTE: This is somewhat basic atm. ... for instance we don't check
          #       that we are going from the old new version. However it's still
          #       better than the RHN rollback code, and people pay for that :).
-@@ -4616,7 +5330,7 @@ class YumBase(depsolve.Depsolve):
+@@ -4616,7 +5640,7 @@ class YumBase(depsolve.Depsolve):
  
          except urlgrabber.grabber.URLGrabError, e:
              raise Errors.YumBaseError(_('GPG key retrieval failed: ') +
@@ -153228,7 +154161,7 @@ index 99039e0..4650639 100644
                                        
          # check for a .asc file accompanying it - that's our gpg sig on the key
          # suck it down and do the check
-@@ -4649,7 +5363,7 @@ class YumBase(depsolve.Depsolve):
+@@ -4649,7 +5673,7 @@ class YumBase(depsolve.Depsolve):
              keys_info = misc.getgpgkeyinfo(rawkey, multiple=True)
          except ValueError, e:
              raise Errors.YumBaseError(_('Invalid GPG Key from %s: %s') % 
@@ -153237,7 +154170,7 @@ index 99039e0..4650639 100644
          keys = []
          for keyinfo in keys_info:
              thiskey = {}
-@@ -4674,39 +5388,49 @@ class YumBase(depsolve.Depsolve):
+@@ -4674,39 +5698,49 @@ class YumBase(depsolve.Depsolve):
              if pkgs:
                  pkgs = sorted(pkgs)[-1]
                  msg = (_('Importing %s key 0x%s:\n'
@@ -153305,7 +154238,7 @@ index 99039e0..4650639 100644
          user_cb_fail = False
          for keyurl in keyurls:
              keys = self._retrievePublicKey(keyurl, repo)
-@@ -4725,7 +5449,9 @@ class YumBase(depsolve.Depsolve):
+@@ -4725,7 +5759,9 @@ class YumBase(depsolve.Depsolve):
                      # Try installing/updating GPG key
                      self._getKeyImportMessage(info, keyurl)
                      rc = False
@@ -153316,7 +154249,7 @@ index 99039e0..4650639 100644
                          rc = True
                          
                      # grab the .sig/.asc for the keyurl, if it exists
-@@ -4751,8 +5477,8 @@ class YumBase(depsolve.Depsolve):
+@@ -4751,8 +5787,8 @@ class YumBase(depsolve.Depsolve):
                  ts = self.rpmdb.readOnlyTS()
                  result = ts.pgpImportPubkey(misc.procgpgkey(info['raw_key']))
                  if result != 0:
@@ -153327,7 +154260,7 @@ index 99039e0..4650639 100644
                  self.logger.info(_('Key imported successfully'))
                  key_installed = True
  
-@@ -4760,18 +5486,20 @@ class YumBase(depsolve.Depsolve):
+@@ -4760,18 +5796,20 @@ class YumBase(depsolve.Depsolve):
              raise Errors.YumBaseError, _("Didn't install any keys")
  
          if not key_installed:
@@ -153353,7 +154286,7 @@ index 99039e0..4650639 100644
      
      def _getAnyKeyForRepo(self, repo, destdir, keyurl_list, is_cakey=False, callback=None):
          """
-@@ -4788,6 +5516,18 @@ class YumBase(depsolve.Depsolve):
+@@ -4788,6 +5826,18 @@ class YumBase(depsolve.Depsolve):
          """
  
          key_installed = False
@@ -153372,7 +154305,7 @@ index 99039e0..4650639 100644
          user_cb_fail = False
          for keyurl in keyurl_list:
              keys = self._retrievePublicKey(keyurl, repo, getSig=not is_cakey)
-@@ -4819,8 +5559,11 @@ class YumBase(depsolve.Depsolve):
+@@ -4819,8 +5869,11 @@ class YumBase(depsolve.Depsolve):
                  if not key_installed:
                      self._getKeyImportMessage(info, keyurl, keytype)
                      rc = False
@@ -153385,7 +154318,7 @@ index 99039e0..4650639 100644
                      elif callback:
                          rc = callback({"repo": repo, "userid": info['userid'],
                                          "hexkeyid": info['hexkeyid'], "keyurl": keyurl,
-@@ -4835,7 +5578,8 @@ class YumBase(depsolve.Depsolve):
+@@ -4835,7 +5888,8 @@ class YumBase(depsolve.Depsolve):
                  # Import the key
                  result = misc.import_key_to_pubring(info['raw_key'], info['hexkeyid'], gpgdir=destdir)
                  if not result:
@@ -153395,7 +154328,7 @@ index 99039e0..4650639 100644
                  self.logger.info(_('Key imported successfully'))
                  key_installed = True
                  # write out the key id to imported_cakeys in the repos basedir
-@@ -4851,36 +5595,35 @@ class YumBase(depsolve.Depsolve):
+@@ -4851,36 +5905,35 @@ class YumBase(depsolve.Depsolve):
                              pass
  
          if not key_installed and user_cb_fail:
@@ -153448,7 +154381,7 @@ index 99039e0..4650639 100644
          self._getAnyKeyForRepo(repo, repo.gpgcadir, repo.gpgcakey, is_cakey=True, callback=callback)
  
      def _limit_installonly_pkgs(self):
-@@ -4927,6 +5670,7 @@ class YumBase(depsolve.Depsolve):
+@@ -4927,6 +5980,7 @@ class YumBase(depsolve.Depsolve):
          ts = self.rpmdb.readOnlyTS()
          (cur_kernel_v, cur_kernel_r) = misc.get_running_kernel_version_release(ts)
          install_only_names = set(self.conf.installonlypkgs)
@@ -153456,7 +154389,7 @@ index 99039e0..4650639 100644
          for m in self.tsInfo.getMembers():
              if m.ts_state not in ('i', 'u'):
                  continue
-@@ -4937,12 +5681,21 @@ class YumBase(depsolve.Depsolve):
+@@ -4937,12 +5991,21 @@ class YumBase(depsolve.Depsolve):
              if not po_names.intersection(install_only_names):
                  continue
  
@@ -153482,7 +154415,7 @@ index 99039e0..4650639 100644
              for po in installed:
                  if (po.version, po.release) == (cur_kernel_v, cur_kernel_r): 
                      # don't remove running
-@@ -4959,19 +5712,22 @@ class YumBase(depsolve.Depsolve):
+@@ -4959,19 +6022,22 @@ class YumBase(depsolve.Depsolve):
              txmbr.depends_on.append(rel)
  
      def processTransaction(self, callback=None,rpmTestDisplay=None, rpmDisplay=None):
@@ -153518,7 +154451,7 @@ index 99039e0..4650639 100644
          
          if not callback:
              callback = callbacks.ProcessTransNoOutputCallback()
-@@ -5114,13 +5870,19 @@ class YumBase(depsolve.Depsolve):
+@@ -5114,13 +6180,19 @@ class YumBase(depsolve.Depsolve):
          return results
  
      def add_enable_repo(self, repoid, baseurls=[], mirrorlist=None, **kwargs):
@@ -153545,7 +154478,7 @@ index 99039e0..4650639 100644
          # out of place fixme - maybe we should make this the default repo addition
          # routine and use it from getReposFromConfigFile(), etc.
          newrepo = yumRepo.YumRepository(repoid)
-@@ -5167,9 +5929,15 @@ class YumBase(depsolve.Depsolve):
+@@ -5167,9 +6239,15 @@ class YumBase(depsolve.Depsolve):
  
      def setCacheDir(self, force=False, tmpdir=None, reuse=True,
                      suffix='/$basearch/$releasever'):
@@ -153564,7 +154497,7 @@ index 99039e0..4650639 100644
          if not force and os.geteuid() == 0:
              return True # We are root, not forced, so happy with the global dir.
          if tmpdir is None:
-@@ -5179,7 +5947,7 @@ class YumBase(depsolve.Depsolve):
+@@ -5179,7 +6257,7 @@ class YumBase(depsolve.Depsolve):
          try:
              cachedir = misc.getCacheDir(tmpdir, reuse)
          except (IOError, OSError), e:
@@ -153573,7 +154506,7 @@ index 99039e0..4650639 100644
              cachedir = None
              
          if cachedir is None:
-@@ -5190,6 +5958,7 @@ class YumBase(depsolve.Depsolve):
+@@ -5190,6 +6268,7 @@ class YumBase(depsolve.Depsolve):
              self.prerepoconf.cachedir = cachedir
          else:
              self.repos.setCacheDir(cachedir)
@@ -153581,7 +154514,7 @@ index 99039e0..4650639 100644
          self.conf.cachedir = cachedir
          return True # We got a new cache dir
  
-@@ -5220,13 +5989,24 @@ class YumBase(depsolve.Depsolve):
+@@ -5220,13 +6299,24 @@ class YumBase(depsolve.Depsolve):
          self.history.write_addon_data('config-repos', myrepos)
          
      def verify_plugins_cb(self, verify_package):
@@ -153609,7 +154542,7 @@ index 99039e0..4650639 100644
          if self.tsInfo._unresolvedMembers:
              if auto:
                  self.logger.critical(_("Dependencies not solved. Will not save unresolved transaction."))
-@@ -5234,7 +6014,7 @@ class YumBase(depsolve.Depsolve):
+@@ -5234,7 +6324,7 @@ class YumBase(depsolve.Depsolve):
              raise Errors.YumBaseError(_("Dependencies not solved. Will not save unresolved transaction."))
          
          if not filename:
@@ -153618,7 +154551,7 @@ index 99039e0..4650639 100644
              fd,filename = tempfile.mkstemp(suffix='.yumtx', prefix=prefix)
              f = os.fdopen(fd, 'w')
          else:
-@@ -5244,13 +6024,17 @@ class YumBase(depsolve.Depsolve):
+@@ -5244,13 +6334,17 @@ class YumBase(depsolve.Depsolve):
          
          msg = "%s\n" % self.rpmdb.simpleVersion(main_only=True)[0]
          msg += "%s\n" % self.ts.getTsFlags()
@@ -153639,7 +154572,7 @@ index 99039e0..4650639 100644
          msg += "%s\n" % len(self.tsInfo.getMembers())
          for txmbr in self.tsInfo.getMembers():
              msg += txmbr._dump()
-@@ -5260,13 +6044,25 @@ class YumBase(depsolve.Depsolve):
+@@ -5260,13 +6354,25 @@ class YumBase(depsolve.Depsolve):
          except (IOError, OSError), e:
              self._ts_save_file = None
              if auto:
@@ -153671,7 +154604,7 @@ index 99039e0..4650639 100644
          # check rpmversion - if not match throw a fit
          # check repoversions  (and repos)- if not match throw a fit
          # load each txmbr - if pkgs being updated don't exist, bail w/error
-@@ -5276,26 +6072,45 @@ class YumBase(depsolve.Depsolve):
+@@ -5276,26 +6382,45 @@ class YumBase(depsolve.Depsolve):
          try:
              data = open(filename, 'r').readlines()
          except (IOError, OSError), e:
@@ -153719,7 +154652,7 @@ index 99039e0..4650639 100644
              if ignorerpm:
                  msg += _(" ignoring, as requested.")
                  self.logger.critical(_(msg))
-@@ -5318,8 +6133,17 @@ class YumBase(depsolve.Depsolve):
+@@ -5318,8 +6443,17 @@ class YumBase(depsolve.Depsolve):
          numrepos = int(data[2].strip())
          repos = []
          rindex=3+numrepos
@@ -153738,7 +154671,7 @@ index 99039e0..4650639 100644
  
          # pkgs/txmbrs
          numpkgs = int(data[rindex].strip())
-@@ -5329,6 +6153,7 @@ class YumBase(depsolve.Depsolve):
+@@ -5329,6 +6463,7 @@ class YumBase(depsolve.Depsolve):
          pkgcount = 0
          pkgprob = False
          curpkg = None
@@ -153746,7 +154679,7 @@ index 99039e0..4650639 100644
          for l in data[pkgstart:]:
              l = l.rstrip()
              # our main txmbrs
-@@ -5438,6 +6263,11 @@ class YumBase(depsolve.Depsolve):
+@@ -5438,6 +6573,11 @@ class YumBase(depsolve.Depsolve):
                  msg += _(" aborting.")
                  raise Errors.YumBaseError(msg)
              
@@ -153758,7 +154691,7 @@ index 99039e0..4650639 100644
          return self.tsInfo.getMembers()
  
      def _remove_old_deps(self):
-@@ -5470,18 +6300,6 @@ class YumBase(depsolve.Depsolve):
+@@ -5470,18 +6610,6 @@ class YumBase(depsolve.Depsolve):
                      if requiring == required: # if they are self-requiring skip them
                          continue
                          
@@ -153777,7 +154710,7 @@ index 99039e0..4650639 100644
                      #for tbi_pkg in self.tsInfo.getMembersWithState(output_states=TS_INSTALL_STATES):
                      #   for reqtuple in tbi_pkg.po.requires:
                      #        if required.provides_for(reqtuple):
-@@ -5533,7 +6351,24 @@ class YumBase(depsolve.Depsolve):
+@@ -5533,7 +6661,24 @@ class YumBase(depsolve.Depsolve):
                      # Debugging output
                      self.verbose_logger.log(logginglevels.DEBUG_2, _("%s has revdep %s which was user-installed."), pkg, curpkg)
                      ok_to_remove[pkg] = False
@@ -153802,7 +154735,7 @@ index 99039e0..4650639 100644
                  visited[curpkg] = True
              all_leaves_visited = True
              leaves = curpkg.requiring_packages()
-@@ -5547,4 +6382,3 @@ class YumBase(depsolve.Depsolve):
+@@ -5547,4 +6692,3 @@ class YumBase(depsolve.Depsolve):
          # Debugging output
          self.verbose_logger.log(logginglevels.DEBUG_2, _("%s has no user-installed revdeps."), pkg)
          return False
@@ -153954,6 +154887,322 @@ index 7ad25ce..a9a8e53 100644
 +        """
          pass
          
+diff --git a/yum/comps.py b/yum/comps.py
+index 65f6d5e..cf671b6 100755
+--- a/yum/comps.py
++++ b/yum/comps.py
+@@ -43,6 +43,16 @@ class CompsObj(object):
+         return self.name
+ 
+     @property
++    def compsid(self):
++        """ Return the "id": categoryid, groupid, environmentid. """
++
++        for idT in ('categoryid', 'groupid', 'environmentid'):
++            if hasattr(self, idT):
++                return getattr(self, idT)
++
++        return None
++
++    @property
+     def ui_name(self):
+         """ Return the "name" of the object for the current locale. """
+         return self.nameByLang(get_my_lang_code())
+@@ -108,7 +118,7 @@ class Group(CompsObj):
+         self.optional_packages = {}
+         self.default_packages = {}
+         self.conditional_packages = {}
+-        self.langonly = None ## what the hell is this?
++        self.langonly = None
+         self.groupid = None
+         self.display_order = 1024
+         self.installed = False
+@@ -272,6 +282,137 @@ class Group(CompsObj):
+         return msg      
+ 
+ 
++class Environment(CompsObj):
++    """ Environment object parsed from group data in each repo, and merged """
++
++    def __init__(self, elem=None):
++        self.name = ""
++        self.environmentid = None
++        self.description = ""
++        self.translated_name = {}
++        self.translated_description = {}
++        self.display_order = 1024
++        self.langonly = None
++        self.installed = False
++        self._groups = {}
++        self._options = {}
++
++        if elem:
++            self.parse(elem)
++
++    def _allgroupiter(self):
++        lst = self._groups.keys() + \
++              self._options.keys()
++        return lst
++
++    allgroups = property(_allgroupiter)
++
++    def _groupiter(self):
++        return self._groups.keys()
++
++    groups = property(_groupiter)
++
++    def _optioniter(self):
++        return self._options.keys()
++
++    options = property(_optioniter)
++
++    def parse(self, elem):
++        for child in elem:
++            if child.tag == 'id':
++                myid = child.text
++                if self.environmentid is not None:
++                    raise CompsException
++                self.environmentid = myid
++
++            elif child.tag == 'name':
++                text = child.text
++                if text:
++                    text = text.encode('utf8')
++
++                lang = child.attrib.get(lang_attr)
++                if lang:
++                    self.translated_name[lang] = text
++                else:
++                    self.name = text
++
++            elif child.tag == 'description':
++                text = child.text
++                if text:
++                    text = text.encode('utf8')
++
++                lang = child.attrib.get(lang_attr)
++                if lang:
++                    self.translated_description[lang] = text
++                else:
++                    self.description = text
++
++            elif child.tag == 'grouplist':
++                self.parse_group_list(child)
++
++            elif child.tag == 'optionlist':
++                self.parse_option_list(child)
++
++            elif child.tag == 'display_order':
++                self.display_order = parse_number(child.text)
++
++    def parse_group_list(self, grouplist_elem):
++        for child in grouplist_elem:
++            if child.tag == 'groupid':
++                groupid = child.text
++                self._groups[groupid] = 1
++
++    def parse_option_list(self, optionlist_elem):
++        for child in optionlist_elem:
++            if child.tag == 'groupid':
++                optionid = child.text
++                self._options[optionid] = 1
++
++    def add(self, obj):
++        """Add another category object to this object"""
++
++        for grp in obj.groups:
++            self._groups[grp] = 1
++
++        for grp in obj.options:
++            self._options[grp] = 1
++
++        # name and description translations
++        for lang in obj.translated_name:
++            if lang not in self.translated_name:
++                self.translated_name[lang] = obj.translated_name[lang]
++
++        for lang in obj.translated_description:
++            if lang not in self.translated_description:
++                self.translated_description[lang] = obj.translated_description[lang]
++
++    def xml(self):
++        """write out an xml stanza for the environment object"""
++        msg ="""
++  <environment>
++   <id>%s</id>
++   <display_order>%s</display_order>\n""" % (self.environmentid, self.display_order)
++
++        msg +="""   <name>%s</name>\n""" % self.name
++        for (lang, val) in self.translated_name.items():
++            msg += """   <name xml:lang="%s">%s</name>\n""" % (lang, val)
++
++        msg += """   <description>%s</description>\n""" % self.description
++        for (lang, val) in self.translated_description.items():
++            msg += """    <description xml:lang="%s">%s</description>\n""" % (lang, val)
++
++        msg += """    <grouplist>\n"""
++        for grp in self.groups:
++            msg += """     <groupid>%s</groupid>\n""" % grp
++        msg += """    </grouplist>\n"""
++        msg += """    <optionlist>\n"""
++        for grp in self.options:
++            msg += """     <optionid>%s</optionid>\n""" % grp
++        msg += """    </optionlist>\n"""
++        msg += """  </environment>\n"""
++
++        return msg
++
+ class Category(CompsObj):
+     """ Category object parsed from group data in each repo. and merged. """
+ 
+@@ -376,6 +517,7 @@ class Category(CompsObj):
+ class Comps(object):
+     def __init__(self, overwrite_groups=False):
+         self._groups = {}
++        self._environments = {}
+         self._categories = {}
+         self.compscount = 0
+         self.overwrite_groups = overwrite_groups
+@@ -388,12 +530,18 @@ class Comps(object):
+         grps.sort(key=lambda x: (x.display_order, x.name))
+         return grps
+         
++    def get_environments(self):
++        environments = self._environments.values()
++        environments.sort(key=lambda x: (x.display_order, x.name))
++        return environments
++
+     def get_categories(self):
+         cats = self._categories.values()
+         cats.sort(key=lambda x: (x.display_order, x.name))
+         return cats
+     
+     groups = property(get_groups)
++    environments = property(get_environments)
+     categories = property(get_categories)
+     
+     def has_group(self, grpid):
+@@ -447,6 +595,57 @@ class Comps(object):
+ 
+         return returns.values()
+ 
++    def has_environment(self, environmentid):
++        exists = self.return_environments(environmentid)
++
++        if exists:
++            return True
++
++        return False
++
++    def return_environment(self, environmentid):
++        """Return the first group which matches"""
++        environments = self.return_environments(environmentid)
++        if environments:
++            return environments[0]
++
++        return None
++
++    def return_environments(self, env_pattern, case_sensitive=False):
++        """return all environments which match either by glob or exact match"""
++        returns = {}
++
++        for item in env_pattern.split(','):
++            item = item.strip()
++            if item in self._environments:
++                env = self._environments[item]
++                returns[env.environmentid] = env
++                continue
++
++            if case_sensitive:
++                match = re.compile(fnmatch.translate(item)).match
++            else:
++                match = re.compile(fnmatch.translate(item), flags=re.I).match
++
++            done = False
++            for env in self.environments:
++                for name in env.name, env.environmentid, env.ui_name:
++                    if match(name):
++                        done = True
++                        returns[env.environmentid] = env
++                        break
++            if done:
++                continue
++
++            # If we didn't match to anything in the current locale, try others
++            for env in self.environments:
++                for name in env.translated_name.values():
++                    if match(name):
++                        returns[env.environmentid] = env
++                        break
++
++        return returns.values()
++
+     #  This is close to returnPackages() etc. API ... need to std. these names
+     # the above return_groups uses different, but equal, API.
+     def return_categories(self, pattern, ignore_case=True):
+@@ -490,6 +689,13 @@ class Comps(object):
+         else:
+             self._groups[group.groupid] = group
+ 
++    def add_environment(self, environment):
++        if environment.environmentid in self._environments:
++            env = self._environments[environment.environmentid]
++            env.add(environment)
++        else:
++            self._environments[environment.environmentid] = environment
++
+     def add_category(self, category):
+         if category.categoryid in self._categories:
+             thatcat = self._categories[category.categoryid]
+@@ -520,6 +726,9 @@ class Comps(object):
+                 if elem.tag == "group":
+                     group = Group(elem)
+                     self.add_group(group)
++                if elem.tag == "environment":
++                    environment = Environment(elem)
++                    self.add_environment(environment)
+                 if elem.tag == "category":
+                     category = Category(elem)
+                     self.add_category(category)
+@@ -557,6 +766,24 @@ class Comps(object):
+                     if pkgname in inst_pkg_names:
+                         group.installed = True
+                         break
++
++        # Now do basically the same thing for evgroups.
++        inst_grp_names = {}
++        for group in self.groups:
++            inst_grp_names[group.groupid] = group.installed
++        for evgroup in self.environments:
++            if evgroup.groups:
++                evgroup.installed = True
++                for grpname in evgroup.groups:
++                    if not inst_grp_names.get(grpname):
++                        evgroup.installed = False
++                        break
++            else:
++                evgroup.installed = False
++                for grpname in evgroup.optional:
++                    if grpname in inst_grp_names:
++                        evgroup.installed = True
++                        break
+         
+         self.compiled = True
+     
+@@ -595,6 +822,13 @@ def main():
+             for pkg in group.packages:
+                 print '  ' + pkg
+         
++        for environment in p.environments:
++            print environment.name
++            for group in environment.groups:
++                print '  ' + group
++            for group in environment.options:
++                print '  *' + group
++
+         for category in p.categories:
+             print category.name
+             for group in category.groups:
 diff --git a/yum/config.py b/yum/config.py
 index d09511f..2bf4f45 100644
 --- a/yum/config.py
@@ -156084,10 +157333,10 @@ index 9889bf6..76a258d 100755
      Setup the yum translation domain and make _() and P_() translation wrappers
 diff --git a/yum/igroups.py b/yum/igroups.py
 new file mode 100644
-index 0000000..625ee66
+index 0000000..6a04f3a
 --- /dev/null
 +++ b/yum/igroups.py
-@@ -0,0 +1,141 @@
+@@ -0,0 +1,267 @@
 +#! /usr/bin/python -tt
 +# This program is free software; you can redistribute it and/or modify
 +# it under the terms of the GNU General Public License as published by
@@ -156103,7 +157352,7 @@ index 0000000..625ee66
 +# along with this program; if not, write to the Free Software
 +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 +#
-+# Copyright 2010 Red Hat
++# Copyright 2010, 2012 Red Hat
 +#
 +# James Antill <james at fedoraproject.org>
 +
@@ -156115,6 +157364,7 @@ index 0000000..625ee66
 +    def __init__(self, gid):
 +        self.gid       = gid
 +        self.pkg_names = set()
++        self.environment = None
 +
 +    def __cmp__(self, other):
 +        if other is None:
@@ -156130,12 +157380,36 @@ index 0000000..625ee66
 +        return sorted(pkg_names.difference(self.pkg_names))
 +
 +
++class InstalledEnvironment(object):
++    def __init__(self, evgid):
++        self.evgid     = evgid
++        self.grp_names = set()
++
++    def __cmp__(self, other):
++        if other is None:
++            return 1
++        return cmp(self.evgid, other.evgid)
++
++    def _additions(self, grp_names):
++        grp_names = set(grp_names)
++        return sorted(grp_names.difference(self.grp_names))
++
++    def _removals(self, grp_names):
++        grp_names = set(grp_names)
++        return sorted(grp_names.difference(self.grp_names))
++
++
 +class InstalledGroups(object):
 +    def __init__(self, db_path):
-+        self.filename = db_path + "/installed"
 +        self.groups   = {}
 +        self.changed  = False
++        self.environments = {}
++
++        self._read_pkg_grps(db_path)
++        self._read_grp_grps(db_path)
 +
++    def _read_pkg_grps(self, db_path):
++        self.filename = db_path + "/installed"
 +        if not os.access(self.filename, os.R_OK):
 +            return
 +
@@ -156159,6 +157433,38 @@ index 0000000..625ee66
 +                num -= 1
 +                grp.pkg_names.add(_read_str(fo))
 +
++    def _read_grp_grps(self, db_path):
++        self.grp_filename = db_path + "/environment"
++        if not os.access(self.grp_filename, os.R_OK):
++            return
++
++        def _read_str(fo):
++            return fo.readline()[:-1]
++
++        fo = open(self.grp_filename)
++        ver = int(_read_str(fo))
++        if ver != 1:
++            return
++
++        groups_num = int(_read_str(fo))
++        while groups_num > 0:
++            groups_num -= 1
++
++            evgrp = InstalledEnvironment(_read_str(fo))
++            self.environments[evgrp.evgid] = evgrp
++
++            num = int(_read_str(fo))
++            while num > 0:
++                num -= 1
++                grpname = _read_str(fo)
++                memb = _read_str(fo)
++                evgrp.grp_names.add(grpname)
++                assert memb in ('true', 'false')
++                if memb == 'true':
++                    assert grpname in self.groups
++                    if grpname in self.groups:
++                        self.groups[grpname].environment = evgrp.evgid
++
 +    def close(self):
 +        pass
 +
@@ -156177,6 +157483,12 @@ index 0000000..625ee66
 +        if not os.access(db_path, os.W_OK):
 +            return False
 +
++        self._write_pkg_grps()
++        self._write_grp_grps()
++
++        self.changed = False
++
++    def _write_pkg_grps(self):
 +        fo = open(self.filename + '.tmp', 'w')
 +
 +        fo.write("1\n") # version
@@ -156188,9 +157500,26 @@ index 0000000..625ee66
 +                fo.write("%s\n" % pkgname)
 +        fo.close()
 +        os.rename(self.filename + '.tmp', self.filename)
-+        self.changed = False
 +
-+    def add_group(self, groupid, pkg_names):
++    def _write_grp_grps(self):
++        fo = open(self.grp_filename + '.tmp', 'w')
++
++        fo.write("1\n") # version
++        fo.write("%u\n" % len(self.environments))
++        for evgrp in sorted(self.environments.values()):
++            fo.write("%s\n" % evgrp.evgid)
++            fo.write("%u\n" % len(evgrp.grp_names))
++            for grpname in sorted(evgrp.grp_names):
++                fo.write("%s\n" % grpname)
++                if self.groups[grpname].environment == evgrp.evgid:
++                    fo.write("%s\n" % "true")
++                else:
++                    fo.write("%s\n" % "false")
++
++        fo.close()
++        os.rename(self.grp_filename + '.tmp', self.grp_filename)
++
++    def add_group(self, groupid, pkg_names, ievgrp=None):
 +        self.changed = True
 +
 +        if groupid not in self.groups:
@@ -156200,6 +157529,11 @@ index 0000000..625ee66
 +        for pkg_name in pkg_names:
 +            grp.pkg_names.add(pkg_name)
 +
++        if ievgrp is not None:
++            grp.environment = ievgrp.evgid
++            ievgrp.grp_names.add(groupid)
++        return grp
++
 +    def del_group(self, groupid):
 +        self.changed = True
 +
@@ -156229,6 +157563,47 @@ index 0000000..625ee66
 +                    break
 +
 +        return returns.values()
++
++    def add_environment(self, evgroupid, grp_names):
++        self.changed = True
++
++        if evgroupid not in self.environments:
++            self.environments[evgroupid] = InstalledEnvironment(evgroupid)
++        grp = self.environments[evgroupid]
++
++        for grp_name in grp_names:
++            grp.grp_names.add(grp_name)
++        return grp
++
++    def del_environment(self, evgroupid):
++        self.changed = True
++
++        if evgroupid in self.environments:
++            del self.environments[evgroupid]
++
++    def return_environments(self, evgroup_pattern, case_sensitive=False):
++        returns = {}
++
++        for item in evgroup_pattern.split(','):
++            item = item.strip()
++            if item in self.environments:
++                thisgroup = self.environments[item]
++                returns[thisgroup.evgid] = thisgroup
++                continue
++
++            if case_sensitive:
++                match = re.compile(fnmatch.translate(item)).match
++            else:
++                match = re.compile(fnmatch.translate(item), flags=re.I).match
++
++            done = False
++            for group in self.environments.values():
++                if match(group.evgid):
++                    done = True
++                    returns[group.evgid] = group
++                    break
++
++        return returns.values()
 diff --git a/yum/metalink.py b/yum/metalink.py
 index aaa4f25..51895fd 100755
 --- a/yum/metalink.py
@@ -157297,8 +158672,27 @@ index bfc49b7..9ddcae6 100644
      a = parsever(a)
      b = parsever(b)
  
+diff --git a/yum/repoMDObject.py b/yum/repoMDObject.py
+index 31b1080..eb84123 100755
+--- a/yum/repoMDObject.py
++++ b/yum/repoMDObject.py
+@@ -226,12 +226,11 @@ class RepoMD:
+                 tag = """   <repo>%s</repo>\n""" % (to_xml(item))
+                 tags += tag
+             for (cpeid, item) in self.tags['distro']:
+-                itemlist = list(item) # frellingsets.
+                 if cpeid:
+                     tag = """   <distro cpeid="%s">%s</distro>\n""" % (
+-                                to_xml(cpeid, attrib=True), to_xml(itemlist[0]))
++                                to_xml(cpeid, attrib=True), to_xml(item))
+                 else:
+-                    tag = """   <distro>%s</distro>\n""" % (to_xml(itemlist[0]))
++                    tag = """   <distro>%s</distro>\n""" % (to_xml(item))
+                 tags += tag
+             tags += """ </tags>\n"""
+             msg += tags
 diff --git a/yum/repos.py b/yum/repos.py
-index 3793bad..3cbbe25 100644
+index 3793bad..6d4c20e 100644
 --- a/yum/repos.py
 +++ b/yum/repos.py
 @@ -22,6 +22,7 @@ import misc
@@ -157309,7 +158703,7 @@ index 3793bad..3cbbe25 100644
  
  from weakref import proxy as weakref
  
-@@ -67,6 +68,40 @@ class RepoStorage:
+@@ -67,6 +68,47 @@ class RepoStorage:
          self._cache_enabled_repos = []
          self.quick_enable_disable = {}
  
@@ -157325,7 +158719,14 @@ index 3793bad..3cbbe25 100644
 +        for repo in self.listEnabled():
 +            if repo.cache:
 +                continue
-+            if repo._async and repo._commonLoadRepoXML(repo):
++            try:
++                dl = repo._async and repo._commonLoadRepoXML(repo)
++            except Errors.RepoError, e:
++                if not repo.skip_if_unavailable:
++                    raise
++                self.disableRepo(repo.id)
++                dl = False
++            if dl:
 +                mdtypes = repo._mdpolicy2mdtypes()
 +                downloading = repo._commonRetrieveDataMD_list(mdtypes)
 +                repos.append((repo, downloading, [False]))
@@ -157350,7 +158751,7 @@ index 3793bad..3cbbe25 100644
      def doSetup(self, thisrepo = None):
          
          self.ayum.plugins.run('prereposetup')
-@@ -89,6 +124,7 @@ class RepoStorage:
+@@ -89,6 +131,7 @@ class RepoStorage:
                  self.disableRepo(repo.id)
                  
          self._setup = True
@@ -157358,7 +158759,7 @@ index 3793bad..3cbbe25 100644
          self.ayum.plugins.run('postreposetup')
          
      def __str__(self):
-@@ -223,8 +259,9 @@ class RepoStorage:
+@@ -223,15 +266,16 @@ class RepoStorage:
          
          self._cachedir = cachedir
          for repo in self.repos.values():
@@ -157369,8 +158770,17 @@ index 3793bad..3cbbe25 100644
 +                repo.basecachedir = cachedir
  
  
-     def setProgressBar(self, obj):
-@@ -288,6 +325,16 @@ class RepoStorage:
+-    def setProgressBar(self, obj):
++    def setProgressBar(self, obj, multi_obj=None):
+         """sets the progress bar for downloading files from repos"""
+         
+         for repo in self.repos.values():
+-            repo.setCallback(obj)
++            repo.setCallback(obj, multi_obj)
+ 
+     def setFailureCallback(self, obj):
+         """sets the failure callback for all repos"""
+@@ -288,6 +332,16 @@ class RepoStorage:
          else:
              data = [ mdtype ]
           
@@ -157388,7 +158798,7 @@ index 3793bad..3cbbe25 100644
              sack = repo.getPackageSack()
              try:
 diff --git a/yum/rpmsack.py b/yum/rpmsack.py
-index e289a7a..ed8e3d1 100644
+index e289a7a..a4da4ab 100644
 --- a/yum/rpmsack.py
 +++ b/yum/rpmsack.py
 @@ -48,6 +48,17 @@ def _open_no_umask(*args):
@@ -157428,6 +158838,15 @@ index e289a7a..ed8e3d1 100644
                  continue
  
              pkg = pkg[0]
+@@ -616,7 +628,7 @@ class RPMDBPackageSack(PackageSackBase):
+             for hdr, idx in self._get_packages():
+                 if self._match_repattern(rpats, hdr, ignore_case):
+                     self._makePackageObject(hdr, idx)
+-            self._completely_loaded = patterns is None
++            self._completely_loaded = rpats is None
+ 
+         pkgobjlist = self._idx2pkg.values()
+         # Remove gpg-pubkeys, as no sane callers expects/likes them...
 @@ -993,7 +1005,7 @@ class RPMDBPackageSack(PackageSackBase):
              return
  
@@ -157507,7 +158926,7 @@ index e289a7a..ed8e3d1 100644
              raise AttributeError, "%s has no attribute %s" % (self, attr)
  
 diff --git a/yum/rpmtrans.py b/yum/rpmtrans.py
-index 9b265f9..24a1f9e 100644
+index 9b265f9..61c9a0e 100644
 --- a/yum/rpmtrans.py
 +++ b/yum/rpmtrans.py
 @@ -119,7 +119,11 @@ class RPMBaseCallback:
@@ -157549,10 +158968,29 @@ index 9b265f9..24a1f9e 100644
  
          newFunc.__name__ = func.__name__
          newFunc.__doc__ = func.__doc__
-@@ -621,3 +633,9 @@ class RPMTransaction:
+@@ -463,6 +475,11 @@ class RPMTransaction:
+         # SCRIPT_ERROR is only in rpm >= 4.6.0
+         elif hasattr(rpm, "RPMCALLBACK_SCRIPT_ERROR") and what == rpm.RPMCALLBACK_SCRIPT_ERROR:
+             self._scriptError(bytes, total, h)
++        # SCRIPT_START and SCRIPT_STOP are only in rpm >= 4.10
++        elif hasattr(rpm, "RPMCALLBACK_SCRIPT_START") and what == rpm.RPMCALLBACK_SCRIPT_START:
++            self._scriptStart(bytes, total, h);
++        elif hasattr(rpm, "RPMCALLBACK_SCRIPT_STOP") and what == rpm.RPMCALLBACK_SCRIPT_STOP:
++            self._scriptStop(bytes, total, h);
+     
+     
+     def _transStart(self, bytes, total, h):
+@@ -621,3 +638,16 @@ class RPMTransaction:
          self.display.errorlog(msg)
          # FIXME - what else should we do here? raise a failure and abort?
      
++    def _scriptStart(self, bytes, total, h):
++        pass
++
++    def _scriptStop(self, bytes, total, h):
++        name, txmbr = self._getTxmbr(h)
++        self._scriptout(txmbr or name)
++
 +    def verify_txmbr(self, txmbr, count):
 +        " Callback for post transaction when we are in verifyTransaction(). "
 +        if not hasattr(self.display, 'verify_txmbr'):
@@ -157594,7 +159032,7 @@ index 8a6f6f3..ba929de 100644
          if len(self.filelistsdb) == 0:
              # grab repo object from primarydb and force filelists population in this sack using repo
 diff --git a/yum/transactioninfo.py b/yum/transactioninfo.py
-index 4d89d83..b584338 100644
+index 4d89d83..e7377bf 100644
 --- a/yum/transactioninfo.py
 +++ b/yum/transactioninfo.py
 @@ -87,7 +87,8 @@ class TransactionData:
@@ -157607,7 +159045,16 @@ index 4d89d83..b584338 100644
          self.pkgSackPackages = 0
          self.localSack = PackageSack()
          self._inSack = GetProvReqOnlyPackageSack()
-@@ -105,6 +106,9 @@ class TransactionData:
+@@ -95,6 +96,8 @@ class TransactionData:
+         # lists of txmbrs in their states - just placeholders
+         self.instgroups = []
+         self.removedgroups = []
++        self.instenvironments = []
++        self.removedenvironments = []
+         self.removed = []
+         self.installed = []
+         self.updated = []
+@@ -105,6 +108,9 @@ class TransactionData:
          self.reinstalled = []
          self.downgraded = []
          self.failed = []
@@ -157617,7 +159064,7 @@ index 4d89d83..b584338 100644
          
      def __len__(self):
          return len(self.pkgdict)
-@@ -115,6 +119,18 @@ class TransactionData:
+@@ -115,6 +121,18 @@ class TransactionData:
          else:
              return iter(self.getMembers())
  
@@ -157636,7 +159083,7 @@ index 4d89d83..b584338 100644
      def debugprint(self, msg):
          if self.debug:
              print msg
-@@ -208,7 +224,9 @@ class TransactionData:
+@@ -208,7 +226,9 @@ class TransactionData:
                  txmbrs = self.matchNaevr(na[0], na[1])
  
          if not txmbrs:
@@ -157647,7 +159094,46 @@ index 4d89d83..b584338 100644
                  pkgs = []
              else:
                  pkgs = self.pkgSack.returnPackages(patterns=[pattern])
-@@ -547,9 +565,10 @@ class TransactionData:
+@@ -334,6 +354,8 @@ class TransactionData:
+            
+         self.instgroups = []
+         self.removedgroups = []
++        self.instenvironments = []
++        self.removedenvironments = []
+         self.removed = []
+         self.installed = []
+         self.updated = []
+@@ -365,6 +387,10 @@ class TransactionData:
+                     for g in txmbr.groups:
+                         if g not in self.instgroups:
+                             self.instgroups.append(g)
++                if txmbr.environments:
++                    for evg in txmbr.environments:
++                        if evg not in self.instenvironments:
++                            self.instenvironments.append(evg)
+                 if txmbr.isDep:
+                     self.depinstalled.append(txmbr)
+                 else:
+@@ -377,6 +403,9 @@ class TransactionData:
+                 for g in txmbr.groups:
+                     if g not in self.instgroups:
+                         self.removedgroups.append(g)
++                for evg in txmbr.environments:
++                    if evg not in self.instenvironments:
++                        self.removedenvironments.append(evg)
+                 if txmbr.isDep:
+                     self.depremoved.append(txmbr)
+                 else:
+@@ -402,6 +431,8 @@ class TransactionData:
+         self.depremoved.sort()
+         self.instgroups.sort()
+         self.removedgroups.sort()
++        self.instenvironments.sort()
++        self.removedenvironments.sort()
+         self.reinstalled.sort()
+         self.downgraded.sort()
+         self.failed.sort()
+@@ -547,9 +578,10 @@ class TransactionData:
          return txmbr
  
  
@@ -157660,7 +159146,7 @@ index 4d89d83..b584338 100644
  
      def getNewProvides(self, name, flag=None, version=(None, None, None)):
          """return dict { packages -> list of matching provides }
-@@ -619,6 +638,12 @@ class TransactionData:
+@@ -619,6 +651,12 @@ class TransactionData:
          """ Return a simple version for the future rpmdb. Works like
              rpmdb.simpleVersion(main_only=True)[0], but for the state the rpmdb
              will be in after the transaction. """
@@ -157673,7 +159159,7 @@ index 4d89d83..b584338 100644
          pkgs = self.rpmdb.returnPackages()
          _reinstalled_pkgtups = {}
          for txmbr in self.getMembersWithState(None, TS_INSTALL_STATES):
-@@ -658,6 +683,7 @@ class TransactionData:
+@@ -658,6 +696,7 @@ class TransactionData:
  
          self.rpmdb.transactionCachePackageChecksums(pkg_checksum_tups)
  
@@ -157681,6 +159167,22 @@ index 4d89d83..b584338 100644
          return main
      
      def findObsoletedByThisMember(self, txmbr):
+@@ -752,6 +791,7 @@ class TransactionMember:
+         self.downgraded_by = []
+         self.reinstall = False
+         self.groups = [] # groups it's in
++        self.environments = [] # Env. groups it's in
+         self._poattr = ['pkgtup', 'repoid', 'name', 'arch', 'epoch', 'version',
+                         'release']
+ 
+@@ -825,5 +865,7 @@ class TransactionMember:
+                 
+         if self.groups:
+             msg += "  groups: %s\n" % ' '.join(self.groups)
++        if self.environments:
++            msg += "  environments: %s\n" % ' '.join(self.environments)
+ 
+         return msg
 diff --git a/yum/update_md.py b/yum/update_md.py
 index 2cb1acb..7da6a08 100644
 --- a/yum/update_md.py
@@ -157701,7 +159203,7 @@ index 2cb1acb..7da6a08 100644
          self._md[item] = val
  
 diff --git a/yum/yumRepo.py b/yum/yumRepo.py
-index e5e9ece..128795b 100644
+index e5e9ece..ec68b6a 100644
 --- a/yum/yumRepo.py
 +++ b/yum/yumRepo.py
 @@ -20,10 +20,12 @@ import time
@@ -157751,33 +159253,44 @@ index e5e9ece..128795b 100644
      def populate(self, repo, mdtype='metadata', callback=None, cacheonly=0):
          if mdtype == 'all':
              data = ['metadata', 'filelists', 'otherdata']
-@@ -162,6 +184,25 @@ class YumPackageSack(packageSack.PackageSack):
+@@ -138,8 +160,6 @@ class YumPackageSack(packageSack.PackageSack):
+                 if item in self.added[repo]:
+                     continue
+ 
+-            db_fn = None
+-
+             if item == 'metadata':
+                 mydbtype = 'primary_db'
+                 mymdtype = 'primary'
+@@ -162,25 +182,37 @@ class YumPackageSack(packageSack.PackageSack):
                  continue
  
              if self._check_db_version(repo, mydbtype):
+-                # see if we have the uncompressed db and check it's checksum vs the openchecksum
+-                # if not download the compressed file
+-                # decompress it
+-                # unlink it
 +                #  Use gen decompression on DB files. Keeps exactly what we
 +                # downloaded in the download dir.
-+
+ 
 +                # Backwards compat. ... try the old uncompressed version first.
-+                db_un_fn = self._check_uncompressed_db(repo, mydbtype)
-+                if not db_un_fn:
+                 db_un_fn = self._check_uncompressed_db(repo, mydbtype)
+                 if not db_un_fn:
 +                    db_un_fn = self._check_uncompressed_db_gen(repo, mydbtype)
 +
 +                if not db_un_fn:
-+                    db_fn = repo._retrieveMD(mydbtype)
-+                    if db_fn:
+                     db_fn = repo._retrieveMD(mydbtype)
+                     if db_fn:
+-                        if not repo.cache:
+-                            db_un_fn = misc.decompress(db_fn)
+-                            misc.unlink_f(db_fn)
+-                            db_un_fn = self._check_uncompressed_db(repo, mydbtype)
 +                        db_un_fn = self._check_uncompressed_db_gen(repo,
 +                                                                   mydbtype)
 +                    if not db_un_fn: # Shouldn't happen?
 +                        raise URLGrabError(-1, 'Check uncompressed DB failed')
-+
-+                dobj = repo.cacheHandler.open_database(db_un_fn)
-+
-+            elif self._check_db_version(repo, mydbtype):
-                 # see if we have the uncompressed db and check it's checksum vs the openchecksum
-                 # if not download the compressed file
-                 # decompress it
-@@ -180,7 +221,17 @@ class YumPackageSack(packageSack.PackageSack):
+ 
+                 dobj = repo.cacheHandler.open_database(db_un_fn)
  
              else:
                  repo._xml2sqlite_local = True
@@ -157795,7 +159308,7 @@ index e5e9ece..128795b 100644
                  xmldata = repo.repoXML.getData(mymdtype)
                  (ctype, csum) = xmldata.checksum
                  dobj = repo_cache_function(xml, csum)
-@@ -193,6 +244,25 @@ class YumPackageSack(packageSack.PackageSack):
+@@ -193,6 +225,25 @@ class YumPackageSack(packageSack.PackageSack):
          # get rid of all this stuff we don't need now
          del repo.cacheHandler
  
@@ -157821,7 +159334,7 @@ index e5e9ece..128795b 100644
      def _check_uncompressed_db(self, repo, mdtype):
          """return file name of uncompressed db is good, None if not"""
          mydbdata = repo.repoXML.getData(mdtype)
-@@ -201,9 +271,11 @@ class YumPackageSack(packageSack.PackageSack):
+@@ -201,9 +252,11 @@ class YumPackageSack(packageSack.PackageSack):
          compressed_fn = repo.cachedir + '/' + fname
          db_un_fn = misc.decompress(compressed_fn, fn_only=True)
  
@@ -157834,7 +159347,20 @@ index e5e9ece..128795b 100644
          if os.path.exists(db_un_fn):
              if skip_old_DBMD_check and repo._using_old_MD:
                  return db_un_fn
-@@ -285,6 +357,7 @@ class YumRepository(Repository, config.RepoConf):
+@@ -260,10 +313,11 @@ class YumRepository(Repository, config.RepoConf):
+         self.copy_local = 0
+         # holder for stuff we've grabbed
+         self.retrieved = { 'primary':0, 'filelists':0, 'other':0, 'group':0,
+-                           'updateinfo':0, 'prestodelta' : 0}
++                           'updateinfo':0}
+ 
+         # callbacks
+         self.callback = None  # for the grabber
++        self.multi_callback = None
+         self.failure_obj = None
+         self.mirror_failure_obj = None
+         self.interrupt_callback = None
+@@ -285,6 +339,7 @@ class YumRepository(Repository, config.RepoConf):
  
          self._grabfunc = None
          self._grab = None
@@ -157842,7 +159368,16 @@ index e5e9ece..128795b 100644
  
      def __cmp__(self, other):
          """ Sort yum repos. by cost, and then by alphanumeric on their id. """
-@@ -431,25 +504,18 @@ class YumRepository(Repository, config.RepoConf):
+@@ -378,7 +433,7 @@ class YumRepository(Repository, config.RepoConf):
+                         'basecachedir', 'http_headers', 'metadata_cookie',
+                         'metadata_cookie_fn', 'quick_enable_disable',
+                         'repoMDFile', 'timestamp_check', 'urls', 'mirrorurls',
+-                        'yumvar', 'repofile')
++                        'yumvar', 'repofile', 'multi_callback')
+         for attr in dir(self):
+             if attr.startswith('_'):
+                 continue
+@@ -431,25 +486,18 @@ class YumRepository(Repository, config.RepoConf):
          self._proxy_dict = {} # zap it
          proxy_string = None
          empty = (None, '_none_', '')
@@ -157876,7 +159411,13 @@ index e5e9ece..128795b 100644
  
          if proxy_string is not None:
              self._proxy_dict['http'] = proxy_string
-@@ -488,8 +554,20 @@ class YumRepository(Repository, config.RepoConf):
+@@ -483,13 +531,26 @@ class YumRepository(Repository, config.RepoConf):
+ 
+         ugopts = self._default_grabopts()
+         self._grabfunc = URLGrabber(progress_obj=self.callback,
++                                    multi_progress_obj=self.multi_callback,
+                                     failure_callback=self.failure_obj,
+                                     interrupt_callback=self.interrupt_callback,
                                      copy_local=self.copy_local,
                                      reget='simple',
                                      **ugopts)
@@ -157898,7 +159439,7 @@ index e5e9ece..128795b 100644
                               failure_callback=self.mirror_failure_obj)
  
      def _default_grabopts(self, cache=True):
-@@ -499,6 +577,7 @@ class YumRepository(Repository, config.RepoConf):
+@@ -499,6 +560,7 @@ class YumRepository(Repository, config.RepoConf):
                   'throttle': self.throttle,
                   'proxies': self.proxy_dict,
                   'timeout': self.timeout,
@@ -157906,7 +159447,7 @@ index e5e9ece..128795b 100644
                   'http_headers': tuple(self.__headersListFromDict(cache=cache)),
                   'ssl_verify_peer': self.sslverify,
                   'ssl_verify_host': self.sslverify,
-@@ -714,7 +793,7 @@ class YumRepository(Repository, config.RepoConf):
+@@ -714,7 +776,7 @@ class YumRepository(Repository, config.RepoConf):
              local = self.metalink_filename + '.tmp'
              if not self._metalinkCurrent():
                  url = misc.to_utf8(self.metalink)
@@ -157915,7 +159456,7 @@ index e5e9ece..128795b 100644
                  try:
                      ug = URLGrabber(progress_obj = self.callback, **ugopts)
                      result = ug.urlgrab(url, local, text=self.id + "/metalink")
-@@ -751,7 +830,7 @@ class YumRepository(Repository, config.RepoConf):
+@@ -751,7 +813,7 @@ class YumRepository(Repository, config.RepoConf):
  
      def _getFile(self, url=None, relative=None, local=None, start=None, end=None,
              copy_local=None, checkfunc=None, text=None, reget='simple', 
@@ -157924,7 +159465,7 @@ index e5e9ece..128795b 100644
          """retrieve file from the mirrorgroup for the repo
             relative to local, optionally get range from
             start to end, also optionally retrieve from a specific baseurl"""
-@@ -796,6 +875,16 @@ class YumRepository(Repository, config.RepoConf):
+@@ -796,6 +858,16 @@ class YumRepository(Repository, config.RepoConf):
              except Errors.MediaError, e:
                  verbose_logger.log(logginglevels.DEBUG_2, "Error getting package from media; falling back to url %s" %(e,))
  
@@ -157941,7 +159482,19 @@ index e5e9ece..128795b 100644
          if url and scheme != "media":
              ugopts = self._default_grabopts(cache=cache)
              ug = URLGrabber(progress_obj = self.callback,
-@@ -835,7 +924,8 @@ class YumRepository(Repository, config.RepoConf):
+@@ -819,10 +891,7 @@ class YumRepository(Repository, config.RepoConf):
+                 if self.mirrorurls:
+                     errstr +="\n  You could try running: yum clean expire-cache"
+                     errstr +="\n  To get a new set of mirrors."
+-                if e.errno == 256:
+-                    raise Errors.NoMoreMirrorsRepoError, errstr
+-                else:
+-                    raise Errors.RepoError, errstr
++                raise Errors.RepoError, errstr
+ 
+ 
+         else:
+@@ -835,19 +904,18 @@ class YumRepository(Repository, config.RepoConf):
                                             reget = reget,
                                             checkfunc=checkfunc,
                                             http_headers=headers,
@@ -157951,7 +159504,13 @@ index e5e9ece..128795b 100644
                                             )
              except URLGrabError, e:
                  errstr = "failure: %s from %s: %s" % (relative, self.id, e)
-@@ -847,7 +937,7 @@ class YumRepository(Repository, config.RepoConf):
+-                if e.errno == 256:
+-                    raise Errors.NoMoreMirrorsRepoError, errstr
+-                else:
+-                    raise Errors.RepoError, errstr
++                errors = getattr(e, 'errors', None)
++                raise Errors.NoMoreMirrorsRepoError(errstr, errors)
+ 
          return result
      __get = _getFile
  
@@ -157960,15 +159519,35 @@ index e5e9ece..128795b 100644
          remote = package.relativepath
          local = package.localPkg()
          basepath = package.basepath
-@@ -864,6 +954,7 @@ class YumRepository(Repository, config.RepoConf):
+@@ -857,15 +925,26 @@ class YumRepository(Repository, config.RepoConf):
+                 return local
+             misc.unlink_f(local)
+ 
+-        return self._getFile(url=basepath,
++        if checkfunc is None:
++            checkfunc = lambda obj: package.verifyLocalPkg()
++
++        ret = self._getFile(url=basepath,
+                         relative=remote,
+                         local=local,
+                         checkfunc=checkfunc,
                          text=text,
                          cache=cache,
                          size=package.size,
 +                        **kwargs
                          )
  
++        if not package.verifyLocalPkg(): # Don't return as "success" when bad.
++            msg = "Downloaded package %s, from %s, but it was invalid."
++            msg = msg % (package, package.repo.id)
++            raise Errors.RepoError, msg
++
++        return ret
++
      def getHeader(self, package, checkfunc = None, reget = 'simple',
-@@ -1020,7 +1111,7 @@ class YumRepository(Repository, config.RepoConf):
+             cache = True):
+ 
+@@ -1020,7 +1099,7 @@ class YumRepository(Repository, config.RepoConf):
              if grab_can_fail:
                  return None
              raise Errors.RepoError, 'Error downloading file %s: %s' % (local, e)
@@ -157977,7 +159556,7 @@ index e5e9ece..128795b 100644
              misc.unlink_f(tfname)
              if grab_can_fail:
                  return None
-@@ -1260,6 +1351,9 @@ class YumRepository(Repository, config.RepoConf):
+@@ -1260,6 +1339,9 @@ class YumRepository(Repository, config.RepoConf):
          return True
  
      def _check_db_version(self, mdtype, repoXML=None):
@@ -157987,7 +159566,7 @@ index e5e9ece..128795b 100644
          if repoXML is None:
              repoXML = self.repoXML
          if mdtype in repoXML.repoData:
-@@ -1277,11 +1371,11 @@ class YumRepository(Repository, config.RepoConf):
+@@ -1277,11 +1359,11 @@ class YumRepository(Repository, config.RepoConf):
              return None
  
          if not file_check:
@@ -158002,7 +159581,7 @@ index e5e9ece..128795b 100644
              if not os.path.exists(local):
                  local = misc.decompress(local, fn_only=True)
                  compressed = True
-@@ -1302,6 +1396,17 @@ class YumRepository(Repository, config.RepoConf):
+@@ -1302,6 +1384,17 @@ class YumRepository(Repository, config.RepoConf):
              into the delete list, this means metadata can change filename
              without us leaking it. """
  
@@ -158020,7 +159599,22 @@ index e5e9ece..128795b 100644
          def _mdtype_eq(omdtype, odata, nmdtype, ndata):
              """ Check if two returns from _get_mdtype_data() are equal. """
              if ndata is None:
-@@ -1333,8 +1438,7 @@ class YumRepository(Repository, config.RepoConf):
+@@ -1321,6 +1414,14 @@ class YumRepository(Repository, config.RepoConf):
+             return True
+ 
+         all_mdtypes = self.retrieved.keys()
++        # Add in any extra stuff we don't know about.
++        for mdtype in self.repoXML.fileTypes():
++            if mdtype in all_mdtypes:
++                continue
++            if mdtype in ('primary_db', 'filelists_db', 'other_db', 'group_gz'):
++                continue
++            all_mdtypes.append(mdtype)
++
+         if mdtypes is None:
+             mdtypes = all_mdtypes
+ 
+@@ -1333,8 +1434,7 @@ class YumRepository(Repository, config.RepoConf):
  
          # Inited twice atm. ... sue me
          self._oldRepoMDData['new_MD_files'] = []
@@ -158030,7 +159624,7 @@ index e5e9ece..128795b 100644
          for mdtype in all_mdtypes:
              (nmdtype, ndata) = self._get_mdtype_data(mdtype)
  
-@@ -1371,43 +1475,16 @@ class YumRepository(Repository, config.RepoConf):
+@@ -1371,43 +1471,16 @@ class YumRepository(Repository, config.RepoConf):
              # No old repomd data, but we might still have uncompressed MD
              if self._groupCheckDataMDValid(ndata, nmdtype, mdtype):
                  continue
@@ -158079,7 +159673,7 @@ index e5e9ece..128795b 100644
  
      def _groupLoadRepoXML(self, text=None, mdtypes=None):
          """ Retrieve the new repomd.xml from the repository, then check it
-@@ -1421,7 +1498,7 @@ class YumRepository(Repository, config.RepoConf):
+@@ -1421,7 +1494,7 @@ class YumRepository(Repository, config.RepoConf):
              self._commonRetrieveDataMD(mdtypes)
  
      def _mdpolicy2mdtypes(self):
@@ -158088,7 +159682,7 @@ index e5e9ece..128795b 100644
                       'group:primary' : ['primary'],
                       'group:small'   : ["primary", "updateinfo"],
                       'group:main'    : ["primary", "group", "filelists",
-@@ -1436,6 +1513,7 @@ class YumRepository(Repository, config.RepoConf):
+@@ -1436,6 +1509,7 @@ class YumRepository(Repository, config.RepoConf):
          if not mdtypes or 'group:all' in mdtypes:
              mdtypes = None
          else:
@@ -158096,7 +159690,7 @@ index e5e9ece..128795b 100644
              mdtypes = sorted(list(mdtypes))
          return mdtypes
  
-@@ -1451,12 +1529,7 @@ class YumRepository(Repository, config.RepoConf):
+@@ -1451,12 +1525,7 @@ class YumRepository(Repository, config.RepoConf):
      def _getRepoXML(self):
          if self._repoXML:
              return self._repoXML
@@ -158110,7 +159704,7 @@ index e5e9ece..128795b 100644
          return self._repoXML
  
  
-@@ -1514,7 +1587,7 @@ class YumRepository(Repository, config.RepoConf):
+@@ -1514,7 +1583,7 @@ class YumRepository(Repository, config.RepoConf):
          return self._checkMD(fn, mdtype, openchecksum)
  
      def _checkMD(self, fn, mdtype, openchecksum=False,
@@ -158119,7 +159713,7 @@ index e5e9ece..128795b 100644
          """ Internal function, use .checkMD() from outside yum. """
  
          thisdata = data # So the argument name is nicer
-@@ -1537,6 +1610,18 @@ class YumRepository(Repository, config.RepoConf):
+@@ -1537,6 +1606,18 @@ class YumRepository(Repository, config.RepoConf):
          if size is not None:
              size = int(size)
  
@@ -158138,7 +159732,7 @@ index e5e9ece..128795b 100644
          try: # get the local checksum
              l_csum = self._checksum(r_ctype, file, datasize=size)
          except Errors.RepoError, e:
-@@ -1551,15 +1636,13 @@ class YumRepository(Repository, config.RepoConf):
+@@ -1551,15 +1632,13 @@ class YumRepository(Repository, config.RepoConf):
                  return None
              raise URLGrabError(-1, 'Metadata file does not match checksum')
  
@@ -158155,7 +159749,7 @@ index e5e9ece..128795b 100644
          """ Internal function, use .retrieveMD() from outside yum. """
          #  Note that this can raise Errors.RepoMDError if mdtype doesn't exist
          # for this repo.
-@@ -1597,7 +1680,9 @@ class YumRepository(Repository, config.RepoConf):
+@@ -1597,7 +1676,9 @@ class YumRepository(Repository, config.RepoConf):
                  return local # it's the same return the local one
  
          try:
@@ -158166,7 +159760,7 @@ index e5e9ece..128795b 100644
              text = "%s/%s" % (self.id, mdtype)
              if thisdata.size is None:
                  reget = None
-@@ -1613,8 +1698,9 @@ class YumRepository(Repository, config.RepoConf):
+@@ -1613,8 +1694,9 @@ class YumRepository(Repository, config.RepoConf):
                                    checkfunc=checkfunc, 
                                    text=text,
                                    cache=self.http_caching == 'all',
@@ -158178,7 +159772,7 @@ index e5e9ece..128795b 100644
              if retrieve_can_fail:
                  return None
              raise
-@@ -1624,7 +1710,6 @@ class YumRepository(Repository, config.RepoConf):
+@@ -1624,7 +1706,6 @@ class YumRepository(Repository, config.RepoConf):
              raise Errors.RepoError, \
                  "Could not retrieve %s matching remote checksum from %s" % (local, self)
          else:
@@ -158186,8 +159780,42 @@ index e5e9ece..128795b 100644
              return local
  
  
+@@ -1646,13 +1727,21 @@ class YumRepository(Repository, config.RepoConf):
+ 
+     def getGroups(self):
+         """gets groups and returns group file path for the repository, if there
+-           is none it returns None"""
++           is none or retrieve/decompress fails, it returns None"""
+         if 'group_gz' in self.repoXML.fileTypes():
+-            return self._retrieveMD('group_gz', retrieve_can_fail=True)
++            fn = self._retrieveMD('group_gz', retrieve_can_fail=True)
++            if fn:
++                try:
++                    fn = misc.repo_gen_decompress(fn, 'comps.xml')
++                except IOError, e:
++                    logger.warning(e)
++                    fn = None
++            return fn
+         return self._retrieveMD('group', retrieve_can_fail=True)
+ 
+-    def setCallback(self, callback):
++    def setCallback(self, callback, multi_callback=None):
+         self.callback = callback
++        self.multi_callback = multi_callback
+         self._callbacks_changed = True
+ 
+     def setFailureObj(self, failure_obj):
+@@ -1681,7 +1770,7 @@ class YumRepository(Repository, config.RepoConf):
+                 print "Could not read mirrorlist %s, error was \n%s" %(url, e)
+                 content = []
+             for line in content:
+-                if re.match('\s*(#|$)', line):
++                if not re.match('\w+://\S+\s*$', line):
+                     continue
+                 mirror = line.rstrip() # no more trailing \n's
+                 mirror = mirror.replace('$ARCH', '$BASEARCH')
 diff --git a/yumcommands.py b/yumcommands.py
-index 4dcbea7..bc30bd4 100644
+index 4dcbea7..52c3127 100644
 --- a/yumcommands.py
 +++ b/yumcommands.py
 @@ -29,7 +29,7 @@ import operator
@@ -158705,7 +160333,7 @@ index 4dcbea7..bc30bd4 100644
                                                   '=' : clar, 'not in' : clai})
              rep = base.listPkgs(ypl.extras, _('Extra Packages'), basecmd,
                                  columns=columns)
-@@ -389,45 +635,121 @@ class InfoCommand(YumCommand):
+@@ -389,45 +635,144 @@ class InfoCommand(YumCommand):
              return 0, []
  
      def needTs(self, base, basecmd, extcmds):
@@ -158750,12 +160378,15 @@ index 4dcbea7..bc30bd4 100644
 +
          
      def getNames(self):
+-        return ['erase', 'remove']
 +        """Return a list containing the names of this command.  This
 +        command can be called from the command line by using any of these names.
 +
 +        :return: a list containing the names of this command
 +        """
-         return ['erase', 'remove']
++        return ['erase', 'remove', 'autoremove',
++                'erase-n', 'erase-na', 'erase-nevra',
++                'remove-n', 'remove-na', 'remove-nevra']
  
      def getUsage(self):
 +        """Return a usage string for this command.
@@ -158782,6 +160413,8 @@ index 4dcbea7..bc30bd4 100644
 +        :param extcmds: the command line arguments passed to *basecmd*
 +        """
          checkRootUID(base)
++        if basecmd == 'autoremove':
++            return
          checkPackageArg(base, basecmd, extcmds)
  
      def doCommand(self, base, basecmd, extcmds):
@@ -158798,12 +160431,32 @@ index 4dcbea7..bc30bd4 100644
 +            1 = we've errored, exit with error string
 +            2 = we've got work yet to do, onto the next stage
 +        """
++
++        pos = False
++        if basecmd == 'autoremove':
++            #  We have to alter this, as it's used in resolving stage. Which
++            # sucks. Just be careful in "yum shell".
++            base.conf.clean_requirements_on_remove = True
++
++            if not extcmds:
++                pos = True
++                extcmds = []
++                for pkg in sorted(base.rpmdb.returnLeafNodes()):
++                    if 'reason' not in pkg.yumdb_info:
++                        continue
++                    if pkg.yumdb_info.reason != 'dep':
++                        continue
++                    extcmds.append(pkg)
++
          self.doneCommand(base, _("Setting up Remove Process"))
          try:
-             return base.erasePkgs(extcmds)
+-            return base.erasePkgs(extcmds)
++            ret = base.erasePkgs(extcmds, pos=pos, basecmd=basecmd)
          except yum.Errors.YumBaseError, e:
 -            return 1, [str(e)]
-+            return 1, [exception2msg(e)]
++            ret = (1, [exception2msg(e)])
++
++        return ret
  
      def needTs(self, base, basecmd, extcmds):
 +        """Return whether a transaction set must be set up before this
@@ -158828,7 +160481,7 @@ index 4dcbea7..bc30bd4 100644
          return True
  
   
-@@ -442,12 +764,25 @@ class GroupsCommand(YumCommand):
+@@ -442,12 +787,25 @@ class GroupsCommand(YumCommand):
                         'groupinfo'    : 'info'}
  
      def getNames(self):
@@ -158854,7 +160507,7 @@ index 4dcbea7..bc30bd4 100644
          return _("Display, or use, the groups information")
      
      def _grp_setup_doCommand(self, base):
-@@ -459,7 +794,7 @@ class GroupsCommand(YumCommand):
+@@ -459,7 +817,7 @@ class GroupsCommand(YumCommand):
          except yum.Errors.GroupsError:
              return 1, [_('No Groups on which to run command')]
          except yum.Errors.YumBaseError, e:
@@ -158863,7 +160516,7 @@ index 4dcbea7..bc30bd4 100644
  
      def _grp_cmd(self, basecmd, extcmds):
          if basecmd in self.direct_commands:
-@@ -470,6 +805,10 @@ class GroupsCommand(YumCommand):
+@@ -470,6 +828,10 @@ class GroupsCommand(YumCommand):
          else:
              cmd = 'summary'
  
@@ -158874,7 +160527,7 @@ index 4dcbea7..bc30bd4 100644
          remap = {'update' : 'upgrade',
                   'erase' : 'remove',
                   'mark-erase' : 'mark-remove',
-@@ -479,32 +818,70 @@ class GroupsCommand(YumCommand):
+@@ -479,32 +841,76 @@ class GroupsCommand(YumCommand):
          return cmd, extcmds
  
      def doCheck(self, base, basecmd, extcmds):
@@ -158899,12 +160552,18 @@ index 4dcbea7..bc30bd4 100644
 +            ocmds_arg = ('mark-install', 'mark-remove',
 +                         'mark-packages', 'mark-packages-force',
 +                         'unmark-packages',
-+                         'mark-packages-sync', 'mark-packages-sync-force')
++                         'mark-packages-sync', 'mark-packages-sync-force',
++                         'mark-groups', 'mark-groups-force',
++                         'unmark-groups',
++                         'mark-groups-sync', 'mark-groups-sync-force')
 +
 +            ocmds_all = ('mark-install', 'mark-remove', 'mark-convert',
 +                         'mark-packages', 'mark-packages-force',
 +                         'unmark-packages',
-+                         'mark-packages-sync', 'mark-packages-sync-force')
++                         'mark-packages-sync', 'mark-packages-sync-force',
++                         'mark-groups', 'mark-groups-force',
++                         'unmark-groups',
++                         'mark-groups-sync', 'mark-groups-sync-force')
 +
 +        if cmd in ('install', 'remove', 'info') or cmd in ocmds_arg:
              checkGroupArg(base, cmd, extcmds)
@@ -158955,23 +160614,28 @@ index 4dcbea7..bc30bd4 100644
          cmd, extcmds = self._grp_cmd(basecmd, extcmds)
  
          self._grp_setup_doCommand(base)
-@@ -524,39 +901,195 @@ class GroupsCommand(YumCommand):
+@@ -524,39 +930,258 @@ class GroupsCommand(YumCommand):
              if cmd == 'remove':
                  return base.removeGroups(extcmds)
  
 +            if cmd == 'mark-install':
-+                for strng in extcmds:
-+                    for group in base.comps.return_groups(strng):
-+                        base.igroups.add_group(group.groupid, group.packages)
++                gRG = base._groupReturnGroups(extcmds,ignore_case=False)
++                igrps, grps, ievgrps, evgrps = gRG
++                for evgrp in evgrps:
++                    base.igroups.add_environment(evgrp.environmentid,
++                                                 evgrp.allgroups)
++                for grp in grps:
++                    base.igroups.add_group(grp.groupid, grp.packages)
 +                base.igroups.save()
 +                return 0, ['Marked install: ' + ','.join(extcmds)]
 +
 +            if cmd in ('mark-packages', 'mark-packages-force'):
 +                if len(extcmds) < 2:
 +                    return 1, ['No group or package given']
-+                igrps, grps = base._groupReturnGroups([extcmds[0]],
-+                                                      ignore_case=False)
-+                if igrps is not None and len(igrps) != 1:
++                gRG = base._groupReturnGroups([extcmds[0]],
++                                              ignore_case=False)
++                igrps, grps, ievgrps, evgrps = gRG
++                if igrps is None or len(igrps) != 1:
 +                    return 1, ['No group matched']
 +                grp = igrps[0]
 +                force = cmd == 'mark-packages-force'
@@ -158991,7 +160655,8 @@ index 4dcbea7..bc30bd4 100644
 +                return 0, ['UnMarked packages: ' + ','.join(extcmds)]
 +
 +            if cmd in ('mark-packages-sync', 'mark-packages-sync-force'):
-+                igrps, grps = base._groupReturnGroups(extcmds,ignore_case=False)
++                gRG = base._groupReturnGroups(extcmds,ignore_case=False)
++                igrps, grps, ievgrps, evgrps = gRG
 +                if not igrps:
 +                    return 1, ['No group matched']
 +                force = cmd == 'mark-packages-sync-force'
@@ -159005,6 +160670,60 @@ index 4dcbea7..bc30bd4 100644
 +                else:
 +                    return 0, ['Marked packages-sync: ' + ','.join(extcmds)]
 +
++            if cmd in ('mark-groups', 'mark-groups-force'):
++                if len(extcmds) < 2:
++                    return 1, ['No environment or group given']
++                gRG = base._groupReturnGroups([extcmds[0]],
++                                              ignore_case=False)
++                igrps, grps, ievgrps, evgrps = gRG
++                if ievgrps is None or len(ievgrps) != 1:
++                    return 1, ['No environment matched']
++                evgrp = ievgrps[0]
++                force = cmd == 'mark-groups-force'
++                gRG = base._groupReturnGroups(extcmds[1:], ignore_case=False)
++                for grp in gRG[1]:
++                    # Packages full or empty?
++                    self.igroups.add_group(grp.groupid,
++                                           grp.packages, ievgrp)
++                if force:
++                    for grp in gRG[0]:
++                        grp.environment = evgrp.evgid
++                        base.igroups.changed = True
++                base.igroups.save()
++                return 0, ['Marked groups: ' + ','.join(extcmds[1:])]
++
++            if cmd == 'unmark-groups':
++                gRG = base._groupReturnGroups([extcmds[0]],
++                                              ignore_case=False)
++                igrps, grps, ievgrps, evgrps = gRG
++                if igrps is None:
++                    return 1, ['No groups matched']
++                for grp in igrps:
++                    grp.environment = None
++                    base.igroups.changed = True
++                base.igroups.save()
++                return 0, ['UnMarked groups: ' + ','.join(extcmds)]
++
++            if cmd in ('mark-groups-sync', 'mark-groups-sync-force'):
++                gRG = base._groupReturnGroups(extcmds,ignore_case=False)
++                igrps, grps, ievgrps, evgrps = gRG
++                if not ievgrps:
++                    return 1, ['No environment matched']
++                force = cmd == 'mark-groups-sync-force'
++                for evgrp in ievgrps:
++                    grp_names = ",".join(sorted(evgrp.grp_names))
++                    for grp in base.igroups.return_groups(grp_names):
++                        if not force and grp.environment is not None:
++                            continue
++                        grp.environment = evgrp.evgid
++                        base.igroups.changed = True
++                base.igroups.save()
++                if force:
++                    return 0, ['Marked groups-sync-force: '+','.join(extcmds)]
++                else:
++                    return 0, ['Marked groups-sync: ' + ','.join(extcmds)]
++
++            # FIXME: This doesn't do environment groups atm.
 +            if cmd == 'mark-convert':
 +                # Convert old style info. into groups as objects.
 +
@@ -159054,9 +160773,12 @@ index 4dcbea7..bc30bd4 100644
 +                return 0, ['Converted old style groups to objects.']
 +
 +            if cmd == 'mark-remove':
-+                for strng in extcmds:
-+                    for group in base.comps.return_groups(strng):
-+                        base.igroups.del_group(group.groupid)
++                gRG = base._groupReturnGroups(extcmds,ignore_case=False)
++                igrps, grps, ievgrps, evgrps = gRG
++                for evgrp in ievgrps:
++                    base.igroups.del_environment(evgrp.evgid)
++                for grp in igrps:
++                    base.igroups.del_group(grp.gid)
 +                base.igroups.save()
 +                return 0, ['Marked remove: ' + ','.join(extcmds)]
 +
@@ -159153,7 +160875,7 @@ index 4dcbea7..bc30bd4 100644
          base.logger.debug(_("Making cache files for all metadata files."))
          base.logger.debug(_("This may take a while depending on the speed of this computer"))
          try:
-@@ -565,81 +1098,204 @@ class MakeCacheCommand(YumCommand):
+@@ -565,81 +1190,216 @@ class MakeCacheCommand(YumCommand):
                  repo.mdpolicy = "group:all"
              base.doRepoSetup(dosack=0)
              base.repos.doSetup()
@@ -159169,6 +160891,18 @@ index 4dcbea7..bc30bd4 100644
 -            base.repos.populateSack(mdtype='otherdata', cacheonly=1)
 +            base.repos.populateSack(mdtype='all', cacheonly=1)
  
++            # Now decompress stuff, so that -C works, sigh.
++            fname_map = {'group_gz'   : 'groups.xml',
++                         'pkgtags'    : 'pkgtags.sqlite',
++                         'updateinfo' : 'updateinfo.xml',
++                         }
++            for repo in base.repos.listEnabled():
++                for MD in repo.repoXML.fileTypes():
++                    if MD not in fname_map:
++                        continue
++                    misc.repo_gen_decompress(repo.retrieveMD(MD),
++                                             fname_map[MD],
++                                             cached=repo.cache)
  
          except yum.Errors.YumBaseError, e:
 -            return 1, [str(e)]
@@ -159365,7 +161099,7 @@ index 4dcbea7..bc30bd4 100644
          obscmds = ['obsoletes'] + extcmds
          base.extcmds.insert(0, 'updates')
          result = 0
-@@ -676,161 +1332,437 @@ class CheckUpdateCommand(YumCommand):
+@@ -676,161 +1436,437 @@ class CheckUpdateCommand(YumCommand):
                                                columns=columns)
                  result = 100
          except yum.Errors.YumBaseError, e:
@@ -159811,7 +161545,7 @@ index 4dcbea7..bc30bd4 100644
          def _repo_size(repo):
              ret = 0
              for pkg in repo.sack.returnPackages():
-@@ -866,6 +1798,13 @@ class RepoListCommand(YumCommand):
+@@ -866,6 +1902,13 @@ class RepoListCommand(YumCommand):
              except yum.Errors.RepoError:
                  if verbose:
                      raise
@@ -159825,7 +161559,7 @@ index 4dcbea7..bc30bd4 100644
  
          repos = base.repos.repos.values()
          repos.sort()
-@@ -924,111 +1863,108 @@ class RepoListCommand(YumCommand):
+@@ -924,111 +1967,108 @@ class RepoListCommand(YumCommand):
                  ui_enabled = dhibeg + _('disabled') + hiend
                  ui_endis_wid = utf8_width(_('disabled'))
  
@@ -160030,7 +161764,7 @@ index 4dcbea7..bc30bd4 100644
  
          if not verbose and cols:
              #  Work out the first (id) and last (enabled/disalbed/count),
-@@ -1088,21 +2024,54 @@ class RepoListCommand(YumCommand):
+@@ -1088,21 +2128,54 @@ class RepoListCommand(YumCommand):
          return 0, ['repolist: ' +to_unicode(locale.format("%d", tot_num, True))]
  
      def needTs(self, base, basecmd, extcmds):
@@ -160085,7 +161819,7 @@ index 4dcbea7..bc30bd4 100644
          if len(extcmds) == 0:
              base.usage()
              raise cli.CliError
-@@ -1147,28 +2116,85 @@ class HelpCommand(YumCommand):
+@@ -1147,28 +2220,85 @@ class HelpCommand(YumCommand):
          return help_output
  
      def doCommand(self, base, basecmd, extcmds):
@@ -160171,7 +161905,7 @@ index 4dcbea7..bc30bd4 100644
          self.doneCommand(base, _("Setting up Reinstall Process"))
          try:
              return base.reinstallPkgs(extcmds)
-@@ -1177,49 +2203,139 @@ class ReInstallCommand(YumCommand):
+@@ -1177,49 +2307,139 @@ class ReInstallCommand(YumCommand):
              return 1, [to_unicode(e)]
  
      def getSummary(self):
@@ -160312,7 +162046,7 @@ index 4dcbea7..bc30bd4 100644
          vcmd = 'installed'
          if extcmds:
              vcmd = extcmds[0]
-@@ -1308,7 +2424,7 @@ class VersionCommand(YumCommand):
+@@ -1308,7 +2528,7 @@ class VersionCommand(YumCommand):
                                       str(data[2][grp])))
                          _append_repos(cols, data[3][grp])
              except yum.Errors.YumBaseError, e:
@@ -160321,7 +162055,7 @@ index 4dcbea7..bc30bd4 100644
          if vcmd in ('available', 'all', 'group-available', 'group-all'):
              try:
                  data = base.pkgSack.simpleVersion(not verbose, groups=groups)
-@@ -1327,7 +2443,7 @@ class VersionCommand(YumCommand):
+@@ -1327,7 +2547,7 @@ class VersionCommand(YumCommand):
                          if verbose:
                              _append_repos(cols, data[3][grp])
              except yum.Errors.YumBaseError, e:
@@ -160330,7 +162064,7 @@ index 4dcbea7..bc30bd4 100644
  
          data = {'rid' : {}, 'ver' : {}}
          for (rid, ver) in cols:
-@@ -1344,6 +2460,14 @@ class VersionCommand(YumCommand):
+@@ -1344,6 +2564,14 @@ class VersionCommand(YumCommand):
          return 0, ['version']
  
      def needTs(self, base, basecmd, extcmds):
@@ -160345,7 +162079,7 @@ index 4dcbea7..bc30bd4 100644
          vcmd = 'installed'
          if extcmds:
              vcmd = extcmds[0]
-@@ -1354,23 +2478,62 @@ class VersionCommand(YumCommand):
+@@ -1354,23 +2582,62 @@ class VersionCommand(YumCommand):
  
  
  class HistoryCommand(YumCommand):
@@ -160409,7 +162143,7 @@ index 4dcbea7..bc30bd4 100644
              return 2, ["Repeating transaction %u" % (old.tid,)]
  
      def _hcmd_undo(self, base, extcmds):
-@@ -1426,12 +2589,57 @@ class HistoryCommand(YumCommand):
+@@ -1426,12 +2693,57 @@ class HistoryCommand(YumCommand):
      def _hcmd_new(self, base, extcmds):
          base.history._create_db_file()
  
@@ -160468,7 +162202,7 @@ index 4dcbea7..bc30bd4 100644
          if extcmds and extcmds[0] not in cmds:
              base.logger.critical(_('Invalid history sub-command, use: %s.'),
                                   ", ".join(cmds))
-@@ -1444,6 +2652,19 @@ class HistoryCommand(YumCommand):
+@@ -1444,6 +2756,19 @@ class HistoryCommand(YumCommand):
              raise cli.CliError
  
      def doCommand(self, base, basecmd, extcmds):
@@ -160488,7 +162222,7 @@ index 4dcbea7..bc30bd4 100644
          vcmd = 'list'
          if extcmds:
              vcmd = extcmds[0]
-@@ -1468,12 +2689,26 @@ class HistoryCommand(YumCommand):
+@@ -1468,12 +2793,26 @@ class HistoryCommand(YumCommand):
              ret = self._hcmd_rollback(base, extcmds)
          elif vcmd == 'new':
              ret = self._hcmd_new(base, extcmds)
@@ -160515,7 +162249,7 @@ index 4dcbea7..bc30bd4 100644
          vcmd = 'list'
          if extcmds:
              vcmd = extcmds[0]
-@@ -1481,16 +2716,46 @@ class HistoryCommand(YumCommand):
+@@ -1481,16 +2820,46 @@ class HistoryCommand(YumCommand):
  
  
  class CheckRpmdbCommand(YumCommand):
@@ -160562,7 +162296,7 @@ index 4dcbea7..bc30bd4 100644
          chkcmd = 'all'
          if extcmds:
              chkcmd = extcmds
-@@ -1505,19 +2770,57 @@ class CheckRpmdbCommand(YumCommand):
+@@ -1505,19 +2874,57 @@ class CheckRpmdbCommand(YumCommand):
          return rc, ['%s %s' % (basecmd, chkcmd)]
  
      def needTs(self, base, basecmd, extcmds):
@@ -160620,7 +162354,7 @@ index 4dcbea7..bc30bd4 100644
          if not extcmds:
              base.logger.critical(_("No saved transaction file specified."))
              raise cli.CliError
-@@ -1533,5 +2836,13 @@ class LoadTransactionCommand(YumCommand):
+@@ -1533,5 +2940,13 @@ class LoadTransactionCommand(YumCommand):
  
  
      def needTs(self, base, basecmd, extcmds):
diff --git a/yum-completion-helper.patch b/yum-completion-helper.patch
new file mode 100644
index 0000000..44e6c73
--- /dev/null
+++ b/yum-completion-helper.patch
@@ -0,0 +1,11 @@
+diff -up yum-3.4.3/completion-helper.py.old yum-3.4.3/completion-helper.py
+--- yum-3.4.3/completion-helper.py.old	2012-08-22 17:00:47.444104234 +0200
++++ yum-3.4.3/completion-helper.py	2012-08-22 17:00:57.954647525 +0200
+@@ -70,6 +70,7 @@ def get_pattern(extcmds):
+ 
+ def main(args):
+     base = cli.YumBaseCli()
++    base.setCacheDir = lambda *x: True # use the system cachedir
+     base.yum_cli_commands.clear()
+     base.registerCommand(GroupsCompletionCommand())
+     base.registerCommand(ListCompletionCommand())
diff --git a/yum.spec b/yum.spec
index 04f8f30..82d3406 100644
--- a/yum.spec
+++ b/yum.spec
@@ -18,7 +18,7 @@
 Summary: RPM package installer/updater/manager
 Name: yum
 Version: 3.4.3
-Release: 28%{?dist}
+Release: 29%{?dist}
 License: GPLv2+
 Group: System Environment/Base
 Source0: http://yum.baseurl.org/download/3.4/%{name}-%{version}.tar.gz
@@ -31,6 +31,7 @@ Patch6: yum-HEAD.patch
 Patch7: yum-ppc64-preferred.patch
 Patch8: BZ-803346-no-only-update.patch
 Patch20: yum-manpage-files.patch
+Patch21: yum-completion-helper.patch
 
 URL: http://yum.baseurl.org/
 BuildArchitectures: noarch
@@ -139,6 +140,7 @@ Install this package if you want auto yum updates nightly via cron.
 %patch7 -p1
 %patch8 -p1
 %patch20 -p1
+%patch21 -p1
 %patch1 -p1
 
 %build
@@ -313,6 +315,11 @@ exit 0
 %endif
 
 %changelog
+* Tue Aug 28 2012 Zdenek Pavlas <zpavlas at redhat.com> - 3.4.3-29
+- update to latest HEAD.
+- new groups code
+- lots of bugfixes
+
 * Mon Jun 25 2012 Zdenek Pavlas <zpavlas at redhat.com> - 3.4.3-28
 - update to latest HEAD.
 - No async downloading when --cacheonly.  BZ 830523.


More information about the scm-commits mailing list