diff --git a/NEWS b/NEWS
index 487f1ddd..060f5545 100644
--- a/NEWS
+++ b/NEWS
@@ -8,6 +8,13 @@ xbps-0.41 (???):
* A performance improvement for xbps_file_hash(), by Enno Boland.
+ * Added reverts field to package definitions. If defined, a package will
+ replace all versions specified in this field even if the installed version is
+ newer. This is helpful when a broken package is released. The maintainer can
+ downgrade this package and define a reverts field to the broken version. On
+ the next update xbps-install will automatically replace the broken release by
+ the new downgraded release.
+
xbps-0.40 (2014-09-18):
* xbps-install(8): handle xbps_transaction_prepare() returning ENOSPC, to
diff --git a/bin/xbps-create/main.c b/bin/xbps-create/main.c
index f84d630e..427c386a 100644
--- a/bin/xbps-create/main.c
+++ b/bin/xbps-create/main.c
@@ -94,6 +94,8 @@ usage(void)
" -q --quiet Work silently.\n"
" -R --replaces Replaces (blank separated list,\n"
" e.g: 'foo>=1.0 blah<2.0').\n"
+ " -r --reverts Reverts (blank separated list,\n"
+ " e.g: '1.0_1 2.0_3').\n"
" -S --long-desc Long description (80 cols per line).\n"
" -s --desc Short description (max 80 characters).\n"
" -t --tags A list of tags/categories (blank separated list).\n"
@@ -591,7 +593,7 @@ set_build_date(void)
int
main(int argc, char **argv)
{
- const char *shortopts = "A:B:C:D:F:G:H:hl:M:m:n:P:pqR:S:s:t:V";
+ const char *shortopts = "A:B:C:D:F:G:H:hl:M:m:n:P:pqr:R:S:s:t:V";
const struct option longopts[] = {
{ "architecture", required_argument, NULL, 'A' },
{ "built-with", required_argument, NULL, 'B' },
@@ -609,6 +611,7 @@ main(int argc, char **argv)
{ "preserve", no_argument, NULL, 'p' },
{ "quiet", no_argument, NULL, 'q' },
{ "replaces", required_argument, NULL, 'R' },
+ { "reverts", required_argument, NULL, 'r' },
{ "long-desc", required_argument, NULL, 'S' },
{ "desc", required_argument, NULL, 's' },
{ "tags", required_argument, NULL, 't' },
@@ -624,7 +627,7 @@ main(int argc, char **argv)
struct archive_entry_linkresolver *resolver;
struct stat st;
const char *conflicts, *deps, *homepage, *license, *maint, *bwith;
- const char *provides, *pkgver, *replaces, *desc, *ldesc;
+ const char *provides, *pkgver, *replaces, *reverts, *desc, *ldesc;
const char *arch, *config_files, *mutable_files, *version;
const char *buildopts, *shlib_provides, *shlib_requires;
const char *compression, *tags = NULL, *srcrevs = NULL;
@@ -634,8 +637,9 @@ main(int argc, char **argv)
mode_t myumask;
arch = conflicts = deps = homepage = license = maint = compression = NULL;
- provides = pkgver = replaces = desc = ldesc = bwith = buildopts = NULL;
- config_files = mutable_files = shlib_provides = shlib_requires = NULL;
+ provides = pkgver = replaces = reverts = desc = ldesc = bwith = NULL;
+ buildopts = config_files = mutable_files = shlib_provides = NULL;
+ shlib_requires = NULL;
while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) {
if (optarg && strcmp(optarg, "") == 0)
@@ -690,6 +694,9 @@ main(int argc, char **argv)
case 'R':
replaces = optarg;
break;
+ case 'r':
+ reverts = optarg;
+ break;
case 'S':
ldesc = optarg;
break;
@@ -794,6 +801,7 @@ main(int argc, char **argv)
process_array("conflicts", conflicts);
process_array("provides", provides);
process_array("replaces", replaces);
+ process_array("reverts", reverts);
process_array("shlib-provides", shlib_provides);
process_array("shlib-requires", shlib_requires);
diff --git a/bin/xbps-create/xbps-create.8 b/bin/xbps-create/xbps-create.8
index 6c54bbed..f1b7f257 100644
--- a/bin/xbps-create/xbps-create.8
+++ b/bin/xbps-create/xbps-create.8
@@ -58,6 +58,9 @@ Enable quiet operation.
.It Fl R, Fl -replaces Ar list
A list of package patterns this package replaces, separated by whitespaces. Example:
.Ar 'foo>=1.0 blah>=2.0' .
+.It Fl r, Fl -reverts Ar list
+A list of versions this package reverts, separated by whitespaces. Example:
+.Ar '2.0_1 2.1_1' .
.It Fl S, Fl -long-desc Ar string
A long description for this package.
.It Fl s, Fl -desc Ar string
diff --git a/bin/xbps-rindex/index-add.c b/bin/xbps-rindex/index-add.c
index a61e4e5e..e8955a81 100644
--- a/bin/xbps-rindex/index-add.c
+++ b/bin/xbps-rindex/index-add.c
@@ -117,6 +117,22 @@ index_add(struct xbps_handle *xhp, int argc, char **argv, bool force)
xbps_dictionary_get_cstring(curpkgd, "pkgver", &opkgver);
xbps_dictionary_get_cstring(curpkgd, "architecture", &oarch);
ret = xbps_cmpver(pkgver, opkgver);
+
+ /*
+ * If the considered package reverts the package in the index,
+ * consider the current package as the newer one.
+ */
+ if(ret < 0 && xbps_pkg_reverts(binpkgd, opkgver)) {
+ ret = 1;
+ }
+ /*
+ * If package in the index reverts considered package, consider the
+ * package in the index as the newer one.
+ */
+ else if (ret > 0 && xbps_pkg_reverts(curpkgd, pkgver)) {
+ ret = -1;
+ }
+
if (ret <= 0) {
/* Same version or index version greater */
fprintf(stderr, "index: skipping `%s' (%s), already registered.\n", pkgver, arch);
diff --git a/include/xbps.h.in b/include/xbps.h.in
index f5a1a8fb..2e909a0f 100644
--- a/include/xbps.h.in
+++ b/include/xbps.h.in
@@ -48,7 +48,7 @@
*
* This header documents the full API for the XBPS Library.
*/
-#define XBPS_API_VERSION "20140913"
+#define XBPS_API_VERSION "20140923"
#ifndef XBPS_VERSION
#define XBPS_VERSION "UNSET"
@@ -1835,6 +1835,21 @@ bool xbps_pkg_arch_match(struct xbps_handle *xhp,
*/
int xbps_humanize_number(char *buf, int64_t bytes);
+/**
+ * Tests if pkgver is reverted by pkg
+ *
+ * The package version is defined by:
+ * ${NAME}-{${VERSION}_${REVISION}.
+ *
+ * the name part is ignored.
+ *
+ * @param[in] pkg a package which is a candidate to revert pkgver.
+ * @param[in] pkgver a package version string
+ *
+ * @return true if pkg reverts pkgver, false otherwise.
+ */
+bool xbps_pkg_reverts(xbps_dictionary_t pkg, const char *pkgver);
+
/**
* Compares package version strings.
*
diff --git a/lib/transaction_ops.c b/lib/transaction_ops.c
index 7dd52e4d..63e6edf4 100644
--- a/lib/transaction_ops.c
+++ b/lib/transaction_ops.c
@@ -117,7 +117,8 @@ trans_find_pkg(struct xbps_handle *xhp, const char *pkg, bool reinstall)
*/
xbps_dictionary_get_cstring_nocopy(pkg_pkgdb,
"pkgver", &instpkgver);
- if (xbps_cmpver(repopkgver, instpkgver) <= 0) {
+ if (xbps_cmpver(repopkgver, instpkgver) <= 0 &&
+ !xbps_pkg_reverts(pkg_repod, repopkgver)) {
xbps_dbg_printf(xhp, "[rpool] Skipping `%s' "
"(installed: %s) from repository `%s'\n",
repopkgver, instpkgver, repoloc);
diff --git a/lib/util.c b/lib/util.c
index 11ea33b1..a05e6451 100644
--- a/lib/util.c
+++ b/lib/util.c
@@ -425,3 +425,26 @@ xbps_humanize_number(char *buf, int64_t bytes)
return humanize_number(buf, 7, bytes, "B",
HN_AUTOSCALE, HN_DECIMAL|HN_NOSPACE);
}
+
+/*
+ * Check if pkg is explicitly marked to replace a specific installed version.
+ */
+bool
+xbps_pkg_reverts(xbps_dictionary_t pkg, const char *pkgver) {
+ unsigned int i;
+ xbps_array_t reverts;
+ const char *version = xbps_pkg_version(pkgver);
+ const char *revertver;
+
+ if ((reverts = xbps_dictionary_get(pkg, "reverts")) == NULL)
+ return false;
+
+ for (i = 0; i < xbps_array_count(reverts); i++) {
+ xbps_array_get_cstring_nocopy(reverts, i, &revertver);
+ if (strcmp(version, revertver) == 0) {
+ return false;
+ }
+ }
+
+ return false;
+}
diff --git a/tests/xbps/libxbps/pkgdb/main.c b/tests/xbps/libxbps/pkgdb/main.c
index e810523f..b87b8ca8 100644
--- a/tests/xbps/libxbps/pkgdb/main.c
+++ b/tests/xbps/libxbps/pkgdb/main.c
@@ -146,11 +146,42 @@ ATF_TC_BODY(pkgdb_get_pkg_revdeps_test, tc)
ATF_REQUIRE_STREQ(xbps_string_cstring_nocopy(pstr), eout);
}
+ATF_TC(pkgdb_pkg_reverts_test);
+ATF_TC_HEAD(pkgdb_pkg_reverts_test, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test xbps_pkg_reverts()");
+}
+
+ATF_TC_BODY(pkgdb_pkg_reverts_test, tc)
+{
+ struct xbps_handle xh;
+ const char *tcsdir;
+ xbps_dictionary_t pkgd;
+
+ /* get test source dir */
+ tcsdir = atf_tc_get_config_var(tc, "srcdir");
+
+ memset(&xh, 0, sizeof(xh));
+ strncpy(xh.rootdir, tcsdir, sizeof(xh.rootdir));
+ strncpy(xh.metadir, tcsdir, sizeof(xh.metadir));
+ xh.flags = XBPS_FLAG_DEBUG;
+ ATF_REQUIRE_EQ(xbps_init(&xh), 0);
+
+ pkgd = xbps_pkgdb_get_pkg(&xh, "reverts");
+ ATF_REQUIRE_EQ(xbps_object_type(pkgd), XBPS_TYPE_DICTIONARY);
+
+ ATF_REQUIRE_EQ(xbps_pkg_reverts(pkgd, "reverts-0.2_1"), 0);
+ ATF_REQUIRE_EQ(xbps_pkg_reverts(pkgd, "reverts-0.3_1"), 1);
+ ATF_REQUIRE_EQ(xbps_pkg_reverts(pkgd, "reverts-0.4_1"), 1);
+ ATF_REQUIRE_EQ(xbps_pkg_reverts(pkgd, "reverts-0.5_1"), 0);
+}
+
ATF_TP_ADD_TCS(tp)
{
ATF_TP_ADD_TC(tp, pkgdb_get_pkg_test);
ATF_TP_ADD_TC(tp, pkgdb_get_virtualpkg_test);
ATF_TP_ADD_TC(tp, pkgdb_get_pkg_revdeps_test);
+ ATF_TP_ADD_TC(tp, pkgdb_pkg_reverts_test);
return atf_no_error();
}
diff --git a/tests/xbps/libxbps/pkgdb/pkgdb-0.38.plist b/tests/xbps/libxbps/pkgdb/pkgdb-0.38.plist
index b6bfa35b..7c4393ad 100644
--- a/tests/xbps/libxbps/pkgdb/pkgdb-0.38.plist
+++ b/tests/xbps/libxbps/pkgdb/pkgdb-0.38.plist
@@ -94,5 +94,21 @@
state
installed
+ reverts
+
+ automatic-install
+
+ pkgver
+ reverts-0.2_1
+ reverts
+
+ 0.3_1
+ 0.4_1
+
+ short_desc
+ two descriptionm
+ state
+ installed
+
diff --git a/tests/xbps/xbps-rindex/add_test.sh b/tests/xbps/xbps-rindex/add_test.sh
index 82302072..a84b5178 100644
--- a/tests/xbps/xbps-rindex/add_test.sh
+++ b/tests/xbps/xbps-rindex/add_test.sh
@@ -30,6 +30,33 @@ update_body() {
atf_check_equal $rv 0
}
+revert_head() {
+ atf_set "descr" "xbps-rindex(8) -a: revert version test"
+}
+
+revert_body() {
+ mkdir -p some_repo pkg_A
+ touch pkg_A/file00
+ cd some_repo
+ xbps-create -A noarch -n foo-1.1_1 -s "foo pkg" ../pkg_A
+ atf_check_equal $? 0
+ xbps-rindex -a *.xbps
+ atf_check_equal $? 0
+ xbps-create -A noarch -n foo-1.0_1 -r "1.1_1" -s "foo pkg" ../pkg_A
+ atf_check_equal $? 0
+ xbps-rindex -a *.xbps
+ atf_check_equal $? 0
+ cd ..
+ result="$(xbps-query -r root -C empty.conf --repository=some_repo -o \*)"
+ expected="foo-1.0_1: /file00 (some_repo)"
+ rv=0
+ if [ "$result" != "$expected" ]; then
+ rv=1
+ fi
+ atf_check_equal $rv 0
+}
+
atf_init_test_cases() {
atf_add_test_case update
+ atf_add_test_case revert
}