[yum] update to latest HEAD.

James Antill james at fedoraproject.org
Wed Jan 16 19:46:19 UTC 2013


commit 955dba3d4aedaaa43e7bdcf1038cffc48c530a28
Author: James Antill <james at and.org>
Date:   Wed Jan 16 14:46:08 2013 -0500

    update to latest HEAD.
    
    - Add repo-pkgs.
    - Add swap.
    - Add remove_leaf_only and repopkgremove_leaf_only.
    - Add metadata_expire_filter.

 yum-HEAD.patch | 1248 ++++++++++++++++++++++++++++++++++++++++++++------------
 yum.spec       |   11 +-
 2 files changed, 994 insertions(+), 265 deletions(-)
---
diff --git a/yum-HEAD.patch b/yum-HEAD.patch
index 8617fb0..7518010 100644
--- a/yum-HEAD.patch
+++ b/yum-HEAD.patch
@@ -96,7 +96,7 @@ index 2f6154e..2e5a052 100644
 diff --git a/cli.py b/cli.py
 old mode 100644
 new mode 100755
-index 6056d38..9225d5a
+index 6056d38..01024cd
 --- a/cli.py
 +++ b/cli.py
 @@ -25,7 +25,7 @@ import sys
@@ -151,8 +151,12 @@ index 6056d38..9225d5a
         
      def __init__(self):
          # handle sigquit early on
-@@ -106,15 +109,24 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -104,17 +107,28 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+         self.registerCommand(yumcommands.CheckRpmdbCommand())
+         self.registerCommand(yumcommands.DistroSyncCommand())
          self.registerCommand(yumcommands.LoadTransactionCommand())
++        self.registerCommand(yumcommands.SwapCommand())
++        self.registerCommand(yumcommands.RepoPkgsCommand())
  
      def registerCommand(self, command):
 +        """Register a :class:`yumcommands.YumCommand` so that it can be called by
@@ -179,7 +183,7 @@ index 6056d38..9225d5a
          if self._repos and thisrepo is None:
              return self._repos
              
-@@ -163,8 +175,18 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -163,8 +177,18 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
          mainopts = yum.misc.GenericHolder()
          mainopts.items = []
  
@@ -199,7 +203,7 @@ index 6056d38..9225d5a
              period = k.find('.') 
              if period != -1:
                  repo = k[:period]
-@@ -180,13 +202,15 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -180,13 +204,15 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
          
          self.main_setopts = mainopts
          self.repo_setopts = repoopts
@@ -220,7 +224,7 @@ index 6056d38..9225d5a
          self.optparser = YumOptionParser(base=self, usage=self._makeUsage())
          
          # Parse only command line options that affect basic yum setup
-@@ -199,7 +223,7 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -199,7 +225,7 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
              opts.verbose = False
  
          # go through all the setopts and set the global ones
@@ -229,7 +233,7 @@ index 6056d38..9225d5a
          
          if self.main_setopts:
              for opt in self.main_setopts.items:
-@@ -229,6 +253,12 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -229,6 +255,12 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
              pc.releasever = opts.releasever
              self.conf
              
@@ -242,7 +246,7 @@ index 6056d38..9225d5a
              # now set  all the non-first-start opts from main from our setopts
              if self.main_setopts:
                  for opt in self.main_setopts.items:
-@@ -240,6 +270,10 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -240,6 +272,10 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
          except yum.Errors.ConfigError, e:
              self.logger.critical(_('Config Error: %s'), e)
              sys.exit(1)
@@ -253,7 +257,7 @@ index 6056d38..9225d5a
          except ValueError, e:
              self.logger.critical(_('Options Error: %s'), e)
              sys.exit(1)
-@@ -290,9 +324,9 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -290,9 +326,9 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
                                     self.term.MODE['normal'])
                  print _("  Installed: %s-%s at %s") %(name, ver,
                                                     sm_ui_time(pkg.installtime))
@@ -265,7 +269,7 @@ index 6056d38..9225d5a
                                                      sm_ui_date(pkg.committime))
              sys.exit(0)
  
-@@ -318,9 +352,11 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -318,9 +354,11 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
          time.sleep(sleeptime)
          
      def parseCommands(self):
@@ -280,7 +284,7 @@ index 6056d38..9225d5a
          self.verbose_logger.debug('Yum Version: %s', yum.__version__)
          self.verbose_logger.log(yum.logginglevels.DEBUG_4,
                                  'COMMAND: %s', self.cmdstring)
-@@ -365,7 +401,11 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -365,7 +403,11 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
          self.history.write_addon_data('shell-cmds', data)
  
      def doShell(self):
@@ -293,7 +297,7 @@ index 6056d38..9225d5a
  
          yumshell = shell.YumShell(base=self)
  
-@@ -382,8 +422,12 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -382,8 +424,12 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
          return yumshell.result, yumshell.resultmsgs
  
      def errorSummary(self, errstring):
@@ -308,7 +312,7 @@ index 6056d38..9225d5a
          summary = ''
          # do disk space report first
          p = re.compile('needs (\d+)MB on the (\S+) filesystem')
-@@ -407,17 +451,45 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -407,17 +453,45 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
          return summary
  
  
@@ -364,12 +368,18 @@ index 6056d38..9225d5a
          # at this point we know the args are valid - we don't know their meaning
          # but we know we're not being sent garbage
          
-@@ -435,14 +507,18 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -435,14 +509,24 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
              try:
                  self._getTs(needTsRemove)
              except yum.Errors.YumBaseError, e:
 -                return 1, [str(e)]
 +                return 1, [exception2msg(e)]
++
++        cacheReq = 'write'
++        if hasattr(cmd, 'cacheRequirement'):
++            cacheReq = cmd.cacheRequirement(self, self.basecmd, self.extcmds)
++        for repo in self.repos.listEnabled():
++            repo._metadata_cache_req = cacheReq
  
          return self.yum_cli_commands[self.basecmd].doCommand(self, self.basecmd, self.extcmds)
  
@@ -387,7 +397,7 @@ index 6056d38..9225d5a
          # just make sure there's not, well, nothing to do
          if len(self.tsInfo) == 0:
              self.verbose_logger.info(_('Trying to run the transaction but nothing to do. Exiting.'))
-@@ -453,7 +529,7 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -453,7 +537,7 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
          lsts = self.listTransaction()
          if self.verbose_logger.isEnabledFor(yum.logginglevels.INFO_1):
              self.verbose_logger.log(yum.logginglevels.INFO_1, lsts)
@@ -396,7 +406,7 @@ index 6056d38..9225d5a
              #  If we are in quiet, and assumeyes isn't on we want to output
              # at least the transaction list anyway.
              self.logger.warn(lsts)
-@@ -463,7 +539,6 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -463,7 +547,6 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
          rmpkgs = []
          stuff_to_download = False
          install_only = True
@@ -404,7 +414,7 @@ index 6056d38..9225d5a
          for txmbr in self.tsInfo.getMembers():
              if txmbr.ts_state not in ('i', 'u'):
                  install_only = False
-@@ -471,7 +546,6 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -471,7 +554,6 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
                  if po:
                      rmpkgs.append(po)
              else:
@@ -412,7 +422,7 @@ index 6056d38..9225d5a
                  stuff_to_download = True
                  po = txmbr.po
                  if po:
-@@ -489,9 +563,20 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -489,9 +571,20 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
          else:
              self.reportDownloadSize(downloadpkgs, install_only)
          
@@ -434,7 +444,7 @@ index 6056d38..9225d5a
                  self.verbose_logger.info(_('Exiting on user Command'))
                  return -1
  
-@@ -609,12 +694,14 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -609,12 +702,14 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
          return resultobject.return_code
          
      def gpgsigcheck(self, pkgs):
@@ -454,7 +464,7 @@ index 6056d38..9225d5a
          for po in pkgs:
              result, errmsg = self.sigCheckPkg(po)
  
-@@ -623,7 +710,8 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -623,7 +718,8 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
                  continue            
  
              elif result == 1:
@@ -464,7 +474,7 @@ index 6056d38..9225d5a
                      raise yum.Errors.YumBaseError, \
                              _('Refusing to automatically import keys when running ' \
                              'unattended.\nUse "-y" to override.')
-@@ -691,12 +779,62 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -691,12 +787,62 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
                                       ", ".join(matches))
              self.verbose_logger.log(yum.logginglevels.INFO_2, msg)
  
@@ -513,7 +523,7 @@ index 6056d38..9225d5a
 +
 +        return ret
 +
-+    def installPkgs(self, userlist, basecmd='install'):
++    def installPkgs(self, userlist, basecmd='install', repoid=None):
 +        """Attempt to take the user specified list of packages or
 +        wildcards and install them, or if they are installed, update
 +        them to a newer version. If a complete version number is
@@ -533,7 +543,7 @@ index 6056d38..9225d5a
          # get the list of available packages
          # iterate over the user's list
          # add packages to Transaction holding class if they match.
-@@ -710,11 +848,36 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -710,11 +856,36 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
          for arg in userlist:
              if (arg.endswith('.rpm') and (yum.misc.re_remote_url(arg) or
                                            os.path.exists(arg))):
@@ -572,7 +582,7 @@ index 6056d38..9225d5a
              except yum.Errors.InstallError:
                  self.verbose_logger.log(yum.logginglevels.INFO_2,
                                          _('No package %s%s%s available.'),
-@@ -723,6 +886,7 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -723,6 +894,7 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
                  self._maybeYouMeant(arg)
              else:
                  done = True
@@ -580,7 +590,7 @@ index 6056d38..9225d5a
          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 +896,27 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -732,9 +904,27 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
          return 0, [_('Nothing to do')]
          
      def updatePkgs(self, userlist, quiet=0, update_to=False):
@@ -611,7 +621,7 @@ index 6056d38..9225d5a
          # 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 +927,18 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -745,21 +935,18 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
  
          else:
              # go through the userlist - look for items that are local rpms. If we find them
@@ -642,7 +652,7 @@ index 6056d38..9225d5a
  
          if len(self.tsInfo) > oldcount:
              change = len(self.tsInfo) - oldcount
-@@ -770,9 +949,24 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -770,9 +957,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):
@@ -670,7 +680,7 @@ index 6056d38..9225d5a
  
          level = 'diff'
          if userlist and userlist[0] in ('full', 'diff', 'different'):
-@@ -831,6 +1025,7 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -831,6 +1033,7 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
                          continue
  
                      nayi = napkg.yumdb_info
@@ -678,7 +688,7 @@ index 6056d38..9225d5a
                      for apkg in self.pkgSack.searchPkgTuple(napkg.pkgtup):
                          if ('checksum_type' in nayi and
                              'checksum_data' in nayi and
-@@ -865,15 +1060,54 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -865,15 +1068,54 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
          else:
              return 0, [_('No Packages marked for Distribution Synchronization')]
  
@@ -739,7 +749,7 @@ index 6056d38..9225d5a
              if not rms:
                  self._checkMaybeYouMeant(arg, always_output=False, rpmdb_only=True)
              all_rms.extend(rms)
-@@ -884,12 +1118,24 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -884,12 +1126,24 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
              return 0, [_('No Packages marked for removal')]
      
      def downgradePkgs(self, userlist):
@@ -767,7 +777,7 @@ index 6056d38..9225d5a
          for arg in userlist:
              if (arg.endswith('.rpm') and (yum.misc.re_remote_url(arg) or
                                            os.path.exists(arg))):
-@@ -905,26 +1151,44 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -905,26 +1159,44 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
                                          self.term.MODE['bold'], arg,
                                          self.term.MODE['normal'])
                  self._maybeYouMeant(arg)
@@ -816,7 +826,7 @@ index 6056d38..9225d5a
              except yum.Errors.ReinstallRemoveError:
                  self._checkMaybeYouMeant(arg, always_output=False)
              except yum.Errors.ReinstallInstallError, e:
-@@ -940,15 +1204,31 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -940,15 +1212,31 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
              except yum.Errors.ReinstallError, e:
                  assert False, "Shouldn't happen, but just in case"
                  self.verbose_logger.log(yum.logginglevels.INFO_2, e)
@@ -851,7 +861,7 @@ index 6056d38..9225d5a
          # 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 +1252,25 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -972,20 +1260,25 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
          return 0, [_('Nothing to do')]
  
      def returnPkgLists(self, extcmds, installed_available=False):
@@ -891,7 +901,7 @@ index 6056d38..9225d5a
          special = ['available', 'installed', 'all', 'extras', 'updates', 'recent',
                     'obsoletes']
          
-@@ -1017,8 +1302,25 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -1017,8 +1310,25 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
          return ypl
  
      def search(self, args):
@@ -919,7 +929,7 @@ index 6056d38..9225d5a
          
          # call the yum module search function with lists of tags to search
          # and what to search for
-@@ -1108,9 +1410,20 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -1108,9 +1418,20 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
          return 0, matching
  
      def deplist(self, args):
@@ -942,7 +952,7 @@ index 6056d38..9225d5a
          pkgs = []
          for arg in args:
              if (arg.endswith('.rpm') and (yum.misc.re_remote_url(arg) or
-@@ -1131,10 +1444,19 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -1131,10 +1452,19 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
          return 0, []
  
      def provides(self, args):
@@ -966,7 +976,7 @@ index 6056d38..9225d5a
          old_sdup = self.conf.showdupesfromrepos
          # For output, as searchPackageProvides() is always in showdups mode
          self.conf.showdupesfromrepos = True
-@@ -1147,6 +1469,8 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -1147,6 +1477,8 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
              paths = set(sys.path + os.environ['PATH'].split(':'))
              nargs = []
              for arg in args:
@@ -975,7 +985,7 @@ index 6056d38..9225d5a
                  if yum.misc.re_filename(arg) or yum.misc.re_glob(arg):
                      continue
                  for path in paths:
-@@ -1163,20 +1487,77 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -1163,20 +1495,77 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
          return 0, []
      
      def resolveDepCli(self, args):
@@ -1058,7 +1068,7 @@ index 6056d38..9225d5a
          hdrcode = pkgcode = xmlcode = dbcode = expccode = 0
          pkgresults = hdrresults = xmlresults = dbresults = expcresults = []
          msg = self.fmtKeyValFill(_('Cleaning repos: '), 
-@@ -1228,130 +1609,257 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -1228,130 +1617,257 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
          return code, []
  
      def returnGroupLists(self, userlist):
@@ -1396,7 +1406,7 @@ index 6056d38..9225d5a
                  except yum.Errors.GroupsError:
                      self.logger.critical(_('Warning: Group %s does not exist.'), group_string)
                      continue
-@@ -1368,17 +1876,61 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -1368,17 +1884,61 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
              return 2, [P_('%d package to Install', '%d packages to Install', len(pkgs_used)) % len(pkgs_used)]
  
      def removeGroups(self, grouplist):
@@ -1466,7 +1476,7 @@ index 6056d38..9225d5a
                  
          if not pkgs_used:
              return 0, [_('No packages to remove from groups')]
-@@ -1389,7 +1941,7 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -1389,7 +1949,7 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
  
      def _promptWanted(self):
          # shortcut for the always-off/always-on options
@@ -1475,7 +1485,7 @@ index 6056d38..9225d5a
              return False
          if self.conf.alwaysprompt:
              return True
-@@ -1400,7 +1952,6 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -1400,7 +1960,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 \
@@ -1483,7 +1493,7 @@ index 6056d38..9225d5a
                     txmbr.name not in self.extcmds:
                  return True
          
-@@ -1408,11 +1959,11 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -1408,11 +1967,11 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
          return False
  
      def usage(self):
@@ -1497,7 +1507,7 @@ index 6056d38..9225d5a
          sys.stdout.write(self.optparser.get_usage())
      
      def _installable(self, pkg, ematch=False):
-@@ -1468,9 +2019,9 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -1468,9 +2027,9 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
          return False
  
  class YumOptionParser(OptionParser):
@@ -1509,7 +1519,7 @@ index 6056d38..9225d5a
  
      def __init__(self,base, **kwargs):
          # check if this is called with a utils=True/False parameter
-@@ -1488,13 +2039,23 @@ class YumOptionParser(OptionParser):
+@@ -1488,13 +2047,23 @@ class YumOptionParser(OptionParser):
          self._addYumBasicOptions()
  
      def error(self, msg):
@@ -1535,7 +1545,7 @@ index 6056d38..9225d5a
          try:
              args = _filtercmdline(
                          ('--noplugins','--version','-q', '-v', "--quiet", "--verbose"), 
-@@ -1521,7 +2082,15 @@ class YumOptionParser(OptionParser):
+@@ -1521,7 +2090,15 @@ class YumOptionParser(OptionParser):
          return ret
          
      def setupYumConfig(self, args=None):
@@ -1552,7 +1562,7 @@ index 6056d38..9225d5a
          if not args:
              (opts, cmds) = self.parse_args()
          else:
-@@ -1533,16 +2102,22 @@ class YumOptionParser(OptionParser):
+@@ -1533,16 +2110,22 @@ class YumOptionParser(OptionParser):
          try:
              # config file is parsed and moving us forward
              # set some things in it.
@@ -1581,7 +1591,7 @@ index 6056d38..9225d5a
                  self.base.conf.cache = 1
  
              if opts.obsoletes:
-@@ -1610,10 +2185,6 @@ class YumOptionParser(OptionParser):
+@@ -1610,10 +2193,6 @@ class YumOptionParser(OptionParser):
                      self.base.usage()
                      sys.exit(1)
  
@@ -1592,7 +1602,7 @@ index 6056d38..9225d5a
              # Disable all gpg key checking, if requested.
              if opts.nogpgcheck:
                  #  Altering the normal configs. doesn't work too well, esp. with
-@@ -1640,10 +2211,18 @@ class YumOptionParser(OptionParser):
+@@ -1640,10 +2219,18 @@ class YumOptionParser(OptionParser):
          sys.exit(1)
  
      def getRoot(self,opts):
@@ -1612,7 +1622,7 @@ index 6056d38..9225d5a
              if os.access(opts.installroot+'/'+opts.conffile, os.R_OK):
                  opts.conffile = opts.installroot+'/'+opts.conffile
              elif opts.conffile == '/etc/yum/yum.conf':
-@@ -1701,6 +2280,9 @@ class YumOptionParser(OptionParser):
+@@ -1701,6 +2288,9 @@ class YumOptionParser(OptionParser):
          group.add_option("--showduplicates", dest="showdupesfromrepos",
                          action="store_true",
                  help=_("show duplicates, in repos, in list/search commands"))
@@ -1622,7 +1632,7 @@ index 6056d38..9225d5a
          group.add_option("-e", "--errorlevel", dest="errorlevel", default=None,
                  help=_("error output level"), type='int',
                  metavar='[error level]')
-@@ -1713,6 +2295,10 @@ class YumOptionParser(OptionParser):
+@@ -1713,6 +2303,10 @@ class YumOptionParser(OptionParser):
                          help=_("verbose operation"))
          group.add_option("-y", "--assumeyes", dest="assumeyes",
                  action="store_true", help=_("answer yes for all questions"))
@@ -1633,7 +1643,7 @@ index 6056d38..9225d5a
          group.add_option("--version", action="store_true", 
                  help=_("show Yum version and exit"))
          group.add_option("--installroot", help=_("set install root"), 
-@@ -1748,6 +2334,10 @@ class YumOptionParser(OptionParser):
+@@ -1748,6 +2342,10 @@ class YumOptionParser(OptionParser):
                  help=_("control whether color is used"))
          group.add_option("", "--releasever", dest="releasever", default=None, 
                  help=_("set value of $releasever in yum config and repo files"))
@@ -2408,7 +2418,7 @@ index 0000000..4d01a5a
 + the mailing list, yum at lists.baseurl.org, or consult bugzilla.
 +.fi
 diff --git a/docs/yum.8 b/docs/yum.8
-index 1a8202a..604bc26 100644
+index 1a8202a..505289d 100644
 --- a/docs/yum.8
 +++ b/docs/yum.8
 @@ -52,6 +52,7 @@ gnome\-packagekit application\&.
@@ -2561,7 +2571,7 @@ index 1a8202a..604bc26 100644
  legacy reasons only.
  .IP
  .IP "\fBreinstall\fP"
-@@ -260,7 +306,7 @@ on groups, files, provides and filelists just like the "install" command\&.
+@@ -260,10 +306,26 @@ on groups, files, provides and filelists just like the "install" command\&.
  Will try and downgrade a package from the version currently installed to the
  previously highest version (or the specified version).
  The depsolver will not necessarily work, but if you specify all the packages it
@@ -2570,7 +2580,26 @@ index 1a8202a..604bc26 100644
  work for "installonly" packages, like Kernels. downgrade operates
  on groups, files, provides, filelists and rpm files just like the "install" command\&.
  .IP
-@@ -291,11 +337,15 @@ then yum will ignore any repo errors and output the information it can get
++.IP "\fBswap\fP"
++At it's simplest this is just a simpler way to remove one set of package(s) and
++install another set of package(s) without having to use the "shell" command.
++However you can specify different commands to call than just remove or install,
++and you can list multiple packages (it splits using the "--" marker).
++Note that option parsing will remove the first "--" in an argument list on the
++command line.
++
++
++Examples:
++
++swap foo bar
++swap -- remove foo -- install bar
++swap foo group install bar-grp
++swap -- group remove foo-grp -- group install bar-grp
++.IP
+ .IP "\fBdeplist\fP"
+ Produces a list of all dependencies and what packages provide those
+ dependencies for the given packages. As of 3.2.30 it now just shows the latest
+@@ -291,11 +353,15 @@ then yum will ignore any repo errors and output the information it can get
  (Eg. "yum clean all; yum -C repolist" will output something, although the
  package counts/etc. will be zeroed out).
  .IP
@@ -2588,7 +2617,7 @@ index 1a8202a..604bc26 100644
  packages (in sorted order), and the checksum_type/checksum_data entries from
  the yumdb. Note that this rpmdb version is now also used significantly within
  yum (esp. in yum history).
-@@ -321,26 +371,33 @@ and so takes sub-commands:
+@@ -321,26 +387,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
@@ -2628,7 +2657,7 @@ index 1a8202a..604bc26 100644
  A (if it is not still installed), and "rollback 1" will try to remove packages
  B and C. Note that after a "rollback 1" you will have a fourth transaction,
  although the ending rpmdb version (see: yum version) should be the same in
-@@ -349,6 +406,12 @@ transactions 1 and 4.
+@@ -349,6 +422,12 @@ transactions 1 and 4.
  The addon-info command takes a transaction ID, and the packages-list command
  takes a package (with wildcards).
  
@@ -2641,7 +2670,7 @@ index 1a8202a..604bc26 100644
  In "history list" you can change the behaviour of the 2nd column via. the
  configuration option history_list_view.
  
-@@ -371,6 +434,15 @@ end of the package column in the packages-list command).
+@@ -371,6 +450,15 @@ end of the package column in the packages-list command).
  .I \fBs\fR - The transaction completed fine, but --skip-broken was enabled and had to skip some packages.
  .br
  
@@ -2657,7 +2686,7 @@ index 1a8202a..604bc26 100644
  .IP
  .IP "\fBcheck\fP"
  Checks the local rpmdb and produces information on any problems it finds. You
-@@ -401,6 +473,11 @@ Assume yes; assume that the answer to any question which would be asked
+@@ -401,6 +489,11 @@ Assume yes; assume that the answer to any question which would be asked
  is yes\&.
  .br
  Configuration Option: \fBassumeyes\fP
@@ -2669,7 +2698,7 @@ index 1a8202a..604bc26 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 +497,7 @@ Sets the error level to [number] Practical range 0 \- 10. 0 means print only cri
+@@ -420,7 +513,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" 
@@ -2678,7 +2707,7 @@ index 1a8202a..604bc26 100644
  options are: 'critical', 'emergency', 'error', 'warn' and 'debug'.
  .br
  Configuration Option: \fBrpmverbosity\fP
-@@ -506,7 +583,14 @@ option will corrupt your cache (and you can use $releasever in your cachedir
+@@ -506,7 +599,14 @@ option will corrupt your cache (and you can use $releasever in your cachedir
  configuration to stop this).
  .PP 
  .IP "\fB\-t, \-\-tolerant\fP"
@@ -2695,7 +2724,7 @@ index 1a8202a..604bc26 100644
  .IP "\fB\-\-setopt=option=value\fP"
  Set any config option in yum config or repo files. For options in the global 
 diff --git a/docs/yum.conf.5 b/docs/yum.conf.5
-index 515aa73..029fa75 100644
+index 515aa73..17f9bae 100644
 --- a/docs/yum.conf.5
 +++ b/docs/yum.conf.5
 @@ -114,15 +114,27 @@ are causing problems from the transaction.
@@ -2738,7 +2767,40 @@ index 515aa73..029fa75 100644
  .br
  Command-line option: \fB\-t\fP
  
-@@ -215,6 +228,30 @@ of packages in groups will be installed when 'groupinstall' is called.
+@@ -188,12 +201,32 @@ Default is `true'.
+ Command-line option: \fB\-\-obsoletes\fP
+ 
+ .IP
++\fBremove_leaf_only \fR
++Either `0' or `1'. Used to determine yum's behaviour when a package is removed.
++If \fBremove_leaf_only\fR is `0' (default) then
++packages, and their deps, will be removed.  If \fBremove_leaf_only\fR is
++`1' then only those packages that aren't required by another
++package will be removed.
++
++.IP
++\fBrepopkgsremove_leaf_only \fR
++Either `0' or `1'. Used to determine yum's behaviour when the repo-pkg remove
++command is run.  If \fBrepopkgremove_leaf_only\fR is `0' (default) then
++all packages in the repo. will be removed.  If \fBrepopkgremove_leaf_only\fR is
++`1' then only those packages in the repo. that aren't required by another
++package will be removed.
++Note that this option does not override remove_leaf_only, so enabling that
++option means this has almost no affect.
++
++.IP
+ \fBoverwrite_groups \fR
+ Either `0' or `1'. Used to determine yum's behaviour if two or more
+ repositories offer the package groups with the same name. If
+ \fBoverwrite_groups\fR is `1' then the group packages of the last matching
+ repository will be used. If \fBoverwrite_groups\fR is `0' then the groups
+ from all matching repositories will be merged together as one large group.
++Note that this option does not override remove_leaf_only, so enabling that
++option means this has almost no affect.
+ 
+ .IP
+ \fBgroupremove_leaf_only \fR
+@@ -215,6 +248,30 @@ of packages in groups will be installed when 'groupinstall' is called.
  Default is: default, mandatory
  
  .IP
@@ -2769,7 +2831,7 @@ index 515aa73..029fa75 100644
  \fBinstallroot \fR
  Specifies an alternative installroot, relative to which all packages will be
  installed. 
-@@ -300,6 +337,21 @@ with the \fBthrottle\fR option (above). If \fBthrottle\fR is a percentage and
+@@ -300,6 +357,21 @@ with the \fBthrottle\fR option (above). If \fBthrottle\fR is a percentage and
  ignored. Default is `0' (no bandwidth throttling). 
  
  .IP
@@ -2791,7 +2853,7 @@ index 515aa73..029fa75 100644
  \fBsslcacert \fR
  Path to the directory containing the databases of the certificate authorities
  yum should use to verify SSL certificates. Defaults to none - uses system
-@@ -331,6 +383,15 @@ Path to the SSL client key yum should use to connect to repos/remote sites
+@@ -331,6 +403,15 @@ Path to the SSL client key yum should use to connect to repos/remote sites
  Defaults to none.
  
  .IP
@@ -2807,7 +2869,39 @@ index 515aa73..029fa75 100644
  \fBhistory_record \fR
  Boolean - should yum record history entries for transactions. This takes some
  disk space, and some extra time in the transactions. But it allows how to know a
-@@ -480,6 +541,19 @@ not listed above is the other metadata, which contains the changelog information
+@@ -438,6 +519,31 @@ It's also possible to use the word "never", meaning that the metadata will
+ never expire. Note that when using a metalink file the metalink must always
+ be newer than the metadata for the repository, due to the validation, so this
+ timeout also applies to the metalink file.
++Also note that "never" does not override "yum clean expire-cache"
++
++.IP
++\fBmetadata_expire_filter \fR
++Filter the metadata_expire time, allowing a trade of speed for accuracy if
++a command doesn't require it. Each yum command can specify that it requires a
++certain level of timeliness quality from the remote repos. from "I'm about to
++install/upgrade, so this better be current" to "Anything that's available
++is good enough".
++
++'never' - Nothing is filtered, always obey metadata_expire.
++
++'read-only:past' - Commands that only care about past information
++are filtered from metadata expiring.
++Eg. yum history info (if history needs to lookup anything about a previous
++transaction, then by definition the remote package was available in the past).
++
++'read-only:present' - Commands that are balanced between past and future.
++This is the default.
++Eg. yum list yum
++
++'read-only:future' - Commands that are likely to result in running other
++commands which will require the latest metadata. Eg. yum check-update
++
++Note that this option does not override "yum clean expire-cache".
+ 
+ .IP
+ \fBmirrorlist_expire \fR
+@@ -480,6 +586,19 @@ not listed above is the other metadata, which contains the changelog information
  which is used by yum-changelog. This is what "yum makecache" uses.
  
  .IP
@@ -2827,7 +2921,7 @@ index 515aa73..029fa75 100644
  \fBmultilib_policy \fR
  Can be set to 'all' or 'best'. All means install all possible arches for any package you 
  want to install. Therefore yum install foo will install foo.i386 and foo.x86_64 on x86_64, 
-@@ -593,8 +667,53 @@ See color_list_installed_older for possible values.
+@@ -593,8 +712,53 @@ See color_list_installed_older for possible values.
  When removing packages (by removal, update or obsoletion) go through each
  package's dependencies. If any of them are no longer required by any other 
  package then also mark them to be removed.
@@ -2837,15 +2931,15 @@ index 515aa73..029fa75 100644
 +\fBupgrade_requirements_on_install \fR
 +When installing/reinstalling/upgrading packages go through each package's
 +installed dependencies and check for an update.
-+Boolean (1, 0, True, False, yes,no) Defaults to False
-+
+ Boolean (1, 0, True, False, yes,no) Defaults to False
+ 
 +.IP
 +\fBrecheck_installed_requires \fR
 +When upgrading a package do we recheck any requirements that existed in the old
 +package. Turning this on shouldn't do anything but slow yum depsolving down,
 +however using rpm --nodeps etc. can break the rpmdb and then this will help.
- Boolean (1, 0, True, False, yes,no) Defaults to False
- 
++Boolean (1, 0, True, False, yes,no) Defaults to False
++
 +.IP
 +\fBreset_nice \fR
 +If set to true then yum will try to reset the nice value to zero, before
@@ -2881,7 +2975,7 @@ index 515aa73..029fa75 100644
  
  
  .SH "[repository] OPTIONS"
-@@ -755,6 +874,11 @@ repository.
+@@ -755,6 +919,11 @@ repository.
  Overrides the \fBbandwidth\fR option from the [main] section for this
  repository.
  
@@ -2893,7 +2987,7 @@ index 515aa73..029fa75 100644
  
  .IP
  \fBsslcacert \fR
-@@ -776,6 +900,10 @@ repository.
+@@ -776,6 +945,10 @@ repository.
  Overrides the \fBsslclientkey\fR option from the [main] section for this
  repository.
  
@@ -2904,7 +2998,19 @@ index 515aa73..029fa75 100644
  
  .IP
  \fBmetadata_expire \fR
-@@ -824,7 +952,11 @@ as greater/less than any other. defaults to 1000
+@@ -783,6 +956,11 @@ Overrides the \fBmetadata_expire\fR option from the [main] section for this
+ repository.
+ 
+ .IP
++\fBmetadata_expire_filter \fR
++Overrides the \fBmetadata_expire_filter\fR option from the [main] section for
++this repository.
++
++.IP
+ \fBmirrorlist_expire \fR
+ Overrides the \fBmirrorlist_expire\fR option from the [main] section for this
+ repository.
+@@ -824,7 +1002,11 @@ as greater/less than any other. defaults to 1000
  If set to True yum will continue running if this repository cannot be 
  contacted for any reason. This should be set carefully as all repos are consulted
  for any given command. Defaults to False.
@@ -3687,7 +3793,7 @@ index f1e06e8..ac06316 100644
  complete -F _yum -o filenames yum yummain.py
  
 diff --git a/output.py b/output.py
-index b6aa277..885eb09 100755
+index b6aa277..26e3928 100755
 --- a/output.py
 +++ b/output.py
 @@ -1,6 +1,6 @@
@@ -5055,7 +5161,7 @@ index b6aa277..885eb09 100755
          modedict = { 'i': _('installed'),
                       'u': _('an update'),
                       'e': _('erased'),
-@@ -2104,43 +2779,85 @@ class DepSolveProgressCallBack:
+@@ -2104,43 +2779,86 @@ class DepSolveProgressCallBack:
              modeterm)
          
      def start(self):
@@ -5107,17 +5213,20 @@ index b6aa277..885eb09 100755
              _('--> Processing Dependency: %s for package: %s'), formatted_req,
              po)
      
-     def groupRemoveReq(self, po, hits):
+-    def groupRemoveReq(self, po, hits):
++    def removeReq(self, po, deppo, hits):
 +        """Output a message stating that the given package will not be
-+        removed. This method is used during leaf-only group remove
-+        commands to indicate that the package will be kept.
++        removed. This method is used during leaf-only group remove, leaf-only
++        repo-pkg remove and normal remove commands to indicate that the
++        package will be kept.
 +
 +        :param po: the :class:`yum.packages.PackageObject` that will
 +           not be removed
 +        :param hits: unused
 +        """
          self.verbose_logger.log(logginglevels.INFO_2,
-             _('---> Keeping package: %s'), po)
+-            _('---> Keeping package: %s'), po)
++            _('---> Keeping package: %s due to %s'), po, deppo)
  
      def unresolved(self, msg):
 +        """Output a message stating that there is an unresolved
@@ -5143,7 +5252,7 @@ index b6aa277..885eb09 100755
          needname, needflags, needversion = reqTup
  
          yb = self.ayum
-@@ -2225,46 +2942,106 @@ class DepSolveProgressCallBack:
+@@ -2225,46 +2943,106 @@ class DepSolveProgressCallBack:
          return msg
      
      def procConflict(self, name, confname):
@@ -5255,7 +5364,7 @@ index b6aa277..885eb09 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 +3093,7 @@ def _pkgname_ui(ayum, pkgname, ts_states=None):
+@@ -2316,10 +3094,7 @@ def _pkgname_ui(ayum, pkgname, ts_states=None):
      return pkgname
  
  class YumCliRPMCallBack(RPMBaseCallback):
@@ -5267,7 +5376,7 @@ index b6aa277..885eb09 100755
  
      width = property(lambda x: _term_width())
  
-@@ -2337,21 +3111,34 @@ class YumCliRPMCallBack(RPMBaseCallback):
+@@ -2337,21 +3112,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)):
@@ -5312,7 +5421,7 @@ index b6aa277..885eb09 100755
          
          if type(package) not in types.StringTypes:
              pkgname = str(package)
-@@ -2363,9 +3150,25 @@ class YumCliRPMCallBack(RPMBaseCallback):
+@@ -2363,9 +3151,25 @@ class YumCliRPMCallBack(RPMBaseCallback):
              percent = 0
          else:
              percent = (te_current*100L)/te_total
@@ -5339,7 +5448,7 @@ index b6aa277..885eb09 100755
                                                pkgname=pkgname, wid1=wid1)
              msg = fmt % (utf8_width_fill(process, wid1, wid1),
                           utf8_width_fill(pkgname, wid2, wid2))
-@@ -2377,6 +3180,11 @@ class YumCliRPMCallBack(RPMBaseCallback):
+@@ -2377,6 +3181,11 @@ class YumCliRPMCallBack(RPMBaseCallback):
                  print " "
  
      def scriptout(self, package, msgs):
@@ -5351,7 +5460,7 @@ index b6aa277..885eb09 100755
          if msgs:
              sys.stdout.write(to_unicode(msgs))
              sys.stdout.flush()
-@@ -2429,8 +3237,30 @@ class YumCliRPMCallBack(RPMBaseCallback):
+@@ -2429,8 +3238,30 @@ class YumCliRPMCallBack(RPMBaseCallback):
              wid2 = pnl
          return fmt, wid1, wid2
  
@@ -152445,7 +152554,7 @@ index c1af4ad..e3e3956 100644
      pass
      
 diff --git a/yum/__init__.py b/yum/__init__.py
-index 99039e0..10a5477 100644
+index 99039e0..8a0501d 100644
 --- a/yum/__init__.py
 +++ b/yum/__init__.py
 @@ -21,6 +21,7 @@ The Yum RPM software updater.
@@ -154942,7 +155051,22 @@ index 99039e0..10a5477 100644
          pkgs = []
          was_pattern = False
          if po:
-@@ -3477,20 +4433,12 @@ class YumBase(depsolve.Depsolve):
+@@ -3464,9 +4420,14 @@ class YumBase(depsolve.Depsolve):
+                 if kwargs['pattern'] and kwargs['pattern'][0] == '@':
+                     return self._at_groupinstall(kwargs['pattern'])
+ 
++                repoid = None # All of them
++                if 'repoid' in kwargs:
++                    repoid = kwargs['repoid']
++
+                 was_pattern = True
+                 pats = [kwargs['pattern']]
+                 mypkgs = self.pkgSack.returnPackages(patterns=pats,
++                                                     repoid=repoid,
+                                                       ignore_case=False)
+                 pkgs.extend(mypkgs)
+                 # if we have anything left unmatched, let's take a look for it
+@@ -3477,20 +4438,12 @@ class YumBase(depsolve.Depsolve):
                      self.verbose_logger.debug(_('Checking for virtual provide or file-provide for %s'), 
                          arg)
  
@@ -154969,7 +155093,24 @@ index 99039e0..10a5477 100644
              else:
                  nevra_dict = self._nevra_kwarg_parse(kwargs)
  
-@@ -3577,17 +4525,20 @@ class YumBase(depsolve.Depsolve):
+@@ -3499,6 +4452,16 @@ class YumBase(depsolve.Depsolve):
+                      ver=nevra_dict['version'], rel=nevra_dict['release'])
+                 self._add_not_found_a(pkgs, nevra_dict)
+                 
++                if 'repoid' in kwargs:
++                    def _filter_repoid(pkgs):
++                        ret = []
++                        for pkg in pkgs:
++                            if pkg.repoid != kwargs['repoid']:
++                                continue
++                            ret.append(pkg)
++                        return ret
++                    pkgs = _filter_repoid(pkgs)
++
+             if pkgs:
+                 # if was_pattern or nevra-dict['arch'] is none, take the list
+                 # of arches based on our multilib_compat config and 
+@@ -3577,17 +4540,20 @@ class YumBase(depsolve.Depsolve):
                      continue
              
              # make sure this shouldn't be passed to update:
@@ -154993,7 +155134,7 @@ index 99039e0..10a5477 100644
                  obsoleting_pkg = self._test_loop(po, self._pkg2obspkg)
              if obsoleting_pkg is not None:
                  # this is not a definitive check but it'll make sure we don't
-@@ -3600,23 +4551,23 @@ class YumBase(depsolve.Depsolve):
+@@ -3600,23 +4566,23 @@ class YumBase(depsolve.Depsolve):
                      already_obs = pkgs[0]
  
                  if already_obs:
@@ -155024,7 +155165,7 @@ index 99039e0..10a5477 100644
                      continue
  
              # make sure we don't have a name.arch of this already installed
-@@ -3630,7 +4581,7 @@ class YumBase(depsolve.Depsolve):
+@@ -3630,7 +4596,7 @@ class YumBase(depsolve.Depsolve):
                          found = True
                          break
                  if not found:
@@ -155033,7 +155174,7 @@ index 99039e0..10a5477 100644
                      txmbrs = self.update(po=po)
                      tx_return.extend(txmbrs)
                      continue
-@@ -3719,19 +4670,47 @@ class YumBase(depsolve.Depsolve):
+@@ -3719,19 +4685,47 @@ class YumBase(depsolve.Depsolve):
          return txmbr
  
      def update(self, po=None, requiringPo=None, update_to=False, **kwargs):
@@ -155088,7 +155229,7 @@ index 99039e0..10a5477 100644
          tx_return = []
          if not po and not kwargs: # update everything (the easy case)
              self.verbose_logger.log(logginglevels.DEBUG_2, _('Updating Everything'))
-@@ -3765,7 +4744,14 @@ class YumBase(depsolve.Depsolve):
+@@ -3765,7 +4759,14 @@ class YumBase(depsolve.Depsolve):
                      if new is None:
                          continue
                      tx_return.extend(self.update(po=new))
@@ -155104,7 +155245,7 @@ index 99039e0..10a5477 100644
              return tx_return
  
          # complications
-@@ -3787,7 +4773,7 @@ class YumBase(depsolve.Depsolve):
+@@ -3787,7 +4788,7 @@ class YumBase(depsolve.Depsolve):
                  return self._minus_deselect(kwargs['pattern'])
  
              if kwargs['pattern'] and kwargs['pattern'][0] == '@':
@@ -155113,7 +155254,7 @@ index 99039e0..10a5477 100644
  
              arg = kwargs['pattern']
              if not update_to:
-@@ -3843,7 +4829,7 @@ class YumBase(depsolve.Depsolve):
+@@ -3843,7 +4844,7 @@ class YumBase(depsolve.Depsolve):
                      availpkgs = self._compare_providers(availpkgs, requiringPo)
                      availpkgs = map(lambda x: x[0], availpkgs)
                  elif not availpkgs:
@@ -155122,7 +155263,7 @@ index 99039e0..10a5477 100644
         
          # for any thing specified
          # get the list of available pkgs matching it (or take the po)
-@@ -3920,6 +4906,21 @@ class YumBase(depsolve.Depsolve):
+@@ -3920,6 +4921,21 @@ class YumBase(depsolve.Depsolve):
                      tx_return.append(txmbr)
                          
          for available_pkg in availpkgs:
@@ -155144,7 +155285,7 @@ index 99039e0..10a5477 100644
              #  Make sure we're not installing a package which is obsoleted by
              # something else in the repo. Unless there is a obsoletion loop,
              # at which point ignore everything.
-@@ -3985,11 +4986,18 @@ class YumBase(depsolve.Depsolve):
+@@ -3985,11 +5001,18 @@ class YumBase(depsolve.Depsolve):
          return tx_return
          
      def remove(self, po=None, **kwargs):
@@ -155168,7 +155309,38 @@ index 99039e0..10a5477 100644
          if not po and not kwargs:
              raise Errors.RemoveError, 'Nothing specified to remove'
          
-@@ -4055,17 +5063,19 @@ class YumBase(depsolve.Depsolve):
+@@ -4008,6 +5031,20 @@ class YumBase(depsolve.Depsolve):
+                     return self._at_groupremove(kwargs['pattern'])
+ 
+                 (e,m,u) = self.rpmdb.matchPackageNames([kwargs['pattern']])
++                if 'repoid' in kwargs:
++                    def _filter_repoid(pkgs):
++                        ret = []
++                        for pkg in pkgs:
++                            if 'from_repo' not in pkg.yumdb_info:
++                                continue
++                            if pkg.yumdb_info.from_repo != kwargs['repoid']:
++                                continue
++                            ret.append(pkg)
++                        return ret
++
++                    e = _filter_repoid(e)
++                    m = _filter_repoid(m)
++
+                 pkgs.extend(e)
+                 pkgs.extend(m)
+                 if u:
+@@ -4018,6 +5055,9 @@ class YumBase(depsolve.Depsolve):
+                     except yum.Errors.YumBaseError, e:
+                         self.logger.critical(_('%s') % e)
+                     
++                    if 'repoid' in kwargs:
++                        depmatches = _filter_repoid(depmatches)
++
+                     if not depmatches:
+                         arg = to_unicode(arg)
+                         self.logger.critical(_('No Match for argument: %s') % to_unicode(arg))
+@@ -4055,17 +5095,19 @@ class YumBase(depsolve.Depsolve):
          return tx_return
  
      def installLocal(self, pkg, po=None, updateonly=False):
@@ -155198,7 +155370,7 @@ index 99039e0..10a5477 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 +5193,15 @@ class YumBase(depsolve.Depsolve):
+@@ -4183,16 +5225,15 @@ class YumBase(depsolve.Depsolve):
          return tx_return
  
      def reinstallLocal(self, pkg, po=None):
@@ -155223,7 +155395,7 @@ index 99039e0..10a5477 100644
          if not po:
              try:
                  po = YumUrlPackage(self, ts=self.rpmdb.readOnlyTS(), url=pkg,
-@@ -4215,9 +5224,19 @@ class YumBase(depsolve.Depsolve):
+@@ -4215,9 +5256,19 @@ class YumBase(depsolve.Depsolve):
          return self.reinstall(po=po)
  
      def reinstall(self, po=None, **kwargs):
@@ -155246,7 +155418,7 @@ index 99039e0..10a5477 100644
          self._add_prob_flags(rpm.RPMPROB_FILTER_REPLACEPKG,
                               rpm.RPMPROB_FILTER_REPLACENEWFILES,
                               rpm.RPMPROB_FILTER_REPLACEOLDFILES)
-@@ -4259,16 +5278,15 @@ class YumBase(depsolve.Depsolve):
+@@ -4259,16 +5310,15 @@ class YumBase(depsolve.Depsolve):
          return tx_mbrs
          
      def downgradeLocal(self, pkg, po=None):
@@ -155271,7 +155443,7 @@ index 99039e0..10a5477 100644
          if not po:
              try:
                  po = YumUrlPackage(self, ts=self.rpmdb.readOnlyTS(), url=pkg,
-@@ -4309,13 +5327,19 @@ class YumBase(depsolve.Depsolve):
+@@ -4309,13 +5359,19 @@ class YumBase(depsolve.Depsolve):
          return False
          
      def downgrade(self, po=None, **kwargs):
@@ -155298,7 +155470,7 @@ index 99039e0..10a5477 100644
          if not po and not kwargs:
              raise Errors.DowngradeError, 'Nothing specified to downgrade'
  
-@@ -4397,6 +5421,10 @@ class YumBase(depsolve.Depsolve):
+@@ -4397,6 +5453,10 @@ class YumBase(depsolve.Depsolve):
          # installed version. Indexed fromn the latest installed pkgtup.
          downgrade_apkgs = {}
          for pkg in sorted(apkgs):
@@ -155309,7 +155481,7 @@ index 99039e0..10a5477 100644
              na  = (pkg.name, pkg.arch)
  
              # Here we allow downgrades from .i386 => .noarch, or .i586 => .i386
-@@ -4421,6 +5449,9 @@ class YumBase(depsolve.Depsolve):
+@@ -4421,6 +5481,9 @@ class YumBase(depsolve.Depsolve):
                  warned_nas.add(na)
                  continue
  
@@ -155319,7 +155491,7 @@ index 99039e0..10a5477 100644
              if pkg.verGE(lipkg):
                  if na not in warned_nas:
                      msg = _('Only Upgrade available on package: %s') % pkg
-@@ -4457,7 +5488,7 @@ class YumBase(depsolve.Depsolve):
+@@ -4457,7 +5520,7 @@ class YumBase(depsolve.Depsolve):
          if e and v and r:
              evr = '%s:%s-%s' % (e, v, r)
          elif v and r:
@@ -155328,7 +155500,7 @@ index 99039e0..10a5477 100644
          elif e and v:
              evr = '%s:%s' % (e, v)
          elif v: # e and r etc. is just too weird to print
-@@ -4500,12 +5531,24 @@ class YumBase(depsolve.Depsolve):
+@@ -4500,12 +5563,24 @@ class YumBase(depsolve.Depsolve):
  
          return returndict
  
@@ -155356,7 +155528,7 @@ index 99039e0..10a5477 100644
          old_conf_obs = self.conf.obsoletes
          self.conf.obsoletes = False
          done = False
-@@ -4515,19 +5558,46 @@ class YumBase(depsolve.Depsolve):
+@@ -4515,19 +5590,46 @@ class YumBase(depsolve.Depsolve):
                      done = True
          for pkg in transaction.trans_data:
              if pkg.state == 'Downgrade':
@@ -155403,7 +155575,7 @@ index 99039e0..10a5477 100644
                  if self.install(pkgtup=pkg.pkgtup):
                      done = True
          for pkg in transaction.trans_data:
-@@ -4538,8 +5608,14 @@ class YumBase(depsolve.Depsolve):
+@@ -4538,8 +5640,14 @@ class YumBase(depsolve.Depsolve):
          return done
  
      def history_undo(self, transaction):
@@ -155420,7 +155592,7 @@ index 99039e0..10a5477 100644
          # NOTE: This is somewhat basic atm. ... for instance we don't check
          #       that we are going from the old new version. However it's still
          #       better than the RHN rollback code, and people pay for that :).
-@@ -4616,7 +5692,7 @@ class YumBase(depsolve.Depsolve):
+@@ -4616,7 +5724,7 @@ class YumBase(depsolve.Depsolve):
  
          except urlgrabber.grabber.URLGrabError, e:
              raise Errors.YumBaseError(_('GPG key retrieval failed: ') +
@@ -155429,7 +155601,7 @@ index 99039e0..10a5477 100644
                                        
          # check for a .asc file accompanying it - that's our gpg sig on the key
          # suck it down and do the check
-@@ -4649,7 +5725,7 @@ class YumBase(depsolve.Depsolve):
+@@ -4649,7 +5757,7 @@ class YumBase(depsolve.Depsolve):
              keys_info = misc.getgpgkeyinfo(rawkey, multiple=True)
          except ValueError, e:
              raise Errors.YumBaseError(_('Invalid GPG Key from %s: %s') % 
@@ -155438,7 +155610,7 @@ index 99039e0..10a5477 100644
          keys = []
          for keyinfo in keys_info:
              thiskey = {}
-@@ -4674,39 +5750,49 @@ class YumBase(depsolve.Depsolve):
+@@ -4674,39 +5782,49 @@ class YumBase(depsolve.Depsolve):
              if pkgs:
                  pkgs = sorted(pkgs)[-1]
                  msg = (_('Importing %s key 0x%s:\n'
@@ -155506,7 +155678,7 @@ index 99039e0..10a5477 100644
          user_cb_fail = False
          for keyurl in keyurls:
              keys = self._retrievePublicKey(keyurl, repo)
-@@ -4725,7 +5811,9 @@ class YumBase(depsolve.Depsolve):
+@@ -4725,7 +5843,9 @@ class YumBase(depsolve.Depsolve):
                      # Try installing/updating GPG key
                      self._getKeyImportMessage(info, keyurl)
                      rc = False
@@ -155517,7 +155689,7 @@ index 99039e0..10a5477 100644
                          rc = True
                          
                      # grab the .sig/.asc for the keyurl, if it exists
-@@ -4751,8 +5839,8 @@ class YumBase(depsolve.Depsolve):
+@@ -4751,8 +5871,8 @@ class YumBase(depsolve.Depsolve):
                  ts = self.rpmdb.readOnlyTS()
                  result = ts.pgpImportPubkey(misc.procgpgkey(info['raw_key']))
                  if result != 0:
@@ -155528,7 +155700,7 @@ index 99039e0..10a5477 100644
                  self.logger.info(_('Key imported successfully'))
                  key_installed = True
  
-@@ -4760,18 +5848,20 @@ class YumBase(depsolve.Depsolve):
+@@ -4760,18 +5880,20 @@ class YumBase(depsolve.Depsolve):
              raise Errors.YumBaseError, _("Didn't install any keys")
  
          if not key_installed:
@@ -155554,7 +155726,7 @@ index 99039e0..10a5477 100644
      
      def _getAnyKeyForRepo(self, repo, destdir, keyurl_list, is_cakey=False, callback=None):
          """
-@@ -4788,6 +5878,18 @@ class YumBase(depsolve.Depsolve):
+@@ -4788,6 +5910,18 @@ class YumBase(depsolve.Depsolve):
          """
  
          key_installed = False
@@ -155573,7 +155745,7 @@ index 99039e0..10a5477 100644
          user_cb_fail = False
          for keyurl in keyurl_list:
              keys = self._retrievePublicKey(keyurl, repo, getSig=not is_cakey)
-@@ -4819,8 +5921,11 @@ class YumBase(depsolve.Depsolve):
+@@ -4819,8 +5953,11 @@ class YumBase(depsolve.Depsolve):
                  if not key_installed:
                      self._getKeyImportMessage(info, keyurl, keytype)
                      rc = False
@@ -155586,7 +155758,7 @@ index 99039e0..10a5477 100644
                      elif callback:
                          rc = callback({"repo": repo, "userid": info['userid'],
                                          "hexkeyid": info['hexkeyid'], "keyurl": keyurl,
-@@ -4835,7 +5940,8 @@ class YumBase(depsolve.Depsolve):
+@@ -4835,7 +5972,8 @@ class YumBase(depsolve.Depsolve):
                  # Import the key
                  result = misc.import_key_to_pubring(info['raw_key'], info['hexkeyid'], gpgdir=destdir)
                  if not result:
@@ -155596,7 +155768,7 @@ index 99039e0..10a5477 100644
                  self.logger.info(_('Key imported successfully'))
                  key_installed = True
                  # write out the key id to imported_cakeys in the repos basedir
-@@ -4851,36 +5957,35 @@ class YumBase(depsolve.Depsolve):
+@@ -4851,36 +5989,35 @@ class YumBase(depsolve.Depsolve):
                              pass
  
          if not key_installed and user_cb_fail:
@@ -155649,7 +155821,7 @@ index 99039e0..10a5477 100644
          self._getAnyKeyForRepo(repo, repo.gpgcadir, repo.gpgcakey, is_cakey=True, callback=callback)
  
      def _limit_installonly_pkgs(self):
-@@ -4889,7 +5994,7 @@ class YumBase(depsolve.Depsolve):
+@@ -4889,7 +6026,7 @@ class YumBase(depsolve.Depsolve):
              New in 3.2.24: Obey yumdb_info.installonly data. """
  
          def _sort_and_filter_installonly(pkgs):
@@ -155658,7 +155830,7 @@ index 99039e0..10a5477 100644
                  using the yumdb. """
              ret_beg = []
              ret_mid = []
-@@ -4926,23 +6031,30 @@ class YumBase(depsolve.Depsolve):
+@@ -4926,23 +6063,30 @@ class YumBase(depsolve.Depsolve):
          # so self.rpmdb.ts should be valid.
          ts = self.rpmdb.readOnlyTS()
          (cur_kernel_v, cur_kernel_r) = misc.get_running_kernel_version_release(ts)
@@ -155697,7 +155869,7 @@ index 99039e0..10a5477 100644
              for po in installed:
                  if (po.version, po.release) == (cur_kernel_v, cur_kernel_r): 
                      # don't remove running
-@@ -4959,19 +6071,22 @@ class YumBase(depsolve.Depsolve):
+@@ -4959,19 +6103,22 @@ class YumBase(depsolve.Depsolve):
              txmbr.depends_on.append(rel)
  
      def processTransaction(self, callback=None,rpmTestDisplay=None, rpmDisplay=None):
@@ -155733,7 +155905,7 @@ index 99039e0..10a5477 100644
          
          if not callback:
              callback = callbacks.ProcessTransNoOutputCallback()
-@@ -5114,13 +6229,19 @@ class YumBase(depsolve.Depsolve):
+@@ -5114,13 +6261,19 @@ class YumBase(depsolve.Depsolve):
          return results
  
      def add_enable_repo(self, repoid, baseurls=[], mirrorlist=None, **kwargs):
@@ -155760,7 +155932,7 @@ index 99039e0..10a5477 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 +6288,15 @@ class YumBase(depsolve.Depsolve):
+@@ -5167,9 +6320,15 @@ class YumBase(depsolve.Depsolve):
  
      def setCacheDir(self, force=False, tmpdir=None, reuse=True,
                      suffix='/$basearch/$releasever'):
@@ -155779,7 +155951,7 @@ index 99039e0..10a5477 100644
          if not force and os.geteuid() == 0:
              return True # We are root, not forced, so happy with the global dir.
          if tmpdir is None:
-@@ -5179,7 +6306,7 @@ class YumBase(depsolve.Depsolve):
+@@ -5179,7 +6338,7 @@ class YumBase(depsolve.Depsolve):
          try:
              cachedir = misc.getCacheDir(tmpdir, reuse)
          except (IOError, OSError), e:
@@ -155788,7 +155960,7 @@ index 99039e0..10a5477 100644
              cachedir = None
              
          if cachedir is None:
-@@ -5190,6 +6317,7 @@ class YumBase(depsolve.Depsolve):
+@@ -5190,6 +6349,7 @@ class YumBase(depsolve.Depsolve):
              self.prerepoconf.cachedir = cachedir
          else:
              self.repos.setCacheDir(cachedir)
@@ -155796,7 +155968,7 @@ index 99039e0..10a5477 100644
          self.conf.cachedir = cachedir
          return True # We got a new cache dir
  
-@@ -5220,13 +6348,24 @@ class YumBase(depsolve.Depsolve):
+@@ -5220,13 +6380,24 @@ class YumBase(depsolve.Depsolve):
          self.history.write_addon_data('config-repos', myrepos)
          
      def verify_plugins_cb(self, verify_package):
@@ -155824,7 +155996,7 @@ index 99039e0..10a5477 100644
          if self.tsInfo._unresolvedMembers:
              if auto:
                  self.logger.critical(_("Dependencies not solved. Will not save unresolved transaction."))
-@@ -5234,7 +6373,7 @@ class YumBase(depsolve.Depsolve):
+@@ -5234,7 +6405,7 @@ class YumBase(depsolve.Depsolve):
              raise Errors.YumBaseError(_("Dependencies not solved. Will not save unresolved transaction."))
          
          if not filename:
@@ -155833,7 +156005,7 @@ index 99039e0..10a5477 100644
              fd,filename = tempfile.mkstemp(suffix='.yumtx', prefix=prefix)
              f = os.fdopen(fd, 'w')
          else:
-@@ -5244,13 +6383,17 @@ class YumBase(depsolve.Depsolve):
+@@ -5244,13 +6415,17 @@ class YumBase(depsolve.Depsolve):
          
          msg = "%s\n" % self.rpmdb.simpleVersion(main_only=True)[0]
          msg += "%s\n" % self.ts.getTsFlags()
@@ -155854,7 +156026,7 @@ index 99039e0..10a5477 100644
          msg += "%s\n" % len(self.tsInfo.getMembers())
          for txmbr in self.tsInfo.getMembers():
              msg += txmbr._dump()
-@@ -5260,13 +6403,25 @@ class YumBase(depsolve.Depsolve):
+@@ -5260,13 +6435,25 @@ class YumBase(depsolve.Depsolve):
          except (IOError, OSError), e:
              self._ts_save_file = None
              if auto:
@@ -155886,7 +156058,7 @@ index 99039e0..10a5477 100644
          # check rpmversion - if not match throw a fit
          # check repoversions  (and repos)- if not match throw a fit
          # load each txmbr - if pkgs being updated don't exist, bail w/error
-@@ -5276,26 +6431,45 @@ class YumBase(depsolve.Depsolve):
+@@ -5276,26 +6463,45 @@ class YumBase(depsolve.Depsolve):
          try:
              data = open(filename, 'r').readlines()
          except (IOError, OSError), e:
@@ -155934,7 +156106,7 @@ index 99039e0..10a5477 100644
              if ignorerpm:
                  msg += _(" ignoring, as requested.")
                  self.logger.critical(_(msg))
-@@ -5318,8 +6492,17 @@ class YumBase(depsolve.Depsolve):
+@@ -5318,8 +6524,17 @@ class YumBase(depsolve.Depsolve):
          numrepos = int(data[2].strip())
          repos = []
          rindex=3+numrepos
@@ -155953,7 +156125,7 @@ index 99039e0..10a5477 100644
  
          # pkgs/txmbrs
          numpkgs = int(data[rindex].strip())
-@@ -5329,6 +6512,7 @@ class YumBase(depsolve.Depsolve):
+@@ -5329,6 +6544,7 @@ class YumBase(depsolve.Depsolve):
          pkgcount = 0
          pkgprob = False
          curpkg = None
@@ -155961,7 +156133,7 @@ index 99039e0..10a5477 100644
          for l in data[pkgstart:]:
              l = l.rstrip()
              # our main txmbrs
-@@ -5356,6 +6540,7 @@ class YumBase(depsolve.Depsolve):
+@@ -5356,6 +6572,7 @@ class YumBase(depsolve.Depsolve):
                      if not ignoremissing:
                          raise Errors.YumBaseError(msg)
                      else:
@@ -155969,7 +156141,7 @@ index 99039e0..10a5477 100644
                          self.logger.critical(msg)
                  else:
                      pkgcount += 1
-@@ -5432,12 +6617,18 @@ class YumBase(depsolve.Depsolve):
+@@ -5432,12 +6649,18 @@ class YumBase(depsolve.Depsolve):
          if pkgprob:
              msg = _("Transaction members, relations are missing or ts has been modified,")
              if ignoremissing:
@@ -155988,7 +156160,7 @@ index 99039e0..10a5477 100644
          return self.tsInfo.getMembers()
  
      def _remove_old_deps(self):
-@@ -5470,18 +6661,6 @@ class YumBase(depsolve.Depsolve):
+@@ -5470,18 +6693,6 @@ class YumBase(depsolve.Depsolve):
                      if requiring == required: # if they are self-requiring skip them
                          continue
                          
@@ -156007,7 +156179,7 @@ index 99039e0..10a5477 100644
                      #for tbi_pkg in self.tsInfo.getMembersWithState(output_states=TS_INSTALL_STATES):
                      #   for reqtuple in tbi_pkg.po.requires:
                      #        if required.provides_for(reqtuple):
-@@ -5533,7 +6712,24 @@ class YumBase(depsolve.Depsolve):
+@@ -5533,7 +6744,24 @@ class YumBase(depsolve.Depsolve):
                      # Debugging output
                      self.verbose_logger.log(logginglevels.DEBUG_2, _("%s has revdep %s which was user-installed."), pkg, curpkg)
                      ok_to_remove[pkg] = False
@@ -156032,7 +156204,7 @@ index 99039e0..10a5477 100644
                  visited[curpkg] = True
              all_leaves_visited = True
              leaves = curpkg.requiring_packages()
-@@ -5547,4 +6743,3 @@ class YumBase(depsolve.Depsolve):
+@@ -5547,4 +6775,3 @@ class YumBase(depsolve.Depsolve):
          # Debugging output
          self.verbose_logger.log(logginglevels.DEBUG_2, _("%s has no user-installed revdeps."), pkg)
          return False
@@ -156640,7 +156812,7 @@ index 65f6d5e..fe5649d 100755
          print >> sys.stderr, "newcomps.py: No such file:\'%s\'" % sys.argv[1]
          sys.exit(1)
 diff --git a/yum/config.py b/yum/config.py
-index d09511f..74be397 100644
+index d09511f..5856aa2 100644
 --- a/yum/config.py
 +++ b/yum/config.py
 @@ -45,15 +45,18 @@ from misc import get_uuid, read_in_items_from_dot_dir
@@ -157370,7 +157542,13 @@ index d09511f..74be397 100644
      alwaysprompt = BoolOption(True)
      exactarch = BoolOption(True)
      tolerant = BoolOption(True)
-@@ -681,11 +778,17 @@ class YumConf(StartupConf):
+@@ -677,18 +774,30 @@ class YumConf(StartupConf):
+     obsoletes = BoolOption(True)
+     showdupesfromrepos = BoolOption(False)
+     enabled = BoolOption(True)
++    remove_leaf_only = BoolOption(False)
++    repopkgsremove_leaf_only = BoolOption(False)
+     enablegroups = BoolOption(True)
      enable_group_conditionals = BoolOption(True)
      groupremove_leaf_only = BoolOption(False)
      group_package_types = ListOption(['mandatory', 'default'])
@@ -157388,7 +157566,14 @@ index d09511f..74be397 100644
  
      http_caching = SelectionOption('all', ('none', 'packages', 'all'))
      metadata_expire = SecondsOption(60 * 60 * 6) # Time in seconds (6h).
-@@ -698,12 +801,14 @@ class YumConf(StartupConf):
++    metadata_expire_filter = SelectionOption('read-only:present',
++                                             ('never', 'read-only:future',
++                                              'read-only:present',
++                                              'read-only:past'))
+     # Time in seconds (1 day). NOTE: This isn't used when using metalinks
+     mirrorlist_expire = SecondsOption(60 * 60 * 24)
+     # XXX rpm_check_debug is unused, left around for API compatibility for now
+@@ -698,12 +807,14 @@ class YumConf(StartupConf):
      #  Note that "instant" is the old behaviour, but group:primary is very
      # similar but better :).
      mdpolicy = ListOption(['group:primary'])
@@ -157405,7 +157590,7 @@ index d09511f..74be397 100644
  
      color = SelectionOption('auto', ('auto', 'never', 'always'),
                              mapper={'on' : 'always', 'yes' : 'always',
-@@ -715,11 +820,13 @@ class YumConf(StartupConf):
+@@ -715,11 +826,13 @@ class YumConf(StartupConf):
      color_list_installed_newer = Option('bold,yellow')
      color_list_installed_reinstall = Option('normal')
      color_list_installed_extra = Option('bold,red')
@@ -157419,7 +157604,7 @@ index d09511f..74be397 100644
  
      color_update_installed = Option('normal')
      color_update_local     = Option('bold')
-@@ -731,6 +838,7 @@ class YumConf(StartupConf):
+@@ -731,6 +844,7 @@ class YumConf(StartupConf):
      sslverify = BoolOption(True)
      sslclientcert = Option()
      sslclientkey = Option()
@@ -157427,7 +157612,7 @@ index d09511f..74be397 100644
  
      history_record = BoolOption(True)
      history_record_packages = ListOption(['yum', 'rpm'])
-@@ -744,18 +852,29 @@ class YumConf(StartupConf):
+@@ -744,18 +858,29 @@ class YumConf(StartupConf):
      
      loadts_ignoremissing = BoolOption(False)
      loadts_ignorerpm = BoolOption(False)
@@ -157457,7 +157642,7 @@ index d09511f..74be397 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 +897,12 @@ class YumConf(StartupConf):
+@@ -778,14 +903,12 @@ class YumConf(StartupConf):
          return output
  
  class RepoConf(BaseConfig):
@@ -157475,7 +157660,7 @@ index d09511f..74be397 100644
          ck = self.__cached_keys
          if not isinstance(self, RepoConf):
              ck = set()
-@@ -823,39 +940,44 @@ class RepoConf(BaseConfig):
+@@ -823,39 +946,45 @@ class RepoConf(BaseConfig):
      bandwidth = Inherit(YumConf.bandwidth)
      throttle = Inherit(YumConf.throttle)
      timeout = Inherit(YumConf.timeout)
@@ -157483,6 +157668,7 @@ index d09511f..74be397 100644
 +
      http_caching = Inherit(YumConf.http_caching)
      metadata_expire = Inherit(YumConf.metadata_expire)
++    metadata_expire_filter = Inherit(YumConf.metadata_expire_filter)
      mirrorlist_expire = Inherit(YumConf.mirrorlist_expire)
      # NOTE: metalink expire _must_ be the same as metadata_expire, due to the
      #       checksumming of the repomd.xml.
@@ -157531,7 +157717,7 @@ index d09511f..74be397 100644
  
      # ' xemacs syntax hack
  
-@@ -876,20 +998,24 @@ def readStartupConfig(configfile, root):
+@@ -876,20 +1005,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
@@ -157562,7 +157748,7 @@ index d09511f..74be397 100644
      
      # ' xemacs syntax hack
  
-@@ -956,6 +1082,12 @@ def readMainConfig(startupconf):
+@@ -956,6 +1089,12 @@ def readMainConfig(startupconf):
      return yumconf
  
  def readVersionGroupsConfig(configfile="/etc/yum/version-groups.conf"):
@@ -157575,7 +157761,7 @@ index d09511f..74be397 100644
      parser = ConfigParser()
      confpp_obj = ConfigPreProcessor(configfile)
      try:
-@@ -970,17 +1102,16 @@ def readVersionGroupsConfig(configfile="/etc/yum/version-groups.conf"):
+@@ -970,17 +1109,16 @@ def readVersionGroupsConfig(configfile="/etc/yum/version-groups.conf"):
  
  
  def getOption(conf, section, name, option):
@@ -157603,7 +157789,7 @@ index d09511f..74be397 100644
      try: 
          val = conf.get(section, name)
      except (NoSectionError, NoOptionError):
-@@ -1028,25 +1159,28 @@ def _getsysver(installroot, distroverpkg):
+@@ -1028,25 +1166,28 @@ def _getsysver(installroot, distroverpkg):
      if idx.count() == 0:
          releasever = '$releasever'
      else:
@@ -157643,7 +157829,7 @@ index d09511f..74be397 100644
      # b/c repoids can have $values in them we need to map both ways to figure
      # out which one is which
      section_id = repo.id
-@@ -1054,6 +1188,19 @@ def writeRawRepoFile(repo,only=None):
+@@ -1054,6 +1195,19 @@ def writeRawRepoFile(repo,only=None):
          for sect in ini._sections.keys():
              if varReplace(sect, repo.yumvar) == repo.id:
                  section_id = sect
@@ -157663,7 +157849,7 @@ index d09511f..74be397 100644
      
      # Updated the ConfigParser with the changed values    
      cfgOptions = repo.cfg.options(repo.id)
-@@ -1069,7 +1216,7 @@ def writeRawRepoFile(repo,only=None):
+@@ -1069,7 +1223,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
@@ -157673,7 +157859,7 @@ index d09511f..74be397 100644
  
          if name not in cfgOptions and option.default == value:
 diff --git a/yum/depsolve.py b/yum/depsolve.py
-index 6d744c0..a16f1f5 100644
+index 6d744c0..74f9a48 100644
 --- a/yum/depsolve.py
 +++ b/yum/depsolve.py
 @@ -31,13 +31,15 @@ from transactioninfo import TransactionMember
@@ -158006,7 +158192,45 @@ index 6d744c0..a16f1f5 100644
          oldreqs = set(oldreqs)
  
          ret = []
-@@ -1150,6 +1297,11 @@ class Depsolve(object):
+@@ -974,12 +1121,36 @@ class Depsolve(object):
+             # FIXME: This is probably the best place to fix the postfix rename
+             # problem long term (post .21) ... see compare_providers.
+             for pkg, hits in self.tsInfo.getRequires(*prov).iteritems():
+-                # See the docs, this is to make groupremove "more useful".
++                # See the docs, this is to make remove* "more useful".
++                if (self.conf.repopkgsremove_leaf_only and txmbr.repopkg and
++                    txmbr.output_state == TS_ERASE):
++                    cb = self.dsCallback
++                    if cb and hasattr(cb, 'repoPkgRemoveReq'):
++                        cb.repoPkgRemoveReq(txmbr.po, hits)
++                    elif cb and hasattr(cb, 'removeReq'):
++                        cb.removeReq(txmbr.po, pkg, hits)
++                    #  We don't undo anything else here ... hopefully that's
++                    # fine.
++                    self.tsInfo.remove(txmbr.pkgtup)
++                    return []
++
+                 if (self.conf.groupremove_leaf_only and txmbr.groups and
+                     txmbr.output_state == TS_ERASE):
+                     cb = self.dsCallback
+                     if cb and hasattr(cb, 'groupRemoveReq'):
+                         cb.groupRemoveReq(pkg, hits)
++                    elif cb and hasattr(cb, 'removeReq'):
++                        cb.removeReq(txmbr.po, pkg, hits)
++                    #  We don't undo anything else here ... hopefully that's
++                    # fine.
++                    self.tsInfo.remove(txmbr.pkgtup)
++                    return []
++
++                if (self.conf.remove_leaf_only and
++                    txmbr.output_state == TS_ERASE):
++                    cb = self.dsCallback
++                    if cb and hasattr(cb, 'removeReq'):
++                        cb.removeReq(txmbr.po, pkg, hits)
+                     #  We don't undo anything else here ... hopefully that's
+                     # fine.
+                     self.tsInfo.remove(txmbr.pkgtup)
+@@ -1150,6 +1321,11 @@ class Depsolve(object):
          return ret
  
      def isPackageInstalled(self, pkgname):
@@ -158018,7 +158242,7 @@ index 6d744c0..a16f1f5 100644
          lst = self.tsInfo.matchNaevr(name = pkgname)
          for txmbr in lst:
              if txmbr.output_state in TS_INSTALL_STATES:
-@@ -1166,7 +1318,7 @@ class Depsolve(object):
+@@ -1166,7 +1342,7 @@ class Depsolve(object):
          return True
      _isPackageInstalled = isPackageInstalled
  
@@ -158027,7 +158251,7 @@ index 6d744c0..a16f1f5 100644
          """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 +1362,24 @@ class Depsolve(object):
+@@ -1210,6 +1386,24 @@ class Depsolve(object):
                  return None
              return x
  
@@ -158052,7 +158276,7 @@ index 6d744c0..a16f1f5 100644
          #  Actual start of _compare_providers().
  
          # Do a NameArch filtering, based on repo. __cmp__
-@@ -1332,6 +1502,26 @@ class Depsolve(object):
+@@ -1332,6 +1526,26 @@ class Depsolve(object):
                          _('common prefix of %s between %s and %s' % (cpl, po, reqpo)))
                  
                      pkgresults[po] += cpl*2
@@ -158079,7 +158303,7 @@ index 6d744c0..a16f1f5 100644
                  
          #  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 +1583,52 @@ class Depsolve(object):
+@@ -1393,42 +1607,52 @@ class Depsolve(object):
  
  
  class DepCheck(object):
@@ -158122,12 +158346,12 @@ index 6d744c0..a16f1f5 100644
  
  class Requires(object):
 -
+-    """
+-    A pure data class for holding a package and the list of things it
+-    requires.
 +    """A pure data class for holding a package and the list of things
 +    it requires.
      """
--    A pure data class for holding a package and the list of things it
--    requires.
--    """
 -
      def __init__(self, pkg,requires):
          self.pkg = pkg # po of requiring pkg
@@ -158136,12 +158360,12 @@ index 6d744c0..a16f1f5 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
@@ -161012,7 +161236,7 @@ index 8a6f6f3..65401a9 100644
          """Returns a list of pkg tuples (n, a, e, v, r), optionally from a
             single repoid. Note that the packages are always filtered to those
 diff --git a/yum/transactioninfo.py b/yum/transactioninfo.py
-index 4d89d83..2fdd8f0 100644
+index 4d89d83..96c75b5 100644
 --- a/yum/transactioninfo.py
 +++ b/yum/transactioninfo.py
 @@ -87,7 +87,8 @@ class TransactionData:
@@ -161156,27 +161380,30 @@ index 4d89d83..2fdd8f0 100644
          return main
      
      def findObsoletedByThisMember(self, txmbr):
-@@ -752,6 +793,7 @@ class TransactionMember:
+@@ -752,6 +793,8 @@ class TransactionMember:
          self.downgraded_by = []
          self.reinstall = False
          self.groups = [] # groups it's in
 +        self.environments = [] # Env. groups it's in
++        self.repopkg = None # repo pkg "group" it was removed/installed by
          self._poattr = ['pkgtup', 'repoid', 'name', 'arch', 'epoch', 'version',
                          'release']
  
-@@ -825,5 +867,7 @@ class TransactionMember:
+@@ -825,5 +868,9 @@ class TransactionMember:
                  
          if self.groups:
              msg += "  groups: %s\n" % ' '.join(self.groups)
 +        if self.environments:
 +            msg += "  environments: %s\n" % ' '.join(self.environments)
++        if self.repopkg:
++            msg += "  repopkg: %s\n" % self.repopkg
  
          return msg
 diff --git a/yum/update_md.py b/yum/update_md.py
-index 2cb1acb..d719be8 100644
+index 2cb1acb..3ac0010 100644
 --- a/yum/update_md.py
 +++ b/yum/update_md.py
-@@ -79,11 +79,37 @@ class UpdateNotice(object):
+@@ -79,11 +79,38 @@ class UpdateNotice(object):
  
      def __getitem__(self, item):
          """ Allows scriptable metadata access (ie: un['update_id']). """
@@ -161203,6 +161430,7 @@ index 2cb1acb..d719be8 100644
 +            return False
 +
 +        for data in ('type', 'update_id', 'status', 'rights',
++                     'severity', 'release',
 +                     'issued', 'updated', 'version', 'pushcount',
 +                     'from', 'title', 'summary', 'description', 'solution'):
 +            if self._md[data] != other._md[data]:
@@ -161215,7 +161443,7 @@ index 2cb1acb..d719be8 100644
      def text(self, skip_data=('files', 'summary', 'rights', 'solution')):
          head = """
  ===============================================================================
-@@ -304,7 +330,9 @@ class UpdateNotice(object):
+@@ -304,7 +331,9 @@ class UpdateNotice(object):
                  to_xml(self._md['title']), to_xml(self._md['release']),
                  to_xml(self._md['issued'], attrib=True),
                  to_xml(self._md['description']))
@@ -161226,7 +161454,7 @@ index 2cb1acb..d719be8 100644
          if self._md['summary']:
              msg += """  <summary>%s</summary>\n""" % (to_xml(self._md['summary']))
          if self._md['solution']:
-@@ -345,7 +373,7 @@ class UpdateNotice(object):
+@@ -345,7 +374,7 @@ class UpdateNotice(object):
                                  to_xml(pkg['version'], attrib=True),
                                  to_xml(pkg['filename']))
                  msg += """    </collection>\n"""
@@ -161235,14 +161463,15 @@ index 2cb1acb..d719be8 100644
          msg += """</update>\n"""
          return msg
  
-@@ -423,9 +451,42 @@ class UpdateMetadata(object):
+@@ -423,8 +452,41 @@ class UpdateMetadata(object):
      def add_notice(self, un):
          """ Add an UpdateNotice object. This should be fully populated with
              data, esp. update_id and pkglist/packages. """
 -        if not un or not un["update_id"] or un['update_id'] in self._notices:
+-            return
 +        if not un or not un["update_id"]:
-             return
- 
++            return False
++
 +        #  This is "special", the main thing we want to deal with here is
 +        # having one errata that has multiple packages in it rpmA and rpmB, but
 +        # the packages are in repos. repoA and repoB. So instead of doing a
@@ -161252,7 +161481,7 @@ index 2cb1acb..d719be8 100644
 +        if un['update_id'] in self._notices:
 +            oun = self._notices[un['update_id']]
 +            if oun != un:
-+                return
++                return False
 +
 +            # Ok, main parts of errata are the same, so now merge references:
 +            seen = set()
@@ -161275,12 +161504,30 @@ index 2cb1acb..d719be8 100644
 +                oun['pkglist'].append(pkg)
 +
 +            un = oun
-+
+ 
          self._notices[un['update_id']] = un
          for pkg in un['pkglist']:
-             for filedata in pkg['packages']:
+@@ -435,6 +497,8 @@ class UpdateMetadata(object):
+                 no = self._no_cache.setdefault(filedata['name'], set())
+                 no.add(un)
+ 
++        return True
++
+     def add(self, obj, mdtype='updateinfo'):
+         """ Parse a metadata from a given YumRepository, file, or filename. """
+         if not obj:
+@@ -464,7 +528,8 @@ class UpdateMetadata(object):
+                     print >> sys.stderr, "An update notice is broken, skipping."
+                     # what else should we do?
+                     continue
+-                self.add_notice(un)
++                if not self.add_notice(un):
++                    print >> sys.stderr, "An update notice is broken, or duplicate, skipping:", un['update_id']
+ 
+     def __unicode__(self):
+         ret = u''
 diff --git a/yum/yumRepo.py b/yum/yumRepo.py
-index e5e9ece..02778dd 100644
+index e5e9ece..09b2534 100644
 --- a/yum/yumRepo.py
 +++ b/yum/yumRepo.py
 @@ -20,10 +20,12 @@ import time
@@ -161348,7 +161595,7 @@ index e5e9ece..02778dd 100644
              if item == 'metadata':
                  mydbtype = 'primary_db'
                  mymdtype = 'primary'
-@@ -162,25 +182,39 @@ class YumPackageSack(packageSack.PackageSack):
+@@ -162,25 +182,37 @@ class YumPackageSack(packageSack.PackageSack):
                  continue
  
              if self._check_db_version(repo, mydbtype):
@@ -161371,8 +161618,6 @@ index e5e9ece..02778dd 100644
 -                            db_un_fn = misc.decompress(db_fn)
 -                            misc.unlink_f(db_fn)
 -                            db_un_fn = self._check_uncompressed_db(repo, mydbtype)
-+                        # unlink the decompressed file, we know it's not valid
-+                        misc.unlink_f(repo.cachedir +'/gen/%s.sqlite' % mydbtype)
 +                        db_un_fn = self._check_uncompressed_db_gen(repo,
 +                                                                   mydbtype)
 +                    if not db_un_fn: # Shouldn't happen?
@@ -161396,7 +161641,7 @@ index e5e9ece..02778dd 100644
                  xmldata = repo.repoXML.getData(mymdtype)
                  (ctype, csum) = xmldata.checksum
                  dobj = repo_cache_function(xml, csum)
-@@ -193,6 +227,25 @@ class YumPackageSack(packageSack.PackageSack):
+@@ -193,6 +225,25 @@ class YumPackageSack(packageSack.PackageSack):
          # get rid of all this stuff we don't need now
          del repo.cacheHandler
  
@@ -161422,7 +161667,7 @@ index e5e9ece..02778dd 100644
      def _check_uncompressed_db(self, repo, mdtype):
          """return file name of uncompressed db is good, None if not"""
          mydbdata = repo.repoXML.getData(mdtype)
-@@ -201,9 +254,11 @@ class YumPackageSack(packageSack.PackageSack):
+@@ -201,9 +252,11 @@ class YumPackageSack(packageSack.PackageSack):
          compressed_fn = repo.cachedir + '/' + fname
          db_un_fn = misc.decompress(compressed_fn, fn_only=True)
  
@@ -161435,7 +161680,7 @@ index e5e9ece..02778dd 100644
          if os.path.exists(db_un_fn):
              if skip_old_DBMD_check and repo._using_old_MD:
                  return db_un_fn
-@@ -260,10 +315,11 @@ class YumRepository(Repository, config.RepoConf):
+@@ -260,10 +313,11 @@ class YumRepository(Repository, config.RepoConf):
          self.copy_local = 0
          # holder for stuff we've grabbed
          self.retrieved = { 'primary':0, 'filelists':0, 'other':0, 'group':0,
@@ -161448,7 +161693,7 @@ index e5e9ece..02778dd 100644
          self.failure_obj = None
          self.mirror_failure_obj = None
          self.interrupt_callback = None
-@@ -285,6 +341,7 @@ class YumRepository(Repository, config.RepoConf):
+@@ -285,6 +339,7 @@ class YumRepository(Repository, config.RepoConf):
  
          self._grabfunc = None
          self._grab = None
@@ -161456,7 +161701,7 @@ index e5e9ece..02778dd 100644
  
      def __cmp__(self, other):
          """ Sort yum repos. by cost, and then by alphanumeric on their id. """
-@@ -311,6 +368,37 @@ class YumRepository(Repository, config.RepoConf):
+@@ -311,6 +366,37 @@ class YumRepository(Repository, config.RepoConf):
          return self._sack
      sack = property(_getSack)
  
@@ -161494,7 +161739,7 @@ index e5e9ece..02778dd 100644
      def close(self):
          if self._sack is not None:
              self.sack.close()
-@@ -350,6 +438,10 @@ class YumRepository(Repository, config.RepoConf):
+@@ -350,6 +436,10 @@ class YumRepository(Repository, config.RepoConf):
          return thisdata.location
  
      def __str__(self):
@@ -161505,7 +161750,7 @@ index e5e9ece..02778dd 100644
          return self.id
  
      def _checksum(self, sumtype, file, CHUNK=2**16, checksum_can_fail=False,
-@@ -378,7 +470,7 @@ class YumRepository(Repository, config.RepoConf):
+@@ -378,7 +468,7 @@ class YumRepository(Repository, config.RepoConf):
                          'basecachedir', 'http_headers', 'metadata_cookie',
                          'metadata_cookie_fn', 'quick_enable_disable',
                          'repoMDFile', 'timestamp_check', 'urls', 'mirrorurls',
@@ -161514,7 +161759,7 @@ index e5e9ece..02778dd 100644
          for attr in dir(self):
              if attr.startswith('_'):
                  continue
-@@ -422,7 +514,7 @@ class YumRepository(Repository, config.RepoConf):
+@@ -422,7 +512,7 @@ class YumRepository(Repository, config.RepoConf):
             on then raise a repo error"""
          if len(self._urls) < 1 and not self.mediaid:
              raise Errors.RepoError, \
@@ -161523,7 +161768,7 @@ index e5e9ece..02778dd 100644
  
      def doProxyDict(self):
          if self._proxy_dict:
-@@ -431,25 +523,18 @@ class YumRepository(Repository, config.RepoConf):
+@@ -431,25 +521,18 @@ class YumRepository(Repository, config.RepoConf):
          self._proxy_dict = {} # zap it
          proxy_string = None
          empty = (None, '_none_', '')
@@ -161557,7 +161802,7 @@ index e5e9ece..02778dd 100644
  
          if proxy_string is not None:
              self._proxy_dict['http'] = proxy_string
-@@ -483,13 +568,30 @@ class YumRepository(Repository, config.RepoConf):
+@@ -483,13 +566,30 @@ class YumRepository(Repository, config.RepoConf):
  
          ugopts = self._default_grabopts()
          self._grabfunc = URLGrabber(progress_obj=self.callback,
@@ -161589,7 +161834,7 @@ index e5e9ece..02778dd 100644
                               failure_callback=self.mirror_failure_obj)
  
      def _default_grabopts(self, cache=True):
-@@ -499,6 +601,7 @@ class YumRepository(Repository, config.RepoConf):
+@@ -499,6 +599,7 @@ class YumRepository(Repository, config.RepoConf):
                   'throttle': self.throttle,
                   'proxies': self.proxy_dict,
                   'timeout': self.timeout,
@@ -161597,7 +161842,7 @@ index e5e9ece..02778dd 100644
                   'http_headers': tuple(self.__headersListFromDict(cache=cache)),
                   'ssl_verify_peer': self.sslverify,
                   'ssl_verify_host': self.sslverify,
-@@ -561,7 +664,7 @@ class YumRepository(Repository, config.RepoConf):
+@@ -561,7 +662,7 @@ class YumRepository(Repository, config.RepoConf):
          cookie = self.cachedir + '/' + self.metadata_cookie_fn
          self.setAttribute('_dir_setup_metadata_cookie', cookie)
  
@@ -161606,7 +161851,7 @@ index e5e9ece..02778dd 100644
              self._dirSetupMkdir_p(dir)
  
          # persistdir is really root-only but try the make anyway and just
-@@ -714,15 +817,15 @@ class YumRepository(Repository, config.RepoConf):
+@@ -714,15 +815,15 @@ class YumRepository(Repository, config.RepoConf):
              local = self.metalink_filename + '.tmp'
              if not self._metalinkCurrent():
                  url = misc.to_utf8(self.metalink)
@@ -161626,7 +161871,7 @@ index e5e9ece..02778dd 100644
                          raise Errors.RepoError, msg
                      #  Now, we have an old usable metalink, so we can't move to
                      # a newer repomd.xml ... or checksums won't match.
-@@ -749,9 +852,22 @@ class YumRepository(Repository, config.RepoConf):
+@@ -749,9 +850,22 @@ class YumRepository(Repository, config.RepoConf):
                                                                value),
                               fdel=lambda self: setattr(self, "_metalink", None))
  
@@ -161650,7 +161895,7 @@ index e5e9ece..02778dd 100644
          """retrieve file from the mirrorgroup for the repo
             relative to local, optionally get range from
             start to end, also optionally retrieve from a specific baseurl"""
-@@ -768,7 +884,7 @@ class YumRepository(Repository, config.RepoConf):
+@@ -768,7 +882,7 @@ class YumRepository(Repository, config.RepoConf):
  
          if local is None or relative is None:
              raise Errors.RepoError, \
@@ -161659,7 +161904,7 @@ index e5e9ece..02778dd 100644
  
          if self.cache == 1:
              if os.path.exists(local): # FIXME - we should figure out a way
-@@ -778,7 +894,7 @@ class YumRepository(Repository, config.RepoConf):
+@@ -778,7 +892,7 @@ class YumRepository(Repository, config.RepoConf):
                  raise Errors.RepoError, \
                      "Caching enabled but no local cache of %s from %s" % (local,
  
@@ -161668,7 +161913,7 @@ index e5e9ece..02778dd 100644
  
          if url:
              (scheme, netloc, path, query, fragid) = urlparse.urlsplit(url)
-@@ -796,6 +912,16 @@ class YumRepository(Repository, config.RepoConf):
+@@ -796,6 +910,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,))
  
@@ -161685,7 +161930,7 @@ index e5e9ece..02778dd 100644
          if url and scheme != "media":
              ugopts = self._default_grabopts(cache=cache)
              ug = URLGrabber(progress_obj = self.callback,
-@@ -815,14 +941,11 @@ class YumRepository(Repository, config.RepoConf):
+@@ -815,14 +939,11 @@ class YumRepository(Repository, config.RepoConf):
                                      range=(start, end),
                                      )
              except URLGrabError, e:
@@ -161702,7 +161947,7 @@ index e5e9ece..02778dd 100644
  
  
          else:
-@@ -835,19 +958,18 @@ class YumRepository(Repository, config.RepoConf):
+@@ -835,19 +956,18 @@ class YumRepository(Repository, config.RepoConf):
                                             reget = reget,
                                             checkfunc=checkfunc,
                                             http_headers=headers,
@@ -161728,7 +161973,7 @@ index e5e9ece..02778dd 100644
          remote = package.relativepath
          local = package.localPkg()
          basepath = package.basepath
-@@ -857,15 +979,30 @@ class YumRepository(Repository, config.RepoConf):
+@@ -857,15 +977,30 @@ class YumRepository(Repository, config.RepoConf):
                  return local
              misc.unlink_f(local)
  
@@ -161760,7 +162005,41 @@ index e5e9ece..02778dd 100644
      def getHeader(self, package, checkfunc = None, reget = 'simple',
              cache = True):
  
-@@ -991,7 +1128,7 @@ class YumRepository(Repository, config.RepoConf):
+@@ -933,7 +1068,7 @@ class YumRepository(Repository, config.RepoConf):
+             self._metadataCurrent = False
+         return self._metadataCurrent
+ 
+-    def withinCacheAge(self, myfile, expiration_time):
++    def withinCacheAge(self, myfile, expiration_time, expire_req_filter=True):
+         """check if any file is older than a certain amount of time. Used for
+            the cachecookie and the mirrorlist
+            return True if w/i the expiration time limit
+@@ -943,6 +1078,24 @@ class YumRepository(Repository, config.RepoConf):
+            file. If any of them are newer then invalidate the cache
+            """
+ 
++        # Never/write means we just skip this...
++        if (expire_req_filter and hasattr(self, '_metadata_cache_req') and
++            self._metadata_cache_req.startswith("read-only:") and
++            self.metadata_expire_filter.startswith("read-only:")):
++
++            cache_filt = self.metadata_expire_filter[len("read-only:"):]
++            cache_req  = self._metadata_cache_req[len("read-only:"):]
++
++            if cache_filt == 'future':
++                assert cache_req in ('past', 'present', 'future')
++                expiration_time = -1
++            if cache_filt == 'present':
++                if cache_req in ('past', 'present'):
++                    expiration_time = -1
++            if cache_filt == 'past':
++                if cache_req == 'past':
++                    expiration_time = -1
++
+         # -1 is special and should never get refreshed
+         if expiration_time == -1 and os.path.exists(myfile):
+             return True
+@@ -991,7 +1144,7 @@ class YumRepository(Repository, config.RepoConf):
      def _cachingRepoXML(self, local):
          """ Should we cache the current repomd.xml """
          if self.cache and not os.path.exists(local):
@@ -161769,7 +162048,7 @@ index e5e9ece..02778dd 100644
          if self.cache or self.metadataCurrent():
              return True
          return False
-@@ -1020,7 +1157,7 @@ class YumRepository(Repository, config.RepoConf):
+@@ -1020,7 +1173,7 @@ class YumRepository(Repository, config.RepoConf):
              if grab_can_fail:
                  return None
              raise Errors.RepoError, 'Error downloading file %s: %s' % (local, e)
@@ -161778,7 +162057,7 @@ index e5e9ece..02778dd 100644
              misc.unlink_f(tfname)
              if grab_can_fail:
                  return None
-@@ -1047,7 +1184,7 @@ class YumRepository(Repository, config.RepoConf):
+@@ -1047,7 +1200,7 @@ class YumRepository(Repository, config.RepoConf):
                  parse_can_fail = 'old_repo_XML' in self._oldRepoMDData
              if parse_can_fail:
                  return None
@@ -161787,7 +162066,7 @@ index e5e9ece..02778dd 100644
  
      def _saveOldRepoXML(self, local):
          """ If we have an older repomd.xml file available, save it out. """
-@@ -1074,7 +1211,7 @@ class YumRepository(Repository, config.RepoConf):
+@@ -1074,7 +1227,7 @@ class YumRepository(Repository, config.RepoConf):
          #  We still want the old data, so we don't download twice. So we
          # pretend everything is good until the revert.
          if not self.timestamp_check:
@@ -161796,7 +162075,7 @@ index e5e9ece..02778dd 100644
  
          if 'old_repo_XML' not in self._oldRepoMDData:
              self._oldRepoMDData = {}
-@@ -1260,6 +1397,9 @@ class YumRepository(Repository, config.RepoConf):
+@@ -1260,6 +1413,9 @@ class YumRepository(Repository, config.RepoConf):
          return True
  
      def _check_db_version(self, mdtype, repoXML=None):
@@ -161806,7 +162085,7 @@ index e5e9ece..02778dd 100644
          if repoXML is None:
              repoXML = self.repoXML
          if mdtype in repoXML.repoData:
-@@ -1277,11 +1417,11 @@ class YumRepository(Repository, config.RepoConf):
+@@ -1277,11 +1433,11 @@ class YumRepository(Repository, config.RepoConf):
              return None
  
          if not file_check:
@@ -161821,7 +162100,7 @@ index e5e9ece..02778dd 100644
              if not os.path.exists(local):
                  local = misc.decompress(local, fn_only=True)
                  compressed = True
-@@ -1302,6 +1442,17 @@ class YumRepository(Repository, config.RepoConf):
+@@ -1302,6 +1458,17 @@ class YumRepository(Repository, config.RepoConf):
              into the delete list, this means metadata can change filename
              without us leaking it. """
  
@@ -161839,7 +162118,7 @@ index e5e9ece..02778dd 100644
          def _mdtype_eq(omdtype, odata, nmdtype, ndata):
              """ Check if two returns from _get_mdtype_data() are equal. """
              if ndata is None:
-@@ -1321,6 +1472,14 @@ class YumRepository(Repository, config.RepoConf):
+@@ -1321,6 +1488,14 @@ class YumRepository(Repository, config.RepoConf):
              return True
  
          all_mdtypes = self.retrieved.keys()
@@ -161854,7 +162133,7 @@ index e5e9ece..02778dd 100644
          if mdtypes is None:
              mdtypes = all_mdtypes
  
-@@ -1333,8 +1492,7 @@ class YumRepository(Repository, config.RepoConf):
+@@ -1333,8 +1508,7 @@ class YumRepository(Repository, config.RepoConf):
  
          # Inited twice atm. ... sue me
          self._oldRepoMDData['new_MD_files'] = []
@@ -161864,7 +162143,7 @@ index e5e9ece..02778dd 100644
          for mdtype in all_mdtypes:
              (nmdtype, ndata) = self._get_mdtype_data(mdtype)
  
-@@ -1371,43 +1529,16 @@ class YumRepository(Repository, config.RepoConf):
+@@ -1371,43 +1545,16 @@ class YumRepository(Repository, config.RepoConf):
              # No old repomd data, but we might still have uncompressed MD
              if self._groupCheckDataMDValid(ndata, nmdtype, mdtype):
                  continue
@@ -161879,14 +162158,14 @@ index e5e9ece..02778dd 100644
 -        if len(downloading_with_size) == 1:
 -            downloading_no_size.extend(downloading_with_size)
 -            downloading_with_size = []
--
++    def _commonRetrieveDataMD_done(self, downloading):
++        """ Uncompress the downloaded metadata """
+ 
 -        remote_size = 0
 -        local_size  = 0
 -        for (ndata, nmdtype) in downloading_with_size: # Get total size...
 -            remote_size += int(ndata.size)
-+    def _commonRetrieveDataMD_done(self, downloading):
-+        """ Uncompress the downloaded metadata """
- 
+-
 -        for (ndata, nmdtype) in downloading_with_size:
 -            urlgrabber.progress.text_meter_total_size(remote_size, local_size)
 -            if not self._retrieveMD(nmdtype, retrieve_can_fail=True):
@@ -161913,7 +162192,7 @@ index e5e9ece..02778dd 100644
  
      def _groupLoadRepoXML(self, text=None, mdtypes=None):
          """ Retrieve the new repomd.xml from the repository, then check it
-@@ -1421,7 +1552,7 @@ class YumRepository(Repository, config.RepoConf):
+@@ -1421,7 +1568,7 @@ class YumRepository(Repository, config.RepoConf):
              self._commonRetrieveDataMD(mdtypes)
  
      def _mdpolicy2mdtypes(self):
@@ -161922,7 +162201,7 @@ index e5e9ece..02778dd 100644
                       'group:primary' : ['primary'],
                       'group:small'   : ["primary", "updateinfo"],
                       'group:main'    : ["primary", "group", "filelists",
-@@ -1436,6 +1567,7 @@ class YumRepository(Repository, config.RepoConf):
+@@ -1436,6 +1583,7 @@ class YumRepository(Repository, config.RepoConf):
          if not mdtypes or 'group:all' in mdtypes:
              mdtypes = None
          else:
@@ -161930,7 +162209,7 @@ index e5e9ece..02778dd 100644
              mdtypes = sorted(list(mdtypes))
          return mdtypes
  
-@@ -1446,17 +1578,12 @@ class YumRepository(Repository, config.RepoConf):
+@@ -1446,17 +1594,12 @@ class YumRepository(Repository, config.RepoConf):
          except KeyboardInterrupt:
              self._revertOldRepoXML() # Undo metadata cookie?
              raise
@@ -161950,7 +162229,7 @@ index e5e9ece..02778dd 100644
          return self._repoXML
  
  
-@@ -1480,7 +1607,7 @@ class YumRepository(Repository, config.RepoConf):
+@@ -1480,7 +1623,7 @@ class YumRepository(Repository, config.RepoConf):
                  result = self._getFile(relative='repodata/repomd.xml.asc',
                                         copy_local=1,
                                         local = sigfile,
@@ -161959,7 +162238,7 @@ index e5e9ece..02778dd 100644
                                         reget=None,
                                         checkfunc=None,
                                         cache=self.http_caching == 'all',
-@@ -1514,7 +1641,7 @@ class YumRepository(Repository, config.RepoConf):
+@@ -1514,7 +1657,7 @@ class YumRepository(Repository, config.RepoConf):
          return self._checkMD(fn, mdtype, openchecksum)
  
      def _checkMD(self, fn, mdtype, openchecksum=False,
@@ -161968,7 +162247,7 @@ index e5e9ece..02778dd 100644
          """ Internal function, use .checkMD() from outside yum. """
  
          thisdata = data # So the argument name is nicer
-@@ -1537,6 +1664,18 @@ class YumRepository(Repository, config.RepoConf):
+@@ -1537,6 +1680,18 @@ class YumRepository(Repository, config.RepoConf):
          if size is not None:
              size = int(size)
  
@@ -161987,7 +162266,7 @@ index e5e9ece..02778dd 100644
          try: # get the local checksum
              l_csum = self._checksum(r_ctype, file, datasize=size)
          except Errors.RepoError, e:
-@@ -1551,15 +1690,13 @@ class YumRepository(Repository, config.RepoConf):
+@@ -1551,15 +1706,13 @@ class YumRepository(Repository, config.RepoConf):
                  return None
              raise URLGrabError(-1, 'Metadata file does not match checksum')
  
@@ -162004,7 +162283,7 @@ index e5e9ece..02778dd 100644
          """ Internal function, use .retrieveMD() from outside yum. """
          #  Note that this can raise Errors.RepoMDError if mdtype doesn't exist
          # for this repo.
-@@ -1580,15 +1717,19 @@ class YumRepository(Repository, config.RepoConf):
+@@ -1580,15 +1733,19 @@ class YumRepository(Repository, config.RepoConf):
                  try:
                      self.checkMD(local, mdtype)
                  except URLGrabError, e:
@@ -162025,7 +162304,7 @@ index e5e9ece..02778dd 100644
  
          if (os.path.exists(local) or
              self._preload_md_from_system_cache(os.path.basename(local))):
-@@ -1597,8 +1738,10 @@ class YumRepository(Repository, config.RepoConf):
+@@ -1597,8 +1754,10 @@ class YumRepository(Repository, config.RepoConf):
                  return local # it's the same return the local one
  
          try:
@@ -162038,7 +162317,7 @@ index e5e9ece..02778dd 100644
              if thisdata.size is None:
                  reget = None
              else:
-@@ -1613,8 +1756,9 @@ class YumRepository(Repository, config.RepoConf):
+@@ -1613,8 +1772,9 @@ class YumRepository(Repository, config.RepoConf):
                                    checkfunc=checkfunc, 
                                    text=text,
                                    cache=self.http_caching == 'all',
@@ -162050,7 +162329,7 @@ index e5e9ece..02778dd 100644
              if retrieve_can_fail:
                  return None
              raise
-@@ -1622,9 +1766,8 @@ class YumRepository(Repository, config.RepoConf):
+@@ -1622,9 +1782,8 @@ class YumRepository(Repository, config.RepoConf):
              if retrieve_can_fail:
                  return None
              raise Errors.RepoError, \
@@ -162061,7 +162340,7 @@ index e5e9ece..02778dd 100644
              return local
  
  
-@@ -1646,13 +1789,21 @@ class YumRepository(Repository, config.RepoConf):
+@@ -1646,13 +1805,21 @@ class YumRepository(Repository, config.RepoConf):
  
      def getGroups(self):
          """gets groups and returns group file path for the repository, if there
@@ -162086,7 +162365,7 @@ index e5e9ece..02778dd 100644
          self._callbacks_changed = True
  
      def setFailureObj(self, failure_obj):
-@@ -1681,7 +1832,7 @@ class YumRepository(Repository, config.RepoConf):
+@@ -1681,7 +1848,7 @@ class YumRepository(Repository, config.RepoConf):
                  print "Could not read mirrorlist %s, error was \n%s" %(url, e)
                  content = []
              for line in content:
@@ -162095,7 +162374,17 @@ index e5e9ece..02778dd 100644
                      continue
                  mirror = line.rstrip() # no more trailing \n's
                  mirror = mirror.replace('$ARCH', '$BASEARCH')
-@@ -1713,7 +1864,7 @@ class YumRepository(Repository, config.RepoConf):
+@@ -1701,7 +1868,8 @@ class YumRepository(Repository, config.RepoConf):
+         fo = None
+ 
+         cacheok = False
+-        if self.withinCacheAge(self.mirrorlist_file, self.mirrorlist_expire):
++        if self.withinCacheAge(self.mirrorlist_file, self.mirrorlist_expire,
++                               expire_req_filter=False):
+             cacheok = True
+             fo = open(self.mirrorlist_file, 'r')
+             url = 'file://' + self.mirrorlist_file # just to keep self._readMirrorList(fo,url) happy
+@@ -1713,7 +1881,7 @@ class YumRepository(Repository, config.RepoConf):
              ugopts = self._default_grabopts()
              try:
                  fo = urlgrabber.grabber.urlopen(url, **ugopts)
@@ -162104,7 +162393,7 @@ index e5e9ece..02778dd 100644
                  print "Could not retrieve mirrorlist %s error was\n%s: %s" % (url, e.args[0], misc.to_unicode(e.args[1]))
                  fo = None
  
-@@ -1877,7 +2028,7 @@ def getMirrorList(mirrorlist, pdict = None):
+@@ -1877,7 +2045,7 @@ def getMirrorList(mirrorlist, pdict = None):
  
      try:
          fo = urlresolver.urlopen(url, proxies=pdict)
@@ -162114,10 +162403,18 @@ index e5e9ece..02778dd 100644
          fo = None
  
 diff --git a/yumcommands.py b/yumcommands.py
-index 4dcbea7..a2e0b1b 100644
+index 4dcbea7..591b541 100644
 --- a/yumcommands.py
 +++ b/yumcommands.py
-@@ -29,7 +29,7 @@ import operator
+@@ -22,14 +22,14 @@ Classes for subcommands of the yum command line interface.
+ import os
+ import cli
+ from yum import logginglevels
+-from yum import _
++from yum import _, P_
+ from yum import misc
+ import yum.Errors
+ import operator
  import locale
  import fnmatch
  import time
@@ -162154,7 +162451,7 @@ index 4dcbea7..a2e0b1b 100644
      if not base.gpgKeyCheck():
          for repo in base.repos.listEnabled():
              if (repo.gpgcheck or repo.repo_gpgcheck) and not repo.gpgkey:
-@@ -75,6 +83,14 @@ For more information contact your distribution or package provider.
+@@ -75,31 +83,118 @@ For more information contact your distribution or package provider.
                  raise cli.CliError
  
  def checkPackageArg(base, basecmd, extcmds):
@@ -162169,9 +162466,61 @@ index 4dcbea7..a2e0b1b 100644
      if len(extcmds) == 0:
          base.logger.critical(
                  _('Error: Need to pass a list of pkgs to %s') % basecmd)
-@@ -82,24 +98,51 @@ def checkPackageArg(base, basecmd, extcmds):
+         _err_mini_usage(base, basecmd)
          raise cli.CliError
  
++def checkSwapPackageArg(base, basecmd, extcmds):
++    """Verify that *extcmds* contains the name of at least two packages for
++    *basecmd* to act on.
++
++    :param base: a :class:`yum.Yumbase` object.
++    :param basecmd: the name of the command being checked for
++    :param extcmds: a list of arguments passed to *basecmd*
++    :raises: :class:`cli.CliError`
++    """
++    min_args = 2
++    if '--' in extcmds:
++        min_args = 3
++    if len(extcmds) < min_args:
++        base.logger.critical(
++                _('Error: Need at least two packages to %s') % basecmd)
++        _err_mini_usage(base, basecmd)
++        raise cli.CliError
++
++def checkRepoPackageArg(base, basecmd, extcmds):
++    """Verify that *extcmds* contains the name of at least one package for
++    *basecmd* to act on.
++
++    :param base: a :class:`yum.Yumbase` object.
++    :param basecmd: the name of the command being checked for
++    :param extcmds: a list of arguments passed to *basecmd*
++    :raises: :class:`cli.CliError`
++    """
++    if len(extcmds) < 2: # <repoid> install|remove [pkgs]
++        base.logger.critical(
++                _('Error: Need to pass a repoid. and command to %s') % basecmd)
++        _err_mini_usage(base, basecmd)
++        raise cli.CliError
++
++    repos = base.repos.findRepos(extcmds[0])
++    if not repos:
++        base.logger.critical(
++                _('Error: Need to pass a single valid repoid. to %s') % basecmd)
++        _err_mini_usage(base, basecmd)
++        raise cli.CliError
++
++    if len(repos) != 1 or repos[0].id != extcmds[0]:
++        base.logger.critical(
++                _('Error: Need to pass a single valid repoid. to %s') % basecmd)
++        _err_mini_usage(base, basecmd)
++        raise cli.CliError
++    if not repos[0].isEnabled():
++        base.logger.critical(
++                _('Error: Repo %s is not enabled') % extcmds[0])
++        _err_mini_usage(base, basecmd)
++        raise cli.CliError
++
++
  def checkItemArg(base, basecmd, extcmds):
 +    """Verify that *extcmds* contains the name of at least one item for
 +    *basecmd* to act on.  Generally, the items are command-line
@@ -162221,7 +162570,7 @@ index 4dcbea7..a2e0b1b 100644
  
      for cmd in extcmds:
          if cmd not in VALID_ARGS:
-@@ -108,12 +151,14 @@ def checkCleanArg(base, basecmd, extcmds):
+@@ -108,12 +203,14 @@ def checkCleanArg(base, basecmd, extcmds):
              raise cli.CliError
  
  def checkShellArg(base, basecmd, extcmds):
@@ -162242,7 +162591,7 @@ index 4dcbea7..a2e0b1b 100644
      """
      if len(extcmds) == 0:
          base.verbose_logger.debug(_("No argument to shell"))
-@@ -133,10 +178,12 @@ def checkShellArg(base, basecmd, extcmds):
+@@ -133,10 +230,12 @@ def checkShellArg(base, basecmd, extcmds):
          raise cli.CliError
  
  def checkEnabledRepo(base, possible_local_files=[]):
@@ -162258,7 +162607,7 @@ index 4dcbea7..a2e0b1b 100644
      """
      if base.repos.listEnabled():
          return
-@@ -152,113 +199,273 @@ def checkEnabledRepo(base, possible_local_files=[]):
+@@ -152,113 +251,301 @@ def checkEnabledRepo(base, possible_local_files=[]):
      raise cli.CliError
  
  class YumCommand:
@@ -162354,7 +162703,35 @@ index 4dcbea7..a2e0b1b 100644
 +        :return: True if a transaction set is needed, False otherwise
 +        """
          return True
++
++    #  Some of this is subjective, esp. between past/present, but roughly use:
++    #
++    # write = I'm using package data to alter the rpmdb in anyway.
++    # read-only:future  = I'm providing data that is likely to result in a
++    #                     future write, so we might as well do it now.
++    #                     Eg. yum check-update && yum update -q -y
++    # read-only:present = I'm providing data about the present state of
++    #                     packages in the repo.
++    #                     Eg. yum list yum
++    # read-only:past    = I'm providing context data about past writes, or just
++    #                     anything that is available is good enough for me
++    #                     (speed is much better than quality).
++    #                     Eg. yum history info
++    #                     Eg. TAB completion
++    #
++    # ...default is write, which does the same thing we always did (obey
++    # metadata_expire and live with it).
++    def cacheRequirement(self, base, basecmd, extcmds):
++        """Return the cache requirements for the remote repos.
++
++        :param base: a :class:`yum.Yumbase` object
++        :param basecmd: the name of the command
++        :param extcmds: a list of arguments passed to *basecmd*
++        :return: Type of requirement: read-only:past, read-only:present, read-only:future, write
++        """
++        return 'write'
          
++
  class InstallCommand(YumCommand):
 +    """A class containing methods needed by the cli to execute the
 +    install command.
@@ -162548,7 +162925,7 @@ index 4dcbea7..a2e0b1b 100644
  
  def _add_pkg_simple_list_lens(data, pkg, indent=''):
      """ Get the length of each pkg's column. Add that to data.
-@@ -289,21 +496,56 @@ def _list_cmd_calc_columns(base, ypl):
+@@ -289,21 +576,56 @@ def _list_cmd_calc_columns(base, ypl):
      return (-columns[0], -columns[1], -columns[2])
  
  class InfoCommand(YumCommand):
@@ -162606,7 +162983,7 @@ index 4dcbea7..a2e0b1b 100644
          else:
              update_pkgs = {}
              inst_pkgs   = {}
-@@ -341,6 +583,7 @@ class InfoCommand(YumCommand):
+@@ -341,6 +663,7 @@ class InfoCommand(YumCommand):
                          local_pkgs[(po.name, po.arch)] = po
  
              # Output the packages:
@@ -162614,7 +162991,7 @@ index 4dcbea7..a2e0b1b 100644
              clio = base.conf.color_list_installed_older
              clin = base.conf.color_list_installed_newer
              clir = base.conf.color_list_installed_reinstall
-@@ -348,7 +591,9 @@ class InfoCommand(YumCommand):
+@@ -348,7 +671,9 @@ class InfoCommand(YumCommand):
              rip = base.listPkgs(ypl.installed, _('Installed Packages'), basecmd,
                                  highlight_na=update_pkgs, columns=columns,
                                  highlight_modes={'>' : clio, '<' : clin,
@@ -162624,7 +163001,7 @@ index 4dcbea7..a2e0b1b 100644
              clau = base.conf.color_list_available_upgrade
              clad = base.conf.color_list_available_downgrade
              clar = base.conf.color_list_available_reinstall
-@@ -356,6 +601,7 @@ class InfoCommand(YumCommand):
+@@ -356,6 +681,7 @@ class InfoCommand(YumCommand):
              rap = base.listPkgs(ypl.available, _('Available Packages'), basecmd,
                                  highlight_na=inst_pkgs, columns=columns,
                                  highlight_modes={'<' : clau, '>' : clad,
@@ -162632,7 +163009,7 @@ index 4dcbea7..a2e0b1b 100644
                                                   '=' : clar, 'not in' : clai})
              rep = base.listPkgs(ypl.extras, _('Extra Packages'), basecmd,
                                  columns=columns)
-@@ -389,45 +635,144 @@ class InfoCommand(YumCommand):
+@@ -389,45 +715,157 @@ class InfoCommand(YumCommand):
              return 0, []
  
      def needTs(self, base, basecmd, extcmds):
@@ -162649,6 +163026,19 @@ index 4dcbea7..a2e0b1b 100644
          
          return True
  
++    def cacheRequirement(self, base, basecmd, extcmds):
++        """Return the cache requirements for the remote repos.
++
++        :param base: a :class:`yum.Yumbase` object
++        :param basecmd: the name of the command
++        :param extcmds: a list of arguments passed to *basecmd*
++        :return: Type of requirement: read-only:past, read-only:present, read-only:future, write
++        """
++        if len(extcmds) and extcmds[0] in ('updates', 'obsoletes'):
++            return 'read-only:future'
++        return 'read-only:present'
++
++
  class ListCommand(InfoCommand):
 +    """A class containing methods needed by the cli to execute the
 +    list command.
@@ -162780,7 +163170,7 @@ index 4dcbea7..a2e0b1b 100644
          return True
  
   
-@@ -442,12 +787,25 @@ class GroupsCommand(YumCommand):
+@@ -442,12 +880,25 @@ class GroupsCommand(YumCommand):
                         'groupinfo'    : 'info'}
  
      def getNames(self):
@@ -162806,7 +163196,7 @@ index 4dcbea7..a2e0b1b 100644
          return _("Display, or use, the groups information")
      
      def _grp_setup_doCommand(self, base):
-@@ -459,7 +817,7 @@ class GroupsCommand(YumCommand):
+@@ -459,7 +910,7 @@ class GroupsCommand(YumCommand):
          except yum.Errors.GroupsError:
              return 1, [_('No Groups on which to run command')]
          except yum.Errors.YumBaseError, e:
@@ -162815,7 +163205,7 @@ index 4dcbea7..a2e0b1b 100644
  
      def _grp_cmd(self, basecmd, extcmds):
          if basecmd in self.direct_commands:
-@@ -470,6 +828,10 @@ class GroupsCommand(YumCommand):
+@@ -470,6 +921,10 @@ class GroupsCommand(YumCommand):
          else:
              cmd = 'summary'
  
@@ -162826,7 +163216,7 @@ index 4dcbea7..a2e0b1b 100644
          remap = {'update' : 'upgrade',
                   'erase' : 'remove',
                   'mark-erase' : 'mark-remove',
-@@ -479,32 +841,76 @@ class GroupsCommand(YumCommand):
+@@ -479,32 +934,76 @@ class GroupsCommand(YumCommand):
          return cmd, extcmds
  
      def doCheck(self, base, basecmd, extcmds):
@@ -162913,7 +163303,7 @@ index 4dcbea7..a2e0b1b 100644
          cmd, extcmds = self._grp_cmd(basecmd, extcmds)
  
          self._grp_setup_doCommand(base)
-@@ -524,39 +930,258 @@ class GroupsCommand(YumCommand):
+@@ -524,39 +1023,258 @@ class GroupsCommand(YumCommand):
              if cmd == 'remove':
                  return base.removeGroups(extcmds)
  
@@ -163174,7 +163564,7 @@ index 4dcbea7..a2e0b1b 100644
          base.logger.debug(_("Making cache files for all metadata files."))
          base.logger.debug(_("This may take a while depending on the speed of this computer"))
          try:
-@@ -565,81 +1190,216 @@ class MakeCacheCommand(YumCommand):
+@@ -565,81 +1283,227 @@ class MakeCacheCommand(YumCommand):
                  repo.mdpolicy = "group:all"
              base.doRepoSetup(dosack=0)
              base.repos.doSetup()
@@ -163343,6 +163733,17 @@ index 4dcbea7..a2e0b1b 100644
          except yum.Errors.YumBaseError, e:
 -            return 1, [str(e)]
 +            return 1, [exception2msg(e)]
++
++    def cacheRequirement(self, base, basecmd, extcmds):
++        """Return the cache requirements for the remote repos.
++
++        :param base: a :class:`yum.Yumbase` object
++        :param basecmd: the name of the command
++        :param extcmds: a list of arguments passed to *basecmd*
++        :return: Type of requirement: read-only:past, read-only:present, read-only:future, write
++        """
++        return 'read-only:past'
++
  
  class CheckUpdateCommand(YumCommand):
 +    """A class containing methods needed by the cli to execute the
@@ -163398,7 +163799,7 @@ index 4dcbea7..a2e0b1b 100644
          obscmds = ['obsoletes'] + extcmds
          base.extcmds.insert(0, 'updates')
          result = 0
-@@ -676,161 +1436,437 @@ class CheckUpdateCommand(YumCommand):
+@@ -676,161 +1540,480 @@ class CheckUpdateCommand(YumCommand):
                                                columns=columns)
                  result = 100
          except yum.Errors.YumBaseError, e:
@@ -163407,6 +163808,17 @@ index 4dcbea7..a2e0b1b 100644
          else:
              return result, []
  
++    def cacheRequirement(self, base, basecmd, extcmds):
++        """Return the cache requirements for the remote repos.
++
++        :param base: a :class:`yum.Yumbase` object
++        :param basecmd: the name of the command
++        :param extcmds: a list of arguments passed to *basecmd*
++        :return: Type of requirement: read-only:past, read-only:present, read-only:future, write
++        """
++        return 'read-only:future'
++
++
  class SearchCommand(YumCommand):
 +    """A class containing methods needed by the cli to execute the
 +    search command.
@@ -163476,6 +163888,17 @@ index 4dcbea7..a2e0b1b 100644
 +        """
          return False
  
++    def cacheRequirement(self, base, basecmd, extcmds):
++        """Return the cache requirements for the remote repos.
++
++        :param base: a :class:`yum.Yumbase` object
++        :param basecmd: the name of the command
++        :param extcmds: a list of arguments passed to *basecmd*
++        :return: Type of requirement: read-only:past, read-only:present, read-only:future, write
++        """
++        return 'read-only:present'
++
++
  class UpgradeCommand(YumCommand):
 +    """A class containing methods needed by the cli to execute the
 +    upgrade command.
@@ -163669,6 +164092,17 @@ index 4dcbea7..a2e0b1b 100644
          except yum.Errors.YumBaseError, e:
 -            return 1, [str(e)]
 +            return 1, [exception2msg(e)]
++
++    def cacheRequirement(self, base, basecmd, extcmds):
++        """Return the cache requirements for the remote repos.
++
++        :param base: a :class:`yum.Yumbase` object
++        :param basecmd: the name of the command
++        :param extcmds: a list of arguments passed to *basecmd*
++        :return: Type of requirement: read-only:past, read-only:present, read-only:future, write
++        """
++        return 'read-only:past'
++
  
  class ShellCommand(YumCommand):
 +    """A class containing methods needed by the cli to execute the
@@ -163798,6 +164232,16 @@ index 4dcbea7..a2e0b1b 100644
          except yum.Errors.YumBaseError, e:
 -            return 1, [str(e)]
 +            return 1, [exception2msg(e)]
++
++    def cacheRequirement(self, base, basecmd, extcmds):
++        """Return the cache requirements for the remote repos.
++
++        :param base: a :class:`yum.Yumbase` object
++        :param basecmd: the name of the command
++        :param extcmds: a list of arguments passed to *basecmd*
++        :return: Type of requirement: read-only:past, read-only:present, read-only:future, write
++        """
++        return 'read-only:past' # read-only ?
  
  
  class RepoListCommand(YumCommand):
@@ -163845,7 +164289,7 @@ index 4dcbea7..a2e0b1b 100644
          def _repo_size(repo):
              ret = 0
              for pkg in repo.sack.returnPackages():
-@@ -857,7 +1893,10 @@ class RepoListCommand(YumCommand):
+@@ -857,7 +2040,10 @@ class RepoListCommand(YumCommand):
              arg = 'enabled'
          extcmds = map(lambda x: x.lower(), extcmds)
  
@@ -163857,7 +164301,7 @@ index 4dcbea7..a2e0b1b 100644
          if arg != 'disabled' or extcmds:
              try:
                  # Setup so len(repo.sack) is correct
-@@ -866,6 +1905,13 @@ class RepoListCommand(YumCommand):
+@@ -866,6 +2052,13 @@ class RepoListCommand(YumCommand):
              except yum.Errors.RepoError:
                  if verbose:
                      raise
@@ -163871,7 +164315,7 @@ index 4dcbea7..a2e0b1b 100644
  
          repos = base.repos.repos.values()
          repos.sort()
-@@ -924,111 +1970,108 @@ class RepoListCommand(YumCommand):
+@@ -924,111 +2117,108 @@ class RepoListCommand(YumCommand):
                  ui_enabled = dhibeg + _('disabled') + hiend
                  ui_endis_wid = utf8_width(_('disabled'))
  
@@ -164076,7 +164520,7 @@ index 4dcbea7..a2e0b1b 100644
  
          if not verbose and cols:
              #  Work out the first (id) and last (enabled/disalbed/count),
-@@ -1088,21 +2131,54 @@ class RepoListCommand(YumCommand):
+@@ -1088,21 +2278,64 @@ class RepoListCommand(YumCommand):
          return 0, ['repolist: ' +to_unicode(locale.format("%d", tot_num, True))]
  
      def needTs(self, base, basecmd, extcmds):
@@ -164090,6 +164534,16 @@ index 4dcbea7..a2e0b1b 100644
 +        """
          return False
  
++    def cacheRequirement(self, base, basecmd, extcmds):
++        """Return the cache requirements for the remote repos.
++
++        :param base: a :class:`yum.Yumbase` object
++        :param basecmd: the name of the command
++        :param extcmds: a list of arguments passed to *basecmd*
++        :return: Type of requirement: read-only:past, read-only:present, read-only:future, write
++        """
++        return 'read-only:past'
++
  
  class HelpCommand(YumCommand):
 +    """A class containing methods needed by the cli to execute the
@@ -164131,7 +164585,7 @@ index 4dcbea7..a2e0b1b 100644
          if len(extcmds) == 0:
              base.usage()
              raise cli.CliError
-@@ -1147,28 +2223,85 @@ class HelpCommand(YumCommand):
+@@ -1147,28 +2380,85 @@ class HelpCommand(YumCommand):
          return help_output
  
      def doCommand(self, base, basecmd, extcmds):
@@ -164217,7 +164671,7 @@ index 4dcbea7..a2e0b1b 100644
          self.doneCommand(base, _("Setting up Reinstall Process"))
          try:
              return base.reinstallPkgs(extcmds)
-@@ -1177,49 +2310,139 @@ class ReInstallCommand(YumCommand):
+@@ -1177,49 +2467,139 @@ class ReInstallCommand(YumCommand):
              return 1, [to_unicode(e)]
  
      def getSummary(self):
@@ -164358,7 +164812,7 @@ index 4dcbea7..a2e0b1b 100644
          vcmd = 'installed'
          if extcmds:
              vcmd = extcmds[0]
-@@ -1308,7 +2531,7 @@ class VersionCommand(YumCommand):
+@@ -1308,7 +2688,7 @@ class VersionCommand(YumCommand):
                                       str(data[2][grp])))
                          _append_repos(cols, data[3][grp])
              except yum.Errors.YumBaseError, e:
@@ -164367,7 +164821,7 @@ index 4dcbea7..a2e0b1b 100644
          if vcmd in ('available', 'all', 'group-available', 'group-all'):
              try:
                  data = base.pkgSack.simpleVersion(not verbose, groups=groups)
-@@ -1327,7 +2550,7 @@ class VersionCommand(YumCommand):
+@@ -1327,7 +2707,7 @@ class VersionCommand(YumCommand):
                          if verbose:
                              _append_repos(cols, data[3][grp])
              except yum.Errors.YumBaseError, e:
@@ -164376,7 +164830,7 @@ index 4dcbea7..a2e0b1b 100644
  
          data = {'rid' : {}, 'ver' : {}}
          for (rid, ver) in cols:
-@@ -1344,6 +2567,14 @@ class VersionCommand(YumCommand):
+@@ -1344,6 +2724,14 @@ class VersionCommand(YumCommand):
          return 0, ['version']
  
      def needTs(self, base, basecmd, extcmds):
@@ -164391,8 +164845,20 @@ index 4dcbea7..a2e0b1b 100644
          vcmd = 'installed'
          if extcmds:
              vcmd = extcmds[0]
-@@ -1354,23 +2585,62 @@ class VersionCommand(YumCommand):
+@@ -1352,25 +2740,74 @@ class VersionCommand(YumCommand):
+             return True
+         return vcmd in ('available', 'all', 'group-available', 'group-all')
  
++    def cacheRequirement(self, base, basecmd, extcmds):
++        """Return the cache requirements for the remote repos.
++
++        :param base: a :class:`yum.Yumbase` object
++        :param basecmd: the name of the command
++        :param extcmds: a list of arguments passed to *basecmd*
++        :return: Type of requirement: read-only:past, read-only:present, read-only:future, write
++        """
++        return 'read-only:present'
++
  
  class HistoryCommand(YumCommand):
 +    """A class containing methods needed by the cli to execute the
@@ -164455,7 +164921,7 @@ index 4dcbea7..a2e0b1b 100644
              return 2, ["Repeating transaction %u" % (old.tid,)]
  
      def _hcmd_undo(self, base, extcmds):
-@@ -1426,12 +2696,57 @@ class HistoryCommand(YumCommand):
+@@ -1426,12 +2863,57 @@ class HistoryCommand(YumCommand):
      def _hcmd_new(self, base, extcmds):
          base.history._create_db_file()
  
@@ -164514,7 +164980,7 @@ index 4dcbea7..a2e0b1b 100644
          if extcmds and extcmds[0] not in cmds:
              base.logger.critical(_('Invalid history sub-command, use: %s.'),
                                   ", ".join(cmds))
-@@ -1444,6 +2759,19 @@ class HistoryCommand(YumCommand):
+@@ -1444,6 +2926,19 @@ class HistoryCommand(YumCommand):
              raise cli.CliError
  
      def doCommand(self, base, basecmd, extcmds):
@@ -164534,7 +165000,7 @@ index 4dcbea7..a2e0b1b 100644
          vcmd = 'list'
          if extcmds:
              vcmd = extcmds[0]
-@@ -1468,12 +2796,26 @@ class HistoryCommand(YumCommand):
+@@ -1468,29 +2963,88 @@ class HistoryCommand(YumCommand):
              ret = self._hcmd_rollback(base, extcmds)
          elif vcmd == 'new':
              ret = self._hcmd_new(base, extcmds)
@@ -164561,8 +165027,23 @@ index 4dcbea7..a2e0b1b 100644
          vcmd = 'list'
          if extcmds:
              vcmd = extcmds[0]
-@@ -1481,16 +2823,46 @@ class HistoryCommand(YumCommand):
+         return vcmd in ('repeat', 'redo', 'undo', 'rollback')
  
++    def cacheRequirement(self, base, basecmd, extcmds):
++        """Return the cache requirements for the remote repos.
++
++        :param base: a :class:`yum.Yumbase` object
++        :param basecmd: the name of the command
++        :param extcmds: a list of arguments passed to *basecmd*
++        :return: Type of requirement: read-only:past, read-only:present, read-only:future, write
++        """
++        vcmd = 'list'
++        if extcmds:
++            vcmd = extcmds[0]
++        if vcmd in ('repeat', 'redo', 'undo', 'rollback'):
++            return 'write'
++        return 'read-only:past'
++
  
  class CheckRpmdbCommand(YumCommand):
 +    """A class containing methods needed by the cli to execute the
@@ -164608,7 +165089,7 @@ index 4dcbea7..a2e0b1b 100644
          chkcmd = 'all'
          if extcmds:
              chkcmd = extcmds
-@@ -1505,19 +2877,57 @@ class CheckRpmdbCommand(YumCommand):
+@@ -1505,19 +3059,58 @@ class CheckRpmdbCommand(YumCommand):
          return rc, ['%s %s' % (basecmd, chkcmd)]
  
      def needTs(self, base, basecmd, extcmds):
@@ -164622,6 +165103,7 @@ index 4dcbea7..a2e0b1b 100644
 +        """
          return False
  
++
  class LoadTransactionCommand(YumCommand):
 +    """A class containing methods needed by the cli to execute the
 +    load-transaction command.
@@ -164666,7 +165148,7 @@ index 4dcbea7..a2e0b1b 100644
          if not extcmds:
              base.logger.critical(_("No saved transaction file specified."))
              raise cli.CliError
-@@ -1533,5 +2943,13 @@ class LoadTransactionCommand(YumCommand):
+@@ -1533,5 +3126,253 @@ class LoadTransactionCommand(YumCommand):
  
  
      def needTs(self, base, basecmd, extcmds):
@@ -164680,6 +165162,246 @@ index 4dcbea7..a2e0b1b 100644
 +        """
          return True
  
++
++class SwapCommand(YumCommand):
++    """A class containing methods needed by the cli to execute the
++    swap command.
++    """
++
++    def getNames(self):
++        """Return a list containing the names of this command.  This
++        command can be called from the command line by using any of these names.
++
++        :return: a list containing the names of this command
++        """
++        return ['swap']
++
++    def getUsage(self):
++        """Return a usage string for this command.
++
++        :return: a usage string for this command
++        """
++        return "[remove|cmd] <pkg|arg(s)> [-- install|cmd] <pkg|arg(s)>"
++
++    def getSummary(self):
++        """Return a one line summary of this command.
++
++        :return: a one line summary of this command
++        """
++        return _("Simple way to swap packages, isntead of using shell")
++
++    def doCheck(self, base, basecmd, extcmds):
++        """Verify that conditions are met so that this command can run.
++        These include that the program is being run by the root user,
++        that there are enabled repositories with gpg keys, and that
++        this command is called with appropriate arguments.
++
++        :param base: a :class:`yum.Yumbase` object
++        :param basecmd: the name of the command
++        :param extcmds: the command line arguments passed to *basecmd*
++        """
++        checkRootUID(base)
++        checkGPGKey(base)
++        checkSwapPackageArg(base, basecmd, extcmds)
++        checkEnabledRepo(base, extcmds)
++
++    def doCommand(self, base, basecmd, extcmds):
++        """Execute this command.
++
++        :param base: a :class:`yum.Yumbase` object
++        :param basecmd: the name of the command
++        :param extcmds: the command line arguments passed to *basecmd*
++        :return: (exit_code, [ errors ])
++
++        exit_code is::
++
++            0 = we're done, exit
++            1 = we've errored, exit with error string
++            2 = we've got work yet to do, onto the next stage
++        """
++
++        if '--' in extcmds:
++            off = extcmds.index('--')
++            rextcmds = extcmds[:off]
++            iextcmds = extcmds[off+1:]
++        else:
++            rextcmds = extcmds[:1]
++            iextcmds = extcmds[1:]
++
++        if not (rextcmds and iextcmds):
++            return 1, ['swap'] # impossible
++
++        if rextcmds[0] not in base.yum_cli_commands:
++            rextcmds = ['remove'] + rextcmds
++        if iextcmds[0] not in base.yum_cli_commands:
++            iextcmds = ['install'] + iextcmds
++
++        # Very similar to what the shell command does...
++        ocmds = base.cmds
++        oline = base.cmdstring
++        for cmds in (rextcmds, iextcmds):
++            base.cmdstring = " ".join(cmds)
++            base.cmds = cmds
++            #  Don't call this atm. as the line has gone through it already,
++            # also makes it hard to do the "is ?extcmds[0] a cmd" check.
++            # base.plugins.run('args', args=base.cmds)
++
++            # We don't catch exceptions, just pass them up and fail...
++            base.parseCommands()
++            cmdret = base.doCommands()
++            if cmdret[0] != 2:
++                return cmdret[0], ['%s %s' % (basecmd, " ".join(cmds))]
++        base.cmds      = ocmds
++        base.cmdstring = oline
++
++        return 2, ['%s %s' % (basecmd, " ".join(extcmds))]
++
++
++class RepoPkgsCommand(YumCommand):
++    """A class containing methods needed by the cli to execute the
++    repo command.
++    """
++
++    def getNames(self):
++        """Return a list containing the names of this command.  This
++        command can be called from the command line by using any of these names.
++
++        :return: a list containing the names of this command
++        """
++        return ['repo-pkgs',
++                'repo-packages', 'repository-pkgs', 'repository-packages']
++
++    def getUsage(self):
++        """Return a usage string for this command.
++
++        :return: a usage string for this command
++        """
++        return "<enabled-repoid> <install|remove|remove-or-reinstall> [pkg(s)]"
++
++    def getSummary(self):
++        """Return a one line summary of this command.
++
++        :return: a one line summary of this command
++        """
++        return _("Treat a repo. as a group of packages, so we can install/remove all of them")
++
++    def doCheck(self, base, basecmd, extcmds):
++        """Verify that conditions are met so that this command can run.
++        These include that the program is being run by the root user,
++        that there are enabled repositories with gpg keys, and that
++        this command is called with appropriate arguments.
++
++        :param base: a :class:`yum.Yumbase` object
++        :param basecmd: the name of the command
++        :param extcmds: the command line arguments passed to *basecmd*
++        """
++        checkRootUID(base)
++        checkGPGKey(base)
++        checkRepoPackageArg(base, basecmd, extcmds)
++        checkEnabledRepo(base, extcmds)
++
++    def doCommand(self, base, basecmd, extcmds):
++        """Execute this command.
++
++        :param base: a :class:`yum.Yumbase` object
++        :param basecmd: the name of the command
++        :param extcmds: the command line arguments passed to *basecmd*
++        :return: (exit_code, [ errors ])
++
++        exit_code is::
++
++            0 = we're done, exit
++            1 = we've errored, exit with error string
++            2 = we've got work yet to do, onto the next stage
++        """
++
++        def _add_repopkg2txmbrs(txmbrs, repoid):
++            for txmbr in txmbrs:
++                txmbr.repopkg = repoid
++
++        repoid = extcmds[0]
++        cmd = extcmds[1]
++        args = extcmds[2:]
++        if not args:
++            args = ['*']
++        num = 0
++        if False: pass
++        elif cmd == 'install': # install is simpler version of installPkgs...
++            for arg in args:
++                txmbrs = base.install(pattern=arg, repoid=repoid)
++                _add_repopkg2txmbrs(txmbrs, repoid)
++                num += len(txmbrs)
++
++            if num:
++                return 2, P_('%d package to install', '%d packages to install',
++                             num)
++
++        elif cmd == 'remove': # Also mostly the same...
++            for arg in args:
++                txmbrs = base.remove(pattern=arg, repoid=repoid)
++                _add_repopkg2txmbrs(txmbrs, repoid)
++                num += len(txmbrs)
++
++            if num:
++                return 2, P_('%d package to remove', '%d packages to remove',
++                             num)
++
++        elif cmd == 'remove-or-reinstall': # More complicated...
++            for arg in args:
++                txmbrs = base.remove(pattern=arg, repoid=repoid)
++                # Add an install() if it's in another repo.
++                for txmbr in txmbrs[:]:
++                    pkgs = base.pkgSack.searchPkgTuple(txmbr.pkgtup)
++                    for pkg in sorted(pkgs):
++                        if pkg.repoid == repoid:
++                            continue
++                        txmbrs += base.install(po=pkg)
++                        break
++
++                _add_repopkg2txmbrs(txmbrs, repoid)
++                num += len(txmbrs)
++
++        elif cmd == 'remove-or-sync': # Even more complicated...
++            for arg in args:
++                txmbrs = base.remove(pattern=arg, repoid=repoid)
++                #  Add an install/upgrade/downgrade if a version is in another
++                # repo.
++                for txmbr in txmbrs[:]:
++                    pkgs = base.pkgSack.searchNames([txmbr.name])
++                    toinst = None
++                    for pkg in sorted(pkgs):
++                        if pkg.repoid == repoid:
++                            continue
++                        if toinst is None:
++                            toinst = pkg
++                        if toinst.verLT(pkg):
++                            if toinst.verEQ(txmbr.po):
++                                break
++                            toinst = pkg
++                        if toinst.verEQ(txmbr.po) and toinst.arch == txmbr.arch:
++                            break
++
++                    if toinst is not None:
++                        if toinst.verEQ(txmbr.po):
++                            txmbrs += base.install(po=toinst)
++                        elif toinst.verGT(txmbr.po):
++                            txmbrs += base.update(po=toinst)
++                        else:
++                            base.tsInfo.remove(txmbr.pkgtup)
++                            txmbrs.remove(txmbr)
++                            txmbrs += base.downgrade(po=toinst)
++
++                _add_repopkg2txmbrs(txmbrs, repoid)
++                num += len(txmbrs)
++
++            if num:
++                return 2, P_('%d package to remove/reinstall',
++                             '%d packages to remove/reinstall', num)
++
++        else:
++            return 1, [_('Not a valid sub-command of %s') % basecmd]
++
++        return 0, [_('Nothing to do')]
 diff --git a/yummain.py b/yummain.py
 index 9f79f4f..a9f001b 100755
 --- a/yummain.py
diff --git a/yum.spec b/yum.spec
index 5f6d76a..58740b6 100644
--- a/yum.spec
+++ b/yum.spec
@@ -18,7 +18,7 @@
 Summary: RPM package installer/updater/manager
 Name: yum
 Version: 3.4.3
-Release: 52%{?dist}
+Release: 53%{?dist}
 License: GPLv2+
 Group: System Environment/Base
 Source0: http://yum.baseurl.org/download/3.4/%{name}-%{version}.tar.gz
@@ -92,9 +92,9 @@ Obsoletes: yum-plugin-protect-packages < 1.1.27-0
 Provides: yum-protect-packages = 1.1.27-0.yum
 Provides: yum-plugin-protect-packages = 1.1.27-0.yum
 Obsoletes: yum-plugin-download-order <= 0.2-2
-BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
 Obsoletes: yum-plugin-downloadonly <= 1.1.31-6.fc19
 Provides: yum-plugin-downloadonly = 3.4.3-44.yum
+BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
 
 %description
 Yum is a utility that can check for and automatically download and
@@ -284,6 +284,13 @@ exit 0
 %endif
 
 %changelog
+* Wed Jan 16 2013 James Antill <james at fedoraproject.org> - 3.4.3-53
+- update to latest HEAD.
+- Add repo-pkgs.
+- Add swap.
+- Add remove_leaf_only and repopkgremove_leaf_only.
+- Add metadata_expire_filter.
+
 * Tue Jan 15 2013 Zdeněk Pavlas <zpavlas at redhat.com> - 3.4.3-52
 - update to latest HEAD
 - _lock(): yet another exception2msg fix.  BZ 895060


More information about the scm-commits mailing list