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