[PATCH v3 0/3] Add `tag` refspec property to repo

82 views
Skip to first unread message

Félix Piédallu

unread,
Oct 12, 2023, 10:17:55 AM10/12/23
to kas-...@googlegroups.com, Félix Piédallu

This patchset adds support for `tag` property to repo, tu replace the usage of `refspec`.

As tags are mutable, users can also specify a commit to ensure reproductibility.
If commit is not specified, a small warning is issued, informing users of the possible
unsafeness of using a mutable refspec.

This is a follow-up to https://groups.google.com/g/kas-devel/c/kPPoXeG6CRU/m/n2HTAzJJAAAJ.

> MercurialRepo.contains_refspec_cmd
> I'm not a HG expert, but does this actually work with tags? Has this been tested?

Yes I tested contains_refspec_cmd / hg log, and it works fine.

> MercurialRepo.resolve_tag_cmd
> Tags and branches are not the same. Please briefly explain why this
> works and also resolves correctly in case identical branch and tag
> names exist with different SHAs (which is discouraged but supported in
> git).

You are completely right. I did not have this case (hopefully rare...) in mind.

Your review also helped me find a bug in the existing hg-specific code:
If the repository contains a tag and a branch with the same name,
resolve_branch_cmd / hg identify will return the tag commit ID instead of the branch.
The first patch in this new series fixes this.

> please also add a test where a branch and tag with identical name but
> different SHA exists.

This is now done with two tests:
* a `3.0.1` branch on master HEAD and checking that `tag: 3.0.1` does not resolve to it
* a `master` tag on an old commit and checking that `branch: master` does not resolve to it

Félix Piédallu (3):
Fix MercurialRepo.resolve_branch_cmd
Add "tag" property to repo, to replace usage of refspec.
Add tests for tag property of repo

docs/userguide.rst | 4 ++
kas/libkas.py | 4 +-
kas/plugins/dump.py | 8 +--
kas/plugins/for_all_repos.py | 4 ++
kas/repos.py | 108 +++++++++++++++++++++++++----------
kas/schema-kas.json | 3 +
tests/test_refspec.py | 91 +++++++++++++++++++++++++++++
tests/test_refspec/test.yml | 9 +++
tests/test_refspec/test2.yml | 8 +++
tests/test_refspec/test3.yml | 4 ++
tests/test_refspec/test7.yml | 10 ++++
tests/test_refspec/test8.yml | 10 ++++
12 files changed, 226 insertions(+), 37 deletions(-)
create mode 100644 tests/test_refspec/test7.yml
create mode 100644 tests/test_refspec/test8.yml


base-commit: f4fb559db391fec727423ff4a6edaf9b94cf43e1
--
2.42.0

Félix Piédallu

unread,
Oct 12, 2023, 10:33:59 AM10/12/23
to kas-...@googlegroups.com, Félix Piédallu
By default, if a tag exists with the same name as a branch, hg identify will
prefer the tag's commit id.
Using hg revset pattern `limit(heads(branch(self.branch)))` ensures we get the
branch's head commit id.

Signed-off-by: Félix Piédallu <fe...@piedallu.me>
---
kas/repos.py | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/kas/repos.py b/kas/repos.py
index 95038c1..292a512 100644
--- a/kas/repos.py
+++ b/kas/repos.py
@@ -535,8 +535,11 @@ class MercurialRepo(RepoImpl):
return ['hg', 'diff']

def resolve_branch_cmd(self):
- return ['hg', 'identify', '--id', '-r', self.branch or self.refspec,
- 'default']
+ if self.branch:
+ return ['hg', 'identify', '--id', '-r',
+ 'limit(heads(branch({})))'.format(self.branch)]
+ else:
+ return ['hg', 'identify', '--id', '-r', self.refspec]

def checkout_cmd(self, desired_ref, is_branch):
cmd = ['hg', 'checkout', desired_ref]
--
2.42.0

Félix Piédallu

unread,
Oct 12, 2023, 10:53:59 AM10/12/23
to kas-...@googlegroups.com, Félix Piédallu
* test_refspec_switch
* test mix of refspec and tag
* test if kas fails if commit and tag do not match
* test if kas warns about tag without commit being unsafe
* test if kas does not mixes up tag and branch with the same name

Signed-off-by: Félix Piédallu <fe...@piedallu.me>
---
tests/test_refspec.py | 91 ++++++++++++++++++++++++++++++++++++
tests/test_refspec/test.yml | 9 ++++
tests/test_refspec/test2.yml | 8 ++++
tests/test_refspec/test3.yml | 4 ++
tests/test_refspec/test7.yml | 10 ++++
tests/test_refspec/test8.yml | 10 ++++
6 files changed, 132 insertions(+)
create mode 100644 tests/test_refspec/test7.yml
create mode 100644 tests/test_refspec/test8.yml

diff --git a/tests/test_refspec.py b/tests/test_refspec.py
index c1dfcde..83ff6e5 100644
--- a/tests/test_refspec.py
+++ b/tests/test_refspec.py
@@ -50,6 +50,10 @@ def test_refspec_switch(changedir, tmpdir):
fail=False, liveupdate=False)
assert rc == 0
assert output.strip() == 'refs/heads/master'
+ (rc, output) = run_cmd(['git', 'tag', '--points-at', 'HEAD'], cwd='kas3',
+ fail=False, liveupdate=False)
+ assert rc == 0
+ assert output.strip() == '3.0.1'

kas.kas(['shell', 'test2.yml', '-c', 'true'])
(rc, output) = run_cmd(['git', 'symbolic-ref', '-q', 'HEAD'], cwd='kas',
@@ -64,6 +68,14 @@ def test_refspec_switch(changedir, tmpdir):
fail=False, liveupdate=False)
assert rc == 0
assert output.strip() == '907816a5c4094b59a36aec12226e71c461c05b77'
+ (rc, output) = run_cmd(['git', 'symbolic-ref', '-q', 'HEAD'], cwd='kas3',
+ fail=False, liveupdate=False)
+ assert rc == 0
+ assert output.strip() == 'refs/heads/master'
+ (rc, output) = run_cmd(['git', 'tag', '--points-at', 'HEAD'], cwd='kas4',
+ fail=False, liveupdate=False)
+ assert rc == 0
+ assert output.strip() == '2.6.3'


def test_refspec_absolute(changedir, tmpdir):
@@ -87,6 +99,10 @@ def test_refspec_absolute(changedir, tmpdir):
cwd='kas_rel', fail=False, liveupdate=False)
assert rc == 0
assert output_kas_abs.strip() == output_kas_rel.strip()
+ (rc, output) = run_cmd(['git', 'tag', '--points-at', 'HEAD'],
+ cwd='kas_tag_abs', fail=False, liveupdate=False)
+ assert rc == 0
+ assert output.strip() == '3.0.1'


def test_url_no_refspec(changedir, tmpdir):
@@ -111,6 +127,81 @@ def test_commit_refspec_mix(changedir, tmpdir):
kas.kas(['shell', 'test5.yml', '-c', 'true'])
with pytest.raises(RepoRefError):
kas.kas(['shell', 'test6.yml', '-c', 'true'])
+ with pytest.raises(RepoRefError):
+ kas.kas(['shell', 'test7.yml', '-c', 'true'])
+
+
+def test_tag_commit_do_not_match(changedir, tmpdir):
+ """
+ Test that giving tag and commit that do not match raises an error.
+ """
+ tdir = str(tmpdir / 'test_tag_commit_do_not_match')
+ shutil.copytree('tests/test_refspec', tdir)
+ os.chdir(tdir)
+ with pytest.raises(RepoRefError):
+ kas.kas(['shell', 'test8.yml', '-c', 'true'])
+
+
+def test_unsafe_tag_warning(capsys, changedir, tmpdir):
+ """
+ Test that using tag without commit issues a warning, but only once.
+ """
+ tdir = str(tmpdir / 'test_unsafe_tag_warning')
+ shutil.copytree('tests/test_refspec', tdir)
+ os.chdir(tdir)
+ # needs to be reset in case other tests ran before
+ Repo.__no_commit_tag_warned__ = []
+ kas.kas(['shell', 'test2.yml', '-c', 'true'])
+ assert capsys.readouterr().err.count(
+ 'Using tag without commit for repository "kas4" is unsafe as tags '
+ 'are mutable.') == 1
+
+
+def test_tag_branch_same_name(capsys, changedir, tmpdir):
+ """
+ Test that kas uses the tag if a branch has the same name as the tag.
+ """
+ tdir = str(tmpdir / 'test_tag_branch_same_name')
+ shutil.copytree('tests/test_refspec', tdir)
+ os.chdir(tdir)
+
+ # Checkout the repositories
+ kas.kas(['shell', 'test.yml', '-c', 'true'])
+
+ # In kas3: create a branch named "3.0.1" on master HEAD
+ # A tag named "3.0.1" already exists on an old commit from 2022
+ (rc, output) = run_cmd(['git', 'switch', 'master'], cwd='kas3',
+ fail=False, liveupdate=False)
+ assert rc == 0
+ (rc, output) = run_cmd(['git', 'branch', '3.0.1'], cwd='kas3',
+ fail=False, liveupdate=False)
+ assert rc == 0
+
+ # In kas4: create a tag named "master" on existing 2.6.3 tag
+ (rc, output) = run_cmd(['git', 'checkout', '2.6.3'], cwd='kas4',
+ fail=False, liveupdate=False)
+ assert rc == 0
+ (rc, output) = run_cmd(['git', 'tag', 'master'], cwd='kas4',
+ fail=False, liveupdate=False)
+ assert rc == 0
+
+ # Checkout the repositories again
+ kas.kas(['shell', 'test.yml', '-c', 'true'])
+
+ # Check the commit hashes
+ (rc, output) = run_cmd(['git', 'rev-parse', 'HEAD'], cwd='kas3',
+ fail=False, liveupdate=False)
+ assert rc == 0
+ assert output.strip() == '229310958b17dc2b505b789c1cc1d0e2fddccc44'
+
+ (rc, output) = run_cmd(['git', 'rev-parse', 'HEAD'], cwd='kas4',
+ fail=False, liveupdate=False)
+ assert rc == 0
+
+ (rc, output2) = run_cmd(['git', 'rev-parse', 'refs/heads/master'], cwd='kas4',
+ fail=False, liveupdate=False)
+ assert rc == 0
+ assert output.strip() == output2.strip()


def test_refspec_warning(capsys, changedir, tmpdir):
diff --git a/tests/test_refspec/test.yml b/tests/test_refspec/test.yml
index c15d27d..196e7e2 100644
--- a/tests/test_refspec/test.yml
+++ b/tests/test_refspec/test.yml
@@ -11,3 +11,12 @@ repos:
kas2:
url: https://github.com/siemens/kas.git
branch: master
+
+ kas3:
+ url: https://github.com/siemens/kas.git
+ tag: 3.0.1
+ commit: 229310958b17dc2b505b789c1cc1d0e2fddccc44
+
+ kas4:
+ url: https://github.com/siemens/kas.git
+ branch: master
diff --git a/tests/test_refspec/test2.yml b/tests/test_refspec/test2.yml
index ae6fb4e..e68cf52 100644
--- a/tests/test_refspec/test2.yml
+++ b/tests/test_refspec/test2.yml
@@ -12,3 +12,11 @@ repos:
url: https://github.com/siemens/kas.git
# keep legacy refspec here for testing purposes
refspec: 907816a5c4094b59a36aec12226e71c461c05b77
+
+ kas3:
+ url: https://github.com/siemens/kas.git
+ branch: master
+
+ kas4:
+ url: https://github.com/siemens/kas.git
+ tag: 2.6.3
diff --git a/tests/test_refspec/test3.yml b/tests/test_refspec/test3.yml
index 41e3859..7f8b4ce 100644
--- a/tests/test_refspec/test3.yml
+++ b/tests/test_refspec/test3.yml
@@ -11,3 +11,7 @@ repos:
kas_rel:
url: https://github.com/siemens/kas.git
branch: master
+
+ kas_tag_abs:
+ url: https://github.com/siemens/kas.git
+ tag: refs/tags/3.0.1
diff --git a/tests/test_refspec/test7.yml b/tests/test_refspec/test7.yml
new file mode 100644
index 0000000..76447c9
--- /dev/null
+++ b/tests/test_refspec/test7.yml
@@ -0,0 +1,10 @@
+header:
+ version: 14
+
+repos:
+ this:
+
+ kas:
+ url: https://github.com/siemens/kas.git
+ refspec: dc44638cd87c4d0045ea2ca441e682f3525d8b91
+ tag: 3.0.1
diff --git a/tests/test_refspec/test8.yml b/tests/test_refspec/test8.yml
new file mode 100644
index 0000000..eb312cd
--- /dev/null
+++ b/tests/test_refspec/test8.yml
@@ -0,0 +1,10 @@
+header:
+ version: 14
+
+repos:
+ this:
+
+ kas:
+ url: https://github.com/siemens/kas.git
+ tag: 3.0.1
+ commit: de4dcafe
--
2.42.0

Félix Piédallu

unread,
Oct 12, 2023, 11:33:56 AM10/12/23
to kas-...@googlegroups.com, Félix Piédallu
Tags usually never move, but they are mutable, so a configuration
can reference commit and tag at the same time.
Kas will check that the commit and tag match, and if not, error out.

If no commit is provided with the tag, kas will issue a warning about
the unsafeness of tags.

Signed-off-by: Félix Piédallu <fe...@piedallu.me>
---
docs/userguide.rst | 4 ++
kas/libkas.py | 4 +-
kas/plugins/dump.py | 8 +--
kas/plugins/for_all_repos.py | 4 ++
kas/repos.py | 101 +++++++++++++++++++++++++----------
kas/schema-kas.json | 3 ++
6 files changed, 89 insertions(+), 35 deletions(-)

diff --git a/docs/userguide.rst b/docs/userguide.rst
index ec93224..7b1f382 100644
--- a/docs/userguide.rst
+++ b/docs/userguide.rst
@@ -443,6 +443,10 @@ Configuration reference
The upstream branch that should be tracked. If no ``commit`` was
specified, the head of the upstream is checked out.

+ ``tag``: string [optional]
+ The tag that should be used. If no ``commit`` was specified, the commit
+ referenced by this tag is checked out.
+
``path``: string [optional]
The path where the repository is stored.
If the ``url`` and ``path`` is missing, the repository where the
diff --git a/kas/libkas.py b/kas/libkas.py
index 24d55ff..68ce1f2 100644
--- a/kas/libkas.py
+++ b/kas/libkas.py
@@ -412,8 +412,8 @@ def setup_parser_common_args(parser):
help='Skip build steps',
default=[])
parser.add_argument('--force-checkout', action='store_true',
- help='Always checkout the desired commit/branch of '
- 'each repository, discarding any local changes')
+ help='Always checkout the desired commit/branch/tag '
+ 'of each repository, discarding any local changes')
parser.add_argument('--update', action='store_true',
help='Pull new upstream changes to the desired '
'branch even if it is already checked out locally')
diff --git a/kas/plugins/dump.py b/kas/plugins/dump.py
index 8d4a990..3588f5b 100644
--- a/kas/plugins/dump.py
+++ b/kas/plugins/dump.py
@@ -31,9 +31,9 @@

When running with ``--lock``, a locking spec is created which only contains
the exact commit of each repository. This can be used to pin the commit of
- floating branches, while still keeping an easy update path. When combining
- with ``--inplace``, a lockfile is created next to the first file on the kas
- cmdline. For details on the locking support, see
+ floating branches and tags, while still keeping an easy update path. When
+ combining with ``--inplace``, a lockfile is created next to the first file
+ on the kas cmdline. For details on the locking support, see
:class:`kas.includehandler.IncludeHandler`.

Please note:
@@ -208,7 +208,7 @@ class Dump(Checkout):

if args.resolve_refs and not args.lock:
for r in repos:
- if r.commit or r.branch:
+ if r.commit or r.branch or r.tag:
config_expanded['repos'][r.name]['commit'] = r.revision
elif r.refspec:
config_expanded['repos'][r.name]['refspec'] = r.revision
diff --git a/kas/plugins/for_all_repos.py b/kas/plugins/for_all_repos.py
index b720eb2..f37ea3c 100644
--- a/kas/plugins/for_all_repos.py
+++ b/kas/plugins/for_all_repos.py
@@ -56,6 +56,9 @@
repository, or an empty string if no branch was given in the config
file.

+ * ``KAS_REPO_TAG``: The tag which was checked out for this repository,
+ or an empty string if no tag was given in the config file.
+
* ``KAS_REPO_REFSPEC``: The refspec which was checked out for this
repository, or an empty string if no refspec was given in the config
file. This variable is obsolete and will be removed when support for
@@ -119,6 +122,7 @@ class ForAllReposCommand(Command):
'KAS_REPO_URL': '' if repo.operations_disabled else repo.url,
'KAS_REPO_COMMIT': repo.commit or '',
'KAS_REPO_BRANCH': repo.branch or '',
+ 'KAS_REPO_TAG': repo.tag or '',
'KAS_REPO_REFSPEC': repo.refspec or '',
}
logging.info('%s$ %s', repo.path, self.command)
diff --git a/kas/repos.py b/kas/repos.py
index 292a512..99205f4 100644
--- a/kas/repos.py
+++ b/kas/repos.py
@@ -76,12 +76,13 @@ class Repo:
Represents a repository in the kas configuration.
"""

- def __init__(self, name, url, path, commit, branch, refspec, layers,
+ def __init__(self, name, url, path, commit, tag, branch, refspec, layers,
patches, disable_operations):
self.name = name
self.url = url
self.path = path
self.commit = commit
+ self.tag = tag
self.branch = branch
self.refspec = refspec
self._layers = layers
@@ -113,6 +114,8 @@ class Repo:
elif item == 'revision':
if self.commit:
return self.commit
+ if self.tag:
+ return self.tag
branch = self.branch or self.refspec
if not branch:
return None
@@ -126,14 +129,17 @@ class Repo:
raise AttributeError

def __str__(self):
- if self.commit and self.branch:
- return '%s:%s(%s) %s %s' % (self.url, self.commit, self.branch,
+ if self.commit and (self.tag or self.branch):
+ return '%s:%s(%s) %s %s' % (self.url, self.commit,
+ self.tag or self.branch,
self.path, self._layers)
return '%s:%s %s %s' % (self.url,
- self.commit or self.branch or self.refspec,
+ self.commit or self.tag or self.branch
+ or self.refspec,
self.path, self._layers)

__legacy_refspec_warned__ = []
+ __no_commit_tag_warned__ = []

@staticmethod
def factory(name, repo_config, repo_defaults, repo_fallback_path,
@@ -168,27 +174,33 @@ class Repo:
name = repo_config.get('name', name)
typ = repo_config.get('type', 'git')
commit = repo_config.get('commit', None)
+ tag = repo_config.get('tag', None)
branch = repo_config.get('branch', repo_defaults.get('branch', None))
refspec = repo_config.get('refspec',
repo_defaults.get('refspec', None))
- if commit is None and branch is None and refspec is None \
- and url is not None:
- raise RepoRefError('No commit or branch specified for repository '
- '"{}". This is only allowed for local '
- 'repositories.'.format(name))
+ if commit is None and tag is None and branch is None \
+ and refspec is None and url is not None:
+ raise RepoRefError('No commit, tag or branch specified for '
+ 'repository "{}". This is only allowed for '
+ 'local repositories.'.format(name))
if refspec is None:
commit = repo_overrides.get('commit', commit)
else:
if name not in Repo.__legacy_refspec_warned__:
- logging.warning('Using deprecated refspec for repository '
- '"%s". You should migrate to commit/branch.',
+ logging.warning('Using deprecated refspec for repository "%s".'
+ ' You should migrate to commit/tag/branch.',
name)
Repo.__legacy_refspec_warned__.append(name)
- if commit is not None or branch is not None:
+ if commit is not None or tag is not None or branch is not None:
raise RepoRefError('Unsupported mixture of legacy refspec '
- 'and commit/branch for repository "{}"'
+ 'and commit/tag/branch for repository "{}"'
.format(name))
refspec = repo_overrides.get('commit', refspec)
+ if tag and not commit:
+ if name not in Repo.__no_commit_tag_warned__:
+ logging.warning('Using tag without commit for repository '
+ '"%s" is unsafe as tags are mutable.', name)
+ Repo.__no_commit_tag_warned__.append(name)
path = repo_config.get('path', None)
disable_operations = False

@@ -209,10 +221,10 @@ class Repo:
disable_operations = True

if typ == 'git':
- return GitRepo(name, url, path, commit, branch, refspec, layers,
- patches, disable_operations)
+ return GitRepo(name, url, path, commit, tag, branch, refspec,
+ layers, patches, disable_operations)
if typ == 'hg':
- return MercurialRepo(name, url, path, commit, branch, refspec,
+ return MercurialRepo(name, url, path, commit, tag, branch, refspec,
layers, patches, disable_operations)
raise UnsupportedRepoTypeError('Repo type "%s" not supported.' % typ)

@@ -289,12 +301,12 @@ class RepoImpl(Repo):
'the remote url.')

# take what came out of clone and stick to that forever
- if self.commit is None and self.branch is None \
+ if self.commit is None and self.tag is None and self.branch is None \
and self.refspec is None:
return 0

if not get_context().update:
- # Do commit/branch/refspec exist in the current repository?
+ # Do commit/tag/branch/refspec exist in the current repository?
(retc, output) = await run_cmd_async(self.contains_refspec_cmd(),
cwd=self.path,
fail=False,
@@ -302,11 +314,12 @@ class RepoImpl(Repo):
if retc == 0:
logging.info('Repository %s already contains %s as %s',
self.name,
- self.commit or self.branch or self.refspec,
+ self.commit or self.tag or self.branch
+ or self.refspec,
output.strip())
return retc

- # Try to fetch if commit/branch/refspec is missing or if --update
+ # Try to fetch if commit/tag/branch/refspec is missing or if --update
# argument was passed
(retc, output) = await run_cmd_async(self.fetch_cmd(),
cwd=self.path,
@@ -323,8 +336,8 @@ class RepoImpl(Repo):
Checks out the correct revision of the repo.
"""
if self.operations_disabled \
- or (self.commit is None and self.branch is None
- and self.refspec is None):
+ or (self.commit is None and self.tag is None
+ and self.branch is None and self.refspec is None):
return

if not get_context().force_checkout:
@@ -336,9 +349,27 @@ class RepoImpl(Repo):
logging.warning('Repo %s is dirty - no checkout', self.name)
return

+ if self.tag:
+ (retc, output) = run_cmd(self.resolve_tag_cmd(),
+ cwd=self.path,
+ fail=False)
+ if retc:
+ raise RepoRefError(
+ 'Tag "{}" cannot be found in repository {}'
+ .format(self.tag, self.name))
+
+ if self.commit and output.strip() != self.commit:
+ # Ensure provided commit and tag match
+ raise RepoRefError('Provided tag "{}" does not match provided '
+ 'commit "{}" in repository "{}", aborting!'
+ .format(self.tag, self.commit, self.name))
+
if self.commit:
desired_ref = self.commit
is_branch = False
+ elif self.tag:
+ desired_ref = self.tag
+ is_branch = False
else:
(_, output) = run_cmd(self.resolve_branch_cmd(),
cwd=self.path, fail=False)
@@ -438,9 +469,9 @@ class GitRepo(RepoImpl):
Provides the git functionality for a Repo.
"""

- def remove_ref_prefix(self, branch):
+ def remove_ref_prefix(self, ref):
ref_prefix = 'refs/'
- return branch[branch.startswith(ref_prefix) and len(ref_prefix):]
+ return ref[ref.startswith(ref_prefix) and len(ref_prefix):]

def add_cmd(self):
return ['git', 'add', '-A']
@@ -463,10 +494,13 @@ class GitRepo(RepoImpl):
branch = self.branch or self.refspec
if branch and branch.startswith('refs/'):
branch = 'remotes/origin/' + self.remove_ref_prefix(branch)
- return ['git', 'cat-file', '-t', self.commit or branch]
+ return ['git', 'cat-file', '-t', self.commit or self.tag or branch]

def fetch_cmd(self):
cmd = ['git', 'fetch', '-q']
+ if self.tag:
+ cmd.append('--tags')
+
branch = self.branch or self.refspec
if branch and branch.startswith('refs/'):
cmd.extend(['origin',
@@ -485,6 +519,10 @@ class GitRepo(RepoImpl):
format(branch=self.remove_ref_prefix(
self.branch or self.refspec))]

+ def resolve_tag_cmd(self):
+ return ['git', 'rev-list', '-n', '1',
+ self.remove_ref_prefix(self.tag)]
+
def checkout_cmd(self, desired_ref, is_branch):
cmd = ['git', 'checkout', '-q', self.remove_ref_prefix(desired_ref)]
if is_branch:
@@ -496,10 +534,10 @@ class GitRepo(RepoImpl):
return cmd

def prepare_patches_cmd(self):
- branch = self.branch or self.refspec
+ ref = self.tag or self.branch or self.refspec
return ['git', 'checkout', '-q', '-B',
'patched-{refspec}'.
- format(refspec=self.commit or self.remove_ref_prefix(branch))]
+ format(refspec=self.commit or self.remove_ref_prefix(ref))]

def apply_patches_file_cmd(self, path):
return ['git', 'apply', '--whitespace=nowarn', path]
@@ -526,7 +564,8 @@ class MercurialRepo(RepoImpl):
return ['hg', 'commit', '--user', 'kas <k...@example.com>', '-m', 'msg']

def contains_refspec_cmd(self):
- return ['hg', 'log', '-r', self.commit or self.branch or self.refspec]
+ return ['hg', 'log', '-r', self.commit or self.tag or self.branch
+ or self.refspec]

def fetch_cmd(self):
return ['hg', 'pull']
@@ -541,6 +580,10 @@ class MercurialRepo(RepoImpl):
else:
return ['hg', 'identify', '--id', '-r', self.refspec]

+ def resolve_tag_cmd(self):
+ return ['hg', 'identify', '--id', '-r',
+ 'tag({})'.format(self.tag or self.refspec)]
+
def checkout_cmd(self, desired_ref, is_branch):
cmd = ['hg', 'checkout', desired_ref]
if get_context().force_checkout:
@@ -548,7 +591,7 @@ class MercurialRepo(RepoImpl):
return cmd

def prepare_patches_cmd(self):
- refspec = self.commit or self.branch or self.refspec
+ refspec = self.commit or self.tag or self.branch or self.refspec
return ['hg', 'branch', '-f',
'patched-{refspec}'.format(refspec=refspec)]

diff --git a/kas/schema-kas.json b/kas/schema-kas.json
index 65a3198..8b9fc8a 100644
--- a/kas/schema-kas.json
+++ b/kas/schema-kas.json
@@ -168,6 +168,9 @@
"branch": {
"type": "string"
},
+ "tag": {
+ "type": "string"
+ },
"refspec": {
"type": "string"
},
--
2.42.0

Jan Kiszka

unread,
Oct 12, 2023, 12:45:37 PM10/12/23
to Félix Piédallu, kas-...@googlegroups.com
Good catch! I'm taking this already.

Thanks,
Jan

--
Siemens AG, Technology
Linux Expert Center

Jan Kiszka

unread,
Oct 12, 2023, 12:45:59 PM10/12/23
to Félix Piédallu, kas-...@googlegroups.com
On 12.10.23 16:17, Félix Piédallu wrote:
And if commit was specified, both are validated for consistency. That
should be stated here as well.
We also need an entry in format-changelog.rst and a bump of the revision
to 15. Check previous changes if the format where to touch things.

Jan Kiszka

unread,
Oct 12, 2023, 12:47:21 PM10/12/23
to Félix Piédallu, kas-...@googlegroups.com
On 12.10.23 16:17, Félix Piédallu wrote:
When you bump the format version, make sure to bump all test files as
well that use tag:.

Félix Piédallu

unread,
Oct 13, 2023, 8:08:59 AM10/13/23
to kas-...@googlegroups.com, Félix Piédallu
--
2.42.0

Félix Piédallu

unread,
Oct 13, 2023, 8:13:57 AM10/13/23
to kas-...@googlegroups.com, Félix Piédallu
> Good catch! I'm taking this already.

Alright. Not sure what’s the good practice so I’ll keep it in my patchset.

> And if commit was specified, both are validated for consistency. That
> should be stated here as well.

Gah, that was dropped by a rebase, sorry. Fixed.

> We also need an entry in format-changelog.rst and a bump of the revision
> to 15. Check previous changes if the format where to touch things.

Indeed.

Félix Piédallu (4):
Fix MercurialRepo.resolve_branch_cmd
Add "tag" property to repo, to replace usage of refspec.
Add tests for tag property of repo
Bump format version to 15 with the new `tags` key in `repos`, update
tests formats

docs/format-changelog.rst | 9 +++
docs/userguide.rst | 4 ++
kas/__version__.py | 2 +-
kas/libkas.py | 4 +-
kas/plugins/dump.py | 10 ++--
kas/plugins/for_all_repos.py | 4 ++
kas/repos.py | 108 +++++++++++++++++++++++++----------
kas/schema-kas.json | 3 +
tests/test_refspec.py | 91 +++++++++++++++++++++++++++++
tests/test_refspec/test.yml | 11 +++-
tests/test_refspec/test2.yml | 10 +++-
tests/test_refspec/test3.yml | 6 +-
tests/test_refspec/test7.yml | 10 ++++
tests/test_refspec/test8.yml | 10 ++++
14 files changed, 240 insertions(+), 42 deletions(-)
create mode 100644 tests/test_refspec/test7.yml
create mode 100644 tests/test_refspec/test8.yml


base-commit: 5c765487dad5b7d732f3211c5c34c6af064223b9
--
2.42.0

Félix Piédallu

unread,
Oct 13, 2023, 8:14:01 AM10/13/23
to kas-...@googlegroups.com, Félix Piédallu
---
docs/format-changelog.rst | 9 +++++++++
kas/__version__.py | 2 +-
kas/plugins/dump.py | 2 +-
tests/test_refspec/test.yml | 2 +-
tests/test_refspec/test2.yml | 2 +-
tests/test_refspec/test3.yml | 2 +-
tests/test_refspec/test7.yml | 2 +-
tests/test_refspec/test8.yml | 2 +-
8 files changed, 16 insertions(+), 7 deletions(-)

diff --git a/docs/format-changelog.rst b/docs/format-changelog.rst
index f093741..c44fb3f 100644
--- a/docs/format-changelog.rst
+++ b/docs/format-changelog.rst
@@ -145,3 +145,12 @@ Added
and provides the path to the top repo at time of invoking the plugin.
- ``_source_dir_host`` top-level entry is auto-generated by kas-container to
track the source path outside of the container.
+
+Version 15
+----------
+
+Added
+~~~~~
+
+- The key ``tag`` is introduced as a complement to ``commit`` and ``branch``
+ in ``repos`` and ``default``.
diff --git a/kas/__version__.py b/kas/__version__.py
index c13522b..8720b8d 100644
--- a/kas/__version__.py
+++ b/kas/__version__.py
@@ -28,5 +28,5 @@ __copyright__ = 'Copyright (c) Siemens AG, 2017-2020'
__version__ = '4.0'

# Please update docs/format-changelog.rst when changing the file version.
-__file_version__ = 14
+__file_version__ = 15
__compatible_file_version__ = 1
diff --git a/kas/plugins/dump.py b/kas/plugins/dump.py
index 3588f5b..e9f5b13 100644
--- a/kas/plugins/dump.py
+++ b/kas/plugins/dump.py
@@ -182,7 +182,7 @@ class Dump(Checkout):

super().run(args)
ctx = get_context()
- schema_v = 14 if args.lock else 7
+ schema_v = 15 if args.lock else 7
config_expanded = {'header': {'version': schema_v}} if args.lock \
else ctx.config.get_config()
repos = ctx.config.get_repos()
diff --git a/tests/test_refspec/test.yml b/tests/test_refspec/test.yml
index 196e7e2..7e3657e 100644
--- a/tests/test_refspec/test.yml
+++ b/tests/test_refspec/test.yml
@@ -1,5 +1,5 @@
header:
- version: 14
+ version: 15

repos:
this:
diff --git a/tests/test_refspec/test2.yml b/tests/test_refspec/test2.yml
index e68cf52..7fddc40 100644
--- a/tests/test_refspec/test2.yml
+++ b/tests/test_refspec/test2.yml
@@ -1,5 +1,5 @@
header:
- version: 14
+ version: 15

repos:
this:
diff --git a/tests/test_refspec/test3.yml b/tests/test_refspec/test3.yml
index 7f8b4ce..1e206f1 100644
--- a/tests/test_refspec/test3.yml
+++ b/tests/test_refspec/test3.yml
@@ -1,5 +1,5 @@
header:
- version: 14
+ version: 15

repos:
this:
diff --git a/tests/test_refspec/test7.yml b/tests/test_refspec/test7.yml
index 76447c9..2ba0d4b 100644
--- a/tests/test_refspec/test7.yml
+++ b/tests/test_refspec/test7.yml
@@ -1,5 +1,5 @@
header:
- version: 14
+ version: 15

repos:
this:
diff --git a/tests/test_refspec/test8.yml b/tests/test_refspec/test8.yml
index eb312cd..26c5d38 100644
--- a/tests/test_refspec/test8.yml
+++ b/tests/test_refspec/test8.yml
@@ -1,5 +1,5 @@
header:
- version: 14
+ version: 15

repos:
this:
--
2.42.0

Félix Piédallu

unread,
Oct 13, 2023, 8:14:01 AM10/13/23
to kas-...@googlegroups.com, Félix Piédallu
By default, if a tag exists with the same name as a branch, hg identify will
prefer the tag's commit id.
Using hg revset pattern `limit(heads(branch(self.branch)))` ensures we get the
branch's head commit id.

Signed-off-by: Félix Piédallu <fe...@piedallu.me>
---
kas/repos.py | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/kas/repos.py b/kas/repos.py
index 95038c1..292a512 100644
--- a/kas/repos.py
+++ b/kas/repos.py
@@ -535,8 +535,11 @@ class MercurialRepo(RepoImpl):
return ['hg', 'diff']

def resolve_branch_cmd(self):
- return ['hg', 'identify', '--id', '-r', self.branch or self.refspec,
- 'default']
+ if self.branch:
+ return ['hg', 'identify', '--id', '-r',
+ 'limit(heads(branch({})))'.format(self.branch)]
+ else:
+ return ['hg', 'identify', '--id', '-r', self.refspec]

def checkout_cmd(self, desired_ref, is_branch):
cmd = ['hg', 'checkout', desired_ref]
--
2.42.0

Félix Piédallu

unread,
Oct 13, 2023, 8:23:55 AM10/13/23
to kas-...@googlegroups.com, Félix Piédallu
---
docs/userguide.rst | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/docs/userguide.rst b/docs/userguide.rst
index 7b1f382..de41db4 100644
--- a/docs/userguide.rst
+++ b/docs/userguide.rst
@@ -444,8 +444,8 @@ Configuration reference
specified, the head of the upstream is checked out.

``tag``: string [optional]
- The tag that should be used. If no ``commit`` was specified, the commit
- referenced by this tag is checked out.
+ The tag that should be checked out. If a ``commit`` was specified, kas
+ checks that the tag points to this commit.

``path``: string [optional]
The path where the repository is stored.
--
2.42.0

Félix Piédallu

unread,
Oct 13, 2023, 8:23:56 AM10/13/23
to kas-...@googlegroups.com, Félix Piédallu

Félix Piédallu

unread,
Oct 13, 2023, 8:23:56 AM10/13/23
to kas-...@googlegroups.com, Félix Piédallu
Tags usually never move, but they are mutable, so a configuration
can reference commit and tag at the same time.
Kas will check that the commit and tag match, and if not, error out.

If no commit is provided with the tag, kas will issue a warning about
the unsafeness of tags.

Signed-off-by: Félix Piédallu <fe...@piedallu.me>
---
docs/userguide.rst | 4 ++
kas/libkas.py | 4 +-
kas/plugins/dump.py | 8 +--
kas/plugins/for_all_repos.py | 4 ++
kas/repos.py | 101 +++++++++++++++++++++++++----------
kas/schema-kas.json | 3 ++
6 files changed, 89 insertions(+), 35 deletions(-)

diff --git a/docs/userguide.rst b/docs/userguide.rst
index ec93224..de41db4 100644
--- a/docs/userguide.rst
+++ b/docs/userguide.rst
@@ -443,6 +443,10 @@ Configuration reference
The upstream branch that should be tracked. If no ``commit`` was
specified, the head of the upstream is checked out.

+ ``tag``: string [optional]
+ The tag that should be checked out. If a ``commit`` was specified, kas
+ checks that the tag points to this commit.
+
``path``: string [optional]
The path where the repository is stored.
If the ``url`` and ``path`` is missing, the repository where the
diff --git a/kas/libkas.py b/kas/libkas.py
index 24d55ff..68ce1f2 100644
--- a/kas/libkas.py
+++ b/kas/libkas.py
@@ -412,8 +412,8 @@ def setup_parser_common_args(parser):
help='Skip build steps',
default=[])
parser.add_argument('--force-checkout', action='store_true',
- help='Always checkout the desired commit/branch of '
- 'each repository, discarding any local changes')
+ help='Always checkout the desired commit/branch/tag '
+ 'of each repository, discarding any local changes')
parser.add_argument('--update', action='store_true',
help='Pull new upstream changes to the desired '
'branch even if it is already checked out locally')
diff --git a/kas/plugins/dump.py b/kas/plugins/dump.py
index 8d4a990..3588f5b 100644
--- a/kas/plugins/dump.py
+++ b/kas/plugins/dump.py
diff --git a/kas/repos.py b/kas/repos.py
index 292a512..99205f4 100644
--- a/kas/repos.py
+++ b/kas/repos.py
def checkout_cmd(self, desired_ref, is_branch):
cmd = ['git', 'checkout', '-q', self.remove_ref_prefix(desired_ref)]
if is_branch:
@@ -496,10 +534,10 @@ class GitRepo(RepoImpl):
return cmd

def prepare_patches_cmd(self):
- branch = self.branch or self.refspec
+ ref = self.tag or self.branch or self.refspec
return ['git', 'checkout', '-q', '-B',
'patched-{refspec}'.
- format(refspec=self.commit or self.remove_ref_prefix(branch))]
+ format(refspec=self.commit or self.remove_ref_prefix(ref))]

def apply_patches_file_cmd(self, path):
return ['git', 'apply', '--whitespace=nowarn', path]
@@ -526,7 +564,8 @@ class MercurialRepo(RepoImpl):
return ['hg', 'commit', '--user', 'kas <k...@example.com>', '-m', 'msg']

def contains_refspec_cmd(self):
- return ['hg', 'log', '-r', self.commit or self.branch or self.refspec]
+ return ['hg', 'log', '-r', self.commit or self.tag or self.branch
+ or self.refspec]

def fetch_cmd(self):
return ['hg', 'pull']
@@ -541,6 +580,10 @@ class MercurialRepo(RepoImpl):
else:
return ['hg', 'identify', '--id', '-r', self.refspec]

+ def resolve_tag_cmd(self):
+ return ['hg', 'identify', '--id', '-r',
+ 'tag({})'.format(self.tag or self.refspec)]
+
def checkout_cmd(self, desired_ref, is_branch):
cmd = ['hg', 'checkout', desired_ref]
if get_context().force_checkout:
@@ -548,7 +591,7 @@ class MercurialRepo(RepoImpl):
return cmd

def prepare_patches_cmd(self):
- refspec = self.commit or self.branch or self.refspec
+ refspec = self.commit or self.tag or self.branch or self.refspec
return ['hg', 'branch', '-f',
'patched-{refspec}'.format(refspec=refspec)]

diff --git a/kas/schema-kas.json b/kas/schema-kas.json
index 65a3198..8b9fc8a 100644
--- a/kas/schema-kas.json
+++ b/kas/schema-kas.json
@@ -168,6 +168,9 @@
"branch": {
"type": "string"
},
+ "tag": {
+ "type": "string"
+ },
"refspec": {
"type": "string"
},
--
2.42.0

Félix Piédallu

unread,
Oct 13, 2023, 8:24:01 AM10/13/23
to kas-...@googlegroups.com, Félix Piédallu
* test_refspec_switch
* test mix of refspec and tag
* test if kas fails if commit and tag do not match
* test if kas warns about tag without commit being unsafe
* test if kas does not mixes up tag and branch with the same name

Signed-off-by: Félix Piédallu <fe...@piedallu.me>
---
tests/test_refspec.py | 91 ++++++++++++++++++++++++++++++++++++
tests/test_refspec/test.yml | 9 ++++
tests/test_refspec/test2.yml | 8 ++++
tests/test_refspec/test3.yml | 4 ++
tests/test_refspec/test7.yml | 10 ++++
tests/test_refspec/test8.yml | 10 ++++
6 files changed, 132 insertions(+)
create mode 100644 tests/test_refspec/test7.yml
create mode 100644 tests/test_refspec/test8.yml

diff --git a/tests/test_refspec/test.yml b/tests/test_refspec/test.yml
index c15d27d..196e7e2 100644
--- a/tests/test_refspec/test.yml
+++ b/tests/test_refspec/test.yml
@@ -11,3 +11,12 @@ repos:
kas2:
url: https://github.com/siemens/kas.git
branch: master
+
+ kas3:
+ url: https://github.com/siemens/kas.git
+ tag: 3.0.1
+ commit: 229310958b17dc2b505b789c1cc1d0e2fddccc44
+
+ kas4:
+ url: https://github.com/siemens/kas.git
+ branch: master
diff --git a/tests/test_refspec/test2.yml b/tests/test_refspec/test2.yml
index ae6fb4e..e68cf52 100644
--- a/tests/test_refspec/test2.yml
+++ b/tests/test_refspec/test2.yml
@@ -12,3 +12,11 @@ repos:
url: https://github.com/siemens/kas.git
# keep legacy refspec here for testing purposes
refspec: 907816a5c4094b59a36aec12226e71c461c05b77
+
+ kas3:
+ url: https://github.com/siemens/kas.git
+ branch: master
+
+ kas4:
+ url: https://github.com/siemens/kas.git
+ tag: 2.6.3
diff --git a/tests/test_refspec/test3.yml b/tests/test_refspec/test3.yml
index 41e3859..7f8b4ce 100644
--- a/tests/test_refspec/test3.yml
+++ b/tests/test_refspec/test3.yml

Félix Piédallu

unread,
Oct 13, 2023, 8:24:01 AM10/13/23
to kas-...@googlegroups.com, Félix Piédallu
tests/test_refspec/test7.yml | 10 ++++
tests/test_refspec/test8.yml | 10 ++++
14 files changed, 240 insertions(+), 42 deletions(-)
create mode 100644 tests/test_refspec/test7.yml
create mode 100644 tests/test_refspec/test8.yml


base-commit: 5c765487dad5b7d732f3211c5c34c6af064223b9
--
2.42.0

Félix Piédallu

unread,
Oct 13, 2023, 8:24:01 AM10/13/23
to kas-...@googlegroups.com, Félix Piédallu
Tags usually never move, but they are mutable, so a configuration
can reference commit and tag at the same time.
Kas will check that the commit and tag match, and if not, error out.

If no commit is provided with the tag, kas will issue a warning about
the unsafeness of tags.

Signed-off-by: Félix Piédallu <fe...@piedallu.me>
---

Félix Piédallu

unread,
Oct 13, 2023, 8:35:48 AM10/13/23
to kas-...@googlegroups.com
Please ignore this patchset. I forgot to cleanup my outgoing directory, so there's garbage... the next patchset is clean.
Sorry about this, i'm not yet used to work with patchsets by email...


Le 13 octobre 2023 14:06:41 GMT+02:00, "Félix Piédallu" <fe...@piedallu.me> a écrit :

diff --git a/kas/plugins/dump.py b/kas/plugins/dump.py
index 3588f5b..e9f5b13 100644
--- a/kas/plugins/dump.py
+++ b/kas/plugins/dump.py

@@ -182,7 +182,7 @@ class Dump(Checkout):

super().run(args)
ctx = get_context()
- schema_v = 14 if args.lock else 7
+ schema_v = 15 if args.lock else 7
config_expanded = {'header': {'version': schema_v}} if args.lock \
else ctx.config.get_config()
repos = ctx.config.get_repos()
diff --git a/tests/test_refspec/test.yml b/tests/test_refspec/test.yml
index 196e7e2..7e3657e 100644
--- a/tests/test_refspec/test.yml
+++ b/tests/test_refspec/test.yml

@@ -1,5 +1,5 @@
header:
- version: 14
+ version: 15

repos:
this:
diff --git a/tests/test_refspec/test2.yml b/tests/test_refspec/test2.yml
index e68cf52..7fddc40 100644
--- a/tests/test_refspec/test2.yml
+++ b/tests/test_refspec/test2.yml

@@ -1,5 +1,5 @@
header:
- version: 14
+ version: 15

repos:
this:
diff --git a/tests/test_refspec/test3.yml b/tests/test_refspec/test3.yml
index 7f8b4ce..1e206f1 100644
--- a/tests/test_refspec/test3.yml
+++ b/tests/test_refspec/test3.yml

Félix Piédallu

unread,
Oct 13, 2023, 9:23:56 AM10/13/23
to kas-...@googlegroups.com, Félix Piédallu
---
--
2.42.0

Jan Kiszka

unread,
Oct 13, 2023, 11:55:09 AM10/13/23
to Félix Piédallu, kas-...@googlegroups.com
$ scripts/checkcode.sh .
Checking with pycodestyle
./tests/test_refspec.py:201:80: E501 line too long (82 > 79 characters)
Checking with flake8
./tests/test_refspec.py:201:80: E501 line too long (82 > 79 characters)
[...]

Félix Piédallu

unread,
Oct 18, 2023, 10:44:09 AM10/18/23
to kas-...@googlegroups.com, Félix Piédallu
This patchset adds support for `tag` property to repo, tu replace the usage of `refspec`.

As tags are mutable, users can also specify a commit to ensure reproductibility.
If commit is not specified, a small warning is issued, informing users of the possible
unsafeness of using a mutable refspec.

This is a follow-up to https://groups.google.com/g/kas-devel/c/kPPoXeG6CRU/m/n2HTAzJJAAAJ.

Fixed the last patch version, and added the sign-offs to commits.

Félix Piédallu (4):
Fix MercurialRepo.resolve_branch_cmd
Add "tag" property to repo, to replace usage of refspec.
Add tests for tag property of repo
Bump format version to 15 with the new `tags` key in `repos`, update
tests formats

docs/format-changelog.rst | 9 +++
docs/userguide.rst | 4 ++
kas/__version__.py | 2 +-
kas/libkas.py | 4 +-
kas/plugins/dump.py | 8 +--
kas/plugins/for_all_repos.py | 4 ++
kas/repos.py | 108 +++++++++++++++++++++++++----------
kas/schema-kas.json | 3 +
tests/test_refspec.py | 91 +++++++++++++++++++++++++++++
tests/test_refspec/test.yml | 11 +++-
tests/test_refspec/test2.yml | 10 +++-
tests/test_refspec/test3.yml | 6 +-
tests/test_refspec/test7.yml | 10 ++++
tests/test_refspec/test8.yml | 10 ++++
14 files changed, 239 insertions(+), 41 deletions(-)
create mode 100644 tests/test_refspec/test7.yml
create mode 100644 tests/test_refspec/test8.yml


base-commit: 5c765487dad5b7d732f3211c5c34c6af064223b9
--
2.42.0

Félix Piédallu

unread,
Oct 18, 2023, 10:44:10 AM10/18/23
to kas-...@googlegroups.com, Félix Piédallu
* test_refspec_switch
* test mix of refspec and tag
* test if kas fails if commit and tag do not match
* test if kas warns about tag without commit being unsafe
* test if kas does not mixes up tag and branch with the same name

Signed-off-by: Félix Piédallu <fe...@piedallu.me>
---
tests/test_refspec.py | 91 ++++++++++++++++++++++++++++++++++++
tests/test_refspec/test.yml | 9 ++++
tests/test_refspec/test2.yml | 8 ++++
tests/test_refspec/test3.yml | 4 ++
tests/test_refspec/test7.yml | 10 ++++
tests/test_refspec/test8.yml | 10 ++++
6 files changed, 132 insertions(+)
create mode 100644 tests/test_refspec/test7.yml
create mode 100644 tests/test_refspec/test8.yml

--
2.42.0

Félix Piédallu

unread,
Oct 18, 2023, 10:44:12 AM10/18/23
to kas-...@googlegroups.com, Félix Piédallu
Signed-off-by: Félix Piédallu <fe...@piedallu.me>
---
docs/format-changelog.rst | 9 +++++++++
kas/__version__.py | 2 +-
tests/test_refspec/test.yml | 2 +-
tests/test_refspec/test2.yml | 2 +-
tests/test_refspec/test3.yml | 2 +-
tests/test_refspec/test7.yml | 2 +-
tests/test_refspec/test8.yml | 2 +-
7 files changed, 15 insertions(+), 6 deletions(-)
diff --git a/tests/test_refspec/test.yml b/tests/test_refspec/test.yml
index 196e7e2..7e3657e 100644
--- a/tests/test_refspec/test.yml
+++ b/tests/test_refspec/test.yml
@@ -1,5 +1,5 @@
header:
- version: 14
+ version: 15

repos:
this:
diff --git a/tests/test_refspec/test2.yml b/tests/test_refspec/test2.yml
index e68cf52..7fddc40 100644
--- a/tests/test_refspec/test2.yml
+++ b/tests/test_refspec/test2.yml
@@ -1,5 +1,5 @@
header:
- version: 14
+ version: 15

repos:
this:
diff --git a/tests/test_refspec/test3.yml b/tests/test_refspec/test3.yml
index 7f8b4ce..1e206f1 100644
--- a/tests/test_refspec/test3.yml
+++ b/tests/test_refspec/test3.yml

Félix Piédallu

unread,
Oct 18, 2023, 10:54:08 AM10/18/23
to kas-...@googlegroups.com, Félix Piédallu
By default, if a tag exists with the same name as a branch, hg identify will
prefer the tag's commit id.
Using hg revset pattern `limit(heads(branch(self.branch)))` ensures we get the
branch's head commit id.

Signed-off-by: Félix Piédallu <fe...@piedallu.me>
---
kas/repos.py | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/kas/repos.py b/kas/repos.py
index 95038c1..292a512 100644
--- a/kas/repos.py
+++ b/kas/repos.py
@@ -535,8 +535,11 @@ class MercurialRepo(RepoImpl):
return ['hg', 'diff']

def resolve_branch_cmd(self):
- return ['hg', 'identify', '--id', '-r', self.branch or self.refspec,
- 'default']
+ if self.branch:
+ return ['hg', 'identify', '--id', '-r',
+ 'limit(heads(branch({})))'.format(self.branch)]
+ else:
+ return ['hg', 'identify', '--id', '-r', self.refspec]

def checkout_cmd(self, desired_ref, is_branch):
cmd = ['hg', 'checkout', desired_ref]
--
2.42.0

Félix Piédallu

unread,
Oct 18, 2023, 10:54:12 AM10/18/23
to kas-...@googlegroups.com, Félix Piédallu
Tags usually never move, but they are mutable, so a configuration
can reference commit and tag at the same time.
Kas will check that the commit and tag match, and if not, error out.

If no commit is provided with the tag, kas will issue a warning about
the unsafeness of tags.

Signed-off-by: Félix Piédallu <fe...@piedallu.me>
---
docs/userguide.rst | 4 ++
kas/libkas.py | 4 +-
kas/plugins/dump.py | 8 +--
kas/plugins/for_all_repos.py | 4 ++
diff --git a/kas/plugins/dump.py b/kas/plugins/dump.py
index 8d4a990..3588f5b 100644
--- a/kas/plugins/dump.py
+++ b/kas/plugins/dump.py
diff --git a/kas/repos.py b/kas/repos.py
index 292a512..99205f4 100644
--- a/kas/repos.py
+++ b/kas/repos.py
def checkout_cmd(self, desired_ref, is_branch):
cmd = ['git', 'checkout', '-q', self.remove_ref_prefix(desired_ref)]
if is_branch:
@@ -496,10 +534,10 @@ class GitRepo(RepoImpl):
return cmd

def prepare_patches_cmd(self):
- branch = self.branch or self.refspec
+ ref = self.tag or self.branch or self.refspec
return ['git', 'checkout', '-q', '-B',
'patched-{refspec}'.
- format(refspec=self.commit or self.remove_ref_prefix(branch))]
+ format(refspec=self.commit or self.remove_ref_prefix(ref))]

def apply_patches_file_cmd(self, path):
return ['git', 'apply', '--whitespace=nowarn', path]
@@ -526,7 +564,8 @@ class MercurialRepo(RepoImpl):
return ['hg', 'commit', '--user', 'kas <k...@example.com>', '-m', 'msg']

def contains_refspec_cmd(self):
- return ['hg', 'log', '-r', self.commit or self.branch or self.refspec]
+ return ['hg', 'log', '-r', self.commit or self.tag or self.branch
+ or self.refspec]

def fetch_cmd(self):
return ['hg', 'pull']
@@ -541,6 +580,10 @@ class MercurialRepo(RepoImpl):
else:
return ['hg', 'identify', '--id', '-r', self.refspec]

+ def resolve_tag_cmd(self):
+ return ['hg', 'identify', '--id', '-r',
+ 'tag({})'.format(self.tag or self.refspec)]
+
def checkout_cmd(self, desired_ref, is_branch):
cmd = ['hg', 'checkout', desired_ref]

Jan Kiszka

unread,
Oct 18, 2023, 1:46:30 PM10/18/23
to Félix Piédallu, kas-...@googlegroups.com
That's why you were still carrying patch 1: next was at
70ed73ffd32f952447be91eacc712e3a476efe59.

I fixed the style issue of patch 3 and applied 2..4 to next.

Thanks!
Reply all
Reply to author
Forward
0 new messages