Hi, I put together a script called diff-comps.py that may be a good fit for yum-utils. It takes in two comps.xml files, creates Comps objects, and then prints a list of the differences. I've attached the script as well as sample output.
You will see that the output is intended to be human-readable. This isn't meant to replace the standard diff/patch tools. Instead, the goal is to print the differences in such a way that it's easy to sanity-check and easy to understand the differences. Please let me know if you have any thoughts or suggestions. Cheers -- Dennis
$ ./diff-comps.py ../comps/comps-f12.xml.in ../comps/comps-f13.xml.in CATEGORY_ADDED: content CATEGORY_CHANGED: development ADDED(groups): openoffice.org-development REMOVED(groups): books GROUP_ADDED: dogtag GROUP_ADDED: openoffice.org-development GROUP_CHANGED: base ADDED(default_packages): cifs-utils ADDED(default_packages): gnupg2 ADDED(default_packages): sssd ADDED(default_packages): yum-presto REMOVED(default_packages): gnupg ADDED(mandatory_packages): dbus ADDED(optional_packages): gnupg2-smime ADDED(optional_packages): yum-langpacks REMOVED(optional_packages): gnupg2 GROUP_CHANGED: base-x REMOVED(default_packages): bitmap-fonts ADDED(optional_packages): bitmap-console-fonts ADDED(optional_packages): bitmap-fangsongti-fonts ADDED(optional_packages): bitmap-fixed-fonts ADDED(optional_packages): bitmap-lucida-typewriter-fonts ADDED(optional_packages): ucs-miscfixed-fonts GROUP_CHANGED: chinese-support ADDED(conditional_packages): ibus-pinyin-open-phrase REMOVED(conditional_packages): poppler-data ADDED(default_packages): cjkuni-ukai-fonts ADDED(default_packages): cjkuni-uming-fonts ADDED(default_packages): wqy-zenhei-fonts REMOVED(mandatory_packages): cjkuni-ukai-fonts REMOVED(mandatory_packages): cjkuni-uming-fonts ADDED(optional_packages): wqy-microhei-fonts REMOVED(optional_packages): wqy-zenhei-fonts GROUP_CHANGED: clustering REMOVED(default_packages): system-config-cluster GROUP_CHANGED: development-libs ADDED(optional_packages): poco-devel ADDED(optional_packages): poco-doc GROUP_CHANGED: development-tools REMOVED(optional_packages): bazaar GROUP_CHANGED: eclipse ADDED(default_packages): eclipse-collabnet-merge ADDED(optional_packages): eclipse-dltk-ruby ADDED(optional_packages): eclipse-dltk-tcl GROUP_CHANGED: editors REMOVED(optional_packages): virtaal GROUP_CHANGED: electronic-lab ADDED(default_packages): covered ADDED(default_packages): dia-CMOS ADDED(default_packages): dia-Digital ADDED(default_packages): dia-electric2 ADDED(default_packages): dia-electronic ADDED(default_packages): geda-gaf REMOVED(default_packages): geda-docs REMOVED(default_packages): geda-examples REMOVED(default_packages): geda-gattrib REMOVED(default_packages): geda-gnetlist REMOVED(default_packages): geda-gschem REMOVED(default_packages): geda-gsymcheck REMOVED(default_packages): geda-symbols REMOVED(default_packages): geda-utils GROUP_CHANGED: ethiopic-support ADDED(default_packages): sil-abyssinica-fonts REMOVED(default_packages): abyssinica-fonts GROUP_CHANGED: font-design ADDED(default_packages): fontaine ADDED(optional_packages): woff ADDED(optional_packages): woffTools GROUP_CHANGED: fonts ADDED(default_packages): lohit-devanagari-fonts ADDED(default_packages): paratype-pt-sans-fonts ADDED(default_packages): sil-abyssinica-fonts ADDED(default_packages): wqy-zenhei-fonts REMOVED(default_packages): abyssinica-fonts REMOVED(default_packages): cjkuni-uming-fonts REMOVED(default_packages): lohit-hindi-fonts REMOVED(default_packages): lohit-maithili-fonts REMOVED(default_packages): lohit-marathi-fonts ADDED(optional_packages): cjkuni-uming-fonts ADDED(optional_packages): gfs-goschen-fonts ADDED(optional_packages): saab-fonts ADDED(optional_packages): sil-padauk-fonts ADDED(optional_packages): vemana2000-fonts ADDED(optional_packages): wqy-microhei-fonts REMOVED(optional_packages): lohit-kashmiri-fonts REMOVED(optional_packages): lohit-konkani-fonts REMOVED(optional_packages): lohit-nepali-fonts REMOVED(optional_packages): lohit-sindhi-fonts REMOVED(optional_packages): paratype-pt-sans-fonts REMOVED(optional_packages): wqy-zenhei-fonts GROUP_CHANGED: games ADDED(optional_packages): mine_detector GROUP_CHANGED: gnome-desktop ADDED(default_packages): caribou ADDED(default_packages): deja-dup ADDED(default_packages): gnome-applets ADDED(default_packages): gnome-color-manager ADDED(default_packages): gnome-games ADDED(default_packages): preupgrade ADDED(default_packages): shotwell ADDED(default_packages): simple-scan REMOVED(default_packages): dasher REMOVED(default_packages): evince-djvu REMOVED(default_packages): gthumb REMOVED(default_packages): yum-presto ADDED(mandatory_packages): polkit-gnome REMOVED(mandatory_packages): gnome-applets ADDED(optional_packages): control-center-extra ADDED(optional_packages): dasher ADDED(optional_packages): evince-djvu ADDED(optional_packages): gthumb REMOVED(optional_packages): shotwell GROUP_CHANGED: gnome-software-development ADDED(optional_packages): cairomm-doc REMOVED(optional_packages): eel2-devel GROUP_CHANGED: graphical-internet ADDED(default_packages): transmission-gtk REMOVED(default_packages): ekiga REMOVED(default_packages): transmission ADDED(optional_packages): ekiga ADDED(optional_packages): transmission-qt REMOVED(optional_packages): gift-gnutella REMOVED(optional_packages): gift-openft GROUP_CHANGED: graphics ADDED(default_packages): shotwell REMOVED(default_packages): f-spot ADDED(optional_packages): f-spot REMOVED(optional_packages): digikam-doc GROUP_CHANGED: greek-support ADDED(optional_packages): gfs-goschen-fonts GROUP_CHANGED: hardware-support ADDED(default_packages): ar9170-firmware ADDED(default_packages): iwl5150-firmware ADDED(default_packages): usb_modeswitch GROUP_CHANGED: haskell REMOVED(default_packages): ghc-time-devel REMOVED(default_packages): ghc-utf8-string-devel GROUP_CHANGED: hindi-support ADDED(mandatory_packages): lohit-devanagari-fonts REMOVED(mandatory_packages): lohit-hindi-fonts GROUP_CHANGED: input-methods ADDED(default_packages): ibus-pinyin-open-phrase ADDED(optional_packages): ibus-xkbc GROUP_CHANGED: japanese-support REMOVED(conditional_packages): poppler-data GROUP_CHANGED: java-development REMOVED(optional_packages): jlint GROUP_CHANGED: kashmiri-support ADDED(mandatory_packages): lohit-devanagari-fonts REMOVED(mandatory_packages): lohit-kashmiri-fonts GROUP_CHANGED: kde-desktop REMOVED(conditional_packages): xine-lib-pulseaudio ADDED(default_packages): kbluetooth ADDED(default_packages): kcm_touchpad ADDED(default_packages): knetworkmanager ADDED(default_packages): pinentry-qt4 ADDED(default_packages): qtcurve-gtk2 ADDED(default_packages): qtcurve-kde4 REMOVED(default_packages): NetworkManager-gnome REMOVED(default_packages): kdebluetooth REMOVED(default_packages): pinentry-qt ADDED(optional_packages): kde-plasma-smooth-tasks ADDED(optional_packages): transmission-qt REMOVED(optional_packages): apollon REMOVED(optional_packages): kcm_touchpad REMOVED(optional_packages): kde-plasma-stasks REMOVED(optional_packages): knetworkmanager REMOVED(optional_packages): qtcurve-kde4 REMOVED(optional_packages): scim-qtimm GROUP_CHANGED: konkani-support ADDED(mandatory_packages): lohit-devanagari-fonts REMOVED(mandatory_packages): lohit-konkani-fonts GROUP_CHANGED: korean-support REMOVED(conditional_packages): poppler-data GROUP_CHANGED: legacy-fonts REMOVED(default_packages): bitmap-fonts ADDED(optional_packages): bitmap-console-fonts ADDED(optional_packages): bitmap-fangsongti-fonts ADDED(optional_packages): bitmap-fixed-fonts ADDED(optional_packages): bitmap-lucida-typewriter-fonts ADDED(optional_packages): ucs-miscfixed-fonts REMOVED(optional_packages): bitmap-cjk-fonts GROUP_CHANGED: lxde-desktop REMOVED(mandatory_packages): lxde-settings-daemon GROUP_CHANGED: maithili-support ADDED(mandatory_packages): lohit-devanagari-fonts REMOVED(mandatory_packages): lohit-maithili-fonts GROUP_CHANGED: marathi-support ADDED(mandatory_packages): lohit-devanagari-fonts REMOVED(mandatory_packages): lohit-marathi-fonts GROUP_CHANGED: nepali-support ADDED(mandatory_packages): lohit-devanagari-fonts REMOVED(mandatory_packages): lohit-nepali-fonts GROUP_CHANGED: office ADDED(optional_packages): gfa ADDED(optional_packages): openoffice.org-ogltrans ADDED(optional_packages): openoffice.org-presentation-minimizer ADDED(optional_packages): openoffice.org-report-builder ADDED(optional_packages): openoffice.org-wiki-publisher REMOVED(optional_packages): openoffice.org-pyuno REMOVED(optional_packages): openoffice.org-testtools GROUP_CHANGED: printing ADDED(default_packages): paps ADDED(mandatory_packages): ghostscript-cups GROUP_CHANGED: romanian-support ADDED(mandatory_packages): terminus-console-fonts ADDED(optional_packages): terminus-fonts GROUP_CHANGED: russian-support REMOVED(conditional_packages): poppler-data GROUP_CHANGED: sindhi-support ADDED(mandatory_packages): lohit-devanagari-fonts REMOVED(mandatory_packages): lohit-sindhi-fonts GROUP_CHANGED: smb-server ADDED(default_packages): cifs-utils GROUP_CHANGED: sound-and-video REMOVED(conditional_packages): xine-lib-pulseaudio ADDED(optional_packages): gtk-v4l REMOVED(optional_packages): bmpx GROUP_CHANGED: sql-server ADDED(default_packages): PyGreSQL REMOVED(default_packages): postgresql-python ADDED(optional_packages): tcl-pgtcl REMOVED(optional_packages): postgresql-tcl GROUP_CHANGED: system-tools ADDED(default_packages): cifs-utils ADDED(default_packages): openswan REMOVED(default_packages): ipsec-tools ADDED(optional_packages): ipsec-tools GROUP_CHANGED: thai-support REMOVED(conditional_packages): poppler-data GROUP_CHANGED: ukrainian-support REMOVED(conditional_packages): poppler-data GROUP_CHANGED: xfce-desktop ADDED(default_packages): polkit-gnome REMOVED(default_packages): PolicyKit-gnome
#!/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 2008 red hat, inc import sys import yum.comps from optparse import OptionParser class Diff(object): '''Base class for Diff objects''' def added(self): '''return the list of items added''' raise NotImplemented def removed(self): '''return the list of items removed''' raise NotImplemented def changed(self): '''return the list of items changed''' raise NotImplemented def unchanged(self): '''return the list of items unchanged''' raise NotImplemented class StrDiff(Diff): '''Class to represent the difference between two strings''' def __init__(self, string_a, string_b): '''initialize class''' super(StrDiff, self).__init__() self.string_a = str(string_a) self.string_b = str(string_b) def changed(self): '''return the two strings if they differ''' if self.string_a != self.string_b: return (self.string_a, self.string_b) return () def unchanged(self): '''return the string if they're identical''' if self.string_a == self.string_b: return self.string_a return None def isdiff(self): '''return True if the strings are different''' return self.changed() class ListDiff(Diff): '''Class to represent the difference between two lists''' def __init__(self, list_a, list_b): '''initialize class''' super(ListDiff, self).__init__() assert list_a != None assert list_b != None self.list_a = list(list_a) self.list_a.sort() self.list_b = list(list_b) self.list_b.sort() @staticmethod def _added(list_a, list_b): '''private method to get list of changed items''' keys = {} added = [] for item in list_a: keys.setdefault(item, 0) keys[item] += 1 for item in list_b: if keys.get(item, 0) > 0: keys[item] -= 1 else: added.append(item) return sorted(added) def added(self): '''return list of added items''' return ListDiff._added(self.list_a, self.list_b) def removed(self): '''return list of removed items''' return ListDiff._added(self.list_b, self.list_a) def isdiff(self): '''return true if there is any difference between the lists''' return self.added() or self.removed() class DictDiff(Diff): '''Class to represent the difference between two dicts''' def __init__(self, dict_a, dict_b): '''initialize class''' super(DictDiff, self).__init__() assert dict_a != None assert dict_b != None self.dict_a = dict_a self.dict_b = dict_b def added(self): '''return list of keys that have been added''' return ListDiff(self.dict_a.keys(), self.dict_b.keys()).added() def removed(self): '''return list of keys that have been removed''' return ListDiff(self.dict_a.keys(), self.dict_b.keys()).removed() def changed(self): '''return list of keys where the value has changed''' _changed = [] for key in sorted(self.dict_a.keys()): if self.dict_b.has_key(key): if isinstance(self.dict_a[key], yum.comps.Category) and \ isinstance(self.dict_b[key], yum.comps.Category): if CategoryDiff(self.dict_a[key], self.dict_b[key]).diffs(): _changed.append(key) elif isinstance(self.dict_a[key], yum.comps.Group) and \ isinstance(self.dict_b[key], yum.comps.Group): if GroupDiff(self.dict_a[key], self.dict_b[key]).diffs(): _changed.append(key) elif self.dict_a[key] != self.dict_b[key]: _changed.append(key) return sorted(_changed) def isdiff(self): '''return true if any keys have been added or removed or if any values have changed''' return self.added() or self.removed() or self.changed() class ObjectDiff(object): '''Class to represent the difference between two objects''' def __init__(self, object_a, object_b, attrs={}): '''initialize class''' self.object_a = object_a self.object_b = object_b self.attrs = attrs def diffs(self): '''return a dict of changes between the two objects''' changes = {} for attr, attrclass in self.attrs.items(): diff = attrclass(getattr(self.object_a, attr), getattr(self.object_b, attr)) if diff.isdiff(): changes[attr] = diff return changes class CategoryDiff(ObjectDiff): '''Class to represent the difference between two categories''' def __init__(self, category_a, category_b, include_translations=False): '''initialize class''' super(CategoryDiff, self).__init__(category_a, category_b, attrs = { 'name': StrDiff, 'categoryid': StrDiff, 'description': StrDiff, 'display_order': StrDiff, 'groups': ListDiff } ) class GroupDiff(ObjectDiff): '''Class to represent the difference between two groups''' def __init__(self, group_a, group_b, include_translations=False): '''initialize class''' super(GroupDiff, self).__init__(group_a, group_b, attrs = { "user_visible": StrDiff, "default": StrDiff, "selected": StrDiff, "name": StrDiff, "description": StrDiff, "mandatory_packages": DictDiff, "optional_packages": DictDiff, "default_packages": DictDiff, "conditional_packages": DictDiff, "langonly": StrDiff, "groupid": StrDiff, "display_order": StrDiff, } ) def myprint(key, value, indent=" "): '''helper method for printing diffs''' if isinstance(value, StrDiff): print "%sCHANGED(%s): %s -> %s" % (indent, key, value.changed()[0], value.changed()[1]) elif isinstance(value, DictDiff): for identifier in value.added(): print "%sADDED(%s): %s" % (indent, key, identifier) for identifier in value.removed(): print "%sREMOVED(%s): %s" % (indent, key, identifier) for identifier in value.changed(): print "%sCHANGED(%s): %s: %s -> %s" % (indent, key, identifier, value.dict_a[identifier], value.dict_b[identifier]) elif isinstance(value, ListDiff): for identifier in value.added(): print "%sADDED(%s): %s" % (indent, key, identifier) for identifier in value.removed(): print "%sREMOVED(%s): %s" % (indent, key, identifier) else: print "unknown", value def _parse_args(args): parser = OptionParser("usage: %prog [options] <comps file> <comps file>") args = parser.parse_args(args)[1] if len(args) != 3: parser.error("Incorrect number of arguments") comps_a = yum.comps.Comps() comps_a.add(args[1]) comps_b = yum.comps.Comps() comps_b.add(args[2]) return comps_a, comps_b def main(args): comps_a, comps_b = _parse_args(args) comps_a_categories = dict([ (category.categoryid, category) \ for category in comps_a.categories ]) comps_b_categories = dict([ (category.categoryid, category) \ for category in comps_b.categories ]) catdiff = DictDiff(comps_a_categories, comps_b_categories) for identifier in catdiff.added(): print "CATEGORY_ADDED: %s" % identifier for identifier in catdiff.removed(): print "CATEGORY_REMOVED: %s" % identifier for identifier in catdiff.changed(): diffs = CategoryDiff(comps_a_categories[identifier], comps_b_categories[identifier]).diffs() if diffs: print "CATEGORY_CHANGED: %s" % identifier for key, value in diffs.items(): myprint(key, value) comps_a_groups = dict([ (group.groupid, group) \ for group in comps_a.groups ]) comps_b_groups = dict([ (group.groupid, group) \ for group in comps_b.groups ]) groupdiff = DictDiff(comps_a_groups, comps_b_groups) for identifier in groupdiff.added(): print "GROUP_ADDED: %s" % identifier for identifier in groupdiff.removed(): print "GROUP_REMOVED: %s" % identifier for identifier in groupdiff.changed(): diffs = GroupDiff(comps_a_groups[identifier], comps_b_groups[identifier]).diffs() if diffs: print "GROUP_CHANGED: %s" % identifier for key, value in sorted(diffs.items()): myprint(key, value) if __name__ == '__main__': main(sys.argv)
_______________________________________________ Yum-devel mailing list Yum-devel@lists.baseurl.org http://lists.baseurl.org/mailman/listinfo/yum-devel