[yum: 1/2] update to latest HEAD Added group_command, and changed to groups as objects by default. Minor update

James Antill james at fedoraproject.org
Fri Jan 20 21:20:18 UTC 2012


commit 91401a19c5d45bcc49e779424ec8beca17bff518
Author: James Antill <james at and.org>
Date:   Fri Jan 20 16:19:12 2012 -0500

    update to latest HEAD
    Added group_command, and changed to groups as objects by default.
    Minor updates.

 yum-HEAD.patch           | 2086 ++++++++++++++++++++++++++++++++++++++--------
 yum-distro-configs.patch |    6 +-
 yum.spec                 |    7 +-
 3 files changed, 1758 insertions(+), 341 deletions(-)
---
diff --git a/yum-HEAD.patch b/yum-HEAD.patch
index cef02f3..eb1da45 100644
--- a/yum-HEAD.patch
+++ b/yum-HEAD.patch
@@ -12,7 +12,7 @@ index 911da19..85decd5 100644
  .pydevproject
  asthelper.completions
 diff --git a/Makefile b/Makefile
-index 740b616..f73239b 100644
+index 740b616..e15a3f8 100644
 --- a/Makefile
 +++ b/Makefile
 @@ -1,4 +1,4 @@
@@ -21,7 +21,15 @@ index 740b616..f73239b 100644
  PYFILES = $(wildcard *.py)
  PYLINT_MODULES =  *.py yum rpmUtils
  PYLINT_IGNORE = oldUtils.py
-@@ -38,6 +38,25 @@ install:
+@@ -26,6 +26,7 @@ install:
+ 	for p in $(PYFILES) ; do \
+ 		install -m 644 $$p $(DESTDIR)/usr/share/yum-cli/$$p; \
+ 	done
++	chmod 755 $(DESTDIR)/usr/share/yum-cli/completion-helper.py
+ 	mv $(DESTDIR)/usr/share/yum-cli/yum-updatesd.py $(DESTDIR)/usr/share/yum-cli/yumupd.py
+ 	$(PYTHON) -c "import compileall; compileall.compile_dir('$(DESTDIR)/usr/share/yum-cli', 1, '$(PYDIR)', 1)"
+ 
+@@ -38,6 +39,25 @@ install:
  
  	for d in $(SUBDIRS); do make PYTHON=$(PYTHON) DESTDIR=`cd $(DESTDIR); pwd` -C $$d install; [ $$? = 0 ] || exit 1; done
  
@@ -88,7 +96,7 @@ index 2f6154e..2e5a052 100644
 diff --git a/cli.py b/cli.py
 old mode 100644
 new mode 100755
-index 6056d38..ac9522b
+index 6056d38..cd8effa
 --- a/cli.py
 +++ b/cli.py
 @@ -25,7 +25,7 @@ import sys
@@ -348,7 +356,23 @@ index 6056d38..ac9522b
              #  If we are in quiet, and assumeyes isn't on we want to output
              # at least the transaction list anyway.
              self.logger.warn(lsts)
-@@ -491,7 +540,7 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -463,7 +512,6 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+         rmpkgs = []
+         stuff_to_download = False
+         install_only = True
+-        remove_only  = True
+         for txmbr in self.tsInfo.getMembers():
+             if txmbr.ts_state not in ('i', 'u'):
+                 install_only = False
+@@ -471,7 +519,6 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+                 if po:
+                     rmpkgs.append(po)
+             else:
+-                remove_only = False
+                 stuff_to_download = True
+                 po = txmbr.po
+                 if po:
+@@ -491,7 +538,7 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
          
          # confirm with user
          if self._promptWanted():
@@ -357,7 +381,7 @@ index 6056d38..ac9522b
                  self.verbose_logger.info(_('Exiting on user Command'))
                  return -1
  
-@@ -609,12 +658,14 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -609,12 +656,14 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
          return resultobject.return_code
          
      def gpgsigcheck(self, pkgs):
@@ -377,7 +401,7 @@ index 6056d38..ac9522b
          for po in pkgs:
              result, errmsg = self.sigCheckPkg(po)
  
-@@ -623,7 +674,8 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -623,7 +672,8 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
                  continue            
  
              elif result == 1:
@@ -387,7 +411,7 @@ index 6056d38..ac9522b
                      raise yum.Errors.YumBaseError, \
                              _('Refusing to automatically import keys when running ' \
                              'unattended.\nUse "-y" to override.')
-@@ -691,12 +743,62 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -691,12 +741,62 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
                                       ", ".join(matches))
              self.verbose_logger.log(yum.logginglevels.INFO_2, msg)
  
@@ -455,7 +479,7 @@ index 6056d38..ac9522b
          # get the list of available packages
          # iterate over the user's list
          # add packages to Transaction holding class if they match.
-@@ -710,11 +812,12 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -710,11 +810,12 @@ 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))):
@@ -470,7 +494,7 @@ index 6056d38..ac9522b
              except yum.Errors.InstallError:
                  self.verbose_logger.log(yum.logginglevels.INFO_2,
                                          _('No package %s%s%s available.'),
-@@ -723,6 +826,7 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -723,6 +824,7 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
                  self._maybeYouMeant(arg)
              else:
                  done = True
@@ -478,7 +502,7 @@ index 6056d38..ac9522b
          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 +836,27 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -732,9 +834,27 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
          return 0, [_('Nothing to do')]
          
      def updatePkgs(self, userlist, quiet=0, update_to=False):
@@ -509,13 +533,13 @@ index 6056d38..ac9522b
          # 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 +867,19 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -745,21 +865,18 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
  
          else:
              # go through the userlist - look for items that are local rpms. If we find them
 -            # pass them off to localInstall() and then move on
+-            localupdates = []
 +            # pass them off to installLocal() and then move on
-             localupdates = []
              for item in userlist:
                  if (item.endswith('.rpm') and (yum.misc.re_remote_url(item) or
                                                 os.path.exists(item))):
@@ -540,7 +564,7 @@ index 6056d38..ac9522b
  
          if len(self.tsInfo) > oldcount:
              change = len(self.tsInfo) - oldcount
-@@ -770,9 +890,24 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -770,9 +887,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):
@@ -568,7 +592,7 @@ index 6056d38..ac9522b
  
          level = 'diff'
          if userlist and userlist[0] in ('full', 'diff', 'different'):
-@@ -831,6 +966,7 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -831,6 +963,7 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
                          continue
  
                      nayi = napkg.yumdb_info
@@ -576,13 +600,14 @@ index 6056d38..ac9522b
                      for apkg in self.pkgSack.searchPkgTuple(napkg.pkgtup):
                          if ('checksum_type' in nayi and
                              'checksum_data' in nayi and
-@@ -866,9 +1002,19 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -866,10 +999,19 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
              return 0, [_('No Packages marked for Distribution Synchronization')]
  
      def erasePkgs(self, userlist):
 -        """take user commands and populate a transaction wrapper with packages
 -           to be erased/removed"""
 -        
+-        oldcount = len(self.tsInfo)
 +        """Take user commands and populate a transaction wrapper with
 +        packages to be erased.
 +
@@ -596,10 +621,10 @@ index 6056d38..ac9522b
 +            1 = we've errored, exit with error string
 +            2 = we've got work yet to do, onto the next stage
 +        """
-         oldcount = len(self.tsInfo)
  
          all_rms = []
-@@ -884,9 +1030,20 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+         for arg in userlist:
+@@ -884,9 +1026,20 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
              return 0, [_('No Packages marked for removal')]
      
      def downgradePkgs(self, userlist):
@@ -623,7 +648,7 @@ index 6056d38..ac9522b
  
          oldcount = len(self.tsInfo)
          
-@@ -911,20 +1068,32 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -911,20 +1064,32 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
          return 0, [_('Nothing to do')]
          
      def reinstallPkgs(self, userlist):
@@ -660,7 +685,7 @@ index 6056d38..ac9522b
              except yum.Errors.ReinstallRemoveError:
                  self._checkMaybeYouMeant(arg, always_output=False)
              except yum.Errors.ReinstallInstallError, e:
-@@ -940,15 +1109,27 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -940,15 +1105,27 @@ 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)
@@ -691,7 +716,7 @@ index 6056d38..ac9522b
          # 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 +1153,25 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -972,20 +1149,25 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
          return 0, [_('Nothing to do')]
  
      def returnPkgLists(self, extcmds, installed_available=False):
@@ -731,7 +756,7 @@ index 6056d38..ac9522b
          special = ['available', 'installed', 'all', 'extras', 'updates', 'recent',
                     'obsoletes']
          
-@@ -1017,8 +1203,25 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -1017,8 +1199,25 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
          return ypl
  
      def search(self, args):
@@ -759,7 +784,7 @@ index 6056d38..ac9522b
          
          # call the yum module search function with lists of tags to search
          # and what to search for
-@@ -1108,9 +1311,20 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -1108,9 +1307,20 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
          return 0, matching
  
      def deplist(self, args):
@@ -782,7 +807,7 @@ index 6056d38..ac9522b
          pkgs = []
          for arg in args:
              if (arg.endswith('.rpm') and (yum.misc.re_remote_url(arg) or
-@@ -1131,10 +1345,19 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -1131,10 +1341,19 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
          return 0, []
  
      def provides(self, args):
@@ -806,7 +831,7 @@ index 6056d38..ac9522b
          old_sdup = self.conf.showdupesfromrepos
          # For output, as searchPackageProvides() is always in showdups mode
          self.conf.showdupesfromrepos = True
-@@ -1163,20 +1386,68 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -1163,20 +1382,68 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
          return 0, []
      
      def resolveDepCli(self, args):
@@ -880,7 +905,7 @@ index 6056d38..ac9522b
          hdrcode = pkgcode = xmlcode = dbcode = expccode = 0
          pkgresults = hdrresults = xmlresults = dbresults = expcresults = []
          msg = self.fmtKeyValFill(_('Cleaning repos: '), 
-@@ -1228,7 +1499,19 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -1228,7 +1495,19 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
          return code, []
  
      def returnGroupLists(self, userlist):
@@ -900,7 +925,7 @@ index 6056d38..ac9522b
          uservisible=1
              
          if len(userlist) > 0:
-@@ -1254,7 +1537,7 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -1254,7 +1533,7 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
                  msg += ' (%s)' % group.groupid
              if group.langonly:
                  msg += ' [%s]' % group.langonly
@@ -909,7 +934,7 @@ index 6056d38..ac9522b
  
          done = False
          for group in installed:
-@@ -1283,7 +1566,20 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -1283,7 +1562,20 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
          return 0, [_('Done')]
  
      def returnGroupSummary(self, userlist):
@@ -930,7 +955,7 @@ index 6056d38..ac9522b
          uservisible=1
              
          if len(userlist) > 0:
-@@ -1327,7 +1623,19 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -1327,7 +1619,19 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
          return 0, [_('Done')]
      
      def returnGroupInfo(self, userlist):
@@ -951,12 +976,14 @@ index 6056d38..ac9522b
          for strng in userlist:
              group_matched = False
              for group in self.comps.return_groups(strng):
-@@ -1340,8 +1648,18 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -1339,9 +1643,19 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+         
          return 0, []
          
-     def installGroups(self, grouplist):
+-    def installGroups(self, grouplist):
 -        """for each group requested do 'selectGroup' on them."""
 -        
++    def installGroups(self, grouplist, upgrade=False):
 +        """Mark the packages in the given groups for installation.
 +
 +        :param grouplist: a list of names or wildcards specifying
@@ -972,7 +999,16 @@ index 6056d38..ac9522b
          pkgs_used = []
          
          for group_string in grouplist:
-@@ -1368,8 +1686,18 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -1351,7 +1665,7 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+ 
+             
+                 try:
+-                    txmbrs = self.selectGroup(group.groupid)
++                    txmbrs = self.selectGroup(group.groupid, upgrade=upgrade)
+                 except yum.Errors.GroupsError:
+                     self.logger.critical(_('Warning: Group %s does not exist.'), group_string)
+                     continue
+@@ -1368,8 +1682,18 @@ 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):
@@ -992,7 +1028,7 @@ index 6056d38..ac9522b
          pkgs_used = []
          for group_string in grouplist:
              try:
-@@ -1389,7 +1717,7 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -1389,7 +1713,7 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
  
      def _promptWanted(self):
          # shortcut for the always-off/always-on options
@@ -1001,7 +1037,7 @@ index 6056d38..ac9522b
              return False
          if self.conf.alwaysprompt:
              return True
-@@ -1400,7 +1728,6 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -1400,7 +1724,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 \
@@ -1009,7 +1045,7 @@ index 6056d38..ac9522b
                     txmbr.name not in self.extcmds:
                  return True
          
-@@ -1408,11 +1735,11 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -1408,11 +1731,11 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
          return False
  
      def usage(self):
@@ -1023,7 +1059,7 @@ index 6056d38..ac9522b
          sys.stdout.write(self.optparser.get_usage())
      
      def _installable(self, pkg, ematch=False):
-@@ -1468,9 +1795,9 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -1468,9 +1791,9 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
          return False
  
  class YumOptionParser(OptionParser):
@@ -1035,7 +1071,7 @@ index 6056d38..ac9522b
  
      def __init__(self,base, **kwargs):
          # check if this is called with a utils=True/False parameter
-@@ -1488,13 +1815,23 @@ class YumOptionParser(OptionParser):
+@@ -1488,13 +1811,23 @@ class YumOptionParser(OptionParser):
          self._addYumBasicOptions()
  
      def error(self, msg):
@@ -1061,7 +1097,7 @@ index 6056d38..ac9522b
          try:
              args = _filtercmdline(
                          ('--noplugins','--version','-q', '-v', "--quiet", "--verbose"), 
-@@ -1521,7 +1858,15 @@ class YumOptionParser(OptionParser):
+@@ -1521,7 +1854,15 @@ class YumOptionParser(OptionParser):
          return ret
          
      def setupYumConfig(self, args=None):
@@ -1078,7 +1114,7 @@ index 6056d38..ac9522b
          if not args:
              (opts, cmds) = self.parse_args()
          else:
-@@ -1536,13 +1881,14 @@ class YumOptionParser(OptionParser):
+@@ -1536,13 +1877,14 @@ class YumOptionParser(OptionParser):
                  
              # Handle remaining options
              if opts.assumeyes:
@@ -1098,7 +1134,18 @@ index 6056d38..ac9522b
                  self.base.conf.cache = 1
  
              if opts.obsoletes:
-@@ -1640,6 +1986,14 @@ class YumOptionParser(OptionParser):
+@@ -1610,10 +1952,6 @@ class YumOptionParser(OptionParser):
+                     self.base.usage()
+                     sys.exit(1)
+ 
+-            # make sure the added repos are setup.        
+-            if len(opts.repos) > 0:
+-                self.base._getRepos(doSetup=True)
+-
+             # Disable all gpg key checking, if requested.
+             if opts.nogpgcheck:
+                 #  Altering the normal configs. doesn't work too well, esp. with
+@@ -1640,6 +1978,14 @@ class YumOptionParser(OptionParser):
          sys.exit(1)
  
      def getRoot(self,opts):
@@ -1113,7 +1160,7 @@ index 6056d38..ac9522b
          self._checkAbsInstallRoot(opts)
          # If the conf file is inside the  installroot - use that.
          # otherwise look for it in the normal root
-@@ -1713,6 +2067,10 @@ class YumOptionParser(OptionParser):
+@@ -1713,6 +2059,10 @@ class YumOptionParser(OptionParser):
                          help=_("verbose operation"))
          group.add_option("-y", "--assumeyes", dest="assumeyes",
                  action="store_true", help=_("answer yes for all questions"))
@@ -1124,6 +1171,95 @@ index 6056d38..ac9522b
          group.add_option("--version", action="store_true", 
                  help=_("show Yum version and exit"))
          group.add_option("--installroot", help=_("set install root"), 
+diff --git a/completion-helper.py b/completion-helper.py
+new file mode 100755
+index 0000000..e4164f7
+--- /dev/null
++++ b/completion-helper.py
+@@ -0,0 +1,83 @@
++#!/usr/bin/python -t
++# -*- coding: utf-8 -*-
++#
++# Copyright (C) 2011 Ville Skyttä
++#
++# 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
++# the Free Software Foundation; either version 2 of the License, or
++# (at your option) any later version.
++#
++# This program is distributed in the hope that it will be useful,
++# but WITHOUT ANY WARRANTY; without even the implied warranty of
++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++# GNU General Public License for more details.
++#
++# You should have received a copy of the GNU General Public License
++# along with this program; if not, write to the Free Software Foundation,
++# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
++
++
++import shlex
++import sys
++
++import cli
++import yumcommands
++
++
++class GroupsCompletionCommand(yumcommands.GroupsCommand):
++    def doCommand(self, base, basecmd, extcmds):
++        cmd, extcmds = self._grp_cmd(basecmd, extcmds)
++        # case insensitivity is fine here because groupinstall etc are that too
++        installed, available = base.doGroupLists(
++            patterns=[get_pattern(extcmds)])
++        if extcmds[0] in ("installed", "all"):
++            for group in installed:
++                print group.ui_name
++        if extcmds[0] in ("available", "all"):
++            for group in available:
++                print group.ui_name
++
++class ListCompletionCommand(yumcommands.ListCommand):
++    def doCommand(self, base, basecmd, extcmds):
++        ypl = base.doPackageLists(pkgnarrow=extcmds[0],
++                                  patterns=[get_pattern(extcmds)])
++        if extcmds[0] in ("installed", "all"):
++            for pkg in ypl.installed:
++                print pkg.na
++        if extcmds[0] in ("available", "all"):
++            for pkg in ypl.available:
++                print pkg.na
++
++class RepoListCompletionCommand(yumcommands.RepoListCommand):
++    def doCommand(self, base, basecmd, extcmds):
++        import fnmatch
++        pattern = get_pattern(extcmds)
++        for repo in base.repos.repos.values():
++            if fnmatch.fnmatch(repo.id, pattern) \
++                    and (extcmds[0] == "all" or
++                         (extcmds[0] == "enabled" and repo.isEnabled()) or
++                         (extcmds[0] == "disabled" and not repo.isEnabled())):
++                print repo.id
++
++
++def get_pattern(extcmds):
++    if len(extcmds) > 1 and extcmds[-1]:
++        return shlex.split(extcmds[-1])[0] + "*"
++    return "*"
++
++def main(args):
++    base = cli.YumBaseCli()
++    base.yum_cli_commands.clear()
++    base.registerCommand(GroupsCompletionCommand())
++    base.registerCommand(ListCompletionCommand())
++    base.registerCommand(RepoListCompletionCommand())
++    base.getOptionsConfig(args)
++    base.parseCommands()
++    base.doCommands()
++
++if __name__ == "__main__":
++    try:
++        main(sys.argv[1:])
++    except KeyboardInterrupt, e:
++        sys.exit(1)
 diff --git a/docs/sphinxdocs/Makefile b/docs/sphinxdocs/Makefile
 new file mode 100644
 index 0000000..623c22b
@@ -1723,7 +1859,7 @@ index 0000000..d2a0ed1
 +if __name__ == "__main__":
 +    generateAll(os.getcwd(), os.getcwd())
 diff --git a/docs/yum.8 b/docs/yum.8
-index 1a8202a..499da41 100644
+index 1a8202a..6cc0411 100644
 --- a/docs/yum.8
 +++ b/docs/yum.8
 @@ -52,6 +52,7 @@ gnome\-packagekit application\&.
@@ -1745,6 +1881,33 @@ index 1a8202a..499da41 100644
  .br
  .I \fR * check
  .br 
+@@ -91,7 +94,7 @@ 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
+ 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
++"provides" are searched (e.g. "_sqlitecache.so()(64bit)") as are
+ filelists (Eg. "/usr/bin/yum"). Also note that for filelists, wildcards will
+ match multiple packages\&.
+ .IP 
+@@ -111,7 +114,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
+-noticable when you do "\fBupdate\fP foo-1-2" which will act exactly as
++noticeable when you do "\fBupdate\fP foo-1-2" which will act exactly as
+ "\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,7 +227,7 @@ 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
+-yum-list-data plugins to get/use the data the other way around (Ie. what
++yum-list-data plugins to get/use the data the other way around (i.e. what
+ groups own packages need updating). If you pass the \-v option, to enable verbose
+ mode, then the package names are matched against installed/available packages
+ similar to the list command.
 @@ -235,12 +238,13 @@ that file is executed in yum shell mode. See \fIyum-shell(8)\fP for more info
  .IP
  .IP "\fBresolvedep\fP"
@@ -1779,7 +1942,18 @@ index 1a8202a..499da41 100644
  work for "installonly" packages, like Kernels. downgrade operates
  on groups, files, provides, filelists and rpm files just like the "install" command\&.
  .IP
-@@ -321,20 +325,27 @@ and so takes sub-commands:
+@@ -294,8 +298,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
+-version-groups config. file. If you pass \-v, for verbose mode, more
+-information is listed. The version is calculated by taking a sha1 hash of the
++version-groups configuration file. If you pass \-v, for verbose mode, more
++information is listed. The version is calculated by taking an SHA1 hash of the
+ 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 +325,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
@@ -1811,6 +1985,14 @@ index 1a8202a..499da41 100644
  
  The undo/redo commands act on the specified transaction, undo'ing or repeating
  the work of that transaction. While the rollback command will undo all
+-transactions upto the point of the specified transaction. For example, if you
++transactions up to the point of the specified transaction. For example, if you
+ have 3 transactions, where package A; B and C where installed respectively.
+-Then "undo 1" will try to remove pacakge A, "redo 1" will try to install package
++Then "undo 1" will try to remove package A, "redo 1" will try to install package
+ 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 +360,12 @@ transactions 1 and 4.
  The addon-info command takes a transaction ID, and the packages-list command
  takes a package (with wildcards).
@@ -1818,7 +2000,7 @@ index 1a8202a..499da41 100644
 +The stats command shows some statistics about the current history DB.
 +
 +The sync commands allows you to change the rpmdb/yumdb data stored for any
-+installed packages, to whaever is in the current rpmdb/yumdb (this is mostly
++installed packages, to whatever is in the current rpmdb/yumdb (this is mostly
 +useful when this data was not stored when the package went into the history DB).
 +
  In "history list" you can change the behaviour of the 2nd column via. the
@@ -1852,6 +2034,15 @@ index 1a8202a..499da41 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 +451,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" 
+-Sets the debug level to [name] for rpm scriplets. 'info' is the default, other
++Sets the debug level to [name] for rpm scriptlets. 'info' is the default, other
+ options are: 'critical', 'emergency', 'error', 'warn' and 'debug'.
+ .br
+ Configuration Option: \fBrpmverbosity\fP
 diff --git a/docs/yum.conf.5 b/docs/yum.conf.5
 index 515aa73..d6fe824 100644
 --- a/docs/yum.conf.5
@@ -2294,32 +2485,72 @@ index c60fa08..0000000
 -ts run
 -exit
 diff --git a/etc/yum.bash b/etc/yum.bash
-index f1e06e8..f6a0039 100644
+index f1e06e8..6d21e51 100644
 --- a/etc/yum.bash
 +++ b/etc/yum.bash
-@@ -8,7 +8,7 @@ _yum_list()
-     # Fail fast for things that look like paths.
-     [[ $2 == */* || $2 == [.~]* ]] && return
+@@ -1,53 +1,17 @@
+ # bash completion for yum
  
+-# arguments:
+-#   1 = argument to "yum list" (all, available, updates etc)
+-#   2 = current word to be completed
+-_yum_list()
+-{
+-    # Fail fast for things that look like paths.
+-    [[ $2 == */* || $2 == [.~]* ]] && return
+-
 -    if [ "$1" = all ] ; then
-+    if [[ $1 == all ]] ; then
-         # Try to strip in between headings like "Available Packages" - would
-         # be nice if e.g. -d 0 did that for us.  This will obviously only work
-         # for English :P
-@@ -44,9 +44,9 @@ _yum_repolist()
- _yum_grouplist()
+-        # Try to strip in between headings like "Available Packages" - would
+-        # be nice if e.g. -d 0 did that for us.  This will obviously only work
+-        # for English :P
+-        COMPREPLY+=( $( ${yum:-yum} -d 0 -C list $1 "$2*" 2>/dev/null | \
+-            sed -ne '/^Available /d' -e '/^Installed /d' -e '/^Updated /d' \
+-            -e 's/[[:space:]].*//p' ) )
+-    else
+-        # Drop first line (e.g. "Updated Packages") - would be nice if e.g.
+-        # -d 0 did that for us.
+-        COMPREPLY+=( $( ${yum:-yum} -d 0 -C list $1 "$2*" 2>/dev/null | \
+-            sed -ne 1d -e 's/[[:space:]].*//p' ) )
+-    fi
+-}
+-
+-# arguments:
+-#   1 = argument to "yum repolist" (enabled, disabled etc)
+-#   2 = current word to be completed
+-_yum_repolist()
++_yum_helper()
  {
-     local IFS=$'\n'
+-    # TODO: add -d 0 when http://yum.baseurl.org/ticket/29 is fixed
+-    #       (for now --noplugins is used to get rid of "Loaded plugins: ...")
+-    # Drop first ("repo id      repo name") and last ("repolist: ...") rows -
+-    # would be nice if e.g. -d 0 did that for us.
+-    COMPREPLY+=(
+-        $( compgen -W "$( ${yum:-yum} --noplugins -C repolist $1 2>/dev/null | \
+-            sed -ne '/^repo\s\{1,\}id/d' -e '/^repolist:/d' \
+-            -e 's/[[:space:]].*//p' )" -- "$2" ) )
++    local IFS=$'\n'
++    COMPREPLY+=( $(
++        /usr/share/yum-cli/completion-helper.py -d 0 -C "$@" 2>/dev/null ) )
+ }
+ 
+-# arguments:
+-#   1 = argument to "yum grouplist" (usually empty (""), or hidden)
+-#   2 = current word to be completed
+-_yum_grouplist()
++_yum_list()
+ {
+-    local IFS=$'\n'
 -    # TODO: add -d 0 when http://yum.baseurl.org/ticket/29 is fixed
 -    COMPREPLY=( $( compgen -W "$( ${yum:-yum} -C grouplist $1 "$2*" \
 -        2>/dev/null | sed -ne 's/^[[:space:]]\{1,\}\(.\{1,\}\)/\1/p' )" \
-+    COMPREPLY=( $( compgen -W "$( ${yum:-yum} -d 0 -C grouplist $1 \
-+        2>/dev/null | sed -e 's/[[:space:]]\{1,\}\[.*$//' \
-+        -ne 's/^[[:space:]]\{1,\}\(.\{1,\}\)/\1/p' )" \
-         -- "$2" ) )
+-        -- "$2" ) )
++    # Fail fast for things that look like paths.
++    [[ $2 == */* || $2 == [.~]* ]] && return
++    _yum_helper list "$@"
  }
  
-@@ -56,7 +56,7 @@ _yum_grouplist()
+ # arguments:
+@@ -56,7 +20,7 @@ _yum_grouplist()
  _yum_plugins()
  {
      local val
@@ -2328,7 +2559,7 @@ index f1e06e8..f6a0039 100644
      COMPREPLY+=( $( compgen -W '$( command grep -il "^\s*enabled\s*=\s*$val" \
          /etc/yum/pluginconf.d/*.conf 2>/dev/null \
          | sed -ne "s|^.*/\([^/]\{1,\}\)\.conf$|\1|p" )' -- "$2" ) )
-@@ -75,7 +75,7 @@ _yum_baseopts()
+@@ -75,7 +39,7 @@ _yum_baseopts()
  {
      local opts='--help --tolerant --cacheonly --config --randomwait
          --debuglevel --showduplicates --errorlevel --rpmverbosity --quiet
@@ -2337,14 +2568,14 @@ index f1e06e8..f6a0039 100644
          --disablerepo --exclude --disableexcludes --obsoletes --noplugins
          --nogpgcheck --skip-broken --color --releasever --setopt'
      [[ $COMP_LINE == *--noplugins* ]] || \
-@@ -89,6 +89,16 @@ _yum_transactions()
+@@ -89,6 +53,16 @@ _yum_transactions()
          sed -ne 's/^[[:space:]]*\([0-9]\{1,\}\).*/\1/p' )" -- "$cur" ) )
  }
  
 +_yum_atgroups()
 +{
 +    if [[ $1 == \@* ]]; then
-+        _yum_grouplist "" "${1:1}"
++        _yum_helper groups list all "${1:1}"
 +        COMPREPLY=( "${COMPREPLY[@]/#/@}" )
 +        return 0
 +    fi
@@ -2354,18 +2585,56 @@ index f1e06e8..f6a0039 100644
  # arguments:
  #   1 = current word to be completed
  #   2 = previous word
-@@ -184,8 +194,8 @@ _yum()
+@@ -119,17 +93,17 @@ _yum_complete_baseopts()
+             ;;
+ 
+         --enablerepo)
+-            _yum_repolist disabled "$1"
++            _yum_helper repolist disabled "$1"
+             return 0
+             ;;
+ 
+         --disablerepo)
+-            _yum_repolist enabled "$1"
++            _yum_helper repolist enabled "$1"
+             return 0
+             ;;
+ 
+         --disableexcludes)
+-            _yum_repolist all "$1"
++            _yum_helper repolist all "$1"
+             COMPREPLY=( $( compgen -W '${COMPREPLY[@]} all main' -- "$1" ) )
+             return 0
+             ;;
+@@ -183,16 +157,16 @@ _yum()
+ 
      # Commands offered as completions
      local cmds=( check check-update clean deplist distro-sync downgrade
-         groupinfo groupinstall grouplist groupremove help history info install
+-        groupinfo groupinstall grouplist groupremove help history info install
 -        list makecache provides reinstall remove repolist resolvedep search
 -        shell update upgrade version )
-+        list makecache provides reinstall remove repolist search shell update
-+        upgrade version )
++        groups help history info install list makecache provides reinstall
++        remove repolist search shell update upgrade version )
  
      local i c cmd subcmd
      for (( i=1; i < ${#words[@]}-1; i++ )) ; do
-@@ -211,7 +221,7 @@ _yum()
+         [[ -n $cmd ]] && subcmd=${words[i]} && break
+         # Recognize additional commands and aliases
+         for c in ${cmds[@]} check-rpmdb distribution-synchronization erase \
+-            groupupdate grouperase localinstall localupdate whatprovides ; do
++            group groupinfo groupinstall grouplist groupremove groupupdate \
++            grouperase localinstall localupdate whatprovides ; do
+             [[ ${words[i]} == $c ]] && cmd=$c && break
+         done
+     done
+@@ -205,13 +179,12 @@ _yum()
+             return 0
+             ;;
+ 
+-        check-update|grouplist|makecache|provides|whatprovides|resolvedep|\
+-        search)
++        check-update|makecache|provides|whatprovides|resolvedep|search)
+             return 0
              ;;
  
          clean)
@@ -2374,7 +2643,7 @@ index f1e06e8..f6a0039 100644
                  COMPREPLY=( $( compgen -W 'expire-cache packages headers
                      metadata cache dbcache all' -- "$cur" ) )
              return 0
-@@ -224,20 +234,22 @@ _yum()
+@@ -224,59 +197,83 @@ _yum()
              ;;
  
          distro-sync|distribution-synchronization)
@@ -2401,7 +2670,15 @@ index f1e06e8..f6a0039 100644
              return 0
              ;;
  
-@@ -247,36 +259,53 @@ _yum()
+         group*)
+-            _yum_grouplist "" "$cur"
++            if [[ ($cmd == groups || $cmd == group) && $prev == $cmd ]] ; then
++                COMPREPLY=( $( compgen -W 'info install list remove summary' \
++                    -- "$cur" ) )
++            else
++                _yum_helper groups list all "$cur"
++            fi
+             return 0
              ;;
  
          help)
@@ -2466,7 +2743,7 @@ index f1e06e8..f6a0039 100644
                      ;;
              esac
              return 0
-@@ -288,13 +317,15 @@ _yum()
+@@ -288,13 +285,15 @@ _yum()
              ;;
  
          install)
@@ -2485,7 +2762,7 @@ index f1e06e8..f6a0039 100644
                  COMPREPLY=( $( compgen -W 'all available updates installed
                      extras obsoletes recent' -- "$cur" ) )
              return 0
-@@ -306,24 +337,26 @@ _yum()
+@@ -306,24 +305,26 @@ _yum()
              ;;
  
          repolist)
@@ -2517,7 +2794,7 @@ index f1e06e8..f6a0039 100644
                  COMPREPLY=( $( compgen -W 'all installed available nogroups
                      grouplist groupinfo' -- "$cur" ) )
              return 0
-@@ -337,7 +370,11 @@ _yum()
+@@ -337,7 +338,11 @@ _yum()
  
      $split && return 0
  
@@ -2531,7 +2808,7 @@ index f1e06e8..f6a0039 100644
  complete -F _yum -o filenames yum yummain.py
  
 diff --git a/output.py b/output.py
-index b6aa277..01f0c40 100755
+index b6aa277..f21b790 100755
 --- a/output.py
 +++ b/output.py
 @@ -1,6 +1,6 @@
@@ -3037,7 +3314,75 @@ index b6aa277..01f0c40 100755
          yui = (to_unicode(_('y')), to_unicode(_('yes')))
          nui = (to_unicode(_('n')), to_unicode(_('no')))
          aui = (yui[0], yui[1], nui[0], nui[1])
-@@ -774,6 +1052,10 @@ class YumOutput:
+@@ -739,27 +1017,58 @@ class YumOutput:
+         return ret
+ 
+     def _calcDataPkgColumns(self, data, pkg_names, pkg_names2pkgs,
+-                            indent='   '):
++                            indent='   ', igroup_data=None):
+         for item in pkg_names:
+             if item not in pkg_names2pkgs:
+                 continue
+             for (apkg, ipkg) in pkg_names2pkgs[item]:
+                 pkg = ipkg or apkg
+                 envra = utf8_width(str(pkg)) + utf8_width(indent)
++                if igroup_data:
++                    envra += 1
+                 rid = len(pkg.ui_from_repo)
+                 for (d, v) in (('envra', envra), ('rid', rid)):
+                     data[d].setdefault(v, 0)
+                     data[d][v] += 1
+ 
+     def _displayPkgsFromNames(self, pkg_names, verbose, pkg_names2pkgs,
+-                              indent='   ', columns=None):
++                              indent='   ', columns=None, igroup_data=None):
++
++        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
++
+         if not verbose:
+             for item in sorted(pkg_names):
+-                print '%s%s' % (indent, item)
++                pindent = _get_igrp_data(item, indent)
++                if pindent is None:
++                    continue
++
++                print '%s%s' % (pindent, item)
+         else:
+             for item in sorted(pkg_names):
++                pindent = _get_igrp_data(item, indent)
++                if pindent is None:
++                    continue
++
+                 if item not in pkg_names2pkgs:
+-                    print '%s%s' % (indent, item)
++                    print '%s%s' % (pindent, item)
+                     continue
+                 for (apkg, ipkg) in sorted(pkg_names2pkgs[item],
+                                            key=lambda x: x[1] or x[0]):
+@@ -770,18 +1079,38 @@ class YumOutput:
+                     else:
+                         highlight = False
+                     self.simpleEnvraList(ipkg or apkg, ui_overflow=True,
+-                                         indent=indent, highlight=highlight,
++                                         indent=pindent, highlight=highlight,
                                           columns=columns)
      
      def displayPkgsInGroups(self, group):
@@ -3048,8 +3393,53 @@ index b6aa277..01f0c40 100755
          print _('\nGroup: %s') % group.ui_name
  
          verb = self.verbose_logger.isEnabledFor(logginglevels.DEBUG_3)
-@@ -807,8 +1089,11 @@ class YumOutput:
-                                            columns=columns)
+         if verb:
+             print _(' Group-Id: %s') % to_unicode(group.groupid)
++
++        igroup_data = self._groupInstalledData(group)
++        igrp_only   = set()
++        for pkg_name in igroup_data:
++            if igroup_data[pkg_name] == 'installed':
++                igrp_only.add(pkg_name)
++        igrp_only.difference_update(group.packages)
++        all_pkgs = group.packages + list(igrp_only)
++
+         pkg_names2pkgs = None
+         if verb:
+-            pkg_names2pkgs = self._group_names2aipkgs(group.packages)
++            pkg_names2pkgs = self._group_names2aipkgs(all_pkgs)
++        else:
++            pkg_names2pkgs = {}
++            for ipkg in self.rpmdb.searchNames(all_pkgs):
++                if ipkg.name not in pkg_names2pkgs:
++                    pkg_names2pkgs[ipkg.name] = []
++                pkg_names2pkgs[ipkg.name].append(ipkg)
++
+         if group.ui_description:
+             print _(' Description: %s') % to_unicode(group.ui_description)
+         if group.langonly:
+@@ -795,7 +1124,8 @@ class YumOutput:
+         if verb:
+             data = {'envra' : {}, 'rid' : {}}
+             for (section_name, pkg_names) in sections:
+-                self._calcDataPkgColumns(data, pkg_names, pkg_names2pkgs)
++                self._calcDataPkgColumns(data, pkg_names, pkg_names2pkgs,
++                                         igroup_data=igroup_data)
+             data = [data['envra'], data['rid']]
+             columns = self.calcColumns(data)
+             columns = (-columns[0], -columns[1])
+@@ -804,11 +1134,20 @@ class YumOutput:
+             if len(pkg_names) > 0:
+                 print section_name
+                 self._displayPkgsFromNames(pkg_names, verb, pkg_names2pkgs,
+-                                           columns=columns)
++                                           columns=columns,
++                                           igroup_data=igroup_data)
++        if igrp_only:
++            print _(' Installed Packages:')
++            self._displayPkgsFromNames(igrp_only, verb, pkg_names2pkgs,
++                                       columns=columns,
++                                       igroup_data=igroup_data)
  
      def depListOutput(self, results):
 -        """take a list of findDeps results and 'pretty print' the output"""
@@ -3062,7 +3452,7 @@ index b6aa277..01f0c40 100755
          verb = self.verbose_logger.isEnabledFor(logginglevels.DEBUG_3)
          for pkg in sorted(results):
              print _("package: %s") % pkg.compactPrint()
-@@ -832,7 +1117,18 @@ class YumOutput:
+@@ -832,7 +1171,18 @@ class YumOutput:
                      print "   provider: %s" % po.compactPrint()
  
      def format_number(self, number, SI=0, space=' '):
@@ -3082,7 +3472,7 @@ index b6aa277..01f0c40 100755
          symbols = [ ' ', # (none)
                      'k', # kilo
                      'M', # mega
-@@ -870,16 +1166,31 @@ class YumOutput:
+@@ -870,16 +1220,31 @@ class YumOutput:
  
      @staticmethod
      def format_time(seconds, use_hours=0):
@@ -3120,7 +3510,7 @@ index b6aa277..01f0c40 100755
          if self.conf.showdupesfromrepos:
              msg = '%s : ' % po
          else:
-@@ -935,10 +1246,23 @@ class YumOutput:
+@@ -935,10 +1300,23 @@ class YumOutput:
          print '\n\n'
  
      def matchcallback_verbose(self, po, values, matchfor=None):
@@ -3145,7 +3535,7 @@ index b6aa277..01f0c40 100755
          totsize = 0
          locsize = 0
          insize  = 0
-@@ -982,7 +1306,10 @@ class YumOutput:
+@@ -982,7 +1360,10 @@ class YumOutput:
                                          self.format_number(insize))
  
      def reportRemoveSize(self, packages):
@@ -3157,7 +3547,7 @@ index b6aa277..01f0c40 100755
          totsize = 0
          error = False
          for pkg in packages:
-@@ -1002,8 +1329,9 @@ class YumOutput:
+@@ -1002,8 +1383,9 @@ class YumOutput:
                                      self.format_number(totsize))
              
      def listTransaction(self):
@@ -3169,7 +3559,7 @@ index b6aa277..01f0c40 100755
          self.tsInfo.makelists(True, True)
          pkglist_lines = []
          data  = {'n' : {}, 'v' : {}, 'r' : {}}
-@@ -1032,8 +1360,7 @@ class YumOutput:
+@@ -1032,8 +1414,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
@@ -3179,7 +3569,7 @@ index b6aa277..01f0c40 100755
              return a_wid
  
          for (action, pkglist) in [(_('Installing'), self.tsInfo.installed),
-@@ -1102,19 +1429,72 @@ class YumOutput:
+@@ -1102,19 +1483,72 @@ class YumOutput:
  Transaction Summary
  %s
  """) % ('=' * self.term.columns))
@@ -3262,7 +3652,7 @@ index b6aa277..01f0c40 100755
          out = ''
          
          self.tsInfo.makelists()
-@@ -1179,9 +1559,9 @@ Transaction Summary
+@@ -1179,9 +1613,9 @@ Transaction Summary
          return out
  
      def setupProgressCallbacks(self):
@@ -3275,7 +3665,7 @@ index b6aa277..01f0c40 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 +1596,12 @@ Transaction Summary
+@@ -1216,10 +1650,12 @@ Transaction Summary
          self.dsCallback = dscb
      
      def setupProgessCallbacks(self):
@@ -3289,7 +3679,7 @@ index b6aa277..01f0c40 100755
          confirm_func = self._cli_confirm_gpg_key_import
          gpg_import_func = self.getKeyForRepo
          gpgca_import_func = self.getCAKeyForRepo
-@@ -1233,14 +1615,12 @@ Transaction Summary
+@@ -1233,14 +1669,12 @@ Transaction Summary
              self.repos.gpgca_import_func = gpgca_import_func
  
      def interrupt_callback(self, cbobj):
@@ -3309,7 +3699,7 @@ index b6aa277..01f0c40 100755
          '''
          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 +1649,14 @@ to exit.
+@@ -1269,6 +1703,14 @@ to exit.
  
      def download_callback_total_cb(self, remote_pkgs, remote_size,
                                     download_start_timestamp):
@@ -3324,7 +3714,7 @@ index b6aa277..01f0c40 100755
          if len(remote_pkgs) <= 1:
              return
          if not hasattr(urlgrabber.progress, 'TerminalLine'):
-@@ -1434,8 +1822,17 @@ to exit.
+@@ -1434,8 +1876,17 @@ to exit.
          return tids, printall
  
      def historyListCmd(self, extcmds):
@@ -3343,7 +3733,7 @@ index b6aa277..01f0c40 100755
          tids, printall = self._history_list_transactions(extcmds)
          if tids is None:
              return 1, ['Failed history list']
-@@ -1564,6 +1961,16 @@ to exit.
+@@ -1564,6 +2015,16 @@ to exit.
          return old[0]
  
      def historyInfoCmd(self, extcmds):
@@ -3360,7 +3750,7 @@ index b6aa277..01f0c40 100755
          def str2int(x):
              try:
                  return int(x)
-@@ -1656,6 +2063,9 @@ to exit.
+@@ -1656,6 +2117,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. """
@@ -3370,7 +3760,7 @@ index b6aa277..01f0c40 100755
          ipkgs = self.rpmdb.searchPkgTuple(hpkg.pkgtup)
          if not ipkgs:
              apkgs = self.pkgSack.searchPkgTuple(hpkg.pkgtup)
-@@ -1672,13 +2082,12 @@ to exit.
+@@ -1672,13 +2136,12 @@ to exit.
                                   'o' : _('Updated'), 'n' : _('Downgraded')}
          _pkg_states_available = {'i' : _('Installed'), 'e' : _('Not installed'),
                                   'o' : _('Older'), 'n' : _('Newer')}
@@ -3387,7 +3777,7 @@ index b6aa277..01f0c40 100755
              prefix = " " * prefix_len
              if was_installed:
                  _pkg_states = _pkg_states_installed
-@@ -1702,9 +2111,11 @@ to exit.
+@@ -1702,9 +2165,11 @@ to exit.
              else:
                  (hibeg, hiend) = self._highlight('normal')
              state = utf8_width_fill(state, _pkg_states['maxlen'])
@@ -3401,7 +3791,7 @@ index b6aa277..01f0c40 100755
  
          if type(old.tid) == type([]):
              print _("Transaction ID :"), "%u..%u" % (old.tid[0], old.tid[-1])
-@@ -1778,8 +2189,8 @@ to exit.
+@@ -1778,8 +2243,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:
@@ -3412,7 +3802,7 @@ index b6aa277..01f0c40 100755
  
          if old.trans_with:
              # This is _possible_, but not common
-@@ -1794,7 +2205,9 @@ to exit.
+@@ -1794,7 +2259,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:
@@ -3423,7 +3813,7 @@ index b6aa277..01f0c40 100755
  
          if old.rpmdb_problems:
              print _("Rpmdb Problems:")
-@@ -1833,6 +2246,13 @@ to exit.
+@@ -1833,6 +2300,13 @@ to exit.
                                'Updated'      : _('Updated'),
                                }
      def historyInfoCmdPkgsAltered(self, old, pats=[]):
@@ -3437,7 +3827,7 @@ index b6aa277..01f0c40 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 +2306,10 @@ to exit.
+@@ -1886,6 +2360,10 @@ to exit.
                                          self._hpkg2from_repo(hpkg))
  
      def historySummaryCmd(self, extcmds):
@@ -3448,7 +3838,7 @@ index b6aa277..01f0c40 100755
          tids, printall = self._history_list_transactions(extcmds)
          if tids is None:
              return 1, ['Failed history info']
-@@ -1946,6 +2370,10 @@ to exit.
+@@ -1946,6 +2424,10 @@ to exit.
                               utf8_width_fill(uiacts, 16, 16), count)
  
      def historyAddonInfoCmd(self, extcmds):
@@ -3459,7 +3849,7 @@ index b6aa277..01f0c40 100755
          tid = None
          if len(extcmds) > 1:
              tid = extcmds[1]
-@@ -1983,16 +2411,19 @@ to exit.
+@@ -1983,16 +2465,19 @@ to exit.
          
          for item in extcmds[2:]:
              if item in addon_info:
@@ -3485,7 +3875,7 @@ index b6aa277..01f0c40 100755
          tids = self.history.search(extcmds)
          limit = None
          if extcmds and not tids:
-@@ -2078,9 +2509,95 @@ to exit.
+@@ -2078,9 +2563,94 @@ to exit.
              if lastdbv.end_rpmdbversion != rpmdbv:
                  self._rpmdb_warn_checks()
  
@@ -3508,7 +3898,6 @@ index b6aa277..01f0c40 100755
 +        for old in self.history.old(tids, limit=limit):
 +            if limit is not None and num and (num +len(old.trans_data)) > limit:
 +                break
-+            last = None
 +
 +            for hpkg in old.trans_data: # Find a pkg to go with each cmd...
 +                if limit is None:
@@ -3582,7 +3971,7 @@ index b6aa277..01f0c40 100755
      
      def __init__(self, ayum=None):
          """requires yum-cli log and errorlog functions as arguments"""
-@@ -2089,6 +2606,25 @@ class DepSolveProgressCallBack:
+@@ -2089,6 +2659,25 @@ class DepSolveProgressCallBack:
          self.ayum = ayum
  
      def pkgAdded(self, pkgtup, mode):
@@ -3608,7 +3997,7 @@ index b6aa277..01f0c40 100755
          modedict = { 'i': _('installed'),
                       'u': _('an update'),
                       'e': _('erased'),
-@@ -2104,43 +2640,85 @@ class DepSolveProgressCallBack:
+@@ -2104,43 +2693,85 @@ class DepSolveProgressCallBack:
              modeterm)
          
      def start(self):
@@ -3696,7 +4085,7 @@ index b6aa277..01f0c40 100755
          needname, needflags, needversion = reqTup
  
          yb = self.ayum
-@@ -2225,46 +2803,106 @@ class DepSolveProgressCallBack:
+@@ -2225,46 +2856,106 @@ class DepSolveProgressCallBack:
          return msg
      
      def procConflict(self, name, confname):
@@ -3808,7 +4197,7 @@ index b6aa277..01f0c40 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 +2954,7 @@ def _pkgname_ui(ayum, pkgname, ts_states=None):
+@@ -2316,10 +3007,7 @@ def _pkgname_ui(ayum, pkgname, ts_states=None):
      return pkgname
  
  class YumCliRPMCallBack(RPMBaseCallback):
@@ -3820,7 +4209,7 @@ index b6aa277..01f0c40 100755
  
      width = property(lambda x: _term_width())
  
-@@ -2337,21 +2972,34 @@ class YumCliRPMCallBack(RPMBaseCallback):
+@@ -2337,21 +3025,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)):
@@ -3865,7 +4254,7 @@ index b6aa277..01f0c40 100755
          
          if type(package) not in types.StringTypes:
              pkgname = str(package)
-@@ -2363,9 +3011,25 @@ class YumCliRPMCallBack(RPMBaseCallback):
+@@ -2363,9 +3064,25 @@ class YumCliRPMCallBack(RPMBaseCallback):
              percent = 0
          else:
              percent = (te_current*100L)/te_total
@@ -3892,7 +4281,7 @@ index b6aa277..01f0c40 100755
                                                pkgname=pkgname, wid1=wid1)
              msg = fmt % (utf8_width_fill(process, wid1, wid1),
                           utf8_width_fill(pkgname, wid2, wid2))
-@@ -2377,6 +3041,11 @@ class YumCliRPMCallBack(RPMBaseCallback):
+@@ -2377,6 +3094,11 @@ class YumCliRPMCallBack(RPMBaseCallback):
                  print " "
  
      def scriptout(self, package, msgs):
@@ -3904,7 +4293,7 @@ index b6aa277..01f0c40 100755
          if msgs:
              sys.stdout.write(to_unicode(msgs))
              sys.stdout.flush()
-@@ -2429,8 +3098,30 @@ class YumCliRPMCallBack(RPMBaseCallback):
+@@ -2429,8 +3151,30 @@ class YumCliRPMCallBack(RPMBaseCallback):
              wid2 = pnl
          return fmt, wid1, wid2
  
@@ -146342,7 +146731,7 @@ index 1ce4720..25e3022
      gobject.threads_init()
      dbus.glib.threads_init()
 diff --git a/yum.spec b/yum.spec
-index abd203f..b78a9f6 100644
+index abd203f..572112a 100644
 --- a/yum.spec
 +++ b/yum.spec
 @@ -1,24 +1,51 @@
@@ -146527,7 +146916,7 @@ index abd203f..b78a9f6 100644
  
  
  %post cron
-@@ -165,21 +237,29 @@ exit 0
+@@ -165,21 +237,30 @@ exit 0
  
  
  %files -f %{name}.lang
@@ -146551,6 +146940,7 @@ index abd203f..b78a9f6 100644
  %{_sysconfdir}/bash_completion.d
 +%dir %{_datadir}/yum-cli
  %{_datadir}/yum-cli/*
++%exclude %{_datadir}/yum-cli/completion-helper.py?
 +%if %{yum_updatesd}
  %exclude %{_datadir}/yum-cli/yumupd.py*
 +%endif
@@ -146562,7 +146952,7 @@ index abd203f..b78a9f6 100644
  %dir /var/cache/yum
  %dir /var/lib/yum
  %ghost /var/lib/yum/uuid
-@@ -188,20 +268,24 @@ exit 0
+@@ -188,20 +269,24 @@ exit 0
  %ghost /var/lib/yum/yumdb
  %{_mandir}/man*/yum.*
  %{_mandir}/man*/yum-shell*
@@ -146594,7 +146984,7 @@ index abd203f..b78a9f6 100644
  %files updatesd
  %defattr(-, root, root)
  %config(noreplace) %{_sysconfdir}/yum/yum-updatesd.conf
-@@ -210,8 +294,12 @@ exit 0
+@@ -210,8 +295,12 @@ exit 0
  %{_datadir}/yum-cli/yumupd.py*
  %{_sbindir}/yum-updatesd
  %{_mandir}/man*/yum-updatesd*
@@ -146608,10 +146998,18 @@ index abd203f..b78a9f6 100644
  - 3.4.1
  - umask bug fix.
 diff --git a/yum/__init__.py b/yum/__init__.py
-index 99039e0..9163ad0 100644
+index 99039e0..cc64a90 100644
 --- a/yum/__init__.py
 +++ b/yum/__init__.py
-@@ -82,7 +82,7 @@ from packages import YumAvailablePackage, YumLocalPackage, YumInstalledPackage
+@@ -73,6 +73,7 @@ import logginglevels
+ import yumRepo
+ import callbacks
+ import yum.history
++import yum.igroups
+ 
+ import warnings
+ warnings.simplefilter("ignore", Errors.YumFutureDeprecationWarning)
+@@ -82,7 +83,7 @@ from packages import YumAvailablePackage, YumLocalPackage, YumInstalledPackage
  from packages import YumUrlPackage, YumNotFoundPackage
  from constants import *
  from yum.rpmtrans import RPMTransaction,SimpleCliCallBack
@@ -146620,7 +147018,7 @@ index 99039e0..9163ad0 100644
  
  import string
  import StringIO
-@@ -102,10 +102,12 @@ default_grabber.opts.user_agent += " yum/" + __version__
+@@ -102,10 +103,12 @@ default_grabber.opts.user_agent += " yum/" + __version__
  
  
  class _YumPreBaseConf:
@@ -146637,7 +147035,7 @@ index 99039e0..9163ad0 100644
      def __init__(self):
          self.fn = '/etc/yum/yum.conf'
          self.root = '/'
-@@ -125,10 +127,12 @@ class _YumPreBaseConf:
+@@ -125,10 +128,12 @@ class _YumPreBaseConf:
  
  
  class _YumPreRepoConf:
@@ -146654,7 +147052,7 @@ index 99039e0..9163ad0 100644
      def __init__(self):
          self.progressbar = None
          self.callback = None
-@@ -164,11 +168,11 @@ class _YumCostExclude:
+@@ -164,11 +169,11 @@ class _YumCostExclude:
          return False
  
  class YumBase(depsolve.Depsolve):
@@ -146671,7 +147069,15 @@ index 99039e0..9163ad0 100644
      def __init__(self):
          depsolve.Depsolve.__init__(self)
          self._conf = None
-@@ -213,6 +217,8 @@ class YumBase(depsolve.Depsolve):
+@@ -177,6 +182,7 @@ class YumBase(depsolve.Depsolve):
+         self._up = None
+         self._comps = None
+         self._history = None
++        self._igroups = None
+         self._pkgSack = None
+         self._lockfile = None
+         self._tags = None
+@@ -213,10 +219,15 @@ class YumBase(depsolve.Depsolve):
          for cb in self._cleanup: cb()
  
      def close(self):
@@ -146680,7 +147086,14 @@ index 99039e0..9163ad0 100644
          # We don't want to create the object, so we test if it's been created
          if self._history is not None:
              self.history.close()
-@@ -225,15 +231,33 @@ class YumBase(depsolve.Depsolve):
+ 
++        if self._igroups is not None:
++            self.igroups.close()
++
+         if self._repos:
+             self._repos.close()
+ 
+@@ -225,15 +236,33 @@ class YumBase(depsolve.Depsolve):
          return transactioninfo.TransactionData()
  
      def doGenericSetup(self, cache=0):
@@ -146716,7 +147129,7 @@ index 99039e0..9163ad0 100644
          warnings.warn(_('doConfigSetup() will go away in a future version of Yum.\n'),
                  Errors.YumFutureDeprecationWarning, stacklevel=2)
  
-@@ -297,7 +321,7 @@ class YumBase(depsolve.Depsolve):
+@@ -297,7 +326,7 @@ class YumBase(depsolve.Depsolve):
              # Try the old default
              fn = '/etc/yum.conf'
  
@@ -146725,7 +147138,7 @@ index 99039e0..9163ad0 100644
          startupconf.arch = arch
          startupconf.basearch = self.arch.basearch
          if uuid:
-@@ -367,22 +391,36 @@ class YumBase(depsolve.Depsolve):
+@@ -367,22 +396,36 @@ class YumBase(depsolve.Depsolve):
      def doLoggingSetup(self, debuglevel, errorlevel,
                         syslog_ident=None, syslog_facility=None,
                         syslog_device='/dev/log'):
@@ -146769,7 +147182,7 @@ index 99039e0..9163ad0 100644
          if repo_age is None:
              repo_age = os.stat(repofn)[8]
          
-@@ -448,8 +486,11 @@ class YumBase(depsolve.Depsolve):
+@@ -448,8 +491,11 @@ class YumBase(depsolve.Depsolve):
                  self.logger.warning(e)
          
      def getReposFromConfig(self):
@@ -146783,7 +147196,7 @@ index 99039e0..9163ad0 100644
          # Read .repo files from directories specified by the reposdir option
          # (typically /etc/yum/repos.d)
          repo_config_age = self.conf.config_file_age
-@@ -472,12 +513,13 @@ class YumBase(depsolve.Depsolve):
+@@ -472,12 +518,13 @@ class YumBase(depsolve.Depsolve):
                      self.getReposFromConfigFile(repofn, repo_age=thisrepo_age)
  
      def readRepoConfig(self, parser, section):
@@ -146802,7 +147215,7 @@ index 99039e0..9163ad0 100644
          repo = yumRepo.YumRepository(section)
          try:
              repo.populate(parser, section, self.conf)
-@@ -500,31 +542,31 @@ class YumBase(depsolve.Depsolve):
+@@ -500,31 +547,31 @@ class YumBase(depsolve.Depsolve):
          return repo
  
      def disablePlugins(self):
@@ -146855,7 +147268,7 @@ index 99039e0..9163ad0 100644
          if isinstance(self.plugins, plugins.YumPlugins):
              raise RuntimeError(_("plugins already initialised"))
  
-@@ -533,6 +575,8 @@ class YumBase(depsolve.Depsolve):
+@@ -533,6 +580,8 @@ class YumBase(depsolve.Depsolve):
  
      
      def doRpmDBSetup(self):
@@ -146864,7 +147277,7 @@ index 99039e0..9163ad0 100644
          warnings.warn(_('doRpmDBSetup() will go away in a future version of Yum.\n'),
                  Errors.YumFutureDeprecationWarning, stacklevel=2)
  
-@@ -552,7 +596,8 @@ class YumBase(depsolve.Depsolve):
+@@ -552,7 +601,8 @@ class YumBase(depsolve.Depsolve):
          return self._rpmdb
  
      def closeRpmDB(self):
@@ -146874,7 +147287,7 @@ index 99039e0..9163ad0 100644
          if self._rpmdb is not None:
              self._rpmdb.ts = None
              self._rpmdb.dropCachedData()
-@@ -567,6 +612,12 @@ class YumBase(depsolve.Depsolve):
+@@ -567,6 +617,12 @@ class YumBase(depsolve.Depsolve):
          self._ts = None
  
      def doRepoSetup(self, thisrepo=None):
@@ -146887,7 +147300,7 @@ index 99039e0..9163ad0 100644
          warnings.warn(_('doRepoSetup() will go away in a future version of Yum.\n'),
                  Errors.YumFutureDeprecationWarning, stacklevel=2)
  
-@@ -630,6 +681,14 @@ class YumBase(depsolve.Depsolve):
+@@ -630,6 +686,14 @@ class YumBase(depsolve.Depsolve):
          self._repos = RepoStorage(self)
      
      def doSackSetup(self, archlist=None, thisrepo=None):
@@ -146902,7 +147315,7 @@ index 99039e0..9163ad0 100644
          warnings.warn(_('doSackSetup() will go away in a future version of Yum.\n'),
                  Errors.YumFutureDeprecationWarning, stacklevel=2)
  
-@@ -711,6 +770,9 @@ class YumBase(depsolve.Depsolve):
+@@ -711,6 +775,9 @@ class YumBase(depsolve.Depsolve):
              
             
      def doUpdateSetup(self):
@@ -146912,7 +147325,7 @@ index 99039e0..9163ad0 100644
          warnings.warn(_('doUpdateSetup() will go away in a future version of Yum.\n'),
                  Errors.YumFutureDeprecationWarning, stacklevel=2)
  
-@@ -765,6 +827,8 @@ class YumBase(depsolve.Depsolve):
+@@ -765,6 +832,8 @@ class YumBase(depsolve.Depsolve):
          return self._up
      
      def doGroupSetup(self):
@@ -146921,7 +147334,7 @@ index 99039e0..9163ad0 100644
          warnings.warn(_('doGroupSetup() will go away in a future version of Yum.\n'),
                  Errors.YumFutureDeprecationWarning, stacklevel=2)
  
-@@ -881,7 +945,8 @@ class YumBase(depsolve.Depsolve):
+@@ -881,9 +950,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,
@@ -146930,8 +147343,30 @@ index 99039e0..9163ad0 100644
 +                                                   releasever=self.conf.yumvar['releasever'])
          return self._history
      
++    def _getIGroups(self):
++        """auto create the installed groups object that to access/change the
++           installed groups information. """
++        if self._igroups is None:
++            pdb_path = self.conf.persistdir + "/groups"
++            self._igroups = yum.igroups.InstalledGroups(db_path=pdb_path)
++        return self._igroups
++
      # properties so they auto-create themselves with defaults
-@@ -928,9 +993,10 @@ class YumBase(depsolve.Depsolve):
+     repos = property(fget=lambda self: self._getRepos(),
+                      fset=lambda self, value: setattr(self, "_repos", value),
+@@ -921,6 +999,11 @@ class YumBase(depsolve.Depsolve):
+                        fdel=lambda self: setattr(self, "_history", None),
+                        doc="Yum History Object")
+ 
++    igroups = property(fget=lambda self: self._getIGroups(),
++                       fset=lambda self, value: setattr(self, "_igroups",value),
++                       fdel=lambda self: setattr(self, "_igroups", None),
++                       doc="Yum Installed Groups Object")
++
+     pkgtags = property(fget=lambda self: self._getTags(),
+                        fset=lambda self, value: setattr(self, "_tags",value),
+                        fdel=lambda self: setattr(self, "_tags", None),
+@@ -928,9 +1011,10 @@ class YumBase(depsolve.Depsolve):
      
      
      def doSackFilelistPopulate(self):
@@ -146945,7 +147380,7 @@ index 99039e0..9163ad0 100644
          necessary = False
          
          # I can't think of a nice way of doing this, we have to have the sack here
-@@ -951,8 +1017,12 @@ class YumBase(depsolve.Depsolve):
+@@ -951,8 +1035,12 @@ class YumBase(depsolve.Depsolve):
              self.repos.populateSack(mdtype='filelists')
             
      def yumUtilsMsg(self, func, prog):
@@ -146960,7 +147395,7 @@ index 99039e0..9163ad0 100644
          if self.rpmdb.contains(name="yum-utils"):
              return
  
-@@ -964,8 +1034,17 @@ class YumBase(depsolve.Depsolve):
+@@ -964,8 +1052,17 @@ class YumBase(depsolve.Depsolve):
               (hibeg, prog, hiend))
  
      def buildTransaction(self, unfinished_transactions_check=True):
@@ -146980,7 +147415,7 @@ index 99039e0..9163ad0 100644
          if (unfinished_transactions_check and
              misc.find_unfinished_transactions(yumlibpath=self.conf.persistdir)):
              msg = _('There are unfinished transactions remaining. You might ' \
-@@ -1242,13 +1321,15 @@ class YumBase(depsolve.Depsolve):
+@@ -1242,13 +1339,15 @@ class YumBase(depsolve.Depsolve):
          if None in pkgtup:
              return None
          return pkgtup
@@ -147000,7 +147435,7 @@ index 99039e0..9163ad0 100644
          if pkgtup is None:
              return
          self._not_found_i[pkgtup] = YumNotFoundPackage(pkgtup)
-@@ -1454,8 +1535,14 @@ class YumBase(depsolve.Depsolve):
+@@ -1454,8 +1553,14 @@ class YumBase(depsolve.Depsolve):
          return probs
  
      def runTransaction(self, cb):
@@ -147016,7 +147451,7 @@ index 99039e0..9163ad0 100644
          self.plugins.run('pretrans')
  
          #  We may want to put this other places, eventually, but for now it's
-@@ -1516,10 +1603,23 @@ class YumBase(depsolve.Depsolve):
+@@ -1516,10 +1621,23 @@ class YumBase(depsolve.Depsolve):
                  pass
          self._ts_save_file = None
          
@@ -147040,7 +147475,7 @@ index 99039e0..9163ad0 100644
          
          # make resultobject - just a plain yumgenericholder object
          resultobject = misc.GenericHolder()
-@@ -1567,13 +1667,22 @@ class YumBase(depsolve.Depsolve):
+@@ -1567,13 +1685,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):
@@ -147049,6 +147484,8 @@ index 99039e0..9163ad0 100644
 +            if hasattr(cb, 'verify_txmbr'):
 +                vTcb = cb.verify_txmbr
 +            self.verifyTransaction(resultobject, vTcb)
++            if self.conf.group_command == 'objects':
++                self.igroups.save()
          return resultobject
  
 -    def verifyTransaction(self, resultobject=None):
@@ -147068,7 +147505,7 @@ index 99039e0..9163ad0 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 +1693,16 @@ class YumBase(depsolve.Depsolve):
+@@ -1584,9 +1713,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
@@ -147085,7 +147522,7 @@ index 99039e0..9163ad0 100644
          for txmbr in self.tsInfo:
              if txmbr.output_state in TS_INSTALL_STATES:
                  if not self.rpmdb.contains(po=txmbr.po):
-@@ -1596,7 +1712,9 @@ class YumBase(depsolve.Depsolve):
+@@ -1596,7 +1732,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
@@ -147095,7 +147532,25 @@ index 99039e0..9163ad0 100644
                  po = self.getInstalledPackageObject(txmbr.pkgtup)
                  rpo = txmbr.po
                  po.yumdb_info.from_repo = rpo.repoid
-@@ -1645,6 +1763,9 @@ class YumBase(depsolve.Depsolve):
+@@ -1630,6 +1768,10 @@ class YumBase(depsolve.Depsolve):
+                     if md:
+                         po.yumdb_info.from_repo_timestamp = str(md.timestamp)
+ 
++                if hasattr(txmbr, 'group_member'):
++                    # FIXME:
++                    po.yumdb_info.group_member = txmbr.group_member
++
+                 loginuid = misc.getloginuid()
+                 if txmbr.updates or txmbr.downgrades or txmbr.reinstall:
+                     if txmbr.updates:
+@@ -1640,11 +1782,16 @@ class YumBase(depsolve.Depsolve):
+                         opo = po
+                     if 'installed_by' in opo.yumdb_info:
+                         po.yumdb_info.installed_by = opo.yumdb_info.installed_by
++                    if 'group_member' in opo.yumdb_info:
++                        po.yumdb_info.group_member = opo.yumdb_info.group_member
+                     if loginuid is not None:
+                         po.yumdb_info.changed_by = str(loginuid)
                  elif loginuid is not None:
                      po.yumdb_info.installed_by = str(loginuid)
  
@@ -147105,7 +147560,7 @@ index 99039e0..9163ad0 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 +1783,13 @@ class YumBase(depsolve.Depsolve):
+@@ -1662,10 +1809,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
@@ -147119,7 +147574,7 @@ index 99039e0..9163ad0 100644
                  self.verbose_logger.log(logginglevels.DEBUG_2, 'What is this? %s' % txmbr.po)
  
          self.plugins.run('postverifytrans')
-@@ -1680,10 +1804,11 @@ class YumBase(depsolve.Depsolve):
+@@ -1680,10 +1830,11 @@ class YumBase(depsolve.Depsolve):
          self.verbose_logger.debug('VerifyTransaction time: %0.3f' % (time.time() - vt_st))
  
      def costExcludePackages(self):
@@ -147135,7 +147590,7 @@ index 99039e0..9163ad0 100644
          # if all the repo.costs are equal then don't bother running things
          costs = {}
          for r in self.repos.listEnabled():
-@@ -1705,10 +1830,12 @@ class YumBase(depsolve.Depsolve):
+@@ -1705,10 +1856,12 @@ class YumBase(depsolve.Depsolve):
              done = True
  
      def excludePackages(self, repo=None):
@@ -147151,7 +147606,7 @@ index 99039e0..9163ad0 100644
          if "all" in self.conf.disable_excludes:
              return
          
-@@ -1735,9 +1862,11 @@ class YumBase(depsolve.Depsolve):
+@@ -1735,9 +1888,11 @@ class YumBase(depsolve.Depsolve):
              self.pkgSack.addPackageExcluder(repoid, exid,'exclude.match', match)
  
      def includePackages(self, repo):
@@ -147166,7 +147621,7 @@ index 99039e0..9163ad0 100644
          includelist = repo.getIncludePkgList()
          
          if len(includelist) == 0:
-@@ -1757,8 +1886,11 @@ class YumBase(depsolve.Depsolve):
+@@ -1757,8 +1912,11 @@ class YumBase(depsolve.Depsolve):
          self.pkgSack.addPackageExcluder(repo.id, exid, 'exclude.marked')
          
      def doLock(self, lockfile = YUM_PID_FILE):
@@ -147180,7 +147635,7 @@ index 99039e0..9163ad0 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 +1906,26 @@ class YumBase(depsolve.Depsolve):
+@@ -1774,38 +1932,26 @@ class YumBase(depsolve.Depsolve):
          
          mypid=str(os.getpid())    
          while not self._lock(lockfile, mypid, 0644):
@@ -147234,7 +147689,7 @@ index 99039e0..9163ad0 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 +1950,69 @@ class YumBase(depsolve.Depsolve):
+@@ -1830,31 +1976,69 @@ class YumBase(depsolve.Depsolve):
          self._unlock(lockfile)
          self._lockfile = None
          
@@ -147314,7 +147769,7 @@ index 99039e0..9163ad0 100644
          failed = False
  
          if type(fo) is types.InstanceType:
-@@ -1894,9 +2052,16 @@ class YumBase(depsolve.Depsolve):
+@@ -1894,9 +2078,16 @@ class YumBase(depsolve.Depsolve):
          
          
      def verifyChecksum(self, fo, checksumType, csum):
@@ -147334,7 +147789,7 @@ index 99039e0..9163ad0 100644
          try:
              filesum = misc.checksum(checksumType, fo)
          except Errors.MiscError, e:
-@@ -1908,6 +2073,17 @@ class YumBase(depsolve.Depsolve):
+@@ -1908,6 +2099,17 @@ class YumBase(depsolve.Depsolve):
          return 0
  
      def downloadPkgs(self, pkglist, callback=None, callback_total=None):
@@ -147352,7 +147807,7 @@ index 99039e0..9163ad0 100644
          def mediasort(apo, bpo):
              # FIXME: we should probably also use the mediaid; else we
              # could conceivably ping-pong between different disc1's
-@@ -1998,16 +2174,6 @@ class YumBase(depsolve.Depsolve):
+@@ -1998,16 +2200,6 @@ class YumBase(depsolve.Depsolve):
                      os.unlink(local)
  
              checkfunc = (self.verifyPkg, (po, 1), {})
@@ -147369,7 +147824,7 @@ index 99039e0..9163ad0 100644
              try:
                  if i == 1 and not local_size and remote_size == po.size:
                      text = os.path.basename(po.relativepath)
-@@ -2032,7 +2198,7 @@ class YumBase(depsolve.Depsolve):
+@@ -2032,7 +2224,7 @@ class YumBase(depsolve.Depsolve):
                  done_repos.add(po.repoid)
  
              except Errors.RepoError, e:
@@ -147378,7 +147833,7 @@ index 99039e0..9163ad0 100644
              else:
                  po.localpath = mylocal
                  if po in errors:
-@@ -2052,7 +2218,22 @@ class YumBase(depsolve.Depsolve):
+@@ -2052,7 +2244,22 @@ class YumBase(depsolve.Depsolve):
          return errors
  
      def verifyHeader(self, fo, po, raiseError):
@@ -147402,7 +147857,7 @@ index 99039e0..9163ad0 100644
          if type(fo) is types.InstanceType:
              fo = fo.filename
              
-@@ -2076,9 +2257,12 @@ class YumBase(depsolve.Depsolve):
+@@ -2076,9 +2283,12 @@ class YumBase(depsolve.Depsolve):
          return 1
          
      def downloadHeader(self, po):
@@ -147417,7 +147872,7 @@ index 99039e0..9163ad0 100644
          if hasattr(po, 'pkgtype') and po.pkgtype == 'local':
              return
                  
-@@ -2122,15 +2306,17 @@ class YumBase(depsolve.Depsolve):
+@@ -2122,15 +2332,17 @@ class YumBase(depsolve.Depsolve):
              return
  
      def sigCheckPkg(self, po):
@@ -147443,7 +147898,7 @@ index 99039e0..9163ad0 100644
          if self._override_sigchecks:
              check = False
              hasgpgkey = 0
-@@ -2181,6 +2367,9 @@ class YumBase(depsolve.Depsolve):
+@@ -2181,6 +2393,9 @@ class YumBase(depsolve.Depsolve):
          return result, msg
  
      def cleanUsedHeadersPackages(self):
@@ -147453,7 +147908,7 @@ index 99039e0..9163ad0 100644
          filelist = []
          for txmbr in self.tsInfo:
              if txmbr.po.state not in TS_INSTALL_STATES:
-@@ -2218,27 +2407,42 @@ class YumBase(depsolve.Depsolve):
+@@ -2218,27 +2433,42 @@ class YumBase(depsolve.Depsolve):
                      _('%s removed'), fn)
          
      def cleanHeaders(self):
@@ -147498,7 +147953,7 @@ index 99039e0..9163ad0 100644
          cachedir = self.conf.persistdir + "/rpmdb-indexes/"
          if not os.path.exists(cachedir):
              filelist = []
-@@ -2272,8 +2476,29 @@ class YumBase(depsolve.Depsolve):
+@@ -2272,8 +2502,29 @@ class YumBase(depsolve.Depsolve):
  
      def doPackageLists(self, pkgnarrow='all', patterns=None, showdups=None,
                         ignore_case=False):
@@ -147530,7 +147985,7 @@ index 99039e0..9163ad0 100644
          if showdups is None:
              showdups = self.conf.showdupesfromrepos
          ygh = misc.GenericHolder(iter=pkgnarrow)
-@@ -2461,14 +2686,13 @@ class YumBase(depsolve.Depsolve):
+@@ -2461,14 +2712,13 @@ class YumBase(depsolve.Depsolve):
  
          
      def findDeps(self, pkgs):
@@ -147550,7 +148005,7 @@ index 99039e0..9163ad0 100644
          results = {}
  
          for pkg in pkgs:
-@@ -2495,10 +2719,22 @@ class YumBase(depsolve.Depsolve):
+@@ -2495,10 +2745,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):
@@ -147577,7 +148032,7 @@ index 99039e0..9163ad0 100644
          sql_fields = []
          for f in fields:
              sql_fields.append(RPM_TO_SQLITE.get(f, f))
-@@ -2661,6 +2897,14 @@ class YumBase(depsolve.Depsolve):
+@@ -2661,6 +2923,14 @@ class YumBase(depsolve.Depsolve):
                      yield (po, vs)
  
      def searchPackageTags(self, criteria):
@@ -147592,7 +148047,7 @@ index 99039e0..9163ad0 100644
          results = {} # name = [(criteria, taglist)]
          for c in criteria:
              c = c.lower()
-@@ -2677,11 +2921,16 @@ class YumBase(depsolve.Depsolve):
+@@ -2677,11 +2947,16 @@ class YumBase(depsolve.Depsolve):
          return results
          
      def searchPackages(self, fields, criteria, callback=None):
@@ -147614,7 +148069,7 @@ index 99039e0..9163ad0 100644
          warnings.warn(_('searchPackages() will go away in a future version of Yum.\
                        Use searchGenerator() instead. \n'),
                  Errors.YumFutureDeprecationWarning, stacklevel=2)           
-@@ -2700,6 +2949,19 @@ class YumBase(depsolve.Depsolve):
+@@ -2700,6 +2975,19 @@ class YumBase(depsolve.Depsolve):
      
      def searchPackageProvides(self, args, callback=None,
                                callback_has_matchfor=False):
@@ -147634,9 +148089,71 @@ index 99039e0..9163ad0 100644
          def _arg_data(arg):
              if not misc.re_glob(arg):
                  isglob = False
-@@ -2818,11 +3080,17 @@ class YumBase(depsolve.Depsolve):
+@@ -2723,7 +3011,7 @@ class YumBase(depsolve.Depsolve):
+                 where = self.returnPackagesByDep(arg)
+             else:
+                 usedDepString = False
+-                where = self.pkgSack.searchAll(arg, False)
++                where = self.pkgSack.searchProvides(arg)
+             self.verbose_logger.log(logginglevels.DEBUG_1,
+                P_('Searching %d package', 'Searching %d packages', len(where)), len(where))
+             
+@@ -2817,25 +3105,93 @@ class YumBase(depsolve.Depsolve):
+             
          return matches
  
++    def _groupInstalledData(self, group):
++        """ Return a dict of
++             pkg_name =>
++             (installed, available,
++             backlisted-installed, blacklisted-available). """
++        ret = {}
++        if not group or self.conf.group_command != 'objects':
++            return ret
++
++        pkg_names = {}
++        if group.groupid in self.igroups.groups:
++            pkg_names = self.igroups.groups[group.groupid].pkg_names
++
++        for pkg_name in set(group.packages + list(pkg_names)):
++            ipkgs = self.rpmdb.searchNames([pkg_name])
++            if pkg_name not in pkg_names and not ipkgs:
++                ret[pkg_name] = 'available'
++                continue
++
++            if not ipkgs:
++                ret[pkg_name] = 'blacklisted-available'
++                continue
++
++            for ipkg in ipkgs:
++                # Multiarch, if any are installed for the group we count "both"
++                if ipkg.yumdb_info.get('group_member', '') != group.groupid:
++                    continue
++                ret[pkg_name] = 'installed'
++                break
++            else:
++                ret[pkg_name] = 'blacklisted-installed'
++
++        return ret
++
++    def _groupReturnGroups(self, patterns=None, ignore_case=True):
++        igrps = None
++        if patterns is None:
++            grps = self.comps.groups
++            if self.conf.group_command == 'objects':
++                igrps = self.igroups.groups.values()
++            return igrps, grps
++
++        pats = ",".join(patterns)
++        cs   = not ignore_case
++        grps = self.comps.return_groups(pats, 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
++
      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
@@ -147657,9 +148174,55 @@ index 99039e0..9163ad0 100644
          installed = []
          available = []
  
-@@ -2852,8 +3120,13 @@ class YumBase(depsolve.Depsolve):
-     
+         if self.comps.compscount == 0:
+             raise Errors.GroupsError, _('No group data available for configured repositories')
+         
+-        if patterns is None:
+-            grps = self.comps.groups
+-        else:
+-            grps = self.comps.return_groups(",".join(patterns),
+-                                            case_sensitive=not ignore_case)
++        igrps, grps = self._groupReturnGroups(patterns, ignore_case)
++
++        if igrps is not None:
++            digrps = {}
++            for igrp in igrps:
++                digrps[igrp.gid] = igrp
++            igrps = digrps
++
+         for grp in grps:
+-            if grp.installed:
++            if igrps is None:
++                grp_installed = grp.installed
++            else:
++                grp_installed = grp.groupid in igrps
++                if grp_installed:
++                    del igrps[grp.groupid]
++
++            if grp_installed:
+                 if uservisible:
+                     if grp.user_visible:
+                         installed.append(grp)
+@@ -2848,12 +3204,29 @@ class YumBase(depsolve.Depsolve):
+                 else:
+                     available.append(grp)
+             
++        if igrps is None:
++            return sorted(installed), sorted(available)
++
++        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.installed = True
++            grp.name = grp.groupid
++            for pkg_name in igrp.pkg_names:
++                grp.mandatory_packages[pkg_name] = 1
++            installed.append(grp)
++
+         return sorted(installed), sorted(available)
      
+-    
      def groupRemove(self, grpid):
 -        """mark all the packages in this group to be removed"""
 -        
@@ -147673,7 +148236,25 @@ index 99039e0..9163ad0 100644
          txmbrs_used = []
          
          thesegroups = self.comps.return_groups(grpid)
-@@ -2872,9 +3145,10 @@ class YumBase(depsolve.Depsolve):
+@@ -2861,20 +3234,28 @@ class YumBase(depsolve.Depsolve):
+             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:
++                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)
++            if igroup_data:
++                self.igroups.del_group(thisgroup.groupid)
+             
          return txmbrs_used
  
      def groupUnremove(self, grpid):
@@ -147686,16 +148267,19 @@ index 99039e0..9163ad0 100644
          thesegroups = self.comps.return_groups(grpid)
          if not thesegroups:
              raise Errors.GroupsError, _("No Group named %s exists") % to_unicode(grpid)
-@@ -2899,12 +3173,16 @@ class YumBase(depsolve.Depsolve):
+@@ -2898,13 +3279,18 @@ class YumBase(depsolve.Depsolve):
+                             self.tsInfo.remove(txmbr.po.pkgtup)
          
          
-     def selectGroup(self, grpid, group_package_types=[], enable_group_conditionals=None):
+-    def selectGroup(self, grpid, group_package_types=[], enable_group_conditionals=None):
 -        """mark all the packages in the group to be installed
 -           returns a list of transaction members it added to the transaction 
 -           set
 -           Optionally take:
 -           group_package_types=List - overrides self.conf.group_package_types
 -           enable_group_conditionals=Bool - overrides self.conf.enable_group_conditionals
++    def selectGroup(self, grpid, group_package_types=[],
++                    enable_group_conditionals=None, upgrade=False):
 +        """Mark all the packages in the given group to be installed.
 +
 +        :param grpid: the name of the group containing the packages to
@@ -147709,16 +148293,64 @@ index 99039e0..9163ad0 100644
          """
  
          if not self.comps.has_group(grpid):
-@@ -2939,7 +3217,7 @@ class YumBase(depsolve.Depsolve):
+@@ -2934,12 +3320,47 @@ class YumBase(depsolve.Depsolve):
+             if 'optional' in package_types:
+                 pkgs.extend(thisgroup.optional_packages)
+ 
++            igroup_data = self._groupInstalledData(thisgroup)
++            igrp = None
++            if igroup_data:
++                if thisgroup.groupid in self.igroups.groups:
++                    igrp = self.igroups.groups[thisgroup.groupid]
++                else:
++                    self.igroups.add_group(thisgroup.groupid,thisgroup.packages)
++            pkgs.extend(list(igroup_data.keys()))
++
+             old_txmbrs = len(txmbrs_used)
+             for pkg in pkgs:
++                if self.conf.group_command == 'objects':
++                    assert pkg in igroup_data
++                    if (pkg not in igroup_data or
++                        igroup_data[pkg].startswith('blacklisted')):
++                        # (upgrade and igroup_data[pkg] == 'available')):
++                        msg = _('Skipping package %s from group %s'),
++                        self.verbose_logger.log(logginglevels.DEBUG_2,
++                                                msg, pkg, thisgroup.groupid)
++                        continue
++
                  self.verbose_logger.log(logginglevels.DEBUG_2,
                      _('Adding package %s from group %s'), pkg, thisgroup.groupid)
++
++                if igrp is not None:
++                    igrp.pkg_names.add(pkg)
++                    self.igroups.changed = True
++
++                txmbrs = []
                  try:
 -                    txmbrs = self.install(name = pkg)
-+                    txmbrs = self.install(name=pkg, pkg_warning_level='debug2')
++                    if (upgrade and
++                        (self.conf.group_command == 'simple' or
++                         (igroup_data and igroup_data[pkg] == 'installed'))):
++                        txmbrs = self.update(name = pkg)
++                    elif igroup_data and igroup_data[pkg] == 'installed':
++                        pass # Don't upgrade on install.
++                    else:
++                        txmbrs = self.install(name = pkg,
++                                              pkg_warning_level='debug2')
++                        for txmbr in txmbrs:
++                            txmbr.group_member = thisgroup.groupid
                  except Errors.InstallError, e:
                      self.verbose_logger.debug(_('No package named %s available to be installed'),
                          pkg)
-@@ -2997,10 +3275,14 @@ class YumBase(depsolve.Depsolve):
+@@ -2953,6 +3374,7 @@ class YumBase(depsolve.Depsolve):
+                 group_conditionals = enable_group_conditionals
+ 
+             count_cond_test = 0
++            # FIXME: What do we do about group conditionals when group==objects
+             if group_conditionals:
+                 for condreq, cond in thisgroup.conditional_packages.iteritems():
+                     if self.isPackageInstalled(cond):
+@@ -2997,10 +3419,14 @@ class YumBase(depsolve.Depsolve):
          return txmbrs_used
  
      def deselectGroup(self, grpid, force=False):
@@ -147737,7 +148369,7 @@ index 99039e0..9163ad0 100644
          
          if not self.comps.has_group(grpid):
              raise Errors.GroupsError, _("No Group named %s exists") % to_unicode(grpid)
-@@ -3035,12 +3317,21 @@ class YumBase(depsolve.Depsolve):
+@@ -3035,12 +3461,21 @@ class YumBase(depsolve.Depsolve):
                              self.tsInfo.remove(pkg.pkgtup)
          
      def getPackageObject(self, pkgtup, allow_missing=False):
@@ -147765,7 +148397,7 @@ index 99039e0..9163ad0 100644
          # look it up in the self.localPackages first:
          for po in self.localPackages:
              if po.pkgtup == pkgtup:
-@@ -3049,7 +3340,7 @@ class YumBase(depsolve.Depsolve):
+@@ -3049,7 +3484,7 @@ class YumBase(depsolve.Depsolve):
          pkgs = self.pkgSack.searchPkgTuple(pkgtup)
  
          if len(pkgs) == 0:
@@ -147774,7 +148406,7 @@ index 99039e0..9163ad0 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 +3356,21 @@ class YumBase(depsolve.Depsolve):
+@@ -3065,13 +3500,21 @@ class YumBase(depsolve.Depsolve):
          return result
  
      def getInstalledPackageObject(self, pkgtup):
@@ -147801,7 +148433,7 @@ index 99039e0..9163ad0 100644
              raise Errors.RpmDBError, _('Package tuple %s could not be found in rpmdb') % str(pkgtup)
  
          # Dito. FIXME from getPackageObject() for len() > 1 ... :)
-@@ -3079,9 +3378,11 @@ class YumBase(depsolve.Depsolve):
+@@ -3079,9 +3522,11 @@ class YumBase(depsolve.Depsolve):
          return po
          
      def gpgKeyCheck(self):
@@ -147815,7 +148447,7 @@ index 99039e0..9163ad0 100644
          gpgkeyschecked = self.conf.cachedir + '/.gpgkeyschecked.yum'
          if os.path.exists(gpgkeyschecked):
              return 1
-@@ -3106,9 +3407,13 @@ class YumBase(depsolve.Depsolve):
+@@ -3106,9 +3551,13 @@ class YumBase(depsolve.Depsolve):
              return 1
  
      def returnPackagesByDep(self, depstring):
@@ -147831,7 +148463,7 @@ index 99039e0..9163ad0 100644
          if not depstring:
              return []
  
-@@ -3135,9 +3440,16 @@ class YumBase(depsolve.Depsolve):
+@@ -3135,9 +3584,16 @@ class YumBase(depsolve.Depsolve):
          return self.pkgSack.getProvides(depname, depflags, depver).keys()
  
      def returnPackageByDep(self, depstring):
@@ -147851,7 +148483,16 @@ index 99039e0..9163ad0 100644
          # we get all sorts of randomness here
          errstring = depstring
          if type(depstring) not in types.StringTypes:
-@@ -3156,9 +3468,14 @@ class YumBase(depsolve.Depsolve):
+@@ -3149,16 +3605,22 @@ class YumBase(depsolve.Depsolve):
+             raise Errors.YumBaseError, _('No Package found for %s') % errstring
+         
+         ps = ListPackageSack(pkglist)
+-        result = self._bestPackageFromList(ps.returnNewestByNameArch())
++        result = self._bestPackageFromList(ps.returnNewestByNameArch(),
++                                           req=errstring)
+         if result is None:
+             raise Errors.YumBaseError, _('No Package found for %s') % errstring
+         
          return result
  
      def returnInstalledPackagesByDep(self, depstring):
@@ -147869,10 +148510,11 @@ index 99039e0..9163ad0 100644
          if not depstring:
              return []
  
-@@ -3184,6 +3501,34 @@ class YumBase(depsolve.Depsolve):
+@@ -3184,12 +3646,47 @@ class YumBase(depsolve.Depsolve):
  
          return self.rpmdb.getProvides(depname, depflags, depver).keys()
  
+-    def _bestPackageFromList(self, pkglist):
 +    def returnInstalledPackageByDep(self, depstring):
 +        """Return the best, or first, installed package object that provides the
 +        given dependencies.
@@ -147895,23 +148537,44 @@ index 99039e0..9163ad0 100644
 +            raise Errors.YumBaseError, _('No Package found for %s') % errstring
 +        
 +        ps = ListPackageSack(pkglist)
-+        result = self._bestPackageFromList(ps.returnNewestByNameArch())
++        result = self._bestPackageFromList(ps.returnNewestByNameArch(),
++                                           req=errstring)
 +        if result is None:
 +            raise Errors.YumBaseError, _('No Package found for %s') % errstring
 +        
 +        return result
 +
-     def _bestPackageFromList(self, pkglist):
++    def _bestPackageFromList(self, pkglist, req=None):
          """take list of package objects and return the best package object.
             If the list is empty, return None. 
-@@ -3202,10 +3547,17 @@ class YumBase(depsolve.Depsolve):
+            
+            Note: this is not aware of multilib so make sure you're only
+-           passing it packages of a single arch group."""
++           passing it packages of a single arch group.
++
++           :param pkglist: the list of packages to return the best
++             packages from
++           :param req: the requirement from the user
++           :return: a list of the best packages from *pkglist*
++        """
+         
+         
+         if len(pkglist) == 0:
+@@ -3198,14 +3695,23 @@ class YumBase(depsolve.Depsolve):
+         if len(pkglist) == 1:
+             return pkglist[0]
+ 
+-        bestlist = self._compare_providers(pkglist, None)
++        bestlist = self._compare_providers(pkglist, reqpo=None, req=req)
          return bestlist[0][0]
  
-     def bestPackagesFromList(self, pkglist, arch=None, single_name=False):
+-    def bestPackagesFromList(self, pkglist, arch=None, single_name=False):
 -        """Takes a list of packages, returns the best packages.
 -           This function is multilib aware so that it will not compare
 -           multilib to singlelib packages""" 
 -    
++    def bestPackagesFromList(self, pkglist, arch=None, single_name=False,
++                             req=None):
 +        """Return the best packages from a list of packages.  This
 +        function is multilib aware, so that it will not compare
 +        multilib to singlelib packages.
@@ -147921,12 +148584,82 @@ index 99039e0..9163ad0 100644
 +        :param arch: packages will be selected that are compatible
 +           with the architecture specified by *arch*
 +        :param single_name: whether to return a single package name
++        :param req: the requirement from the user
 +        :return: a list of the best packages from *pkglist*
 +        """
          returnlist = []
          compatArchList = self.arch.get_arch_list(arch)
          multiLib = []
-@@ -3438,13 +3790,35 @@ class YumBase(depsolve.Depsolve):
+@@ -3222,9 +3728,9 @@ class YumBase(depsolve.Depsolve):
+                 singleLib.append(po)
+                 
+         # we now have three lists.  find the best package(s) of each
+-        multi = self._bestPackageFromList(multiLib)
+-        single = self._bestPackageFromList(singleLib)
+-        no = self._bestPackageFromList(noarch)
++        multi = self._bestPackageFromList(multiLib, req=req)
++        single = self._bestPackageFromList(singleLib, req=req)
++        no = self._bestPackageFromList(noarch, req=req)
+ 
+         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 +3744,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:
+-            best = self._bestPackageFromList([multi,no])
++            best = self._bestPackageFromList([multi,no], req=req)
+             if best.arch == "noarch":
+                 returnlist.append(no)
+             else:
+@@ -3246,7 +3752,7 @@ class YumBase(depsolve.Depsolve):
+                 if single: returnlist.append(single)
+         # similar for the non-multilib case
+         elif single:
+-            best = self._bestPackageFromList([single,no])
++            best = self._bestPackageFromList([single,no], req=req)
+             if best.arch == "noarch":
+                 returnlist.append(no)
+             else:
+@@ -3353,20 +3859,24 @@ class YumBase(depsolve.Depsolve):
+             if next == slow:
+                 return None
+ 
+-    def _at_groupinstall(self, pattern):
+-        " Do groupinstall via. leading @ on the cmd line, for install/update."
++    def _at_groupinstall(self, pattern, upgrade=False):
++        " Do groupinstall via. leading @ on the cmd line, for install."
+         assert pattern[0] == '@'
+         group_string = pattern[1:]
+         tx_return = []
+         for group in self.comps.return_groups(group_string):
+             try:
+-                txmbrs = self.selectGroup(group.groupid)
++                txmbrs = self.selectGroup(group.groupid, upgrade=upgrade)
+                 tx_return.extend(txmbrs)
+             except yum.Errors.GroupsError:
+                 self.logger.critical(_('Warning: Group %s does not exist.'), group_string)
+                 continue
+         return tx_return
+-        
++
++    def _at_groupupgrade(self, pattern):
++        " Do group upgrade via. leading @ on the cmd line, for update."
++        return self._at_groupinstall(pattern, upgrade=True)
++
+     def _at_groupremove(self, pattern):
+         " Do groupremove via. leading @ on the cmd line, for remove."
+         assert pattern[0] == '@'
+@@ -3398,7 +3908,7 @@ 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[0] == '@':
+             pat = pat[1:]
+@@ -3438,13 +3948,35 @@ class YumBase(depsolve.Depsolve):
                  self.tsInfo.probFilterFlags.append(flag)
  
      def install(self, po=None, **kwargs):
@@ -147968,7 +148701,33 @@ index 99039e0..9163ad0 100644
          pkgs = []
          was_pattern = False
          if po:
-@@ -3600,23 +3974,23 @@ class YumBase(depsolve.Depsolve):
+@@ -3477,20 +4009,12 @@ class YumBase(depsolve.Depsolve):
+                     self.verbose_logger.debug(_('Checking for virtual provide or file-provide for %s'), 
+                         arg)
+ 
+-                    try:
+-                        mypkgs = self.returnPackagesByDep(arg)
+-                    except yum.Errors.YumBaseError, e:
+-                        self.logger.critical(_('No Match for argument: %s') % to_unicode(arg))
+-                    else:
+-                        # install MTA* == fail, because provides don't do globs
+-                        # install /usr/kerberos/bin/* == success (and we want
+-                        #                                all of the pkgs)
+-                        if mypkgs and not misc.re_glob(arg):
++                        mypkgs = self.pkgSack.searchProvides(arg)
++                        if not misc.re_glob(arg):
+                             mypkgs = self.bestPackagesFromList(mypkgs,
+-                                                               single_name=True)
+-                        if mypkgs:
+-                            pkgs.extend(mypkgs)
+-                        
++                                                               single_name=True,
++                                                               req=arg)
++                        pkgs.extend(mypkgs)
+             else:
+                 nevra_dict = self._nevra_kwarg_parse(kwargs)
+ 
+@@ -3600,23 +4124,23 @@ class YumBase(depsolve.Depsolve):
                      already_obs = pkgs[0]
  
                  if already_obs:
@@ -147999,7 +148758,7 @@ index 99039e0..9163ad0 100644
                      continue
  
              # make sure we don't have a name.arch of this already installed
-@@ -3630,7 +4004,7 @@ class YumBase(depsolve.Depsolve):
+@@ -3630,7 +4154,7 @@ class YumBase(depsolve.Depsolve):
                          found = True
                          break
                  if not found:
@@ -148008,7 +148767,7 @@ index 99039e0..9163ad0 100644
                      txmbrs = self.update(po=po)
                      tx_return.extend(txmbrs)
                      continue
-@@ -3719,14 +4093,33 @@ class YumBase(depsolve.Depsolve):
+@@ -3719,14 +4243,33 @@ class YumBase(depsolve.Depsolve):
          return txmbr
  
      def update(self, po=None, requiringPo=None, update_to=False, **kwargs):
@@ -148030,7 +148789,7 @@ index 99039e0..9163ad0 100644
 +           be run if it will update the given package to the given
 +           version.  For example, if the package foo-1-2 is installed,::
 +
-+             updatePkgs(["foo-1-2], update_to=False)
++             updatePkgs(["foo-1-2"], update_to=False)
 +           will work identically to::
              
 -            returns the list of txmbr of the items it marked for update"""
@@ -148049,7 +148808,30 @@ index 99039e0..9163ad0 100644
          # check for args - if no po nor kwargs, do them all
          # if po, do it, ignore all else
          # if no po do kwargs
-@@ -3985,11 +4378,18 @@ class YumBase(depsolve.Depsolve):
+@@ -3765,7 +4308,12 @@ class YumBase(depsolve.Depsolve):
+                     if new is None:
+                         continue
+                     tx_return.extend(self.update(po=new))
+-            
++
++            # Upgrade the installed groups, as part of generic "yum upgrade"
++            if self.conf.group_command == 'objects':
++                for igrp in self.igroups.groups:
++                    tx_return.extend(self._at_groupupgrade(igrp))
++
+             return tx_return
+ 
+         # complications
+@@ -3787,7 +4335,7 @@ class YumBase(depsolve.Depsolve):
+                 return self._minus_deselect(kwargs['pattern'])
+ 
+             if kwargs['pattern'] and kwargs['pattern'][0] == '@':
+-                return self._at_groupinstall(kwargs['pattern'])
++                return self._at_groupupgrade(kwargs['pattern'])
+ 
+             arg = kwargs['pattern']
+             if not update_to:
+@@ -3985,11 +4533,18 @@ class YumBase(depsolve.Depsolve):
          return tx_return
          
      def remove(self, po=None, **kwargs):
@@ -148073,7 +148855,7 @@ index 99039e0..9163ad0 100644
          if not po and not kwargs:
              raise Errors.RemoveError, 'Nothing specified to remove'
          
-@@ -4055,17 +4455,19 @@ class YumBase(depsolve.Depsolve):
+@@ -4055,17 +4610,19 @@ class YumBase(depsolve.Depsolve):
          return tx_return
  
      def installLocal(self, pkg, po=None, updateonly=False):
@@ -148103,7 +148885,7 @@ index 99039e0..9163ad0 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 +4585,15 @@ class YumBase(depsolve.Depsolve):
+@@ -4183,16 +4740,15 @@ class YumBase(depsolve.Depsolve):
          return tx_return
  
      def reinstallLocal(self, pkg, po=None):
@@ -148128,7 +148910,7 @@ index 99039e0..9163ad0 100644
          if not po:
              try:
                  po = YumUrlPackage(self, ts=self.rpmdb.readOnlyTS(), url=pkg,
-@@ -4215,9 +4616,19 @@ class YumBase(depsolve.Depsolve):
+@@ -4215,9 +4771,19 @@ class YumBase(depsolve.Depsolve):
          return self.reinstall(po=po)
  
      def reinstall(self, po=None, **kwargs):
@@ -148151,7 +148933,7 @@ index 99039e0..9163ad0 100644
          self._add_prob_flags(rpm.RPMPROB_FILTER_REPLACEPKG,
                               rpm.RPMPROB_FILTER_REPLACENEWFILES,
                               rpm.RPMPROB_FILTER_REPLACEOLDFILES)
-@@ -4259,16 +4670,15 @@ class YumBase(depsolve.Depsolve):
+@@ -4259,16 +4825,15 @@ class YumBase(depsolve.Depsolve):
          return tx_mbrs
          
      def downgradeLocal(self, pkg, po=None):
@@ -148176,7 +148958,7 @@ index 99039e0..9163ad0 100644
          if not po:
              try:
                  po = YumUrlPackage(self, ts=self.rpmdb.readOnlyTS(), url=pkg,
-@@ -4309,13 +4719,19 @@ class YumBase(depsolve.Depsolve):
+@@ -4309,13 +4874,19 @@ class YumBase(depsolve.Depsolve):
          return False
          
      def downgrade(self, po=None, **kwargs):
@@ -148203,7 +148985,7 @@ index 99039e0..9163ad0 100644
          if not po and not kwargs:
              raise Errors.DowngradeError, 'Nothing specified to downgrade'
  
-@@ -4457,7 +4873,7 @@ class YumBase(depsolve.Depsolve):
+@@ -4457,7 +5028,7 @@ class YumBase(depsolve.Depsolve):
          if e and v and r:
              evr = '%s:%s-%s' % (e, v, r)
          elif v and r:
@@ -148212,7 +148994,7 @@ index 99039e0..9163ad0 100644
          elif e and v:
              evr = '%s:%s' % (e, v)
          elif v: # e and r etc. is just too weird to print
-@@ -4500,12 +4916,24 @@ class YumBase(depsolve.Depsolve):
+@@ -4500,12 +5071,24 @@ class YumBase(depsolve.Depsolve):
  
          return returndict
  
@@ -148240,7 +149022,7 @@ index 99039e0..9163ad0 100644
          old_conf_obs = self.conf.obsoletes
          self.conf.obsoletes = False
          done = False
-@@ -4515,19 +4943,46 @@ class YumBase(depsolve.Depsolve):
+@@ -4515,19 +5098,46 @@ class YumBase(depsolve.Depsolve):
                      done = True
          for pkg in transaction.trans_data:
              if pkg.state == 'Downgrade':
@@ -148287,7 +149069,7 @@ index 99039e0..9163ad0 100644
                  if self.install(pkgtup=pkg.pkgtup):
                      done = True
          for pkg in transaction.trans_data:
-@@ -4538,8 +4993,14 @@ class YumBase(depsolve.Depsolve):
+@@ -4538,8 +5148,14 @@ class YumBase(depsolve.Depsolve):
          return done
  
      def history_undo(self, transaction):
@@ -148304,7 +149086,7 @@ index 99039e0..9163ad0 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 :).
-@@ -4674,39 +5135,49 @@ class YumBase(depsolve.Depsolve):
+@@ -4674,39 +5290,49 @@ class YumBase(depsolve.Depsolve):
              if pkgs:
                  pkgs = sorted(pkgs)[-1]
                  msg = (_('Importing %s key 0x%s:\n'
@@ -148372,7 +149154,7 @@ index 99039e0..9163ad0 100644
          user_cb_fail = False
          for keyurl in keyurls:
              keys = self._retrievePublicKey(keyurl, repo)
-@@ -4725,7 +5196,9 @@ class YumBase(depsolve.Depsolve):
+@@ -4725,7 +5351,9 @@ class YumBase(depsolve.Depsolve):
                      # Try installing/updating GPG key
                      self._getKeyImportMessage(info, keyurl)
                      rc = False
@@ -148383,7 +149165,7 @@ index 99039e0..9163ad0 100644
                          rc = True
                          
                      # grab the .sig/.asc for the keyurl, if it exists
-@@ -4751,8 +5224,8 @@ class YumBase(depsolve.Depsolve):
+@@ -4751,8 +5379,8 @@ class YumBase(depsolve.Depsolve):
                  ts = self.rpmdb.readOnlyTS()
                  result = ts.pgpImportPubkey(misc.procgpgkey(info['raw_key']))
                  if result != 0:
@@ -148394,7 +149176,7 @@ index 99039e0..9163ad0 100644
                  self.logger.info(_('Key imported successfully'))
                  key_installed = True
  
-@@ -4760,18 +5233,20 @@ class YumBase(depsolve.Depsolve):
+@@ -4760,18 +5388,20 @@ class YumBase(depsolve.Depsolve):
              raise Errors.YumBaseError, _("Didn't install any keys")
  
          if not key_installed:
@@ -148420,7 +149202,7 @@ index 99039e0..9163ad0 100644
      
      def _getAnyKeyForRepo(self, repo, destdir, keyurl_list, is_cakey=False, callback=None):
          """
-@@ -4788,6 +5263,18 @@ class YumBase(depsolve.Depsolve):
+@@ -4788,6 +5418,18 @@ class YumBase(depsolve.Depsolve):
          """
  
          key_installed = False
@@ -148439,7 +149221,7 @@ index 99039e0..9163ad0 100644
          user_cb_fail = False
          for keyurl in keyurl_list:
              keys = self._retrievePublicKey(keyurl, repo, getSig=not is_cakey)
-@@ -4819,8 +5306,11 @@ class YumBase(depsolve.Depsolve):
+@@ -4819,8 +5461,11 @@ class YumBase(depsolve.Depsolve):
                  if not key_installed:
                      self._getKeyImportMessage(info, keyurl, keytype)
                      rc = False
@@ -148452,7 +149234,7 @@ index 99039e0..9163ad0 100644
                      elif callback:
                          rc = callback({"repo": repo, "userid": info['userid'],
                                          "hexkeyid": info['hexkeyid'], "keyurl": keyurl,
-@@ -4835,7 +5325,8 @@ class YumBase(depsolve.Depsolve):
+@@ -4835,7 +5480,8 @@ class YumBase(depsolve.Depsolve):
                  # Import the key
                  result = misc.import_key_to_pubring(info['raw_key'], info['hexkeyid'], gpgdir=destdir)
                  if not result:
@@ -148462,7 +149244,7 @@ index 99039e0..9163ad0 100644
                  self.logger.info(_('Key imported successfully'))
                  key_installed = True
                  # write out the key id to imported_cakeys in the repos basedir
-@@ -4851,36 +5342,35 @@ class YumBase(depsolve.Depsolve):
+@@ -4851,36 +5497,35 @@ class YumBase(depsolve.Depsolve):
                              pass
  
          if not key_installed and user_cb_fail:
@@ -148515,7 +149297,7 @@ index 99039e0..9163ad0 100644
          self._getAnyKeyForRepo(repo, repo.gpgcadir, repo.gpgcakey, is_cakey=True, callback=callback)
  
      def _limit_installonly_pkgs(self):
-@@ -4959,19 +5449,22 @@ class YumBase(depsolve.Depsolve):
+@@ -4959,19 +5604,22 @@ class YumBase(depsolve.Depsolve):
              txmbr.depends_on.append(rel)
  
      def processTransaction(self, callback=None,rpmTestDisplay=None, rpmDisplay=None):
@@ -148551,7 +149333,7 @@ index 99039e0..9163ad0 100644
          
          if not callback:
              callback = callbacks.ProcessTransNoOutputCallback()
-@@ -5114,13 +5607,19 @@ class YumBase(depsolve.Depsolve):
+@@ -5114,13 +5762,19 @@ class YumBase(depsolve.Depsolve):
          return results
  
      def add_enable_repo(self, repoid, baseurls=[], mirrorlist=None, **kwargs):
@@ -148578,7 +149360,7 @@ index 99039e0..9163ad0 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 +5666,15 @@ class YumBase(depsolve.Depsolve):
+@@ -5167,9 +5821,15 @@ class YumBase(depsolve.Depsolve):
  
      def setCacheDir(self, force=False, tmpdir=None, reuse=True,
                      suffix='/$basearch/$releasever'):
@@ -148597,7 +149379,7 @@ index 99039e0..9163ad0 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:
-@@ -5220,13 +5725,24 @@ class YumBase(depsolve.Depsolve):
+@@ -5220,13 +5880,24 @@ class YumBase(depsolve.Depsolve):
          self.history.write_addon_data('config-repos', myrepos)
          
      def verify_plugins_cb(self, verify_package):
@@ -148625,7 +149407,7 @@ index 99039e0..9163ad0 100644
          if self.tsInfo._unresolvedMembers:
              if auto:
                  self.logger.critical(_("Dependencies not solved. Will not save unresolved transaction."))
-@@ -5234,7 +5750,7 @@ class YumBase(depsolve.Depsolve):
+@@ -5234,7 +5905,7 @@ class YumBase(depsolve.Depsolve):
              raise Errors.YumBaseError(_("Dependencies not solved. Will not save unresolved transaction."))
          
          if not filename:
@@ -148634,7 +149416,7 @@ index 99039e0..9163ad0 100644
              fd,filename = tempfile.mkstemp(suffix='.yumtx', prefix=prefix)
              f = os.fdopen(fd, 'w')
          else:
-@@ -5266,7 +5782,17 @@ class YumBase(depsolve.Depsolve):
+@@ -5266,7 +5937,17 @@ class YumBase(depsolve.Depsolve):
  
          
      def load_ts(self, filename, ignorerpm=None, ignoremissing=None):
@@ -148653,7 +149435,7 @@ index 99039e0..9163ad0 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
-@@ -5292,6 +5818,16 @@ class YumBase(depsolve.Depsolve):
+@@ -5292,6 +5973,16 @@ class YumBase(depsolve.Depsolve):
          # 3+numrepos = num pkgs
          # 3+numrepos+1 -> EOF= txmembers
          
@@ -148670,7 +149452,7 @@ index 99039e0..9163ad0 100644
          # rpm db ver
          rpmv = data[0].strip()
          if rpmv != str(self.rpmdb.simpleVersion(main_only=True)[0]):
-@@ -5329,6 +5865,7 @@ class YumBase(depsolve.Depsolve):
+@@ -5329,6 +6020,7 @@ class YumBase(depsolve.Depsolve):
          pkgcount = 0
          pkgprob = False
          curpkg = None
@@ -148826,16 +149608,17 @@ index 7ad25ce..a9a8e53 100644
          pass
          
 diff --git a/yum/config.py b/yum/config.py
-index d09511f..6c09ee9 100644
+index d09511f..982c0c5 100644
 --- a/yum/config.py
 +++ b/yum/config.py
-@@ -45,15 +45,17 @@ from misc import get_uuid, read_in_items_from_dot_dir
+@@ -45,15 +45,18 @@ from misc import get_uuid, read_in_items_from_dot_dir
  # Alter/patch these to change the default checking...
  __pkgs_gpgcheck_default__ = False
  __repo_gpgcheck_default__ = False
 +__main_multilib_policy_default__ = 'all'
 +__main_failovermethod_default__ = 'roundrobin'
 +__main_installonly_limit_default__ = 0
++__group_command_default__ = 'compat'
  
  class Option(object):
 -    '''
@@ -148851,7 +149634,7 @@ index d09511f..6c09ee9 100644
  
      def __init__(self, default=None, parse_default=False):
          self._setattrname()
-@@ -63,19 +65,19 @@ class Option(object):
+@@ -63,19 +66,19 @@ class Option(object):
          self.default = default
  
      def _setattrname(self):
@@ -148879,7 +149662,7 @@ index d09511f..6c09ee9 100644
          # xemacs highlighting hack: '
          if obj is None:
              return self
-@@ -83,12 +85,11 @@ class Option(object):
+@@ -83,12 +86,11 @@ class Option(object):
          return getattr(obj, self._attrname, None)
  
      def __set__(self, obj, value):
@@ -148896,7 +149679,7 @@ index d09511f..6c09ee9 100644
          # Only try to parse if it's a string
          if isinstance(value, basestring):
              try:
-@@ -100,62 +101,59 @@ class Option(object):
+@@ -100,62 +102,59 @@ class Option(object):
          setattr(obj, self._attrname, value)
  
      def setup(self, obj, name):
@@ -148985,7 +149768,7 @@ index d09511f..6c09ee9 100644
  
      def __init__(self, default=None, parse_default=False):
          if default is None:
-@@ -163,10 +161,12 @@ class ListOption(Option):
+@@ -163,10 +162,12 @@ class ListOption(Option):
          super(ListOption, self).__init__(default, parse_default)
  
      def parse(self, s):
@@ -149001,7 +149784,7 @@ index d09511f..6c09ee9 100644
          """
          # we need to allow for the '\n[whitespace]' continuation - easier
          # to sub the \n with a space and then read the lines
-@@ -183,12 +183,18 @@ class ListOption(Option):
+@@ -183,12 +184,18 @@ class ListOption(Option):
          return results
  
      def tostring(self, value):
@@ -149023,7 +149806,7 @@ index d09511f..6c09ee9 100644
  
      def __init__(self, default=None, schemes=('http', 'ftp', 'file', 'https'), 
              allow_none=False):
-@@ -197,6 +203,13 @@ class UrlOption(Option):
+@@ -197,6 +204,13 @@ class UrlOption(Option):
          self.allow_none = allow_none
  
      def parse(self, url):
@@ -149037,7 +149820,7 @@ index d09511f..6c09ee9 100644
          url = url.strip()
  
          # Handle the "_none_" special case
-@@ -224,10 +237,9 @@ class UrlOption(Option):
+@@ -224,10 +238,9 @@ class UrlOption(Option):
              return '%s or %s' % (', '.join(self.schemes[:-1]), self.schemes[-1])
  
  class UrlListOption(ListOption):
@@ -149051,7 +149834,7 @@ index d09511f..6c09ee9 100644
      def __init__(self, default=None, schemes=('http', 'ftp', 'file', 'https'),
                   parse_default=False):
          super(UrlListOption, self).__init__(default, parse_default)
-@@ -236,6 +248,13 @@ class UrlListOption(ListOption):
+@@ -236,6 +249,13 @@ class UrlListOption(ListOption):
          self._urloption = UrlOption(schemes=schemes)
          
      def parse(self, s):
@@ -149065,7 +149848,7 @@ index d09511f..6c09ee9 100644
          out = []
          s = s.replace('\n', ' ')
          s = s.replace(',', ' ')
-@@ -254,10 +273,7 @@ class UrlListOption(ListOption):
+@@ -254,10 +274,7 @@ class UrlListOption(ListOption):
  
  
  class IntOption(Option):
@@ -149077,7 +149860,7 @@ index d09511f..6c09ee9 100644
  
      def __init__(self, default=None, range_min=None, range_max=None):
          super(IntOption, self).__init__(default)
-@@ -265,6 +281,13 @@ class IntOption(Option):
+@@ -265,6 +282,13 @@ class IntOption(Option):
          self._range_max = range_max
          
      def parse(self, s):
@@ -149091,7 +149874,7 @@ index d09511f..6c09ee9 100644
          try:
              val = int(s)
          except (ValueError, TypeError), e:
-@@ -276,39 +299,56 @@ class IntOption(Option):
+@@ -276,39 +300,56 @@ class IntOption(Option):
          return val
  
  class PositiveIntOption(IntOption):
@@ -149162,7 +149945,7 @@ index d09511f..6c09ee9 100644
          if len(s) < 1:
              raise ValueError("no value specified")
  
-@@ -335,14 +375,20 @@ class SecondsOption(Option):
+@@ -335,14 +376,20 @@ class SecondsOption(Option):
          return int(n * mult)
  
  class BoolOption(Option):
@@ -149188,7 +149971,7 @@ index d09511f..6c09ee9 100644
          s = s.lower()
          if s in ('0', 'no', 'false'):
              return False
-@@ -352,30 +398,49 @@ class BoolOption(Option):
+@@ -352,30 +399,49 @@ class BoolOption(Option):
              raise ValueError('invalid boolean value')
  
      def tostring(self, value):
@@ -149243,7 +150026,7 @@ index d09511f..6c09ee9 100644
          if s in self._mapper:
              s = self._mapper[s]
          if s not in self._allowed:
-@@ -383,18 +448,21 @@ class SelectionOption(Option):
+@@ -383,18 +449,21 @@ class SelectionOption(Option):
          return s
  
  class CaselessSelectionOption(SelectionOption):
@@ -149273,7 +150056,7 @@ index d09511f..6c09ee9 100644
      """
      # Multipliers for unit symbols
      MULTS = {
-@@ -404,20 +472,18 @@ class BytesOption(Option):
+@@ -404,20 +473,18 @@ class BytesOption(Option):
      }
  
      def parse(self, s):
@@ -149304,7 +150087,7 @@ index d09511f..6c09ee9 100644
          """
          if len(s) < 1:
              raise ValueError("no value specified")
-@@ -443,25 +509,23 @@ class BytesOption(Option):
+@@ -443,25 +510,23 @@ class BytesOption(Option):
          return int(n * mult)
  
  class ThrottleOption(BytesOption):
@@ -149344,7 +150127,7 @@ index d09511f..6c09ee9 100644
          """
          if len(s) < 1:
              raise ValueError("no value specified")
-@@ -479,10 +543,9 @@ class ThrottleOption(BytesOption):
+@@ -479,10 +544,9 @@ class ThrottleOption(BytesOption):
              return BytesOption.parse(self, s)
  
  class BaseConfig(object):
@@ -149358,7 +150141,7 @@ index d09511f..6c09ee9 100644
  
      def __init__(self):
          self._section = None
-@@ -499,13 +562,14 @@ class BaseConfig(object):
+@@ -499,13 +563,14 @@ class BaseConfig(object):
          return '\n'.join(out)
  
      def populate(self, parser, section, parent=None):
@@ -149379,7 +150162,7 @@ index d09511f..6c09ee9 100644
          self.cfg = parser
          self._section = section
  
-@@ -527,8 +591,19 @@ class BaseConfig(object):
+@@ -527,8 +592,19 @@ class BaseConfig(object):
                  setattr(self, name, value)
  
      def optionobj(cls, name, exceptions=True):
@@ -149401,7 +150184,7 @@ index d09511f..6c09ee9 100644
          obj = getattr(cls, name, None)
          if isinstance(obj, Option):
              return obj
-@@ -539,37 +614,41 @@ class BaseConfig(object):
+@@ -539,37 +615,41 @@ class BaseConfig(object):
      optionobj = classmethod(optionobj)
  
      def isoption(cls, name):
@@ -149458,7 +150241,7 @@ index d09511f..6c09ee9 100644
          # Write section heading
          if section is None:
              if self._section is None:
-@@ -586,6 +665,14 @@ class BaseConfig(object):
+@@ -586,6 +666,14 @@ class BaseConfig(object):
          self.cfg.write(fileobj)
  
      def getConfigOption(self, option, default=None):
@@ -149473,7 +150256,7 @@ index d09511f..6c09ee9 100644
          warnings.warn('getConfigOption() will go away in a future version of Yum.\n'
                  'Please access option values as attributes or using getattr().',
                  DeprecationWarning)
-@@ -594,6 +681,12 @@ class BaseConfig(object):
+@@ -594,6 +682,12 @@ class BaseConfig(object):
          return default
  
      def setConfigOption(self, option, value):
@@ -149486,7 +150269,7 @@ index d09511f..6c09ee9 100644
          warnings.warn('setConfigOption() will go away in a future version of Yum.\n'
                  'Please set option values as attributes or using setattr().',
                  DeprecationWarning)
-@@ -603,11 +696,10 @@ class BaseConfig(object):
+@@ -603,11 +697,10 @@ class BaseConfig(object):
              raise Errors.ConfigError, 'No such option %s' % option
  
  class StartupConf(BaseConfig):
@@ -149502,7 +150285,7 @@ index d09511f..6c09ee9 100644
      # xemacs highlighting hack: '
      debuglevel = IntOption(2, 0, 10)
      errorlevel = IntOption(2, 0, 10)
-@@ -625,13 +717,13 @@ class StartupConf(BaseConfig):
+@@ -625,13 +718,13 @@ class StartupConf(BaseConfig):
      persistdir = Option('/var/lib/yum')
      
  class YumConf(StartupConf):
@@ -149520,7 +150303,7 @@ index d09511f..6c09ee9 100644
  
      cachedir = Option('/var/cache/yum')
  
-@@ -641,7 +733,7 @@ class YumConf(StartupConf):
+@@ -641,7 +734,7 @@ class YumConf(StartupConf):
  
      commands = ListOption()
      exclude = ListOption()
@@ -149529,7 +150312,7 @@ index d09511f..6c09ee9 100644
      proxy = UrlOption(schemes=('http', 'ftp', 'https'), allow_none=True)
      proxy_username = Option()
      proxy_password = Option()
-@@ -654,7 +746,8 @@ class YumConf(StartupConf):
+@@ -654,7 +747,8 @@ class YumConf(StartupConf):
      # NOTE: If you set this to 2, then because it keeps the current kernel it
      # means if you ever install an "old" kernel it'll get rid of the newest one
      # so you probably want to use 3 as a minimum ... if you turn it on.
@@ -149539,7 +150322,7 @@ index d09511f..6c09ee9 100644
                                            names_of_0=["0", "<off>"])
      kernelpkgnames = ListOption(['kernel','kernel-smp', 'kernel-enterprise',
              'kernel-bigmem', 'kernel-BOOT', 'kernel-PAE', 'kernel-PAE-debug'])
-@@ -664,6 +757,7 @@ class YumConf(StartupConf):
+@@ -664,6 +758,7 @@ class YumConf(StartupConf):
      tsflags = ListOption()
  
      assumeyes = BoolOption(False)
@@ -149547,7 +150330,14 @@ index d09511f..6c09ee9 100644
      alwaysprompt = BoolOption(True)
      exactarch = BoolOption(True)
      tolerant = BoolOption(True)
-@@ -686,6 +780,9 @@ class YumConf(StartupConf):
+@@ -681,11 +776,16 @@ class YumConf(StartupConf):
+     enable_group_conditionals = BoolOption(True)
+     groupremove_leaf_only = BoolOption(False)
+     group_package_types = ListOption(['mandatory', 'default'])
++    group_command = SelectionOption(__group_command_default__,
++                                    ('compat', 'objects', 'simple'))
+     
+     timeout = FloatOption(30.0) # FIXME: Should use variation of SecondsOption
  
      bandwidth = BytesOption(0)
      throttle = ThrottleOption(0)
@@ -149557,7 +150347,7 @@ index d09511f..6c09ee9 100644
  
      http_caching = SelectionOption('all', ('none', 'packages', 'all'))
      metadata_expire = SecondsOption(60 * 60 * 6) # Time in seconds (6h).
-@@ -698,12 +795,14 @@ class YumConf(StartupConf):
+@@ -698,12 +798,14 @@ class YumConf(StartupConf):
      #  Note that "instant" is the old behaviour, but group:primary is very
      # similar but better :).
      mdpolicy = ListOption(['group:primary'])
@@ -149574,7 +150364,7 @@ index d09511f..6c09ee9 100644
  
      color = SelectionOption('auto', ('auto', 'never', 'always'),
                              mapper={'on' : 'always', 'yes' : 'always',
-@@ -747,6 +846,7 @@ class YumConf(StartupConf):
+@@ -747,6 +849,7 @@ class YumConf(StartupConf):
      
      clean_requirements_on_remove = BoolOption(False)
  
@@ -149582,7 +150372,7 @@ index d09511f..6c09ee9 100644
  
      history_list_view = SelectionOption('single-user-commands',
                                          ('single-user-commands', 'users',
-@@ -756,6 +856,12 @@ class YumConf(StartupConf):
+@@ -756,6 +859,12 @@ class YumConf(StartupConf):
      _reposlist = []
  
      def dump(self):
@@ -149595,7 +150385,7 @@ index d09511f..6c09ee9 100644
          output = '[main]\n'
          # we exclude all vars which start with _ or are in this list:
          excluded_vars = ('cfg', 'uid', 'yumvar', 'progress_obj', 'failure_obj',
-@@ -778,14 +884,12 @@ class YumConf(StartupConf):
+@@ -778,14 +887,12 @@ class YumConf(StartupConf):
          return output
  
  class RepoConf(BaseConfig):
@@ -149613,7 +150403,7 @@ index d09511f..6c09ee9 100644
          ck = self.__cached_keys
          if not isinstance(self, RepoConf):
              ck = set()
-@@ -823,12 +927,15 @@ class RepoConf(BaseConfig):
+@@ -823,12 +930,15 @@ class RepoConf(BaseConfig):
      bandwidth = Inherit(YumConf.bandwidth)
      throttle = Inherit(YumConf.throttle)
      timeout = Inherit(YumConf.timeout)
@@ -149629,7 +150419,7 @@ index d09511f..6c09ee9 100644
      cost = IntOption(1000)
      
      sslcacert = Inherit(YumConf.sslcacert)
-@@ -839,23 +946,23 @@ class RepoConf(BaseConfig):
+@@ -839,23 +949,23 @@ class RepoConf(BaseConfig):
      skip_if_unavailable = BoolOption(False)
      
  class VersionGroupConf(BaseConfig):
@@ -149664,7 +150454,7 @@ index d09511f..6c09ee9 100644
  
      # ' xemacs syntax hack
  
-@@ -876,20 +983,24 @@ def readStartupConfig(configfile, root):
+@@ -876,20 +986,24 @@ def readStartupConfig(configfile, root):
              raise Errors.ConfigError("All plugin search paths must be absolute")
      # Stuff this here to avoid later re-parsing
      startupconf._parser = parser
@@ -149695,7 +150485,7 @@ index d09511f..6c09ee9 100644
      
      # ' xemacs syntax hack
  
-@@ -956,6 +1067,12 @@ def readMainConfig(startupconf):
+@@ -956,6 +1070,12 @@ def readMainConfig(startupconf):
      return yumconf
  
  def readVersionGroupsConfig(configfile="/etc/yum/version-groups.conf"):
@@ -149708,7 +150498,7 @@ index d09511f..6c09ee9 100644
      parser = ConfigParser()
      confpp_obj = ConfigPreProcessor(configfile)
      try:
-@@ -970,17 +1087,16 @@ def readVersionGroupsConfig(configfile="/etc/yum/version-groups.conf"):
+@@ -970,17 +1090,16 @@ def readVersionGroupsConfig(configfile="/etc/yum/version-groups.conf"):
  
  
  def getOption(conf, section, name, option):
@@ -149736,7 +150526,7 @@ index d09511f..6c09ee9 100644
      try: 
          val = conf.get(section, name)
      except (NoSectionError, NoOptionError):
-@@ -1028,7 +1144,10 @@ def _getsysver(installroot, distroverpkg):
+@@ -1028,7 +1147,10 @@ def _getsysver(installroot, distroverpkg):
      if idx.count() == 0:
          releasever = '$releasever'
      else:
@@ -149748,7 +150538,7 @@ index d09511f..6c09ee9 100644
          releasever = hdr['version']
          del hdr
      del idx
-@@ -1036,13 +1155,12 @@ def _getsysver(installroot, distroverpkg):
+@@ -1036,13 +1158,12 @@ def _getsysver(installroot, distroverpkg):
      return releasever
  
  def writeRawRepoFile(repo,only=None):
@@ -149767,7 +150557,7 @@ index d09511f..6c09ee9 100644
      if not _use_iniparse:
          return
  
-@@ -1069,7 +1187,7 @@ def writeRawRepoFile(repo,only=None):
+@@ -1069,7 +1190,7 @@ def writeRawRepoFile(repo,only=None):
          #  If the value is the same, but just interpreted ... when we don't want
          # to keep the interpreted values.
          if (name in ini[section_id] and
@@ -149777,18 +150567,20 @@ index d09511f..6c09ee9 100644
  
          if name not in cfgOptions and option.default == value:
 diff --git a/yum/depsolve.py b/yum/depsolve.py
-index 6d744c0..720188c 100644
+index 6d744c0..de01582 100644
 --- a/yum/depsolve.py
 +++ b/yum/depsolve.py
-@@ -32,7 +32,6 @@ import rpm
+@@ -31,8 +31,8 @@ from transactioninfo import TransactionMember
+ import rpm
  
  from packageSack import ListPackageSack
++from packages import PackageEVR
  from constants import *
 -import packages
  import logginglevels
  import Errors
  import warnings
-@@ -58,12 +57,12 @@ flags = {"GT": rpm.RPMSENSE_GREATER,
+@@ -58,12 +58,12 @@ flags = {"GT": rpm.RPMSENSE_GREATER,
           "LE": rpm.RPMSENSE_LESS | rpm.RPMSENSE_EQUAL,
           "EQ": rpm.RPMSENSE_EQUAL,
           None: 0 }
@@ -149805,7 +150597,7 @@ index 6d744c0..720188c 100644
  
      def __init__(self):
          self._ts = None
-@@ -81,6 +80,8 @@ class Depsolve(object):
+@@ -81,6 +81,8 @@ class Depsolve(object):
          self.installedUnresolvedFileRequires = None
  
      def doTsSetup(self):
@@ -149814,7 +150606,7 @@ index 6d744c0..720188c 100644
          warnings.warn(_('doTsSetup() will go away in a future version of Yum.\n'),
                  Errors.YumFutureDeprecationWarning, stacklevel=2)
          return self._getTs()
-@@ -131,7 +132,7 @@ class Depsolve(object):
+@@ -131,7 +133,7 @@ class Depsolve(object):
          
  
      def initActionTs(self):
@@ -149823,7 +150615,7 @@ index 6d744c0..720188c 100644
          
          self._ts = rpmUtils.transaction.TransactionWrapper(self.conf.installroot)
          ts_flags_to_rpm = { 'noscripts': rpm.RPMTRANS_FLAG_NOSCRIPTS,
-@@ -158,19 +159,31 @@ class Depsolve(object):
+@@ -158,19 +160,31 @@ class Depsolve(object):
          self._ts.setProbFilter(probfilter)
  
      def whatProvides(self, name, flags, version):
@@ -149862,7 +150654,7 @@ index 6d744c0..720188c 100644
          iopkgs = set(self.conf.installonlypkgs)
          if po.name in iopkgs:
              return True
-@@ -182,8 +195,11 @@ class Depsolve(object):
+@@ -182,8 +196,11 @@ class Depsolve(object):
          return False
  
      def populateTs(self, test=0, keepold=1):
@@ -149875,7 +150667,7 @@ index 6d744c0..720188c 100644
          if self.dsCallback: self.dsCallback.transactionPopulation()
          ts_elem = {}
          
-@@ -393,9 +409,27 @@ class Depsolve(object):
+@@ -393,9 +410,27 @@ class Depsolve(object):
              self.conf.obsoletes = 0
              txmbrs = self.update(po=requiringPo, requiringPo=requiringPo)
              self.conf.obsoletes = origobs
@@ -149905,7 +150697,7 @@ index 6d744c0..720188c 100644
                      msg = self._err_missing_requires(requiringPo, requirement)
                      self.verbose_logger.log(logginglevels.DEBUG_2, _('No update paths found for %s. Failure!'), requiringPo)
                      return self._requiringFromTransaction(requiringPo, requirement, errorlist)
-@@ -696,6 +730,13 @@ class Depsolve(object):
+@@ -696,6 +731,13 @@ class Depsolve(object):
                  self.tsInfo.remove(txmbr.pkgtup)
  
      def prof_resolveDeps(self):
@@ -149919,7 +150711,7 @@ index 6d744c0..720188c 100644
          fn = "anaconda.prof.0"
          import hotshot, hotshot.stats
          prof = hotshot.Profile(fn)
-@@ -709,6 +750,13 @@ class Depsolve(object):
+@@ -709,6 +751,13 @@ class Depsolve(object):
          return rc
  
      def cprof_resolveDeps(self):
@@ -149933,7 +150725,7 @@ index 6d744c0..720188c 100644
          import cProfile, pstats
          prof = cProfile.Profile()
          rc = prof.runcall(self.resolveDeps)
-@@ -722,7 +770,17 @@ class Depsolve(object):
+@@ -722,7 +771,17 @@ class Depsolve(object):
          return rc
  
      def resolveDeps(self, full_check=True, skipping_broken=False):
@@ -149952,7 +150744,7 @@ index 6d744c0..720188c 100644
          if not len(self.tsInfo):
              return (0, [_('Success - empty transaction')])
  
-@@ -778,6 +836,25 @@ class Depsolve(object):
+@@ -778,6 +837,25 @@ class Depsolve(object):
                      if checkdep:
                          break # The next conflict might be the same pkg
  
@@ -149978,7 +150770,7 @@ index 6d744c0..720188c 100644
                  if CheckDeps:
                      if self.dsCallback: self.dsCallback.restartLoop()
                      self.verbose_logger.log(logginglevels.DEBUG_1, _('Restarting Loop'))
-@@ -1150,6 +1227,11 @@ class Depsolve(object):
+@@ -1150,6 +1228,11 @@ class Depsolve(object):
          return ret
  
      def isPackageInstalled(self, pkgname):
@@ -149990,7 +150782,68 @@ index 6d744c0..720188c 100644
          lst = self.tsInfo.matchNaevr(name = pkgname)
          for txmbr in lst:
              if txmbr.output_state in TS_INSTALL_STATES:
-@@ -1393,42 +1475,52 @@ class Depsolve(object):
+@@ -1166,7 +1249,7 @@ class Depsolve(object):
+         return True
+     _isPackageInstalled = isPackageInstalled
+ 
+-    def _compare_providers(self, pkgs, reqpo):
++    def _compare_providers(self, pkgs, reqpo, req=None):
+         """take the list of pkgs and score them based on the requesting package
+            return a dictionary of po=score"""
+         self.verbose_logger.log(logginglevels.DEBUG_4,
+@@ -1210,6 +1293,24 @@ class Depsolve(object):
+                 return None
+             return x
+ 
++        def _pkg2prov_version(pkg, provname):
++            ''' This converts a package into a specific version tuple for the
++            required provide. The provide _must_ be '=' and epoch=None and
++            release=None == '0'.
++               If there is 0 or 2+ matches, return None.
++               If the req does not == the provide name, return None. '''
++            ret = None
++            for prov in pkg.provides:
++                (n, f, (e, v, r)) = prov
++                if n != provname:
++                    continue
++                if f != 'EQ':
++                    continue
++                if ret is not None:
++                    return None
++                ret = (e or '0', v, r or '0')
++            return ret
++
+         #  Actual start of _compare_providers().
+ 
+         # Do a NameArch filtering, based on repo. __cmp__
+@@ -1332,6 +1433,26 @@ class Depsolve(object):
+                         _('common prefix of %s between %s and %s' % (cpl, po, reqpo)))
+                 
+                     pkgresults[po] += cpl*2
++
++        if req is not None:
++            bestnum = max(pkgresults.values())
++            prov_depsolve = {}
++            for po in pkgs:
++                if pkgresults[po] != bestnum:
++                    continue
++                evr = _pkg2prov_version(po, req)
++                if evr is None:
++                    prov_depsolve = {}
++                    break
++                prov_depsolve[po] = evr
++            if len(prov_depsolve) > 1:
++                self.verbose_logger.log(logginglevels.DEBUG_4,
++                                        _('provides vercmp: %s') % str(req))
++                newest = sorted(prov_depsolve,
++                                key = lambda x: PackageEVR(*prov_depsolve[x]))[-1]
++                self.verbose_logger.log(logginglevels.DEBUG_4,
++                                        _(' Winner: %s') % newest)
++                pkgresults[newest] += 1
+                 
+         #  If we have more than one "best", see what would happen if we picked
+         # each package ... ie. what things do they require that _aren't_ already
+@@ -1393,42 +1514,52 @@ class Depsolve(object):
  
  
  class DepCheck(object):
@@ -150047,12 +150900,12 @@ index 6d744c0..720188c 100644
  
  class Conflicts(object):
 -
--    """
--    A pure data class for holding a package and the list of things it
--    conflicts.
 +    """A pure data class for holding a list packages and what the
 +    conflict between them is.
      """
+-    A pure data class for holding a package and the list of things it
+-    conflicts.
+-    """
 -
      def __init__(self, pkglist, conflict):
          self.pkglist = pkglist # list of conflicting package objects
@@ -150710,6 +151563,153 @@ index 9889bf6..76a258d 100755
  try: 
      '''
      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
+--- /dev/null
++++ b/yum/igroups.py
+@@ -0,0 +1,141 @@
++#! /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
++# the Free Software Foundation; either version 2 of the License, or
++# (at your option) any later version.
++#
++# This program is distributed in the hope that it will be useful,
++# but WITHOUT ANY WARRANTY; without even the implied warranty of
++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++# GNU Library General Public License for more details.
++#
++# You should have received a copy of the GNU General Public License
++# 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
++#
++# James Antill <james at fedoraproject.org>
++
++import os
++import fnmatch
++import re
++
++class InstalledGroup(object):
++    def __init__(self, gid):
++        self.gid       = gid
++        self.pkg_names = set()
++
++    def __cmp__(self, other):
++        if other is None:
++            return 1
++        return cmp(self.gid, other.gid)
++
++    def _additions(self, pkg_names):
++        pkg_names = set(pkg_names)
++        return sorted(pkg_names.difference(self.pkg_names))
++
++    def _removals(self, pkg_names):
++        pkg_names = set(pkg_names)
++        return sorted(pkg_names.difference(self.pkg_names))
++
++
++class InstalledGroups(object):
++    def __init__(self, db_path):
++        self.filename = db_path + "/installed"
++        self.groups   = {}
++        self.changed  = False
++
++        if not os.access(self.filename, os.R_OK):
++            return
++
++        def _read_str(fo):
++            return fo.readline()[:-1]
++
++        fo = open(self.filename)
++        ver = int(_read_str(fo))
++        if ver != 1:
++            return
++
++        groups_num = int(_read_str(fo))
++        while groups_num > 0:
++            groups_num -= 1
++
++            grp = InstalledGroup(_read_str(fo))
++            self.groups[grp.gid] = grp
++
++            num = int(_read_str(fo))
++            while num > 0:
++                num -= 1
++                grp.pkg_names.add(_read_str(fo))
++
++    def close(self):
++        pass
++
++    def save(self, force=False):
++        if not force and not self.changed:
++            return False
++
++        db_path = os.path.dirname(self.filename)
++        if not os.path.exists(db_path):
++            try:
++                os.makedirs(db_path)
++            except (IOError, OSError), e:
++                # some sort of useful thing here? A warning?
++                return False
++
++        if not os.access(db_path, os.W_OK):
++            return False
++
++        fo = open(self.filename + '.tmp', 'w')
++
++        fo.write("1\n") # version
++        fo.write("%u\n" % len(self.groups))
++        for grp in sorted(self.groups.values()):
++            fo.write("%s\n" % grp.gid)
++            fo.write("%u\n" % len(grp.pkg_names))
++            for pkgname in sorted(grp.pkg_names):
++                fo.write("%s\n" % pkgname)
++        fo.close()
++        os.rename(self.filename + '.tmp', self.filename)
++        self.changed = False
++
++    def add_group(self, groupid, pkg_names):
++        self.changed = True
++
++        if groupid not in self.groups:
++            self.groups[groupid] = InstalledGroup(groupid)
++        grp = self.groups[groupid]
++
++        for pkg_name in pkg_names:
++            grp.pkg_names.add(pkg_name)
++
++    def del_group(self, groupid):
++        self.changed = True
++
++        if groupid in self.groups:
++            del self.groups[groupid]
++
++    def return_groups(self, group_pattern, case_sensitive=False):
++        returns = {}
++
++        for item in group_pattern.split(','):
++            item = item.strip()
++            if item in self.groups:
++                thisgroup = self.groups[item]
++                returns[thisgroup.gid] = 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.groups.values():
++                if match(group.gid):
++                    done = True
++                    returns[group.gid] = group
++                    break
++
++        return returns.values()
 diff --git a/yum/misc.py b/yum/misc.py
 index 2f6ddfe..5321003 100644
 --- a/yum/misc.py
@@ -150797,7 +151797,7 @@ index 4af563a..47832fc 100644
          self.obsoletes = {} #obs[obsoletename] = [pkg1, pkg2, pkg3] 
                   #the package lists are packages that obsolete the key name
 diff --git a/yum/packages.py b/yum/packages.py
-index 5ef9951..15316c8 100644
+index 5ef9951..6bc909e 100644
 --- a/yum/packages.py
 +++ b/yum/packages.py
 @@ -243,34 +243,87 @@ class PackageObject(object):
@@ -150908,7 +151908,31 @@ index 5ef9951..15316c8 100644
      def __str__(self):
          return self.ui_envra
  
-@@ -1083,7 +1136,7 @@ class YumAvailablePackage(PackageObject, RpmBase):
+@@ -481,15 +534,14 @@ class RpmBase(object):
+             if prcotuple in self._prco_lookup[prcotype]:
+                 return 1
+ 
+-        if True: # Keep indentation for patch smallness...
+-            # make us look it up and compare
+-            (reqn, reqf, (reqe, reqv ,reqr)) = prcotuple
+-            if reqf is not None:
+-                return self.inPrcoRange(prcotype, prcotuple)
+-            else:
+-                for (n, f, (e, v, r)) in self.returnPrco(prcotype):
+-                    if i18n.str_eq(reqn, n):
+-                        return 1
++        # make us look it up and compare
++        (reqn, reqf, (reqe, reqv ,reqr)) = prcotuple
++        if reqf is not None:
++            return self.inPrcoRange(prcotype, prcotuple)
++        else:
++            for (n, f, (e, v, r)) in self.returnPrco(prcotype):
++                if i18n.str_eq(reqn, n):
++                    return 1
+ 
+         return 0
+ 
+@@ -1083,7 +1135,7 @@ class YumAvailablePackage(PackageObject, RpmBase):
           misc.to_unicode(misc.to_xml(self.summary)), 
           misc.to_unicode(misc.to_xml(self.description)), 
           packager, url, self.filetime, 
@@ -150917,7 +151941,7 @@ index 5ef9951..15316c8 100644
          
          msg += self._return_remote_location()
          return msg
-@@ -1133,7 +1186,7 @@ class YumAvailablePackage(PackageObject, RpmBase):
+@@ -1133,7 +1185,7 @@ class YumAvailablePackage(PackageObject, RpmBase):
          msg = ""
          mylist = getattr(self, pcotype)
          if mylist: msg = "\n    <rpm:%s>\n" % pcotype
@@ -150926,7 +151950,7 @@ index 5ef9951..15316c8 100644
              pcostring = '''      <rpm:entry name="%s"''' % misc.to_xml(name, attrib=True)
              if flags:
                  pcostring += ''' flags="%s"''' % misc.to_xml(flags, attrib=True)
-@@ -1161,11 +1214,11 @@ class YumAvailablePackage(PackageObject, RpmBase):
+@@ -1161,11 +1213,11 @@ class YumAvailablePackage(PackageObject, RpmBase):
              dirs = self.returnFileEntries('dir', primary_only=True)
              ghosts = self.returnFileEntries('ghost', primary_only=True)
                  
@@ -150941,7 +151965,7 @@ index 5ef9951..15316c8 100644
              msg += """    <file type="ghost">%s</file>\n""" % misc.to_xml(fn)
          
          return msg
-@@ -1194,8 +1247,8 @@ class YumAvailablePackage(PackageObject, RpmBase):
+@@ -1194,8 +1246,8 @@ class YumAvailablePackage(PackageObject, RpmBase):
                          continue
                      newlist.append(i)
                  mylist = newlist
@@ -150952,7 +151976,7 @@ index 5ef9951..15316c8 100644
              if name.startswith('rpmlib('):
                  continue
              # this drops out requires that the pkg provides for itself.
-@@ -1217,13 +1270,16 @@ class YumAvailablePackage(PackageObject, RpmBase):
+@@ -1217,13 +1269,16 @@ class YumAvailablePackage(PackageObject, RpmBase):
                      prcostring += ''' ver="%s"''' % misc.to_xml(v, attrib=True)
                  if r:
                      prcostring += ''' rel="%s"''' % misc.to_xml(r, attrib=True)
@@ -150971,7 +151995,7 @@ index 5ef9951..15316c8 100644
          return msg
  
      def _dump_changelog(self, clog_limit):
-@@ -1299,7 +1355,8 @@ class YumHeaderPackage(YumAvailablePackage):
+@@ -1299,7 +1354,8 @@ class YumHeaderPackage(YumAvailablePackage):
          self.pkgid = self.hdr[rpm.RPMTAG_SHA1HEADER]
          if not self.pkgid:
              self.pkgid = "%s.%s" %(self.hdr['name'], self.hdr['buildtime'])
@@ -151696,7 +152720,7 @@ index 9b265f9..24a1f9e 100644
 +
 +        self.display.verify_txmbr(self.base, txmbr, count)
 diff --git a/yum/sqlitesack.py b/yum/sqlitesack.py
-index 8a6f6f3..19193ad 100644
+index 8a6f6f3..f6df93e 100644
 --- a/yum/sqlitesack.py
 +++ b/yum/sqlitesack.py
 @@ -406,7 +406,7 @@ class YumAvailablePackageSqlite(YumAvailablePackage, PackageObject, RpmBase):
@@ -151708,11 +152732,26 @@ index 8a6f6f3..19193ad 100644
                  pre = "1"
              prco_set = (_share_data(ob['name']), _share_data(ob['flags']),
                          (_share_data(ob['epoch']),
+@@ -917,8 +917,7 @@ class YumSqlitePackageSack(yumRepo.YumPackageSack):
+ 
+         # ultra simple optimization 
+         if misc.re_primary_filename(name):
+-            if not misc.re_glob(dirname): # is the dirname a glob?
+-                return self._search_primary_files(name)
++            return self._search_primary_files(name)
+         
+         if len(self.filelistsdb) == 0:
+             # grab repo object from primarydb and force filelists population in this sack using repo
 diff --git a/yum/yumRepo.py b/yum/yumRepo.py
-index e5e9ece..f645a1a 100644
+index e5e9ece..62e53f8 100644
 --- a/yum/yumRepo.py
 +++ b/yum/yumRepo.py
-@@ -24,6 +24,7 @@ urlparse.uses_fragment.append("media")
+@@ -20,10 +20,12 @@ import time
+ import types
+ import urlparse
+ urlparse.uses_fragment.append("media")
++import urllib
+ 
  import Errors
  from urlgrabber.grabber import URLGrabber
  from urlgrabber.grabber import default_grabber
@@ -151720,7 +152759,7 @@ index e5e9ece..f645a1a 100644
  import urlgrabber.mirror
  from urlgrabber.grabber import URLGrabError
  import repoMDObject
-@@ -35,6 +36,7 @@ import sqlitesack
+@@ -35,6 +37,7 @@ import sqlitesack
  from yum import config
  from yum import misc
  from yum import comps
@@ -151728,7 +152767,35 @@ index e5e9ece..f645a1a 100644
  from constants import *
  import metalink
  
-@@ -499,6 +501,7 @@ class YumRepository(Repository, config.RepoConf):
+@@ -434,22 +437,13 @@ class YumRepository(Repository, config.RepoConf):
+         if self.proxy not in empty:
+             proxy_string = '%s' % self.proxy
+             if self.proxy_username not in empty:
+-                proxy_parsed = urlparse.urlsplit(self.proxy, allow_fragments=0)
+-                proxy_proto = proxy_parsed[0]
+-                proxy_host = proxy_parsed[1]
+-                # http://foo:123 == ('http', 'foo:123', '', '', '')
+-                # don't turn that into: http://foo:123? - bug#328121
+-                if proxy_parsed[2] == '':
+-                    proxy_rest = ''
+-                else:
+-                    proxy_rest = proxy_parsed[2] + '?' + proxy_parsed[3]
+-                proxy_string = '%s://%s@%s%s' % (proxy_proto,
+-                        self.proxy_username, proxy_host, proxy_rest)
+ 
++                auth = urllib.quote(self.proxy_username)
+                 if self.proxy_password not in empty:
+-                    proxy_string = '%s://%s:%s@%s%s' % (proxy_proto,
+-                              self.proxy_username, self.proxy_password,
+-                              proxy_host, proxy_rest)
++                    auth += ':' + urllib.quote(self.proxy_password)
++
++                proto, rest = re.match('(\w+://)(.+)', proxy_string).groups()
++                proxy_string = '%s%s@%s' % (proto, auth, rest)
+ 
+         if proxy_string is not None:
+             self._proxy_dict['http'] = proxy_string
+@@ -499,6 +493,7 @@ class YumRepository(Repository, config.RepoConf):
                   'throttle': self.throttle,
                   'proxies': self.proxy_dict,
                   'timeout': self.timeout,
@@ -151736,7 +152803,7 @@ index e5e9ece..f645a1a 100644
                   'http_headers': tuple(self.__headersListFromDict(cache=cache)),
                   'ssl_verify_peer': self.sslverify,
                   'ssl_verify_host': self.sslverify,
-@@ -796,6 +799,16 @@ class YumRepository(Repository, config.RepoConf):
+@@ -796,6 +791,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,))
  
@@ -151753,7 +152820,7 @@ index e5e9ece..f645a1a 100644
          if url and scheme != "media":
              ugopts = self._default_grabopts(cache=cache)
              ug = URLGrabber(progress_obj = self.callback,
-@@ -1020,7 +1033,7 @@ class YumRepository(Repository, config.RepoConf):
+@@ -1020,7 +1025,7 @@ class YumRepository(Repository, config.RepoConf):
              if grab_can_fail:
                  return None
              raise Errors.RepoError, 'Error downloading file %s: %s' % (local, e)
@@ -151762,7 +152829,7 @@ index e5e9ece..f645a1a 100644
              misc.unlink_f(tfname)
              if grab_can_fail:
                  return None
-@@ -1260,6 +1273,9 @@ class YumRepository(Repository, config.RepoConf):
+@@ -1260,6 +1265,9 @@ class YumRepository(Repository, config.RepoConf):
          return True
  
      def _check_db_version(self, mdtype, repoXML=None):
@@ -151772,7 +152839,21 @@ index e5e9ece..f645a1a 100644
          if repoXML is None:
              repoXML = self.repoXML
          if mdtype in repoXML.repoData:
-@@ -1614,7 +1630,7 @@ class YumRepository(Repository, config.RepoConf):
+@@ -1451,12 +1459,7 @@ class YumRepository(Repository, config.RepoConf):
+     def _getRepoXML(self):
+         if self._repoXML:
+             return self._repoXML
+-        try:
+-            self._loadRepoXML(text=self)
+-        except Errors.RepoError, e:
+-            msg = ("Cannot retrieve repository metadata (repomd.xml) for repository: %s. "
+-                  "Please verify its path and try again" % self )
+-            raise Errors.RepoError, msg
++        self._loadRepoXML(text=self)
+         return self._repoXML
+ 
+ 
+@@ -1614,7 +1617,7 @@ class YumRepository(Repository, config.RepoConf):
                                    text=text,
                                    cache=self.http_caching == 'all',
                                    size=thisdata.size)
@@ -151782,7 +152863,7 @@ index e5e9ece..f645a1a 100644
                  return None
              raise
 diff --git a/yumcommands.py b/yumcommands.py
-index 4dcbea7..36b19b3 100644
+index 4dcbea7..31cb190 100644
 --- a/yumcommands.py
 +++ b/yumcommands.py
 @@ -43,16 +43,24 @@ def _err_mini_usage(base, basecmd):
@@ -151828,7 +152909,7 @@ index 4dcbea7..36b19b3 100644
      if len(extcmds) == 0:
          base.logger.critical(
                  _('Error: Need to pass a list of pkgs to %s') % basecmd)
-@@ -82,18 +98,44 @@ def checkPackageArg(base, basecmd, extcmds):
+@@ -82,24 +98,51 @@ def checkPackageArg(base, basecmd, extcmds):
          raise cli.CliError
  
  def checkItemArg(base, basecmd, extcmds):
@@ -151873,7 +152954,14 @@ index 4dcbea7..36b19b3 100644
      VALID_ARGS = ('headers', 'packages', 'metadata', 'dbcache', 'plugins',
                    'expire-cache', 'rpmdb', 'all')
  
-@@ -108,12 +150,14 @@ def checkCleanArg(base, basecmd, extcmds):
+     if len(extcmds) == 0:
+         base.logger.critical(_('Error: clean requires an option: %s') % (
+             ", ".join(VALID_ARGS)))
++        raise cli.CliError
+ 
+     for cmd in extcmds:
+         if cmd not in VALID_ARGS:
+@@ -108,12 +151,14 @@ def checkCleanArg(base, basecmd, extcmds):
              raise cli.CliError
  
  def checkShellArg(base, basecmd, extcmds):
@@ -151894,7 +152982,7 @@ index 4dcbea7..36b19b3 100644
      """
      if len(extcmds) == 0:
          base.verbose_logger.debug(_("No argument to shell"))
-@@ -133,10 +177,12 @@ def checkShellArg(base, basecmd, extcmds):
+@@ -133,10 +178,12 @@ def checkShellArg(base, basecmd, extcmds):
          raise cli.CliError
  
  def checkEnabledRepo(base, possible_local_files=[]):
@@ -151910,7 +152998,7 @@ index 4dcbea7..36b19b3 100644
      """
      if base.repos.listEnabled():
          return
-@@ -152,63 +198,145 @@ def checkEnabledRepo(base, possible_local_files=[]):
+@@ -152,63 +199,145 @@ def checkEnabledRepo(base, possible_local_files=[]):
      raise cli.CliError
  
  class YumCommand:
@@ -152066,7 +153154,7 @@ index 4dcbea7..36b19b3 100644
          self.doneCommand(base, _("Setting up Install Process"))
          try:
              return base.installPkgs(extcmds)
-@@ -216,21 +344,60 @@ class InstallCommand(YumCommand):
+@@ -216,21 +345,60 @@ class InstallCommand(YumCommand):
              return 1, [str(e)]
  
  class UpdateCommand(YumCommand):
@@ -152127,7 +153215,7 @@ index 4dcbea7..36b19b3 100644
          self.doneCommand(base, _("Setting up Update Process"))
          try:
              return base.updatePkgs(extcmds, update_to=(basecmd == 'update-to'))
-@@ -238,21 +405,59 @@ class UpdateCommand(YumCommand):
+@@ -238,21 +406,59 @@ class UpdateCommand(YumCommand):
              return 1, [str(e)]
  
  class DistroSyncCommand(YumCommand):
@@ -152187,12 +153275,12 @@ index 4dcbea7..36b19b3 100644
          self.doneCommand(base, _("Setting up Distribution Synchronization Process"))
          try:
              base.conf.obsoletes = 1
-@@ -289,16 +494,46 @@ def _list_cmd_calc_columns(base, ypl):
+@@ -289,16 +495,46 @@ def _list_cmd_calc_columns(base, ypl):
      return (-columns[0], -columns[1], -columns[2])
  
  class InfoCommand(YumCommand):
 +    """A class containing methods needed by the cli to execute the
-+    update command.
++    info command.
 +    """
 +
      def getNames(self):
@@ -152234,7 +153322,7 @@ index 4dcbea7..36b19b3 100644
          try:
              highlight = base.term.MODE['bold']
              ypl = base.returnPkgLists(extcmds, installed_available=highlight)
-@@ -389,35 +624,95 @@ class InfoCommand(YumCommand):
+@@ -389,35 +625,95 @@ class InfoCommand(YumCommand):
              return 0, []
  
      def needTs(self, base, basecmd, extcmds):
@@ -152330,7 +153418,7 @@ index 4dcbea7..36b19b3 100644
          self.doneCommand(base, _("Setting up Remove Process"))
          try:
              return base.erasePkgs(extcmds)
-@@ -425,9 +720,25 @@ class EraseCommand(YumCommand):
+@@ -425,9 +721,25 @@ class EraseCommand(YumCommand):
              return 1, [str(e)]
  
      def needTs(self, base, basecmd, extcmds):
@@ -152356,7 +153444,7 @@ index 4dcbea7..36b19b3 100644
          return True
  
   
-@@ -442,12 +753,25 @@ class GroupsCommand(YumCommand):
+@@ -442,12 +754,25 @@ class GroupsCommand(YumCommand):
                         'groupinfo'    : 'info'}
  
      def getNames(self):
@@ -152382,7 +153470,7 @@ index 4dcbea7..36b19b3 100644
          return _("Display, or use, the groups information")
      
      def _grp_setup_doCommand(self, base):
-@@ -479,6 +803,14 @@ class GroupsCommand(YumCommand):
+@@ -479,32 +804,68 @@ class GroupsCommand(YumCommand):
          return cmd, extcmds
  
      def doCheck(self, base, basecmd, extcmds):
@@ -152397,9 +153485,48 @@ index 4dcbea7..36b19b3 100644
          cmd, extcmds = self._grp_cmd(basecmd, extcmds)
  
          checkEnabledRepo(base)
-@@ -505,6 +837,19 @@ class GroupsCommand(YumCommand):
+ 
+         if cmd in ('install', 'remove',
+                    'mark-install', 'mark-remove',
+-                   'mark-members', 'info', 'mark-members-sync'):
++                   'info',
++                   'mark-packages', 'mark-packages-force', 'unmark-packages',
++                   'mark-packages-sync', 'mark-packages-sync-force'):
+             checkGroupArg(base, cmd, extcmds)
+ 
+         if cmd in ('install', 'remove', 'upgrade',
+                    'mark-install', 'mark-remove',
+-                   'mark-members', 'mark-members-sync'):
++                   'mark-packages', 'mark-packages-force', 'unmark-packages',
++                   'mark-packages-sync', 'mark-packages-sync-force'):
+             checkRootUID(base)
+ 
+         if cmd in ('install', 'upgrade'):
+             checkGPGKey(base)
+ 
+-        cmds = ('list', 'info', 'remove', 'install', 'upgrade', 'summary',
+-                'mark-install', 'mark-remove',
+-                'mark-members', 'mark-members-sync')
++        cmds = set(('list', 'info', 'remove', 'install', 'upgrade', 'summary'))
++        if base.conf.group_command == 'objects':
++            ocmds = ('mark-install', 'mark-remove',
++                     'mark-packages', 'mark-packages-force', 'unmark-packages',
++                     'mark-packages-sync', 'mark-packages-sync-force')
++            cmds.update(ocmds)
++
+         if cmd not in cmds:
+             base.logger.critical(_('Invalid groups sub-command, use: %s.'),
+                                  ", ".join(cmds))
              raise cli.CliError
  
++        if base.conf.group_command != 'objects':
++            pass
++        elif not os.path.exists(base.igroups.filename):
++            base.logger.critical(_("There is no installed groups file."))
++        elif not os.access(base.igroups.filename, os.R_OK):
++            base.logger.critical(_("You don't have access to the groups DB."))
++            raise cli.CliError
++
      def doCommand(self, base, basecmd, extcmds):
 +        """Execute this command.
 +
@@ -152417,7 +153544,66 @@ index 4dcbea7..36b19b3 100644
          cmd, extcmds = self._grp_cmd(basecmd, extcmds)
  
          self._grp_setup_doCommand(base)
-@@ -529,6 +874,14 @@ class GroupsCommand(YumCommand):
+@@ -524,39 +885,147 @@ 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)
++                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:
++                    return 1, ['No group matched']
++                grp = igrps[0]
++                force = cmd == 'mark-packages-force'
++                for pkg in base.rpmdb.returnPackages(patterns=extcmds[1:]):
++                    if not force and 'group_member' in pkg.yumdb_info:
++                        continue
++                    pkg.yumdb_info.group_member = grp.gid
++                    grp.pkg_names.add(pkg.name)
++                    base.igroups.changed = True
++                base.igroups.save()
++                return 0, ['Marked packages: ' + ','.join(extcmds[1:])]
++
++            if cmd == 'unmark-packages':
++                for pkg in base.rpmdb.returnPackages(patterns=extcmds):
++                    if 'group_member' in pkg.yumdb_info:
++                        del pkg.yumdb_info.group_member
++                return 0, ['UnMarked packages: ' + ','.join(extcmds)]
++
++            if cmd in ('mark-packages-sync', 'mark-packages-sync-force'):
++                igrps, grps = base._groupReturnGroups(extcmds,ignore_case=False)
++                if not igrps:
++                    return 1, ['No group matched']
++                force = cmd == 'mark-packages-sync-force'
++                for grp in igrps:
++                    for pkg in base.rpmdb.searchNames(grp.pkg_names):
++                        if not force and 'group_member' in pkg.yumdb_info:
++                            continue
++                        pkg.yumdb_info.group_member = grp.gid
++                if force:
++                    return 0, ['Marked packages-sync-force: '+','.join(extcmds)]
++                else:
++                    return 0, ['Marked packages-sync: ' + ','.join(extcmds)]
++
++            if cmd == 'mark-remove':
++                for strng in extcmds:
++                    for group in base.comps.return_groups(strng):
++                        base.igroups.del_group(group.groupid)
++                base.igroups.save()
++                return 0, ['Marked remove: ' + ','.join(extcmds)]
++
++
+         except yum.Errors.YumBaseError, e:
+             return 1, [str(e)]
  
  
      def needTs(self, base, basecmd, extcmds):
@@ -152432,7 +153618,9 @@ index 4dcbea7..36b19b3 100644
          cmd, extcmds = self._grp_cmd(basecmd, extcmds)
  
          if cmd in ('list', 'info', 'remove', 'summary'):
-@@ -536,27 +889,71 @@ class GroupsCommand(YumCommand):
+             return False
++        if cmd.startswith('mark') or cmd.startswith('unmark'):
++            return False
          return True
  
      def needTsRemove(self, base, basecmd, extcmds):
@@ -152505,7 +153693,18 @@ index 4dcbea7..36b19b3 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:
-@@ -582,44 +979,134 @@ class MakeCacheCommand(YumCommand):
+@@ -572,9 +1041,7 @@ class MakeCacheCommand(YumCommand):
+             # we can't remove them until *LoadRepo() can do:
+             # 1. Download a .sqlite.bz2 and convert to .sqlite
+             # 2. Download a .xml.gz and convert to .xml.gz.sqlite
+-            base.repos.populateSack(mdtype='metadata', cacheonly=1)
+-            base.repos.populateSack(mdtype='filelists', cacheonly=1)
+-            base.repos.populateSack(mdtype='otherdata', cacheonly=1)
++            base.repos.populateSack(mdtype='all', cacheonly=1)
+ 
+ 
+         except yum.Errors.YumBaseError, e:
+@@ -582,44 +1049,134 @@ class MakeCacheCommand(YumCommand):
          return 0, [_('Metadata Cache Created')]
  
      def needTs(self, base, basecmd, extcmds):
@@ -152640,12 +153839,12 @@ index 4dcbea7..36b19b3 100644
          base.logger.debug("Searching Packages: ")
          try:
              return base.provides(extcmds)
-@@ -627,19 +1114,56 @@ class ProvidesCommand(YumCommand):
+@@ -627,19 +1184,56 @@ class ProvidesCommand(YumCommand):
              return 1, [str(e)]
  
  class CheckUpdateCommand(YumCommand):
 +    """A class containing methods needed by the cli to execute the
-+    update command.
++    check-update command.
 +    """
 +
      def getNames(self):
@@ -152697,7 +153896,7 @@ index 4dcbea7..36b19b3 100644
          obscmds = ['obsoletes'] + extcmds
          base.extcmds.insert(0, 'updates')
          result = 0
-@@ -681,19 +1205,56 @@ class CheckUpdateCommand(YumCommand):
+@@ -681,19 +1275,56 @@ class CheckUpdateCommand(YumCommand):
              return result, []
  
  class SearchCommand(YumCommand):
@@ -152754,7 +153953,7 @@ index 4dcbea7..36b19b3 100644
          base.logger.debug(_("Searching Packages: "))
          try:
              return base.search(extcmds)
-@@ -701,24 +1262,70 @@ class SearchCommand(YumCommand):
+@@ -701,24 +1332,70 @@ class SearchCommand(YumCommand):
              return 1, [str(e)]
  
      def needTs(self, base, basecmd, extcmds):
@@ -152770,7 +153969,7 @@ index 4dcbea7..36b19b3 100644
  
  class UpgradeCommand(YumCommand):
 +    """A class containing methods needed by the cli to execute the
-+    update command.
++    upgrade command.
 +    """
 +
      def getNames(self):
@@ -152825,7 +154024,7 @@ index 4dcbea7..36b19b3 100644
          base.conf.obsoletes = 1
          self.doneCommand(base, _("Setting up Upgrade Process"))
          try:
-@@ -727,25 +1334,64 @@ class UpgradeCommand(YumCommand):
+@@ -727,25 +1404,64 @@ class UpgradeCommand(YumCommand):
              return 1, [str(e)]
  
  class LocalInstallCommand(YumCommand):
@@ -152890,7 +154089,7 @@ index 4dcbea7..36b19b3 100644
          self.doneCommand(base, _("Setting up Local Package Process"))
  
          updateonly = basecmd == 'localupdate'
-@@ -755,19 +1401,61 @@ class LocalInstallCommand(YumCommand):
+@@ -755,19 +1471,61 @@ class LocalInstallCommand(YumCommand):
              return 1, [str(e)]
  
      def needTs(self, base, basecmd, extcmds):
@@ -152953,7 +154152,7 @@ index 4dcbea7..36b19b3 100644
          base.logger.debug(_("Searching Packages for Dependency:"))
          try:
              return base.resolveDepCli(extcmds)
-@@ -775,19 +1463,56 @@ class ResolveDepCommand(YumCommand):
+@@ -775,19 +1533,56 @@ class ResolveDepCommand(YumCommand):
              return 1, [str(e)]
  
  class ShellCommand(YumCommand):
@@ -153010,7 +154209,7 @@ index 4dcbea7..36b19b3 100644
          self.doneCommand(base, _('Setting up Yum Shell'))
          try:
              return base.doShell()
-@@ -795,23 +1520,69 @@ class ShellCommand(YumCommand):
+@@ -795,23 +1590,69 @@ class ShellCommand(YumCommand):
              return 1, [str(e)]
  
      def needTs(self, base, basecmd, extcmds):
@@ -153080,7 +154279,7 @@ index 4dcbea7..36b19b3 100644
          self.doneCommand(base, _("Finding dependencies: "))
          try:
              return base.deplist(extcmds)
-@@ -820,17 +1591,46 @@ class DepListCommand(YumCommand):
+@@ -820,17 +1661,46 @@ class DepListCommand(YumCommand):
  
  
  class RepoListCommand(YumCommand):
@@ -153127,7 +154326,7 @@ index 4dcbea7..36b19b3 100644
          def _repo_size(repo):
              ret = 0
              for pkg in repo.sack.returnPackages():
-@@ -866,6 +1666,13 @@ class RepoListCommand(YumCommand):
+@@ -866,6 +1736,13 @@ class RepoListCommand(YumCommand):
              except yum.Errors.RepoError:
                  if verbose:
                      raise
@@ -153141,7 +154340,212 @@ index 4dcbea7..36b19b3 100644
  
          repos = base.repos.repos.values()
          repos.sort()
-@@ -1088,21 +1895,54 @@ class RepoListCommand(YumCommand):
+@@ -924,111 +1801,108 @@ class RepoListCommand(YumCommand):
+                 ui_enabled = dhibeg + _('disabled') + hiend
+                 ui_endis_wid = utf8_width(_('disabled'))
+ 
+-            if True: # Here to make patch smaller, TODO: rm
+-                if not verbose:
+-                    rid = str(repo)
+-                    if enabled and repo.metalink:
+-                        mdts = repo.metalink_data.repomd.timestamp
+-                        if mdts > repo.repoXML.timestamp:
+-                            rid = '*' + rid
+-                    cols.append((rid, repo.name,
+-                                 (ui_enabled, ui_endis_wid), ui_num))
++            if not verbose:
++                rid = str(repo)
++                if enabled and repo.metalink:
++                    mdts = repo.metalink_data.repomd.timestamp
++                    if mdts > repo.repoXML.timestamp:
++                        rid = '*' + rid
++                cols.append((rid, repo.name,
++                             (ui_enabled, ui_endis_wid), ui_num))
++            else:
++                if enabled:
++                    md = repo.repoXML
+                 else:
++                    md = None
++                out = [base.fmtKeyValFill(_("Repo-id      : "), repo),
++                       base.fmtKeyValFill(_("Repo-name    : "), repo.name)]
++
++                if force_show or extcmds:
++                    out += [base.fmtKeyValFill(_("Repo-status  : "),
++                                               ui_enabled)]
++                if md and md.revision is not None:
++                    out += [base.fmtKeyValFill(_("Repo-revision: "),
++                                               md.revision)]
++                if md and md.tags['content']:
++                    tags = md.tags['content']
++                    out += [base.fmtKeyValFill(_("Repo-tags    : "),
++                                               ", ".join(sorted(tags)))]
++
++                if md and md.tags['distro']:
++                    for distro in sorted(md.tags['distro']):
++                        tags = md.tags['distro'][distro]
++                        out += [base.fmtKeyValFill(_("Repo-distro-tags: "),
++                                                   "[%s]: %s" % (distro,
++                                                   ", ".join(sorted(tags))))]
++
++                if md:
++                    out += [base.fmtKeyValFill(_("Repo-updated : "),
++                                               time.ctime(md.timestamp)),
++                            base.fmtKeyValFill(_("Repo-pkgs    : "),ui_num),
++                            base.fmtKeyValFill(_("Repo-size    : "),ui_size)]
++
++                if hasattr(repo, '_orig_baseurl'):
++                    baseurls = repo._orig_baseurl
++                else:
++                    baseurls = repo.baseurl
++                if baseurls:
++                    out += [base.fmtKeyValFill(_("Repo-baseurl : "),
++                                               ", ".join(baseurls))]
++
++                if enabled:
++                    # This needs to be here due to the mirrorlists are
++                    # metalinks hack.
++                    repo.urls
++                if repo.metalink:
++                    out += [base.fmtKeyValFill(_("Repo-metalink: "),
++                                               repo.metalink)]
+                     if enabled:
+-                        md = repo.repoXML
+-                    else:
+-                        md = None
+-                    out = [base.fmtKeyValFill(_("Repo-id      : "), repo),
+-                           base.fmtKeyValFill(_("Repo-name    : "), repo.name)]
+-
+-                    if force_show or extcmds:
+-                        out += [base.fmtKeyValFill(_("Repo-status  : "),
+-                                                   ui_enabled)]
+-                    if md and md.revision is not None:
+-                        out += [base.fmtKeyValFill(_("Repo-revision: "),
+-                                                   md.revision)]
+-                    if md and md.tags['content']:
+-                        tags = md.tags['content']
+-                        out += [base.fmtKeyValFill(_("Repo-tags    : "),
+-                                                   ", ".join(sorted(tags)))]
+-
+-                    if md and md.tags['distro']:
+-                        for distro in sorted(md.tags['distro']):
+-                            tags = md.tags['distro'][distro]
+-                            out += [base.fmtKeyValFill(_("Repo-distro-tags: "),
+-                                                       "[%s]: %s" % (distro,
+-                                                       ", ".join(sorted(tags))))]
+-
+-                    if md:
+-                        out += [base.fmtKeyValFill(_("Repo-updated : "),
+-                                                   time.ctime(md.timestamp)),
+-                                base.fmtKeyValFill(_("Repo-pkgs    : "),ui_num),
+-                                base.fmtKeyValFill(_("Repo-size    : "),ui_size)]
+-
+-                    if hasattr(repo, '_orig_baseurl'):
+-                        baseurls = repo._orig_baseurl
+-                    else:
+-                        baseurls = repo.baseurl
+-                    if baseurls:
+-                        out += [base.fmtKeyValFill(_("Repo-baseurl : "),
+-                                                   ", ".join(baseurls))]
+-
+-                    if enabled:
+-                        #  This needs to be here due to the mirrorlists are
+-                        # metalinks hack.
+-                        repo.urls
+-                    if repo.metalink:
+-                        out += [base.fmtKeyValFill(_("Repo-metalink: "),
+-                                                   repo.metalink)]
+-                        if enabled:
+-                            ts = repo.metalink_data.repomd.timestamp
+-                            out += [base.fmtKeyValFill(_("  Updated    : "),
+-                                                       time.ctime(ts))]
+-                    elif repo.mirrorlist:
+-                        out += [base.fmtKeyValFill(_("Repo-mirrors : "),
+-                                                   repo.mirrorlist)]
+-                    if enabled and repo.urls and not baseurls:
+-                        url = repo.urls[0]
+-                        if len(repo.urls) > 1:
+-                            url += ' (%d more)' % (len(repo.urls) - 1)
+-                        out += [base.fmtKeyValFill(_("Repo-baseurl : "),
+-                                                   url)]
+-
+-                    if not os.path.exists(repo.metadata_cookie):
+-                        last = _("Unknown")
+-                    else:
+-                        last = os.stat(repo.metadata_cookie).st_mtime
+-                        last = time.ctime(last)
++                        ts = repo.metalink_data.repomd.timestamp
++                        out += [base.fmtKeyValFill(_("  Updated    : "),
++                                                   time.ctime(ts))]
++                elif repo.mirrorlist:
++                    out += [base.fmtKeyValFill(_("Repo-mirrors : "),
++                                               repo.mirrorlist)]
++                if enabled and repo.urls and not baseurls:
++                    url = repo.urls[0]
++                    if len(repo.urls) > 1:
++                        url += ' (%d more)' % (len(repo.urls) - 1)
++                    out += [base.fmtKeyValFill(_("Repo-baseurl : "), url)]
++
++                if not os.path.exists(repo.metadata_cookie):
++                    last = _("Unknown")
++                else:
++                    last = os.stat(repo.metadata_cookie).st_mtime
++                    last = time.ctime(last)
+ 
+-                    if repo.metadata_expire <= -1:
+-                        num = _("Never (last: %s)") % last
+-                    elif not repo.metadata_expire:
+-                        num = _("Instant (last: %s)") % last
+-                    else:
+-                        num = _num2ui_num(repo.metadata_expire)
+-                        num = _("%s second(s) (last: %s)") % (num, last)
++                if repo.metadata_expire <= -1:
++                    num = _("Never (last: %s)") % last
++                elif not repo.metadata_expire:
++                    num = _("Instant (last: %s)") % last
++                else:
++                    num = _num2ui_num(repo.metadata_expire)
++                    num = _("%s second(s) (last: %s)") % (num, last)
+ 
+-                    out += [base.fmtKeyValFill(_("Repo-expire  : "), num)]
++                out += [base.fmtKeyValFill(_("Repo-expire  : "), num)]
+ 
+-                    if repo.exclude:
+-                        out += [base.fmtKeyValFill(_("Repo-exclude : "),
+-                                                   ", ".join(repo.exclude))]
++                if repo.exclude:
++                    out += [base.fmtKeyValFill(_("Repo-exclude : "),
++                                               ", ".join(repo.exclude))]
+ 
+-                    if repo.includepkgs:
+-                        out += [base.fmtKeyValFill(_("Repo-include : "),
+-                                                   ", ".join(repo.includepkgs))]
++                if repo.includepkgs:
++                    out += [base.fmtKeyValFill(_("Repo-include : "),
++                                               ", ".join(repo.includepkgs))]
+ 
+-                    if ui_excludes_num:
+-                        out += [base.fmtKeyValFill(_("Repo-excluded: "),
+-                                                   ui_excludes_num)]
++                if ui_excludes_num:
++                    out += [base.fmtKeyValFill(_("Repo-excluded: "),
++                                               ui_excludes_num)]
+ 
+-                    if repo.repofile:
+-                        out += [base.fmtKeyValFill(_("Repo-filename: "),
+-                                                   repo.repofile)]
++                if repo.repofile:
++                    out += [base.fmtKeyValFill(_("Repo-filename: "),
++                                               repo.repofile)]
+ 
+-                    base.verbose_logger.log(logginglevels.DEBUG_3,
+-                                            "%s\n",
+-                                            "\n".join(map(misc.to_unicode, out)))
++                base.verbose_logger.log(logginglevels.DEBUG_3, "%s\n",
++                                        "\n".join(map(misc.to_unicode, out)))
+ 
+         if not verbose and cols:
+             #  Work out the first (id) and last (enabled/disalbed/count),
+@@ -1088,21 +1962,54 @@ class RepoListCommand(YumCommand):
          return 0, ['repolist: ' +to_unicode(locale.format("%d", tot_num, True))]
  
      def needTs(self, base, basecmd, extcmds):
@@ -153196,7 +154600,7 @@ index 4dcbea7..36b19b3 100644
          if len(extcmds) == 0:
              base.usage()
              raise cli.CliError
-@@ -1147,28 +1987,85 @@ class HelpCommand(YumCommand):
+@@ -1147,28 +2054,85 @@ class HelpCommand(YumCommand):
          return help_output
  
      def doCommand(self, base, basecmd, extcmds):
@@ -153282,7 +154686,7 @@ index 4dcbea7..36b19b3 100644
          self.doneCommand(base, _("Setting up Reinstall Process"))
          try:
              return base.reinstallPkgs(extcmds)
-@@ -1177,25 +2074,73 @@ class ReInstallCommand(YumCommand):
+@@ -1177,25 +2141,73 @@ class ReInstallCommand(YumCommand):
              return 1, [to_unicode(e)]
  
      def getSummary(self):
@@ -153356,7 +154760,7 @@ index 4dcbea7..36b19b3 100644
          self.doneCommand(base, _("Setting up Downgrade Process"))
          try:
              return base.downgradePkgs(extcmds)
-@@ -1203,23 +2148,65 @@ class DowngradeCommand(YumCommand):
+@@ -1203,23 +2215,65 @@ class DowngradeCommand(YumCommand):
              return 1, [str(e)]
  
      def getSummary(self):
@@ -153422,7 +154826,7 @@ index 4dcbea7..36b19b3 100644
          vcmd = 'installed'
          if extcmds:
              vcmd = extcmds[0]
-@@ -1344,6 +2331,14 @@ class VersionCommand(YumCommand):
+@@ -1344,6 +2398,14 @@ class VersionCommand(YumCommand):
          return 0, ['version']
  
      def needTs(self, base, basecmd, extcmds):
@@ -153437,7 +154841,7 @@ index 4dcbea7..36b19b3 100644
          vcmd = 'installed'
          if extcmds:
              vcmd = extcmds[0]
-@@ -1354,23 +2349,62 @@ class VersionCommand(YumCommand):
+@@ -1354,23 +2416,62 @@ class VersionCommand(YumCommand):
  
  
  class HistoryCommand(YumCommand):
@@ -153501,7 +154905,7 @@ index 4dcbea7..36b19b3 100644
              return 2, ["Repeating transaction %u" % (old.tid,)]
  
      def _hcmd_undo(self, base, extcmds):
-@@ -1426,12 +2460,54 @@ class HistoryCommand(YumCommand):
+@@ -1426,12 +2527,54 @@ class HistoryCommand(YumCommand):
      def _hcmd_new(self, base, extcmds):
          base.history._create_db_file()
  
@@ -153557,7 +154961,7 @@ index 4dcbea7..36b19b3 100644
          if extcmds and extcmds[0] not in cmds:
              base.logger.critical(_('Invalid history sub-command, use: %s.'),
                                   ", ".join(cmds))
-@@ -1444,6 +2520,19 @@ class HistoryCommand(YumCommand):
+@@ -1444,6 +2587,19 @@ class HistoryCommand(YumCommand):
              raise cli.CliError
  
      def doCommand(self, base, basecmd, extcmds):
@@ -153577,7 +154981,7 @@ index 4dcbea7..36b19b3 100644
          vcmd = 'list'
          if extcmds:
              vcmd = extcmds[0]
-@@ -1468,12 +2557,26 @@ class HistoryCommand(YumCommand):
+@@ -1468,12 +2624,26 @@ class HistoryCommand(YumCommand):
              ret = self._hcmd_rollback(base, extcmds)
          elif vcmd == 'new':
              ret = self._hcmd_new(base, extcmds)
@@ -153604,7 +155008,7 @@ index 4dcbea7..36b19b3 100644
          vcmd = 'list'
          if extcmds:
              vcmd = extcmds[0]
-@@ -1481,16 +2584,46 @@ class HistoryCommand(YumCommand):
+@@ -1481,16 +2651,46 @@ class HistoryCommand(YumCommand):
  
  
  class CheckRpmdbCommand(YumCommand):
@@ -153651,7 +155055,7 @@ index 4dcbea7..36b19b3 100644
          chkcmd = 'all'
          if extcmds:
              chkcmd = extcmds
-@@ -1505,19 +2638,57 @@ class CheckRpmdbCommand(YumCommand):
+@@ -1505,19 +2705,57 @@ class CheckRpmdbCommand(YumCommand):
          return rc, ['%s %s' % (basecmd, chkcmd)]
  
      def needTs(self, base, basecmd, extcmds):
@@ -153709,7 +155113,7 @@ index 4dcbea7..36b19b3 100644
          if not extcmds:
              base.logger.critical(_("No saved transaction file specified."))
              raise cli.CliError
-@@ -1533,5 +2704,13 @@ class LoadTransactionCommand(YumCommand):
+@@ -1533,5 +2771,13 @@ class LoadTransactionCommand(YumCommand):
  
  
      def needTs(self, base, basecmd, extcmds):
@@ -153724,7 +155128,7 @@ index 4dcbea7..36b19b3 100644
          return True
  
 diff --git a/yummain.py b/yummain.py
-index 9f79f4f..12582d2 100755
+index 9f79f4f..e1a9702 100755
 --- a/yummain.py
 +++ b/yummain.py
 @@ -29,13 +29,13 @@ from yum import Errors
@@ -153744,7 +155148,7 @@ index 9f79f4f..12582d2 100755
  
      yum.misc.setup_locale(override_time=True)
  
-@@ -102,15 +102,15 @@ def main(args):
+@@ -102,15 +102,21 @@ def main(args):
          return exFatal(e)
  
      # Try to open the current directory to see if we have 
@@ -153760,10 +155164,16 @@ index 9f79f4f..12582d2 100755
      else:
 -        close(f)
 +        f.close()
++    try:
++        os.getcwd()
++    except OSError, e:
++        if e.errno == errno.ENOENT:
++            logger.critical(_('No getcwd() access in current directory, moving to /'))
++            os.chdir("/")
  
      lockerr = ""
      while True:
-@@ -120,16 +120,16 @@ def main(args):
+@@ -120,16 +126,16 @@ def main(args):
              if exception2msg(e) != lockerr:
                  lockerr = exception2msg(e)
                  logger.critical(lockerr)
@@ -153785,7 +155195,7 @@ index 9f79f4f..12582d2 100755
              else:
                  logger.critical(_("Another app is currently holding the yum lock; exiting as configured by exit_on_lock"))
                  return 1
-@@ -238,9 +238,15 @@ def main(args):
+@@ -238,9 +244,15 @@ def main(args):
          rpmdb_warn_checks()
          return_code = result
          if base._ts_save_file:
@@ -153802,7 +155212,7 @@ index 9f79f4f..12582d2 100755
      else:
          verbose_logger.log(logginglevels.INFO_2, _('Complete!'))
  
-@@ -248,6 +254,11 @@ def main(args):
+@@ -248,6 +260,11 @@ def main(args):
      return return_code
  
  def hotshot(func, *args, **kwargs):
@@ -153814,7 +155224,7 @@ index 9f79f4f..12582d2 100755
      import hotshot.stats
      fn = os.path.expanduser("~/yum.prof")
      prof = hotshot.Profile(fn)
-@@ -257,6 +268,11 @@ def hotshot(func, *args, **kwargs):
+@@ -257,6 +274,11 @@ def hotshot(func, *args, **kwargs):
      return rc
  
  def cprof(func, *args, **kwargs):
@@ -153826,7 +155236,7 @@ index 9f79f4f..12582d2 100755
      import cProfile, pstats
      fn = os.path.expanduser("~/yum.prof")
      prof = cProfile.Profile()
-@@ -266,6 +282,10 @@ def cprof(func, *args, **kwargs):
+@@ -266,6 +288,10 @@ def cprof(func, *args, **kwargs):
      return rc
  
  def print_stats(stats):
@@ -153837,7 +155247,7 @@ index 9f79f4f..12582d2 100755
      stats.strip_dirs()
      stats.sort_stats('time', 'calls')
      stats.print_stats(20)
-@@ -273,7 +293,14 @@ def print_stats(stats):
+@@ -273,7 +299,14 @@ def print_stats(stats):
      stats.print_stats(40)
  
  def user_main(args, exit_code=False):
diff --git a/yum-distro-configs.patch b/yum-distro-configs.patch
index 28b7856..a4608a2 100644
--- a/yum-distro-configs.patch
+++ b/yum-distro-configs.patch
@@ -1,17 +1,19 @@
 diff -ru yum-3.4.3-orig/yum/config.py yum-3.4.3/yum/config.py
 --- yum-3.4.3-orig/yum/config.py	2011-12-02 15:45:41.617448597 -0500
 +++ yum-3.4.3/yum/config.py	2011-12-02 15:46:20.576285275 -0500
-@@ -45,9 +45,9 @@
+@@ -45,10 +45,10 @@
  # Alter/patch these to change the default checking...
  __pkgs_gpgcheck_default__ = False
  __repo_gpgcheck_default__ = False
 -__main_multilib_policy_default__ = 'all'
 -__main_failovermethod_default__ = 'roundrobin'
 -__main_installonly_limit_default__ = 0
+-__group_command_default__ = 'compat'
 +__main_multilib_policy_default__ = 'best'
 +__main_failovermethod_default__ = 'priority'
 +__main_installonly_limit_default__ = 3
- 
++__group_command_default__ = 'objects'
+
  class Option(object):
      """
 Only in yum-3.4.3/yum: config.py~
diff --git a/yum.spec b/yum.spec
index abc7153..1e133e0 100644
--- a/yum.spec
+++ b/yum.spec
@@ -18,7 +18,7 @@
 Summary: RPM package installer/updater/manager
 Name: yum
 Version: 3.4.3
-Release: 16%{?dist}
+Release: 17%{?dist}
 License: GPLv2+
 Group: System Environment/Base
 Source0: http://yum.baseurl.org/download/3.4/%{name}-%{version}.tar.gz
@@ -311,6 +311,11 @@ exit 0
 %endif
 
 %changelog
+* Fri Jan 20 2012 James Antill <james at fedoraproject.org> - 3.4.3-17
+- update to latest HEAD
+- Added group_command, and changed to groups as objects by default.
+- Minor updates.
+
 * Tue Dec 13 2011 James Antill <james at fedoraproject.org> - 3.4.3-16
 - update to latest HEAD
 - Have users always use their own dirs.


More information about the scm-commits mailing list