diff --git a/src/pip/_internal/vcs/git.py b/src/pip/_internal/vcs/git.py index 5643bcbf399172ab05d6609b632866ecf4ddb73a..7679ad6dc076adf115cb5394ea42d0114c986aca 100644 --- a/src/pip/_internal/vcs/git.py +++ b/src/pip/_internal/vcs/git.py @@ -89,6 +89,29 @@ class Git(VersionControl): show_stdout=False, cwd=temp_dir.path ) + def get_revision_sha(self, dest, rev): + """ + Return a commit hash for the given revision if it names a remote + branch or tag. Otherwise, return None. + + Args: + dest: the repository directory. + rev: the revision name. + """ + # Pass rev to pre-filter the list. + output = self.run_command(['show-ref', rev], cwd=dest, + show_stdout=False, on_returncode='ignore') + refs = {} + for line in output.strip().splitlines(): + sha, ref = line.split(None, 1) + refs[ref] = sha + + sha = refs.get('refs/remotes/origin/{}'.format(rev)) + if sha is not None: + return sha + + return refs.get('refs/tags/{}'.format(rev)) + def check_rev_options(self, dest, rev_options): """Check the revision options before checkout to compensate that tags and branches may need origin/ as a prefix. diff --git a/tests/functional/test_vcs_git.py b/tests/functional/test_vcs_git.py new file mode 100644 index 0000000000000000000000000000000000000000..1751c64d10f4caf74265819e2c57fb6e16d17e39 --- /dev/null +++ b/tests/functional/test_vcs_git.py @@ -0,0 +1,91 @@ +""" +Contains functional tests of the Git class. +""" + +from pip.utils.temp_dir import TempDirectory +from pip.vcs.git import Git + + +def get_head_sha(script, dest): + """ + Return the HEAD sha. + """ + result = script.run('git', 'rev-parse', 'HEAD', cwd=dest) + sha = result.stdout.strip() + return sha + + +def do_commit(script, dest): + script.run( + 'git', 'commit', '-q', '--author', 'pip ', + '--allow-empty', '-m', 'test commit', cwd=dest + ) + sha = get_head_sha(script, dest) + + return sha + + +def add_commits(script, dest, count): + """ + Return a list of the commit hashes from oldest to newest. + """ + shas = [] + for index in range(count): + sha = do_commit(script, dest) + shas.append(sha) + + return shas + + +def check_rev(repo_dir, rev, expected_sha): + git = Git() + assert git.get_revision_sha(repo_dir, rev) == expected_sha + + +def test_get_revision_sha(script): + with TempDirectory(kind="testing") as temp: + repo_dir = temp.path + script.run('git', 'init', cwd=repo_dir) + shas = add_commits(script, repo_dir, count=6) + + local_branch_sha = shas[0] + tag_sha = shas[1] + origin_branch_sha = shas[2] + upstream_branch_sha = shas[3] + ref_sha = shas[4] + head_sha = shas[5] + assert head_sha == shas[-1] + + script.run( + 'git', 'branch', 'local-branch', local_branch_sha, cwd=repo_dir + ) + script.run('git', 'tag', 'v1.0', tag_sha, cwd=repo_dir) + script.run( + 'git', 'update-ref', 'refs/remotes/origin/origin-branch', + origin_branch_sha, cwd=repo_dir + ) + script.run( + 'git', 'update-ref', 'refs/remotes/upstream/upstream-branch', + upstream_branch_sha, cwd=repo_dir + ) + script.run( + 'git', 'update-ref', 'refs/generic-ref', ref_sha, cwd=repo_dir + ) + + # Test two tags pointing to the same sha. + script.run('git', 'tag', 'v2.0', tag_sha, cwd=repo_dir) + # Test tags sharing the same suffix as another tag, both before and + # after alphabetically. + script.run('git', 'tag', 'aaa/v1.0', head_sha, cwd=repo_dir) + script.run('git', 'tag', 'zzz/v1.0', head_sha, cwd=repo_dir) + + check_rev(repo_dir, 'local-branch', None) + check_rev(repo_dir, 'v1.0', tag_sha) + check_rev(repo_dir, 'v2.0', tag_sha) + check_rev(repo_dir, 'origin-branch', origin_branch_sha) + check_rev(repo_dir, 'upstream-branch', None) + check_rev(repo_dir, 'generic-ref', None) + # Test passing a valid commit hash. + check_rev(repo_dir, tag_sha, None) + # Test passing a non-existent name. + check_rev(repo_dir, 'does-not-exist', None)