Esempio n. 1
0
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
Esempio n. 2
0
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_and_update_remote_fails_next_rebase(mock_repo, monkeypatch):
    """
    GIVEN Rebaser initialized correctly
    WHEN rebase_and_update_remote is called
    AND the remote changed once during the rebase
    AND the rebase fails with RebaseException during the second rebase
    THEN a RebaseException is raised
    AND git.push is not called
    """
    monkeypatch.setattr(time, 'sleep', lambda s: None)

    mock_repo.branch.rebase_to_hash.side_effect = [
        None, exceptions.RebaseException
    ]
    mock_repo.commit.same.side_effect = [False, True]

    rebaser = Rebaser(mock_repo,
                      "my_branch",
                      "my_commit",
                      "my_remote",
                      "my_tstamp",
                      dev_mode=True)
    with pytest.raises(exceptions.RebaseException):
        rebaser.rebase_and_update_remote()

    assert mock_repo.remote.fetch.call_count == 3

    assert mock_repo.tag.create.call_count == 2
    mock_repo.tag.delete.assert_called_once()
    mock_repo.git.push.assert_not_called()
def test_rebase_and_update_remote_success_after_retry(mock_repo, monkeypatch):
    """
    GIVEN Rebaser initialized correctly
    WHEN rebase_and_update_remote is called
    AND the remote changes once during the rebase
    THEN the tag gets created
    AND the previous tag gets deleted and re-created during the retry
    AND git.push is called
    """
    monkeypatch.setattr(time, 'sleep', lambda s: None)

    rebaser = Rebaser(mock_repo,
                      "my_branch",
                      "my_commit",
                      "my_remote",
                      "my_tstamp",
                      dev_mode=True)

    mock_repo.commit.same.side_effect = [False, True]
    rebaser.rebase_and_update_remote()

    assert mock_repo.tag.create.call_count == 2
    assert mock_repo.remote.fetch.call_count == 4
    mock_repo.tag.delete.assert_called_once()

    mock_repo.git.push.assert_called()
def test_rebase_and_update_remote(mock_repo, monkeypatch):
    """
    GIVEN Rebaser initialized correctly
    WHEN rebase_and_update_remote is called
    THEN a tag is created
    AND remote.fetch is called twice to catch remote updates during the rebase
    AND git.push is called
    """
    monkeypatch.setattr(time, 'sleep', lambda s: None)

    rebaser = Rebaser(mock_repo,
                      "my_branch",
                      "my_commit",
                      "my_remote",
                      "000",
                      dev_mode=True,
                      release='15.0')

    mock_repo.commit.same.return_value = True
    rebaser.rebase_and_update_remote()

    assert mock_repo.tag.create.call_count == 1
    assert mock_repo.remote.fetch.call_count == 2

    expected = [(("-n", "my_remote", "private-rebaser-15.0-000-previous"), ),
                (("-nf", "--follow-tags", "my_remote", "my_branch"), )]
    assert mock_repo.git.push.call_args_list == expected
    def retry(max_retries):
        rebaser = Rebaser(mock_repo, "my_branch", "my_commit", "my_remote",
                          "my_tstamp", True, max_retries)
        rebaser.rebase_and_update_remote()

        assert mock_repo.tag.create.call_count == 1 + max_retries
        assert mock_repo.remote.fetch.call_count == 2 + 2 * max_retries
        assert mock_repo.tag.delete.call_count == max_retries
        mock_repo.git.push.assert_not_called()

        mock_repo.reset_mock()
Esempio n. 7
0
def test_rebase(local_repo, patches_repo_root):
    """
    GIVEN a local repository initialized with origin and upstream remotes
    WHEN running Rebaser.rebase_and_update_remote
    THEN the local repo is rebased to contain both upstream and origin commits
    AND the origin remote is updated with same
    """
    patches_repo = GitRepo(patches_repo_root)
    commit_to_rebase_to = "61a18a2a"

    # confirm we are the same as the patches repo
    local_repo_head = local_repo.repo.head.object.hexsha
    assert patches_repo.repo.head.object.hexsha == local_repo_head

    # confirm we have incoming patches from upstream
    assert len(
        local_repo.branch.cherry_on_head_only("master",
                                              commit_to_rebase_to)) == 2

    # confirm we have additional patches compared to upstream
    assert len(
        local_repo.branch.cherry_on_head_only(commit_to_rebase_to,
                                              "master")) == 1

    # Rebase with dev_mode off
    rebaser = Rebaser(local_repo, "master", commit_to_rebase_to, "origin",
                      "0000", False)
    rebaser.rebase_and_update_remote()

    # confirm no more incoming patches from upstream
    assert len(
        local_repo.branch.cherry_on_head_only("master",
                                              commit_to_rebase_to)) == 0

    # confirm we still have our additional patch
    assert len(
        local_repo.branch.cherry_on_head_only(commit_to_rebase_to,
                                              "master")) == 1

    # assert remote repo was updated as well
    local_repo_head = local_repo.repo.head.object.hexsha
    assert patches_repo.repo.head.object.hexsha == local_repo_head
Esempio n. 8
0
def test_rebase_retry_logic(local_repo, patches_repo_root, datadir,
                            monkeypatch):
    """
    GIVEN a local repository initialized with origin and upstream remotes
    WHEN running Rebaser.rebase_and_update_remote
    AND the remote branch changes during the rebase
    THEN the rebase operation gets run again
    AND the local repo is rebased and contains both upstream commits and all
        commits from the remote branch, including the new change
    AND the remote branch is updated with the rebase result
    """
    commit_to_rebase_to = "61a18a2acd05b2f37bd75164b8ddfdb71011fe68"
    orig_head = local_repo.repo.head.commit.hexsha

    # Mock sleep to avoid unnecessary waiting
    monkeypatch.setattr(time, 'sleep', lambda s: None)

    # Mock rebaser perform_rebase function so we can create a change
    # during that window
    rebaser = Rebaser(local_repo, "master", commit_to_rebase_to, "origin",
                      "0000", False)
    orig_perform_rebase = rebaser.perform_rebase

    def modified_perform_rebase():
        apply_inflight_patch()
        orig_perform_rebase()

    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")

    # Confirm we have one local-only patch
    assert len(
        local_repo.branch.cherry_on_head_only(commit_to_rebase_to,
                                              "master")) == 1

    # Test rebase with one inflight patch
    with patch.object(Rebaser, 'perform_rebase') as patched_rebase:
        patched_rebase.side_effect = modified_perform_rebase
        rebaser.rebase_and_update_remote()

    assert patched_rebase.call_count == 2

    # Now we have two local-only patches
    assert len(
        local_repo.branch.cherry_on_head_only(commit_to_rebase_to,
                                              "master")) == 2

    # Check we have both local-only patches + upstream commits
    log = local_repo.branch.log_diff(orig_head, "master", "$hash $summary")
    assert "INFLIGHT" in log[0]
    assert "LOCAL-ONLY" in log[1]
    assert commit_to_rebase_to in log[2]

    patches_repo = GitRepo(patches_repo_root)
    assert patches_repo.repo.head.commit.hexsha in log[0]