[PATCH 0/2] add repo diff plugin

8 views
Skip to first unread message

Tamino Larisch

unread,
Jul 21, 2026, 11:22:20 AM (13 days ago) Jul 21
to kas-...@googlegroups.com, Tamino Larisch
Hello everyone,

This patch series introduces a new `repo-diff` plugin to kas. This
plugin compares a kas configuration against or between git revisions and
outputs the differences. The idea for this plugin was provided by Felix
Moessbauer and Jan Kiska. This series partly depends on the no-color
changes [1]. I can look into how to practically test this if the
implementation is accepted.

Best regards,
Tamino

[1] https://groups.google.com/g/kas-devel/c/qN7q0yFyefI

Tamino Larisch (2):
feat: root_path option for Config initialization
feat: add repo-diff plugin

container-entrypoint | 2 +-
docs/_man/kas-plugin-repo-diff.rst | 24 +++
docs/conf.py | 2 +
docs/userguide/plugins.rst | 11 ++
kas-container | 8 +
kas/config.py | 6 +-
kas/includehandler.py | 7 +-
kas/libkas.py | 20 +++
kas/plugins/__init__.py | 2 +
kas/plugins/diff.py | 78 ++++-----
kas/plugins/repo_diff.py | 250 +++++++++++++++++++++++++++++
11 files changed, 360 insertions(+), 50 deletions(-)
create mode 100644 docs/_man/kas-plugin-repo-diff.rst
create mode 100644 kas/plugins/repo_diff.py

--
2.39.5

Tamino Larisch

unread,
Jul 21, 2026, 11:22:23 AM (13 days ago) Jul 21
to kas-...@googlegroups.com, Tamino Larisch
Adds an optional `root_path` parameter to the `Config` class
constructor. If present, this path will be used as the repository root,
and the Git command to find the repository root will not be executed.

This addresses scenarios where the Git repository root cannot be
automatically discovered from the current working directory. For
example, this is the case when processing configurations generated by
`git archive`, which will be used for the upcoming `repo-diff` plugin.

Signed-off-by: Tamino Larisch <tamino....@siemens.com>
---
kas/config.py | 6 ++++--
kas/includehandler.py | 7 ++++---
2 files changed, 8 insertions(+), 5 deletions(-)

diff --git a/kas/config.py b/kas/config.py
index 4afdbbc..d4847e3 100644
--- a/kas/config.py
+++ b/kas/config.py
@@ -42,9 +42,10 @@ class Config:
"""
Implements the kas configuration based on config files.
"""
- def __init__(self, ctx, filename, target=None, task=None):
+ def __init__(self, ctx, filename, target=None, task=None, root_path=None):
self._override_target = target
self._override_task = task
+ self._root_path = root_path
self._build_dir = ctx.build_dir
self.__config = {}
if not filename:
@@ -83,7 +84,8 @@ class Config:
the internal config dictionary.
"""
(self.__config, missing_repo_names) = \
- self.handler.get_config(repos=repo_paths)
+ self.handler.get_config(repos=repo_paths,
+ root_path=self._root_path)

return missing_repo_names

diff --git a/kas/includehandler.py b/kas/includehandler.py
index 2da4f6d..c59eeca 100644
--- a/kas/includehandler.py
+++ b/kas/includehandler.py
@@ -229,10 +229,11 @@ class IncludeHandler:
f'include {candidate} resolves outside repository {repo}')
return str(candidate)

- def get_config(self, repos=None):
+ def get_config(self, repos=None, root_path=None):
"""
Parameters:
repos -- A dictionary that maps repo names to directory paths
+ repo_root -- The repo root path of the configuration

Returns:
(config, repos)
@@ -390,9 +391,9 @@ class IncludeHandler:
self.config_files = []
missing_repos = []
self.ensure_from_same_repo()
+ root_path = root_path or self.get_top_repo_path()
for idx, configfile in enumerate(self.top_files):
- cfgs, reps = _internal_include_handler(configfile,
- self.get_top_repo_path(),
+ cfgs, reps = _internal_include_handler(configfile, root_path,
is_main_file=(idx == 0))
self.config_files.extend(cfgs)
for repo in reps:
--
2.39.5

Tamino Larisch

unread,
Jul 21, 2026, 11:22:27 AM (13 days ago) Jul 21
to kas-...@googlegroups.com, Tamino Larisch
This plugin compares a kas configuration against or between git
revisions and outputs the differences. It operates similarly to the diff
plugin, but instead of providing two configs directly, the user provides
one config and one or two revisions to compare against or between.

For getting other configurations via git, many implementations, like
using clone or stash, were considered and tested. In the end, git
archive seemed to be the most robust. Stashing away any local changes
and then checking out the required revision is too fatal when anything
goes wrong and cannot be reversed. A local git clone seemed promising,
but specifying a specific revision to clone only came in git version
2.49.0. Copying the complete, here irrelevant, history seemed to be more
work than simply getting the required files with git archive.

Signed-off-by: Tamino Larisch <tamino....@siemens.com>
---
container-entrypoint | 2 +-
docs/_man/kas-plugin-repo-diff.rst | 24 +++
docs/conf.py | 2 +
docs/userguide/plugins.rst | 11 ++
kas-container | 8 +
kas/libkas.py | 20 +++
kas/plugins/__init__.py | 2 +
kas/plugins/diff.py | 78 ++++-----
kas/plugins/repo_diff.py | 250 +++++++++++++++++++++++++++++
9 files changed, 352 insertions(+), 45 deletions(-)
create mode 100644 docs/_man/kas-plugin-repo-diff.rst
create mode 100644 kas/plugins/repo_diff.py

diff --git a/container-entrypoint b/container-entrypoint
index 9086346..a99f72e 100755
--- a/container-entrypoint
+++ b/container-entrypoint
@@ -202,7 +202,7 @@ fi

if [ -n "$1" ]; then
case "$1" in
- build|checkout|clean*|diff|dump|for-all-repos|lock|menu|purge|shell|-*)
+ build|checkout|clean*|diff|dump|for-all-repos|lock|menu|purge|repo-diff|shell|-*)
# We must restore the dir owner after every kas invocation.
# This is cheap as only the top-level dirs are changed (non recursive).
if [ "$KAS_DOCKER_ROOTLESS" = "1" ]; then
diff --git a/docs/_man/kas-plugin-repo-diff.rst b/docs/_man/kas-plugin-repo-diff.rst
new file mode 100644
index 0000000..0a98184
--- /dev/null
+++ b/docs/_man/kas-plugin-repo-diff.rst
@@ -0,0 +1,24 @@
+:orphan:
+
+kas repo-diff plugin
+====================
+
+.. argparse::
+ :module: kas.kas
+ :func: kas_get_argparser
+ :prog: kas
+ :path: repo-diff
+ :manpage:
+
+ .. automodule:: kas.plugins.repo_diff
+ :noindex:
+
+SEE ALSO
+--------
+
+:manpage:`kas-project-config(1)`,
+:manpage:`kas-checkout(1)`,
+:manpage:`kas-lock(1)`,
+:manpage:`kas-build(1)`
+
+.. include:: _kas-man-footer.inc
diff --git a/docs/conf.py b/docs/conf.py
index 96b5553..d2f920c 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -352,6 +352,8 @@ man_pages = [
[author], 1),
('_man/kas-plugin-purge', 'kas-purge', 'kas purge command',
[author], 1),
+ ('_man/kas-plugin-repo-diff', 'kas-repo-diff', 'kas repo-diff plugin',
+ [author], 1),
('_man/kas-plugin-shell', 'kas-shell', 'kas shell plugin',
[author], 1),
('_man/kas-project-config',
diff --git a/docs/userguide/plugins.rst b/docs/userguide/plugins.rst
index f486e3d..583b33d 100644
--- a/docs/userguide/plugins.rst
+++ b/docs/userguide/plugins.rst
@@ -130,6 +130,17 @@ typically provides a single command.
:prog: kas
:path: purge

+``repo-diff`` plugin
+--------------------
+
+.. automodule:: kas.plugins.repo_diff
+
+.. argparse::
+ :module: kas.kas
+ :func: kas_get_argparser
+ :prog: kas
+ :path: repo-diff
+
``shell`` plugin
----------------

diff --git a/kas-container b/kas-container
index aa1839d..a8ff9fd 100755
--- a/kas-container
+++ b/kas-container
@@ -43,6 +43,7 @@ usage()
printf "%b" "Usage: ${SELF} [OPTIONS] { build | shell } [KASOPTIONS] [KASFILE]\n"
printf "%b" " ${SELF} [OPTIONS] { checkout | dump | lock } [KASOPTIONS] [KASFILE]\n"
printf "%b" " ${SELF} [OPTIONS] { diff } [KASOPTIONS] config1 config2\n"
+ printf "%b" " ${SELF} [OPTIONS] { repo-diff } [KASOPTIONS] revision config\n"
printf "%b" " ${SELF} [OPTIONS] for-all-repos [KASOPTIONS] [KASFILE] COMMAND\n"
printf "%b" " ${SELF} [OPTIONS] { clean | cleansstate | cleanall | purge} [KASFILE]\n"
printf "%b" " ${SELF} [OPTIONS] menu [KCONFIG]\n"
@@ -50,6 +51,7 @@ usage()
printf "%b" "build\t\t\tCheck out repositories and build target.\n"
printf "%b" "checkout\t\tCheck out repositories but do not build.\n"
printf "%b" "diff\t\t\tCompare two kas configurations.\n"
+ printf "%b" "repo-diff\t\tCompare a kas configuration across two revisions.\n"
printf "%b" "dump\t\t\tCheck out repositories and write flat version\n"
printf "%b" " \t\t\tof config to stdout.\n"
printf "%b" "lock\t\t\tCreate and update kas project lockfiles.\n"
@@ -437,6 +439,12 @@ while [ $# -gt 0 ]; do
shift 1
break
;;
+ repo-diff)
+ KAS_REPO_MOUNT_OPT_DEFAULT="ro"
+ KAS_CMD=$1
+ shift 1
+ break
+ ;;
dump)
if printf '%s\0' "$@" | grep -xqz -- '--inplace\|-i'; then
KAS_REPO_MOUNT_OPT_DEFAULT="rw"
diff --git a/kas/libkas.py b/kas/libkas.py
index 5aaf99b..fb421d2 100644
--- a/kas/libkas.py
+++ b/kas/libkas.py
@@ -667,6 +667,26 @@ def setup_parser_config_arg(parser):
nargs='?')


+def setup_parser_diff_args(parser):
+ parser.add_argument('--format',
+ choices=['json', 'text'],
+ default='text',
+ help='Diff output format (default: text)')
+ parser.add_argument('--oneline',
+ action='store_true',
+ help='Use git oneline output for differing commits.')
+ parser.add_argument('--commit-only',
+ action='store_true',
+ help='This will not display the differences in the '
+ 'kas configurations; it will only list commits '
+ 'resulting from different repository revisions.')
+ parser.add_argument('--content-only',
+ action='store_true',
+ help='This will only display the differences in the '
+ 'kas configurations and will not include '
+ 'repository differences.')
+
+
def setup_parser_preserve_env_arg(parser):
parser.add_argument('-E', '--preserve-env',
help='Keep current user environment block',
diff --git a/kas/plugins/__init__.py b/kas/plugins/__init__.py
index 1243d44..36acefe 100644
--- a/kas/plugins/__init__.py
+++ b/kas/plugins/__init__.py
@@ -47,6 +47,7 @@ def load():
from . import for_all_repos
from . import lock
from . import menu
+ from . import repo_diff
from . import shell

register_plugins(build)
@@ -57,6 +58,7 @@ def load():
register_plugins(for_all_repos)
register_plugins(lock)
register_plugins(menu)
+ register_plugins(repo_diff)
register_plugins(shell)


diff --git a/kas/plugins/diff.py b/kas/plugins/diff.py
index 3eb33d1..0401aff 100644
--- a/kas/plugins/diff.py
+++ b/kas/plugins/diff.py
@@ -40,7 +40,7 @@ import difflib
from kas.context import create_global_context
from kas.config import Config
from kas.libcmds import Macro
-from kas.libkas import setup_parser_common_args
+from kas.libkas import setup_parser_common_args, setup_parser_diff_args

__license__ = 'MIT'
__copyright__ = 'Copyright (c) Siemens, 2025'
@@ -64,25 +64,8 @@ class Diff:
help='The first config file to be compared.')
parser.add_argument('config2',
help='The second config file to be compared.')
- parser.add_argument('--format',
- choices=['json', 'text'],
- default='text',
- help='Diff output format (default: text)')
- parser.add_argument('--oneline',
- action='store_true',
- help='Use git oneline output for differing '
- 'commits.')
- parser.add_argument('--commit-only',
- action='store_true',
- help='This will not display the differences in '
- 'the kas configurations; it will only list '
- 'commits resulting from different '
- 'repository revisions.')
- parser.add_argument('--content-only',
- action='store_true',
- help='This will only display the differences in '
- 'the kas configurations and will not '
- 'include repository differences.')
+
+ setup_parser_diff_args(parser)

@staticmethod
def compare_dicts(dict1, dict2, parent_key=''):
@@ -203,30 +186,8 @@ class Diff:
if key != list(vcs_dict.keys())[-1]:
print("---")

- def run(self, args):
- args.skip += [
- 'setup_environ',
- 'write_bbconfig'
- ]
- ctx = create_global_context(args)
- ctx.config = Config(ctx, args.config1)
- macro = Macro()
- macro.run(ctx, args.skip)
- config1 = ctx.config.get_config(remove_includes=True,
- apply_overrides=True)
- repos1 = ctx.config.get_repos()
-
- args.skip += [
- 'setup_dir',
- 'setup_home',
- 'setup_ssh_agent'
- ]
- ctx.config = Config(ctx, args.config2)
- macro.run(ctx, args.skip)
- config2 = ctx.config.get_config(remove_includes=True,
- apply_overrides=True)
- repos2 = ctx.config.get_repos()
-
+ @staticmethod
+ def create_diff_output(config1, config2, repos1, repos2):
diff = Diff.compare_dicts(config1, config2)
diff_output = {}
if diff:
@@ -258,6 +219,35 @@ class Diff:
repo_diff = each_repo.diff(commit1, commit2)
if len(repo_diff.get(each_repo.name)) > 0:
vcs_dict.update(repo_diff)
+ return diff_output, vcs_dict
+
+ def run(self, args):
+ args.skip += [
+ 'setup_environ',
+ 'write_bbconfig'
+ ]
+ ctx = create_global_context(args)
+ ctx.config = Config(ctx, args.config1)
+ macro = Macro()
+ macro.run(ctx, args.skip)
+ config1 = ctx.config.get_config(remove_includes=True,
+ apply_overrides=True)
+ repos1 = ctx.config.get_repos()
+
+ args.skip += [
+ 'setup_dir',
+ 'setup_home',
+ 'setup_ssh_agent'
+ ]
+ ctx.config = Config(ctx, args.config2)
+ macro.run(ctx, args.skip)
+ config2 = ctx.config.get_config(remove_includes=True,
+ apply_overrides=True)
+ repos2 = ctx.config.get_repos()
+
+ diff_output, vcs_dict = Diff.create_diff_output(config1, config2,
+ repos1, repos2)
+
if vcs_dict:
diff_output['vcs'] = vcs_dict
if args.format == 'json':
diff --git a/kas/plugins/repo_diff.py b/kas/plugins/repo_diff.py
new file mode 100644
index 0000000..e56db09
--- /dev/null
+++ b/kas/plugins/repo_diff.py
@@ -0,0 +1,250 @@
+# kas - setup tool for bitbake based projects
+#
+# Copyright (c) Siemens, 2026
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+"""
+This plugin implements the ``kas repo-diff`` command.
+
+This plugin compares a kas configuration against or between Git revisions
+and outputs the differences. The diff includes both content differences in
+the configuration files and repository differences if commit IDs or tags
+have changed.
+
+It accepts the same dotted revision syntax as ``git diff``:
+``kas repo-diff [revision|revision1..revision2|revision1...revision2] config``.
+
+Examples:
+ - Compare current state with 5 revisions ago:
+ ``kas repo-diff @~5 path/to/config``
+ - Show differences between commits abc and def:
+ ``kas repo-diff abc..def path/to/config``
+ - Show differences between commits abc and def starting at the
+ last common ancestor of abc and def:
+ ``kas repo-diff abc...def path/to/config``
+
+Additionally, you can use the ``--format json`` option to output
+the diff in JSON format.
+
+.. note::
+ The text output of the plugin should not considered to be stable. If
+ stable output is needed, use a machine readable format like json.
+"""
+
+import json
+import logging
+import os
+import re
+import subprocess
+import tarfile
+import tempfile
+from kas.context import create_global_context
+from kas.config import Config
+from kas.kasusererror import KasUserError
+from kas.libcmds import Macro
+from kas.libkas import run_cmd, setup_parser_common_args
+from kas.libkas import setup_parser_diff_args
+from kas.plugins.diff import Diff
+
+__license__ = "MIT"
+__copyright__ = "Copyright (c) Siemens, 2026"
+
+
+class RepoDiff:
+ """
+ kas plugin to compute diff of a kas configuration against or between Git
+ revisions.
+ """
+
+ name = "repo-diff"
+ helpmsg = "Compare a kas configuration against or between Git revisions."
+
+ @classmethod
+ def setup_parser(cls, parser):
+ setup_parser_common_args(parser)
+
+ parser.add_argument(
+ "revision",
+ help="Git revision(s) to compare against. Can "
+ "be a single revision (e.g., HEAD~1, v1.0, main) "
+ "or a range (e.g., abc...def, HEAD~3..@^).",
+ )
+ parser.add_argument("config", help="The config file to be compared.")
+
+ setup_parser_diff_args(parser)
+
+ def ensure_git_repo(self):
+ (retc, output) = run_cmd(
+ ['git', 'rev-parse', '--is-inside-work-tree'], cwd=self.path
+ )
+ if retc != 0 or output.strip() != "true":
+ raise KasUserError(f"Not inside a git repository: {output}")
+
+ def git_verify_revision(self, revision):
+ (retc, output) = run_cmd(
+ ['git', 'rev-parse', '--verify', revision], cwd=self.path
+ )
+ if retc != 0:
+ raise KasUserError(f"Invalid revision {revision}: {output}")
+
+ def git_merge_base(self, revision1, revision2):
+ (retc, output) = run_cmd(
+ ['git', 'merge-base', revision1, revision2], cwd=self.path
+ )
+ if retc != 0:
+ raise RuntimeError(
+ f"Failed to get merge base of {revision1} and {revision2}: "
+ f"{output}"
+ )
+ return output.strip()
+
+ def resolve_config(self, ctx, args, macro, config_path=None,
+ root_path=None):
+ """
+ Resolves the kas configuration and its repositories.
+ If config_filepath is provided, it uses that path; otherwise, it uses
+ args.config.
+ """
+ ctx.config = Config(ctx, config_path or args.config,
+ root_path=root_path)
+ macro.run(ctx, args.skip)
+ config = ctx.config.get_config(remove_includes=True,
+ apply_overrides=True)
+ repos = ctx.config.get_repos()
+ return config, repos
+
+ def resolve_config_path(self, folder, repo_root, config):
+ """
+ Resolves a configuration path to be relative to the repo_root,
+ then joins it with the provided folder.
+ """
+ if os.path.isabs(config):
+ relative_config = os.path.relpath(config, repo_root)
+ else:
+ absolute_config = os.path.join(self.path, config)
+ relative_config = os.path.relpath(absolute_config, repo_root)
+ return os.path.join(folder, relative_config)
+
+ def resolve_revision_config(self, revision, ctx, args, macro):
+ """
+ Resolves the kas configuration for a specific Git revision.
+ This involves archiving the repository at the given revision,
+ extracting it to a temporary directory and then resolving the
+ config file from that cloned state.
+ """
+ (retc, output) = run_cmd(['git', 'rev-parse', '--show-toplevel'],
+ cwd=self.path)
+ if retc != 0:
+ raise RuntimeError(f"Failed to get top-level directory: {output}")
+ repo_root = output.strip()
+
+ with tempfile.TemporaryDirectory(prefix="kas-repo-diff-") as tmp_dir:
+ logging.info(f"Archiving repo {self.path} to checkout {revision}")
+ try:
+ with subprocess.Popen(
+ ['git', 'archive', revision],
+ cwd=repo_root,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ ) as archive_process:
+ with tarfile.open(fileobj=archive_process.stdout,
+ mode='r|') as tar:
+ tar.extractall(path=tmp_dir)
+ _, stderr = archive_process.communicate()
+ if archive_process.returncode != 0:
+ raise RuntimeError(f"Git archive failed: {stderr}")
+ except Exception as e:
+ raise e
+
+ config_path = ':'.join(
+ [self.resolve_config_path(tmp_dir, repo_root, config)
+ for config in args.config.split(':')]
+ )
+ return self.resolve_config(ctx, args, macro, config_path, tmp_dir)
+
+ def run(self, args):
+ ctx = create_global_context(args)
+ self.path = ctx.kas_work_dir
+ self.ensure_git_repo()
+
+ if ".." in args.revision:
+ revisions = re.split(r"\.{2,3}", args.revision, maxsplit=1)
+ if (
+ len(revisions) != 2
+ or not revisions[0].strip()
+ or not revisions[1].strip()
+ ):
+ raise KasUserError(
+ f"Invalid revision format: '{args.revision}'. Expected "
+ "format: 'revision', 'revision1..revision2', or "
+ "'revision1...revision2'."
+ )
+ revision1, revision2 = revisions
+ self.git_verify_revision(revision1)
+ self.git_verify_revision(revision2)
+ logging.info(f"Comparing revisions {revision1} and {revision2}")
+ if "..." in args.revision:
+ revision1 = self.git_merge_base(revision1, revision2)
+ logging.info(f"Using merge base {revision1} for 3 dot diff")
+ else:
+ revision1 = args.revision
+ revision2 = None
+ self.git_verify_revision(revision1)
+
+ args.skip += [
+ 'setup_environ',
+ 'write_bbconfig'
+ ]
+ macro = Macro()
+
+ config1, repos1 = self.resolve_revision_config(revision1, ctx, args,
+ macro)
+
+ args.skip += [
+ 'setup_dir',
+ 'setup_home',
+ 'setup_ssh_agent'
+ ]
+
+ if not revision2:
+ config2, repos2 = self.resolve_config(ctx, args, macro)
+ else:
+ config2, repos2 = self.resolve_revision_config(revision2, ctx,
+ args, macro)
+
+ diff_output, vcs_dict = Diff.create_diff_output(
+ config1, config2, repos1, repos2
+ )
+
+ if vcs_dict:
+ diff_output["vcs"] = vcs_dict
+ if args.format == "json":
+ print(json.dumps(diff_output, indent=4))
+ else:
+ config1_label = f"{args.config}@{revision1}"
+ config2_label = args.config
+ if revision2:
+ config2_label += f"@{revision2}"
+ Diff.formatting_diff_output(
+ config1_label, config2_label, diff_output, args.oneline,
+ args.no_color, args.commit_only, args.content_only,
+ )
+
+
+__KAS_PLUGINS__ = [RepoDiff]
--
2.39.5

Jan Kiszka

unread,
Jul 22, 2026, 6:49:15 AM (12 days ago) Jul 22
to Tamino Larisch, kas-...@googlegroups.com
It's the top repo root path, to be more precise here.

>
> Returns:
> (config, repos)
> @@ -390,9 +391,9 @@ class IncludeHandler:
> self.config_files = []
> missing_repos = []
> self.ensure_from_same_repo()
> + root_path = root_path or self.get_top_repo_path()
> for idx, configfile in enumerate(self.top_files):
> - cfgs, reps = _internal_include_handler(configfile,
> - self.get_top_repo_path(),
> + cfgs, reps = _internal_include_handler(configfile, root_path,
> is_main_file=(idx == 0))
> self.config_files.extend(cfgs)
> for repo in reps:

Jan

--
Siemens AG, Foundational Technologies
Linux Expert Center

Jan Kiszka

unread,
Jul 22, 2026, 6:54:14 AM (12 days ago) Jul 22
to Tamino Larisch, kas-...@googlegroups.com
On 21.07.26 17:22, 'Tamino Larisch' via kas-devel wrote:
> This plugin compares a kas configuration against or between git
> revisions and outputs the differences. It operates similarly to the diff
> plugin, but instead of providing two configs directly, the user provides
> one config and one or two revisions to compare against or between.
>
> For getting other configurations via git, many implementations, like
> using clone or stash, were considered and tested. In the end, git
> archive seemed to be the most robust. Stashing away any local changes
> and then checking out the required revision is too fatal when anything
> goes wrong and cannot be reversed. A local git clone seemed promising,
> but specifying a specific revision to clone only came in git version
> 2.49.0. Copying the complete, here irrelevant, history seemed to be more
> work than simply getting the required files with git archive.
>

...

> +This plugin implements the ``kas repo-diff`` command.
> +
> +This plugin compares a kas configuration against or between Git revisions
> +and outputs the differences. The diff includes both content differences in
> +the configuration files and repository differences if commit IDs or tags
> +have changed.
> +
> +It accepts the same dotted revision syntax as ``git diff``:
> +``kas repo-diff [revision|revision1..revision2|revision1...revision2] config``.
> +
> +Examples:
> + - Compare current state with 5 revisions ago:
> + ``kas repo-diff @~5 path/to/config``
> + - Show differences between commits abc and def:
> + ``kas repo-diff abc..def path/to/config``
> + - Show differences between commits abc and def starting at the
> + last common ancestor of abc and def:
> + ``kas repo-diff abc...def path/to/config``
> +

Why can't this be an argument variation of the pre-existing kas command?
Is it hard to detect whether positional arg1 is a config file or a
revision range?

I would also like to have this supported, if possible somehow:

kas diff [config]

with config either being explicitly provided or picked up from
.config.yaml when left out. The range would be the repo revision of the
currently checked-in state of the configuration up to the dirty state in
the working directory.
Reply all
Reply to author
Forward
0 new messages