diff options
| -rwxr-xr-x | repo | 103 | ||||
| -rw-r--r-- | tests/test_wrapper.py | 87 |
2 files changed, 167 insertions, 23 deletions
| @@ -475,13 +475,7 @@ def _Init(args, gitc_init=False): | |||
| 475 | opt.verbose = opt.output_mode is True | 475 | opt.verbose = opt.output_mode is True |
| 476 | 476 | ||
| 477 | url = opt.repo_url or REPO_URL | 477 | url = opt.repo_url or REPO_URL |
| 478 | branch = opt.repo_rev or REPO_REV | 478 | rev = opt.repo_rev or REPO_REV |
| 479 | |||
| 480 | if branch.startswith('refs/heads/'): | ||
| 481 | branch = branch[len('refs/heads/'):] | ||
| 482 | if branch.startswith('refs/'): | ||
| 483 | print("fatal: invalid branch name '%s'" % branch, file=sys.stderr) | ||
| 484 | raise CloneFailure() | ||
| 485 | 479 | ||
| 486 | try: | 480 | try: |
| 487 | if gitc_init: | 481 | if gitc_init: |
| @@ -532,12 +526,15 @@ def _Init(args, gitc_init=False): | |||
| 532 | dst = os.path.abspath(os.path.join(repodir, S_repo)) | 526 | dst = os.path.abspath(os.path.join(repodir, S_repo)) |
| 533 | _Clone(url, dst, opt.clone_bundle, opt.quiet, opt.verbose) | 527 | _Clone(url, dst, opt.clone_bundle, opt.quiet, opt.verbose) |
| 534 | 528 | ||
| 529 | remote_ref, local_rev = resolve_repo_rev(dst, rev) | ||
| 530 | if not opt.quiet and not remote_ref.startswith('refs/heads/'): | ||
| 531 | print('warning: repo is not tracking a remote branch, so it will not ' | ||
| 532 | 'receive updates', file=sys.stderr) | ||
| 535 | if do_verify: | 533 | if do_verify: |
| 536 | rev = _Verify(dst, branch, opt.quiet) | 534 | rev = _Verify(dst, remote_ref, local_rev, opt.quiet) |
| 537 | else: | 535 | else: |
| 538 | rev = 'refs/remotes/origin/%s^0' % branch | 536 | rev = local_rev |
| 539 | 537 | _Checkout(dst, remote_ref, rev, opt.quiet) | |
| 540 | _Checkout(dst, branch, rev, opt.quiet) | ||
| 541 | 538 | ||
| 542 | if not os.path.isfile(os.path.join(dst, 'repo')): | 539 | if not os.path.isfile(os.path.join(dst, 'repo')): |
| 543 | print("warning: '%s' does not look like a git-repo repository, is " | 540 | print("warning: '%s' does not look like a git-repo repository, is " |
| @@ -845,23 +842,83 @@ def _Clone(url, cwd, clone_bundle, quiet, verbose): | |||
| 845 | _Fetch(url, cwd, 'origin', quiet, verbose) | 842 | _Fetch(url, cwd, 'origin', quiet, verbose) |
| 846 | 843 | ||
| 847 | 844 | ||
| 848 | def _Verify(cwd, branch, quiet): | 845 | def resolve_repo_rev(cwd, committish): |
| 849 | """Verify the branch has been signed by a tag. | 846 | """Figure out what REPO_REV represents. |
| 847 | |||
| 848 | We support: | ||
| 849 | * refs/heads/xxx: Branch. | ||
| 850 | * refs/tags/xxx: Tag. | ||
| 851 | * xxx: Branch or tag or commit. | ||
| 852 | |||
| 853 | Args: | ||
| 854 | cwd: The git checkout to run in. | ||
| 855 | committish: The REPO_REV argument to resolve. | ||
| 856 | |||
| 857 | Returns: | ||
| 858 | A tuple of (remote ref, commit) as makes sense for the committish. | ||
| 859 | For branches, this will look like ('refs/heads/stable', <revision>). | ||
| 860 | For tags, this will look like ('refs/tags/v1.0', <revision>). | ||
| 861 | For commits, this will be (<revision>, <revision>). | ||
| 850 | """ | 862 | """ |
| 851 | try: | 863 | def resolve(committish): |
| 852 | ret = run_git('describe', 'origin/%s' % branch, cwd=cwd) | 864 | ret = run_git('rev-parse', '--verify', '%s^{commit}' % (committish,), |
| 853 | cur = ret.stdout.strip() | 865 | cwd=cwd, check=False) |
| 854 | except CloneFailure: | 866 | return None if ret.returncode else ret.stdout.strip() |
| 855 | print("fatal: branch '%s' has not been signed" % branch, file=sys.stderr) | 867 | |
| 856 | raise | 868 | # An explicit branch. |
| 869 | if committish.startswith('refs/heads/'): | ||
| 870 | remote_ref = committish | ||
| 871 | committish = committish[len('refs/heads/'):] | ||
| 872 | rev = resolve('refs/remotes/origin/%s' % committish) | ||
| 873 | if rev is None: | ||
| 874 | print('repo: error: unknown branch "%s"' % (committish,), | ||
| 875 | file=sys.stderr) | ||
| 876 | raise CloneFailure() | ||
| 877 | return (remote_ref, rev) | ||
| 878 | |||
| 879 | # An explicit tag. | ||
| 880 | if committish.startswith('refs/tags/'): | ||
| 881 | remote_ref = committish | ||
| 882 | committish = committish[len('refs/tags/'):] | ||
| 883 | rev = resolve(remote_ref) | ||
| 884 | if rev is None: | ||
| 885 | print('repo: error: unknown tag "%s"' % (committish,), | ||
| 886 | file=sys.stderr) | ||
| 887 | raise CloneFailure() | ||
| 888 | return (remote_ref, rev) | ||
| 889 | |||
| 890 | # See if it's a short branch name. | ||
| 891 | rev = resolve('refs/remotes/origin/%s' % committish) | ||
| 892 | if rev: | ||
| 893 | return ('refs/heads/%s' % (committish,), rev) | ||
| 894 | |||
| 895 | # See if it's a tag. | ||
| 896 | rev = resolve('refs/tags/%s' % committish) | ||
| 897 | if rev: | ||
| 898 | return ('refs/tags/%s' % (committish,), rev) | ||
| 899 | |||
| 900 | # See if it's a commit. | ||
| 901 | rev = resolve(committish) | ||
| 902 | if rev and rev.lower().startswith(committish.lower()): | ||
| 903 | return (rev, rev) | ||
| 904 | |||
| 905 | # Give up! | ||
| 906 | print('repo: error: unable to resolve "%s"' % (committish,), file=sys.stderr) | ||
| 907 | raise CloneFailure() | ||
| 908 | |||
| 909 | |||
| 910 | def _Verify(cwd, remote_ref, rev, quiet): | ||
| 911 | """Verify the commit has been signed by a tag.""" | ||
| 912 | ret = run_git('describe', rev, cwd=cwd) | ||
| 913 | cur = ret.stdout.strip() | ||
| 857 | 914 | ||
| 858 | m = re.compile(r'^(.*)-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur) | 915 | m = re.compile(r'^(.*)-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur) |
| 859 | if m: | 916 | if m: |
| 860 | cur = m.group(1) | 917 | cur = m.group(1) |
| 861 | if not quiet: | 918 | if not quiet: |
| 862 | print(file=sys.stderr) | 919 | print(file=sys.stderr) |
| 863 | print("info: Ignoring branch '%s'; using tagged release '%s'" | 920 | print("warning: '%s' is not signed; falling back to signed release '%s'" |
| 864 | % (branch, cur), file=sys.stderr) | 921 | % (remote_ref, cur), file=sys.stderr) |
| 865 | print(file=sys.stderr) | 922 | print(file=sys.stderr) |
| 866 | 923 | ||
| 867 | env = os.environ.copy() | 924 | env = os.environ.copy() |
| @@ -870,13 +927,13 @@ def _Verify(cwd, branch, quiet): | |||
| 870 | return '%s^0' % cur | 927 | return '%s^0' % cur |
| 871 | 928 | ||
| 872 | 929 | ||
| 873 | def _Checkout(cwd, branch, rev, quiet): | 930 | def _Checkout(cwd, remote_ref, rev, quiet): |
| 874 | """Checkout an upstream branch into the repository and track it. | 931 | """Checkout an upstream branch into the repository and track it. |
| 875 | """ | 932 | """ |
| 876 | run_git('update-ref', 'refs/heads/default', rev, cwd=cwd) | 933 | run_git('update-ref', 'refs/heads/default', rev, cwd=cwd) |
| 877 | 934 | ||
| 878 | _SetConfig(cwd, 'branch.default.remote', 'origin') | 935 | _SetConfig(cwd, 'branch.default.remote', 'origin') |
| 879 | _SetConfig(cwd, 'branch.default.merge', 'refs/heads/%s' % branch) | 936 | _SetConfig(cwd, 'branch.default.merge', remote_ref) |
| 880 | 937 | ||
| 881 | run_git('symbolic-ref', 'HEAD', 'refs/heads/default', cwd=cwd) | 938 | run_git('symbolic-ref', 'HEAD', 'refs/heads/default', cwd=cwd) |
| 882 | 939 | ||
diff --git a/tests/test_wrapper.py b/tests/test_wrapper.py index c105a3ce..73c62cc1 100644 --- a/tests/test_wrapper.py +++ b/tests/test_wrapper.py | |||
| @@ -20,6 +20,8 @@ from __future__ import print_function | |||
| 20 | 20 | ||
| 21 | import os | 21 | import os |
| 22 | import re | 22 | import re |
| 23 | import shutil | ||
| 24 | import tempfile | ||
| 23 | import unittest | 25 | import unittest |
| 24 | 26 | ||
| 25 | from pyversion import is_python3 | 27 | from pyversion import is_python3 |
| @@ -241,5 +243,90 @@ class CheckGitVersion(RepoWrapperTestCase): | |||
| 241 | self.wrapper._CheckGitVersion() | 243 | self.wrapper._CheckGitVersion() |
| 242 | 244 | ||
| 243 | 245 | ||
| 246 | class ResolveRepoRev(RepoWrapperTestCase): | ||
| 247 | """Check resolve_repo_rev behavior.""" | ||
| 248 | |||
| 249 | GIT_DIR = None | ||
| 250 | REV_LIST = None | ||
| 251 | |||
| 252 | @classmethod | ||
| 253 | def setUpClass(cls): | ||
| 254 | # Create a repo to operate on, but do it once per-class. | ||
| 255 | cls.GIT_DIR = tempfile.mkdtemp(prefix='repo-rev-tests') | ||
| 256 | run_git = wrapper.Wrapper().run_git | ||
| 257 | |||
| 258 | remote = os.path.join(cls.GIT_DIR, 'remote') | ||
| 259 | os.mkdir(remote) | ||
| 260 | run_git('init', cwd=remote) | ||
| 261 | run_git('commit', '--allow-empty', '-minit', cwd=remote) | ||
| 262 | run_git('branch', 'stable', cwd=remote) | ||
| 263 | run_git('tag', 'v1.0', cwd=remote) | ||
| 264 | run_git('commit', '--allow-empty', '-m2nd commit', cwd=remote) | ||
| 265 | cls.REV_LIST = run_git('rev-list', 'HEAD', cwd=remote).stdout.splitlines() | ||
| 266 | |||
| 267 | run_git('init', cwd=cls.GIT_DIR) | ||
| 268 | run_git('fetch', remote, '+refs/heads/*:refs/remotes/origin/*', cwd=cls.GIT_DIR) | ||
| 269 | |||
| 270 | @classmethod | ||
| 271 | def tearDownClass(cls): | ||
| 272 | if not cls.GIT_DIR: | ||
| 273 | return | ||
| 274 | |||
| 275 | shutil.rmtree(cls.GIT_DIR) | ||
| 276 | |||
| 277 | def test_explicit_branch(self): | ||
| 278 | """Check refs/heads/branch argument.""" | ||
| 279 | rrev, lrev = self.wrapper.resolve_repo_rev(self.GIT_DIR, 'refs/heads/stable') | ||
| 280 | self.assertEqual('refs/heads/stable', rrev) | ||
| 281 | self.assertEqual(self.REV_LIST[1], lrev) | ||
| 282 | |||
| 283 | with self.assertRaises(wrapper.CloneFailure): | ||
| 284 | self.wrapper.resolve_repo_rev(self.GIT_DIR, 'refs/heads/unknown') | ||
| 285 | |||
| 286 | def test_explicit_tag(self): | ||
| 287 | """Check refs/tags/tag argument.""" | ||
| 288 | rrev, lrev = self.wrapper.resolve_repo_rev(self.GIT_DIR, 'refs/tags/v1.0') | ||
| 289 | self.assertEqual('refs/tags/v1.0', rrev) | ||
| 290 | self.assertEqual(self.REV_LIST[1], lrev) | ||
| 291 | |||
| 292 | with self.assertRaises(wrapper.CloneFailure): | ||
| 293 | self.wrapper.resolve_repo_rev(self.GIT_DIR, 'refs/tags/unknown') | ||
| 294 | |||
| 295 | def test_branch_name(self): | ||
| 296 | """Check branch argument.""" | ||
| 297 | rrev, lrev = self.wrapper.resolve_repo_rev(self.GIT_DIR, 'stable') | ||
| 298 | self.assertEqual('refs/heads/stable', rrev) | ||
| 299 | self.assertEqual(self.REV_LIST[1], lrev) | ||
| 300 | |||
| 301 | rrev, lrev = self.wrapper.resolve_repo_rev(self.GIT_DIR, 'master') | ||
| 302 | self.assertEqual('refs/heads/master', rrev) | ||
| 303 | self.assertEqual(self.REV_LIST[0], lrev) | ||
| 304 | |||
| 305 | def test_tag_name(self): | ||
| 306 | """Check tag argument.""" | ||
| 307 | rrev, lrev = self.wrapper.resolve_repo_rev(self.GIT_DIR, 'v1.0') | ||
| 308 | self.assertEqual('refs/tags/v1.0', rrev) | ||
| 309 | self.assertEqual(self.REV_LIST[1], lrev) | ||
| 310 | |||
| 311 | def test_full_commit(self): | ||
| 312 | """Check specific commit argument.""" | ||
| 313 | commit = self.REV_LIST[0] | ||
| 314 | rrev, lrev = self.wrapper.resolve_repo_rev(self.GIT_DIR, commit) | ||
| 315 | self.assertEqual(commit, rrev) | ||
| 316 | self.assertEqual(commit, lrev) | ||
| 317 | |||
| 318 | def test_partial_commit(self): | ||
| 319 | """Check specific (partial) commit argument.""" | ||
| 320 | commit = self.REV_LIST[0][0:20] | ||
| 321 | rrev, lrev = self.wrapper.resolve_repo_rev(self.GIT_DIR, commit) | ||
| 322 | self.assertEqual(self.REV_LIST[0], rrev) | ||
| 323 | self.assertEqual(self.REV_LIST[0], lrev) | ||
| 324 | |||
| 325 | def test_unknown(self): | ||
| 326 | """Check unknown ref/commit argument.""" | ||
| 327 | with self.assertRaises(wrapper.CloneFailure): | ||
| 328 | self.wrapper.resolve_repo_rev(self.GIT_DIR, 'boooooooya') | ||
| 329 | |||
| 330 | |||
| 244 | if __name__ == '__main__': | 331 | if __name__ == '__main__': |
| 245 | unittest.main() | 332 | unittest.main() |
