def test_local_pull(self): target_repo = self.get_empty_repo() source_repo = self.get_clone_repo() # Create a new branch in source repo master_commit = source_repo.commit_ids[-1] new_branch_commit = source_repo.commit_ids[-3] source_repo._checkout(new_branch_commit) source_repo._checkout('new_branch', create=True) target_repo._local_pull(source_repo.path, 'new_branch') target_repo = GitRepository(target_repo.path) assert target_repo.head == new_branch_commit target_repo._local_pull(source_repo.path, 'master') target_repo = GitRepository(target_repo.path) assert target_repo.head == master_commit
class TestGitRepository: # pylint: disable=protected-access def __check_for_existing_repo(self): if os.path.exists(TEST_GIT_REPO_CLONE): self.fail('Cannot test git clone repo as location %s already ' 'exists. You should manually remove it first.' % TEST_GIT_REPO_CLONE) @pytest.fixture(autouse=True) def prepare(self, request, pylonsapp): self.repo = GitRepository(TEST_GIT_REPO, bare=True) def get_clone_repo(self): """ Return a non bare clone of the base repo. """ clone_path = next(REPO_PATH_GENERATOR) repo_clone = GitRepository( clone_path, create=True, src_url=self.repo.path, bare=False) return repo_clone def get_empty_repo(self, bare=False): """ Return a non bare empty repo. """ return GitRepository(next(REPO_PATH_GENERATOR), create=True, bare=bare) def test_wrong_repo_path(self): wrong_repo_path = '/tmp/errorrepo' with pytest.raises(RepositoryError): GitRepository(wrong_repo_path) def test_repo_clone(self): self.__check_for_existing_repo() repo = GitRepository(TEST_GIT_REPO) repo_clone = GitRepository( TEST_GIT_REPO_CLONE, src_url=TEST_GIT_REPO, create=True, update_after_clone=True) assert len(repo.commit_ids) == len(repo_clone.commit_ids) # Checking hashes of commits should be enough for commit in repo.get_commits(): raw_id = commit.raw_id assert raw_id == repo_clone.get_commit(raw_id).raw_id def test_repo_clone_without_create(self): with pytest.raises(RepositoryError): GitRepository( TEST_GIT_REPO_CLONE + '_wo_create', src_url=TEST_GIT_REPO) def test_repo_clone_with_update(self): repo = GitRepository(TEST_GIT_REPO) clone_path = TEST_GIT_REPO_CLONE + '_with_update' repo_clone = GitRepository( clone_path, create=True, src_url=TEST_GIT_REPO, update_after_clone=True) assert len(repo.commit_ids) == len(repo_clone.commit_ids) # check if current workdir was updated fpath = os.path.join(clone_path, 'MANIFEST.in') assert os.path.isfile(fpath) def test_repo_clone_without_update(self): repo = GitRepository(TEST_GIT_REPO) clone_path = TEST_GIT_REPO_CLONE + '_without_update' repo_clone = GitRepository( clone_path, create=True, src_url=TEST_GIT_REPO, update_after_clone=False) assert len(repo.commit_ids) == len(repo_clone.commit_ids) # check if current workdir was *NOT* updated fpath = os.path.join(clone_path, 'MANIFEST.in') # Make sure it's not bare repo assert not repo_clone.bare assert not os.path.isfile(fpath) def test_repo_clone_into_bare_repo(self): repo = GitRepository(TEST_GIT_REPO) clone_path = TEST_GIT_REPO_CLONE + '_bare.git' repo_clone = GitRepository( clone_path, create=True, src_url=repo.path, bare=True) assert repo_clone.bare def test_create_repo_is_not_bare_by_default(self): repo = GitRepository(get_new_dir('not-bare-by-default'), create=True) assert not repo.bare def test_create_bare_repo(self): repo = GitRepository(get_new_dir('bare-repo'), create=True, bare=True) assert repo.bare def test_update_server_info(self): self.repo._update_server_info() def test_fetch(self, vcsbackend_git): # Note: This is a git specific part of the API, it's only implemented # by the git backend. source_repo = vcsbackend_git.repo target_repo = vcsbackend_git.create_repo() target_repo.fetch(source_repo.path) # Note: Get a fresh instance, avoids caching trouble target_repo = vcsbackend_git.backend(target_repo.path) assert len(source_repo.commit_ids) == len(target_repo.commit_ids) def test_commit_ids(self): # there are 112 commits (by now) # so we can assume they would be available from now on subset = set([ 'c1214f7e79e02fc37156ff215cd71275450cffc3', '38b5fe81f109cb111f549bfe9bb6b267e10bc557', 'fa6600f6848800641328adbf7811fd2372c02ab2', '102607b09cdd60e2793929c4f90478be29f85a17', '49d3fd156b6f7db46313fac355dca1a0b94a0017', '2d1028c054665b962fa3d307adfc923ddd528038', 'd7e0d30fbcae12c90680eb095a4f5f02505ce501', 'ff7ca51e58c505fec0dd2491de52c622bb7a806b', 'dd80b0f6cf5052f17cc738c2951c4f2070200d7f', '8430a588b43b5d6da365400117c89400326e7992', 'd955cd312c17b02143c04fa1099a352b04368118', 'f67b87e5c629c2ee0ba58f85197e423ff28d735b', 'add63e382e4aabc9e1afdc4bdc24506c269b7618', 'f298fe1189f1b69779a4423f40b48edf92a703fc', 'bd9b619eb41994cac43d67cf4ccc8399c1125808', '6e125e7c890379446e98980d8ed60fba87d0f6d1', 'd4a54db9f745dfeba6933bf5b1e79e15d0af20bd', '0b05e4ed56c802098dfc813cbe779b2f49e92500', '191caa5b2c81ed17c0794bf7bb9958f4dcb0b87e', '45223f8f114c64bf4d6f853e3c35a369a6305520', 'ca1eb7957a54bce53b12d1a51b13452f95bc7c7e', 'f5ea29fc42ef67a2a5a7aecff10e1566699acd68', '27d48942240f5b91dfda77accd2caac94708cc7d', '622f0eb0bafd619d2560c26f80f09e3b0b0d78af', 'e686b958768ee96af8029fe19c6050b1a8dd3b2b']) assert subset.issubset(set(self.repo.commit_ids)) def test_slicing(self): # 4 1 5 10 95 for sfrom, sto, size in [(0, 4, 4), (1, 2, 1), (10, 15, 5), (10, 20, 10), (5, 100, 95)]: commit_ids = list(self.repo[sfrom:sto]) assert len(commit_ids) == size assert commit_ids[0] == self.repo.get_commit(commit_idx=sfrom) assert commit_ids[-1] == self.repo.get_commit(commit_idx=sto - 1) def test_branches(self): # TODO: Need more tests here # Removed (those are 'remotes' branches for cloned repo) # assert 'master' in self.repo.branches # assert 'gittree' in self.repo.branches # assert 'web-branch' in self.repo.branches for __, commit_id in self.repo.branches.items(): assert isinstance(self.repo.get_commit(commit_id), GitCommit) def test_tags(self): # TODO: Need more tests here assert 'v0.1.1' in self.repo.tags assert 'v0.1.2' in self.repo.tags for __, commit_id in self.repo.tags.items(): assert isinstance(self.repo.get_commit(commit_id), GitCommit) def _test_single_commit_cache(self, commit_id): commit = self.repo.get_commit(commit_id) assert commit_id in self.repo.commits assert commit is self.repo.commits[commit_id] def test_initial_commit(self): commit_id = self.repo.commit_ids[0] init_commit = self.repo.get_commit(commit_id) init_author = init_commit.author assert init_commit.message == 'initial import\n' assert init_author == 'Marcin Kuzminski <*****@*****.**>' assert init_author == init_commit.committer for path in ('vcs/__init__.py', 'vcs/backends/BaseRepository.py', 'vcs/backends/__init__.py'): assert isinstance(init_commit.get_node(path), FileNode) for path in ('', 'vcs', 'vcs/backends'): assert isinstance(init_commit.get_node(path), DirNode) with pytest.raises(NodeDoesNotExistError): init_commit.get_node(path='foobar') node = init_commit.get_node('vcs/') assert hasattr(node, 'kind') assert node.kind == NodeKind.DIR node = init_commit.get_node('vcs') assert hasattr(node, 'kind') assert node.kind == NodeKind.DIR node = init_commit.get_node('vcs/__init__.py') assert hasattr(node, 'kind') assert node.kind == NodeKind.FILE def test_not_existing_commit(self): with pytest.raises(RepositoryError): self.repo.get_commit('f' * 40) def test_commit10(self): commit10 = self.repo.get_commit(self.repo.commit_ids[9]) README = """=== VCS === Various Version Control System management abstraction layer for Python. Introduction ------------ TODO: To be written... """ node = commit10.get_node('README.rst') assert node.kind == NodeKind.FILE assert node.content == README def test_head(self): assert self.repo.head == self.repo.get_commit().raw_id def test_checkout_with_create(self): repo_clone = self.get_clone_repo() new_branch = 'new_branch' assert repo_clone._current_branch() == 'master' assert set(repo_clone.branches) == set(('master',)) repo_clone._checkout(new_branch, create=True) # Branches is a lazy property so we need to recrete the Repo object. repo_clone = GitRepository(repo_clone.path) assert set(repo_clone.branches) == set(('master', new_branch)) assert repo_clone._current_branch() == new_branch def test_checkout(self): repo_clone = self.get_clone_repo() repo_clone._checkout('new_branch', create=True) repo_clone._checkout('master') assert repo_clone._current_branch() == 'master' def test_checkout_same_branch(self): repo_clone = self.get_clone_repo() repo_clone._checkout('master') assert repo_clone._current_branch() == 'master' def test_checkout_branch_already_exists(self): repo_clone = self.get_clone_repo() with pytest.raises(RepositoryError): repo_clone._checkout('master', create=True) def test_checkout_bare_repo(self): with pytest.raises(RepositoryError): self.repo._checkout('master') def test_current_branch_bare_repo(self): with pytest.raises(RepositoryError): self.repo._current_branch() def test_current_branch_empty_repo(self): repo = self.get_empty_repo() assert repo._current_branch() is None def test_local_clone(self): clone_path = next(REPO_PATH_GENERATOR) self.repo._local_clone(clone_path, 'master') repo_clone = GitRepository(clone_path) assert self.repo.commit_ids == repo_clone.commit_ids def test_local_clone_with_specific_branch(self): source_repo = self.get_clone_repo() # Create a new branch in source repo new_branch_commit = source_repo.commit_ids[-3] source_repo._checkout(new_branch_commit) source_repo._checkout('new_branch', create=True) clone_path = next(REPO_PATH_GENERATOR) source_repo._local_clone(clone_path, 'new_branch') repo_clone = GitRepository(clone_path) assert source_repo.commit_ids[:-3 + 1] == repo_clone.commit_ids clone_path = next(REPO_PATH_GENERATOR) source_repo._local_clone(clone_path, 'master') repo_clone = GitRepository(clone_path) assert source_repo.commit_ids == repo_clone.commit_ids def test_local_clone_fails_if_target_exists(self): with pytest.raises(RepositoryError): self.repo._local_clone(self.repo.path, 'master') def test_local_fetch(self): target_repo = self.get_empty_repo() source_repo = self.get_clone_repo() # Create a new branch in source repo master_commit = source_repo.commit_ids[-1] new_branch_commit = source_repo.commit_ids[-3] source_repo._checkout(new_branch_commit) source_repo._checkout('new_branch', create=True) target_repo._local_fetch(source_repo.path, 'new_branch') assert target_repo._last_fetch_heads() == [new_branch_commit] target_repo._local_fetch(source_repo.path, 'master') assert target_repo._last_fetch_heads() == [master_commit] def test_local_fetch_from_bare_repo(self): target_repo = self.get_empty_repo() target_repo._local_fetch(self.repo.path, 'master') master_commit = self.repo.commit_ids[-1] assert target_repo._last_fetch_heads() == [master_commit] def test_local_fetch_from_same_repo(self): with pytest.raises(ValueError): self.repo._local_fetch(self.repo.path, 'master') def test_local_fetch_branch_does_not_exist(self): target_repo = self.get_empty_repo() with pytest.raises(RepositoryError): target_repo._local_fetch(self.repo.path, 'new_branch') def test_local_pull(self): target_repo = self.get_empty_repo() source_repo = self.get_clone_repo() # Create a new branch in source repo master_commit = source_repo.commit_ids[-1] new_branch_commit = source_repo.commit_ids[-3] source_repo._checkout(new_branch_commit) source_repo._checkout('new_branch', create=True) target_repo._local_pull(source_repo.path, 'new_branch') target_repo = GitRepository(target_repo.path) assert target_repo.head == new_branch_commit target_repo._local_pull(source_repo.path, 'master') target_repo = GitRepository(target_repo.path) assert target_repo.head == master_commit def test_local_pull_in_bare_repo(self): with pytest.raises(RepositoryError): self.repo._local_pull(self.repo.path, 'master') def test_local_merge(self): target_repo = self.get_empty_repo() source_repo = self.get_clone_repo() # Create a new branch in source repo master_commit = source_repo.commit_ids[-1] new_branch_commit = source_repo.commit_ids[-3] source_repo._checkout(new_branch_commit) source_repo._checkout('new_branch', create=True) # This is required as one cannot do a -ff-only merge in an empty repo. target_repo._local_pull(source_repo.path, 'new_branch') target_repo._local_fetch(source_repo.path, 'master') merge_message = 'Merge message\n\nDescription:...' user_name = 'Albert Einstein' user_email = '*****@*****.**' target_repo._local_merge(merge_message, user_name, user_email, target_repo._last_fetch_heads()) target_repo = GitRepository(target_repo.path) assert target_repo.commit_ids[-2] == master_commit last_commit = target_repo.get_commit(target_repo.head) assert last_commit.message.strip() == merge_message assert last_commit.author == '%s <%s>' % (user_name, user_email) assert not os.path.exists( os.path.join(target_repo.path, '.git', 'MERGE_HEAD')) def test_local_merge_raises_exception_on_conflict(self, vcsbackend_git): target_repo = vcsbackend_git.create_repo(number_of_commits=1) vcsbackend_git.ensure_file('README', 'I will conflict with you!!!') target_repo._local_fetch(self.repo.path, 'master') with pytest.raises(RepositoryError): target_repo._local_merge( 'merge_message', 'user name', '*****@*****.**', target_repo._last_fetch_heads()) # Check we are not left in an intermediate merge state assert not os.path.exists( os.path.join(target_repo.path, '.git', 'MERGE_HEAD')) def test_local_merge_into_empty_repo(self): target_repo = self.get_empty_repo() # This is required as one cannot do a -ff-only merge in an empty repo. target_repo._local_fetch(self.repo.path, 'master') with pytest.raises(RepositoryError): target_repo._local_merge( 'merge_message', 'user name', '*****@*****.**', target_repo._last_fetch_heads()) def test_local_merge_in_bare_repo(self): with pytest.raises(RepositoryError): self.repo._local_merge( 'merge_message', 'user name', '*****@*****.**', None) def test_local_push_non_bare(self): target_repo = self.get_empty_repo() pushed_branch = 'pushed_branch' self.repo._local_push('master', target_repo.path, pushed_branch) # Fix the HEAD of the target repo, or otherwise GitRepository won't # report any branches. with open(os.path.join(target_repo.path, '.git', 'HEAD'), 'w') as f: f.write('ref: refs/heads/%s' % pushed_branch) target_repo = GitRepository(target_repo.path) assert (target_repo.branches[pushed_branch] == self.repo.branches['master']) def test_local_push_bare(self): target_repo = self.get_empty_repo(bare=True) pushed_branch = 'pushed_branch' self.repo._local_push('master', target_repo.path, pushed_branch) # Fix the HEAD of the target repo, or otherwise GitRepository won't # report any branches. with open(os.path.join(target_repo.path, 'HEAD'), 'w') as f: f.write('ref: refs/heads/%s' % pushed_branch) target_repo = GitRepository(target_repo.path) assert (target_repo.branches[pushed_branch] == self.repo.branches['master']) def test_local_push_non_bare_target_branch_is_checked_out(self): target_repo = self.get_clone_repo() pushed_branch = 'pushed_branch' # Create a new branch in source repo new_branch_commit = target_repo.commit_ids[-3] target_repo._checkout(new_branch_commit) target_repo._checkout(pushed_branch, create=True) self.repo._local_push('master', target_repo.path, pushed_branch) target_repo = GitRepository(target_repo.path) assert (target_repo.branches[pushed_branch] == self.repo.branches['master']) def test_local_push_raises_exception_on_conflict(self, vcsbackend_git): target_repo = vcsbackend_git.create_repo(number_of_commits=1) with pytest.raises(RepositoryError): self.repo._local_push('master', target_repo.path, 'master') def test_hooks_can_be_enabled_via_env_variable_for_local_push(self): target_repo = self.get_empty_repo(bare=True) with mock.patch.object(self.repo, 'run_git_command') as run_mock: self.repo._local_push( 'master', target_repo.path, 'master', enable_hooks=True) env = run_mock.call_args[1]['extra_env'] assert 'RC_SKIP_HOOKS' not in env def _add_failing_hook(self, repo_path, hook_name, bare=False): path_components = ( ['hooks', hook_name] if bare else ['.git', 'hooks', hook_name]) hook_path = os.path.join(repo_path, *path_components) with open(hook_path, 'w') as f: script_lines = [ '#!%s' % sys.executable, 'import os', 'import sys', 'if os.environ.get("RC_SKIP_HOOKS"):', ' sys.exit(0)', 'sys.exit(1)', ] f.write('\n'.join(script_lines)) os.chmod(hook_path, 0755) def test_local_push_does_not_execute_hook(self): target_repo = self.get_empty_repo() pushed_branch = 'pushed_branch' self._add_failing_hook(target_repo.path, 'pre-receive') self.repo._local_push('master', target_repo.path, pushed_branch) # Fix the HEAD of the target repo, or otherwise GitRepository won't # report any branches. with open(os.path.join(target_repo.path, '.git', 'HEAD'), 'w') as f: f.write('ref: refs/heads/%s' % pushed_branch) target_repo = GitRepository(target_repo.path) assert (target_repo.branches[pushed_branch] == self.repo.branches['master']) def test_local_push_executes_hook(self): target_repo = self.get_empty_repo(bare=True) self._add_failing_hook(target_repo.path, 'pre-receive', bare=True) with pytest.raises(RepositoryError): self.repo._local_push( 'master', target_repo.path, 'master', enable_hooks=True) def test_maybe_prepare_merge_workspace(self): workspace = self.repo._maybe_prepare_merge_workspace( 'pr2', Reference('branch', 'master', 'unused')) assert os.path.isdir(workspace) workspace_repo = GitRepository(workspace) assert workspace_repo.branches == self.repo.branches # Calling it a second time should also succeed workspace = self.repo._maybe_prepare_merge_workspace( 'pr2', Reference('branch', 'master', 'unused')) assert os.path.isdir(workspace) def test_cleanup_merge_workspace(self): workspace = self.repo._maybe_prepare_merge_workspace( 'pr3', Reference('branch', 'master', 'unused')) self.repo.cleanup_merge_workspace('pr3') assert not os.path.exists(workspace) def test_cleanup_merge_workspace_invalid_workspace_id(self): # No assert: because in case of an inexistent workspace this function # should still succeed. self.repo.cleanup_merge_workspace('pr4') def test_set_refs(self): test_ref = 'refs/test-refs/abcde' test_commit_id = 'ecb86e1f424f2608262b130db174a7dfd25a6623' self.repo.set_refs(test_ref, test_commit_id) stdout, _ = self.repo.run_git_command(['show-ref']) assert test_ref in stdout assert test_commit_id in stdout def test_remove_ref(self): test_ref = 'refs/test-refs/abcde' test_commit_id = 'ecb86e1f424f2608262b130db174a7dfd25a6623' self.repo.set_refs(test_ref, test_commit_id) stdout, _ = self.repo.run_git_command(['show-ref']) assert test_ref in stdout assert test_commit_id in stdout self.repo.remove_ref(test_ref) stdout, _ = self.repo.run_git_command(['show-ref']) assert test_ref not in stdout assert test_commit_id not in stdout