summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMike Frysinger <vapier@google.com>2020-02-29 02:56:32 -0500
committerMike Frysinger <vapier@google.com>2020-03-24 05:01:23 +0000
commitcfc8111f5e04ece139892bb9af4bd63aac2b75f3 (patch)
tree3bf1cac8e36d3ae01112b3bf481deb350fce1759
parent587f162033b3d39e394cfb0cd13e5703af913901 (diff)
downloadgit-repo-cfc8111f5e04ece139892bb9af4bd63aac2b75f3.tar.gz
init: allow REPO_REV/--repo-rev to specify commits/tags
While the help/usage suggested that revisions would work, they never actually did, and just throw confusing errors. Now that we warn if the checkout isn't tracking a branch, allow people to specify commits or tags explicitly. Hopefully our nags will be sufficient to keep most people on the right path. Bug: https://crbug.com/gerrit/11045 Change-Id: I6ea32c677912185f55ab20faaa23c6c0a4c483b3 Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/259492 Tested-by: Mike Frysinger <vapier@google.com> Reviewed-by: Jonathan Nieder <jrn@google.com>
-rwxr-xr-xrepo103
-rw-r--r--tests/test_wrapper.py87
2 files changed, 167 insertions, 23 deletions
diff --git a/repo b/repo
index 2f4601a7..c78fcacd 100755
--- a/repo
+++ b/repo
@@ -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
848def _Verify(cwd, branch, quiet): 845def 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
910def _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
873def _Checkout(cwd, branch, rev, quiet): 930def _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
21import os 21import os
22import re 22import re
23import shutil
24import tempfile
23import unittest 25import unittest
24 26
25from pyversion import is_python3 27from pyversion import is_python3
@@ -241,5 +243,90 @@ class CheckGitVersion(RepoWrapperTestCase):
241 self.wrapper._CheckGitVersion() 243 self.wrapper._CheckGitVersion()
242 244
243 245
246class 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
244if __name__ == '__main__': 331if __name__ == '__main__':
245 unittest.main() 332 unittest.main()