diff mbox series

fetch2/git: verify if local clone contains tag

Message ID 20250815194401.2866251-1-skandigraun@gmail.com
State Accepted, archived
Commit 546b347b4d3d82c01ecc99f45296f66e44638adc
Headers show
Series fetch2/git: verify if local clone contains tag | expand

Commit Message

Gyorgy Sarvari Aug. 15, 2025, 7:44 p.m. UTC
In case a recipe specifies a git SRC_URI along with revision and tag, but only the
revision is present in the local clone without the tag (because it was tagged after
it was cloned), then unpacking fails with the following error:

... rev-list -n 1 1.0 failed with exit code 128, output:\nfatal: ambiguous argument \'1.0\': unknown revision or path not in the working tree

This happens because the during the download step only the revision's presence is
verified to decide if the repository needs to be updated.

To avoid this, check also if the tag is present in the local repository, when the "tag"
tag is specified.

Signed-off-by: Gyorgy Sarvari <skandigraun@gmail.com>
---
 bitbake/lib/bb/fetch2/git.py  | 10 +++++---
 bitbake/lib/bb/tests/fetch.py | 44 +++++++++++++++++++++++++++++++++++
 2 files changed, 51 insertions(+), 3 deletions(-)

Comments

Richard Purdie Aug. 19, 2025, 10:47 a.m. UTC | #1
On Fri, 2025-08-15 at 21:44 +0200, Gyorgy Sarvari via lists.openembedded.org wrote:
> In case a recipe specifies a git SRC_URI along with revision and tag, but only the
> revision is present in the local clone without the tag (because it was tagged after
> it was cloned), then unpacking fails with the following error:
> 
> ... rev-list -n 1 1.0 failed with exit code 128, output:\nfatal: ambiguous argument \'1.0\': unknown revision or path not in the working tree
> 
> This happens because the during the download step only the revision's presence is
> verified to decide if the repository needs to be updated.
> 
> To avoid this, check also if the tag is present in the local repository, when the "tag"
> tag is specified.
> 
> Signed-off-by: Gyorgy Sarvari <skandigraun@gmail.com>
> ---
>  bitbake/lib/bb/fetch2/git.py  | 10 +++++---
>  bitbake/lib/bb/tests/fetch.py | 44 +++++++++++++++++++++++++++++++++++
>  2 files changed, 51 insertions(+), 3 deletions(-)

Thanks for this. I suspected there was this problem and your patch
confirms what I suspected. I really appreciate adding a test case too,
thanks!

Cheers,

Richard
diff mbox series

Patch

diff --git a/bitbake/lib/bb/fetch2/git.py b/bitbake/lib/bb/fetch2/git.py
index ecaf0a49af..52fffe21d7 100644
--- a/bitbake/lib/bb/fetch2/git.py
+++ b/bitbake/lib/bb/fetch2/git.py
@@ -323,6 +323,8 @@  class Git(FetchMethod):
             return True
         if not self._contains_ref(ud, d, ud.name, ud.clonedir):
             return True
+        if 'tag' in ud.parm and not self._contains_ref(ud, d, ud.name, ud.clonedir, tag=True):
+            return True
         return False
 
     def lfs_need_update(self, ud, d):
@@ -775,14 +777,16 @@  class Git(FetchMethod):
     def supports_srcrev(self):
         return True
 
-    def _contains_ref(self, ud, d, name, wd):
+    def _contains_ref(self, ud, d, name, wd, tag=False):
         cmd = ""
+        git_ref_name = 'refs/tags/%s' % ud.parm['tag'] if tag else ud.revision
+
         if ud.nobranch:
             cmd = "%s log --pretty=oneline -n 1 %s -- 2> /dev/null | wc -l" % (
-                ud.basecmd, ud.revision)
+                ud.basecmd, git_ref_name)
         else:
             cmd =  "%s branch --contains %s --list %s 2> /dev/null | wc -l" % (
-                ud.basecmd, ud.revision, ud.branch)
+                ud.basecmd, git_ref_name, ud.branch)
         try:
             output = runfetchcmd(cmd, d, quiet=True, workdir=wd)
         except bb.fetch2.FetchError:
diff --git a/bitbake/lib/bb/tests/fetch.py b/bitbake/lib/bb/tests/fetch.py
index cde1bf3390..d216eac2ab 100644
--- a/bitbake/lib/bb/tests/fetch.py
+++ b/bitbake/lib/bb/tests/fetch.py
@@ -2647,6 +2647,50 @@  class GitURLWithSpacesTest(FetcherTest):
             self.assertEqual(ud.fullmirror, os.path.join(self.dldir, "git2_" + ref['gitsrcname'] + '.tar.gz'))
             self.assertEqual(ud.method._get_repo_url(ud), ref['repo_url'])
 
+
+class FetchLocallyMissingTagFromRemote(FetcherTest):
+    def setUp(self):
+        FetcherTest.setUp(self)
+        self.gitdir = os.path.join(self.tempdir, 'git')
+        self.srcdir = os.path.join(self.tempdir, 'gitsource')
+
+        bb.utils.mkdirhier(self.srcdir)
+        self.git_init(cwd=self.srcdir)
+        self.d.setVar('WORKDIR', self.tempdir)
+        self.d.setVar('S', self.gitdir)
+
+        uri = 'git://%s;protocol=file;subdir=${S};branch=master' % self.srcdir
+        self.d.setVar('SRC_URI', uri)
+
+        open(os.path.join(self.srcdir, 'dummyfile'), 'w').close()
+        self.git(['add', 'dummyfile'], self.srcdir)
+        self.git(['commit', '-m', 'dummymsg', 'dummyfile'], self.srcdir)
+
+    def _fetch_and_unpack(self, uri_to_fetch):
+        fetcher = bb.fetch2.Fetch([uri_to_fetch], self.d)
+        fetcher.download()
+        fetcher.unpack(self.d.getVar('WORKDIR'))
+
+    def test_tag_present_in_remote_but_not_local(self):
+        # fetch a repo that has no tag in it
+        # then add a tag to this repo, and fetch it again, without
+        # changing SRC_REV, but by adding ';tag=tag1` to SRC_URI
+        # the new tag should be fetched and unpacked
+        srcrev = self.git('rev-parse HEAD', cwd=self.srcdir).strip()
+        self.d.setVar('SRCREV', srcrev)
+        src_uri = self.d.getVar('SRC_URI')
+        self._fetch_and_unpack(src_uri)
+
+        self.git('tag -m -a tag1', cwd=self.srcdir)
+
+        src_uri = '%s;tag=tag1' % self.d.getVar('SRC_URI').split()[0]
+        self.d.setVar('SRC_URI', src_uri)
+        self._fetch_and_unpack(src_uri)
+
+        output = self.git('log --pretty=oneline -n 1 refs/tags/tag1', cwd=self.gitdir)
+        assert "fatal: ambiguous argument" not in output
+
+
 class CrateTest(FetcherTest):
     @skipIfNoNetwork()
     def test_crate_url(self):