diff --git a/meta/classes-global/base.bbclass b/meta/classes-global/base.bbclass
index 62f2814bb7..edf37a8016 100644
--- a/meta/classes-global/base.bbclass
+++ b/meta/classes-global/base.bbclass
@@ -256,6 +256,55 @@ python create_source_date_epoch_stamp() {
 }
 do_unpack[postfuncs] += "create_source_date_epoch_stamp"
 
+PRUNE_KEEP_PATHS ??= ""
+python prune_source_tree() {
+    import shutil
+
+    # Space-separated list of paths (relative to ${S}) to keep.
+    #
+    # Example:
+    #   PRUNE_KEEP_PATHS = "READMEdir/Contents.info src/xxd SECURITY.md"
+    #
+    keep_paths = (d.getVar("PRUNE_KEEP_PATHS") or "").split()
+
+    if not keep_paths:
+        bb.debug(1, "do_unpack: prune_source_tree is disabled")
+        return
+
+    s = d.getVar("S")
+    keep_abs = {os.path.abspath(os.path.join(s, p)) for p in keep_paths}
+
+    def should_keep(path):
+        path = os.path.abspath(path)
+
+        for keep in keep_abs:
+            if (
+                path == keep or # exact match with PRUNE_KEEP_PATHS
+                path.startswith(keep + os.sep) or # parent directories
+                keep.startswith(path + os.sep) # child directories
+            ):
+                return True
+        return False
+
+    for root, dirs, _ in os.walk(s, topdown=False):
+        for d in dirs:
+            path = os.path.join(root, d)
+
+            if not should_keep(path):
+                bb.debug(1, "Removing directory: %s" % path)
+                shutil.rmtree(path)
+
+    # in case there are leftover files:
+    for root, _, files in os.walk(s):
+        for f in files:
+            path = os.path.join(root, f)
+
+            if not should_keep(path):
+                bb.debug(1, "Removing file: %s" % path)
+                os.remove(path)
+}
+do_unpack[postfuncs] .= "${@" prune_source_tree" if d.getVar("PRUNE_KEEP_PATHS") else ''}"
+
 def get_source_date_epoch_value(d):
     return oe.reproducible.epochfile_read(d.getVar('SDE_FILE'), d)
 
diff --git a/meta/classes/archiver.bbclass b/meta/classes/archiver.bbclass
index 1f1ee45bd7..1a0d6f8eea 100644
--- a/meta/classes/archiver.bbclass
+++ b/meta/classes/archiver.bbclass
@@ -620,6 +620,7 @@ addtask do_deploy_archives
 do_build[recrdeptask] += "do_deploy_archives"
 do_rootfs[recrdeptask] += "do_deploy_archives"
 do_populate_sdk[recrdeptask] += "do_deploy_archives"
+do_unpack_and_patch[postfuncs] .= "${@" prune_source_tree" if d.getVar("PRUNE_KEEP_PATHS") else ''}"
 
 python () {
     # Add tasks in the correct order, specifically for linux-yocto to avoid race condition.
diff --git a/meta/recipes-support/vim/vim-xxd_9.2.bb b/meta/recipes-support/vim/vim-xxd_9.2.bb
index 5cfacbcea6..b1058e18b7 100644
--- a/meta/recipes-support/vim/vim-xxd_9.2.bb
+++ b/meta/recipes-support/vim/vim-xxd_9.2.bb
@@ -16,6 +16,10 @@ inherit update-alternatives
 
 PROVIDES += "xxd"
 
+# After unpacking, the files/dirs. outside of this list are removed
+# see prune_source_tree() in base.bbclass
+PRUNE_KEEP_PATHS = "src/xxd/Makefile src/xxd/xxd.c"
+
 do_compile() {
     cd  ${S}/src/xxd;
     oe_runmake xxd
