def test_destroy_and_multiple_remotes(mock_repo, monkeypatch): """ GIVEN GitRepo initialized with a path and repo WHEN destroy_and_reclone is called AND the repo has multiple remotes THEN Repo.clone_from is called AND create_remote is called """ monkeypatch.setattr(shutil, 'rmtree', Mock()) clone = GitRepo(repo=mock_repo) local_dir = '/tmp/8f697668fgitwrappertest' clone.repo.working_dir = local_dir remote = Mock(spec=git.Remote) remote.configure_mock(name="otherremote", url="http://example.com/another") clone.repo.remotes.append(remote) with patch('git.repo.base.Repo.clone_from') as mock_clone: new_repo_mock = Mock() mock_clone.return_value = new_repo_mock clone.destroy_and_reclone() assert mock_clone.called is True mock_clone.assert_called_with('http://example.com', local_dir, bare=False) new_repo_mock.create_remote.assert_called_with( "otherremote", "http://example.com/another" )
def test_bare_clone(): """ GIVEN GitRepo without a path or repo WHEN clone is called with valid parameters and bare set to True THEN Repo.clone_from is called with bare=True """ with patch('git.repo.base.Repo.clone_from') as mock_clone: GitRepo.clone('./', './testclone', True) mock_clone.assert_called_with('./', ANY, bare=True)
def test_branch_setter_wrong_type(mock_repo): """ GIVEN GitRepo is initialized with a path and repo WHEN the branch setter is called with the wrong type THEN a TypeError is raised """ repo = GitRepo('./', mock_repo) with pytest.raises(TypeError): repo.branch = repo
def test_destroy_no_path_no_repo(monkeypatch): """ GIVEN GitRepo initialized with no path or repo object WHEN destroy_and_reclone is called THEN an exception is raised """ monkeypatch.setattr(shutil, 'rmtree', Mock()) with pytest.raises(Exception): clone = GitRepo('', None) clone.destroy_and_reclone()
def test_tag_setter(mock_repo): """ GIVEN GitRepo is initialized with a path and repo WHEN the tag setter is called THEN the tag is set as expected """ repo = GitRepo('./', mock_repo) new_tag = GitTag(git_repo=repo, logger=None) repo.tag = new_tag assert repo.tag == new_tag
def test_commit_setter(mock_repo): """ GIVEN GitRepo is initialized with a path and repo WHEN the commit setter is called THEN the commit is set as expected """ repo = GitRepo('./', mock_repo) new_commit = GitCommit(git_repo=repo, logger=None) repo.commit = new_commit assert repo.commit == new_commit
def test_remote_setter(mock_repo): """ GIVEN GitRepo is initialized with a path and repo WHEN the remote setter is called THEN the remote is set as expected """ repo = GitRepo('./', mock_repo) new_remote = GitRemote(git_repo=repo, logger=None) repo.remote = new_remote assert repo.remote == new_remote
def test_branch_setter(mock_repo): """ GIVEN GitRepo is initialized with a path and repo WHEN the branch setter is called THEN the branch is set as expected """ repo = GitRepo('./', mock_repo) new_branch = GitBranch(git_repo=repo, logger=None) repo.branch = new_branch assert repo.branch == new_branch
def test_clone_failed(): """ GIVEN GitRepo without a path or repo WHEN clone is called with a valid clone_from URL and clone_to path AND Repo.clone_from fails with an exception THEN a RepoCreationException is raised """ with patch('git.repo.base.Repo.clone_from') as mock_clone: mock_clone.side_effect = git.GitCommandError('clone', '') with pytest.raises(exceptions.RepoCreationException): GitRepo.clone('./', './testclone')
def test_destroy_no_remotes(mock_repo, monkeypatch): """ GIVEN GitRepo initialized with a path and repo WHEN destroy_and_reclone is called AND the repo does not have any remotes configured THEN an exception is raised """ monkeypatch.setattr(shutil, 'rmtree', Mock()) clone = GitRepo(repo=mock_repo) with pytest.raises(exceptions.RepoCreationException): clone.repo.remotes = {} clone.destroy_and_reclone()
def test_rebase_doesnt_push_in_dev_mode(local_repo, patches_repo_root): """ GIVEN a local repository initialized with origin and upstream remotes WHEN running Rebaser.rebase_and_update_remote WITH dev_mode set to True THEN the local repo is rebased AND the origin remote is not updated with the rebase results """ patches_repo = GitRepo(patches_repo_root) commit_to_rebase_to = "61a18a2a" # confirm we are the same as the patches repo orig_local_repo_head = local_repo.repo.head.object.hexsha assert patches_repo.repo.head.object.hexsha == orig_local_repo_head # Rebase with dev_mode on rebaser = Rebaser(local_repo, "master", commit_to_rebase_to, "origin", "0000", True) rebaser.rebase_and_update_remote() # assert local repo was updated assert local_repo.repo.head.object.hexsha != orig_local_repo_head # assert remote repo was not updated assert patches_repo.repo.head.object.hexsha == orig_local_repo_head
def test_rebase_avoid_pushing_unnecessary_tags(local_repo, patches_repo_root): """ GIVEN a local repository initialized with origin and upstream remotes WHEN running Rebaser.rebase_and_update_remote AND the rebase history doesn't change THEN the newly created tag doesn't get pushed to the remote """ patches_repo = GitRepo(patches_repo_root) commit_to_rebase_to = "61a18a2a" # Rebase rebaser = Rebaser(local_repo, "master", commit_to_rebase_to, "origin", "0000", False) rebaser.rebase_and_update_remote() # Assert remote repo was updated with the rebase result rebased_head = local_repo.repo.head.commit.hexsha assert patches_repo.repo.head.commit.hexsha == rebased_head # Run another rebase - no changes but new timestamp rebaser = Rebaser(local_repo, "master", commit_to_rebase_to, "origin", "0001", False) rebaser.rebase_and_update_remote() # Check the first tag was pushed, but not the second one patches_repo.commit.describe("private-rebaser-0000-previous") try: patches_repo.commit.describe("private-rebaser-0001-previous") pytest.fail("Tag was pushed when it shouldn't have") except gw_exceptions.ReferenceNotFoundException: pass
def main(): # These variables are set up by DLRN user = os.environ['DLRN_USER'] local_repo = os.environ['DLRN_SOURCEDIR'] commit = os.environ['DLRN_SOURCE_COMMIT'] distroinfo_repo = os.environ['DLRN_DISTROINFO_REPO'] pkg_name = os.environ['DLRN_PACKAGE_NAME'] # The next variables come from patch_rebaser.ini rebaser_config = get_rebaser_config() remote = rebaser_config.get('DEFAULT', 'remote_name') git_name = rebaser_config.get('DEFAULT', 'git_name') git_email = rebaser_config.get('DEFAULT', 'git_email') patches_repo_key = rebaser_config.get('distroinfo', 'patches_repo_key') pkg_to_process = rebaser_config.get('DEFAULT', 'packages_to_process') if pkg_to_process: if "," in pkg_to_process: pkg_to_process = pkg_to_process.split(",") else: pkg_to_process = [pkg_to_process] if pkg_name not in pkg_to_process: LOGGER.info( "Skipping %s, as package not in list of packages_to_process", pkg_name) return set_up_git_config(git_name, git_email) repo = GitRepo(local_repo) # Create a remote for the patches branch patches_repo = get_patches_repo(distroinfo_repo, pkg_name, patches_repo_key) if not patches_repo: return if remote not in repo.remote.names(): if not repo.remote.add(remote, patches_repo): raise Exception("Could not add remote {0} ({1})".format( remote, patches_repo)) repo.remote.fetch_all() # Create local patches branch branch = get_patches_branch(repo, remote, user) # Not every project has a -patches branch for every release if not branch: # TODO: (future) Create and set up patches branch return remote_branch = "{remote}/{branch}".format(remote=remote, branch=branch) repo.branch.create(branch, remote_branch, reset_if_exists=True) # Rebase LOGGER.info("Rebasing %s to %s", branch, commit) repo.branch.rebase_to_hash(branch, commit) LOGGER.info("Rebasing %s to %s", branch, remote_branch) repo.branch.rebase_to_hash(branch, remote_branch)
def test_log_grep(repo_root): repo = GitRepo(repo_root) commits = repo.log.grep_for_commits('master', "Initial commit") assert len(commits) == 1 assert commits[0] == "ba82064c5fea1fc40270fb2748d5d8a783397609"
def test_fetch_all_with_errors(mock_repo): """ GIVEN GitRepo is initialized with a path and repo WHEN remote.fetch_all is called AND one remote fails THEN the other remotes still fetch AND an exception is raised """ mock_remoteA, mock_remoteB, mock_remoteC = Mock(), Mock(), Mock() mock_remoteA.fetch.side_effect = git.GitCommandError("fetch", "") mock_remotes = { "origin": mock_remoteA, "a_remote": mock_remoteB, "other": mock_remoteC } mock_repo.remote = lambda r: mock_remotes[r] repo = GitRepo(repo=mock_repo) repo.remote.names = Mock(return_value=["origin", "a_remote", "other"]) with pytest.raises(exceptions.RemoteException) as exc_info: repo.remote.fetch_all() assert 'origin' in str(exc_info.value) assert mock_remoteA.fetch.called is True assert mock_remoteB.fetch.called is True assert mock_remoteC.fetch.called is True
def test_reset(repo_root): repo = GitRepo(repo_root) branch_name = "test_reset" # Exercise repo refresh repo.remote.fetch("origin") # Save the current reference to origin/master reset_to_commit = git.repo.fun.name_to_object(repo.repo, "origin/master") # Create a new branch based on an old commit repo.git.branch(branch_name, "0.0.1") # Ensure branch head is different from the one we saved branch_commit = repo.repo.branches[branch_name].commit assert branch_commit.hexsha != reset_to_commit.hexsha # Reset the branch to origin/master repo.branch.hard_reset( refresh=False, # Avoid race condition if something new merged branch=branch_name, remote="origin", remote_branch="master") # Ensure the new head matches the origin/master we saved branch_commit = repo.repo.branches[branch_name].commit assert branch_commit.hexsha == reset_to_commit.hexsha
def test_log_grep_empty(repo_root): repo = GitRepo(repo_root) commits = repo.log.grep_for_commits('master', "somethingthatcannotbefound", True) assert len(commits) == 0
def test_log_grep_with_path(repo_root): repo = GitRepo(repo_root) commits = repo.log.grep_for_commits('master', "structure", path=".gitignore") assert len(commits) == 1
def test_not_path_no_repo(): """ GIVEN GitRepo initialized with no path or repo object WHEN the object is created THEN an exception is raised """ with pytest.raises(Exception): GitRepo('', None)
def test_log_diff_wrong_hash(repo_root): repo = GitRepo(repo_root) with pytest.raises(exceptions.ReferenceNotFoundException): repo.branch.log_diff("0.1.0", "123456789z") with pytest.raises(exceptions.ReferenceNotFoundException): repo.branch.log_diff("123456789z", "0.1.0")
def test_log_show_commit(repo_root): repo = GitRepo(repo_root) commit = repo.log.log_show_commit( 'ba82064c5fea1fc40270fb2748d5d8a783397609') assert commit == ('commit ba82064c5fea1fc40270fb2748d5d8a783397609\n' 'Author: Jason Joyce <*****@*****.**>\n' 'Date: Tue Jun 12 14:21:45 2018 -0400\n\n' 'Initial commit of README.rst\n')
def apply_inflight_patch(): tmp_repo_root = "/tmp/test_retry" if os.path.exists(tmp_repo_root): # We already applied the inflight patch return else: tmp_repo = GitRepo.clone(patches_repo_root, tmp_repo_root) tmp_repo.branch.apply_patch("master", (datadir / "inflight.patch")) tmp_repo.git.push("origin", "master")
def test_destroy_and_reclone(mock_repo, monkeypatch): """ GIVEN GitRepo initialized with a path and repo WHEN destroy_and_reclone is called THEN Repo.clone_from is called WITH the expected remote url and local working dir """ monkeypatch.setattr(shutil, 'rmtree', Mock()) clone = GitRepo(repo=mock_repo) local_dir = '/tmp/8f697668fgitwrappertest' clone.repo.working_dir = local_dir with patch('git.repo.base.Repo.clone_from') as mock_clone: clone.destroy_and_reclone() assert mock_clone.called is True mock_clone.assert_called_with('http://example.com', local_dir, bare=False)
def test_git_command(mock_repo): """ GIVEN GitRepo initialized with a path and repo WHEN the git property is called THEN a git object is returned """ git_util = GitRepo('./', mock_repo) assert mock_repo.git is git_util.git
def test_branch_exists_with_invalid_remote(mock_repo): """ GIVEN GitRepo is initialized with a path and repo WHEN branch.exists is called with a valid branch and invalid remote THEN a RemoteException is raised """ repo = GitRepo(repo=mock_repo) with pytest.raises(exceptions.RemoteException): assert repo.branch.exists("another", "doesntexist")
def test_local_branch_doesnt_exist(mock_repo): """ GIVEN GitRepo is initialized with a path and repo WHEN branch.exists is called with an invalid branch and None remote THEN False is returned """ repo = GitRepo(repo=mock_repo) mock_repo.branches = ["master", "test"] assert repo.branch.exists("another-test") is False
def test_local_branch_exists(mock_repo): """ GIVEN GitRepo is initialized with a path and repo WHEN branch.exists is called with a valid branch and None remote THEN True is returned """ repo = GitRepo(repo=mock_repo) mock_repo.branches = ["master", "test"] assert repo.branch.exists("test") is True
def test_reverse_diff(mock_repo): """ GIVEN GitRepo initialized with a path and repo WHEN reverse_diff is called with a valid diff_path THEN git.am called """ repo = GitRepo('./', mock_repo) repo.branch.reverse_diff('./requirements.txt') assert repo.git.apply.called is True
def test_abort_patch_apply(mock_repo): """ GIVEN GitRepo initialized with a path and repo WHEN abort_patch_apply is called THEN git.am called """ repo = GitRepo('./', mock_repo) repo.branch.abort_patch_apply() assert repo.git.am.called is True
def test_abort_rebase(mock_repo): """ GIVEN GitRepo initialized with a path and repo WHEN branch.abort_rebase is called THEN git.rebase called """ repo = GitRepo('./', mock_repo) repo.branch.abort_rebase() assert repo.repo.git.rebase.called is True