Package: git-buildpackage
Followup-For: Bug #1010751

Hello.

0001 seems obsolete.
0002 is improved by IntEnum
0004-6 are improved by NamedTuple, making 0003 unnecessary
All tests pass and mypy is happy.

By the way, the source package differs a lot from the git repository.
It embeds lots of things into tests/component/deb/data/*.
Is this deliberate?
>From 28076a16a87d57b395dc7529b88c18565548813b Mon Sep 17 00:00:00 2001
From: Nicolas Boulenguez <[email protected]>
Date: Sat, 30 Nov 2024 13:29:13 +0100
Subject: [PATCH 1/2] clone: make ExitCodes a subclass of IntEnum

---
 gbp/scripts/clone.py           | 16 +++++++---------
 gbp/scripts/common/__init__.py |  3 ++-
 2 files changed, 9 insertions(+), 10 deletions(-)

diff --git a/gbp/scripts/clone.py b/gbp/scripts/clone.py
index c72c74b8..ac063b09 100755
--- a/gbp/scripts/clone.py
+++ b/gbp/scripts/clone.py
@@ -178,26 +178,24 @@ def parse_args(argv):
 
 
 def main(argv) -> int:
-    retval = 0
-
     (options, args) = parse_args(argv)
     if not options:
         return ExitCodes.parse_error
 
     if len(args) < 2:
         gbp.log.err("Need a repository to clone.")
-        return 1
+        return ExitCodes.failed
     else:
         remote_repo = args[1]
         source = repo_to_url(remote_repo) if options.aliases else remote_repo
         if not source:
-            return 1
+            return ExitCodes.failed
 
     clone_to, auto_name = (os.path.curdir, True) if len(args) < 3 else (args[2], False)
     try:
         GitRepository(clone_to)
         gbp.log.err("Can't run inside a git repository.")
-        return 1
+        return ExitCodes.failed
     except GitRepositoryError:
         pass
 
@@ -250,17 +248,17 @@ def main(argv) -> int:
                  )()
 
     except KeyboardInterrupt:
-        retval = 1
         gbp.log.err("Interrupted. Aborting.")
+        return ExitCodes.failed
     except GitRepositoryError as err:
         gbp.log.err("Git command failed: %s" % err)
-        retval = 1
+        return ExitCodes.failed
     except GbpError as err:
         if str(err):
             gbp.log.err(err)
-        retval = 1
+        return ExitCodes.failed
 
-    return retval
+    return ExitCodes.ok
 
 
 if __name__ == '__main__':
diff --git a/gbp/scripts/common/__init__.py b/gbp/scripts/common/__init__.py
index ff39b257..59b197a9 100644
--- a/gbp/scripts/common/__init__.py
+++ b/gbp/scripts/common/__init__.py
@@ -16,6 +16,7 @@
 #    <http://www.gnu.org/licenses/>
 """Parts shared between the deb and rpm commands"""
 
+import enum
 import re
 import os
 import traceback
@@ -25,7 +26,7 @@ from gbp.pkg import Archive
 from gbp.deb.upstreamsource import DebianAdditionalTarball
 
 
-class ExitCodes(object):
+class ExitCodes(enum.IntEnum):
     ok = 0,
     failed = 1               # All other errors
     no_value = 2             # Value does not exist (gbp config only)
-- 
2.47.3

>From 9e02f887fcbb6aeaaab77665c77bfb10a834ddf7 Mon Sep 17 00:00:00 2001
From: Nicolas Boulenguez <[email protected]>
Date: Tue, 24 Feb 2026 01:52:14 +0100
Subject: [PATCH 2/2] clone: handle -b optional branch specification in VCS-Git

Add typing annotations and match statements

Test repo_to_url, vcs_git_url is an implementation detail.

Remove the confusing case with '-b foo' from the version selection
test.
---
 gbp/scripts/clone.py       | 80 +++++++++++++++++++++++---------------
 tests/test_29_gbp_clone.py | 50 ++++++++++++++++++++----
 2 files changed, 90 insertions(+), 40 deletions(-)

diff --git a/gbp/scripts/clone.py b/gbp/scripts/clone.py
index ac063b09..dfedf0e1 100755
--- a/gbp/scripts/clone.py
+++ b/gbp/scripts/clone.py
@@ -22,6 +22,7 @@
 import re
 import sys
 import os
+import typing
 import yaml
 from gbp.config import (GbpOptionParser, GbpOptionGroup)
 from gbp.deb.git import DebianGitRepository
@@ -37,6 +38,11 @@ import gbp.log
 from functools import cmp_to_key
 
 
+class VCSGit(typing.NamedTuple):
+    repo: str
+    branch: str | None = None
+
+
 def apt_showsrc(pkg: str) -> str:
     try:
         aptsrc = Command("apt-cache", ["showsrc", pkg], capture_stdout=True)
@@ -46,19 +52,22 @@ def apt_showsrc(pkg: str) -> str:
         return ''
 
 
-def vcs_git_url(pkg: str) -> str | None:
-    repos = {}
+def vcs_git_url(pkg: str) -> VCSGit | None:
+    repos = dict[str, VCSGit]()
 
     out = apt_showsrc(pkg)
-    vcs_re = re.compile(r'(x-)?vcs-git:\s*(?P<repo>[\S]+).*$', re.I)
+    vcs_re = re.compile(
+        r'(?:x-)?vcs-git:\s*(?P<repo>\S+)(?:\s+-b\s+(?P<branch>\S+))?$', re.I
+    )
     version_re = re.compile(r'Version:\s*(?P<version>.*)$', re.I)
     end_re = re.compile(r'\s*$')
 
-    version = repo = None
+    version: str | None = None
+    repo: VCSGit | None = None
     for line in out.split('\n'):
         m = vcs_re.match(line)
         if m:
-            repo = m.group('repo')
+            repo = VCSGit(repo=m['repo'], branch=m['branch'])
             continue
         m = version_re.match(line)
         if m:
@@ -78,29 +87,24 @@ def vcs_git_url(pkg: str) -> str | None:
     return repos[s[-1]]
 
 
-def repo_to_url(repo: str) -> str | None:
+def repo_to_url(repo: str) -> VCSGit | None:
     """
     >>> repo_to_url("https://foo.example.com";)
-    'https://foo.example.com'
+    VCSGit(repo='https://foo.example.com', branch=None)
     >>> repo_to_url("salsa:agx/git-buildpackage")
-    'https://salsa.debian.org/agx/git-buildpackage.git'
+    VCSGit(repo='https://salsa.debian.org/agx/git-buildpackage.git', branch=None)
     >>> repo_to_url("github:agx/git-buildpackage")
-    'https://github.com/agx/git-buildpackage.git'
+    VCSGit(repo='https://github.com/agx/git-buildpackage.git', branch=None)
     """
-    parts = repo.split(":", 1)
-    if len(parts) != 2:
-        return repo
-    else:
-        proto, path = parts
-
-    if proto == 'salsa':
-        return 'https://salsa.debian.org/%s.git' % path
-    if proto == 'github':
-        return 'https://github.com/%s.git' % path
-    elif proto in ['vcsgit', 'vcs-git']:
-        return vcs_git_url(path)
-    else:
-        return repo
+    match repo.split(":", 1):
+        case 'salsa', path:
+            return VCSGit(repo='https://salsa.debian.org/%s.git' % path)
+        case 'github', path:
+            return VCSGit(repo='https://github.com/%s.git' % path)
+        case 'vcsgit' | 'vcs-git', path:
+            return vcs_git_url(path)
+        case _:
+            return VCSGit(repo=repo)
 
 
 def add_upstream_vcs(repo):
@@ -182,16 +186,23 @@ def main(argv) -> int:
     if not options:
         return ExitCodes.parse_error
 
-    if len(args) < 2:
-        gbp.log.err("Need a repository to clone.")
-        return ExitCodes.failed
-    else:
-        remote_repo = args[1]
-        source = repo_to_url(remote_repo) if options.aliases else remote_repo
+    match args:
+        case _, remote_repo:
+            clone_to = os.path.curdir
+            auto_name = True
+        case _, remote_repo, clone_to:
+            auto_name = False
+        case _:
+            gbp.log.err("Expected a repository and an optional directory.")
+            return ExitCodes.failed
+
+    if options.aliases:
+        source = repo_to_url(remote_repo)
         if not source:
             return ExitCodes.failed
+    else:
+        source = VCSGit(repo=remote_repo)
 
-    clone_to, auto_name = (os.path.curdir, True) if len(args) < 3 else (args[2], False)
     try:
         GitRepository(clone_to)
         gbp.log.err("Can't run inside a git repository.")
@@ -200,8 +211,8 @@ def main(argv) -> int:
         pass
 
     try:
-        gbp.log.info("Cloning from '%s'%s" % (source, " into '%s'" % clone_to if not auto_name else ''))
-        repo = DebianGitRepository.clone(clone_to, source, options.depth,
+        gbp.log.info("Cloning from '%s'%s" % (source.repo, '' if auto_name else " into '%s'" % clone_to))
+        repo = DebianGitRepository.clone(clone_to, source.repo, options.depth,
                                          auto_name=auto_name, reference=options.reference)
         os.chdir(repo.path)
 
@@ -210,6 +221,11 @@ def main(argv) -> int:
         postclone = options.postclone
         (options, args) = parse_args(argv)
 
+        if source.branch and source.branch != options.debian_branch:
+            gbp.log.warn(f"VCS-Git: -b {source.branch} overrides"
+                         f" --debian-branch={options.debian_branch}")
+            options.debian_branch = source.branch
+
         # Track all branches:
         if options.all:
             remotes = repo.get_remote_branches()
diff --git a/tests/test_29_gbp_clone.py b/tests/test_29_gbp_clone.py
index 96bd905e..a47fa754 100644
--- a/tests/test_29_gbp_clone.py
+++ b/tests/test_29_gbp_clone.py
@@ -1,5 +1,5 @@
 # vim: set fileencoding=utf-8 :
-from gbp.scripts.clone import vcs_git_url
+from gbp.scripts.clone import VCSGit, repo_to_url
 
 import unittest
 from unittest.mock import patch
@@ -8,15 +8,41 @@ from . testutils import skip_without_cmd
 
 
 class TestGbpClone(unittest.TestCase):
+
+    def test_repo_to_url(self):
+        arg = "https://foo.example.com";
+        exp = VCSGit(arg, None)
+        self.assertEqual(repo_to_url(arg), exp)
+
+    def test_repo_to_url_salsa(self):
+        arg = "salsa:agx/git-buildpackage"
+        exp = VCSGit("https://salsa.debian.org/agx/git-buildpackage.git";, None)
+        self.assertEqual(repo_to_url(arg), exp)
+
+    def test_repo_to_url_github(self):
+        arg = "github:agx/git-buildpackage"
+        exp = VCSGit("https://github.com/agx/git-buildpackage.git";, None)
+        self.assertEqual(repo_to_url(arg), exp)
+
+    @skip_without_cmd("dpkg")
+    @patch("gbp.scripts.clone.apt_showsrc", return_value="VCS-Git: https://a";)
+    def test_repo_to_url_vcs_git(self, patch):
+        arg = "vcs-git:pkg"
+        exp = VCSGit("https://a";, None)
+        self.assertEqual(repo_to_url(arg), exp)
+
+    @skip_without_cmd("dpkg")
+    @patch("gbp.scripts.clone.apt_showsrc", return_value="VCS-Git: https://a";)
+    def test_repo_to_url_vcs_git_alias(self, patch):
+        arg = "vcsgit:pkg"
+        exp = VCSGit("https://a";, None)
+        self.assertEqual(repo_to_url(arg), exp)
+
     show_src = """
 Version: 0.6.22
 Standards-Version: 3.9.4
 Vcs-Git: git://honk.sigxcpu.org/git/git-buildpackage.git
 
-Version: 0.8.14
-Standards-Version: 3.9.8
-Vcs-Git: https://git.sigxcpu.org/cgit/git-buildpackage/ -b foo
-
 Version: 0.8.12.2
 Standards-Version: 3.9.8
 Vcs-Git: https://git.sigxcpu.org/cgit/git-buildpackage/
@@ -29,6 +55,14 @@ Vcs-Git: git://honk.sigxcpu.org/git/git-buildpackage.git
 
     @skip_without_cmd('dpkg')
     @patch('gbp.scripts.clone.apt_showsrc', return_value=show_src)
-    def test_vcs_git_url(self, patch):
-        self.assertEqual(vcs_git_url('git-buildpackage'),
-                         'https://git.sigxcpu.org/cgit/git-buildpackage/')
+    def test_repo_to_url_vcs_git_version(self, patch):
+        arg = 'vcs-git:git-buildpackage'
+        exp = VCSGit('https://git.sigxcpu.org/cgit/git-buildpackage/', None)
+        self.assertEqual(repo_to_url(arg), exp)
+
+    @skip_without_cmd('dpkg')
+    @patch('gbp.scripts.clone.apt_showsrc', return_value='VCS-Git: h://a -b d')
+    def test_repo_to_url_vcs_git_branch(self, patch):
+        arg = 'vcs-git:pkg'
+        exp = VCSGit('h://a', 'd')
+        self.assertEqual(repo_to_url(arg), exp)
-- 
2.47.3

Reply via email to