Beispiel #1
0
    def cloneUpstream(self, project, dest):
        # Check for a cached git repo first
        git_cache = '%s/%s' % (self.cache_dir, project)

        # Then, if we are cloning the repo for the zuul_project, then
        # set its origin to be the zuul merger, as it is guaranteed to
        # be correct and up to date even if mirrors haven't updated
        # yet.  Otherwise, we can not be sure about the state of the
        # project, so our best chance to get the most current state is
        # by setting origin to the git_url.
        if (self.zuul_url and project == self.zuul_project):
            git_upstream = '%s/%s' % (self.zuul_url, project)
        else:
            git_upstream = '%s/%s' % (self.git_url, project)

        repo_is_cloned = os.path.exists(os.path.join(dest, '.git'))
        if (self.cache_dir and os.path.exists(git_cache)
                and not repo_is_cloned):
            # file:// tells git not to hard-link across repos
            git_cache = 'file://%s' % git_cache
            self.log.info("Creating repo %s from cache %s", project, git_cache)
            new_repo = git.Repo.clone_from(git_cache, dest)
            self.log.info("Updating origin remote in repo %s to %s", project,
                          git_upstream)
            new_repo.remotes.origin.config_writer.set('url', git_upstream)
        else:
            self.log.info("Creating repo %s from upstream %s", project,
                          git_upstream)
        repo = Repo(remote=git_upstream, local=dest, email=None, username=None)

        if not repo.isInitialized():
            raise Exception("Error cloning %s to %s" % (git_upstream, dest))

        return repo
Beispiel #2
0
    def cloneUpstream(self, project, dest):
        # Check for a cached git repo first
        git_cache = '%s/%s' % (self.cache_dir, project)
        git_upstream = '%s/%s' % (self.git_url, project)
        if (self.cache_dir and
            os.path.exists(git_cache) and
            not os.path.exists(dest)):
            # file:// tells git not to hard-link across repos
            git_cache = 'file://%s' % git_cache
            self.log.info("Creating repo %s from cache %s",
                          project, git_cache)
            new_repo = git.Repo.clone_from(git_cache, dest)
            self.log.info("Updating origin remote in repo %s to %s",
                          project, git_upstream)
            new_repo.remotes.origin.config_writer.set('url', git_upstream)
        else:
            self.log.info("Creating repo %s from upstream %s",
                          project, git_upstream)
        repo = Repo(
            remote=git_upstream,
            local=dest,
            email=None,
            username=None)

        if not repo.isInitialized():
            raise Exception("Error cloning %s to %s" % (git_upstream, dest))

        return repo
Beispiel #3
0
    def test_files_changes_master_fork_merges(self):
        """Regression test for getFilesChanges()

        Check if correct list of changed files is listed for a messy
        branch that has a merge of a fork, with the fork including a
        merge of a new master revision.

        The previously used "git merge-base" approach did not handle this
        case correctly.
        """
        parent_path = os.path.join(self.upstream_root, 'org/project1')
        repo = git.Repo(parent_path)

        self.create_branch('org/project1',
                           'messy',
                           commit_filename='messy1.txt')

        # Let time pass to reproduce the order for this error case
        commit_date = datetime.datetime.now() + datetime.timedelta(seconds=5)
        commit_date = commit_date.replace(microsecond=0).isoformat()

        # Create a commit on 'master' so we can merge it into the fork
        files = {"master.txt": "master"}
        master_ref = self.create_commit('org/project1',
                                        files=files,
                                        message="Add master.txt",
                                        commit_date=commit_date)
        repo.refs.master.commit = master_ref

        # Create a fork of the 'messy' branch and merge
        # 'master' into the fork (no fast-forward)
        repo.create_head("messy-fork")
        repo.heads["messy-fork"].commit = "messy"
        repo.head.reference = 'messy'
        repo.head.reset(index=True, working_tree=True)
        repo.git.checkout('messy-fork')
        repo.git.merge('master', no_ff=True)

        # Merge fork back into 'messy' branch (no fast-forward)
        repo.head.reference = 'messy'
        repo.head.reset(index=True, working_tree=True)
        repo.git.checkout('messy')
        repo.git.merge('messy-fork', no_ff=True)

        # Create another commit on top of 'messy'
        files = {"messy2.txt": "messy2"}
        messy_ref = self.create_commit('org/project1',
                                       files=files,
                                       head='messy',
                                       message="Add messy2.txt")
        repo.refs.messy.commit = messy_ref

        # Check that we get all changes for the 'messy' but not 'master' branch
        work_repo = Repo(parent_path, self.workspace_root, '*****@*****.**',
                         'User Name', '0', '0')
        changed_files = work_repo.getFilesChanges('messy', 'master')
        self.assertEqual(sorted(['messy1.txt', 'messy2.txt']),
                         sorted(changed_files))
Beispiel #4
0
    def test_files_changes(self):
        parent_path = os.path.join(self.upstream_root, 'org/project1')
        self.create_branch('org/project1', 'feature')

        work_repo = Repo(parent_path, self.workspace_root, '*****@*****.**',
                         'User Name', '0', '0')
        changed_files = work_repo.getFilesChanges('feature', 'master')

        self.assertEqual(['README'], changed_files)
    def test_files_changes(self):
        parent_path = os.path.join(self.upstream_root, 'org/project1')
        self.create_branch('org/project1', 'feature')

        work_repo = Repo(parent_path, self.workspace_root,
                         '*****@*****.**', 'User Name', '0', '0')
        changed_files = work_repo.getFilesChanges('feature', 'master')

        self.assertEqual(['README'], changed_files)
 def test_fetch_timeout(self):
     parent_path = os.path.join(self.upstream_root, 'org/project1')
     work_repo = Repo(parent_path, self.workspace_root,
                      '*****@*****.**', 'User Name', '0', '0',
                      retry_attempts=1)
     work_repo.git_timeout = 0.001
     self.patch(git.Git, 'GIT_PYTHON_GIT_EXECUTABLE',
                os.path.join(FIXTURE_DIR, 'fake_git.sh'))
     with testtools.ExpectedException(git.exc.GitCommandError,
                                      r'.*exit code\(-9\)'):
         work_repo.update()
    def test_files_changes_master_fork_merges(self):
        """Regression test for getFilesChanges()

        Check if correct list of changed files is listed for a messy
        branch that has a merge of a fork, with the fork including a
        merge of a new master revision.

        The previously used "git merge-base" approach did not handle this
        case correctly.
        """
        parent_path = os.path.join(self.upstream_root, 'org/project1')
        repo = git.Repo(parent_path)

        self.create_branch('org/project1', 'messy',
                           commit_filename='messy1.txt')

        # Let time pass to reproduce the order for this error case
        commit_date = datetime.datetime.now() + datetime.timedelta(seconds=5)
        commit_date = commit_date.replace(microsecond=0).isoformat()

        # Create a commit on 'master' so we can merge it into the fork
        files = {"master.txt": "master"}
        master_ref = self.create_commit('org/project1', files=files,
                                        message="Add master.txt",
                                        commit_date=commit_date)
        repo.refs.master.commit = master_ref

        # Create a fork of the 'messy' branch and merge
        # 'master' into the fork (no fast-forward)
        repo.create_head("messy-fork")
        repo.heads["messy-fork"].commit = "messy"
        repo.head.reference = 'messy'
        repo.head.reset(index=True, working_tree=True)
        repo.git.checkout('messy-fork')
        repo.git.merge('master', no_ff=True)

        # Merge fork back into 'messy' branch (no fast-forward)
        repo.head.reference = 'messy'
        repo.head.reset(index=True, working_tree=True)
        repo.git.checkout('messy')
        repo.git.merge('messy-fork', no_ff=True)

        # Create another commit on top of 'messy'
        files = {"messy2.txt": "messy2"}
        messy_ref = self.create_commit('org/project1', files=files,
                                       head='messy', message="Add messy2.txt")
        repo.refs.messy.commit = messy_ref

        # Check that we get all changes for the 'messy' but not 'master' branch
        work_repo = Repo(parent_path, self.workspace_root,
                         '*****@*****.**', 'User Name', '0', '0')
        changed_files = work_repo.getFilesChanges('messy', 'master')
        self.assertEqual(sorted(['messy1.txt', 'messy2.txt']),
                         sorted(changed_files))
 def test_fetch_timeout(self):
     parent_path = os.path.join(self.upstream_root, 'org/project1')
     work_repo = Repo(parent_path, self.workspace_root,
                      '*****@*****.**', 'User Name', '0', '0',
                      retry_attempts=1)
     work_repo.git_timeout = 0.001
     self.patch(git.Git, 'GIT_PYTHON_GIT_EXECUTABLE',
                os.path.join(FIXTURE_DIR, 'fake_git.sh'))
     with testtools.ExpectedException(git.exc.GitCommandError,
                                      r'.*exit code\(-9\)'):
         work_repo.update()
Beispiel #9
0
    def test_broken_cache(self):
        parent_path = os.path.join(self.upstream_root, 'org/project1')
        work_repo = Repo(parent_path, self.workspace_root, '*****@*****.**',
                         'User Name', '0', '0')
        self.waitUntilSettled()

        # Break the work repo
        path = work_repo.local_path
        os.remove(os.path.join(path, '.git/HEAD'))

        # And now reset the repo again. This should not crash
        work_repo.reset()
Beispiel #10
0
    def test_create_head_path(self):
        parent_path = os.path.join(self.upstream_root, 'org/project1')
        parent_repo = git.Repo(parent_path)
        parent_repo.create_head("refs/heads/foobar")
        parent_repo.create_head("refs/heads/refs/heads/foobar")

        work_repo = Repo(parent_path, self.workspace_root, '*****@*****.**',
                         'User Name', '0', '0')
        repo = work_repo.createRepoObject(None)
        self.assertIn('foobar', repo.branches)
        self.assertIn('refs/heads/foobar', repo.branches)
        self.assertNotIn('refs/heads/refs/heads/foobar', repo.branches)
 def test_clone_timeout(self):
     parent_path = os.path.join(self.upstream_root, 'org/project1')
     self.patch(git.Git, 'GIT_PYTHON_GIT_EXECUTABLE',
                os.path.join(FIXTURE_DIR, 'fake_git.sh'))
     work_repo = Repo(parent_path, self.workspace_root,
                      '*****@*****.**', 'User Name', '0', '0',
                      git_timeout=0.001, retry_attempts=1)
     # TODO: have the merger and repo classes catch fewer
     # exceptions, including this one on initialization.  For the
     # test, we try cloning again.
     with testtools.ExpectedException(git.exc.GitCommandError,
                                      r'.*exit code\(-9\)'):
         work_repo._ensure_cloned()
 def test_clone_timeout(self):
     parent_path = os.path.join(self.upstream_root, 'org/project1')
     self.patch(git.Git, 'GIT_PYTHON_GIT_EXECUTABLE',
                os.path.join(FIXTURE_DIR, 'fake_git.sh'))
     work_repo = Repo(parent_path, self.workspace_root,
                      '*****@*****.**', 'User Name', '0', '0',
                      git_timeout=0.001, retry_attempts=1)
     # TODO: have the merger and repo classes catch fewer
     # exceptions, including this one on initialization.  For the
     # test, we try cloning again.
     with testtools.ExpectedException(git.exc.GitCommandError,
                                      r'.*exit code\(-9\)'):
         work_repo._ensure_cloned(None)
    def test_set_remote_ref(self):
        parent_path = os.path.join(self.upstream_root, 'org/project1')
        commit_sha = self.create_commit('org/project1')
        self.create_commit('org/project1')

        work_repo = Repo(parent_path, self.workspace_root,
                         '*****@*****.**', 'User Name', '0', '0')
        work_repo.setRemoteRef('master', commit_sha)
        work_repo.setRemoteRef('invalid', commit_sha)

        repo = git.Repo(self.workspace_root)
        self.assertEqual(repo.remotes.origin.refs.master.commit.hexsha,
                         commit_sha)
        self.assertNotIn('invalid', repo.remotes.origin.refs)
 def test_fetch_retry(self):
     parent_path = os.path.join(self.upstream_root, 'org/project1')
     work_repo = Repo(parent_path, self.workspace_root,
                      '*****@*****.**', 'User Name', '0', '0',
                      retry_interval=1)
     self.patch(git.Git, 'GIT_PYTHON_GIT_EXECUTABLE',
                os.path.join(FIXTURE_DIR, 'git_fetch_error.sh'))
     work_repo.update()
     # This is created on the first fetch
     self.assertTrue(os.path.exists(os.path.join(
         self.workspace_root, 'stamp1')))
     # This is created on the second fetch
     self.assertTrue(os.path.exists(os.path.join(
         self.workspace_root, 'stamp2')))
Beispiel #15
0
    def cloneUpstream(self, project, dest):
        git_upstream = '%s/%s' % (self.git_url, project)
        self.log.info("Creating repo %s from upstream %s",
                      project, git_upstream)
        repo = Repo(
            remote=git_upstream,
            local=dest,
            email=None,
            username=None)

        if not repo.isInitialized():
            raise Exception("Error cloning %s to %s" % (git_upstream, dest))

        return repo
 def test_fetch_retry(self):
     parent_path = os.path.join(self.upstream_root, 'org/project1')
     work_repo = Repo(parent_path, self.workspace_root,
                      '*****@*****.**', 'User Name', '0', '0',
                      retry_interval=1)
     self.patch(git.Git, 'GIT_PYTHON_GIT_EXECUTABLE',
                os.path.join(FIXTURE_DIR, 'git_fetch_error.sh'))
     work_repo.update()
     # This is created on the first fetch
     self.assertTrue(os.path.exists(os.path.join(
         self.workspace_root, 'stamp1')))
     # This is created on the second fetch
     self.assertTrue(os.path.exists(os.path.join(
         self.workspace_root, 'stamp2')))
Beispiel #17
0
    def test_branch_rename(self):
        parent_path = os.path.join(self.upstream_root, 'org/project1')
        # Clone upstream so that current head is master
        work_repo = Repo(parent_path, self.workspace_root, '*****@*****.**',
                         'User Name', '0', '0')

        # Rename master to main in upstream repo
        gitrepo = git.Repo(parent_path)
        main_branch = gitrepo.create_head('main')
        gitrepo.head.reference = main_branch
        gitrepo.delete_head(gitrepo.heads['master'], force=True)

        # And now reset the repo. This should not crash
        work_repo.reset()
Beispiel #18
0
    def test_create_head_at_char(self):
        """Test that we can create branches containing the '@' char.

        This is a regression test to make sure we are not using GitPython
        APIs that interpret the '@' as a special char.
        """
        parent_path = os.path.join(self.upstream_root, 'org/project1')
        parent_repo = git.Repo(parent_path)
        parent_repo.create_head("refs/heads/foo@bar")

        work_repo = Repo(parent_path, self.workspace_root, '*****@*****.**',
                         'User Name', '0', '0')
        repo = work_repo.createRepoObject(None)
        self.assertIn('foo@bar', repo.branches)
Beispiel #19
0
    def test_update_needed(self):
        parent_path = os.path.join(self.upstream_root, 'org/project1')
        repo = git.Repo(parent_path)
        self.create_branch('org/project1', 'stable')

        repo_state_no_update_master = {
            'refs/heads/master': repo.commit('refs/heads/master').hexsha,
        }
        repo_state_no_update = {
            'refs/heads/master': repo.commit('refs/heads/master').hexsha,
            'refs/heads/stable': repo.commit('refs/heads/stable').hexsha,
        }
        repo_state_update = {
            'refs/heads/master': repo.commit('refs/heads/master').hexsha,
            'refs/heads/stable': repo.commit('refs/heads/stable').hexsha,
            'refs/heads/test': '1234567',
        }

        work_repo = Repo(parent_path, self.workspace_root,
                         '*****@*****.**', 'User Name', '0', '0')
        self.assertFalse(work_repo.isUpdateNeeded(repo_state_no_update_master))
        self.assertFalse(work_repo.isUpdateNeeded(repo_state_no_update))
        self.assertTrue(work_repo.isUpdateNeeded(repo_state_update))

        # Get repo and update for the first time.
        merger = self.executor_server.merger
        merger.updateRepo('gerrit', 'org/project1')
        repo = merger.getRepo('gerrit', 'org/project1')

        # Branches master and stable must exist
        self.assertEqual(['master', 'stable'], repo.getBranches())

        # Now create an additional branch in the parent repo
        self.create_branch('org/project1', 'stable2')

        # Update with repo state and expect no update done
        self.log.info('Calling updateRepo with repo_state_no_update')
        merger.updateRepo('gerrit', 'org/project1',
                          repo_state=repo_state_no_update)
        repo = merger.getRepo('gerrit', 'org/project1')
        self.assertEqual(['master', 'stable'], repo.getBranches())

        # Update with repo state and expect update
        self.log.info('Calling updateRepo with repo_state_update')
        merger.updateRepo('gerrit', 'org/project1',
                          repo_state=repo_state_update)
        repo = merger.getRepo('gerrit', 'org/project1')
        self.assertEqual(['master', 'stable', 'stable2'], repo.getBranches())
Beispiel #20
0
    def test_broken_gitmodules(self):
        parent_path = os.path.join(self.upstream_root, 'org/project1')
        work_repo = Repo(parent_path, self.workspace_root, '*****@*****.**',
                         'User Name', '0', '0')
        self.waitUntilSettled()

        # Break the gitmodules
        path = work_repo.local_path
        with open(os.path.join(path, '.gitmodules'), 'w') as f:
            f.write('[submodule "libfoo"]\n'
                    'path = include/foo\n'
                    '---\n'
                    'url = git://example.com/git/lib.git')

        # And now reset the repo again. This should not crash
        work_repo.reset()
    def test_broken_gitmodules(self):
        parent_path = os.path.join(self.upstream_root, 'org/project1')
        work_repo = Repo(parent_path, self.workspace_root,
                         '*****@*****.**', 'User Name', '0', '0')
        self.waitUntilSettled()

        # Break the gitmodules
        path = work_repo.local_path
        with open(os.path.join(path, '.gitmodules'), 'w') as f:
            f.write('[submodule "libfoo"]\n'
                    'path = include/foo\n'
                    '---\n'
                    'url = git://example.com/git/lib.git')

        # And now reset the repo again. This should not crash
        work_repo.reset()
Beispiel #22
0
    def test_deleted_local_ref(self):
        parent_path = os.path.join(self.upstream_root, 'org/project1')
        self.create_branch('org/project1', 'foobar')

        work_repo = Repo(parent_path, self.workspace_root, '*****@*****.**',
                         'User Name', '0', '0')

        # Delete local ref on the cached repo. This leaves us with a remote
        # ref but no local ref anymore.
        gitrepo = git.Repo(work_repo.local_path)
        gitrepo.delete_head('foobar', force=True)

        # Delete the branch upstream.
        self.delete_branch('org/project1', 'foobar')

        # And now reset the repo again. This should not crash
        work_repo.reset()
    def test_deleted_local_ref(self):
        parent_path = os.path.join(self.upstream_root, 'org/project1')
        self.create_branch('org/project1', 'foobar')

        work_repo = Repo(parent_path, self.workspace_root,
                         '*****@*****.**', 'User Name', '0', '0')

        # Delete local ref on the cached repo. This leaves us with a remote
        # ref but no local ref anymore.
        gitrepo = git.Repo(work_repo.local_path)
        gitrepo.delete_head('foobar', force=True)

        # Delete the branch upstream.
        self.delete_branch('org/project1', 'foobar')

        # And now reset the repo again. This should not crash
        work_repo.reset()
Beispiel #24
0
    def test_broken_cache(self):
        parent_path = os.path.join(self.upstream_root, 'org/project1')
        work_repo = Repo(parent_path, self.workspace_root, '*****@*****.**',
                         'User Name', '0', '0')
        self.waitUntilSettled()

        # Break the work repo
        path = work_repo.local_path
        os.remove(os.path.join(path, '.git/HEAD'))

        # And now reset the repo again. This should not crash
        work_repo.reset()

        # Now open a cache repo and break it in a way that git.Repo is happy
        # at first but git won't be because of a broken HEAD revision.
        merger = self.executor_server.merger
        cache_repo = merger.getRepo('gerrit', 'org/project')
        with open(os.path.join(cache_repo.local_path, '.git/HEAD'), 'w'):
            pass
        cache_repo.update()

        # Now open a cache repo and break it in a way that git.Repo is happy
        # at first but git won't be because of a corrupt object file.
        #
        # To construct this we create a commit so we have a guaranteed free
        # object file, then we break it by truncating it.
        fn = os.path.join(cache_repo.local_path, 'commit_filename')
        with open(fn, 'a') as f:
            f.write("test")
        repo = cache_repo.createRepoObject(None)
        repo.index.add([fn])
        repo.index.commit('test commit')

        # Pick the first object file we find and break it
        objects_path = os.path.join(cache_repo.local_path, '.git', 'objects')
        object_dir = os.path.join(objects_path, [
            d for d in os.listdir(objects_path) if len(d) == 2
        ][0])
        object_to_break = os.path.join(object_dir, os.listdir(object_dir)[0])
        self.log.error(os.stat(object_to_break))
        os.chmod(object_to_break, 644)
        with open(object_to_break, 'w'):
            pass
        os.chmod(object_to_break, 444)
        cache_repo.update()
Beispiel #25
0
    def test_garbage_collect(self):
        '''Tests that git gc doesn't prune FETCH_HEAD'''
        parent_path = os.path.join(self.upstream_root, 'org/project1')
        repo = git.Repo(parent_path)
        change_ref = 'refs/changes/1/1'

        self.log.info('Creating a commit on %s', change_ref)
        repo.head.reference = repo.head.commit
        files = {"README": "creating fake commit\n"}
        for name, content in files.items():
            file_name = os.path.join(parent_path, name)
            with open(file_name, 'a') as f:
                f.write(content)
            repo.index.add([file_name])
        commit = repo.index.commit('Test commit')
        ref = git.refs.Reference(repo, change_ref)
        ref.set_commit(commit)

        self.log.info('Cloning parent repo')
        work_repo = Repo(parent_path, self.workspace_root, '*****@*****.**',
                         'User Name', '0', '0')

        self.log.info('Fetch %s', change_ref)
        work_repo.fetch(change_ref)

        self.log.info('Checkout master and run garbage collection')
        work_repo_object = work_repo.createRepoObject(None)
        work_repo.checkout('master')
        result = work_repo_object.git.gc('--prune=now')
        self.log.info(result)

        self.log.info('Dereferencing FETCH_HEAD')
        commit = work_repo_object.commit('FETCH_HEAD')
        self.assertIsNotNone(commit)
Beispiel #26
0
    def test_ensure_cloned(self):
        parent_path = os.path.join(self.upstream_root, "org/project1")

        # Forge a repo having a submodule
        parent_repo = git.Repo(parent_path)
        parent_repo.git.submodule("add", os.path.join(self.upstream_root, "org/project2"), "subdir")
        parent_repo.index.commit("Adding project2 as a submodule in subdir")
        # git 1.7.8 changed .git from being a directory to a file pointing
        # to the parent repository /.git/modules/*
        self.assertTrue(
            os.path.exists(os.path.join(parent_path, "subdir", ".git")), msg=".git file in submodule should be a file"
        )

        work_repo = Repo(parent_path, self.workspace_root, "*****@*****.**", "User Name")
        self.assertTrue(
            os.path.isdir(os.path.join(self.workspace_root, "subdir")),
            msg="Cloned repository has a submodule placeholder directory",
        )
        self.assertFalse(
            os.path.exists(os.path.join(self.workspace_root, "subdir", ".git")), msg="Submodule is not initialized"
        )

        sub_repo = Repo(
            os.path.join(self.upstream_root, "org/project2"),
            os.path.join(self.workspace_root, "subdir"),
            "*****@*****.**",
            "User Name",
        )
        self.assertTrue(
            os.path.exists(os.path.join(self.workspace_root, "subdir", ".git")),
            msg="Cloned over the submodule placeholder",
        )

        self.assertEquals(
            os.path.join(self.upstream_root, "org/project1"),
            work_repo.createRepoObject().remotes[0].url,
            message="Parent clone still point to upstream project1",
        )

        self.assertEquals(
            os.path.join(self.upstream_root, "org/project2"),
            sub_repo.createRepoObject().remotes[0].url,
            message="Sub repository points to upstream project2",
        )
    def test_broken_cache(self):
        parent_path = os.path.join(self.upstream_root, 'org/project1')
        work_repo = Repo(parent_path, self.workspace_root,
                         '*****@*****.**', 'User Name', '0', '0')
        self.waitUntilSettled()

        # Break the work repo
        path = work_repo.local_path
        os.remove(os.path.join(path, '.git/HEAD'))

        # And now reset the repo again. This should not crash
        work_repo.reset()

        # Now open a cache repo and break it in a way that git.Repo is happy
        # at first but git won't be.
        merger = self.executor_server.merger
        cache_repo = merger.getRepo('gerrit', 'org/project')
        with open(os.path.join(cache_repo.local_path, '.git/HEAD'), 'w'):
            pass
        cache_repo.update()
Beispiel #28
0
    def test_broken_cache(self):
        parent_path = os.path.join(self.upstream_root, 'org/project1')
        work_repo = Repo(parent_path, self.workspace_root, '*****@*****.**',
                         'User Name', '0', '0')
        self.waitUntilSettled()

        # Break the work repo
        path = work_repo.local_path
        os.remove(os.path.join(path, '.git/HEAD'))

        # And now reset the repo again. This should not crash
        work_repo.reset()

        # Now open a cache repo and break it in a way that git.Repo is happy
        # at first but git won't be.
        merger = self.executor_server.merger
        cache_repo = merger.getRepo('gerrit', 'org/project')
        with open(os.path.join(cache_repo.local_path, '.git/HEAD'), 'w'):
            pass
        cache_repo.update()
    def test_ensure_cloned(self):
        parent_path = os.path.join(self.upstream_root, 'org/project1')

        # Forge a repo having a submodule
        parent_repo = git.Repo(parent_path)
        parent_repo.git.submodule('add', os.path.join(
            self.upstream_root, 'org/project2'), 'subdir')
        parent_repo.index.commit('Adding project2 as a submodule in subdir')
        # git 1.7.8 changed .git from being a directory to a file pointing
        # to the parent repository /.git/modules/*
        self.assertTrue(os.path.exists(
            os.path.join(parent_path, 'subdir', '.git')),
            msg='.git file in submodule should be a file')

        work_repo = Repo(parent_path, self.workspace_root,
                         '*****@*****.**', 'User Name', '0', '0')
        self.assertTrue(
            os.path.isdir(os.path.join(self.workspace_root, 'subdir')),
            msg='Cloned repository has a submodule placeholder directory')
        self.assertFalse(os.path.exists(
            os.path.join(self.workspace_root, 'subdir', '.git')),
            msg='Submodule is not initialized')

        sub_repo = Repo(
            os.path.join(self.upstream_root, 'org/project2'),
            os.path.join(self.workspace_root, 'subdir'),
            '*****@*****.**', 'User Name', '0', '0')
        self.assertTrue(os.path.exists(
            os.path.join(self.workspace_root, 'subdir', '.git')),
            msg='Cloned over the submodule placeholder')

        self.assertEqual(
            os.path.join(self.upstream_root, 'org/project1'),
            work_repo.createRepoObject().remotes[0].url,
            message="Parent clone still point to upstream project1")

        self.assertEqual(
            os.path.join(self.upstream_root, 'org/project2'),
            sub_repo.createRepoObject().remotes[0].url,
            message="Sub repository points to upstream project2")
Beispiel #30
0
    def cloneUpstream(self, project, dest):
        # Check for a cached git repo first
        git_cache = '%s/%s' % (self.cache_dir, project)
        git_upstream = '%s/%s' % (self.git_url, project)
        if (self.cache_dir and os.path.exists(git_cache)
                and not os.path.exists(dest)):
            # file:// tells git not to hard-link across repos
            git_cache = 'file://%s' % git_cache
            self.log.info("Creating repo %s from cache %s", project, git_cache)
            new_repo = git.Repo.clone_from(git_cache, dest)
            self.log.info("Updating origin remote in repo %s to %s", project,
                          git_upstream)
            new_repo.remotes.origin.config_writer.set('url', git_upstream)
        else:
            self.log.info("Creating repo %s from upstream %s", project,
                          git_upstream)
        repo = Repo(remote=git_upstream, local=dest, email=None, username=None)

        if not repo.isInitialized():
            raise Exception("Error cloning %s to %s" % (git_upstream, dest))

        return repo
Beispiel #31
0
    def test_set_refs(self):
        parent_path = os.path.join(self.upstream_root, 'org/project1')
        remote_sha = self.create_commit('org/project1')
        self.create_branch('org/project1', 'foobar')

        work_repo = Repo(parent_path, self.workspace_root, '*****@*****.**',
                         'User Name', '0', '0')
        repo = git.Repo(self.workspace_root)
        new_sha = repo.heads.foobar.commit.hexsha

        work_repo.setRefs({'refs/heads/master': new_sha}, True)
        self.assertEqual(work_repo.getBranchHead('master').hexsha, new_sha)
        self.assertIn('master', repo.remotes.origin.refs)

        work_repo.setRefs({'refs/heads/master': remote_sha})
        self.assertEqual(work_repo.getBranchHead('master').hexsha, remote_sha)
        self.assertNotIn('master', repo.remotes.origin.refs)
    def test_set_refs(self):
        parent_path = os.path.join(self.upstream_root, 'org/project1')
        remote_sha = self.create_commit('org/project1')
        self.create_branch('org/project1', 'foobar')

        work_repo = Repo(parent_path, self.workspace_root,
                         '*****@*****.**', 'User Name', '0', '0')
        repo = git.Repo(self.workspace_root)
        new_sha = repo.heads.foobar.commit.hexsha

        work_repo.setRefs({'refs/heads/master': new_sha}, True)
        self.assertEqual(work_repo.getBranchHead('master').hexsha, new_sha)
        self.assertIn('master', repo.remotes.origin.refs)

        work_repo.setRefs({'refs/heads/master': remote_sha})
        self.assertEqual(work_repo.getBranchHead('master').hexsha, remote_sha)
        self.assertNotIn('master', repo.remotes.origin.refs)
Beispiel #33
0
    def test_set_remote_ref(self):
        parent_path = os.path.join(self.upstream_root, 'org/project1')
        commit_sha = self.create_commit('org/project1')
        self.create_commit('org/project1')

        work_repo = Repo(parent_path, self.workspace_root, '*****@*****.**',
                         'User Name', '0', '0')
        work_repo.setRemoteRef('master', commit_sha)
        work_repo.setRemoteRef('invalid', commit_sha)

        repo = git.Repo(self.workspace_root)
        self.assertEqual(repo.remotes.origin.refs.master.commit.hexsha,
                         commit_sha)
        self.assertNotIn('invalid', repo.remotes.origin.refs)
    def test_ensure_cloned(self):
        parent_path = os.path.join(self.upstream_root, 'org/project1')

        # Forge a repo having a submodule
        parent_repo = git.Repo(parent_path)
        parent_repo.git.submodule('add', os.path.join(
            self.upstream_root, 'org/project2'), 'subdir')
        parent_repo.index.commit('Adding project2 as a submodule in subdir')
        # git 1.7.8 changed .git from being a directory to a file pointing
        # to the parent repository /.git/modules/*
        self.assertTrue(os.path.exists(
            os.path.join(parent_path, 'subdir', '.git')),
            msg='.git file in submodule should be a file')

        work_repo = Repo(parent_path, self.workspace_root,
                         '*****@*****.**', 'User Name', '0', '0')
        self.assertTrue(
            os.path.isdir(os.path.join(self.workspace_root, 'subdir')),
            msg='Cloned repository has a submodule placeholder directory')
        self.assertFalse(os.path.exists(
            os.path.join(self.workspace_root, 'subdir', '.git')),
            msg='Submodule is not initialized')

        sub_repo = Repo(
            os.path.join(self.upstream_root, 'org/project2'),
            os.path.join(self.workspace_root, 'subdir'),
            '*****@*****.**', 'User Name', '0', '0')
        self.assertTrue(os.path.exists(
            os.path.join(self.workspace_root, 'subdir', '.git')),
            msg='Cloned over the submodule placeholder')

        self.assertEqual(
            os.path.join(self.upstream_root, 'org/project1'),
            work_repo.createRepoObject(None).remotes[0].url,
            message="Parent clone still point to upstream project1")

        self.assertEqual(
            os.path.join(self.upstream_root, 'org/project2'),
            sub_repo.createRepoObject(None).remotes[0].url,
            message="Sub repository points to upstream project2")
Beispiel #35
0
 def test_repo_repr(self):
     local_path = "/dev/null"
     repo = Repo("remote", local_path,
                 "*****@*****.**", "User Name", "0", "0")
     self.assertIn(local_path, repr(repo))
Beispiel #36
0
    def test_repo_reset_branch_conflict(self):
        """Test correct reset with conflicting branch names"""
        parent_path = os.path.join(self.upstream_root, 'org/project1')

        parent_repo = git.Repo(parent_path)
        parent_repo.create_head("foobar")

        work_repo = Repo(parent_path, self.workspace_root, '*****@*****.**',
                         'User Name', '0', '0')

        # Checkout branch that will be deleted from the remote repo
        work_repo.checkout("foobar")

        # Delete remote branch and create a branch that conflicts with
        # the branch checked out locally.
        parent_repo.delete_head("foobar")
        parent_repo.create_head("foobar/sub")

        work_repo.reset()
        work_repo.checkout("foobar/sub")

        # Try the reverse conflict
        parent_path = os.path.join(self.upstream_root, 'org/project2')

        parent_repo = git.Repo(parent_path)
        parent_repo.create_head("foobar/sub")

        work_repo = Repo(parent_path, self.workspace_root, '*****@*****.**',
                         'User Name', '0', '0')

        # Checkout branch that will be deleted from the remote repo
        work_repo.checkout("foobar/sub")

        # Delete remote branch and create a branch that conflicts with
        # the branch checked out locally.
        parent_repo.delete_head("foobar/sub")

        # Note: Before git 2.13 deleting a a ref foo/bar leaves an empty
        # directory foo behind that will block creating the reference foo
        # in the future. As a workaround we must clean up empty directories
        # in .git/refs.
        if parent_repo.git.version_info[:2] < (2, 13):
            Repo._cleanup_leaked_ref_dirs(parent_path, None, [])

        parent_repo.create_head("foobar")

        work_repo.reset()
        work_repo.checkout("foobar")
Beispiel #37
0
    def test_merge_temp_refs(self):
        """
        Test that the merge updates local zuul refs in order to avoid
        garbage collection of needed objects.
        """
        merger = self.executor_server.merger

        parent_path = os.path.join(self.upstream_root, 'org/project')
        parent_repo = git.Repo(parent_path)
        parent_repo.create_head("foo/bar")

        # Simple change A
        A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
        item_a = self._item_from_fake_change(A)

        # Simple change B on branch foo/bar
        B = self.fake_gerrit.addFakeChange('org/project', 'foo/bar', 'B')
        item_b = self._item_from_fake_change(B)

        # Simple change C
        C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
        item_c = self._item_from_fake_change(C)

        # Merge A -> B -> C
        result = merger.mergeChanges([item_a, item_b, item_c])
        self.assertIsNotNone(result)
        merge_state = result[3]

        cache_repo = merger.getRepo('gerrit', 'org/project')
        repo = cache_repo.createRepoObject(zuul_event_id="dummy")

        # Make sure zuul refs are updated
        foobar_zuul_ref = Repo.refNameToZuulRef("foo/bar")
        master_zuul_ref = Repo.refNameToZuulRef("master")
        ref_map = {r.path: r for r in repo.refs}
        self.assertIn(foobar_zuul_ref, ref_map)
        self.assertIn(master_zuul_ref, ref_map)

        self.assertEqual(ref_map[master_zuul_ref].commit.hexsha,
                         merge_state[("gerrit", "org/project", "master")])
        self.assertEqual(ref_map[foobar_zuul_ref].commit.hexsha,
                         merge_state[("gerrit", "org/project", "foo/bar")])

        # Delete the remote branch so a reset cleanes up the local branch
        parent_repo.delete_head('foo/bar', force=True)

        # Note: Before git 2.13 deleting a a ref foo/bar leaves an empty
        # directory foo behind that will block creating the reference foo
        # in the future. As a workaround we must clean up empty directories
        # in .git/refs.
        if parent_repo.git.version_info[:2] < (2, 13):
            Repo._cleanup_leaked_ref_dirs(parent_path, None, [])

        cache_repo.reset()
        self.assertNotIn(foobar_zuul_ref, [r.path for r in repo.refs])

        # Create another head 'foo' that can't be created if the 'foo/bar'
        # branch wasn't cleaned up properly
        parent_repo.create_head("foo")

        # Change B now on branch 'foo'
        B = self.fake_gerrit.addFakeChange('org/project', 'foo', 'B')
        item_b = self._item_from_fake_change(B)

        # Merge A -> B -> C
        result = merger.mergeChanges([item_a, item_b, item_c])
        self.assertIsNotNone(result)
        merge_state = result[3]

        foo_zuul_ref = Repo.refNameToZuulRef("foo")
        ref_map = {r.path: r for r in repo.refs}

        self.assertIn(foo_zuul_ref, ref_map)
        self.assertIn(master_zuul_ref, ref_map)
        self.assertEqual(ref_map[master_zuul_ref].commit.hexsha,
                         merge_state[("gerrit", "org/project", "master")])
        self.assertEqual(ref_map[foo_zuul_ref].commit.hexsha,
                         merge_state[("gerrit", "org/project", "foo")])