[yum] update to latest HEAD Add new yum DB data. Add hack to workaround broken python readline in yum shel

James Antill james at fedoraproject.org
Fri Aug 5 17:30:18 UTC 2011


commit 26aebfad16d36af68acccebe4d630c615e4afa8b
Author: James Antill <james at and.org>
Date:   Fri Aug 5 13:29:54 2011 -0400

    update to latest HEAD
    Add new yum DB data.
    Add hack to workaround broken python readline in yum shell.
    Make "yum -q history addon-info last saved_tx" valid input for load-ts.
    Add "history packages-info/stats/sync" sub-commnands.

 yum-HEAD.patch | 1032 +++++++++++++++++++++++++++++++++++++++++++++++++++-----
 yum.spec       |    9 +-
 2 files changed, 955 insertions(+), 86 deletions(-)
---
diff --git a/yum-HEAD.patch b/yum-HEAD.patch
index 68a8d9d..74ff6ed 100644
--- a/yum-HEAD.patch
+++ b/yum-HEAD.patch
@@ -1459,10 +1459,72 @@ index 0000000..d2a0ed1
 +if __name__ == "__main__":
 +    generateAll(os.getcwd(), os.getcwd())
 diff --git a/docs/yum.8 b/docs/yum.8
-index 1a8202a..ea18f34 100644
+index 1a8202a..255c755 100644
 --- a/docs/yum.8
 +++ b/docs/yum.8
-@@ -401,6 +401,11 @@ Assume yes; assume that the answer to any question which would be asked
+@@ -69,7 +69,9 @@ gnome\-packagekit application\&.
+ .br
+ .I \fR * version [ all | installed | available | group-* | nogroups* | grouplist | groupinfo ]
+ .br
+-.I \fR * history [info|list|packages-list|summary|addon-info|redo|undo|rollback|new] 
++.I \fR * history [info|list|packages-list|packages-info|summary|addon-info|redo|undo|rollback|new|sync|stats] 
++.br
++.I \fR * load-transaction [txfile]
+ .br
+ .I \fR * check
+ .br 
+@@ -321,15 +323,17 @@ 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
+-info/list/packages-list/summary to view what happened, undo/redo/rollback to act
+-on that information and new to start a new history file.
++info/list/packages-list/packages-info/summary to view what happened,
++undo/redo/rollback to act on that information and new to start a new history
++file.
+ 
+ The info/list/summary commands take either a transaction id or a package (with
+ wildcards, as in \fBSpecifying package names\fP), all three can also be passed
+ no arguments. list can be passed the keyword "all" to list all the transactions.
+ 
+-The packages-list command takes a package  (with wildcards, as in
+-\fBSpecifying package names\fP).
++The packages-list/packages-info commands takes a package  (with wildcards, as in
++\fBSpecifying package names\fP). And show data from the point of view of that
++package.
+ 
+ The undo/redo/rollback commands take either a single transaction id or the
+ keyword last and an offset from the last transaction (Eg. if you've done 250
+@@ -349,6 +353,12 @@ transactions 1 and 4.
+ The addon-info command takes a transaction ID, and the packages-list command
+ takes a package (with wildcards).
+ 
++The stats command shows some statistics about the current history DB.
++
++The sync commands allows you to change the rpmdb/yumdb data stored for any
++installed packages, to whaever is in the current rpmdb/yumdb (this is mostly
++useful when this data was not stored when the package went into the history DB).
++
+ In "history list" you can change the behaviour of the 2nd column via. the
+ configuration option history_list_view.
+ 
+@@ -371,6 +381,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
+ 
++
++.IP
++.IP "\fBload-transaction\fP"
++This command will re-load a saved yum transaction file, this allows you to
++run a transaction on one machine and then use it on another.
++The two common ways to get a saved yum transaction file are from
++"yum -q history addon-info last saved_tx" or via. the automatic saves in
++$TMPDIR/yum_save_tx.* when a transaction is solved but not run.
++
+ .IP
+ .IP "\fBcheck\fP"
+ Checks the local rpmdb and produces information on any problems it finds. You
+@@ -401,6 +420,11 @@ Assume yes; assume that the answer to any question which would be asked
  is yes\&.
  .br
  Configuration Option: \fBassumeyes\fP
@@ -1956,7 +2018,7 @@ index f1e06e8..2faeb59 100644
              ;;
          version)
 diff --git a/output.py b/output.py
-index b6aa277..9610232 100755
+index b6aa277..00e0e6f 100755
 --- a/output.py
 +++ b/output.py
 @@ -1,6 +1,6 @@
@@ -1967,7 +2029,16 @@ index b6aa277..9610232 100755
  
  # This program is free software; you can redistribute it and/or modify
  # it under the terms of the GNU General Public License as published by
-@@ -60,17 +60,21 @@ def _term_width():
+@@ -47,6 +47,8 @@ import yum.history
+ 
+ from yum.i18n import utf8_width, utf8_width_fill, utf8_text_fill
+ 
++import locale
++
+ def _term_width():
+     """ Simple terminal width, limit to 20 chars. and make 0 == 80. """
+     if not hasattr(urlgrabber.progress, 'terminal_width_cached'):
+@@ -60,17 +62,21 @@ def _term_width():
  
  
  class YumTextMeter(TextMeter):
@@ -1994,7 +2065,7 @@ index b6aa277..9610232 100755
  
      # From initial search for "terminfo and python" got:
      # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/475116
-@@ -145,6 +149,17 @@ class YumTerm:
+@@ -145,6 +151,17 @@ class YumTerm:
          self.BG_COLOR = self.__ansi_forced_BG_COLOR
  
      def reinit(self, term_stream=None, color='auto'):
@@ -2012,7 +2083,7 @@ index b6aa277..9610232 100755
          self.__enabled = True
          if not hasattr(urlgrabber.progress, 'terminal_width_cached'):
              self.columns = 80
-@@ -255,6 +270,37 @@ class YumTerm:
+@@ -255,6 +272,37 @@ class YumTerm:
          return re.sub(r'\$<\d+>[/*]?', '', cap)
  
      def sub(self, haystack, beg, end, needles, escape=None, ignore_case=False):
@@ -2050,7 +2121,7 @@ index b6aa277..9610232 100755
          if not self.__enabled:
              return haystack
  
-@@ -269,27 +315,106 @@ class YumTerm:
+@@ -269,27 +317,106 @@ class YumTerm:
              haystack = re.sub(pat, render, haystack)
          return haystack
      def sub_norm(self, haystack, beg, needles, **kwds):
@@ -2161,7 +2232,7 @@ index b6aa277..9610232 100755
  
      def __init__(self):
          self.logger = logging.getLogger("yum.cli")
-@@ -304,6 +429,12 @@ class YumOutput:
+@@ -304,6 +431,12 @@ class YumOutput:
  
      
      def printtime(self):
@@ -2174,7 +2245,7 @@ index b6aa277..9610232 100755
          months = [_('Jan'), _('Feb'), _('Mar'), _('Apr'), _('May'), _('Jun'),
                    _('Jul'), _('Aug'), _('Sep'), _('Oct'), _('Nov'), _('Dec')]
          now = time.localtime(time.time())
-@@ -312,14 +443,27 @@ class YumOutput:
+@@ -312,14 +445,27 @@ class YumOutput:
          return ret
           
      def failureReport(self, errobj):
@@ -2204,7 +2275,7 @@ index b6aa277..9610232 100755
          progressbar(current, total, name)
  
      def _highlight(self, highlight):
-@@ -368,9 +512,29 @@ class YumOutput:
+@@ -368,9 +514,29 @@ class YumOutput:
  
      def calcColumns(self, data, columns=None, remainder_column=0,
                      total_width=None, indent=''):
@@ -2237,7 +2308,7 @@ index b6aa277..9610232 100755
          if total_width is None:
              total_width = self.term.columns
  
-@@ -473,10 +637,20 @@ class YumOutput:
+@@ -473,10 +639,20 @@ class YumOutput:
          return (val, width, hibeg, hiend)
  
      def fmtColumns(self, columns, msg=u'', end=u'', text_width=utf8_width):
@@ -2262,7 +2333,7 @@ index b6aa277..9610232 100755
          total_width = len(msg)
          data = []
          for col_data in columns[:-1]:
-@@ -513,8 +687,18 @@ class YumOutput:
+@@ -513,8 +689,18 @@ class YumOutput:
  
      def simpleList(self, pkg, ui_overflow=False, indent='', highlight=False,
                     columns=None):
@@ -2283,7 +2354,7 @@ index b6aa277..9610232 100755
          if columns is None:
              columns = (-40, -22, -16) # Old default
          ver = pkg.printVer()
-@@ -526,9 +710,19 @@ class YumOutput:
+@@ -526,9 +712,19 @@ class YumOutput:
  
      def simpleEnvraList(self, pkg, ui_overflow=False,
                          indent='', highlight=False, columns=None):
@@ -2306,7 +2377,7 @@ index b6aa277..9610232 100755
          if columns is None:
              columns = (-63, -16) # Old default
          envra = '%s%s' % (indent, str(pkg))
-@@ -538,7 +732,13 @@ class YumOutput:
+@@ -538,7 +734,13 @@ class YumOutput:
          print self.fmtColumns(columns, text_width=len)
  
      def fmtKeyValFill(self, key, val):
@@ -2321,7 +2392,7 @@ index b6aa277..9610232 100755
          val = to_str(val)
          keylen = utf8_width(key)
          cols = self.term.columns
-@@ -553,6 +753,15 @@ class YumOutput:
+@@ -553,6 +755,15 @@ class YumOutput:
          return ret
      
      def fmtSection(self, name, fill='='):
@@ -2337,7 +2408,7 @@ index b6aa277..9610232 100755
          name = to_str(name)
          cols = self.term.columns - 2
          name_len = utf8_width(name)
-@@ -577,6 +786,12 @@ class YumOutput:
+@@ -577,6 +788,12 @@ class YumOutput:
          return to_unicode(s)
  
      def infoOutput(self, pkg, highlight=False):
@@ -2350,7 +2421,7 @@ index b6aa277..9610232 100755
          (hibeg, hiend) = self._highlight(highlight)
          print _("Name        : %s%s%s") % (hibeg, to_unicode(pkg.name), hiend)
          print _("Arch        : %s") % to_unicode(pkg.arch)
-@@ -617,9 +832,22 @@ class YumOutput:
+@@ -617,9 +834,22 @@ class YumOutput:
          print ""
      
      def updatesObsoletesList(self, uotup, changetype, columns=None):
@@ -2376,7 +2447,7 @@ index b6aa277..9610232 100755
          (changePkg, instPkg) = uotup
  
          if columns is not None:
-@@ -640,12 +868,44 @@ class YumOutput:
+@@ -640,12 +870,44 @@ class YumOutput:
  
      def listPkgs(self, lst, description, outputType, highlight_na={},
                   columns=None, highlight_modes={}):
@@ -2427,7 +2498,7 @@ index b6aa277..9610232 100755
          if outputType in ['list', 'info']:
              thingslisted = 0
              if len(lst) > 0:
-@@ -679,8 +939,11 @@ class YumOutput:
+@@ -679,8 +941,11 @@ class YumOutput:
      
          
      def userconfirm(self):
@@ -2440,7 +2511,7 @@ index b6aa277..9610232 100755
          yui = (to_unicode(_('y')), to_unicode(_('yes')))
          nui = (to_unicode(_('n')), to_unicode(_('no')))
          aui = (yui[0], yui[1], nui[0], nui[1])
-@@ -774,6 +1037,10 @@ class YumOutput:
+@@ -774,6 +1039,10 @@ class YumOutput:
                                           columns=columns)
      
      def displayPkgsInGroups(self, group):
@@ -2451,7 +2522,7 @@ index b6aa277..9610232 100755
          print _('\nGroup: %s') % group.ui_name
  
          verb = self.verbose_logger.isEnabledFor(logginglevels.DEBUG_3)
-@@ -807,8 +1074,11 @@ class YumOutput:
+@@ -807,8 +1076,11 @@ class YumOutput:
                                             columns=columns)
  
      def depListOutput(self, results):
@@ -2465,7 +2536,7 @@ index b6aa277..9610232 100755
          verb = self.verbose_logger.isEnabledFor(logginglevels.DEBUG_3)
          for pkg in sorted(results):
              print _("package: %s") % pkg.compactPrint()
-@@ -832,7 +1102,18 @@ class YumOutput:
+@@ -832,7 +1104,18 @@ class YumOutput:
                      print "   provider: %s" % po.compactPrint()
  
      def format_number(self, number, SI=0, space=' '):
@@ -2485,7 +2556,7 @@ index b6aa277..9610232 100755
          symbols = [ ' ', # (none)
                      'k', # kilo
                      'M', # mega
-@@ -870,16 +1151,31 @@ class YumOutput:
+@@ -870,16 +1153,31 @@ class YumOutput:
  
      @staticmethod
      def format_time(seconds, use_hours=0):
@@ -2523,7 +2594,7 @@ index b6aa277..9610232 100755
          if self.conf.showdupesfromrepos:
              msg = '%s : ' % po
          else:
-@@ -935,10 +1231,23 @@ class YumOutput:
+@@ -935,10 +1233,23 @@ class YumOutput:
          print '\n\n'
  
      def matchcallback_verbose(self, po, values, matchfor=None):
@@ -2548,7 +2619,7 @@ index b6aa277..9610232 100755
          totsize = 0
          locsize = 0
          insize  = 0
-@@ -982,7 +1291,10 @@ class YumOutput:
+@@ -982,7 +1293,10 @@ class YumOutput:
                                          self.format_number(insize))
  
      def reportRemoveSize(self, packages):
@@ -2560,7 +2631,7 @@ index b6aa277..9610232 100755
          totsize = 0
          error = False
          for pkg in packages:
-@@ -1002,8 +1314,9 @@ class YumOutput:
+@@ -1002,8 +1316,9 @@ class YumOutput:
                                      self.format_number(totsize))
              
      def listTransaction(self):
@@ -2572,7 +2643,7 @@ index b6aa277..9610232 100755
          self.tsInfo.makelists(True, True)
          pkglist_lines = []
          data  = {'n' : {}, 'v' : {}, 'r' : {}}
-@@ -1115,6 +1428,12 @@ Transaction Summary
+@@ -1115,6 +1430,12 @@ Transaction Summary
          return ''.join(out)
          
      def postTransactionOutput(self):
@@ -2585,7 +2656,7 @@ index b6aa277..9610232 100755
          out = ''
          
          self.tsInfo.makelists()
-@@ -1179,9 +1498,9 @@ Transaction Summary
+@@ -1179,9 +1500,9 @@ Transaction Summary
          return out
  
      def setupProgressCallbacks(self):
@@ -2598,7 +2669,7 @@ index b6aa277..9610232 100755
          # if we're below 2 on the debug level we don't need to be outputting
          # progress bars - this is hacky - I'm open to other options
          # One of these is a download
-@@ -1216,10 +1535,12 @@ Transaction Summary
+@@ -1216,10 +1537,12 @@ Transaction Summary
          self.dsCallback = dscb
      
      def setupProgessCallbacks(self):
@@ -2612,7 +2683,7 @@ index b6aa277..9610232 100755
          confirm_func = self._cli_confirm_gpg_key_import
          gpg_import_func = self.getKeyForRepo
          gpgca_import_func = self.getCAKeyForRepo
-@@ -1233,14 +1554,12 @@ Transaction Summary
+@@ -1233,14 +1556,12 @@ Transaction Summary
              self.repos.gpgca_import_func = gpgca_import_func
  
      def interrupt_callback(self, cbobj):
@@ -2632,7 +2703,7 @@ index b6aa277..9610232 100755
          '''
          delta_exit_chk = 2.0      # Delta between C-c's so we treat as exit
          delta_exit_str = _("two") # Human readable version of above
-@@ -1269,6 +1588,14 @@ to exit.
+@@ -1269,6 +1590,14 @@ to exit.
  
      def download_callback_total_cb(self, remote_pkgs, remote_size,
                                     download_start_timestamp):
@@ -2647,7 +2718,7 @@ index b6aa277..9610232 100755
          if len(remote_pkgs) <= 1:
              return
          if not hasattr(urlgrabber.progress, 'TerminalLine'):
-@@ -1434,8 +1761,17 @@ to exit.
+@@ -1434,8 +1763,17 @@ to exit.
          return tids, printall
  
      def historyListCmd(self, extcmds):
@@ -2657,16 +2728,16 @@ index b6aa277..9610232 100755
 +
 +        :param extcmds: list of extra command line arguments
 +        :return: (exit_code, [errors])
- 
-+        exit_code is::
 +
++        exit_code is::
+ 
 +            0 = we're done, exit
 +            1 = we've errored, exit with error string
 +        """
          tids, printall = self._history_list_transactions(extcmds)
          if tids is None:
              return 1, ['Failed history list']
-@@ -1564,6 +1900,16 @@ to exit.
+@@ -1564,6 +1902,16 @@ to exit.
          return old[0]
  
      def historyInfoCmd(self, extcmds):
@@ -2683,7 +2754,17 @@ index b6aa277..9610232 100755
          def str2int(x):
              try:
                  return int(x)
-@@ -1833,6 +2179,13 @@ to exit.
+@@ -1656,6 +2004,9 @@ to exit.
+     def _hpkg2from_repo(self, hpkg):
+         """ Given a pkg, find the ipkg.ui_from_repo ... if none, then
+             get an apkg. ... and put a ? in there. """
++        if 'from_repo' in hpkg.yumdb_info:
++            return hpkg.ui_from_repo
++
+         ipkgs = self.rpmdb.searchPkgTuple(hpkg.pkgtup)
+         if not ipkgs:
+             apkgs = self.pkgSack.searchPkgTuple(hpkg.pkgtup)
+@@ -1833,6 +2184,13 @@ to exit.
                                'Updated'      : _('Updated'),
                                }
      def historyInfoCmdPkgsAltered(self, old, pats=[]):
@@ -2697,7 +2778,7 @@ index b6aa277..9610232 100755
          last = None
          #  Note that these don't use _simple_pkg() because we are showing what
          # happened to them in the transaction ... not the difference between the
-@@ -1886,6 +2239,10 @@ to exit.
+@@ -1886,6 +2244,10 @@ to exit.
                                          self._hpkg2from_repo(hpkg))
  
      def historySummaryCmd(self, extcmds):
@@ -2708,7 +2789,7 @@ index b6aa277..9610232 100755
          tids, printall = self._history_list_transactions(extcmds)
          if tids is None:
              return 1, ['Failed history info']
-@@ -1946,6 +2303,10 @@ to exit.
+@@ -1946,6 +2308,10 @@ to exit.
                               utf8_width_fill(uiacts, 16, 16), count)
  
      def historyAddonInfoCmd(self, extcmds):
@@ -2719,8 +2800,20 @@ index b6aa277..9610232 100755
          tid = None
          if len(extcmds) > 1:
              tid = extcmds[1]
-@@ -1991,8 +2352,11 @@ to exit.
-             print ''
+@@ -1983,16 +2349,19 @@ to exit.
+         
+         for item in extcmds[2:]:
+             if item in addon_info:
+-                print '%s:' % item
+-                print self.history.return_addon_data(hist_data.tid, item)
++                self.verbose_logger.log(logginglevels.INFO_2, '%s:', item)
++                print self.history.return_addon_data(hist_data.tid, item),
++                self.verbose_logger.log(logginglevels.INFO_2, '')
+             else:
+                 print _('%s: No additional data found by this name') % item
+-
+-            print ''
++            self.verbose_logger.log(logginglevels.INFO_2, '')
  
      def historyPackageListCmd(self, extcmds):
 -        """ Shows the user a list of data about the history, from the point
@@ -2733,8 +2826,96 @@ index b6aa277..9610232 100755
          tids = self.history.search(extcmds)
          limit = None
          if extcmds and not tids:
-@@ -2080,7 +2444,7 @@ to exit.
+@@ -2078,9 +2447,95 @@ to exit.
+             if lastdbv.end_rpmdbversion != rpmdbv:
+                 self._rpmdb_warn_checks()
  
++    def historyPackageInfoCmd(self, extcmds):
++        """Print information about packages in history transactions.
++
++        :param extcmds: list of extra command line arguments
++        """
++        tids = self.history.search(extcmds)
++        limit = None
++        if extcmds and not tids:
++            self.logger.critical(_('Bad transaction IDs, or package(s), given'))
++            return 1, ['Failed history packages-info']
++        if not tids:
++            limit = 20
++
++        all_uistates = self._history_state2uistate
++
++        num = 0
++        for old in self.history.old(tids, limit=limit):
++            if limit is not None and num and (num +len(old.trans_data)) > limit:
++                break
++            last = None
++
++            for hpkg in old.trans_data: # Find a pkg to go with each cmd...
++                if limit is None:
++                    x,m,u = yum.packages.parsePackages([hpkg], extcmds)
++                    if not x and not m:
++                        continue
++
++                uistate = all_uistates.get(hpkg.state, hpkg.state)
++                if num:
++                    print ""
++                print _("Transaction ID :"), old.tid
++                tm = time.ctime(old.beg_timestamp)
++                print _("Begin time     :"), tm
++                print _("Package        :"), hpkg.ui_nevra
++                print _("State          :"), uistate
++                if hpkg.size is not None:
++                    num = int(hpkg.size)
++                    print _("Size           :"), locale.format("%d", num, True)
++                if hpkg.buildhost is not None:
++                    print _("Build host     :"), hpkg.buildhost
++                if hpkg.buildtime is not None:
++                    tm = time.ctime(int(hpkg.buildtime))
++                    print _("Build time     :"), tm
++                if hpkg.packager is not None:
++                    print _("Packager       :"), hpkg.packager
++                if hpkg.vendor is not None:
++                    print _("Vendor         :"), hpkg.vendor
++                if hpkg.license is not None:
++                    print _("License        :"), hpkg.license
++                if hpkg.url is not None:
++                    print _("URL            :"), hpkg.url
++                if hpkg.sourcerpm is not None:
++                    print _("Source RPM     :"), hpkg.sourcerpm
++                if hpkg.committime is not None:
++                    tm = time.ctime(int(hpkg.committime))
++                    print _("Commit Time    :"), tm
++                if hpkg.committer is not None:
++                    print _("Committer      :"), hpkg.committer
++                if hpkg.yumdb_info.reason is not None:
++                    print _("Reason         :"), hpkg.yumdb_info.reason
++                if hpkg.yumdb_info.command_line is not None:
++                    print _("Command Line   :"), hpkg.yumdb_info.command_line
++                if hpkg.yumdb_info.from_repo is not None:
++                    print _("From repo      :"), hpkg.yumdb_info.from_repo
++                if hpkg.yumdb_info.installed_by is not None:
++                    uid = int(hpkg.yumdb_info.installed_by)
++                    name = self._pwd_ui_username(uid)
++                    print _("Installed by   :"), name
++                if hpkg.yumdb_info.changed_by is not None:
++                    uid = int(hpkg.yumdb_info.changed_by)
++                    name = self._pwd_ui_username(uid)
++                    print _("Changed by     :"), name
++
++                num += 1
++
++        # And, again, copy and paste...
++        lastdbv = self.history.last()
++        if lastdbv is None:
++            self._rpmdb_warn_checks(warn=False)
++        else:
++            #  If this is the last transaction, is good and it doesn't
++            # match the current rpmdb ... then mark it as bad.
++            rpmdbv  = self.rpmdb.simpleVersion(main_only=True)[0]
++            if lastdbv.end_rpmdbversion != rpmdbv:
++                self._rpmdb_warn_checks()
++
  
  class DepSolveProgressCallBack:
 -    """provides text output callback functions for Dependency Solver callback"""
@@ -2742,7 +2923,7 @@ index b6aa277..9610232 100755
      
      def __init__(self, ayum=None):
          """requires yum-cli log and errorlog functions as arguments"""
-@@ -2089,6 +2453,25 @@ class DepSolveProgressCallBack:
+@@ -2089,6 +2544,25 @@ class DepSolveProgressCallBack:
          self.ayum = ayum
  
      def pkgAdded(self, pkgtup, mode):
@@ -2768,7 +2949,7 @@ index b6aa277..9610232 100755
          modedict = { 'i': _('installed'),
                       'u': _('an update'),
                       'e': _('erased'),
-@@ -2104,43 +2487,85 @@ class DepSolveProgressCallBack:
+@@ -2104,43 +2578,85 @@ class DepSolveProgressCallBack:
              modeterm)
          
      def start(self):
@@ -2856,7 +3037,7 @@ index b6aa277..9610232 100755
          needname, needflags, needversion = reqTup
  
          yb = self.ayum
-@@ -2225,45 +2650,89 @@ class DepSolveProgressCallBack:
+@@ -2225,45 +2741,89 @@ class DepSolveProgressCallBack:
          return msg
      
      def procConflict(self, name, confname):
@@ -2950,7 +3131,7 @@ index b6aa277..9610232 100755
          progressbar(current, total, name)
  
  def _pkgname_ui(ayum, pkgname, ts_states=None):
-@@ -2316,10 +2785,7 @@ def _pkgname_ui(ayum, pkgname, ts_states=None):
+@@ -2316,10 +2876,7 @@ def _pkgname_ui(ayum, pkgname, ts_states=None):
      return pkgname
  
  class YumCliRPMCallBack(RPMBaseCallback):
@@ -2962,7 +3143,7 @@ index b6aa277..9610232 100755
  
      width = property(lambda x: _term_width())
  
-@@ -2337,11 +2803,31 @@ class YumCliRPMCallBack(RPMBaseCallback):
+@@ -2337,11 +2894,31 @@ 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)):
@@ -2996,7 +3177,7 @@ index b6aa277..9610232 100755
          process = self.action[action]
  
          if not hasattr(self, '_max_action_wid'):
-@@ -2366,6 +2852,7 @@ class YumCliRPMCallBack(RPMBaseCallback):
+@@ -2366,6 +2943,7 @@ class YumCliRPMCallBack(RPMBaseCallback):
          
          if self.output and (sys.stdout.isatty() or te_current == te_total):
              (fmt, wid1, wid2) = self._makefmt(percent, ts_current, ts_total,
@@ -3004,7 +3185,7 @@ index b6aa277..9610232 100755
                                                pkgname=pkgname, wid1=wid1)
              msg = fmt % (utf8_width_fill(process, wid1, wid1),
                           utf8_width_fill(pkgname, wid2, wid2))
-@@ -2377,6 +2864,11 @@ class YumCliRPMCallBack(RPMBaseCallback):
+@@ -2377,6 +2955,11 @@ class YumCliRPMCallBack(RPMBaseCallback):
                  print " "
  
      def scriptout(self, package, msgs):
@@ -3016,7 +3197,7 @@ index b6aa277..9610232 100755
          if msgs:
              sys.stdout.write(to_unicode(msgs))
              sys.stdout.flush()
-@@ -2431,6 +2923,15 @@ class YumCliRPMCallBack(RPMBaseCallback):
+@@ -2431,6 +3014,15 @@ class YumCliRPMCallBack(RPMBaseCallback):
  
  
  def progressbar(current, total, name=None):
@@ -3083,11 +3264,20 @@ index 6082005..ad7bbb1 100644
      elif myarch.startswith("arm"):
          return "arm"
 diff --git a/shell.py b/shell.py
-index 999bffc..636f12c 100644
+index 999bffc..34a492e 100644
 --- a/shell.py
 +++ b/shell.py
-@@ -29,10 +29,7 @@ import yum.logginglevels as logginglevels
+@@ -23,16 +23,14 @@ import cmd
+ import shlex
+ import logging
  
+-from yum import Errors
++from yum import Errors, _
+ from yum.constants import *
+ import yum.logginglevels as logginglevels
+-
++from yum.i18n import to_utf8
++import __builtin__
  
  class YumShell(cmd.Cmd):
 -
@@ -3098,9 +3288,38 @@ index 999bffc..636f12c 100644
  
      def __init__(self, base):
          cmd.Cmd.__init__(self)
-@@ -77,6 +74,11 @@ class YumShell(cmd.Cmd):
-         return inputs
+@@ -75,8 +73,39 @@ class YumShell(cmd.Cmd):
+                 raise Errors.YumBaseError, "Fatal error in script, exiting"
          
+         return inputs
+-        
++
++    def cmdloop(self, *args, **kwargs):
++        """ Sick hack for readline. """
++
++        oraw_input = raw_input
++        owriter    = sys.stdout
++        _ostdout   = owriter.stream
++
++        def _sick_hack_raw_input(prompt):
++            sys.stdout = _ostdout
++            rret = oraw_input(to_utf8(prompt))
++            sys.stdout = owriter
++
++            return rret
++
++        __builtin__.raw_input = _sick_hack_raw_input
++
++        try:
++            cret = cmd.Cmd.cmdloop(self, *args, **kwargs)
++        except:
++            __builtin__.raw_input  = oraw_input
++            raise
++
++        __builtin__.raw_input = oraw_input
++
++        return cret
++
      def script(self):
 +        """Execute a script file in the yum shell.  The location of
 +        the script file is supplied by the :class:`cli.YumBaseCli`
@@ -3110,7 +3329,7 @@ index 999bffc..636f12c 100644
          try:
              fd = open(self.file, 'r')
          except IOError:
-@@ -90,6 +92,13 @@ class YumShell(cmd.Cmd):
+@@ -90,6 +119,13 @@ class YumShell(cmd.Cmd):
          return True
              
      def default(self, line):
@@ -3124,7 +3343,7 @@ index 999bffc..636f12c 100644
          if len(line) > 0 and line.strip()[0] == '#':
              pass
          else:
-@@ -117,9 +126,15 @@ class YumShell(cmd.Cmd):
+@@ -117,9 +153,15 @@ class YumShell(cmd.Cmd):
                  self.base.doCommands()
      
      def emptyline(self):
@@ -3140,7 +3359,7 @@ index 999bffc..636f12c 100644
          ret = cmd.Cmd.completenames(self, text, line, begidx, endidx)
          for command in self.base.yum_cli_commands:
              if command.startswith(text) and command != "shell":
-@@ -127,6 +142,11 @@ class YumShell(cmd.Cmd):
+@@ -127,6 +169,11 @@ class YumShell(cmd.Cmd):
          return ret
  
      def do_help(self, arg):
@@ -3152,7 +3371,7 @@ index 999bffc..636f12c 100644
          msg = """
      Shell specific arguments:
        config - set config options
-@@ -166,21 +186,47 @@ class YumShell(cmd.Cmd):
+@@ -166,21 +213,47 @@ class YumShell(cmd.Cmd):
          self.verbose_logger.info(msg)
          
      def do_EOF(self, line):
@@ -3200,7 +3419,7 @@ index 999bffc..636f12c 100644
          (cmd, args, line) = self.parseline(line)
          if cmd in ['list', None]:
              self.verbose_logger.log(logginglevels.INFO_2,
-@@ -210,6 +256,15 @@ class YumShell(cmd.Cmd):
+@@ -210,6 +283,15 @@ class YumShell(cmd.Cmd):
              self.do_help('transaction')
      
      def do_config(self, line):
@@ -3216,7 +3435,7 @@ index 999bffc..636f12c 100644
          (cmd, args, line) = self.parseline(line)
          # logs
          if cmd in ['debuglevel', 'errorlevel']:
-@@ -264,9 +319,23 @@ class YumShell(cmd.Cmd):
+@@ -264,9 +346,23 @@ class YumShell(cmd.Cmd):
              self.do_help('config')
  
      def do_repository(self, line):
@@ -3240,7 +3459,7 @@ index 999bffc..636f12c 100644
          (cmd, args, line) = self.parseline(line)
          if cmd in ['list', None]:
              # Munge things to run the repolist command
-@@ -338,6 +407,10 @@ class YumShell(cmd.Cmd):
+@@ -338,6 +434,10 @@ class YumShell(cmd.Cmd):
          print line
          
      def do_run(self, line):
@@ -3251,6 +3470,99 @@ index 999bffc..636f12c 100644
          if len(self.base.tsInfo) > 0:
              try:
                  (code, msgs) = self.base.buildTransaction()
+diff --git a/test/check-po-yes-no.py b/test/check-po-yes-no.py
+index e22318e..b9cb8aa 100755
+--- a/test/check-po-yes-no.py
++++ b/test/check-po-yes-no.py
+@@ -16,6 +16,8 @@ def trans(msg, default):
+         msg = msg[:-2]
+     return unicode(msg, encoding='utf-8')
+ 
++allow_plain_yn = True
++
+ for fname in glob.glob("po/*.po"):
+     next = None
+     is_this_ok  = None
+@@ -32,7 +34,8 @@ for fname in glob.glob("po/*.po"):
+         if next is not None:
+             if next == 'is_this_ok':
+                 sis_this_ok = line
+-                if line == 'msgstr ""\n' or line.find('[y/N]') != -1:
++                if line == 'msgstr ""\n' or (not allow_plain_yn and
++                                             line.find('[y/N]') != -1):
+                     is_this_ok = False
+                 else:
+                     is_this_ok = True
+@@ -62,9 +65,9 @@ for fname in glob.glob("po/*.po"):
+             next = 'n'
+     if (is_this_ok is None or
+         yes is None or
+-        y   is None or
++        (not allow_plain_yn and y   is None) or
+         no  is None or
+-        n   is None):
++        (not allow_plain_yn and n   is None)):
+         print >>sys.stderr, """\
+ ERROR: Can't find all the msg id's in %s
+ is_this_ok %s
+@@ -96,6 +99,10 @@ n          %5s: %s
+        to_utf8(is_this_ok), to_utf8(sis_this_ok),
+        to_utf8(yes), to_utf8(syes), to_utf8(y), to_utf8(sy),
+        to_utf8(no), to_utf8(sno), to_utf8(n), to_utf8(sn))
++
++    if allow_plain_yn:
++        continue
++
+     if syes[0] != sy:
+         print >>sys.stderr, """\
+ ERROR: yes/y translations don't match in: %s
+diff --git a/test/simpleobsoletestests.py b/test/simpleobsoletestests.py
+index 97a9923..70dde98 100644
+--- a/test/simpleobsoletestests.py
++++ b/test/simpleobsoletestests.py
+@@ -244,6 +244,42 @@ class SimpleObsoletesTests(OperationsTests):
+         self.assert_(res=='ok', msg)
+         self.assertResult((p.obsoletes_noarch,))
+ 
++    def testObsoletesOffPostInst1(self):
++        p = self.pkgs
++        res, msg = self.runOperation(['install', 'zsh'], [p.obsoletes_i386], [p.installed_i386])
++        self.assert_(res=='ok', msg)
++        self.assertResult((p.obsoletes_i386,))
++
++    def testObsoletesOffPostInst2(self):
++        p = self.pkgs
++        res, msg = self.runOperation(['install', 'zsh'], [p.obsoletes_i386], [p.installed_i386], {'obsoletes' : False})
++        self.assert_(res=='ok', msg)
++        self.assertResult((p.obsoletes_i386,))
++
++    def testObsoletesOffPostAvail1(self):
++        p = self.pkgs
++        res, msg = self.runOperation(['install', 'zsh-ng', 'zsh'], [], [p.obsoletes_i386, p.installed_i386])
++        self.assert_(res=='ok', msg)
++        self.assertResult((p.obsoletes_i386,))
++
++    def testObsoletesOffPostAvail2(self):
++        p = self.pkgs
++        res, msg = self.runOperation(['install', 'zsh-ng', 'zsh'], [], [p.obsoletes_i386, p.installed_i386], {'obsoletes' : False})
++        self.assert_(res=='ok', msg)
++        self.assertResult((p.obsoletes_i386,))
++
++    def testObsoletesOffPostAvail3(self):
++        p = self.pkgs
++        res, msg = self.runOperation(['install', 'zsh', 'zsh-ng'], [], [p.obsoletes_i386, p.installed_i386])
++        self.assert_(res=='ok', msg)
++        self.assertResult((p.obsoletes_i386,))
++
++    def testObsoletesOffPostAvail4(self):
++        p = self.pkgs
++        res, msg = self.runOperation(['install', 'zsh', 'zsh-ng'], [], [p.obsoletes_i386, p.installed_i386], {'obsoletes' : False})
++        self.assert_(res=='ok', msg)
++        self.assertResult((p.obsoletes_i386,))
++
+     def _MultiObsHelper(self):
+         ret = {'zsh'  : FakePackage('zsh', '1', '1', '0', 'noarch'),
+                'ksh'  : FakePackage('ksh', '1', '1', '0', 'noarch'),
 diff --git a/utils.py b/utils.py
 old mode 100644
 new mode 100755
@@ -4616,10 +4928,20 @@ index abd203f..825f745 100644
  - 3.4.1
  - umask bug fix.
 diff --git a/yum/__init__.py b/yum/__init__.py
-index 99039e0..2149d77 100644
+index 99039e0..530bfd4 100644
 --- a/yum/__init__.py
 +++ b/yum/__init__.py
-@@ -1242,13 +1242,15 @@ class YumBase(depsolve.Depsolve):
+@@ -881,7 +881,8 @@ class YumBase(depsolve.Depsolve):
+         if self._history is None:
+             pdb_path = self.conf.persistdir + "/history"
+             self._history = yum.history.YumHistory(root=self.conf.installroot,
+-                                                   db_path=pdb_path)
++                                                   db_path=pdb_path,
++                                                   releasever=self.conf.yumvar['releasever'])
+         return self._history
+     
+     # properties so they auto-create themselves with defaults
+@@ -1242,13 +1243,15 @@ class YumBase(depsolve.Depsolve):
          if None in pkgtup:
              return None
          return pkgtup
@@ -4639,7 +4961,17 @@ index 99039e0..2149d77 100644
          if pkgtup is None:
              return
          self._not_found_i[pkgtup] = YumNotFoundPackage(pkgtup)
-@@ -2939,7 +2941,7 @@ class YumBase(depsolve.Depsolve):
+@@ -1645,6 +1648,9 @@ class YumBase(depsolve.Depsolve):
+                 elif loginuid is not None:
+                     po.yumdb_info.installed_by = str(loginuid)
+ 
++                if self.conf.history_record:
++                    self.history.sync_alldb(po)
++
+         # Remove old ones after installing new ones, so we can copy values.
+         for txmbr in self.tsInfo:
+             if txmbr.output_state in TS_INSTALL_STATES:
+@@ -2939,7 +2945,7 @@ class YumBase(depsolve.Depsolve):
                  self.verbose_logger.log(logginglevels.DEBUG_2,
                      _('Adding package %s from group %s'), pkg, thisgroup.groupid)
                  try:
@@ -4648,7 +4980,7 @@ index 99039e0..2149d77 100644
                  except Errors.InstallError, e:
                      self.verbose_logger.debug(_('No package named %s available to be installed'),
                          pkg)
-@@ -3049,7 +3051,7 @@ class YumBase(depsolve.Depsolve):
+@@ -3049,7 +3055,7 @@ class YumBase(depsolve.Depsolve):
          pkgs = self.pkgSack.searchPkgTuple(pkgtup)
  
          if len(pkgs) == 0:
@@ -4657,7 +4989,7 @@ index 99039e0..2149d77 100644
              if allow_missing: #  This can happen due to excludes after .up has
                  return None   # happened.
              raise Errors.DepError, _('Package tuple %s could not be found in packagesack') % str(pkgtup)
-@@ -3071,7 +3073,7 @@ class YumBase(depsolve.Depsolve):
+@@ -3071,7 +3077,7 @@ class YumBase(depsolve.Depsolve):
  
          pkgs = self.rpmdb.searchPkgTuple(pkgtup)
          if len(pkgs) == 0:
@@ -4666,13 +4998,16 @@ index 99039e0..2149d77 100644
              raise Errors.RpmDBError, _('Package tuple %s could not be found in rpmdb') % str(pkgtup)
  
          # Dito. FIXME from getPackageObject() for len() > 1 ... :)
-@@ -3445,6 +3447,18 @@ class YumBase(depsolve.Depsolve):
+@@ -3445,6 +3451,21 @@ class YumBase(depsolve.Depsolve):
             
             """
          
 +
 +        #  This is kind of hacky, we really need a better way to do errors than
 +        # doing them directly from .install/etc. ... but this is easy. *sigh*.
++        #  We are only using this in "groupinstall" atm. ... so we don't have
++        # a long list of "blah already installed." messages when people run
++        # "groupinstall mygroup" in yum-cron etc.
 +        pkg_warn = kwargs.get('pkg_warning_level', 'flibble')
 +        def _dbg2(*args, **kwargs):
 +            self.verbose_logger.log(logginglevels.DEBUG_2, *args, **kwargs)
@@ -4685,7 +5020,7 @@ index 99039e0..2149d77 100644
          pkgs = []
          was_pattern = False
          if po:
-@@ -3600,23 +3614,23 @@ class YumBase(depsolve.Depsolve):
+@@ -3600,23 +3621,23 @@ class YumBase(depsolve.Depsolve):
                      already_obs = pkgs[0]
  
                  if already_obs:
@@ -4716,7 +5051,7 @@ index 99039e0..2149d77 100644
                      continue
  
              # make sure we don't have a name.arch of this already installed
-@@ -3630,7 +3644,7 @@ class YumBase(depsolve.Depsolve):
+@@ -3630,7 +3651,7 @@ class YumBase(depsolve.Depsolve):
                          found = True
                          break
                  if not found:
@@ -4725,7 +5060,7 @@ index 99039e0..2149d77 100644
                      txmbrs = self.update(po=po)
                      tx_return.extend(txmbrs)
                      continue
-@@ -4725,7 +4739,9 @@ class YumBase(depsolve.Depsolve):
+@@ -4725,7 +4746,9 @@ class YumBase(depsolve.Depsolve):
                      # Try installing/updating GPG key
                      self._getKeyImportMessage(info, keyurl)
                      rc = False
@@ -4736,7 +5071,7 @@ index 99039e0..2149d77 100644
                          rc = True
                          
                      # grab the .sig/.asc for the keyurl, if it exists
-@@ -4819,8 +4835,11 @@ class YumBase(depsolve.Depsolve):
+@@ -4819,8 +4842,11 @@ class YumBase(depsolve.Depsolve):
                  if not key_installed:
                      self._getKeyImportMessage(info, keyurl, keytype)
                      rc = False
@@ -4749,7 +5084,7 @@ index 99039e0..2149d77 100644
                      elif callback:
                          rc = callback({"repo": repo, "userid": info['userid'],
                                          "hexkeyid": info['hexkeyid'], "keyurl": keyurl,
-@@ -5234,7 +5253,7 @@ class YumBase(depsolve.Depsolve):
+@@ -5234,7 +5260,7 @@ class YumBase(depsolve.Depsolve):
              raise Errors.YumBaseError(_("Dependencies not solved. Will not save unresolved transaction."))
          
          if not filename:
@@ -4758,6 +5093,23 @@ index 99039e0..2149d77 100644
              fd,filename = tempfile.mkstemp(suffix='.yumtx', prefix=prefix)
              f = os.fdopen(fd, 'w')
          else:
+@@ -5292,6 +5318,16 @@ class YumBase(depsolve.Depsolve):
+         # 3+numrepos = num pkgs
+         # 3+numrepos+1 -> EOF= txmembers
+         
++        if data[0] == 'saved_tx:\n':
++            #  Old versions of yum would put "saved_tx:" at the begining and
++            # two blank lines at the end when you used:
++            # "yum -q history addon-info saved_tx".
++            if data[-1] == 'history addon-info\n':
++                # Might as well also DTRT if they hand removed the plugins line
++                data = data[1:-3]
++            else:
++                data = data[1:-2]
++
+         # rpm db ver
+         rpmv = data[0].strip()
+         if rpmv != str(self.rpmdb.simpleVersion(main_only=True)[0]):
 diff --git a/yum/callbacks.py b/yum/callbacks.py
 index 7ad25ce..a9a8e53 100644
 --- a/yum/callbacks.py
@@ -6110,6 +6462,450 @@ index bca9651..00c17ad 100644
          if i == None:
              index = self.failures
          else:
+diff --git a/yum/history.py b/yum/history.py
+index 5385bd1..c91c33a 100644
+--- a/yum/history.py
++++ b/yum/history.py
+@@ -97,9 +97,58 @@ def _setupHistorySearchSQL(patterns=None, ignore_case=False):
+     return (need_full, patterns, fields, False)
+ # ---- horrible Copy and paste from sqlitesack ----
+ 
++class _YumHistPackageYumDB:
++    """ Class to pretend to be yumdb_info for history packages. """
++
++    def __init__(self, pkg):
++        self._pkg = pkg
++
++    _valid_yumdb_keys = set(["command_line",
++                             "from_repo", "from_repo_revision",
++                             "from_repo_timestamp",
++                             "installed_by", "changed_by",
++                             "reason", "releasever"])
++    def __getattr__(self, attr):
++        """ Load yumdb attributes from the history sqlite. """
++        pkg = self._pkg
++        if attr.startswith('_'):
++            raise AttributeError, "%s has no yum attribute %s" % (pkg, attr)
++
++        if attr not in self._valid_yumdb_keys:
++            raise AttributeError, "%s has no yum attribute %s" % (pkg, attr)
++
++        val = pkg._history._load_yumdb_key(pkg, attr)
++        if False and val is None:
++            raise AttributeError, "%s has no yum attribute %s" % (pkg, attr)
++
++        if val is None:
++            return None
++
++        val = str(val) or ""
++        setattr(self, attr, val)
++
++        return val
++
++    def __contains__(self, attr):
++        #  This is faster than __iter__ and it makes things fail in a much more
++        # obvious way in weird FS corruption cases like: BZ 593436
++        x = self.get(attr)
++        return x is not None
++
++    def get(self, attr, default=None):
++        """retrieve an add'l data obj"""
++
++        try:
++            res = getattr(self, attr)
++        except AttributeError:
++            return default
++        return res
++
++
+ class YumHistoryPackage(PackageObject):
+ 
+-    def __init__(self, name, arch, epoch, version, release, checksum=None):
++    def __init__(self, name, arch, epoch, version, release, checksum=None,
++                 history=None):
+         self.name    = name
+         self.version = version
+         self.release = release
+@@ -111,21 +160,69 @@ class YumHistoryPackage(PackageObject):
+             self._checksums = [] # (type, checksum, id(0,1)
+         else:
+             chk = checksum.split(':')
+-            self._checksums = [(chk[0], chk[1], 0)] # (type, checksum, id(0,1))
++            self._checksums = [(chk[0], chk[1], 1)] # (type, checksum, id(0,1))
+         # Needed for equality comparisons in PackageObject
+         self.repoid = "<history>"
+ 
++        self._history = history
++        self.yumdb_info = _YumHistPackageYumDB(self)
++
++    _valid_rpmdb_keys = set(["buildtime", "buildhost",
++                             "license", "packager",
++                             "size", "sourcerpm", "url", "vendor",
++                             # ?
++                             "committer", "committime"])
++    def __getattr__(self, attr):
++        """ Load rpmdb attributes from the history sqlite. """
++        if attr.startswith('_'):
++            raise AttributeError, "%s has no attribute %s" % (self, attr)
++
++        if attr not in self._valid_rpmdb_keys:
++            raise AttributeError, "%s has no attribute %s" % (self, attr)
++
++        val = self._history._load_rpmdb_key(self, attr)
++        if False and val is None:
++            raise AttributeError, "%s has no attribute %s" % (self, attr)
++
++        if val is None:
++            return None
++
++        val = str(val) or ""
++        setattr(self, attr, val)
++
++        return val
++
++    def _ui_from_repo(self):
++        """ This reports the repo the package is from, we integrate YUMDB info.
++            for RPM packages so a package from "fedora" that is installed has a
++            ui_from_repo of "@fedora". Note that, esp. with the --releasever
++            option, "fedora" or "rawhide" isn't authoritive.
++            So we also check against the current releasever and if it is
++            different we also print the YUMDB releasever. This means that
++            installing from F12 fedora, while running F12, would report as
++            "@fedora/13". """
++        if 'from_repo' in self.yumdb_info:
++            self._history.releasever
++            end = ''
++            if (self._history.releasever is not None and
++                'releasever' in self.yumdb_info and
++                self.yumdb_info.releasever != self._history.releasever):
++                end = '/' + self.yumdb_info.releasever
++            return '@' + self.yumdb_info.from_repo + end
++        return self.repoid
++    ui_from_repo = property(fget=lambda self: self._ui_from_repo())
++
++
+ class YumHistoryPackageState(YumHistoryPackage):
+-    def __init__(self, name,arch, epoch,version,release, state, checksum=None):
++    def __init__(self, name,arch, epoch,version,release, state, checksum=None,
++                 history=None):
+         YumHistoryPackage.__init__(self, name,arch, epoch,version,release,
+-                                   checksum)
++                                   checksum, history)
+         self.done  = None
+         self.state = state
+ 
+-        self.repoid = '<history>'
+-
+ 
+-class YumHistoryRpmdbProblem(PackageObject):
++class YumHistoryRpmdbProblem:
+     """ Class representing an rpmdb problem that existed at the time of the
+         transaction. """
+ 
+@@ -328,7 +425,8 @@ class YumMergedHistoryTransaction(YumHistoryTransaction):
+     @staticmethod
+     def _conv_pkg_state(pkg, state):
+         npkg = YumHistoryPackageState(pkg.name, pkg.arch,
+-                                      pkg.epoch,pkg.version,pkg.release, state)
++                                      pkg.epoch,pkg.version,pkg.release, state,
++                                      pkg._history)
+         npkg._checksums = pkg._checksums
+         npkg.done = pkg.done
+         if _sttxt2stcode[npkg.state] in TS_INSTALL_STATES:
+@@ -557,7 +655,7 @@ class YumMergedHistoryTransaction(YumHistoryTransaction):
+ class YumHistory:
+     """ API for accessing the history sqlite data. """
+ 
+-    def __init__(self, root='/', db_path=_history_dir):
++    def __init__(self, root='/', db_path=_history_dir, releasever=None):
+         self._conn = None
+         
+         self.conf = yum.misc.GenericHolder()
+@@ -568,6 +666,8 @@ class YumHistory:
+         self.conf.writable = False
+         self.conf.readable = True
+ 
++        self.releasever = releasever
++
+         if not os.path.exists(self.conf.db_path):
+             try:
+                 os.makedirs(self.conf.db_path)
+@@ -644,7 +744,7 @@ class YumHistory:
+             self._conn.close()
+             self._conn = None
+ 
+-    def _pkgtup2pid(self, pkgtup, checksum=None):
++    def _pkgtup2pid(self, pkgtup, checksum=None, create=True):
+         cur = self._get_cursor()
+         executeSQL(cur, """SELECT pkgtupid, checksum FROM pkgtups
+                            WHERE name=? AND arch=? AND
+@@ -659,6 +759,9 @@ class YumHistory:
+             if checksum == sql_checksum:
+                 return sql_pkgtupid
+         
++        if not create:
++            return None
++
+         (n,a,e,v,r) = pkgtup
+         (n,a,e,v,r) = (to_unicode(n),to_unicode(a),
+                        to_unicode(e),to_unicode(v),to_unicode(r))
+@@ -674,23 +777,28 @@ class YumHistory:
+                                 (name, arch, epoch, version, release)
+                                 VALUES (?, ?, ?, ?, ?)""", (n,a,e,v,r))
+         return cur.lastrowid
+-    def _apkg2pid(self, po):
++    def _apkg2pid(self, po, create=True):
+         csum = po.returnIdSum()
+         if csum is not None:
+             csum = "%s:%s" % (str(csum[0]), str(csum[1]))
+-        return self._pkgtup2pid(po.pkgtup, csum)
+-    def _ipkg2pid(self, po):
++        return self._pkgtup2pid(po.pkgtup, csum, create)
++    def _ipkg2pid(self, po, create=True):
+         csum = None
+         yumdb = po.yumdb_info
+         if 'checksum_type' in yumdb and 'checksum_data' in yumdb:
+             csum = "%s:%s" % (yumdb.checksum_type, yumdb.checksum_data)
+-        return self._pkgtup2pid(po.pkgtup, csum)
+-    def pkg2pid(self, po):
++        return self._pkgtup2pid(po.pkgtup, csum, create)
++    def _hpkg2pid(self, po, create=False):
++        return self._apkg2pid(po, create)
++
++    def pkg2pid(self, po, create=True):
+         if isinstance(po, YumInstalledPackage):
+-            return self._ipkg2pid(po)
++            return self._ipkg2pid(po, create)
+         if isinstance(po, YumAvailablePackage):
+-            return self._apkg2pid(po)
+-        return self._pkgtup2pid(po.pkgtup, None)
++            return self._apkg2pid(po, create)
++        if isinstance(po, YumHistoryPackage):
++            return self._hpkg2pid(po, create)
++        return self._pkgtup2pid(po.pkgtup, None, create)
+ 
+     @staticmethod
+     def txmbr2state(txmbr):
+@@ -984,7 +1092,8 @@ class YumHistory:
+                       ORDER BY name ASC, epoch ASC""", (tid,))
+         ret = []
+         for row in cur:
+-            obj = YumHistoryPackage(row[0],row[1],row[2],row[3],row[4], row[5])
++            obj = YumHistoryPackage(row[0],row[1],row[2],row[3],row[4], row[5],
++                                    history=self)
+             ret.append(obj)
+         return ret
+     def _old_data_pkgs(self, tid):
+@@ -998,7 +1107,7 @@ class YumHistory:
+         ret = []
+         for row in cur:
+             obj = YumHistoryPackageState(row[0],row[1],row[2],row[3],row[4],
+-                                         row[7], row[5])
++                                         row[7], row[5], history=self)
+             obj.done     = row[6] == 'TRUE'
+             obj.state_installed = None
+             if _sttxt2stcode[obj.state] in TS_INSTALL_STATES:
+@@ -1018,7 +1127,8 @@ class YumHistory:
+                       ORDER BY name ASC, epoch ASC""", (tid,))
+         ret = []
+         for row in cur:
+-            obj = YumHistoryPackage(row[0],row[1],row[2],row[3],row[4], row[5])
++            obj = YumHistoryPackage(row[0],row[1],row[2],row[3],row[4], row[5],
++                                    history=self)
+             ret.append(obj)
+         return ret
+     def _old_prob_pkgs(self, rpid):
+@@ -1032,7 +1142,8 @@ class YumHistory:
+                       ORDER BY name ASC, epoch ASC""", (rpid,))
+         ret = []
+         for row in cur:
+-            obj = YumHistoryPackage(row[0],row[1],row[2],row[3],row[4], row[5])
++            obj = YumHistoryPackage(row[0],row[1],row[2],row[3],row[4], row[5],
++                                    history=self)
+             obj.main = row[6] == 'TRUE'
+             ret.append(obj)
+         return ret
+@@ -1151,6 +1262,123 @@ class YumHistory:
+         assert len(ret) == 1
+         return ret[0]
+ 
++    def _load_anydb_key(self, pkg, db, attr):
++        cur = self._get_cursor()
++        if cur is None or not self._update_db_file_3():
++            return None
++
++        pid = self.pkg2pid(pkg, create=False)
++        if pid is None:
++            return None
++
++        sql = """SELECT %(db)sdb_val FROM pkg_%(db)sdb
++                  WHERE pkgtupid=? and %(db)sdb_key=? """ % {'db' : db}
++        executeSQL(cur, sql, (pid, attr))
++        for row in cur:
++            return row[0]
++
++        return None
++
++    def _load_rpmdb_key(self, pkg, attr):
++        return self._load_anydb_key(pkg, "rpm", attr)
++    def _load_yumdb_key(self, pkg, attr):
++        return self._load_anydb_key(pkg, "yum", attr)
++
++    def _save_anydb_key(self, pkg, db, attr, val):
++        cur = self._get_cursor()
++        if cur is None or not self._update_db_file_3():
++            return None
++
++        pid = self.pkg2pid(pkg, create=False)
++        if pid is None:
++            return None
++
++        sql = """INSERT INTO pkg_%(db)sdb (pkgtupid, %(db)sdb_key, %(db)sdb_val)
++                        VALUES (?, ?, ?)""" % {'db' : db}
++        executeSQL(cur, sql, (pid, attr, val))
++        for row in cur:
++            return row[0]
++
++        return None
++
++    def _save_rpmdb_key(self, pkg, attr, val):
++        return self._save_anydb_key(pkg, "rpm", attr, val)
++    def _save_yumdb_key(self, pkg, attr, val):
++        return self._save_anydb_key(pkg, "yum", attr, val)
++
++    def _save_rpmdb(self, ipkg):
++        """ Save all the data for rpmdb for this installed pkg, assumes
++            there is no data currently. """
++        for attr in YumHistoryPackage._valid_rpmdb_keys:
++            val = getattr(ipkg, attr, None)
++            if val is None:
++                continue
++            self._save_anydb_key(ipkg, "rpm", attr, val)
++
++    def _save_yumdb(self, ipkg):
++        """ Save all the data for yumdb for this installed pkg, assumes
++            there is no data currently. """
++        for attr in _YumHistPackageYumDB._valid_yumdb_keys:
++            val = ipkg.yumdb_info.get(attr)
++            if val is None:
++                continue
++            self._save_anydb_key(ipkg, "yum", attr, val)
++
++    def _wipe_anydb(self, pkg, db):
++        """ Delete all the data for rpmdb/yumdb for this installed pkg. """
++        cur = self._get_cursor()
++        if cur is None or not self._update_db_file_3():
++            return False
++
++        pid = self.pkg2pid(pkg, create=False)
++        if pid is None:
++            return False
++
++        sql = """DELETE FROM pkg_%(db)sdb WHERE pkgtupid=?""" % {'db' : db}
++        executeSQL(cur, sql, (pid,))
++
++        return True
++
++    def sync_alldb(self, ipkg):
++        """ Sync. all the data for rpmdb/yumdb for this installed pkg. """
++        if not self._wipe_anydb(ipkg, "rpm"):
++            return False
++        self._wipe_anydb(ipkg, "yum")
++        if not self._save_rpmdb(ipkg):
++            return False
++        self._save_yumdb(ipkg)
++        self._commit()
++        return True
++
++    def _pkg_stats(self):
++        """ Some stats about packages in the DB. """
++
++        ret = {'nevrac' : 0,
++               'nevra'  : 0,
++               'nevr'   : 0,
++               'na'     : 0,
++               'rpmdb'  : 0,
++               'yumdb'  : 0,
++               }
++        cur = self._get_cursor()
++        if cur is None or not self._update_db_file_3():
++            return False
++
++        data = (('nevrac', "COUNT(*)",                      "pkgtups"),
++                ('na',     "COUNT(DISTINCT(name || arch))", "pkgtups"),
++                ('nevra',"COUNT(DISTINCT(name||version||epoch||release||arch))",
++                 "pkgtups"),
++                ('nevr',   "COUNT(DISTINCT(name||version||epoch||release))",
++                 "pkgtups"),
++                ('rpmdb',  "COUNT(DISTINCT(pkgtupid))", "pkg_rpmdb"),
++                ('yumdb',  "COUNT(DISTINCT(pkgtupid))", "pkg_yumdb"))
++
++        for key, bsql, esql in data:
++            executeSQL(cur, "SELECT %s FROM %s" % (bsql, esql))
++            for row in cur:
++                ret[key] = row[0]
++        return ret
++
+     def _yieldSQLDataList(self, patterns, fields, ignore_case):
+         """Yields all the package data for the given params. """
+ 
+@@ -1220,6 +1448,47 @@ class YumHistory:
+             tids.add(row[0])
+         return tids
+ 
++    _update_ops_3 = ['''\
++\
++ CREATE TABLE pkg_rpmdb (
++     pkgtupid INTEGER NOT NULL REFERENCES pkgtups,
++     rpmdb_key TEXT NOT NULL,
++     rpmdb_val TEXT NOT NULL);
++''', '''\
++ CREATE INDEX i_pkgkey_rpmdb ON pkg_rpmdb (pkgtupid, rpmdb_key);
++''', '''\
++ CREATE TABLE pkg_yumdb (
++     pkgtupid INTEGER NOT NULL REFERENCES pkgtups,
++     yumdb_key TEXT NOT NULL,
++     yumdb_val TEXT NOT NULL);
++''', '''\
++ CREATE INDEX i_pkgkey_yumdb ON pkg_yumdb (pkgtupid, yumdb_key);
++''']
++
++    def _update_db_file_3(self):
++        """ Update to version 3 of history, rpmdb/yumdb data. """
++        if not self._update_db_file_2():
++            return False
++
++        if hasattr(self, '_cached_updated_3'):
++            return self._cached_updated_3
++
++        cur = self._get_cursor()
++        if cur is None:
++            return False
++
++        executeSQL(cur, "PRAGMA table_info(pkg_yumdb)")
++        #  If we get anything, we're fine. There might be a better way of
++        # saying "anything" but this works.
++        for ob in cur:
++            break
++        else:
++            for op in self._update_ops_3:
++                cur.execute(op)
++            self._commit()
++        self._cached_updated_3 = True
++        return True
++
+     _update_ops_2 = ['''\
+ \
+  CREATE TABLE trans_skip_pkgs (
+@@ -1374,6 +1643,8 @@ class YumHistory:
+             cur.execute(op)
+         for op in self._update_ops_2:
+             cur.execute(op)
++        for op in self._update_ops_3:
++            cur.execute(op)
+         self._commit()
+ 
+ # Pasted from sqlitesack
 diff --git a/yum/misc.py b/yum/misc.py
 index 2f6ddfe..37c572b 100644
 --- a/yum/misc.py
@@ -6136,10 +6932,25 @@ index 2f6ddfe..37c572b 100644
  def _getloginuid():
      """ Get the audit-uid/login-uid, if available. None is returned if there
 diff --git a/yum/packages.py b/yum/packages.py
-index 5ef9951..79c15db 100644
+index 5ef9951..f72c068 100644
 --- a/yum/packages.py
 +++ b/yum/packages.py
-@@ -1083,7 +1083,7 @@ class YumAvailablePackage(PackageObject, RpmBase):
+@@ -271,6 +271,14 @@ class PackageObject(object):
+         return out
+     ui_nevra = property(fget=lambda self: self._ui_nevra())
+ 
++    def _ui_evr(self):
++        if self.epoch == '0':
++            out = '%s-%s' % (self.version, self.release)
++        else:
++            out = '%s:%s-%s' % (self.epoch, self.version, self.release)
++        return out
++    ui_evr = property(fget=lambda self: self._ui_evr())
++
+     def __str__(self):
+         return self.ui_envra
+ 
+@@ -1083,7 +1091,7 @@ class YumAvailablePackage(PackageObject, RpmBase):
           misc.to_unicode(misc.to_xml(self.summary)), 
           misc.to_unicode(misc.to_xml(self.description)), 
           packager, url, self.filetime, 
@@ -6148,7 +6959,7 @@ index 5ef9951..79c15db 100644
          
          msg += self._return_remote_location()
          return msg
-@@ -1133,7 +1133,7 @@ class YumAvailablePackage(PackageObject, RpmBase):
+@@ -1133,7 +1141,7 @@ class YumAvailablePackage(PackageObject, RpmBase):
          msg = ""
          mylist = getattr(self, pcotype)
          if mylist: msg = "\n    <rpm:%s>\n" % pcotype
@@ -6157,7 +6968,7 @@ index 5ef9951..79c15db 100644
              pcostring = '''      <rpm:entry name="%s"''' % misc.to_xml(name, attrib=True)
              if flags:
                  pcostring += ''' flags="%s"''' % misc.to_xml(flags, attrib=True)
-@@ -1161,11 +1161,11 @@ class YumAvailablePackage(PackageObject, RpmBase):
+@@ -1161,11 +1169,11 @@ class YumAvailablePackage(PackageObject, RpmBase):
              dirs = self.returnFileEntries('dir', primary_only=True)
              ghosts = self.returnFileEntries('ghost', primary_only=True)
                  
@@ -6172,7 +6983,7 @@ index 5ef9951..79c15db 100644
              msg += """    <file type="ghost">%s</file>\n""" % misc.to_xml(fn)
          
          return msg
-@@ -1194,8 +1194,8 @@ class YumAvailablePackage(PackageObject, RpmBase):
+@@ -1194,8 +1202,8 @@ class YumAvailablePackage(PackageObject, RpmBase):
                          continue
                      newlist.append(i)
                  mylist = newlist
@@ -6183,7 +6994,7 @@ index 5ef9951..79c15db 100644
              if name.startswith('rpmlib('):
                  continue
              # this drops out requires that the pkg provides for itself.
-@@ -1217,13 +1217,16 @@ class YumAvailablePackage(PackageObject, RpmBase):
+@@ -1217,13 +1225,16 @@ class YumAvailablePackage(PackageObject, RpmBase):
                      prcostring += ''' ver="%s"''' % misc.to_xml(v, attrib=True)
                  if r:
                      prcostring += ''' rel="%s"''' % misc.to_xml(r, attrib=True)
@@ -6202,7 +7013,7 @@ index 5ef9951..79c15db 100644
          return msg
  
      def _dump_changelog(self, clog_limit):
-@@ -1299,7 +1302,8 @@ class YumHeaderPackage(YumAvailablePackage):
+@@ -1299,7 +1310,8 @@ class YumHeaderPackage(YumAvailablePackage):
          self.pkgid = self.hdr[rpm.RPMTAG_SHA1HEADER]
          if not self.pkgid:
              self.pkgid = "%s.%s" %(self.hdr['name'], self.hdr['buildtime'])
@@ -6304,7 +7115,7 @@ index 8a6f6f3..19193ad 100644
              prco_set = (_share_data(ob['name']), _share_data(ob['flags']),
                          (_share_data(ob['epoch']),
 diff --git a/yumcommands.py b/yumcommands.py
-index 4dcbea7..3a985c3 100644
+index 4dcbea7..d9c70f3 100644
 --- a/yumcommands.py
 +++ b/yumcommands.py
 @@ -43,16 +43,22 @@ def _err_mini_usage(base, basecmd):
@@ -7968,9 +8779,40 @@ index 4dcbea7..3a985c3 100644
          return _("Display, or use, the transaction history")
  
      def _hcmd_redo(self, base, extcmds):
-@@ -1427,6 +2426,14 @@ class HistoryCommand(YumCommand):
+@@ -1426,12 +2425,52 @@ class HistoryCommand(YumCommand):
+     def _hcmd_new(self, base, extcmds):
          base.history._create_db_file()
  
++    def _hcmd_stats(self, base, extcmds):
++        print "File        :", base.history._db_file
++        num = os.stat(base.history._db_file).st_size
++        print "Size        :", locale.format("%d", num, True)
++        counts = base.history._pkg_stats()
++        trans_1 = base.history.old("1")[0]
++        trans_N = base.history.last()
++        print _("Transactions:"), trans_N.tid
++        print _("Begin time  :"), time.ctime(trans_1.beg_timestamp)
++        print _("End time    :"), time.ctime(trans_N.end_timestamp)
++        print _("Counts      :")
++        print _("  NEVRAC :"), locale.format("%6d", counts['nevrac'], True)
++        print _("  NEVRA  :"), locale.format("%6d", counts['nevra'],  True)
++        print _("  NA     :"), locale.format("%6d", counts['na'],     True)
++        print _("  NEVR   :"), locale.format("%6d", counts['nevr'],   True)
++        print _("  rpm DB :"), locale.format("%6d", counts['rpmdb'],  True)
++        print _("  yum DB :"), locale.format("%6d", counts['yumdb'],  True)
++
++    def _hcmd_sync(self, base, extcmds):
++        extcmds = extcmds[1:]
++        if not extcmds:
++            extcmds = None
++        for ipkg in sorted(base.rpmdb.returnPackages(patterns=extcmds)):
++            if base.history.pkg2pid(ipkg, create=False) is None:
++                continue
++
++            print "Syncing rpm/yum DB data for:", ipkg, "...",
++            base.history.sync_alldb(ipkg)
++            print "Done."
++
      def doCheck(self, base, basecmd, extcmds):
 +        """Verify that conditions are met so that this command can
 +        run.  The exact conditions checked will vary depending on the
@@ -7983,7 +8825,15 @@ index 4dcbea7..3a985c3 100644
          cmds = ('list', 'info', 'summary', 'repeat', 'redo', 'undo', 'new',
                  'rollback',
                  'addon', 'addon-info',
-@@ -1444,6 +2451,19 @@ class HistoryCommand(YumCommand):
++                'stats', 'statistics', 'sync', 'synchronize'
+                 'pkg', 'pkgs', 'pkg-list', 'pkgs-list',
+-                'package', 'package-list', 'packages', 'packages-list')
++                'package', 'package-list', 'packages', 'packages-list',
++                'pkg-info', 'pkgs-info', 'package-info', 'packages-info')
+         if extcmds and extcmds[0] not in cmds:
+             base.logger.critical(_('Invalid history sub-command, use: %s.'),
+                                  ", ".join(cmds))
+@@ -1444,6 +2483,19 @@ class HistoryCommand(YumCommand):
              raise cli.CliError
  
      def doCommand(self, base, basecmd, extcmds):
@@ -8003,7 +8853,19 @@ index 4dcbea7..3a985c3 100644
          vcmd = 'list'
          if extcmds:
              vcmd = extcmds[0]
-@@ -1474,6 +2494,14 @@ class HistoryCommand(YumCommand):
+@@ -1468,12 +2520,26 @@ class HistoryCommand(YumCommand):
+             ret = self._hcmd_rollback(base, extcmds)
+         elif vcmd == 'new':
+             ret = self._hcmd_new(base, extcmds)
++        elif vcmd in ('stats', 'statistics'):
++            ret = self._hcmd_stats(base, extcmds)
++        elif vcmd in ('sync', 'synchronize'):
++            ret = self._hcmd_sync(base, extcmds)
++        elif vcmd in ('pkg-info', 'pkgs-info', 'package-info', 'packages-info'):
++            ret = base.historyPackageInfoCmd(extcmds)
+ 
+         if ret is None:
+             return 0, ['history %s' % (vcmd,)]
          return ret
  
      def needTs(self, base, basecmd, extcmds):
@@ -8018,7 +8880,7 @@ index 4dcbea7..3a985c3 100644
          vcmd = 'list'
          if extcmds:
              vcmd = extcmds[0]
-@@ -1481,16 +2509,46 @@ class HistoryCommand(YumCommand):
+@@ -1481,16 +2547,46 @@ class HistoryCommand(YumCommand):
  
  
  class CheckRpmdbCommand(YumCommand):
@@ -8065,7 +8927,7 @@ index 4dcbea7..3a985c3 100644
          chkcmd = 'all'
          if extcmds:
              chkcmd = extcmds
-@@ -1505,19 +2563,57 @@ class CheckRpmdbCommand(YumCommand):
+@@ -1505,19 +2601,57 @@ class CheckRpmdbCommand(YumCommand):
          return rc, ['%s %s' % (basecmd, chkcmd)]
  
      def needTs(self, base, basecmd, extcmds):
@@ -8123,7 +8985,7 @@ index 4dcbea7..3a985c3 100644
          if not extcmds:
              base.logger.critical(_("No saved transaction file specified."))
              raise cli.CliError
-@@ -1533,5 +2629,13 @@ class LoadTransactionCommand(YumCommand):
+@@ -1533,5 +2667,13 @@ class LoadTransactionCommand(YumCommand):
  
  
      def needTs(self, base, basecmd, extcmds):
diff --git a/yum.spec b/yum.spec
index a950fd6..324a0c5 100644
--- a/yum.spec
+++ b/yum.spec
@@ -17,7 +17,7 @@
 Summary: RPM package installer/updater/manager
 Name: yum
 Version: 3.4.3
-Release: 5%{?dist}
+Release: 6%{?dist}
 License: GPLv2+
 Group: System Environment/Base
 Source0: http://yum.baseurl.org/download/3.4/%{name}-%{version}.tar.gz
@@ -310,6 +310,13 @@ exit 0
 %endif
 
 %changelog
+* Fri Aug  5 2011 James Antill <james at fedoraproject.org> - 3.4.3-6
+- update to latest HEAD
+- Add new yum DB data.
+- Add hack to workaround broken python readline in yum shell.
+- Make "yum -q history addon-info last saved_tx" valid input for load-ts.
+- Add "history packages-info/stats/sync" sub-commnands.
+
 * Fri Jul 29 2011 James Antill <james at fedoraproject.org> - 3.4.3-5
 - update to latest HEAD
 - Lots of really minor changes. Docs. and yum-cron mainly.


More information about the scm-commits mailing list