[yum] latest head include panu's callback patches

Seth Vidal skvidal at fedoraproject.org
Mon Feb 28 20:21:35 UTC 2011


commit cee74cde026fc5d0813635871d556b2810b3cdd6
Author: Seth Vidal <skvidal at fedoraproject.org>
Date:   Mon Feb 28 15:21:26 2011 -0500

    latest head include panu's callback patches

 yum-HEAD.patch |  739 +++++++++++++++++++++++++++++++++++++++++++++++++-------
 yum.spec       |    5 +-
 2 files changed, 659 insertions(+), 85 deletions(-)
---
diff --git a/yum-HEAD.patch b/yum-HEAD.patch
index 8509f33..a079b82 100644
--- a/yum-HEAD.patch
+++ b/yum-HEAD.patch
@@ -1,8 +1,16 @@
 diff --git a/cli.py b/cli.py
-index 640f190..6cf6753 100644
+index 640f190..a4c7c79 100644
 --- a/cli.py
 +++ b/cli.py
-@@ -504,30 +504,35 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -73,6 +73,7 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+         self.logger = logging.getLogger("yum.cli")
+         self.verbose_logger = logging.getLogger("yum.verbose.cli")
+         self.yum_cli_commands = {}
++        self.use_txmbr_in_callback = True
+         self.registerCommand(yumcommands.InstallCommand())
+         self.registerCommand(yumcommands.UpdateCommand())
+         self.registerCommand(yumcommands.InfoCommand())
+@@ -504,30 +505,35 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
          if self.gpgsigcheck(downloadpkgs) != 0:
              return 1
          
@@ -32,7 +40,7 @@ index 640f190..6cf6753 100644
 +        rcd_st = time.time()
 +        self.verbose_logger.log(yum.logginglevels.INFO_2, 
 +             _('Running Transaction Check'))
-+        msgs = self.ts.check()
++        msgs = self._run_rpm_check()
 +        if msgs:
 +            rpmlib_only = True
 +            for msg in msgs:
@@ -59,7 +67,7 @@ index 640f190..6cf6753 100644
  
          tt_st = time.time()            
          self.verbose_logger.log(yum.logginglevels.INFO_2,
-@@ -535,14 +540,10 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -535,14 +541,10 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
          if not self.conf.diskspacecheck:
              self.tsInfo.probFilterFlags.append(rpm.RPMPROB_FILTER_DISKSPACE)
              
@@ -76,7 +84,7 @@ index 640f190..6cf6753 100644
          tserrors = self.ts.test(testcb)
          del testcb
  
-@@ -555,7 +556,6 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -555,7 +557,6 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
                   self.errorSummary(errstring)
          self.verbose_logger.log(yum.logginglevels.INFO_2,
               _('Transaction Test Succeeded'))
@@ -84,7 +92,7 @@ index 640f190..6cf6753 100644
          
          self.verbose_logger.debug('Transaction Test time: %0.3f' % (time.time() - tt_st))
          
-@@ -563,10 +563,6 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+@@ -563,10 +564,6 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
          signal.signal(signal.SIGQUIT, signal.SIG_DFL)
          
          ts_st = time.time()
@@ -95,6 +103,47 @@ index 640f190..6cf6753 100644
  
          # put back our depcheck callback
          self.dsCallback = dscb
+@@ -629,7 +626,7 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+                                      ", ".join(matches))
+             self.verbose_logger.log(yum.logginglevels.INFO_2, to_unicode(msg))
+ 
+-    def _checkMaybeYouMeant(self, arg, always_output=True):
++    def _checkMaybeYouMeant(self, arg, always_output=True, rpmdb_only=False):
+         """ If the update/remove argument doesn't match with case, or due
+             to not being installed, tell the user. """
+         # always_output is a wart due to update/remove not producing the
+@@ -638,7 +635,12 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+         # skip it.
+         if not arg or arg[0] == '@':
+             return
+-        matches = self.doPackageLists(patterns=[arg], ignore_case=False)
++        
++        pkgnarrow='all'
++        if rpmdb_only:
++            pkgnarrow='installed'
++        
++        matches = self.doPackageLists(pkgnarrow=pkgnarrow, patterns=[arg], ignore_case=False)
+         if (matches.installed or (not matches.available and
+                                   self.returnInstalledPackagesByDep(arg))):
+             return # Found a match so ignore
+@@ -651,7 +653,7 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+             return
+ 
+         # No package name, so do the maybeYouMeant thing here too
+-        matches = self.doPackageLists(patterns=[arg], ignore_case=True)
++        matches = self.doPackageLists(pkgnarrow=pkgnarrow, patterns=[arg], ignore_case=True)
+         if not matches.installed and matches.available:
+             self.verbose_logger.log(yum.logginglevels.INFO_2,
+                 _('Package(s) %s%s%s available, but not installed.'),
+@@ -822,7 +824,7 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
+         for arg in userlist:
+             rms = self.remove(pattern=arg)
+             if not rms:
+-                self._checkMaybeYouMeant(arg, always_output=False)
++                self._checkMaybeYouMeant(arg, always_output=False, rpmdb_only=True)
+             all_rms.extend(rms)
+         
+         if all_rms:
 diff --git a/docs/yum.8 b/docs/yum.8
 index 52f6b53..3b414e2 100644
 --- a/docs/yum.8
@@ -308,25 +357,17 @@ index b1d92e5..08fe0e3 100755
              limit = 20
  
 diff --git a/rpmUtils/transaction.py b/rpmUtils/transaction.py
-index e8f4459..c5167d3 100644
+index e8f4459..121ad5b 100644
 --- a/rpmUtils/transaction.py
 +++ b/rpmUtils/transaction.py
-@@ -13,6 +13,7 @@
- 
- import rpm
- import miscutils
-+from yum.i18n import to_str
- 
- read_ts = None
- ts = None
-@@ -22,18 +23,12 @@ ts = None
+@@ -22,18 +22,13 @@ ts = None
  class TransactionWrapper:
      def __init__(self, root='/'):
          self.ts = rpm.TransactionSet(root)
 -        self._methods = ['dbMatch',
 -                         'check',
--                         'order',
-+        self._methods = ['order',
++        self._methods = ['check',
+                          'order',
                           'addErase',
                           'addInstall',
                           'run',
@@ -339,21 +380,10 @@ index e8f4459..c5167d3 100644
                           'problems',
                           'setFlags',
                           'setVSFlags',
-@@ -54,6 +49,28 @@ class TransactionWrapper:
+@@ -54,6 +49,17 @@ class TransactionWrapper:
              self.ts = None
              self.open = False
  
-+    def check(self):
-+        results = []
-+        self.ts.check()
-+        for prob in self.ts.problems():
-+            #  Newer rpm (4.8.0+) has problem objects, older have just strings.
-+            #  Should probably move to using the new objects, when we can. For
-+            # now just be compatible.
-+            results.append(to_str(prob))
-+
-+        return results
-+
 +    def dbMatch(self, *args, **kwds):
 +        if 'patterns' in kwds:
 +            patterns = kwds.pop('patterns')
@@ -368,7 +398,7 @@ index e8f4459..c5167d3 100644
      def __getattr__(self, attr):
          if attr in self._methods:
              return self.getMethod(attr)
-@@ -91,6 +108,9 @@ class TransactionWrapper:
+@@ -91,6 +97,9 @@ class TransactionWrapper:
      def isTsFlagSet(self, flag):
          val = self.getTsFlags()
          return bool(flag & val)
@@ -378,7 +408,7 @@ index e8f4459..c5167d3 100644
          
  #    def addProblemFilter(self, filt):
  #        curfilter = self.ts.setProbFilter(0)
-@@ -100,12 +120,14 @@ class TransactionWrapper:
+@@ -100,12 +109,14 @@ class TransactionWrapper:
          """tests the ts we've setup, takes a callback function and a conf dict 
             for flags and what not"""
      
@@ -422,7 +452,7 @@ index a1fbc72..65a2397 100644
  %config(noreplace) %{_sysconfdir}/sysconfig/yum-cron
  
 diff --git a/yum/__init__.py b/yum/__init__.py
-index f6e8a6b..e395cc5 100644
+index f6e8a6b..5c44245 100644
 --- a/yum/__init__.py
 +++ b/yum/__init__.py
 @@ -349,7 +349,10 @@ class YumBase(depsolve.Depsolve):
@@ -449,7 +479,31 @@ index f6e8a6b..e395cc5 100644
                  thisrepo.base_persistdir = self.conf._repos_persistdir
  
  
-@@ -1435,12 +1435,17 @@ class YumBase(depsolve.Depsolve):
+@@ -574,6 +574,11 @@ class YumBase(depsolve.Depsolve):
+ 
+             self.getReposFromConfig()
+ 
++        #  For rhnplugin, and in theory other stuff, calling
++        # .getReposFromConfig() recurses back into this function but only once.
++        # This means that we have two points on the stack leaving the above call
++        # but only one of them can do the repos setup. BZ 678043.
++        if hasattr(self, 'prerepoconf'):
+             # Recursion
+             prerepoconf = self.prerepoconf
+             del self.prerepoconf
+@@ -1024,6 +1029,11 @@ class YumBase(depsolve.Depsolve):
+         for txmbr in txmbrs:
+             if kern_pkgtup is not None and txmbr.pkgtup == kern_pkgtup:
+                 pass
++            elif kern_pkgtup is not None and txmbr.name == kern_pkgtup[0]:
++                #  We don't care if they've explicitly set protected on the
++                # kernel package. Because we don't allow you to uninstall the
++                # running one so it has _special_ semantics anyway.
++                continue
+             elif txmbr.name not in protected:
+                 continue
+             if txmbr.name not in bad_togo:
+@@ -1435,12 +1445,17 @@ class YumBase(depsolve.Depsolve):
          # will be we store what we thought, not what happened (so it'll be an
          # invalid cache).
          self.rpmdb.transactionResultVersion(frpmdbv)
@@ -472,7 +526,7 @@ index f6e8a6b..e395cc5 100644
          self._ts_save_file = None
          
          errors = self.ts.run(cb.callback, '')
-@@ -1485,7 +1490,12 @@ class YumBase(depsolve.Depsolve):
+@@ -1485,7 +1500,12 @@ class YumBase(depsolve.Depsolve):
  
          
          # drop out the rpm cache so we don't step on bad hdr indexes
@@ -486,7 +540,7 @@ index f6e8a6b..e395cc5 100644
          self.plugins.run('posttrans')
          # sync up what just happened versus what is in the rpmdb
          if not self.ts.isTsFlagSet(rpm.RPMTRANS_FLAG_TEST):
-@@ -1674,8 +1684,11 @@ class YumBase(depsolve.Depsolve):
+@@ -1674,8 +1694,11 @@ class YumBase(depsolve.Depsolve):
      def doLock(self, lockfile = YUM_PID_FILE):
          """perform the yum locking, raise yum-based exceptions, not OSErrors"""
          
@@ -499,7 +553,7 @@ index f6e8a6b..e395cc5 100644
              root = self.conf.cachedir
              # Don't want <cachedir>/var/run/yum.pid ... just: <cachedir>/yum.pid
              lockfile = os.path.basename(lockfile)
-@@ -1690,7 +1703,7 @@ class YumBase(depsolve.Depsolve):
+@@ -1690,7 +1713,7 @@ class YumBase(depsolve.Depsolve):
                  fd = open(lockfile, 'r')
              except (IOError, OSError), e:
                  msg = _("Could not open lock %s: %s") % (lockfile, e)
@@ -508,7 +562,7 @@ index f6e8a6b..e395cc5 100644
                  
              try: oldpid = int(fd.readline())
              except ValueError:
-@@ -1707,7 +1720,7 @@ class YumBase(depsolve.Depsolve):
+@@ -1707,7 +1730,7 @@ class YumBase(depsolve.Depsolve):
                      else:
                          # Whoa. What the heck happened?
                          msg = _('Unable to check if PID %s is active') % oldpid
@@ -517,7 +571,7 @@ index f6e8a6b..e395cc5 100644
                  else:
                      # Another copy seems to be running.
                      msg = _('Existing lock %s: another copy is running as pid %s.') % (lockfile, oldpid)
-@@ -1752,7 +1765,7 @@ class YumBase(depsolve.Depsolve):
+@@ -1752,7 +1775,7 @@ class YumBase(depsolve.Depsolve):
              if not msg.errno == errno.EEXIST: 
                  # Whoa. What the heck happened?
                  errmsg = _('Could not create lock at %s: %s ') % (filename, str(msg))
@@ -526,7 +580,76 @@ index f6e8a6b..e395cc5 100644
              return 0
          else:
              os.write(fd, contents)
-@@ -4557,16 +4570,25 @@ class YumBase(depsolve.Depsolve):
+@@ -3417,7 +3440,6 @@ class YumBase(depsolve.Depsolve):
+                 pkgs = po.obsoletedBy(pkgs, limit=1)
+                 if pkgs:
+                     already_obs = pkgs[0]
+-                    continue
+ 
+                 if already_obs:
+                     self.verbose_logger.warning(_('Package %s is obsoleted by %s which is already installed'), 
+@@ -4462,17 +4484,20 @@ class YumBase(depsolve.Depsolve):
+         be imported using askcb.
+         
+         @param po: Package object to retrieve the key of.
+-        @param askcb: Callback function to use for asking for verification.
++        @param askcb: Callback function to use for asking for permission to
++                      import a key. This is verification, but also "choice".
+                       Takes arguments of the po, the userid for the key, and
+                       the keyid.
+-        @param fullaskcb: Callback function to use for asking for verification
+-                          of a key. Differs from askcb in that it gets passed
+-                          a dictionary so that we can expand the values passed.
++        @param fullaskcb: Callback function to use for asking for permission to
++                          import a key. This is verification, but also "choice".
++                          Differs from askcb in that it gets passed a
++                          dictionary so that we can expand the values passed.
+         """
+         repo = self.repos.getRepo(po.repoid)
+         keyurls = repo.gpgkey
+         key_installed = False
+ 
++        user_cb_fail = False
+         for keyurl in keyurls:
+             keys = self._retrievePublicKey(keyurl, repo)
+ 
+@@ -4509,7 +4534,8 @@ class YumBase(depsolve.Depsolve):
+                         rc = askcb(po, info['userid'], info['hexkeyid'])
+ 
+                     if not rc:
+-                        raise Errors.YumBaseError, _("Not installing key")
++                        user_cb_fail = True
++                        continue
+                     
+                 # Import the key
+                 ts = self.rpmdb.readOnlyTS()
+@@ -4520,6 +4546,9 @@ class YumBase(depsolve.Depsolve):
+                 self.logger.info(_('Key imported successfully'))
+                 key_installed = True
+ 
++        if not key_installed and user_cb_fail:
++            raise Errors.YumBaseError, _("Didn't install any keys")
++
+         if not key_installed:
+             raise Errors.YumBaseError, \
+                   _('The GPG keys listed for the "%s" repository are ' \
+@@ -4543,11 +4572,13 @@ class YumBase(depsolve.Depsolve):
+         @param destdir: destination of the gpg pub ring
+         @param keyurl_list: list of urls for gpg keys
+         @param is_cakey: bool - are we pulling in a ca key or not
+-        @param callback: Callback function to use for asking for verification
+-                          of a key. Takes a dictionary of key info.
++        @param callback: Callback function to use for asking for permission to
++                         import a key. This is verification, but also "choice".
++                         Takes a dictionary of key info.
+         """
+ 
+         key_installed = False
++        user_cb_fail = False
+         for keyurl in keyurl_list:
+             keys = self._retrievePublicKey(keyurl, repo, getSig=not is_cakey)
+             for info in keys:
+@@ -4557,16 +4588,25 @@ class YumBase(depsolve.Depsolve):
                          keyurl, info['hexkeyid']))
                      key_installed = True
                      continue
@@ -557,11 +680,20 @@ index f6e8a6b..e395cc5 100644
                      self._getKeyImportMessage(info, keyurl, keytype)
                      rc = False
                      if self.conf.assumeyes:
-@@ -4587,7 +4609,18 @@ class YumBase(depsolve.Depsolve):
+@@ -4579,7 +4619,8 @@ class YumBase(depsolve.Depsolve):
+ 
+ 
+                     if not rc:
+-                        raise Errors.YumBaseError, _("Not installing key for repo %s") % repo
++                        user_cb_fail = True
++                        continue
+                 
+                 # Import the key
+                 result = misc.import_key_to_pubring(info['raw_key'], info['hexkeyid'], gpgdir=destdir)
+@@ -4587,6 +4628,20 @@ class YumBase(depsolve.Depsolve):
                      raise Errors.YumBaseError, _('Key import failed')
                  self.logger.info(_('Key imported successfully'))
                  key_installed = True
--
 +                # write out the key id to imported_cakeys in the repos basedir
 +                if is_cakey and key_installed:
 +                    if info['hexkeyid'] not in cakeys:
@@ -573,11 +705,13 @@ index f6e8a6b..e395cc5 100644
 +                        except (IOError, OSError):
 +                            # maybe a warning - but in general this is not-critical, just annoying to the user
 +                            pass
-+                        
++
++        if not key_installed and user_cb_fail:
++            raise Errors.YumBaseError, _("Didn't install any keys for repo %s") % repo
+ 
          if not key_installed:
              raise Errors.YumBaseError, \
-                   _('The GPG keys listed for the "%s" repository are ' \
-@@ -4775,26 +4808,31 @@ class YumBase(depsolve.Depsolve):
+@@ -4775,26 +4830,31 @@ class YumBase(depsolve.Depsolve):
      
      def _doTestTransaction(self,callback,display=None):
          ''' Do the RPM test transaction '''
@@ -609,7 +743,7 @@ index f6e8a6b..e395cc5 100644
 -                raise Errors.YumRPMCheckError,retmsgs
 +        self.verbose_logger.log(logginglevels.INFO_2, 
 +                 _('Running Transaction Check'))
-+        msgs = self.ts.check()
++        msgs = self._run_rpm_check()
 +        if msgs:
 +            rpmlib_only = True
 +            for msg in msgs:
@@ -628,7 +762,7 @@ index f6e8a6b..e395cc5 100644
          
          tsConf = {}
          for feature in ['diskspacecheck']: # more to come, I'm sure
-@@ -4804,14 +4842,7 @@ class YumBase(depsolve.Depsolve):
+@@ -4804,14 +4864,7 @@ class YumBase(depsolve.Depsolve):
          # overwrite the default display class
          if display:
              testcb.display = display
@@ -643,29 +777,28 @@ index f6e8a6b..e395cc5 100644
          tserrors = self.ts.test( testcb, conf=tsConf )
          del testcb
    
-@@ -4839,22 +4870,6 @@ class YumBase(depsolve.Depsolve):
+@@ -4839,12 +4892,8 @@ class YumBase(depsolve.Depsolve):
              cb.display = display
          self.runTransaction( cb=cb )
  
 -    def _run_rpm_check_debug(self):
--        results = []
++    def _run_rpm_check(self):
+         results = []
 -        # save our dsCallback out
 -        dscb = self.dsCallback
 -        self.dsCallback = None # dumb, dumb dumb dumb!
 -        self.populateTs(test=1)
--        self.ts.check()
--        for prob in self.ts.problems():
--            #  Newer rpm (4.8.0+) has problem objects, older have just strings.
--            #  Should probably move to using the new objects, when we can. For
--            # now just be compatible.
--            results.append(to_str(prob))
--
+         self.ts.check()
+         for prob in self.ts.problems():
+             #  Newer rpm (4.8.0+) has problem objects, older have just strings.
+@@ -4852,7 +4901,6 @@ class YumBase(depsolve.Depsolve):
+             # now just be compatible.
+             results.append(to_str(prob))
+ 
 -        self.dsCallback = dscb
--        return results
--
+         return results
+ 
      def add_enable_repo(self, repoid, baseurls=[], mirrorlist=None, **kwargs):
-         """add and enable a repo with just a baseurl/mirrorlist and repoid
-            requires repoid and at least one of baseurl and mirrorlist
 diff --git a/yum/config.py b/yum/config.py
 index 97e5e3d..9c2db93 100644
 --- a/yum/config.py
@@ -679,10 +812,34 @@ index 97e5e3d..9c2db93 100644
      disable_excludes = ListOption()    
      skip_broken = BoolOption(False)
 diff --git a/yum/depsolve.py b/yum/depsolve.py
-index de2849a..3aaba0e 100644
+index de2849a..8f18ccc 100644
 --- a/yum/depsolve.py
 +++ b/yum/depsolve.py
-@@ -799,9 +799,9 @@ class Depsolve(object):
+@@ -69,6 +69,8 @@ class Depsolve(object):
+         self._ts = None
+         self._tsInfo = None
+         self.dsCallback = None
++        # Callback-style switch, default to legacy (hdr, file) mode
++        self.use_txmbr_in_callback = False
+         self.logger = logging.getLogger("yum.Depsolve")
+         self.verbose_logger = logging.getLogger("yum.verbose.Depsolve")
+ 
+@@ -220,8 +222,13 @@ class Depsolve(object):
+                         txmbr.ts_state = 'i'
+                         txmbr.output_state = TS_INSTALL
+ 
++                # New-style callback with just txmbr instead of full headers?
++                if self.use_txmbr_in_callback:
++                    cbkey = txmbr
++                else:
++                    cbkey = (hdr, rpmfile)
+                 
+-                self.ts.addInstall(hdr, (hdr, rpmfile), txmbr.ts_state)
++                self.ts.addInstall(hdr, cbkey, txmbr.ts_state)
+                 self.verbose_logger.log(logginglevels.DEBUG_1,
+                     _('Adding Package %s in mode %s'), txmbr.po, txmbr.ts_state)
+                 if self.dsCallback:
+@@ -799,9 +806,9 @@ class Depsolve(object):
                      continue
                  done.add((po, err))
                  self.verbose_logger.log(logginglevels.DEBUG_4,
@@ -694,8 +851,40 @@ index de2849a..3aaba0e 100644
              return (1, errors)
  
          if not len(self.tsInfo):
+diff --git a/yum/misc.py b/yum/misc.py
+index 15e571f..8e81c34 100644
+--- a/yum/misc.py
++++ b/yum/misc.py
+@@ -252,6 +252,9 @@ class Checksums:
+     def __len__(self):
+         return self._len
+ 
++    # Note that len(x) is assert limited to INT_MAX, which is 2GB on i686.
++    length = property(fget=lambda self: self._len)
++
+     def update(self, data):
+         self._len += len(data)
+         for sumalgo in self._sumalgos:
+@@ -323,7 +326,7 @@ def checksum(sumtype, file, CHUNK=2**16, datasize=None):
+ 
+         data = Checksums([sumtype])
+         while data.read(fo, CHUNK):
+-            if datasize is not None and len(data) > datasize:
++            if datasize is not None and data.length > datasize:
+                 break
+ 
+         if type(file) is types.StringType:
+@@ -332,7 +335,7 @@ def checksum(sumtype, file, CHUNK=2**16, datasize=None):
+             
+         # This screws up the length, but that shouldn't matter. We only care
+         # if this checksum == what we expect.
+-        if datasize is not None and datasize != len(data):
++        if datasize is not None and datasize != data.length:
+             return '!%u!%s' % (datasize, data.hexdigest(sumtype))
+ 
+         return data.hexdigest(sumtype)
 diff --git a/yum/packages.py b/yum/packages.py
-index 6f61fea..15eeeaa 100644
+index 6f61fea..db3e973 100644
 --- a/yum/packages.py
 +++ b/yum/packages.py
 @@ -1069,6 +1069,9 @@ class YumAvailablePackage(PackageObject, RpmBase):
@@ -708,11 +897,112 @@ index 6f61fea..15eeeaa 100644
          msg +="""    <rpm:header-range start="%s" end="%s"/>""" % (self.hdrstart,
                                                                 self.hdrend)
          msg += self._dump_pco('provides')
+@@ -1243,18 +1246,32 @@ class YumHeaderPackage(YumAvailablePackage):
+         self.ver = self.version
+         self.rel = self.release
+         self.pkgtup = (self.name, self.arch, self.epoch, self.version, self.release)
+-        # Summaries "can be" empty, which rpm return [], see BZ 473239, *sigh*
+-        self.summary = self.hdr['summary'] or ''
+-        self.summary = misc.share_data(self.summary.replace('\n', ''))
+-        self.description = self.hdr['description'] or ''
+-        self.description = misc.share_data(self.description)
++        self._loaded_summary = None
++        self._loaded_description = None
+         self.pkgid = self.hdr[rpm.RPMTAG_SHA1HEADER]
+         if not self.pkgid:
+             self.pkgid = "%s.%s" %(self.hdr['name'], self.hdr['buildtime'])
+         self.packagesize = self.hdr['size']
+         self.__mode_cache = {}
+         self.__prcoPopulated = False
+-        
++
++    def _loadSummary(self):
++        # Summaries "can be" empty, which rpm return [], see BZ 473239, *sigh*
++        if self._loaded_summary is None:
++            summary = self._get_hdr()['summary'] or ''
++            summary = misc.share_data(summary.replace('\n', ''))
++            self._loaded_summary = summary
++        return self._loaded_summary
++    summary = property(lambda x: x._loadSummary())
++
++    def _loadDescription(self):
++        if self._loaded_description is None:
++            description = self._get_hdr()['description'] or ''
++            description = misc.share_data(description)
++            self._loaded_description = description
++        return self._loaded_description
++    description = property(lambda x: x._loadDescription())
++
+     def __str__(self):
+         if self.epoch == '0':
+             val = '%s-%s-%s.%s' % (self.name, self.version, self.release,
+@@ -1828,7 +1845,6 @@ class YumInstalledPackage(YumHeaderPackage):
+                 my_mode = my_st.st_mode
+                 if 'ghost' in ftypes: #  This is what rpm does, although it
+                     my_mode &= 0777   # doesn't usually get here.
+-                    mode    &= 0777
+                 if check_perms and pf.verify_mode and my_mode != pf.mode:
+                     prob = _PkgVerifyProb('mode', 'mode does not match', ftypes)
+                     prob.database_value = pf.mode
+diff --git a/yum/parser.py b/yum/parser.py
+index e46d611..fccf528 100644
+--- a/yum/parser.py
++++ b/yum/parser.py
+@@ -144,6 +144,11 @@ class ConfigPreProcessor:
+                 # the current file returned EOF, pop it off the stack.
+                 self._popfile()
+         
++        # if the section is prefixed by a space then it is breaks iniparser/configparser
++        # so fix it
++        broken_sec_match = re.match(r'\s+\[(?P<section>.*)\]', line)
++        if broken_sec_match:
++            line = line.lstrip()
+         # at this point we have a line from the topmost file on the stack
+         # or EOF if the stack is empty
+         if self._vars:
 diff --git a/yum/rpmsack.py b/yum/rpmsack.py
-index 0982a7c..227ed89 100644
+index 0982a7c..e93df20 100644
 --- a/yum/rpmsack.py
 +++ b/yum/rpmsack.py
-@@ -234,7 +234,7 @@ class RPMDBPackageSack(PackageSackBase):
+@@ -42,11 +42,6 @@ class RPMInstalledPackage(YumInstalledPackage):
+     def __init__(self, rpmhdr, index, rpmdb):
+         self._has_hdr = True
+         YumInstalledPackage.__init__(self, rpmhdr, yumdb=rpmdb.yumdb)
+-        # NOTE: We keep summary/description/url because it doesn't add much
+-        # and "yum search" uses them all.
+-        self.url       = rpmhdr['url']
+-        # Also keep sourcerpm for pirut/etc.
+-        self.sourcerpm = rpmhdr['sourcerpm']
+ 
+         self.idx   = index
+         self.rpmdb = rpmdb
+@@ -67,13 +62,19 @@ class RPMInstalledPackage(YumInstalledPackage):
+             raise Errors.PackageSackError, 'Rpmdb changed underneath us'
+ 
+     def __getattr__(self, varname):
+-        self.hdr = val = self._get_hdr()
+-        self._has_hdr = True
+-        # If these existed, then we wouldn't get here ... and nothing in the DB
+-        # starts and ends with __'s. So these are missing.
+-        if varname.startswith('__') and varname.endswith('__'):
++        # If these existed, then we wouldn't get here...
++        # Prevent access of __foo__, _cached_foo etc from loading the header 
++        if varname.startswith('_'):
+             raise AttributeError, "%s has no attribute %s" % (self, varname)
+-            
++
++        if varname != 'hdr': # Don't cache the hdr, unless explicitly requested
++            #  Note that we don't even cache the .blah value, but looking up the
++            # header is _really_ fast so it's not obvious any of it is worth it.
++            # This is different to prco etc. data, which is loaded separately.
++            val = self._get_hdr()
++        else:
++            self.hdr = val = self._get_hdr()
++            self._has_hdr = True
+         if varname != 'hdr':   #  This is unusual, for anything that happens
+             val = val[varname] # a lot we should preload at __init__.
+                                # Also note that pkg.no_value raises KeyError.
+@@ -234,7 +235,7 @@ class RPMDBPackageSack(PackageSackBase):
                  self._simple_pkgtup_list = csumpkgtups.keys()
  
          if not self._simple_pkgtup_list:
@@ -721,7 +1011,7 @@ index 0982a7c..227ed89 100644
                  self._simple_pkgtup_list.append(self._hdr2pkgTuple(hdr))
              
          return self._simple_pkgtup_list
-@@ -378,54 +378,36 @@ class RPMDBPackageSack(PackageSackBase):
+@@ -378,54 +379,36 @@ class RPMDBPackageSack(PackageSackBase):
          pass
  
      def searchAll(self, name, query_type='like'):
@@ -771,18 +1061,18 @@ index 0982a7c..227ed89 100644
 +            pkg = self._makePackageObject(hdr, idx)
              result.setdefault(pkg.pkgid, pkg)
 -        del mi
- 
+-
 -        result = result.values()
 -
 -        if self.auto_close:
 -            self.ts.close()
--
+ 
 -        return result
 +        return result.values()
          
      def searchPrco(self, name, prcotype):
  
-@@ -438,21 +420,15 @@ class RPMDBPackageSack(PackageSackBase):
+@@ -438,21 +421,15 @@ class RPMDBPackageSack(PackageSackBase):
          if misc.re_glob(n):
              glob = True
              
@@ -806,7 +1096,7 @@ index 0982a7c..227ed89 100644
  
          # If it's not a provides or filename, we are done
          if prcotype == 'provides' and name[0] == '/':
-@@ -463,9 +439,6 @@ class RPMDBPackageSack(PackageSackBase):
+@@ -463,9 +440,6 @@ class RPMDBPackageSack(PackageSackBase):
          result = result.values()
          self._cache[prcotype][name] = result
  
@@ -816,7 +1106,7 @@ index 0982a7c..227ed89 100644
          return result
  
      def searchProvides(self, name):
-@@ -607,7 +580,7 @@ class RPMDBPackageSack(PackageSackBase):
+@@ -607,7 +581,7 @@ class RPMDBPackageSack(PackageSackBase):
  
          if not self._completely_loaded:
              rpats = self._compile_patterns(patterns, ignore_case)
@@ -825,7 +1115,7 @@ index 0982a7c..227ed89 100644
                  if self._match_repattern(rpats, hdr, ignore_case):
                      self._makePackageObject(hdr, idx)
              self._completely_loaded = patterns is None
-@@ -636,18 +609,13 @@ class RPMDBPackageSack(PackageSackBase):
+@@ -636,18 +610,13 @@ class RPMDBPackageSack(PackageSackBase):
  
          if self._cached_conflicts_data is None:
              result = {}
@@ -846,7 +1136,7 @@ index 0982a7c..227ed89 100644
                  result[po.pkgid] = po
                  if po._has_hdr:
                      continue # Unlikely, but, meh...
-@@ -659,9 +627,6 @@ class RPMDBPackageSack(PackageSackBase):
+@@ -659,9 +628,6 @@ class RPMDBPackageSack(PackageSackBase):
                  del po.hdr
              self._cached_conflicts_data = result.values()
  
@@ -856,7 +1146,7 @@ index 0982a7c..227ed89 100644
          return self._cached_conflicts_data
  
      def _write_conflicts_new(self, pkgs, rpmdbv):
-@@ -1168,7 +1133,7 @@ class RPMDBPackageSack(PackageSackBase):
+@@ -1168,7 +1134,7 @@ class RPMDBPackageSack(PackageSackBase):
          if not lowered:
              searchstrings = map(lambda x: x.lower(), searchstrings)
          ret = []
@@ -865,7 +1155,7 @@ index 0982a7c..227ed89 100644
              n = self._find_search_fields(fields, searchstrings, hdr)
              if n > 0:
                  ret.append((self._makePackageObject(hdr, idx), n))
-@@ -1190,41 +1155,20 @@ class RPMDBPackageSack(PackageSackBase):
+@@ -1190,41 +1156,20 @@ class RPMDBPackageSack(PackageSackBase):
          return [ self._makePackageObject(h, mi) for (h, mi) in ts.returnLeafNodes(headers=True) ]
          
      # Helper functions
@@ -887,12 +1177,12 @@ index 0982a7c..227ed89 100644
          del mi
 -        if self.auto_close:
 -            self.ts.close()
--
+ 
 -    def _header_from_index(self, idx):
 -        """returns a package header having been given an index"""
 -        warnings.warn('_header_from_index() will go away in a future version of Yum.\n',
 -                Errors.YumFutureDeprecationWarning, stacklevel=2)
- 
+-
 -        ts = self.readOnlyTS()
 -        try:
 -            mi = ts.dbMatch(0, idx)
@@ -913,7 +1203,7 @@ index 0982a7c..227ed89 100644
      def _search(self, name=None, epoch=None, ver=None, rel=None, arch=None):
          '''List of matching packages, to zero or more of NEVRA.'''
          if name is not None and name in self._pkgname_fails:
-@@ -1254,18 +1198,16 @@ class RPMDBPackageSack(PackageSackBase):
+@@ -1254,18 +1199,16 @@ class RPMDBPackageSack(PackageSackBase):
  
          ts = self.readOnlyTS()
          if name is not None:
@@ -937,7 +1227,7 @@ index 0982a7c..227ed89 100644
              #  We create POs out of all matching names, even if we don't return
              # them.
              self._pkgnames_loaded.add(po.name)
-@@ -1277,9 +1219,6 @@ class RPMDBPackageSack(PackageSackBase):
+@@ -1277,9 +1220,6 @@ class RPMDBPackageSack(PackageSackBase):
              else:
                  ret.append(po)
  
@@ -947,7 +1237,7 @@ index 0982a7c..227ed89 100644
          if not done and name is not None:
              self._pkgname_fails.add(name)
  
-@@ -1323,7 +1262,7 @@ class RPMDBPackageSack(PackageSackBase):
+@@ -1323,7 +1263,7 @@ class RPMDBPackageSack(PackageSackBase):
      def getHdrList(self):
          warnings.warn('getHdrList() will go away in a future version of Yum.\n',
                  DeprecationWarning, stacklevel=2)
@@ -957,10 +1247,32 @@ index 0982a7c..227ed89 100644
      def getNameArchPkgList(self):
          warnings.warn('getNameArchPkgList() will go away in a future version of Yum.\n',
 diff --git a/yum/rpmtrans.py b/yum/rpmtrans.py
-index 0340153..d479829 100644
+index 0340153..08bf99d 100644
 --- a/yum/rpmtrans.py
 +++ b/yum/rpmtrans.py
-@@ -209,8 +209,7 @@ class RPMTransaction:
+@@ -25,6 +25,7 @@ import types
+ import sys
+ from yum.constants import *
+ from yum import _
++from yum.transactioninfo import TransactionMember
+ import misc
+ import tempfile
+ 
+@@ -174,11 +175,11 @@ class RPMTransaction:
+         self.base = base # base yum object b/c we need so much
+         self.test = test # are we a test?
+         self.trans_running = False
+-        self.filehandles = {}
++        self.fd = None
+         self.total_actions = 0
+         self.total_installed = 0
+         self.complete_actions = 0
+-        self.installed_pkg_names = []
++        self.installed_pkg_names = set()
+         self.total_removed = 0
+         self.logger = logging.getLogger('yum.filelogging.RPMInstallCallback')
+         self.filelog = False
+@@ -209,8 +210,7 @@ class RPMTransaction:
          io_r = tempfile.NamedTemporaryFile()
          self._readpipe = io_r
          self._writepipe = open(io_r.name, 'w+b')
@@ -970,6 +1282,249 @@ index 0340153..d479829 100644
          rpmverbosity = {'critical' : 'crit',
                          'emergency' : 'emerg',
                          'error' : 'err',
+@@ -255,12 +255,23 @@ class RPMTransaction:
+ 
+         return (hdr['name'], hdr['arch'], epoch, hdr['version'], hdr['release'])
+ 
+-    def _makeHandle(self, hdr):
+-        handle = '%s:%s.%s-%s-%s' % (hdr['epoch'], hdr['name'], hdr['version'],
+-          hdr['release'], hdr['arch'])
++    # Find out txmbr based on the callback key. On erasures we dont know
++    # the exact txmbr but we always have a name, so return (name, txmbr)
++    # tuples so callers have less twists to deal with.
++    def _getTxmbr(self, cbkey):
++        if isinstance(cbkey, TransactionMember):
++            return (cbkey.name, cbkey)
++        elif isinstance(cbkey, tuple):
++            pkgtup = self._dopkgtup(cbkey[0])
++            txmbrs = self.base.tsInfo.getMembers(pkgtup=pkgtup)
++            # if this is not one, somebody screwed up
++            assert len(txmbrs) == 1
++            return (txmbrs[0].name, txmbrs[0])
++        elif isinstance(cbkey, basestring):
++            return (cbkey, None)
++        else:
++            return (None, None)
+ 
+-        return handle
+-    
+     def ts_done(self, package, action):
+         """writes out the portions of the transaction which have completed"""
+         
+@@ -409,11 +420,10 @@ class RPMTransaction:
+     
+     
+     def _transStart(self, bytes, total, h):
+-        if bytes == 6:
+-            self.total_actions = total
+-            if self.test: return
+-            self.trans_running = True
+-            self.ts_all() # write out what transaction will do
++        self.total_actions = total
++        if self.test: return
++        self.trans_running = True
++        self.ts_all() # write out what transaction will do
+ 
+     def _transProgress(self, bytes, total, h):
+         pass
+@@ -423,62 +433,52 @@ class RPMTransaction:
+ 
+     def _instOpenFile(self, bytes, total, h):
+         self.lastmsg = None
+-        hdr = None
+-        if h is not None:
+-            hdr, rpmloc = h[0], h[1]
+-            handle = self._makeHandle(hdr)
++        name, txmbr = self._getTxmbr(h)
++        if txmbr is not None:
++            rpmloc = txmbr.po.localPkg()
+             try:
+-                fd = os.open(rpmloc, os.O_RDONLY)
+-            except OSError, e:
++                self.fd = file(rpmloc)
++            except IOError, e:
+                 self.display.errorlog("Error: Cannot open file %s: %s" % (rpmloc, e))
+             else:
+-                self.filehandles[handle]=fd
+                 if self.trans_running:
+                     self.total_installed += 1
+                     self.complete_actions += 1
+-                    self.installed_pkg_names.append(hdr['name'])
+-                return fd
++                    self.installed_pkg_names.add(name)
++                return self.fd.fileno()
+         else:
+             self.display.errorlog("Error: No Header to INST_OPEN_FILE")
+             
+     def _instCloseFile(self, bytes, total, h):
+-        hdr = None
+-        if h is not None:
+-            hdr, rpmloc = h[0], h[1]
+-            handle = self._makeHandle(hdr)
+-            os.close(self.filehandles[handle])
+-            fd = 0
++        name, txmbr = self._getTxmbr(h)
++        if txmbr is not None:
++            self.fd.close()
++            self.fd = None
+             if self.test: return
+             if self.trans_running:
+-                pkgtup = self._dopkgtup(hdr)
+-                txmbrs = self.base.tsInfo.getMembers(pkgtup=pkgtup)
+-                for txmbr in txmbrs:
+-                    self.display.filelog(txmbr.po, txmbr.output_state)
+-                    self._scriptout(txmbr.po)
+-                    # NOTE: We only do this for install, not erase atm.
+-                    #       because we don't get pkgtup data for erase (this 
+-                    #       includes "Updated" pkgs).
+-                    pid   = self.base.history.pkg2pid(txmbr.po)
+-                    state = self.base.history.txmbr2state(txmbr)
+-                    self.base.history.trans_data_pid_end(pid, state)
+-                    self.ts_done(txmbr.po, txmbr.output_state)
++                self.display.filelog(txmbr.po, txmbr.output_state)
++                self._scriptout(txmbr.po)
++                # NOTE: We only do this for install, not erase atm.
++                #       because we don't get pkgtup data for erase (this 
++                #       includes "Updated" pkgs).
++                pid   = self.base.history.pkg2pid(txmbr.po)
++                state = self.base.history.txmbr2state(txmbr)
++                self.base.history.trans_data_pid_end(pid, state)
++                self.ts_done(txmbr.po, txmbr.output_state)
+     
+     def _instProgress(self, bytes, total, h):
+-        if h is not None:
+-            # If h is a string, we're repackaging.
++        name, txmbr = self._getTxmbr(h)
++        if name is not None:
++            # If we only have a name, we're repackaging.
+             # Why the RPMCALLBACK_REPACKAGE_PROGRESS flag isn't set, I have no idea
+-            if type(h) == type(""):
+-                self.display.event(h, 'repackaging',  bytes, total,
++            if txmbr is None:
++                self.display.event(name, 'repackaging',  bytes, total,
+                                 self.complete_actions, self.total_actions)
+-
+             else:
+-                hdr, rpmloc = h[0], h[1]
+-                pkgtup = self._dopkgtup(hdr)
+-                txmbrs = self.base.tsInfo.getMembers(pkgtup=pkgtup)
+-                for txmbr in txmbrs:
+-                    action = txmbr.output_state
+-                    self.display.event(txmbr.po, action, bytes, total,
+-                                self.complete_actions, self.total_actions)
++                action = txmbr.output_state
++                self.display.event(txmbr.po, action, bytes, total,
++                            self.complete_actions, self.total_actions)
++
+     def _unInstStart(self, bytes, total, h):
+         pass
+         
+@@ -486,20 +486,21 @@ class RPMTransaction:
+         pass
+     
+     def _unInstStop(self, bytes, total, h):
++        name, txmbr = self._getTxmbr(h)
+         self.total_removed += 1
+         self.complete_actions += 1
+-        if h not in self.installed_pkg_names:
+-            self.display.filelog(h, TS_ERASE)
++        if name not in self.installed_pkg_names:
++            self.display.filelog(name, TS_ERASE)
+             action = TS_ERASE
+         else:
+             action = TS_UPDATED                    
+         
+-        self.display.event(h, action, 100, 100, self.complete_actions,
++        self.display.event(name, action, 100, 100, self.complete_actions,
+                             self.total_actions)
+-        self._scriptout(h)
++        self._scriptout(name)
+         
+         if self.test: return # and we're done
+-        self.ts_done(h, action)
++        self.ts_done(name, action)
+         
+         
+     def _rePackageStart(self, bytes, total, h):
+@@ -512,20 +513,16 @@ class RPMTransaction:
+         pass
+         
+     def _cpioError(self, bytes, total, h):
+-        hdr, rpmloc = h[0], h[1]
+-        pkgtup = self._dopkgtup(hdr)
+-        txmbrs = self.base.tsInfo.getMembers(pkgtup=pkgtup)
+-        for txmbr in txmbrs:
++        name, txmbr = self._getTxmbr(h)
++        if txmbr is not None:
+             msg = "Error in cpio payload of rpm package %s" % txmbr.po
+             txmbr.output_state = TS_FAILED
+             self.display.errorlog(msg)
+             # FIXME - what else should we do here? raise a failure and abort?
+     
+     def _unpackError(self, bytes, total, h):
+-        hdr, rpmloc = h[0], h[1]
+-        pkgtup = self._dopkgtup(hdr)
+-        txmbrs = self.base.tsInfo.getMembers(pkgtup=pkgtup)
+-        for txmbr in txmbrs:
++        name, txmbr = self._getTxmbr(h)
++        if txmbr is not None:
+             txmbr.output_state = TS_FAILED
+             msg = "Error unpacking rpm package %s" % txmbr.po
+             self.display.errorlog(msg)
+@@ -533,35 +530,24 @@ class RPMTransaction:
+             # right behavior should be
+                 
+     def _scriptError(self, bytes, total, h):
+-        if not isinstance(h, types.TupleType):
+-            # fun with install/erase transactions, see rhbz#484729
+-            h = (h, None)
+-        hdr, rpmloc = h[0], h[1]
+-        remove_hdr = False # if we're in a clean up/remove then hdr will not be an rpm.hdr
+-        if not isinstance(hdr, rpm.hdr):
+-            txmbrs = [hdr]
+-            remove_hdr = True
++        # "bytes" carries the failed scriptlet tag,
++        # "total" carries fatal/non-fatal status
++        scriptlet_name = rpm.tagnames.get(bytes, "<unknown>")
++
++        name, txmbr = self._getTxmbr(h)
++        if txmbr is None:
++            package_name = name
+         else:
+-            pkgtup = self._dopkgtup(hdr)
+-            txmbrs = self.base.tsInfo.getMembers(pkgtup=pkgtup)
++            package_name = txmbr.po
+             
+-        for pkg in txmbrs:
+-            # "bytes" carries the failed scriptlet tag,
+-            # "total" carries fatal/non-fatal status
+-            scriptlet_name = rpm.tagnames.get(bytes, "<unknown>")
+-            if remove_hdr:
+-                package_name = pkg
+-            else:
+-                package_name = pkg.po
+-                
+-            if total:
+-                msg = ("Error in %s scriptlet in rpm package %s" % 
+-                        (scriptlet_name, package_name))
+-                if not remove_hdr:        
+-                    pkg.output_state = TS_FAILED
+-            else:
+-                msg = ("Non-fatal %s scriptlet failure in rpm package %s" % 
+-                       (scriptlet_name, package_name))
+-            self.display.errorlog(msg)
+-            # FIXME - what else should we do here? raise a failure and abort?
++        if total:
++            msg = ("Error in %s scriptlet in rpm package %s" % 
++                    (scriptlet_name, package_name))
++            if txmbr is not None:        
++                txmbr.output_state = TS_FAILED
++        else:
++            msg = ("Non-fatal %s scriptlet failure in rpm package %s" % 
++                   (scriptlet_name, package_name))
++        self.display.errorlog(msg)
++        # FIXME - what else should we do here? raise a failure and abort?
+     
 diff --git a/yum/update_md.py b/yum/update_md.py
 index 83e56c6..39fa72e 100644
 --- a/yum/update_md.py
@@ -1000,7 +1555,7 @@ index 83e56c6..39fa72e 100644
                  try:
                      un = UpdateNotice(elem)
 diff --git a/yumcommands.py b/yumcommands.py
-index ecce347..f196477 100644
+index ecce347..62b8746 100644
 --- a/yumcommands.py
 +++ b/yumcommands.py
 @@ -46,7 +46,7 @@ def checkRootUID(base):
@@ -1012,7 +1567,23 @@ index ecce347..f196477 100644
                  msg = _("""
  You have enabled checking of packages via GPG keys. This is a good thing. 
  However, you do not have any GPG public keys installed. You need to download
-@@ -972,6 +972,12 @@ class RepoListCommand(YumCommand):
+@@ -626,13 +626,14 @@ class CheckUpdateCommand(YumCommand):
+         checkEnabledRepo(base)
+ 
+     def doCommand(self, base, basecmd, extcmds):
++        obscmds = ['obsoletes'] + extcmds
+         base.extcmds.insert(0, 'updates')
+         result = 0
+         try:
+             ypl = base.returnPkgLists(extcmds)
+             if (base.conf.obsoletes or
+                 base.verbose_logger.isEnabledFor(logginglevels.DEBUG_3)):
+-                typl = base.returnPkgLists(['obsoletes'])
++                typl = base.returnPkgLists(obscmds)
+                 ypl.obsoletes = typl.obsoletes
+                 ypl.obsoletesTuples = typl.obsoletesTuples
+ 
+@@ -972,6 +973,12 @@ class RepoListCommand(YumCommand):
                      elif repo.mirrorlist:
                          out += [base.fmtKeyValFill(_("Repo-mirrors : "),
                                                     repo.mirrorlist)]
diff --git a/yum.spec b/yum.spec
index 79e3a82..ca4c9fd 100644
--- a/yum.spec
+++ b/yum.spec
@@ -7,7 +7,7 @@
 Summary: RPM package installer/updater/manager
 Name: yum
 Version: 3.2.29
-Release: 6%{?dist}
+Release: 7%{?dist}
 License: GPLv2+
 Group: System Environment/Base
 Source0: http://yum.baseurl.org/download/3.2/%{name}-%{version}.tar.gz
@@ -248,6 +248,9 @@ exit 0
 %config(noreplace) %{_sysconfdir}/sysconfig/yum-cron
 
 %changelog
+* Mon Feb 28 2011 Seth Vidal <skvidal at fedoraproject.org> - 3.2.29-7
+- latest head including all of Panu's rpmutils/callback patches
+
 * Thu Feb 17 2011 Seth Vidal <skvidal at fedoraproject.org> - 3.2.29-6
 - add rpmutils-recursive-import.patch to work around recursive import problems
 


More information about the scm-commits mailing list