commit: 735cc99067988a56c0e98ab1fdec55fc5d1014b1
Author: Fabian Groffen <grobian <AT> gentoo <DOT> org>
AuthorDate: Fri Feb 6 15:08:23 2026 +0000
Commit: Fabian Groffen <grobian <AT> gentoo <DOT> org>
CommitDate: Fri Feb 6 15:08:23 2026 +0000
URL: https://gitweb.gentoo.org/proj/portage-utils.git/commit/?id=735cc990
libq/tree: add tree_merge function
Signed-off-by: Fabian Groffen <grobian <AT> gentoo.org>
libq/tree.c | 135 ++++++++++++++++++++++++++++++++++++++++++++++++++----------
libq/tree.h | 27 +++++++-----
2 files changed, 130 insertions(+), 32 deletions(-)
diff --git a/libq/tree.c b/libq/tree.c
index e6f6f3d8..bb52fcfb 100644
--- a/libq/tree.c
+++ b/libq/tree.c
@@ -83,6 +83,7 @@ struct tree_ {
char *path;
char *repo;
array *cats; /* list of tree_cat_ctx pointers */
+ array *srctrees; /* in case of TREE_MERGED */
int portroot_fd;
enum {
TREE_UNSET = 0,
@@ -91,6 +92,7 @@ struct tree_ {
TREE_PACKAGES,
TREE_BINPKGS,
TREE_GTREE,
+ TREE_MERGED,
} type;
bool cats_complete:1;
};
@@ -565,6 +567,57 @@ tree_ctx *tree_new
return ret;
}
+/* produces a new tree that is the merger of the trees tree1 and tree2
+ * NOTES:
+ * - the trees given should not be freed for as long as the merged tree
+ * is around (not closed)
+ * - the individual trees (tree1, tree2) may be used outside of the
+ * returned tree (except being freed)
+ * - no attempt is made to check for duplicates, the caller should merge
+ * trees sensibly! */
+tree_ctx *tree_merge
+(
+ tree_ctx *tree1,
+ tree_ctx *tree2
+)
+{
+ tree_ctx *ret;
+
+ if (tree1 == NULL ||
+ tree2 == NULL)
+ return NULL;
+
+ if (tree1->type == TREE_MERGED)
+ {
+ ret = tree1;
+ tree1 = NULL;
+ }
+ if (tree2->type == TREE_MERGED)
+ {
+ ret = tree2;
+ tree2 = NULL;
+ }
+
+ if (tree1 == NULL &&
+ tree2 == NULL)
+ return NULL; /* can't merge two merged trees for now */
+
+ if (ret == NULL)
+ {
+ ret = xzalloc(sizeof(*ret));
+ ret->type = TREE_MERGED;
+ ret->srctrees = array_new();
+ ret->portroot_fd = -1;
+ }
+
+ if (tree1 != NULL)
+ array_append(ret->srctrees, tree1);
+ if (tree2 != NULL)
+ array_append(ret->srctrees, tree2);
+
+ return ret;
+}
+
/* helper to free up resources held by a package */
static void tree_pkg_close
(
@@ -1991,6 +2044,10 @@ int tree_foreach_pkg
int ret = 0;
bool filtercat = false;
+ if (tree->type == TREE_MERGED)
+ err("programmer error: "
+ "cannot call tree_foreach_pkg on a tree of type MERGED");
+
/* if we have a query with category, see if we have it already */
if (query != NULL &&
query->CATEGORY != NULL)
@@ -2410,7 +2467,7 @@ array *tree_match_atom
/* a note on the flags that we control the output results with:
* - LATEST: only return the best (latest version) match for each PN
* - FIRST: stop searching after the first match (e.g. atom without
- * category), implies LATEST
+ * category), implies LATEST on non-MERGED trees
* - VIRTUAL: include the virtual category in results
* - ACCT: include the acct-user and acct-group categories in results
* - SORT: return the results in sorted order, this is implied by
@@ -2424,30 +2481,66 @@ array *tree_match_atom
flags & TREE_MATCH_SORT )
sorted = true;
- tree_foreach_pkg(tree, tree_match_atom_cb, ret, sorted, atom);
-
- if (!(flags & TREE_MATCH_VIRTUAL))
+ /* handle merged tree separately */
+ if (tree->type == TREE_MERGED)
{
- array_for_each_rev(ret, n, w)
- if (strcmp(tree_pkg_get_cat_name(w), "virtual") == 0)
- array_remove(ret, n);
- }
+ tree_ctx *stree;
+ array *match;
+
+ /* behaviour of flags is in line here, but because the context is
+ * slightly different:
+ * - LATEST: find the highest version in all of the trees
+ * - FIRST: return the pkg from the first tree with a match
+ * this allows to respect the order (e.g. VDB -> BINPKG -> TREE) of
+ * preference without any extra checks from the caller */
+ array_for_each(tree->srctrees, n, stree)
+ {
+ match = tree_match_atom(stree, atom, flags);
+ if (array_cnt(match) > 0)
+ {
+ array_move(ret, match);
+ array_free(match);
+ if (flags & TREE_MATCH_FIRST)
+ break;
+ }
+ else
+ {
+ array_free(match);
+ }
+ }
- if (!(flags & TREE_MATCH_ACCT))
- {
- array_for_each_rev(ret, n, w)
- if (strncmp("acct-", tree_pkg_get_cat_name(w), sizeof("acct-") - 1) == 0)
- array_remove(ret, n);
+ /* need to re-sort because we pushed results from multiple trees */
+ if (sorted)
+ array_sort(ret, tree_pkg_compar);
}
-
- if (flags & TREE_MATCH_FIRST &&
- array_cnt(ret) > 1)
+ else
{
- /* a bit crude, we can optimise this later */
- array *new = array_new();
- array_append(new, array_get(ret, 0));
- array_free(ret);
- ret = new;
+ tree_foreach_pkg(tree, tree_match_atom_cb, ret, sorted, atom);
+
+ if (!(flags & TREE_MATCH_VIRTUAL))
+ {
+ array_for_each_rev(ret, n, w)
+ if (strcmp(tree_pkg_get_cat_name(w), "virtual") == 0)
+ array_remove(ret, n);
+ }
+
+ if (!(flags & TREE_MATCH_ACCT))
+ {
+ array_for_each_rev(ret, n, w)
+ if (strncmp("acct-",
+ tree_pkg_get_cat_name(w), sizeof("acct-") - 1) == 0)
+ array_remove(ret, n);
+ }
+
+ if (flags & TREE_MATCH_FIRST &&
+ array_cnt(ret) > 1)
+ {
+ /* a bit crude, we can optimise this later */
+ array *new = array_new();
+ array_append(new, array_get(ret, 0));
+ array_free(ret);
+ ret = new;
+ }
}
if (flags & TREE_MATCH_LATEST)
diff --git a/libq/tree.h b/libq/tree.h
index 82868bf6..838a8756 100644
--- a/libq/tree.h
+++ b/libq/tree.h
@@ -78,30 +78,35 @@ enum tree_pkg_meta_keys {
TREE_META_MAX_KEYS
};
-tree_ctx *tree_new(const char *portroot, const char *path, enum tree_open_type
type, bool quiet);
-void tree_close(tree_ctx *tree);
-
-int tree_foreach_pkg(tree_ctx *tree, tree_pkg_cb callback, void *priv, bool
sorted, const atom_ctx *query);
-
-array *tree_match_atom(tree_ctx *tree, const atom_ctx *query, int flags);
+tree_ctx *tree_new(const char *portroot, const char *path,
+ enum tree_open_type type, bool quiet);
+tree_ctx *tree_merge(tree_ctx *tree1, tree_ctx *tree2);
+void tree_close(tree_ctx *tree);
+
+int tree_foreach_pkg(tree_ctx *tree, tree_pkg_cb callback,
+ void *priv, bool sorted,
+ const atom_ctx *query);
+array *tree_match_atom(tree_ctx *tree, const atom_ctx *query,
+ int flags);
#define TREE_MATCH_LATEST (1<<3)
#define TREE_MATCH_FIRST (1<<4)
#define TREE_MATCH_VIRTUAL (1<<5)
#define TREE_MATCH_ACCT (1<<6)
#define TREE_MATCH_SORT (1<<7)
#define TREE_MATCH_DEFAULT (TREE_MATCH_VIRTUAL | \
- TREE_MATCH_ACCT | \
- TREE_MATCH_SORT)
+ TREE_MATCH_ACCT | \
+ TREE_MATCH_SORT )
-tree_metadata_xml *tree_pkg_metadata(tree_pkg_ctx *pkg_ctx);
-void tree_close_metadata(tree_metadata_xml *meta_ctx);
+tree_metadata_xml *tree_pkg_metadata(tree_pkg_ctx *pkg_ctx);
+void tree_close_metadata(tree_metadata_xml *meta_ctx);
char *tree_get_repo_name(tree_ctx *tree);
char *tree_get_path(tree_ctx *tree);
int tree_get_portroot_fd(tree_ctx *tree);
enum tree_open_type tree_get_treetype(tree_ctx *tree);
-char *tree_pkg_meta(tree_pkg_ctx *pkg, enum tree_pkg_meta_keys
key);
+char *tree_pkg_meta(tree_pkg_ctx *pkg,
+ enum tree_pkg_meta_keys key);
atom_ctx *tree_pkg_atom(tree_pkg_ctx *pkg, bool full);
tree_ctx *tree_pkg_get_tree(tree_pkg_ctx *pkg);
char *tree_pkg_get_cat_name(tree_pkg_ctx *pkg);