diff --git a/.gitignore b/.gitignore
index d101600..5aef53c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1 @@
-SOURCES/libdnf-0.39.1.tar.gz
+SOURCES/libdnf-0.48.0.tar.gz
diff --git a/.libdnf.metadata b/.libdnf.metadata
index 4bef024..b596b13 100644
--- a/.libdnf.metadata
+++ b/.libdnf.metadata
@@ -1 +1 @@
-a74a37b029439749298705ff3c1ccfbd0f0fd821 SOURCES/libdnf-0.39.1.tar.gz
+452c2195741b627bd97b0be11cd4a4dc4118e330 SOURCES/libdnf-0.48.0.tar.gz
diff --git a/SOURCES/0001-history-Fix-dnf-history-rollback-when-a-package-was-removed-RhBug1683134.patch b/SOURCES/0001-history-Fix-dnf-history-rollback-when-a-package-was-removed-RhBug1683134.patch
new file mode 100644
index 0000000..079524d
--- /dev/null
+++ b/SOURCES/0001-history-Fix-dnf-history-rollback-when-a-package-was-removed-RhBug1683134.patch
@@ -0,0 +1,244 @@
+From a96d701f7f55ff475e11ac9cda63b81c31c54e7a Mon Sep 17 00:00:00 2001
+From: Daniel Mach <dmach@redhat.com>
+Date: Wed, 6 May 2020 08:34:46 +0200
+Subject: [PATCH] history: Fix dnf history rollback when a package was removed
+ (RhBug:1683134)
+
+---
+ libdnf/transaction/MergedTransaction.cpp      |  25 +++-
+ .../transaction/MergedTransactionTest.cpp     | 120 ++++++++++++++++--
+ .../transaction/MergedTransactionTest.hpp     |   6 +-
+ 3 files changed, 137 insertions(+), 14 deletions(-)
+
+diff --git a/libdnf/transaction/MergedTransaction.cpp b/libdnf/transaction/MergedTransaction.cpp
+index a7c06ffa9..a8d878cb5 100644
+--- a/libdnf/transaction/MergedTransaction.cpp
++++ b/libdnf/transaction/MergedTransaction.cpp
+@@ -19,6 +19,7 @@
+  */
+ 
+ #include "MergedTransaction.hpp"
++#include <algorithm>
+ #include <vector>
+ 
+ namespace libdnf {
+@@ -171,6 +172,21 @@ MergedTransaction::getConsoleOutput()
+     return output;
+ }
+ 
++
++static bool transaction_item_sort_function(const std::shared_ptr<TransactionItemBase> lhs, const std::shared_ptr<TransactionItemBase> rhs) {
++    if (lhs->isForwardAction() && rhs->isForwardAction()) {
++        return false;
++    }
++    if (lhs->isBackwardAction() && rhs->isBackwardAction()) {
++        return false;
++    }
++    if (lhs->isBackwardAction()) {
++        return true;
++    }
++    return false;
++}
++
++
+ /**
+  * Get list of transaction items involved in the merged transaction
+  * Actions are merged using following rules:
+@@ -203,6 +219,9 @@ MergedTransaction::getItems()
+     // iterate over transaction
+     for (auto t : transactions) {
+         auto transItems = t->getItems();
++        // sort transaction items by their action type - forward/backward
++        // this fixes behavior of the merging algorithm in several edge cases
++        std::sort(transItems.begin(), transItems.end(), transaction_item_sort_function);
+         // iterate over transaction items
+         for (auto transItem : transItems) {
+             // get item and its type
+@@ -383,10 +402,6 @@ MergedTransaction::mergeItem(ItemPairMap &itemPairMap, TransactionItemBasePtr mT
+     auto firstState = previousItemPair.first->getAction();
+     auto newState = mTransItem->getAction();
+ 
+-    if (firstState == TransactionItemAction::INSTALL && mTransItem->isBackwardAction()) {
+-        return;
+-    }
+-
+     switch (firstState) {
+         case TransactionItemAction::REMOVE:
+         case TransactionItemAction::OBSOLETED:
+@@ -399,6 +414,8 @@ MergedTransaction::mergeItem(ItemPairMap &itemPairMap, TransactionItemBasePtr mT
+                 // Install -> Remove = (nothing)
+                 itemPairMap.erase(name);
+                 break;
++            } else if (mTransItem->isBackwardAction()) {
++                break;
+             }
+             // altered -> transfer install to the altered package
+             mTransItem->setAction(TransactionItemAction::INSTALL);
+diff --git a/tests/libdnf/transaction/MergedTransactionTest.cpp b/tests/libdnf/transaction/MergedTransactionTest.cpp
+index 90ad182cf..52507700b 100644
+--- a/tests/libdnf/transaction/MergedTransactionTest.cpp
++++ b/tests/libdnf/transaction/MergedTransactionTest.cpp
+@@ -700,7 +700,7 @@ MergedTransactionTest::test_downgrade()
+ }
+ 
+ void
+-MergedTransactionTest::test_install_downgrade()
++MergedTransactionTest::test_install_downgrade_upgrade_remove()
+ {
+     auto trans1 = std::make_shared< libdnf::swdb_private::Transaction >(conn);
+     trans1->addItem(
+@@ -724,19 +724,123 @@ MergedTransactionTest::test_install_downgrade()
+         TransactionItemReason::USER
+     );
+ 
++    auto trans3 = std::make_shared< libdnf::swdb_private::Transaction >(conn);
++    trans3->addItem(
++        nevraToRPMItem(conn, "tour-0:4.6-1.noarch"),
++        "repo2",
++        TransactionItemAction::UPGRADED,
++        TransactionItemReason::USER
++    );
++    trans3->addItem(
++        nevraToRPMItem(conn, "tour-0:4.8-1.noarch"),
++        "repo1",
++        TransactionItemAction::UPGRADE,
++        TransactionItemReason::USER
++    );
++
++    auto trans4 = std::make_shared< libdnf::swdb_private::Transaction >(conn);
++    trans4->addItem(
++        nevraToRPMItem(conn, "tour-0:4.8-1.noarch"),
++        "repo1",
++        TransactionItemAction::REMOVE,
++        TransactionItemReason::USER
++    );
++
+     MergedTransaction merged(trans1);
++
++    // test merging trans1, trans2
+     merged.merge(trans2);
++    auto items2 = merged.getItems();
++    CPPUNIT_ASSERT_EQUAL(1, (int)items2.size());
++    auto item2 = items2.at(0);
++    CPPUNIT_ASSERT_EQUAL(std::string("tour-4.6-1.noarch"), item2->getItem()->toStr());
++    CPPUNIT_ASSERT_EQUAL(std::string("repo2"), item2->getRepoid());
++    CPPUNIT_ASSERT_EQUAL(TransactionItemAction::INSTALL, item2->getAction());
++    CPPUNIT_ASSERT_EQUAL(TransactionItemReason::USER, item2->getReason());
+ 
+-    auto items = merged.getItems();
+-    CPPUNIT_ASSERT_EQUAL(1, (int)items.size());
++    // test merging trans1, trans2, trans3
++    merged.merge(trans3);
++    auto items3 = merged.getItems();
++    CPPUNIT_ASSERT_EQUAL(1, (int)items3.size());
++    auto item3 = items3.at(0);
++    CPPUNIT_ASSERT_EQUAL(std::string("tour-4.8-1.noarch"), item3->getItem()->toStr());
++    CPPUNIT_ASSERT_EQUAL(std::string("repo1"), item3->getRepoid());
++    CPPUNIT_ASSERT_EQUAL(TransactionItemAction::INSTALL, item3->getAction());
++    CPPUNIT_ASSERT_EQUAL(TransactionItemReason::USER, item3->getReason());
+ 
+-    auto item = items.at(0);
+-    CPPUNIT_ASSERT_EQUAL(std::string("tour-4.6-1.noarch"), item->getItem()->toStr());
+-    CPPUNIT_ASSERT_EQUAL(std::string("repo2"), item->getRepoid());
+-    CPPUNIT_ASSERT_EQUAL(TransactionItemAction::INSTALL, item->getAction());
+-    CPPUNIT_ASSERT_EQUAL(TransactionItemReason::USER, item->getReason());
++    // test merging trans1, trans2, trans3, trans4
++    merged.merge(trans4);
++    auto items4 = merged.getItems();
++    CPPUNIT_ASSERT_EQUAL(0, (int)items4.size());
++    // trans4 removes the package, empty output is expected
+ }
+ 
++
++void
++MergedTransactionTest::test_downgrade_upgrade_remove()
++{
++    auto trans1 = std::make_shared< libdnf::swdb_private::Transaction >(conn);
++    trans1->addItem(
++        nevraToRPMItem(conn, "tour-0:4.6-1.noarch"),
++        "repo2",
++        TransactionItemAction::DOWNGRADE,
++        TransactionItemReason::USER
++    );
++    trans1->addItem(
++        nevraToRPMItem(conn, "tour-0:4.8-1.noarch"),
++        "repo1",
++        TransactionItemAction::DOWNGRADED,
++        TransactionItemReason::USER
++    );
++
++    // items are in reversed order than in test_install_downgrade_upgrade_remove()
++    // fixing this required ordering transaction items by forward/backward action
++    auto trans2 = std::make_shared< libdnf::swdb_private::Transaction >(conn);
++    trans2->addItem(
++        nevraToRPMItem(conn, "tour-0:4.8-1.noarch"),
++        "repo1",
++        TransactionItemAction::UPGRADE,
++        TransactionItemReason::USER
++    );
++    trans2->addItem(
++        nevraToRPMItem(conn, "tour-0:4.6-1.noarch"),
++        "repo2",
++        TransactionItemAction::UPGRADED,
++        TransactionItemReason::USER
++    );
++
++    auto trans3 = std::make_shared< libdnf::swdb_private::Transaction >(conn);
++    trans3->addItem(
++        nevraToRPMItem(conn, "tour-0:4.8-1.noarch"),
++        "repo1",
++        TransactionItemAction::REMOVE,
++        TransactionItemReason::USER
++    );
++
++    MergedTransaction merged(trans1);
++
++    // test merging trans1, trans2
++    merged.merge(trans2);
++    auto items2 = merged.getItems();
++    CPPUNIT_ASSERT_EQUAL(1, (int)items2.size());
++    auto item2 = items2.at(0);
++    CPPUNIT_ASSERT_EQUAL(std::string("tour-4.8-1.noarch"), item2->getItem()->toStr());
++    CPPUNIT_ASSERT_EQUAL(std::string("repo1"), item2->getRepoid());
++    CPPUNIT_ASSERT_EQUAL(TransactionItemAction::REINSTALL, item2->getAction());
++    CPPUNIT_ASSERT_EQUAL(TransactionItemReason::USER, item2->getReason());
++
++    // test merging trans1, trans2, trans3
++    merged.merge(trans3);
++    auto items3 = merged.getItems();
++    CPPUNIT_ASSERT_EQUAL(1, (int)items3.size());
++    auto item3 = items3.at(0);
++    CPPUNIT_ASSERT_EQUAL(std::string("tour-4.8-1.noarch"), item3->getItem()->toStr());
++    CPPUNIT_ASSERT_EQUAL(std::string("repo1"), item3->getRepoid());
++    CPPUNIT_ASSERT_EQUAL(TransactionItemAction::REMOVE, item3->getAction());
++    CPPUNIT_ASSERT_EQUAL(TransactionItemReason::USER, item3->getReason());
++}
++
++
+ void
+ MergedTransactionTest::test_multilib_identity()
+ {
+diff --git a/tests/libdnf/transaction/MergedTransactionTest.hpp b/tests/libdnf/transaction/MergedTransactionTest.hpp
+index 9f1ed660a..77585e865 100644
+--- a/tests/libdnf/transaction/MergedTransactionTest.hpp
++++ b/tests/libdnf/transaction/MergedTransactionTest.hpp
+@@ -26,7 +26,8 @@ class MergedTransactionTest : public CppUnit::TestCase {
+     CPPUNIT_TEST(test_add_obsoleted_obsoleted);
+ 
+     CPPUNIT_TEST(test_downgrade);
+-    CPPUNIT_TEST(test_install_downgrade);
++    CPPUNIT_TEST(test_install_downgrade_upgrade_remove);
++    CPPUNIT_TEST(test_downgrade_upgrade_remove);
+ 
+     CPPUNIT_TEST(test_multilib_identity);
+ 
+@@ -56,7 +57,8 @@ class MergedTransactionTest : public CppUnit::TestCase {
+     // END: tests ported from DNF unit tests
+ 
+     void test_downgrade();
+-    void test_install_downgrade();
++    void test_install_downgrade_upgrade_remove();
++    void test_downgrade_upgrade_remove();
+ 
+     void test_multilib_identity();
+ private:
diff --git a/SOURCES/0001-user-agent-Drop-the-whitelist.patch b/SOURCES/0001-user-agent-Drop-the-whitelist.patch
deleted file mode 100644
index f897cb6..0000000
--- a/SOURCES/0001-user-agent-Drop-the-whitelist.patch
+++ /dev/null
@@ -1,131 +0,0 @@
-From 1dffef87fc2f07763f64eeabc1ea891e68d23541 Mon Sep 17 00:00:00 2001
-From: Michal Domonkos <mdomonko@redhat.com>
-Date: Tue, 26 Nov 2019 13:05:49 +0100
-Subject: [PATCH] [user-agent] Drop the whitelist
-
- - Stop checking os-release(5) data against a hard-coded whitelist and
-   just use them as they are, to avoid a maintenance burden in the
-   future (see [1] for details)
-
- - Clean up the getUserAgent() function a bit
-
-Note that, by removing the whitelist, there's a risk of leaking a
-"unique" value from the os-release file now, but a rather small one.
-
-[1] https://github.com/rpm-software-management/libdnf/pull/851
----
- libdnf/utils/os-release.cpp | 58 ++++++++++++++++++++--------------------------------------
- libdnf/utils/os-release.hpp |  7 ++-----
- 2 files changed, 22 insertions(+), 43 deletions(-)
-
-diff --git a/libdnf/utils/os-release.cpp b/libdnf/utils/os-release.cpp
-index 57be110..1d8a95b 100644
---- a/libdnf/utils/os-release.cpp
-+++ b/libdnf/utils/os-release.cpp
-@@ -36,17 +36,8 @@
- namespace libdnf {
- 
- // sorted by precedence (see os-release(5) for details)
--static const std::array<const std::string, 2> paths = {"/etc/os-release", "/usr/lib/os-release"};
--// whitelists used for sanity-checking the os-release data when constructing a
--// User-Agent string (to avoid reporting rare systems or platforms that could
--// be tracked)
--static const std::map<std::string, std::vector<std::string>> distros = {
--    // taken from the {fedora,generic}-release.spec files
--    { "Fedora", { "cinnamon", "cloud", "container", "coreos", "generic", "iot",
--                  "kde", "matecompiz", "server", "silverblue", "snappy", "soas",
--                  "workstation", "xfce" } },
--};
--std::array<const std::string, 1> canons = { "Linux" };
-+static const std::array<const std::string, 2>
-+paths = {"/etc/os-release", "/usr/lib/os-release"};
- 
- std::map<std::string, std::string> getOsReleaseData()
- {
-@@ -118,47 +109,38 @@ std::string getUserAgent(const std::map<std::string, std::string> & osReleaseDat
- {
-     std::ostringstream oss;
-     auto logger(Log::getLogger());
--    std::string msg = "os-release: falling back to basic User-Agent";
- 
--    // start with the basic libdnf string
-     oss << USER_AGENT;
-+    std::string fallback = oss.str();
- 
--    // mandatory OS data (bail out if missing or unknown)
-     if (!osReleaseData.count("NAME") || !osReleaseData.count("VERSION_ID")) {
--        logger->debug(tfm::format("%s: missing NAME or VERSION_ID", msg));
--        return oss.str();
-+        logger->debug(tfm::format(
-+            "User-Agent: falling back to '%s': missing NAME or VERSION_ID",
-+            fallback
-+        ));
-+        return fallback;
-     }
-     std::string name = osReleaseData.at("NAME");
-     std::string version = osReleaseData.at("VERSION_ID");
--    if (!distros.count(name)) {
--        logger->debug(tfm::format("%s: distro %s not whitelisted", msg, name));
--        return oss.str();
--    }
-+    std::string variant = "generic";
-+    if (osReleaseData.count("VARIANT_ID"))
-+        variant = osReleaseData.at("VARIANT_ID");
- 
--    // mandatory platform data from RPM (bail out if missing or unknown)
-     std::string canon = getCanonOs();
-     std::string arch = getBaseArch();
--    if (canon.empty() || arch.empty()
--        || std::find(canons.begin(), canons.end(), canon) == canons.end()) {
--        logger->debug(tfm::format("%s: could not detect canonical OS or basearch", msg));
--        return oss.str();
--    }
--
--    // optional OS data (use fallback values if missing or unknown)
--    std::string variant = "generic";
--    auto list = distros.at(name);
--    if (osReleaseData.count("VARIANT_ID")) {
--        std::string value = osReleaseData.at("VARIANT_ID");
--        if (std::find(list.begin(), list.end(), value) != list.end())
--            variant = value;
-+    if (canon.empty() || arch.empty()) {
-+        logger->debug(tfm::format(
-+            "User-Agent: falling back to '%s': could not detect OS or basearch",
-+            fallback
-+        ));
-+        return fallback;
-     }
- 
--    // good to go!
--    oss << " (" << name << " " << version << "; " << variant << "; "
--        << canon << "." << arch << ")";
-+    oss << " (" << name << " " << version << "; " << variant << "; " << canon
-+        << "." << arch << ")";
- 
-     std::string result = oss.str();
--    logger->debug(tfm::format("os-release: User-Agent constructed: %s", result));
-+    logger->debug(tfm::format("User-Agent: constructed: '%s'", result));
-     return result;
- }
- 
-diff --git a/libdnf/utils/os-release.hpp b/libdnf/utils/os-release.hpp
-index ef4d14f..e7b24a7 100644
---- a/libdnf/utils/os-release.hpp
-+++ b/libdnf/utils/os-release.hpp
-@@ -50,11 +50,8 @@ getOsReleaseData();
-  *   libdnf (NAME VERSION_ID; VARIANT_ID; OS.BASEARCH)
-  *
-  * where NAME, VERSION_ID and VARIANT_ID are OS identifiers read from the
-- * passed os-release data, and OS and BASEARCH (if found) are the canonical OS
-- * name and base architecture, respectively, detected using RPM.
-- *
-- * Note that the OS part (enclosed in parentheses) will only be included for
-- * whitelisted values.
-+ * passed os-release data, and OS and BASEARCH are the canonical OS name and
-+ * base architecture, respectively, detected using RPM.
-  *
-  * @param  osReleaseData a map containing os-release data (will be loaded from
-  *                       disk if not specified)
---
-libgit2 0.28.2
-
diff --git a/SPECS/libdnf.spec b/SPECS/libdnf.spec
index 2ccbcb6..65f8aac 100644
--- a/SPECS/libdnf.spec
+++ b/SPECS/libdnf.spec
@@ -1,8 +1,11 @@
 %global libsolv_version 0.7.7
-%global libmodulemd_version 1.6.1
-%global librepo_version 1.11.0
-%global dnf_conflict 4.2.13
+%global libmodulemd_version 2.5.0
+%global librepo_version 1.12.0
+%global dnf_conflict 4.2.23
 %global swig_version 3.0.12
+%global libdnf_major_version 0
+%global libdnf_minor_version 48
+%global libdnf_micro_version 0
 
 # set sphinx package name according to distro
 %global requires_python2_sphinx python2-sphinx
@@ -43,18 +46,20 @@
 %bcond_without zchunk
 %endif
 
+%bcond_with sanitizers
+
 %global _cmake_opts \\\
     -DENABLE_RHSM_SUPPORT=%{?with_rhsm:ON}%{!?with_rhsm:OFF} \\\
     %{nil}
 
 Name:           libdnf
-Version:        0.39.1
+Version:        %{libdnf_major_version}.%{libdnf_minor_version}.%{libdnf_micro_version}
 Release:        2%{?dist}
 Summary:        Library providing simplified C and Python API to libsolv
 License:        LGPLv2+
 URL:            https://github.com/rpm-software-management/libdnf
 Source0:        %{url}/archive/%{version}/%{name}-%{version}.tar.gz
-Patch1:         0001-user-agent-Drop-the-whitelist.patch
+Patch1:         0001-history-Fix-dnf-history-rollback-when-a-package-was-removed-RhBug1683134.patch
 
 BuildRequires:  cmake
 BuildRequires:  gcc
@@ -78,11 +83,17 @@ BuildRequires:  pkgconfig(sqlite3)
 BuildRequires:  pkgconfig(json-c)
 BuildRequires:  pkgconfig(cppunit)
 BuildRequires:  pkgconfig(libcrypto)
-BuildRequires:  pkgconfig(modulemd) >= %{libmodulemd_version}
+BuildRequires:  pkgconfig(modulemd-2.0) >= %{libmodulemd_version}
 BuildRequires:  pkgconfig(smartcols)
 BuildRequires:  gettext
 BuildRequires:  gpgme-devel
 
+%if %{with sanitizers}
+BuildRequires:  libasan-static
+BuildRequires:  liblsan-static
+BuildRequires:  libubsan-static
+%endif
+
 Requires:       libmodulemd%{?_isa} >= %{libmodulemd_version}
 Requires:       libsolv%{?_isa} >= %{libsolv_version}
 Requires:       librepo%{?_isa} >= %{librepo_version}
@@ -125,7 +136,8 @@ BuildRequires:  swig >= %{swig_version}
 
 %description -n python2-%{name}
 Python 2 bindings for the libdnf library.
-%endif # with python2
+%endif
+# endif with python2
 
 %if %{with python3}
 %package -n python3-%{name}
@@ -159,7 +171,8 @@ Conflicts:      python-dnf < %{dnf_conflict}
 
 %description -n python2-hawkey
 Python 2 bindings for the hawkey library.
-%endif # with python2
+%endif
+# endif with python2
 
 %if %{with python3}
 %package -n python3-hawkey
@@ -183,7 +196,7 @@ Python 3 bindings for the hawkey library.
 %autosetup -p1
 %if %{with python2}
 mkdir build-py2
-%endif # with python2
+%endif
 %if %{with python3}
 mkdir build-py3
 %endif
@@ -196,10 +209,12 @@ pushd build-py2
     %define _cmake_builddir build-py2
     %define __builddir build-py2
   %endif
-  %cmake -DPYTHON_DESIRED:FILEPATH=%{__python2} -DWITH_MAN=OFF ../ %{!?with_zchunk:-DWITH_ZCHUNK=OFF} %{!?with_valgrind:-DDISABLE_VALGRIND=1} %{_cmake_opts}
+  %cmake -DPYTHON_DESIRED:FILEPATH=%{__python2} -DWITH_MAN=OFF ../ %{!?with_zchunk:-DWITH_ZCHUNK=OFF} %{!?with_valgrind:-DDISABLE_VALGRIND=1} %{_cmake_opts} -DLIBDNF_MAJOR_VERSION=%{libdnf_major_version} -DLIBDNF_MINOR_VERSION=%{libdnf_minor_version} -DLIBDNF_MICRO_VERSION=%{libdnf_micro_version} \
+    -DWITH_SANITIZERS=%{?with_sanitizers:ON}%{!?with_sanitizers:OFF}
   %make_build
 popd
-%endif # with python2
+%endif
+# endif with python2
 
 %if %{with python3}
 pushd build-py3
@@ -208,7 +223,8 @@ pushd build-py3
     %define _cmake_builddir build-py3
     %define __builddir build-py3
   %endif
-  %cmake -DPYTHON_DESIRED:FILEPATH=%{__python3} -DWITH_GIR=0 -DWITH_MAN=0 -Dgtkdoc=0 ../ %{!?with_zchunk:-DWITH_ZCHUNK=OFF} %{!?with_valgrind:-DDISABLE_VALGRIND=1} %{_cmake_opts}
+  %cmake -DPYTHON_DESIRED:FILEPATH=%{__python3} -DWITH_GIR=0 -DWITH_MAN=0 -Dgtkdoc=0 ../ %{!?with_zchunk:-DWITH_ZCHUNK=OFF} %{!?with_valgrind:-DDISABLE_VALGRIND=1} %{_cmake_opts} -DLIBDNF_MAJOR_VERSION=%{libdnf_major_version} -DLIBDNF_MINOR_VERSION=%{libdnf_minor_version} -DLIBDNF_MICRO_VERSION=%{libdnf_micro_version} \
+    -DWITH_SANITIZERS=%{?with_sanitizers:ON}%{!?with_sanitizers:OFF}
   %make_build
 popd
 %endif
@@ -218,7 +234,7 @@ popd
 pushd build-py2
   make ARGS="-V" test
 popd
-%endif # with python2
+%endif
 %if %{with python3}
 # If we didn't run the general tests yet, do it now.
 %if %{without python2}
@@ -240,7 +256,7 @@ popd
 pushd build-py2
   %make_install
 popd
-%endif # with python2
+%endif
 %if %{with python3}
 pushd build-py3
   %make_install
@@ -273,7 +289,7 @@ popd
 %if %{with python2}
 %files -n python2-%{name}
 %{python2_sitearch}/%{name}/
-%endif # with python2
+%endif
 
 %if %{with python3}
 %files -n python3-%{name}
@@ -283,7 +299,7 @@ popd
 %if %{with python2}
 %files -n python2-hawkey
 %{python2_sitearch}/hawkey/
-%endif # with python2
+%endif
 
 %if %{with python3}
 %files -n python3-hawkey
@@ -291,6 +307,80 @@ popd
 %endif
 
 %changelog
+* Wed Jun 10 2020 Ales Matej <amatej@redhat.com> - 0.48.0-2
+- [history] Fix dnf history rollback when a package was removed (RhBug:1683134)
+
+* Wed Jun 03 2020 Nicola Sella <nsella@redhat.com> - 0.48.0-1
+- Update to 0.48.0
+- swdb: Catch only SQLite3 exceptions and simplify the messages
+- MergedTransaction list multiple comments (RhBug:1773679)
+- Modify CMake to pull *.po files from weblate
+- Optimize DependencyContainer creation from an existing queue
+- fix a memory leak in dnf_package_get_requires()
+- Fix memory leaks on g_build_filename()
+- Fix memory leak in dnf_context_setup()
+- Add `hy_goal_favor` and `hy_goal_disfavor`
+- Define a cleanup function for `DnfPackageSet`
+- dnf-repo: fix dnf_repo_get_public_keys double-free
+- Do not cache RPMDB
+- Use single-quotes around string literals used in SQL statements
+- SQLite3: Do not close the database if it wasn't opened (RhBug:1761976)
+- Don't create a new history DB connection for in-memory DB
+- transaction/Swdb: Use a single logger variable in constructor
+- utils: Add a safe version of pathExists()
+- swdb: Handle the case when pathExists() fails on e.g. permission
+- Repo: prepend "file://" if a local path is used as baseurl
+- Move urlEncode() to utils
+- utils: Add 'exclude' argument to urlEncode()
+- Encode package URL for downloading through librepo (RhBug:1817130)
+- Replace std::runtime_error with libdnf::RepoError
+- Fixes and error handling improvements of the File class
+- [context] Use ConfigRepo for gpgkey and baseurl (RhBug:1807864)
+- [context] support "priority" option in .repo config file (RhBug:1797265)
+
+* Fri Apr 03 2020 Ales Matej <amatej@redhat.com> - 0.47.0-1
+- Update to 0.47.0
+- Allow excluding packages with "excludepkgs" and globs
+- Make parsing of reldeps more strict (RhBug:1788107)
+- Add expanding solvable provides for dependency matching (RhBug:1534123)
+- DnfRepo: fix module_hotfixes keyfile priority level
+- Add custom exceptions to libdnf interface
+- Port to libmodulemd-2 API (RhBug:1693683)
+- Add prereq_ignoreinst & regular_requires properties for pkg (RhBug:1543449)
+- Reset active modules when no module enabled or default (RhBug:1767351)
+- Add comment option to transaction (RhBug:1773679)
+- Baseurl is not exclusive with mirrorlist/metalink (RhBug:1775184)
+- Add new function to reset all modules in C API (dnf_context_reset_all_modules)
+- Handle situation when an unprivileged user cannot create history database (RhBug:1634385)
+- Add query filter: latest by priority
+- Add setter for running kernel protection setting
+- Add DNF_NO_PROTECTED flag to allow empty list of protected packages
+- Config options: only first empty value clears existing (RhBug:1788154)
+- [conf] Set useful default colors when color is enabled
+- [context] Use installonly_limit from global config (RhBug:1256108)
+- [context] Add API to get/set "install_weak_deps"
+- [context] Adds support for includepkgs in repository configuration.
+- [context] Adds support for excludepkgs, exclude, includepkgs, and disable_excludes in main configuration.
+- [context] Added function dnf_transaction_set_dont_solve_goal
+- [context] Added functions dnf_context_get/set_config_file_path
+- [context] Respect "plugins" global conf value
+- [context] Add API to disable/enable plugins
+- [context] Create new repo instead of reusing old one (RhBug:1795004)
+- [context] Error when main config file can't be opened (RhBug:1794864)
+- [context] Add function function dnf_context_is_set_config_file_path
+- [context] Support repositories defined in main configuration file
+
+* Tue Feb 18 2020 Ales Matej <amatej@redhat.com> - 0.39.1-5
+- Fix filtering of packages by advisory (RhBug:1770125)
+
+* Fri Jan 31 2020 Marek Blaha <mblaha@redhat.com> - 0.39.1-4
+- [translations] Update translations from zanata (RhBug:1754965)
+
+* Mon Jan 13 2020 Ales Matej <amatej@redhat.com> - 0.39.1-3
+- Add two new query filters: obsoletes_by_priority, upgrades_by_priority (RhBug:1769466)
+- [context] Add wildcard support in dnf_context_repo_set_data (RhBug:1781420)
+- Remove killGpgAgent function (RhBug:1781601)
+
 * Thu Dec 12 2019 Pavla Kratochvilova <pkratoch@redhat.com> - 0.39.1-2
 - [user-agent] Stop checking os-release(5) data against a hard-coded whitelist (RhBug:1777255)