diff --git a/SOURCES/0001-Consistently-produce-valid-URLs-by-prepending-protocol-RhBug1632121.patch b/SOURCES/0001-Consistently-produce-valid-URLs-by-prepending-protocol-RhBug1632121.patch
new file mode 100644
index 0000000..72cdf63
--- /dev/null
+++ b/SOURCES/0001-Consistently-produce-valid-URLs-by-prepending-protocol-RhBug1632121.patch
@@ -0,0 +1,121 @@
+From f67be7b6045fb4be42ad226d0054edd685d33b9f Mon Sep 17 00:00:00 2001
+From: Aleš Matěj <amatej@redhat.com>
+Date: Tue, 12 Feb 2019 12:59:14 +0100
+Subject: [PATCH] Consistently produce valid URLs by prepending protocol. (RhBug:1632121)
+
+---
+ src/mergerepo_c.c      | 17 ++---------------
+ src/xml_dump.c         |  8 ++++++++
+ src/xml_dump.h         |  8 ++++++++
+ src/xml_dump_primary.c |  6 ++++--
+ 4 files changed, 22 insertions(+), 17 deletions(-)
+
+diff --git a/src/mergerepo_c.c b/src/mergerepo_c.c
+index 94b929c..1ae3a9b 100644
+--- a/src/mergerepo_c.c
++++ b/src/mergerepo_c.c
+@@ -496,17 +496,6 @@ cr_srpm_val_destroy(gpointer data)
+ }
+ 
+ 
+-/** Prepend protocol if necessary
+- */
+-static gchar *
+-prepend_protocol(const gchar *url)
+-{
+-    if (url && *url == '/')
+-        return g_strconcat("file://", url, NULL);
+-    return g_strdup(url);
+-}
+-
+-
+ int
+ koji_stuff_prepare(struct KojiMergedReposStuff **koji_stuff_ptr,
+                    struct CmdOptions *cmd_options,
+@@ -820,9 +809,7 @@ add_package(cr_Package *pkg,
+     if (!list) {
+         list = g_slist_prepend(list, pkg);
+         if ((!pkg->location_base || *pkg->location_base == '\0') && repopath) {
+-            _cleanup_free_ gchar *repopath_with_protocol = NULL;
+-            repopath_with_protocol = prepend_protocol(repopath);
+-            pkg->location_base = cr_safe_string_chunk_insert(pkg->chunk, repopath_with_protocol);
++            pkg->location_base = cr_safe_string_chunk_insert(pkg->chunk, repopath);
+         }
+         g_hash_table_insert (merged, (gpointer) pkg->name, (gpointer) list);
+         return 1;
+@@ -1039,7 +1026,7 @@ merge_repos(GHashTable *merged,
+                     // Koji-mergerepos specific behaviour -----------
+                     if (koji_stuff && koji_stuff->pkgorigins) {
+                         _cleanup_free_ gchar *nvra = cr_package_nvra(pkg);
+-                        _cleanup_free_ gchar *url = prepend_protocol(ml->original_url);
++                        _cleanup_free_ gchar *url = cr_prepend_protocol(ml->original_url);
+ 
+                         cr_printf(NULL,
+                                   koji_stuff->pkgorigins,
+diff --git a/src/xml_dump.c b/src/xml_dump.c
+index 3fbb422..7a93231 100644
+--- a/src/xml_dump.c
++++ b/src/xml_dump.c
+@@ -53,6 +53,14 @@ gboolean cr_hascontrollchars(const unsigned char *str)
+     return FALSE;
+ }
+ 
++gchar *
++cr_prepend_protocol(const gchar *url)
++{
++    if (url && *url == '/')
++        return g_strconcat("file://", url, NULL);
++    return g_strdup(url);
++}
++
+ void
+ cr_latin1_to_utf8(const unsigned char *in, unsigned char *out)
+ {
+diff --git a/src/xml_dump.h b/src/xml_dump.h
+index 8bb11ed..4289fc2 100644
+--- a/src/xml_dump.h
++++ b/src/xml_dump.h
+@@ -167,6 +167,14 @@ void cr_latin1_to_utf8(const unsigned char *in,
+  */
+ gboolean cr_hascontrollchars(const unsigned char *str);
+ 
++/**
++ * Prepend protocol if necessary
++ *
++ * @param url           input url
++ * @return              output string, must be freed
++ */
++gchar *cr_prepend_protocol(const gchar *url);
++
+ /** @} */
+ 
+ #ifdef __cplusplus
+diff --git a/src/xml_dump_primary.c b/src/xml_dump_primary.c
+index 5695e06..1f0292b 100644
+--- a/src/xml_dump_primary.c
++++ b/src/xml_dump_primary.c
+@@ -30,7 +30,6 @@
+ 
+ #define ERR_DOMAIN      CREATEREPO_C_ERROR
+ 
+-
+ typedef enum {
+     PCO_TYPE_PROVIDES,
+     PCO_TYPE_CONFLICTS,
+@@ -279,9 +278,12 @@ cr_xml_dump_primary_base_items(xmlNodePtr root, cr_Package *package)
+ 
+     // Write location attribute base
+     if (package->location_base && package->location_base[0] != '\0') {
++        gchar *location_base_with_protocol = NULL;
++        location_base_with_protocol = cr_prepend_protocol(package->location_base);
+         cr_xmlNewProp(location,
+                       BAD_CAST "xml:base",
+-                      BAD_CAST package->location_base);
++                      BAD_CAST location_base_with_protocol);
++        g_free(location_base_with_protocol);
+     }
+ 
+     // Write location attribute href
+--
+libgit2 0.27.8
+
diff --git a/SOURCES/0002-modifyrepo_c-Prevent-doubling-of-compression-testgzgz-RhBug1639287.patch b/SOURCES/0002-modifyrepo_c-Prevent-doubling-of-compression-testgzgz-RhBug1639287.patch
new file mode 100644
index 0000000..97a5599
--- /dev/null
+++ b/SOURCES/0002-modifyrepo_c-Prevent-doubling-of-compression-testgzgz-RhBug1639287.patch
@@ -0,0 +1,130 @@
+From a535b3f25e4391f23d1cee46028827285e221de3 Mon Sep 17 00:00:00 2001
+From: Aleš Matěj <amatej@redhat.com>
+Date: Tue, 18 Jun 2019 13:49:27 +0200
+Subject: [PATCH] modifyrepo_c: Prevent doubling of compression (test.gz.gz) (RhBug:1639287)
+
+---
+ src/compression_wrapper.c |  3 ++-
+ src/misc.c                | 22 +++++++---------------
+ src/modifyrepo_shared.c   | 22 +++++++++++++++++++++-
+ 3 files changed, 30 insertions(+), 17 deletions(-)
+
+diff --git a/src/compression_wrapper.c b/src/compression_wrapper.c
+index adc2f39..efb075c 100644
+--- a/src/compression_wrapper.c
++++ b/src/compression_wrapper.c
+@@ -148,7 +148,8 @@ cr_detect_compression(const char *filename, GError **err)
+     } else if (g_str_has_suffix(filename, ".xz"))
+     {
+         return CR_CW_XZ_COMPRESSION;
+-    } else if (g_str_has_suffix(filename, ".xml"))
++    } else if (g_str_has_suffix(filename, ".xml") ||
++               g_str_has_suffix(filename, ".sqlite"))
+     {
+         return CR_CW_NO_COMPRESSION;
+     }
+diff --git a/src/misc.c b/src/misc.c
+index 9937480..c5ccd12 100644
+--- a/src/misc.c
++++ b/src/misc.c
+@@ -437,7 +437,7 @@ cr_compress_file_with_stat(const char *src,
+     int ret = CRE_OK;
+     int readed;
+     char buf[BUFFER_SIZE];
+-    FILE *orig = NULL;
++    CR_FILE *orig = NULL;
+     CR_FILE *new = NULL;
+     gchar *dst = (gchar *) in_dst;
+     GError *tmp_err = NULL;
+@@ -466,7 +466,7 @@ cr_compress_file_with_stat(const char *src,
+                           NULL);
+     }
+ 
+-    orig = fopen(src, "rb");
++    orig = cr_open(src, CR_CW_MODE_READ, CR_CW_AUTO_DETECT_COMPRESSION, &tmp_err);
+     if (orig == NULL) {
+         g_debug("%s: Cannot open source file %s (%s)", __func__, src,
+                 g_strerror(errno));
+@@ -484,21 +484,13 @@ cr_compress_file_with_stat(const char *src,
+         goto compress_file_cleanup;
+     }
+ 
+-    while ((readed = fread(buf, 1, BUFFER_SIZE, orig)) > 0) {
+-        if (readed != BUFFER_SIZE && ferror(orig)) {
+-            g_debug("%s: Error while copy %s -> %s (%s)", __func__, src,
+-                    dst, g_strerror(errno));
+-            g_set_error(err, ERR_DOMAIN, CRE_IO,
+-                        "Error while read %s: %s", src, g_strerror(errno));
+-            ret = CRE_IO;
+-            goto compress_file_cleanup;
+-        }
+-
+-        cr_write(new, buf, readed, &tmp_err);
++    while ((readed = cr_read(orig, buf, BUFFER_SIZE, &tmp_err)) > 0) {
++        if (!tmp_err)
++            cr_write(new, buf, readed, &tmp_err);
+         if (tmp_err) {
+             g_debug("%s: Error while copy %s -> %s", __func__, src, dst);
+             g_propagate_prefixed_error(err, tmp_err,
+-                    "Error while read %s: ", dst);
++                    "Error while copy to %s: ", dst);
+             ret = CRE_IO;
+             goto compress_file_cleanup;
+         }
+@@ -510,7 +502,7 @@ compress_file_cleanup:
+         g_free(dst);
+ 
+     if (orig)
+-        fclose(orig);
++        cr_close(orig, NULL);
+ 
+     if (new)
+         cr_close(new, NULL);
+diff --git a/src/modifyrepo_shared.c b/src/modifyrepo_shared.c
+index 805c894..91e56e8 100644
+--- a/src/modifyrepo_shared.c
++++ b/src/modifyrepo_shared.c
+@@ -50,6 +50,23 @@ cr_modifyrepotask_free(cr_ModifyRepoTask *task)
+     g_free(task);
+ }
+ 
++gchar *
++remove_compression_suffix_if_present(gchar* name, GError **err)
++{
++    cr_CompressionType src_fn_com_type = cr_detect_compression(name, err);
++    if (src_fn_com_type != CR_CW_NO_COMPRESSION && src_fn_com_type != CR_CW_UNKNOWN_COMPRESSION){
++        const gchar *src_suffix = cr_compression_suffix(src_fn_com_type);
++        if (src_suffix){
++            if (g_str_has_suffix(name, src_suffix)){
++                int name_len = strlen(name);
++                int suffix_len = strlen(src_suffix);
++                return g_strndup(name, name_len - suffix_len);
++            }
++        }
++    }
++    return g_strdup(name);
++}
++
+ gboolean
+ cr_modifyrepo(GSList *modifyrepotasks, gchar *repopath, GError **err)
+ {
+@@ -192,12 +209,15 @@ cr_modifyrepo(GSList *modifyrepotasks, gchar *repopath, GError **err)
+             suffix = cr_compression_suffix(compress_type);
+         }
+ 
++        char* sufixless_src_fn = remove_compression_suffix_if_present(task->path, err);
++
+         // Prepare dst filename - Get basename
+         _cleanup_free_ gchar *filename = NULL;
+         if (task->new_name)
+             filename = g_path_get_basename(task->new_name);
+         else
+-            filename = g_path_get_basename(src_fn);
++            filename = g_path_get_basename(sufixless_src_fn);
++        g_free(sufixless_src_fn);
+ 
+         // Prepare dst filename - Add suffix
+         if (suffix) {
+--
+libgit2 0.27.8
+
diff --git a/SOURCES/0003-Correct-pkg-count-in-headers-if-there-were-invalid-pkgs-RhBug1596211.patch b/SOURCES/0003-Correct-pkg-count-in-headers-if-there-were-invalid-pkgs-RhBug1596211.patch
new file mode 100644
index 0000000..e546c4b
--- /dev/null
+++ b/SOURCES/0003-Correct-pkg-count-in-headers-if-there-were-invalid-pkgs-RhBug1596211.patch
@@ -0,0 +1,466 @@
+From dfe7218f07ffa70b73c51c71b0f051be926b6d92 Mon Sep 17 00:00:00 2001
+From: Aleš Matěj <amatej@redhat.com>
+Date: Tue, 14 May 2019 16:48:13 +0200
+Subject: [PATCH] Correct pkg count in headers if there were invalid pkgs (RhBug:1596211)
+
+---
+ src/createrepo_c.c  | 103 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------
+ src/dumper_thread.c |   4 +++-
+ src/dumper_thread.h |   3 ++-
+ src/threads.c       |  23 +++++++++++++++++++++++
+ src/threads.h       |   5 +++++
+ src/xml_file.c      | 123 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ src/xml_file.h      |  15 +++++++++++++++
+ 7 files changed, 260 insertions(+), 16 deletions(-)
+
+diff --git a/src/createrepo_c.c b/src/createrepo_c.c
+index e16ae34..67c2752 100644
+--- a/src/createrepo_c.c
++++ b/src/createrepo_c.c
+@@ -124,7 +124,7 @@ fill_pool(GThreadPool *pool,
+           struct CmdOptions *cmd_options,
+           GSList **current_pkglist,
+           FILE *output_pkg_list,
+-          long *package_count,
++          long *task_count,
+           int  media_id)
+ {
+     GQueue queue = G_QUEUE_INIT;
+@@ -259,13 +259,13 @@ fill_pool(GThreadPool *pool,
+ 
+     // Push sorted tasks into the thread pool
+     while ((task = g_queue_pop_head(&queue)) != NULL) {
+-        task->id = *package_count;
++        task->id = *task_count;
+         task->media_id = media_id;
+         g_thread_pool_push(pool, task, NULL);
+-        ++*package_count;
++        ++*task_count;
+     }
+ 
+-    return *package_count;
++    return *task_count;
+ }
+ 
+ 
+@@ -321,6 +321,27 @@ prepare_cache_dir(struct CmdOptions *cmd_options,
+     return TRUE;
+ }
+ 
++/** Check if task finished without error, if yes
++ *  use content stats of the new file
++ *
++ * @param task          Rewrite pkg count task
++ * @param filename      Name of file with wrong package count
++ * @param exit_val      If errors occured set createrepo_c exit value
++ * @param content_stat  Content stats for filename
++ *
++ */
++static void
++error_check_and_set_content_stat(cr_CompressionTask *task, char *filename, int *exit_val, cr_ContentStat **content_stat){
++    if (task->err) {
++        g_critical("Cannot rewrite pkg count in %s: %s",
++                   filename, task->err->message);
++        *exit_val = 2;
++    }else{
++        cr_contentstat_free(*content_stat, NULL);
++        *content_stat = task->stat;
++        task->stat = NULL;
++    }
++}
+ 
+ int
+ main(int argc, char **argv)
+@@ -478,7 +499,7 @@ main(int argc, char **argv)
+                                           NULL);
+     g_debug("Thread pool ready");
+ 
+-    long package_count = 0;
++    long task_count = 0;
+     GSList *current_pkglist = NULL;
+     /* ^^^ List with basenames of files which will be processed */
+ 
+@@ -490,26 +511,26 @@ main(int argc, char **argv)
+                   cmd_options,
+                   &current_pkglist,
+                   output_pkg_list,
+-                  &package_count,
++                  &task_count,
+                   media_id);
+         g_free(tmp_in_dir);
+     }
+ 
+-    g_debug("Package count: %ld", package_count);
+-    g_message("Directory walk done - %ld packages", package_count);
++    g_debug("Package count: %ld", task_count);
++    g_message("Directory walk done - %ld packages", task_count);
+ 
+     if (output_pkg_list)
+         fclose(output_pkg_list);
+ 
+ 
+     // Load old metadata if --update
+     cr_Metadata *old_metadata = NULL;
+     struct cr_MetadataLocation *old_metadata_location = NULL;
+ 
+-    if (!package_count)
++    if (!task_count)
+         g_debug("No packages found - skipping metadata loading");
+ 
+-    if (package_count && cmd_options->update) {
++    if (task_count && cmd_options->update) {
+         int ret;
+         old_metadata = cr_metadata_new(CR_HT_KEY_FILENAME, 1, current_pkglist);
+         cr_metadata_set_dupaction(old_metadata, CR_HT_DUPACT_REMOVEALL);
+@@ -741,9 +762,9 @@ main(int argc, char **argv)
+ 
+     // Set number of packages
+     g_debug("Setting number of packages");
+-    cr_xmlfile_set_num_of_pkgs(pri_cr_file, package_count, NULL);
+-    cr_xmlfile_set_num_of_pkgs(fil_cr_file, package_count, NULL);
+-    cr_xmlfile_set_num_of_pkgs(oth_cr_file, package_count, NULL);
++    cr_xmlfile_set_num_of_pkgs(pri_cr_file, task_count, NULL);
++    cr_xmlfile_set_num_of_pkgs(fil_cr_file, task_count, NULL);
++    cr_xmlfile_set_num_of_pkgs(oth_cr_file, task_count, NULL);
+ 
+     // Open sqlite databases
+     gchar *pri_db_filename = NULL;
+@@ -832,7 +853,8 @@ main(int argc, char **argv)
+     user_data.checksum_cachedir = cmd_options->checksum_cachedir;
+     user_data.skip_symlinks     = cmd_options->skip_symlinks;
+     user_data.repodir_name_len  = strlen(in_dir);
+-    user_data.package_count     = package_count;
++    user_data.task_count        = task_count;
++    user_data.package_count     = 0;
+     user_data.skip_stat         = cmd_options->skip_stat;
+     user_data.old_metadata      = old_metadata;
+     user_data.mutex_pri         = g_mutex_new();
+@@ -876,6 +898,59 @@ main(int argc, char **argv)
+     cr_xmlfile_close(fil_cr_file, NULL);
+     cr_xmlfile_close(oth_cr_file, NULL);
+ 
++
++    /* At the time of writing xml metadata headers we haven't yet parsed all
++     * the packages and we don't know whether there were some invalid ones,
++     * therefore we write the task count into the headers instead of the actual package count.
++     * If there actually were some invalid packages we have to correct this value
++     * that unfortunately means we have to decompress metadata files change package
++     * count value and compress them again.
++     */
++    if (user_data.package_count != user_data.task_count){
++        g_message("Warning: There were some invalid packages: we have to recompress other, filelists and primary xml metadata files in order to have correct package counts");
++
++        GThreadPool *rewrite_pkg_count_pool = g_thread_pool_new(cr_rewrite_pkg_count_thread,
++                                                                &user_data, 3, FALSE, NULL);
++
++        cr_CompressionTask *pri_rewrite_pkg_count_task;
++        cr_CompressionTask *fil_rewrite_pkg_count_task;
++        cr_CompressionTask *oth_rewrite_pkg_count_task;
++
++        pri_rewrite_pkg_count_task = cr_compressiontask_new(pri_xml_filename,
++                                                            NULL,
++                                                            xml_compression,
++                                                            cmd_options->repomd_checksum_type,
++                                                            1,
++                                                            &tmp_err);
++        g_thread_pool_push(rewrite_pkg_count_pool, pri_rewrite_pkg_count_task, NULL);
++
++        fil_rewrite_pkg_count_task = cr_compressiontask_new(fil_xml_filename,
++                                                            NULL,
++                                                            xml_compression,
++                                                            cmd_options->repomd_checksum_type,
++                                                            1,
++                                                            &tmp_err);
++        g_thread_pool_push(rewrite_pkg_count_pool, fil_rewrite_pkg_count_task, NULL);
++
++        oth_rewrite_pkg_count_task = cr_compressiontask_new(oth_xml_filename,
++                                                            NULL,
++                                                            xml_compression,
++                                                            cmd_options->repomd_checksum_type,
++                                                            1,
++                                                            &tmp_err);
++        g_thread_pool_push(rewrite_pkg_count_pool, oth_rewrite_pkg_count_task, NULL);
++
++        g_thread_pool_free(rewrite_pkg_count_pool, FALSE, TRUE);
++
++        error_check_and_set_content_stat(pri_rewrite_pkg_count_task, pri_xml_filename, &exit_val, &pri_stat);
++        error_check_and_set_content_stat(fil_rewrite_pkg_count_task, fil_xml_filename, &exit_val, &fil_stat);
++        error_check_and_set_content_stat(oth_rewrite_pkg_count_task, oth_xml_filename, &exit_val, &oth_stat);
++
++        cr_compressiontask_free(pri_rewrite_pkg_count_task, NULL);
++        cr_compressiontask_free(fil_rewrite_pkg_count_task, NULL);
++        cr_compressiontask_free(oth_rewrite_pkg_count_task, NULL);
++    }
++
+     g_queue_free(user_data.buffer);
+     g_mutex_free(user_data.mutex_buffer);
+     g_cond_free(user_data.cond_pri);
+diff --git a/src/dumper_thread.c b/src/dumper_thread.c
+index fbaa5be..e282f96 100644
+--- a/src/dumper_thread.c
++++ b/src/dumper_thread.c
+@@ -74,6 +74,8 @@ write_pkg(long id,
+     g_mutex_lock(udata->mutex_pri);
+     while (udata->id_pri != id)
+         g_cond_wait (udata->cond_pri, udata->mutex_pri);
++
++    udata->package_count++;
+     ++udata->id_pri;
+     cr_xmlfile_add_chunk(udata->pri_f, (const char *) res.primary, &tmp_err);
+     if (tmp_err) {
+@@ -476,7 +478,7 @@ cr_dumper_thread(gpointer data, gpointer user_data)
+ 
+     if (g_queue_get_length(udata->buffer) < MAX_TASK_BUFFER_LEN
+         && udata->id_pri != task->id
+-        && udata->package_count > (task->id + 1))
++        && udata->task_count > (task->id + 1))
+     {
+         // If:
+         //  * this isn't our turn
+diff --git a/src/dumper_thread.h b/src/dumper_thread.h
+index ed21053..4e18869 100644
+--- a/src/dumper_thread.h
++++ b/src/dumper_thread.h
+@@ -61,7 +61,8 @@ struct UserData {
+     cr_ChecksumType checksum_type;  // Constant representing selected checksum
+     const char *checksum_cachedir;  // Dir with cached checksums
+     gboolean skip_symlinks;         // Skip symlinks
+-    long package_count;             // Total number of packages to process
++    long task_count;                // Total number of task to process
++    long package_count;             // Total number of packages processed
+ 
+     // Update stuff
+     gboolean skip_stat;             // Skip stat() while updating
+diff --git a/src/threads.c b/src/threads.c
+index aee07d1..844e900 100644
+--- a/src/threads.c
++++ b/src/threads.c
+@@ -21,6 +21,7 @@
+ #include "threads.h"
+ #include "error.h"
+ #include "misc.h"
++#include "dumper_thread.h"
+ 
+ #define ERR_DOMAIN      CREATEREPO_C_ERROR
+ 
+@@ -108,6 +109,28 @@ cr_compressing_thread(gpointer data, G_GNUC_UNUSED gpointer user_data)
+     }
+ }
+ 
++void
++cr_rewrite_pkg_count_thread(gpointer data, gpointer user_data)
++{
++    cr_CompressionTask *task = data;
++    struct UserData *ud = user_data;
++    GError *tmp_err = NULL;
++
++    assert(task);
++
++    cr_rewrite_header_package_count(task->src,
++                                    task->type,
++                                    ud->package_count,
++                                    ud->task_count,
++                                    task->stat,
++                                    &tmp_err);
++
++    if (tmp_err) {
++        // Error encountered
++        g_propagate_error(&task->err, tmp_err);
++    }
++}
++
+ /** Parallel Repomd Record Fill */
+ 
+ cr_RepomdRecordFillTask *
+diff --git a/src/threads.h b/src/threads.h
+index 2d554cd..19ba917 100644
+--- a/src/threads.h
++++ b/src/threads.h
+@@ -150,6 +150,11 @@ cr_repomdrecordfilltask_free(cr_RepomdRecordFillTask *task, GError **err);
+ void
+ cr_repomd_record_fill_thread(gpointer data, gpointer user_data);
+ 
++/** Function for GThread Pool.
++ */
++void
++cr_rewrite_pkg_count_thread(gpointer data, gpointer user_data);
++
+ /** @} */
+ 
+ #ifdef __cplusplus
+diff --git a/src/xml_file.c b/src/xml_file.c
+index 65fb945..1d670ae 100644
+--- a/src/xml_file.c
++++ b/src/xml_file.c
+@@ -18,8 +18,10 @@
+  */
+ 
+ #include <glib.h>
++#include <glib/gstdio.h>
+ #include <assert.h>
+ #include "xml_file.h"
++#include <errno.h>
+ #include "error.h"
+ #include "xml_dump.h"
+ #include "compression_wrapper.h"
+@@ -40,6 +42,9 @@
+ #define XML_PRESTODELTA_HEADER  XML_HEADER"<prestodelta>\n"
+ #define XML_UPDATEINFO_HEADER   XML_HEADER"<updates>\n"
+ 
++#define XML_MAX_HEADER_SIZE     300
++#define XML_RECOMPRESS_BUFFER_SIZE   8192
++
+ #define XML_PRIMARY_FOOTER      "</metadata>"
+ #define XML_FILELISTS_FOOTER    "</filelists>"
+ #define XML_OTHER_FOOTER        "</otherdata>"
+@@ -317,3 +322,121 @@ cr_xmlfile_close(cr_XmlFile *f, GError **err)
+ 
+     return CRE_OK;
+ }
++
++static int
++write_modified_header(int task_count,
++                      int package_count,
++                      cr_XmlFile *cr_file,
++                      gchar *header_buf,
++                      int header_len,
++                      GError **err)
++{
++    GError *tmp_err = NULL;
++    gchar *package_count_string;
++    gchar *task_count_string;
++    int bytes_written = 0;
++    int package_count_string_len = rasprintf(&package_count_string, "packages=\"%i\"", package_count);
++    int task_count_string_len = rasprintf(&task_count_string, "packages=\"%i\"", task_count);
++
++    gchar *pointer_to_pkgs = strstr(header_buf, task_count_string);
++    if (!pointer_to_pkgs){
++        g_free(package_count_string);
++        g_free(task_count_string);
++        return 0;
++    }
++    gchar *pointer_to_pkgs_end = pointer_to_pkgs + task_count_string_len;
++
++    bytes_written += cr_write(cr_file->f, header_buf, pointer_to_pkgs - header_buf, &tmp_err);
++    if (!tmp_err)
++        bytes_written += cr_write(cr_file->f, package_count_string, package_count_string_len, &tmp_err);
++    if (!tmp_err)
++        bytes_written += cr_write(cr_file->f, pointer_to_pkgs_end, header_len - (pointer_to_pkgs_end - header_buf), &tmp_err);
++    if (tmp_err) {
++        g_propagate_prefixed_error(err, tmp_err, "Error encountered while writing header part:");
++        g_free(package_count_string);
++        g_free(task_count_string);
++        return 0;
++    }
++    g_free(package_count_string);
++    g_free(task_count_string);
++    return bytes_written;
++}
++
++void
++cr_rewrite_header_package_count(gchar *original_filename,
++                                cr_CompressionType xml_compression,
++                                int package_count,
++                                int task_count,
++                                cr_ContentStat *file_stat,
++                                GError **err)
++{
++    GError *tmp_err = NULL;
++    CR_FILE *original_file = cr_open(original_filename, CR_CW_MODE_READ, CR_CW_AUTO_DETECT_COMPRESSION, &tmp_err);
++    if (tmp_err) {
++        g_propagate_prefixed_error(err, tmp_err, "Error encountered while reopening for reading:");
++        return;
++    }
++
++    gchar *tmp_xml_filename = g_strconcat(original_filename, ".tmp", NULL);
++    cr_XmlFile *new_file = cr_xmlfile_sopen_primary(tmp_xml_filename,
++                                                    xml_compression,
++                                                    file_stat,
++                                                    &tmp_err);
++    if (tmp_err) {
++        g_propagate_prefixed_error(err, tmp_err, "Error encountered while opening for writing:");
++        cr_close(original_file, NULL);
++        g_free(tmp_xml_filename);
++        return;
++    }
++
++    gchar header_buf[XML_MAX_HEADER_SIZE];
++    int len_read = cr_read(original_file, header_buf, XML_MAX_HEADER_SIZE, &tmp_err);
++    if (!tmp_err)
++        write_modified_header(task_count, package_count, new_file, header_buf, len_read, &tmp_err);
++    if (tmp_err) {
++        g_propagate_prefixed_error(err, tmp_err, "Error encountered while recompressing:");
++        cr_xmlfile_close(new_file, NULL);
++        cr_close(original_file, NULL);
++        g_free(tmp_xml_filename);
++        return;
++    }
++    //Copy the rest of the file
++    gchar copy_buf[XML_RECOMPRESS_BUFFER_SIZE];
++    while(len_read)
++    {
++        len_read = cr_read(original_file, copy_buf, XML_RECOMPRESS_BUFFER_SIZE, &tmp_err);
++        if (!tmp_err)
++            cr_write(new_file->f, copy_buf, len_read, &tmp_err);
++        if (tmp_err) {
++            g_propagate_prefixed_error(err, tmp_err, "Error encountered while recompressing:");
++            cr_xmlfile_close(new_file, NULL);
++            cr_close(original_file, NULL);
++            g_free(tmp_xml_filename);
++            return;
++        }
++    }
++
++    new_file->header = 1;
++    new_file->footer = 1;
++
++    cr_xmlfile_close(new_file, &tmp_err);
++    if (tmp_err) {
++        g_propagate_prefixed_error(err, tmp_err, "Error encountered while writing:");
++        cr_close(original_file, NULL);
++        g_free(tmp_xml_filename);
++        return;
++    }
++    cr_close(original_file, &tmp_err);
++    if (tmp_err) {
++        g_propagate_prefixed_error(err, tmp_err, "Error encountered while writing:");
++        g_free(tmp_xml_filename);
++        return;
++    }
++
++    if (g_rename(tmp_xml_filename, original_filename) == -1) {
++        g_propagate_prefixed_error(err, tmp_err, "Error encountered while renaming:");
++        g_free(tmp_xml_filename);
++        return;
++    }
++    g_free(tmp_xml_filename);
++}
+diff --git a/src/xml_file.h b/src/xml_file.h
+index 96ef5e3..6ac4c97 100644
+--- a/src/xml_file.h
++++ b/src/xml_file.h
+@@ -221,6 +221,21 @@ int cr_xmlfile_add_chunk(cr_XmlFile *f, const char *chunk, GError **err);
+  */
+ int cr_xmlfile_close(cr_XmlFile *f, GError **err);
+ 
++/** Rewrite package count field in repodata header in xml file.
++ * In order to do this we have to decompress and after the change
++ * compress the whole file again, so entirely new file is created.
++ * @param original_filename     Current file with wrong value in header
++ * @param package_count         Actual package count (desired value in header)
++ * @param task_count            Task count (current value in header)
++ * @param file_stat             cr_ContentStat for stats of the new file, it will be modified
++ * @param err                   **GError
++ */
++void cr_rewrite_header_package_count(gchar *original_filename,
++                                     cr_CompressionType xml_compression,
++                                     int package_count,
++                                     int task_count,
++                                     cr_ContentStat *file_stat,
++                                     GError **err);
+ 
+ /** @} */
+ 
+--
+libgit2 0.27.8
+
diff --git a/SOURCES/0004-Add-support-for-modular-errata-RhBug1656584.patch b/SOURCES/0004-Add-support-for-modular-errata-RhBug1656584.patch
new file mode 100644
index 0000000..3765350
--- /dev/null
+++ b/SOURCES/0004-Add-support-for-modular-errata-RhBug1656584.patch
@@ -0,0 +1,1329 @@
+From a48db44b73785b5d5fbe8ae827522695fa0fd9ce Mon Sep 17 00:00:00 2001
+From: Aleš Matěj <amatej@redhat.com>
+Date: Tue, 8 Jan 2019 15:44:55 +0100
+Subject: [PATCH] Add support for modular errata (RhBug:1656584)
+
+---
+ src/python/CMakeLists.txt                         |   1 +
+ src/python/__init__.py                            |   3 +++
+ src/python/createrepo_cmodule.c                   |   8 ++++++++
+ src/python/updatecollection-py.c                  |  43 +++++++++++++++++++++++++++++++++++++++++++
+ src/python/updatecollectionmodule-py.c            | 274 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ src/python/updatecollectionmodule-py.h            |  33 +++++++++++++++++++++++++++++++++
+ src/updateinfo.c                                  |  44 ++++++++++++++++++++++++++++++++++++++++++++
+ src/updateinfo.h                                  |  24 ++++++++++++++++++++++++
+ src/xml_dump_updateinfo.c                         |  20 ++++++++++++++++++++
+ src/xml_parser_internal.h                         |   2 ++
+ src/xml_parser_updateinfo.c                       |  54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ tests/fixtures.h                                  |   1 +
+ tests/python/tests/test_updatecollection.py       |  16 ++++++++++++++++
+ tests/python/tests/test_updatecollectionmodule.py |  31 +++++++++++++++++++++++++++++++
+ tests/python/tests/test_updateinfo.py             | 189 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ tests/test_xml_parser_updateinfo.c                |  86 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ tests/testdata/updateinfo_files/updateinfo_03.xml | 128 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ 17 files changed, 957 insertions(+)
+ create mode 100644 src/python/updatecollectionmodule-py.c
+ create mode 100644 src/python/updatecollectionmodule-py.h
+ create mode 100644 tests/python/tests/test_updatecollectionmodule.py
+ create mode 100644 tests/testdata/updateinfo_files/updateinfo_03.xml
+
+diff --git a/src/python/CMakeLists.txt b/src/python/CMakeLists.txt
+index 9f1ac64..ebf4d4c 100644
+--- a/src/python/CMakeLists.txt
++++ b/src/python/CMakeLists.txt
+@@ -50,6 +50,7 @@ SET (createrepo_cmodule_SRCS
+      sqlite-py.c
+      typeconversion.c
+      updatecollection-py.c
++     updatecollectionmodule-py.c
+      updatecollectionpackage-py.c
+      updateinfo-py.c
+      updaterecord-py.c
+diff --git a/src/python/__init__.py b/src/python/__init__.py
+index 6c29e74..65d7f82 100644
+--- a/src/python/__init__.py
++++ b/src/python/__init__.py
+@@ -206,6 +206,9 @@ class OtherSqlite(Sqlite):
+ 
+ UpdateCollection = _createrepo_c.UpdateCollection
+ 
++# UpdateCollectionModule class
++
++UpdateCollectionModule = _createrepo_c.UpdateCollectionModule
+ 
+ # UpdateCollectionPackage class
+ 
+diff --git a/src/python/createrepo_cmodule.c b/src/python/createrepo_cmodule.c
+index fe4d2ad..9be5f46 100644
+--- a/src/python/createrepo_cmodule.c
++++ b/src/python/createrepo_cmodule.c
+@@ -35,6 +35,7 @@
+ #include "repomdrecord-py.h"
+ #include "sqlite-py.h"
+ #include "updatecollection-py.h"
++#include "updatecollectionmodule-py.h"
+ #include "updatecollectionpackage-py.h"
+ #include "updateinfo-py.h"
+ #include "updaterecord-py.h"
+@@ -185,6 +186,13 @@ init_createrepo_c(void)
+     PyModule_AddObject(m, "UpdateCollection",
+                        (PyObject *)&UpdateCollection_Type);
+ 
++    /* _createrepo_c.UpdateCollectionModule */
++    if (PyType_Ready(&UpdateCollectionModule_Type) < 0)
++        return FAILURE;
++    Py_INCREF(&UpdateCollectionModule_Type);
++    PyModule_AddObject(m, "UpdateCollectionModule",
++                       (PyObject *)&UpdateCollectionModule_Type);
++
+     /* _createrepo_c.UpdateCollectionPackage */
+     if (PyType_Ready(&UpdateCollectionPackage_Type) < 0)
+         return FAILURE;
+diff --git a/src/python/updatecollection-py.c b/src/python/updatecollection-py.c
+index 3a791be..ca97657 100644
+--- a/src/python/updatecollection-py.c
++++ b/src/python/updatecollection-py.c
+@@ -22,6 +22,7 @@
+ #include <stddef.h>
+ 
+ #include "updatecollection-py.h"
++#include "updatecollectionmodule-py.h"
+ #include "updatecollectionpackage-py.h"
+ #include "exception-py.h"
+ #include "typeconversion.h"
+@@ -188,6 +189,13 @@ typedef int (*ConversionToCheckFunc)(PyObject *);
+ typedef void *(*ConversionToFunc)(PyObject *, GStringChunk *);
+ 
+ PyObject *
++PyObject_FromUpdateCollectionModule(cr_UpdateCollectionModule *module)
++{
++    return Object_FromUpdateCollectionModule(
++                        cr_updatecollectionmodule_copy(module));
++}
++
++PyObject *
+ PyObject_FromUpdateCollectionPackage(cr_UpdateCollectionPackage *pkg)
+ {
+     return Object_FromUpdateCollectionPackage(
+@@ -249,6 +257,23 @@ get_list(_UpdateCollectionObject *self, void *conv)
+     return list;
+ }
+ 
++static PyObject *
++get_module(_UpdateCollectionObject *self, void *member_offset)
++{
++    if (check_UpdateCollectionStatus(self))
++        return NULL;
++
++    cr_UpdateCollection *collection = self->collection;
++
++    cr_UpdateCollectionModule *module = *((cr_UpdateCollectionModule **) ((size_t) collection + (size_t) member_offset));
++    if (module == NULL)
++        Py_RETURN_NONE;
++
++    PyObject *py_module = PyObject_FromUpdateCollectionModule(module);
++
++    return py_module;
++}
++
+ static int
+ set_str(_UpdateCollectionObject *self, PyObject *value, void *member_offset)
+ {
+@@ -265,11 +290,29 @@ set_str(_UpdateCollectionObject *self, PyObject *value, void *member_offset)
+     return 0;
+ }
+ 
++static int
++set_module(_UpdateCollectionObject *self, PyObject *value, void *member_offset)
++{
++    if (check_UpdateCollectionStatus(self))
++        return -1;
++    if (!UpdateCollectionModuleObject_Check(value) && value != Py_None) {
++        PyErr_SetString(PyExc_TypeError, "Module or None expected!");
++        return -1;
++    }
++    cr_UpdateCollectionModule *module = UpdateCollectionModule_FromPyObject(value);
++    cr_UpdateCollection *collection = self->collection;
++    *((cr_UpdateCollectionModule **) ((size_t) collection + (size_t) member_offset)) = module;
++
++    return 0;
++}
++
+ static PyGetSetDef updatecollection_getsetters[] = {
+     {"shortname",     (getter)get_str, (setter)set_str,
+         "Short name", OFFSET(shortname)},
+     {"name",          (getter)get_str, (setter)set_str,
+         "Name of the collection", OFFSET(name)},
++    {"module",        (getter)get_module, (setter)set_module,
++        "Module information", OFFSET(module)},
+     {"packages",       (getter)get_list, (setter)NULL,
+         "List of packages", &(list_convertors[0])},
+     {NULL, NULL, NULL, NULL, NULL} /* sentinel */
+diff --git a/src/python/updatecollectionmodule-py.c b/src/python/updatecollectionmodule-py.c
+new file mode 100644
+index 0000000..20b2d99
+--- /dev/null
++++ b/src/python/updatecollectionmodule-py.c
+@@ -0,0 +1,274 @@
++/* createrepo_c - Library of routines for manipulation with repodata
++ * Copyright (C) 2013  Tomas Mlcoch
++ *
++ * 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.
++ */
++
++#include <Python.h>
++#include <assert.h>
++#include <stddef.h>
++
++#include "updatecollectionmodule-py.h"
++#include "exception-py.h"
++#include "typeconversion.h"
++#include "contentstat-py.h"
++
++typedef struct {
++    PyObject_HEAD
++    cr_UpdateCollectionModule *module;
++} _UpdateCollectionModuleObject;
++
++PyObject *
++Object_FromUpdateCollectionModule(cr_UpdateCollectionModule *mod)
++{
++    PyObject *py_rec;
++
++    if (!mod) {
++        PyErr_SetString(PyExc_ValueError, "Expected a cr_UpdateCollectionModule pointer not NULL.");
++        return NULL;
++    }
++
++    py_rec = PyObject_CallObject((PyObject *) &UpdateCollectionModule_Type, NULL);
++    cr_updatecollectionmodule_free(((_UpdateCollectionModuleObject *)py_rec)->module);
++    ((_UpdateCollectionModuleObject *)py_rec)->module = mod;
++
++    return py_rec;
++}
++
++cr_UpdateCollectionModule *
++UpdateCollectionModule_FromPyObject(PyObject *o)
++{
++    if (!UpdateCollectionModuleObject_Check(o)) {
++        PyErr_SetString(PyExc_TypeError, "Expected a UpdateCollectionModule object.");
++        return NULL;
++    }
++    return ((_UpdateCollectionModuleObject *)o)->module;
++}
++
++static int
++check_UpdateCollectionModuleStatus(const _UpdateCollectionModuleObject *self)
++{
++    assert(self != NULL);
++    assert(UpdateCollectionModuleObject_Check(self));
++    if (self->module == NULL) {
++        PyErr_SetString(CrErr_Exception, "Improper createrepo_c UpdateCollectionModule object.");
++        return -1;
++    }
++    return 0;
++}
++
++/* Function on the type */
++
++static PyObject *
++updatecollectionmodule_new(PyTypeObject *type,
++                    G_GNUC_UNUSED PyObject *args,
++                    G_GNUC_UNUSED PyObject *kwds)
++{
++    _UpdateCollectionModuleObject *self = (_UpdateCollectionModuleObject *)type->tp_alloc(type, 0);
++    if (self) {
++        self->module = NULL;
++    }
++    return (PyObject *)self;
++}
++
++PyDoc_STRVAR(updatecollectionmodule_init__doc__,
++".. method:: __init__()\n\n");
++
++static int
++updatecollectionmodule_init(_UpdateCollectionModuleObject *self,
++                     G_GNUC_UNUSED PyObject *args,
++                     G_GNUC_UNUSED PyObject *kwds)
++{
++    /* Free all previous resources when reinitialization */
++    if (self->module)
++        cr_updatecollectionmodule_free(self->module);
++
++    /* Init */
++    self->module = cr_updatecollectionmodule_new();
++    if (self->module == NULL) {
++        PyErr_SetString(CrErr_Exception, "UpdateCollectionModule initialization failed");
++        return -1;
++    }
++
++    return 0;
++}
++
++static void
++updatecollectionmodule_dealloc(_UpdateCollectionModuleObject *self)
++{
++    if (self->module)
++        cr_updatecollectionmodule_free(self->module);
++    Py_TYPE(self)->tp_free(self);
++}
++
++static PyObject *
++updatecollectionmodule_repr(G_GNUC_UNUSED _UpdateCollectionModuleObject *self)
++{
++    return PyUnicode_FromFormat("<createrepo_c.UpdateCollectionModule object>");
++}
++
++/* UpdateCollectionModule methods */
++
++PyDoc_STRVAR(copy__doc__,
++"copy() -> UpdateCollectionModule\n\n"
++"Return copy of the UpdateCollectionModule object");
++
++static PyObject *
++copy_updatecollectionmodule(_UpdateCollectionModuleObject *self,
++                             G_GNUC_UNUSED void *nothing)
++{
++    if (check_UpdateCollectionModuleStatus(self))
++        return NULL;
++    return Object_FromUpdateCollectionModule(cr_updatecollectionmodule_copy(self->module));
++}
++
++static struct PyMethodDef updatecollectionmodule_methods[] = {
++    {"copy", (PyCFunction)copy_updatecollectionmodule, METH_NOARGS,
++        copy__doc__},
++    {NULL} /* sentinel */
++};
++
++/* getsetters */
++
++#define OFFSET(member) (void *) offsetof(cr_UpdateCollectionModule, member)
++
++static PyObject *
++get_str(_UpdateCollectionModuleObject *self, void *member_offset)
++{
++    if (check_UpdateCollectionModuleStatus(self))
++        return NULL;
++    cr_UpdateCollectionModule *module = self->module;
++    char *str = *((char **) ((size_t) module + (size_t) member_offset));
++    if (str == NULL)
++        Py_RETURN_NONE;
++    return PyUnicode_FromString(str);
++}
++
++static PyObject *
++get_uint(_UpdateCollectionModuleObject *self, void *member_offset)
++{
++    if (check_UpdateCollectionModuleStatus(self))
++        return NULL;
++    cr_UpdateCollectionModule *module = self->module;
++    guint64 val = *((guint64 *) ((size_t) module + (size_t) member_offset));
++    return PyLong_FromUnsignedLongLong((guint64) val);
++}
++
++static int
++set_str(_UpdateCollectionModuleObject *self, PyObject *value, void *member_offset)
++{
++    if (check_UpdateCollectionModuleStatus(self))
++        return -1;
++    if (!PyUnicode_Check(value) && !PyBytes_Check(value) && value != Py_None) {
++        PyErr_SetString(PyExc_TypeError, "Unicode, bytes, or None expected!");
++        return -1;
++    }
++
++    if (PyUnicode_Check(value)) {
++        value = PyUnicode_AsUTF8String(value);
++    }
++
++    cr_UpdateCollectionModule *module = self->module;
++    char *str = cr_safe_string_chunk_insert(module->chunk,
++                                            PyObject_ToStrOrNull(value));
++
++    *((char **) ((size_t) module + (size_t) member_offset)) = str;
++    return 0;
++}
++
++static int
++set_uint(_UpdateCollectionModuleObject *self, PyObject *value, void *member_offset)
++{
++    if (check_UpdateCollectionModuleStatus(self))
++        return -1;
++    guint64 val;
++
++    if (PyLong_Check(value)) {
++        val = PyLong_AsUnsignedLongLong(value);
++    } else if (PyFloat_Check(value)) {
++        val = (guint64) PyFloat_AS_DOUBLE(value);
++#if PY_MAJOR_VERSION < 3
++    } else if (PyInt_Check(value)) {
++        val = PyInt_AS_LONG(value);
++#endif
++    } else {
++        PyErr_SetString(PyExc_TypeError, "Number expected!");
++        return -1;
++    }
++
++    cr_UpdateCollectionModule *module = self->module;
++    *((guint64 *) ((size_t) module + (size_t) member_offset)) = (guint64) val;
++    return 0;
++}
++
++static PyGetSetDef updatecollectionmodule_getsetters[] = {
++    {"name",               (getter)get_str, (setter)set_str,
++        "Name",            OFFSET(name)},
++    {"stream",             (getter)get_str, (setter)set_str,
++        "Stream",          OFFSET(stream)},
++    {"version",            (getter)get_uint, (setter)set_uint,
++        "Version",         OFFSET(version)},
++    {"context",            (getter)get_str, (setter)set_str,
++        "Context",         OFFSET(context)},
++    {"arch",               (getter)get_str, (setter)set_str,
++        "Arch",            OFFSET(arch)},
++    {NULL, NULL, NULL, NULL, NULL} /* sentinel */
++};
++
++/* Object */
++
++PyTypeObject UpdateCollectionModule_Type = {
++    PyVarObject_HEAD_INIT(NULL, 0)
++    "createrepo_c.UpdateCollectionModule", /* tp_name */
++    sizeof(_UpdateCollectionModuleObject), /* tp_basicsize */
++    0,                              /* tp_itemsize */
++    (destructor) updatecollectionmodule_dealloc, /* tp_dealloc */
++    0,                              /* tp_print */
++    0,                              /* tp_getattr */
++    0,                              /* tp_setattr */
++    0,                              /* tp_compare */
++    (reprfunc) updatecollectionmodule_repr,/* tp_repr */
++    0,                              /* tp_as_number */
++    0,                              /* tp_as_sequence */
++    0,                              /* tp_as_mapping */
++    0,                              /* tp_hash */
++    0,                              /* tp_call */
++    0,                              /* tp_str */
++    0,                              /* tp_getattro */
++    0,                              /* tp_setattro */
++    0,                              /* tp_as_buffer */
++    Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /* tp_flags */
++    updatecollectionmodule_init__doc__,    /* tp_doc */
++    0,                              /* tp_traverse */
++    0,                              /* tp_clear */
++    0,                              /* tp_richcompare */
++    0,                              /* tp_weaklistoffset */
++    PyObject_SelfIter,              /* tp_iter */
++    0,                              /* tp_iternext */
++    updatecollectionmodule_methods,        /* tp_methods */
++    0,                              /* tp_members */
++    updatecollectionmodule_getsetters,     /* tp_getset */
++    0,                              /* tp_base */
++    0,                              /* tp_dict */
++    0,                              /* tp_descr_get */
++    0,                              /* tp_descr_set */
++    0,                              /* tp_dictoffset */
++    (initproc) updatecollectionmodule_init,/* tp_init */
++    0,                              /* tp_alloc */
++    updatecollectionmodule_new,            /* tp_new */
++    0,                              /* tp_free */
++    0,                              /* tp_is_gc */
++};
+diff --git a/src/python/updatecollectionmodule-py.h b/src/python/updatecollectionmodule-py.h
+new file mode 100644
+index 0000000..5847259
+--- /dev/null
++++ b/src/python/updatecollectionmodule-py.h
+@@ -0,0 +1,33 @@
++/* createrepo_c - Library of routines for manipulation with repodata
++ * Copyright (C) 2013  Tomas Mlcoch
++ *
++ * 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.
++ */
++
++#ifndef CR_UPDATECOLLECTIONMODULE_PY_H
++#define CR_UPDATECOLLECTIONMODULE_PY_H
++
++#include "src/createrepo_c.h"
++
++extern PyTypeObject UpdateCollectionModule_Type;
++
++#define UpdateCollectionModuleObject_Check(o) \
++            PyObject_TypeCheck(o, &UpdateCollectionModule_Type)
++
++PyObject *Object_FromUpdateCollectionModule(cr_UpdateCollectionModule *rec);
++cr_UpdateCollectionModule *UpdateCollectionModule_FromPyObject(PyObject *o);
++
++#endif
+diff --git a/src/updateinfo.c b/src/updateinfo.c
+index 6e45229..cdc4747 100644
+--- a/src/updateinfo.c
++++ b/src/updateinfo.c
+@@ -74,6 +74,46 @@ cr_updatecollectionpackage_free(cr_UpdateCollectionPackage *pkg)
+ 
+ 
+ /*
++ * cr_UpdateCollectionModule
++ */
++
++cr_UpdateCollectionModule *
++cr_updatecollectionmodule_new(void)
++{
++    cr_UpdateCollectionModule *module = g_malloc0(sizeof(*module));
++    module->chunk = g_string_chunk_new(0);
++    return module;
++}
++
++cr_UpdateCollectionModule *
++cr_updatecollectionmodule_copy(const cr_UpdateCollectionModule *orig)
++{
++    cr_UpdateCollectionModule *module;
++
++    if (!orig) return NULL;
++
++    module = cr_updatecollectionmodule_new();
++
++    module->name    = cr_safe_string_chunk_insert(module->chunk, orig->name);
++    module->stream  = cr_safe_string_chunk_insert(module->chunk, orig->stream);
++    module->version = orig->version;
++    module->context = cr_safe_string_chunk_insert(module->chunk, orig->context);
++    module->arch    = cr_safe_string_chunk_insert(module->chunk, orig->arch);
++
++    return module;
++}
++
++void
++cr_updatecollectionmodule_free(cr_UpdateCollectionModule *module)
++{
++    if (!module)
++        return;
++    g_string_chunk_free(module->chunk);
++    g_free(module);
++}
++
++
++/*
+  * cr_UpdateCollection
+  */
+ 
+@@ -97,6 +137,10 @@ cr_updatecollection_copy(const cr_UpdateCollection *orig)
+     col->shortname = cr_safe_string_chunk_insert(col->chunk, orig->shortname);
+     col->name      = cr_safe_string_chunk_insert(col->chunk, orig->name);
+ 
++    if (orig->module) {
++      col->module = cr_updatecollectionmodule_copy(orig->module);
++    }
++
+     if (orig->packages) {
+         GSList *newlist = NULL;
+         for (GSList *elem = orig->packages; elem; elem = g_slist_next(elem)) {
+diff --git a/src/updateinfo.h b/src/updateinfo.h
+index dbf7807..38883e0 100644
+--- a/src/updateinfo.h
++++ b/src/updateinfo.h
+@@ -51,8 +51,19 @@ typedef struct {
+ } cr_UpdateCollectionPackage;
+ 
+ typedef struct {
++    gchar *name;
++    gchar *stream;
++    guint64 version;
++    gchar *context;
++    gchar *arch;
++
++    GStringChunk *chunk;
++} cr_UpdateCollectionModule;
++
++typedef struct {
+     gchar *shortname;   /*!< e.g. rhn-tools-rhel-x86_64-server-6.5.aus */
+     gchar *name;        /*!< e.g. RHN Tools for RHEL AUS (v. 6.5 for 64-bit x86_64) */
++    cr_UpdateCollectionModule *module;
+     GSList *packages;   /*!< List of cr_UpdateCollectionPackage */
+     GStringChunk *chunk;
+ } cr_UpdateCollection;
+@@ -106,6 +117,19 @@ void
+ cr_updatecollectionpackage_free(cr_UpdateCollectionPackage *pkg);
+ 
+ /*
++ * cr_UpdateCollectionModule
++ */
++
++cr_UpdateCollectionModule *
++cr_updatecollectionmodule_new(void);
++
++cr_UpdateCollectionModule *
++cr_updatecollectionmodule_copy(const cr_UpdateCollectionModule *orig);
++
++void
++cr_updatecollectionmodule_free(cr_UpdateCollectionModule *pkg);
++
++/*
+  * cr_UpdateCollection
+  */
+ 
+diff --git a/src/xml_dump_updateinfo.c b/src/xml_dump_updateinfo.c
+index 4fb5720..fafe686 100644
+--- a/src/xml_dump_updateinfo.c
++++ b/src/xml_dump_updateinfo.c
+@@ -66,6 +66,24 @@ cr_xml_dump_updatecollectionpackages(xmlNodePtr collection, GSList *packages)
+ }
+ 
+ void
++cr_xml_dump_updatecollectionmodule(xmlNodePtr collection, cr_UpdateCollectionModule *module)
++{
++    if (!module)
++        return;
++
++    xmlNodePtr xml_module;
++    xml_module = xmlNewChild(collection, NULL, BAD_CAST "module", NULL);
++
++    cr_xmlNewProp_c(xml_module, BAD_CAST "name", BAD_CAST module->name);
++    cr_xmlNewProp_c(xml_module, BAD_CAST "stream", BAD_CAST module->stream);
++    gchar buf[21]; //20 + '\0' is max number of chars of guint64: G_MAXUINT64 (= 18,446,744,073,709,551,615)
++    snprintf(buf, 21, "%" G_GUINT64_FORMAT, module->version);
++    cr_xmlNewProp_c(xml_module, BAD_CAST "version", BAD_CAST buf);
++    cr_xmlNewProp_c(xml_module, BAD_CAST "context", BAD_CAST module->context);
++    cr_xmlNewProp_c(xml_module, BAD_CAST "arch", BAD_CAST module->arch);
++}
++
++void
+ cr_xml_dump_updateinforecord_pkglist(xmlNodePtr update, GSList *collections)
+ {
+     xmlNodePtr pkglist;
+@@ -83,6 +101,8 @@ cr_xml_dump_updateinforecord_pkglist(xmlNodePtr update, GSList *collections)
+                              BAD_CAST "name",
+                              BAD_CAST col->name);
+ 
++        cr_xml_dump_updatecollectionmodule(collection, col->module);
++
+         cr_xml_dump_updatecollectionpackages(collection, col->packages);
+     }
+ }
+diff --git a/src/xml_parser_internal.h b/src/xml_parser_internal.h
+index 6b400eb..e079ece 100644
+--- a/src/xml_parser_internal.h
++++ b/src/xml_parser_internal.h
+@@ -151,6 +151,8 @@ typedef struct _cr_ParserData {
+         Update record object */
+     cr_UpdateCollection *updatecollection; /*!<
+         Update collection object */
++    cr_UpdateCollectionModule *updatecollectionmodule; /*!<
++        Update collection module object */
+     cr_UpdateCollectionPackage *updatecollectionpackage; /*!<
+         Update collection package object */
+ 
+diff --git a/src/xml_parser_updateinfo.c b/src/xml_parser_updateinfo.c
+index 18e5277..c6c6503 100644
+--- a/src/xml_parser_updateinfo.c
++++ b/src/xml_parser_updateinfo.c
+@@ -54,6 +54,7 @@ typedef enum {
+     STATE_PKGLIST,          // <pkglist> ----------------------------
+     STATE_COLLECTION,
+     STATE_NAME,
++    STATE_MODULE,
+     STATE_PACKAGE,
+     STATE_FILENAME,
+     STATE_SUM,
+@@ -89,6 +90,7 @@ static cr_StatesSwitch stateswitches[] = {
+     { STATE_PKGLIST,    "collection",        STATE_COLLECTION,        0 },
+     { STATE_COLLECTION, "package",           STATE_PACKAGE,           0 },
+     { STATE_COLLECTION, "name",              STATE_NAME,              1 },
++    { STATE_COLLECTION, "module",            STATE_MODULE,            0 },
+     { STATE_PACKAGE,    "filename",          STATE_FILENAME,          1 },
+     { STATE_PACKAGE,    "sum",               STATE_SUM,               1 },
+     { STATE_PACKAGE,    "reboot_suggested",  STATE_REBOOTSUGGESTED,   0 },
+@@ -141,6 +143,7 @@ cr_start_handler(void *pdata, const char *element, const char **attr)
+     // Shortcuts
+     cr_UpdateRecord *rec = pd->updaterecord;
+     cr_UpdateCollection *collection = pd->updatecollection;
++    cr_UpdateCollectionModule *module = pd->updatecollectionmodule;
+     cr_UpdateCollectionPackage *package = pd->updatecollectionpackage;
+ 
+     switch(pd->state) {
+@@ -173,6 +176,7 @@ cr_start_handler(void *pdata, const char *element, const char **attr)
+         assert(pd->updateinfo);
+         assert(!pd->updaterecord);
+         assert(!pd->updatecollection);
++        assert(!pd->updatecollectionmodule);
+         assert(!pd->updatecollectionpackage);
+ 
+         rec = cr_updaterecord_new();
+@@ -201,6 +205,7 @@ cr_start_handler(void *pdata, const char *element, const char **attr)
+         assert(pd->updateinfo);
+         assert(pd->updaterecord);
+         assert(!pd->updatecollection);
++        assert(!pd->updatecollectionmodule);
+         assert(!pd->updatecollectionpackage);
+         val = cr_find_attr("date", attr);
+         if (val)
+@@ -211,6 +216,7 @@ cr_start_handler(void *pdata, const char *element, const char **attr)
+         assert(pd->updateinfo);
+         assert(pd->updaterecord);
+         assert(!pd->updatecollection);
++        assert(!pd->updatecollectionmodule);
+         assert(!pd->updatecollectionpackage);
+         val = cr_find_attr("date", attr);
+         if (val)
+@@ -223,6 +229,7 @@ cr_start_handler(void *pdata, const char *element, const char **attr)
+         assert(pd->updateinfo);
+         assert(pd->updaterecord);
+         assert(!pd->updatecollection);
++        assert(!pd->updatecollectionmodule);
+         assert(!pd->updatecollectionpackage);
+ 
+         ref = cr_updatereference_new();
+@@ -251,6 +258,7 @@ cr_start_handler(void *pdata, const char *element, const char **attr)
+         assert(pd->updateinfo);
+         assert(pd->updaterecord);
+         assert(!pd->updatecollection);
++        assert(!pd->updatecollectionmodule);
+         assert(!pd->updatecollectionpackage);
+ 
+         collection = cr_updatecollection_new();
+@@ -263,6 +271,49 @@ cr_start_handler(void *pdata, const char *element, const char **attr)
+ 
+         break;
+ 
++    case STATE_MODULE:
++        assert(pd->updateinfo);
++        assert(pd->updaterecord);
++        assert(pd->updatecollection);
++        assert(!pd->updatecollectionmodule);
++        assert(!pd->updatecollectionpackage);
++
++        module = cr_updatecollectionmodule_new();
++        if (module)
++            collection->module = module;
++
++        val = cr_find_attr("name", attr);
++        if (val)
++            module->name = g_string_chunk_insert(module->chunk, val);
++
++        val = cr_find_attr("stream", attr);
++        if (val)
++            module->stream = g_string_chunk_insert(module->chunk, val);
++
++        val = cr_find_attr("version", attr);
++        if (val){
++            gchar *endptr;
++            errno = 0;
++            module->version = strtoull(val, &endptr, 10);
++            if ((errno == ERANGE && (module->version == ULLONG_MAX))
++                 || (errno != 0 && module->version == 0)) {
++                perror("strtoull error when parsing module version");
++                module->version = 0;
++            }
++            if (endptr == val)
++                module->version = 0;
++        }
++
++        val = cr_find_attr("context", attr);
++        if (val)
++            module->context = g_string_chunk_insert(module->chunk, val);
++
++        val = cr_find_attr("arch", attr);
++        if (val)
++            module->arch = g_string_chunk_insert(module->chunk, val);
++
++        break;
++
+     case STATE_PACKAGE:
+         assert(pd->updateinfo);
+         assert(pd->updaterecord);
+@@ -303,6 +354,7 @@ cr_start_handler(void *pdata, const char *element, const char **attr)
+         assert(pd->updateinfo);
+         assert(pd->updaterecord);
+         assert(pd->updatecollection);
++        assert(pd->updatecollectionmodule);
+         assert(pd->updatecollectionpackage);
+         val = cr_find_attr("type", attr);
+         if (val)
+@@ -313,6 +365,7 @@ cr_start_handler(void *pdata, const char *element, const char **attr)
+         assert(pd->updateinfo);
+         assert(pd->updaterecord);
+         assert(pd->updatecollection);
++        assert(pd->updatecollectionmodule);
+         assert(pd->updatecollectionpackage);
+         package->reboot_suggested = TRUE;
+         break;
+@@ -352,6 +405,7 @@ cr_end_handler(void *pdata, G_GNUC_UNUSED const char *element)
+     case STATE_UPDATED:
+     case STATE_REFERENCES:
+     case STATE_REFERENCE:
++    case STATE_MODULE:
+     case STATE_PKGLIST:
+     case STATE_REBOOTSUGGESTED:
+         // All elements with no text data and without need of any
+diff --git a/tests/fixtures.h b/tests/fixtures.h
+index ee374f5..8567714 100644
+--- a/tests/fixtures.h
++++ b/tests/fixtures.h
+@@ -83,5 +83,6 @@
+ #define TEST_UPDATEINFO_00      TEST_UPDATEINFO_FILES_PATH"updateinfo_00.xml"
+ #define TEST_UPDATEINFO_01      TEST_UPDATEINFO_FILES_PATH"updateinfo_01.xml"
+ #define TEST_UPDATEINFO_02      TEST_UPDATEINFO_FILES_PATH"updateinfo_02.xml.xz"
++#define TEST_UPDATEINFO_03      TEST_UPDATEINFO_FILES_PATH"updateinfo_03.xml"
+ 
+ #endif
+diff --git a/tests/python/tests/test_updatecollection.py b/tests/python/tests/test_updatecollection.py
+index f3433c0..71ac7dd 100644
+--- a/tests/python/tests/test_updatecollection.py
++++ b/tests/python/tests/test_updatecollection.py
+@@ -16,6 +16,13 @@ class TestCaseUpdateCollection(unittest.TestCase):
+         self.assertEqual(col.name, None)
+         self.assertEqual(col.packages, [])
+ 
++        module = cr.UpdateCollectionModule()
++        module.name = "kangaroo"
++        module.stream = "0"
++        module.version = 20180730223407
++        module.context = "deadbeef"
++        module.arch = "noarch"
++
+         pkg = cr.UpdateCollectionPackage()
+         pkg.name = "foo"
+         pkg.version = "1.2"
+@@ -30,12 +37,21 @@ class TestCaseUpdateCollection(unittest.TestCase):
+ 
+         col.shortname = "short name"
+         col.name = "long name"
++        col.module = module
+         col.append(pkg)
+ 
+         self.assertEqual(col.shortname, "short name")
+         self.assertEqual(col.name, "long name")
+         self.assertEqual(len(col.packages), 1)
+ 
++        # Check if the appended module was appended properly
++        module = col.module
++        self.assertEqual(module.name, "kangaroo")
++        self.assertEqual(module.stream, "0")
++        self.assertEqual(module.version, 20180730223407)
++        self.assertEqual(module.context, "deadbeef")
++        self.assertEqual(module.arch, "noarch")
++
+         # Also check if the appended package was appended properly
+         pkg = col.packages[0]
+         self.assertEqual(pkg.name, "foo")
+diff --git a/tests/python/tests/test_updatecollectionmodule.py b/tests/python/tests/test_updatecollectionmodule.py
+new file mode 100644
+index 0000000..1e92b12
+--- /dev/null
++++ b/tests/python/tests/test_updatecollectionmodule.py
+@@ -0,0 +1,31 @@
++import unittest
++import shutil
++import tempfile
++import os.path
++import createrepo_c as cr
++
++from .fixtures import *
++
++class TestCaseUpdateCollectionModule(unittest.TestCase):
++
++    def test_updatecollectionmodule_setters(self):
++        module = cr.UpdateCollectionModule()
++        self.assertTrue(module)
++
++        self.assertEqual(module.name, None)
++        self.assertEqual(module.stream, None)
++        self.assertEqual(module.version, 0)
++        self.assertEqual(module.context, None)
++        self.assertEqual(module.arch, None)
++
++        module.name = "foo"
++        module.stream = "0"
++        module.version = 20180730223407
++        module.context = "deadbeef"
++        module.arch = "noarch"
++
++        self.assertEqual(module.name, "foo")
++        self.assertEqual(module.stream, "0")
++        self.assertEqual(module.version, 20180730223407)
++        self.assertEqual(module.context, "deadbeef")
++        self.assertEqual(module.arch, "noarch")
+diff --git a/tests/python/tests/test_updateinfo.py b/tests/python/tests/test_updateinfo.py
+index f3b88e1..727b707 100644
+--- a/tests/python/tests/test_updateinfo.py
++++ b/tests/python/tests/test_updateinfo.py
+@@ -123,6 +123,100 @@ class TestCaseUpdateInfo(unittest.TestCase):
+         now = datetime(now.year, now.month, now.day, now.hour, now.minute,
+                        now.second, 0)
+ 
++        mod = cr.UpdateCollectionModule()
++        mod.name = "kangaroo"
++        mod.stream = "0"
++        mod.version = 18446744073709551615
++        mod.context = "deadbeef"
++        mod.arch = "x86"
++
++        pkg = cr.UpdateCollectionPackage()
++        pkg.name = "foo"
++        pkg.version = "1.2"
++        pkg.release = "3"
++        pkg.epoch = "0"
++        pkg.arch = "x86"
++        pkg.src = "foo.src.rpm"
++        pkg.filename = "foo.rpm"
++        pkg.sum = "abcdef"
++        pkg.sum_type = cr.SHA1
++        pkg.reboot_suggested = True
++
++        col = cr.UpdateCollection()
++        col.shortname = "short name"
++        col.name = "long name"
++        col.module = mod
++        col.append(pkg)
++
++        ref = cr.UpdateReference()
++        ref.href = "href"
++        ref.id = "id"
++        ref.type = "type"
++        ref.title = "title"
++
++        rec = cr.UpdateRecord()
++        rec.fromstr = "from"
++        rec.status = "status"
++        rec.type = "type"
++        rec.version = "version"
++        rec.id = "id"
++        rec.title = "title"
++        rec.issued_date = now
++        rec.updated_date = now
++        rec.rights = "rights"
++        rec.release = "release"
++        rec.pushcount = "pushcount"
++        rec.severity = "severity"
++        rec.summary = "summary"
++        rec.description = "description"
++        rec.solution = "solution"
++        rec.append_collection(col)
++        rec.append_reference(ref)
++
++        ui = cr.UpdateInfo()
++        ui.append(rec)
++
++        xml = ui.xml_dump()
++
++        self.assertEqual(xml,
++"""<?xml version="1.0" encoding="UTF-8"?>
++<updates>
++  <update from="from" status="status" type="type" version="version">
++    <id>id</id>
++    <title>title</title>
++    <issued date="%(now)s"/>
++    <updated date="%(now)s"/>
++    <rights>rights</rights>
++    <release>release</release>
++    <pushcount>pushcount</pushcount>
++    <severity>severity</severity>
++    <summary>summary</summary>
++    <description>description</description>
++    <solution>solution</solution>
++    <references>
++      <reference href="href" id="id" type="type" title="title"/>
++    </references>
++    <pkglist>
++      <collection short="short name">
++        <name>long name</name>
++        <module name="kangaroo" stream="0" version="18446744073709551615" context="deadbeef" arch="x86"/>
++        <package name="foo" version="1.2" release="3" epoch="0" arch="x86" src="foo.src.rpm">
++          <filename>foo.rpm</filename>
++          <sum type="sha1">abcdef</sum>
++          <reboot_suggested/>
++        </package>
++      </collection>
++    </pkglist>
++  </update>
++</updates>
++""" % {"now": now.strftime("%Y-%m-%d %H:%M:%S")})
++
++    def test_updateinfo_xml_dump_04(self):
++        now = datetime.now()
++        # Microseconds are always 0 in updateinfo
++        now = datetime(now.year, now.month, now.day, now.hour, now.minute,
++                       now.second, 0)
++
+         pkg = cr.UpdateCollectionPackage()
+         pkg.name = "foo"
+         pkg.version = "1.2"
+@@ -135,6 +229,7 @@ class TestCaseUpdateInfo(unittest.TestCase):
+         pkg.sum_type = cr.SHA1
+         pkg.reboot_suggested = True
+ 
++        # Collection without module
+         col = cr.UpdateCollection()
+         col.shortname = "short name"
+         col.name = "long name"
+@@ -167,6 +262,99 @@ class TestCaseUpdateInfo(unittest.TestCase):
+ 
+         ui = cr.UpdateInfo()
+         ui.append(rec)
++
++        xml = ui.xml_dump()
++
++        self.assertEqual(xml,
++"""<?xml version="1.0" encoding="UTF-8"?>
++<updates>
++  <update from="from" status="status" type="type" version="version">
++    <id>id</id>
++    <title>title</title>
++    <issued date="%(now)s"/>
++    <updated date="%(now)s"/>
++    <rights>rights</rights>
++    <release>release</release>
++    <pushcount>pushcount</pushcount>
++    <severity>severity</severity>
++    <summary>summary</summary>
++    <description>description</description>
++    <solution>solution</solution>
++    <references>
++      <reference href="href" id="id" type="type" title="title"/>
++    </references>
++    <pkglist>
++      <collection short="short name">
++        <name>long name</name>
++        <package name="foo" version="1.2" release="3" epoch="0" arch="x86" src="foo.src.rpm">
++          <filename>foo.rpm</filename>
++          <sum type="sha1">abcdef</sum>
++          <reboot_suggested/>
++        </package>
++      </collection>
++    </pkglist>
++  </update>
++</updates>
++""" % {"now": now.strftime("%Y-%m-%d %H:%M:%S")})
++
++    def test_updateinfo_xml_dump_05(self):
++        now = datetime.now()
++        # Microseconds are always 0 in updateinfo
++        now = datetime(now.year, now.month, now.day, now.hour, now.minute,
++                       now.second, 0)
++
++        # Collection module with unset fields
++        mod = cr.UpdateCollectionModule()
++        mod.version = 18446744073709551615
++        mod.context = "deadbeef"
++        mod.arch = "x86"
++
++        pkg = cr.UpdateCollectionPackage()
++        pkg.name = "foo"
++        pkg.version = "1.2"
++        pkg.release = "3"
++        pkg.epoch = "0"
++        pkg.arch = "x86"
++        pkg.src = "foo.src.rpm"
++        pkg.filename = "foo.rpm"
++        pkg.sum = "abcdef"
++        pkg.sum_type = cr.SHA1
++        pkg.reboot_suggested = True
++
++        col = cr.UpdateCollection()
++        col.shortname = "short name"
++        col.name = "long name"
++        col.module = mod
++        col.append(pkg)
++
++        ref = cr.UpdateReference()
++        ref.href = "href"
++        ref.id = "id"
++        ref.type = "type"
++        ref.title = "title"
++
++        rec = cr.UpdateRecord()
++        rec.fromstr = "from"
++        rec.status = "status"
++        rec.type = "type"
++        rec.version = "version"
++        rec.id = "id"
++        rec.title = "title"
++        rec.issued_date = now
++        rec.updated_date = now
++        rec.rights = "rights"
++        rec.release = "release"
++        rec.pushcount = "pushcount"
++        rec.severity = "severity"
++        rec.summary = "summary"
++        rec.description = "description"
++        rec.solution = "solution"
++        rec.append_collection(col)
++        rec.append_reference(ref)
++
++        ui = cr.UpdateInfo()
++        ui.append(rec)
++
+         xml = ui.xml_dump()
+ 
+         self.assertEqual(xml,
+@@ -190,6 +378,7 @@ class TestCaseUpdateInfo(unittest.TestCase):
+     <pkglist>
+       <collection short="short name">
+         <name>long name</name>
++        <module version="18446744073709551615" context="deadbeef" arch="x86"/>
+         <package name="foo" version="1.2" release="3" epoch="0" arch="x86" src="foo.src.rpm">
+           <filename>foo.rpm</filename>
+           <sum type="sha1">abcdef</sum>
+diff --git a/tests/test_xml_parser_updateinfo.c b/tests/test_xml_parser_updateinfo.c
+index 94768ce..3f0cfee 100644
+--- a/tests/test_xml_parser_updateinfo.c
++++ b/tests/test_xml_parser_updateinfo.c
+@@ -168,6 +168,90 @@ test_cr_xml_parse_updateinfo_02(void)
+     cr_updateinfo_free(ui);
+ }
+ 
++//Test for module support
++static void
++test_cr_xml_parse_updateinfo_03(void)
++{
++    GError *tmp_err = NULL;
++    cr_UpdateInfo *ui = cr_updateinfo_new();
++    cr_UpdateRecord *update;
++    cr_UpdateReference *ref;
++    cr_UpdateCollection *col;
++    cr_UpdateCollectionModule *module;
++    cr_UpdateCollectionPackage *pkg;
++
++    int ret = cr_xml_parse_updateinfo(TEST_UPDATEINFO_03, ui,
++                                      NULL, NULL, &tmp_err);
++
++    g_assert(tmp_err == NULL);
++    g_assert_cmpint(ret, ==, CRE_OK);
++
++    g_assert_cmpint(g_slist_length(ui->updates), ==, 6);
++    update = g_slist_nth_data(ui->updates, 3);
++
++    g_assert_cmpstr(update->from, ==, "errata@redhat.com");
++    g_assert_cmpstr(update->status, ==, "stable");
++    g_assert_cmpstr(update->type, ==, "enhancement");
++    g_assert_cmpstr(update->version, ==, "1");
++    g_assert_cmpstr(update->id, ==, "RHEA-2012:0058");
++    g_assert_cmpstr(update->title, ==, "Gorilla_Erratum");
++    g_assert_cmpstr(update->description, ==, "Gorilla_Erratum");
++
++    update = g_slist_nth_data(ui->updates, 4);
++
++    g_assert_cmpstr(update->id, ==, "RHEA-2012:0059");
++    g_assert_cmpstr(update->title, ==, "Duck_Kangaroo_Erratum");
++    g_assert_cmpstr(update->description, ==, "Duck_Kangaro_Erratum description");
++    g_assert_cmpstr(update->issued_date, ==, "2018-01-27 16:08:09");
++    g_assert_cmpstr(update->updated_date, ==, "2018-07-20 06:00:01 UTC");
++    g_assert_cmpstr(update->release, ==, "1");
++
++    g_assert_cmpint(g_slist_length(update->references), ==, 0);
++
++    g_assert_cmpint(g_slist_length(update->collections), ==, 2);
++    col = g_slist_nth_data(update->collections, 0);
++    g_assert_cmpstr(col->shortname, ==, "");
++    g_assert_cmpstr(col->name, ==, "coll_name1");
++
++    module = col->module;
++    g_assert_cmpstr(module->name, ==, "kangaroo");
++    g_assert_cmpstr(module->stream, ==, "0");
++    g_assert_cmpuint(module->version, ==, 20180730223407);
++    g_assert_cmpstr(module->context, ==, "deadbeef");
++    g_assert_cmpstr(module->arch, ==, "noarch");
++
++    g_assert_cmpint(g_slist_length(col->packages), ==, 1);
++    pkg = col->packages->data;
++    g_assert_cmpstr(pkg->name, ==, "kangaroo");
++    g_assert_cmpstr(pkg->version, ==, "0.3");
++    g_assert_cmpstr(pkg->release, ==, "1");
++    g_assert(!pkg->epoch);
++    g_assert_cmpstr(pkg->arch, ==, "noarch");
++    g_assert_cmpstr(pkg->src, ==, "http://www.fedoraproject.org");
++    g_assert_cmpstr(pkg->filename, ==, "kangaroo-0.3-1.noarch.rpm");
++    g_assert(!pkg->sum);
++    g_assert(!pkg->sum_type);
++
++    col = g_slist_nth_data(update->collections, 1);
++    g_assert_cmpstr(col->shortname, ==, "");
++    g_assert_cmpstr(col->name, ==, "coll_name2");
++
++    module = col->module;
++    g_assert_cmpstr(module->name, ==, "duck");
++    g_assert_cmpstr(module->stream, ==, "0");
++    g_assert_cmpuint(module->version, ==, 20180730233102);
++    g_assert_cmpstr(module->context, ==, "deadbeef");
++    g_assert_cmpstr(module->arch, ==, "noarch");
++
++    g_assert_cmpint(g_slist_length(col->packages), ==, 1);
++    pkg = col->packages->data;
++    g_assert_cmpstr(pkg->name, ==, "duck");
++    g_assert_cmpstr(pkg->version, ==, "0.7");
++    g_assert_cmpstr(pkg->filename, ==, "duck-0.7-1.noarch.rpm");
++
++    cr_updateinfo_free(ui);
++}
++
+ int
+ main(int argc, char *argv[])
+ {
+@@ -179,6 +263,8 @@ main(int argc, char *argv[])
+                     test_cr_xml_parse_updateinfo_01);
+     g_test_add_func("/xml_parser_updateinfo/test_cr_xml_parse_updateinfo_02",
+                     test_cr_xml_parse_updateinfo_02);
++    g_test_add_func("/xml_parser_updateinfo/test_cr_xml_parse_updateinfo_03",
++                    test_cr_xml_parse_updateinfo_03);
+ 
+     return g_test_run();
+ }
+diff --git a/tests/testdata/updateinfo_files/updateinfo_03.xml b/tests/testdata/updateinfo_files/updateinfo_03.xml
+new file mode 100644
+index 0000000..ddbd99b
+--- /dev/null
++++ b/tests/testdata/updateinfo_files/updateinfo_03.xml
+@@ -0,0 +1,128 @@
++<?xml version="1.0"?>
++<updates>
++<update from="errata@redhat.com" status="stable" type="security" version="1">
++  <id>RHEA-2012:0055</id>
++  <title>Sea_Erratum</title>
++  <release>1</release>
++  <issued date="2012-01-27 16:08:06"/>
++  <updated date="2012-01-27 16:08:06"/>
++  <description>Sea_Erratum</description>
++  <pkglist>
++    <collection short="">
++      <name>1</name>
++      <package arch="noarch" name="walrus" release="1" src="http://www.fedoraproject.org" version="5.21">
++        <filename>walrus-5.21-1.noarch.rpm</filename>
++      </package>
++      <package arch="noarch" name="penguin" release="1" src="http://www.fedoraproject.org" version="0.9.1">
++        <filename>penguin-0.9.1-1.noarch.rpm</filename>
++      </package>
++      <package arch="noarch" name="shark" release="1" src="http://www.fedoraproject.org" version="0.1">
++        <filename>shark-0.1-1.noarch.rpm</filename>
++      </package>
++    </collection>
++  </pkglist>
++</update>
++
++<update from="errata@redhat.com" status="stable" type="security" version="1">
++  <id>RHEA-2012:0056</id>
++  <title>Bird_Erratum</title>
++  <release>1</release>
++  <issued date="2013-01-27 16:08:08"/>
++  <updated date="2013-02-27 17:00:00"/>
++  <description>ParthaBird_Erratum</description>
++  <pkglist>
++    <collection short="">
++      <name>1</name>
++      <package arch="noarch" name="crow" release="1" src="http://www.fedoraproject.org" version="0.8">
++        <filename>crow-0.8-1.noarch.rpm</filename>
++      </package>
++      <package arch="noarch" name="stork" release="2" src="http://www.fedoraproject.org" version="0.12">
++        <filename>stork-0.12-2.noarch.rpm</filename>
++      </package>
++      <package arch="noarch" name="duck" release="1" src="http://www.fedoraproject.org" version="0.6">
++        <filename>duck-0.6-1.noarch.rpm</filename>
++      </package>
++    </collection>
++  </pkglist>
++</update>
++
++<update from="errata@redhat.com" status="stable" type="security" version="1">
++  <id>RHEA-2012:0057</id>
++  <title>Bear_ErratumPARTHA</title>
++  <release>1</release>
++  <issued date="2013-01-27 16:08:05"/>
++  <updated date="2013-01-27 16:08:05 UTC"/>
++  <description>Bear_Erratum</description>
++  <pkglist>
++    <collection short="">
++      <name>1</name>
++      <package arch="noarch" name="bear" release="1" src="http://www.fedoraproject.org" version="4.1">
++        <filename>bear-4.1-1.noarch.rpm</filename>
++      </package>
++    </collection>
++  </pkglist>
++</update>
++
++<update from="errata@redhat.com" status="stable" type="enhancement" version="1">
++  <id>RHEA-2012:0058</id>
++  <title>Gorilla_Erratum</title>
++  <release>1</release>
++  <issued date="2013-01-27 16:08:09"/>
++  <updated date="2014-07-20 06:00:01 UTC"/>
++  <description>Gorilla_Erratum</description>
++  <pkglist>
++    <collection short="">
++      <name>1</name>
++      <package arch="noarch" name="gorilla" release="1" src="http://www.fedoraproject.org" version="0.62">
++        <filename>gorilla-0.62-1.noarch.rpm</filename>
++      </package>
++    </collection>
++  </pkglist>
++</update>
++
++<update from="errata@redhat.com" status="stable" type="enhancement" version="1">
++  <id>RHEA-2012:0059</id>
++  <title>Duck_Kangaroo_Erratum</title>
++  <release>1</release>
++  <issued date="2018-01-27 16:08:09"/>
++  <updated date="2018-07-20 06:00:01 UTC"/>
++  <description>Duck_Kangaro_Erratum description</description>
++  <pkglist>
++    <collection short="">
++      <name>coll_name1</name>
++      <module name="kangaroo" stream="0" version="20180730223407" context="deadbeef" arch="noarch"/>
++      <package arch="noarch" name="kangaroo" release="1" src="http://www.fedoraproject.org"
++               version="0.3">
++        <filename>kangaroo-0.3-1.noarch.rpm</filename>
++      </package>
++    </collection>
++    <collection short="">
++      <name>coll_name2</name>
++      <module name="duck" stream="0" version="20180730233102" context="deadbeef" arch="noarch"/>
++      <package arch="noarch" name="duck" release="1" src="http://www.fedoraproject.org"
++               version="0.7">
++        <filename>duck-0.7-1.noarch.rpm</filename>
++      </package>
++    </collection>
++  </pkglist>
++</update>
++
++<update from="errata@redhat.com" status="stable" type="enhancement" version="1">
++  <id>RHEA-2012:0060</id>
++  <title>Duck_0.8_Erratum</title>
++  <release>1</release>
++  <issued date="2018-01-29 16:08:09"/>
++  <updated date="2018-07-29 06:00:01 UTC"/>
++  <description>Duck_0.8_Erratum description</description>
++  <pkglist>
++    <collection short="">
++      <name>coll_name</name>
++      <module name="duck" stream="0" version="201809302113907" context="deadbeef" arch="noarch"/>
++      <package arch="noarch" name="duck" release="1" src="http://www.fedoraproject.org"
++               version="0.8">
++        <filename>duck-0.8-1.noarch.rpm</filename>
++      </package>
++    </collection>
++  </pkglist>
++</update>
++</updates>
+--
+libgit2 0.27.8
+
diff --git a/SOURCES/0005-Switch-off-html-timestamps-on-documentation-RhBug1731050.patch b/SOURCES/0005-Switch-off-html-timestamps-on-documentation-RhBug1731050.patch
new file mode 100644
index 0000000..c7e5228
--- /dev/null
+++ b/SOURCES/0005-Switch-off-html-timestamps-on-documentation-RhBug1731050.patch
@@ -0,0 +1,32 @@
+From df4145ae17972b51a2f4e3ccb10c7ed0a6799209 Mon Sep 17 00:00:00 2001
+From: Marek Blaha <mblaha@redhat.com>
+Date: Mon, 29 Jul 2019 12:09:07 +0200
+Subject: [PATCH] Switch off html timestamps on documentation (RhBug:1731050)
+
+This change avoids file conflicts when installing createrepo_c-devel
+package for i686 and x86_64 architectures in parallel.
+After switching the timestamps off the documentation html files are
+identical for both builds and rpm does not consider them conflicting any
+more.
+
+https://bugzilla.redhat.com/show_bug.cgi?id=1731050
+---
+ doc/Doxyfile.in.in | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/doc/Doxyfile.in.in b/doc/Doxyfile.in.in
+index bd6b5a7..35a5e75 100644
+--- a/doc/Doxyfile.in.in
++++ b/doc/Doxyfile.in.in
+@@ -952,7 +952,7 @@ HTML_COLORSTYLE_GAMMA  = 80
+ # page will contain the date and time when the page was generated. Setting
+ # this to NO can help when comparing the output of multiple runs.
+ 
+-HTML_TIMESTAMP         = YES
++HTML_TIMESTAMP         = NO
+ 
+ # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+ # documentation will contain sections that can be hidden and shown after the
+--
+libgit2 0.28.2
+
diff --git a/SPECS/createrepo_c.spec b/SPECS/createrepo_c.spec
index c664178..ff08f69 100644
--- a/SPECS/createrepo_c.spec
+++ b/SPECS/createrepo_c.spec
@@ -7,8 +7,6 @@
 %global bash_completion %{_datadir}/bash-completion/completions/*
 %endif
 
-%{!?python2_sitearch:%global python2_sitearch %{python_sitearch}}
-
 %if 0%{?rhel} && 0%{?rhel} <= 7
 %bcond_with python3
 %bcond_with drpm
@@ -26,10 +24,15 @@
 Summary:        Creates a common metadata repository
 Name:           createrepo_c
 Version:        0.11.0
-Release:        1%{?dist}
+Release:        3%{?dist}
 License:        GPLv2+
 URL:            https://github.com/rpm-software-management/createrepo_c
 Source0:        %{url}/archive/%{version}/%{name}-%{version}.tar.gz
+Patch0:         0001-Consistently-produce-valid-URLs-by-prepending-protocol-RhBug1632121.patch
+Patch1:         0002-modifyrepo_c-Prevent-doubling-of-compression-testgzgz-RhBug1639287.patch
+Patch2:         0003-Correct-pkg-count-in-headers-if-there-were-invalid-pkgs-RhBug1596211.patch
+Patch3:         0004-Add-support-for-modular-errata-RhBug1656584.patch
+Patch4:         0005-Switch-off-html-timestamps-on-documentation-RhBug1731050.patch
 
 BuildRequires:  cmake
 BuildRequires:  gcc
@@ -223,6 +226,16 @@ ln -sr %{buildroot}%{_bindir}/modifyrepo_c %{buildroot}%{_bindir}/modifyrepo
 %endif
 
 %changelog
+* Thu Aug 08 2019 Pavla Kratochvilova <pkratoch@redhat.com> - 0.11.0-3
+- Backport patch to switch off timestamps on documentation in order to remove
+  file conflicts (RhBug:1738788)
+
+* Mon Jul 22 2019 Pavla Kratochvilova <pkratoch@redhat.com> - 0.11.0-2
+- Consistently produce valid URLs by prepending protocol. (RhBug:1632121)
+- modifyrepo_c: Prevent doubling of compression (test.gz.gz) (RhBug:1639287)
+- Correct pkg count in headers if there were invalid pkgs (RhBug:1596211)
+- Add support for modular errata (RhBug:1656584)
+
 * Wed Jun 27 2018 Marek Blaha <mblaha@redhat.com> - 0.11.0-1
 - Update to 0.11.0