Ejemplo n.º 1
0
def write_file(repo: pygit2.Repository, tree: pygit2.Tree, filepath: str,
               contents: str) -> pygit2.Oid:
    blob = repo.create_blob(contents.encode("utf-8"))
    paths = filepath.split("/")
    trees = [tree]
    for path in paths[:-1]:
        try:
            to_insert = repo[trees[0][path].oid]
        except KeyError:
            to_insert = None
        trees.insert(0, to_insert)

    to_insert = blob
    for path in reversed(paths):
        tree = trees.pop(0)
        assert isinstance(to_insert, pygit2.Oid)
        assert isinstance(repo[to_insert], pygit2.Blob) or isinstance(
            repo[to_insert], pygit2.Tree)
        if tree is None:
            tb = repo.TreeBuilder()
        else:
            tb = repo.TreeBuilder(tree)
        tb.insert(
            path, to_insert, pygit2.GIT_FILEMODE_BLOB if isinstance(
                repo[to_insert], pygit2.Blob) else pygit2.GIT_FILEMODE_TREE)
        to_insert = tb.write()

    assert len(trees) == 0

    return to_insert
Ejemplo n.º 2
0
def update_tree(
    repo: git.Repository, tree: git.Oid, path: List[str], content: str
) -> git.Oid:
    """
    adds a blob with `content` at `path` to `tree` in `repo`

    >>> repo = create_repo()
    >>> tree = repo.TreeBuilder().write()
    >>> for i in range(10):
    ...    path = store_hash(f"{i}")
    ...    content = nar_hash(path)
    ...    tree = update_tree(repo, tree, common.shards(path, depth=5), content)
    >>> print(tree)
    00f68bdb866b654d4ce3da90609b74137605bd90
    """
    for entry in repo.get(tree):
        # subdir exists: recurse
        if (entry.name == path[0]) and (entry.type == "tree"):
            sub = update_tree(repo, entry.id, path[1:], content)
            builder = repo.TreeBuilder(repo.get(tree))
            builder.remove(path[0])
            builder.insert(path[0], sub, git.GIT_FILEMODE_TREE)
            return builder.write()

    # subdir does not exist: create required objects
    if len(path) > 1:
        # write leaf node
        sub = update_tree(repo, repo.TreeBuilder().write(), [path[-1]], content)
        # build intermediate nodes
        for d in reversed(path[1:-1]):
            builder = repo.TreeBuilder()
            builder.insert(d, sub, git.GIT_FILEMODE_TREE)
            sub = builder.write()

        # attach to `tree`
        builder = repo.TreeBuilder(repo.get(tree))
        builder.insert(path[0], sub, git.GIT_FILEMODE_TREE)
        return builder.write()

    # path[0] is not a subdir: write blob
    elif len(path) == 1:
        blob = repo.write(git.GIT_OBJ_BLOB, content)
        builder = repo.TreeBuilder(repo.get(tree))
        builder.insert(path[0], blob, git.GIT_FILEMODE_BLOB)
        return builder.write()

    else:
        raise Exception(f"invalid path: {path}")
Ejemplo n.º 3
0
class ChangeCtxBaseTestCase(unittest.TestCase):
    def setUp(self):
        self.repo_path = mkdtemp()
        init_repository(self.repo_path, False)
        self.repo = Repository(self.repo_path)

        # create files and commit
        self.sign = Signature('foo', '*****@*****.**')
        self.repo_files = ['a%i.rst' % i for i in range(5)]
        for i in self.repo_files:
            with codecs.open(os.path.join(self.repo_path, i),
                             'w',
                             encoding='utf-8') as fp:
                fp.write('dumb file %s\n' % i)
        self.tree = self.repo.TreeBuilder()
        self.old_commit = git_commit(self.repo, self.tree, self.repo_files)

    def tearDown(self):
        try:
            rmtree(self.repo_path)
        except:
            pass

    @property
    def ctx_class(self):
        raise NotImplementedError

    def get_ctx(self):
        return self.ctx_class(self.repo_path)
Ejemplo n.º 4
0
class FileCtxTestCase(unittest.TestCase):
    def setUp(self):
        self.repo_path = mkdtemp()
        init_repository(self.repo_path, False)
        self.repo = Repository(self.repo_path)
        self.file_name = 'foo.rst'
        self.file_path = os.path.join(self.repo_path, self.file_name)
        with codecs.open(self.file_path, 'w', encoding='utf-8') as fp:
            fp.write('test\n')
        self.tree = self.repo.TreeBuilder()
        self.last_commit = git_commit(self.repo, self.tree, [self.file_name])
        self.changectx = self.repo.head

    def tearDown(self):
        try:
            rmtree(self.repo_path)
        except:
            pass

    def test_path(self):
        ctx = FileCtx(self.repo, self.changectx, self.file_name)
        self.assertEqual(ctx.path, 'foo.rst')

    def test_content(self):
        ctx = FileCtx(self.repo, self.changectx, self.file_name)
        self.assertEqual(ctx.content, 'test\n')
        with codecs.open(self.file_path, 'a', encoding='utf-8') as fp:
            fp.write('lol\n')  # change file without git add
        ctx = FileCtx(self.repo, self.changectx, self.file_name)
        self.assertEqual(ctx.content, 'test\n')

    def test_content_from_index_read_without_git_add(self):
        ctx = FileCtx(self.repo, self.changectx, self.file_name, True)
        self.assertEqual(ctx.content, 'test\n')
        with codecs.open(self.file_path, 'a', encoding='utf-8') as fp:
            fp.write('lol\n')  # change file without git add
        ctx = FileCtx(self.repo, self.changectx, self.file_name, True)
        self.assertEqual(ctx.content, 'test\nlol\n')

    def test_author(self):
        ctx = FileCtx(self.repo, self.changectx, self.file_name)
        self.assertEqual(ctx.author, 'foo <*****@*****.**>')

    def test_date_and_mdate(self):
        ctx = FileCtx(self.repo, self.changectx, self.file_name)
        time.sleep(1)
        self.assertTrue(ctx.date < time.time())
        self.assertTrue(ctx.mdate is None)
        old_date = ctx.date
        time.sleep(1)
        with codecs.open(self.file_path, 'a', encoding='utf-8') as fp:
            fp.write('foo\n')
        git_commit(self.repo, self.tree, [self.file_name], [self.last_commit])
        ctx = FileCtx(self.repo, self.changectx, self.file_name)
        self.assertEqual(ctx.date, old_date)
        self.assertTrue(ctx.mdate > old_date)
Ejemplo n.º 5
0
def get_empty_tree_hash(repo: pygit2.Repository) -> str:
    """
    The hash for the empty tree can be generated using: `git hash-object -t tree /dev/null`
    (see this Stack Overflow post for more details: https://stackoverflow.com/q/9765453)
    We prefer to generate the empty hash using the pygit2 Repository's TreeBuilder, which avoids use
    of the SHA-1 magic number: `4b825dc642cb6eb9a060e54bf8d69288fbee4904`.
    This makes locust as future-compatible as pygit2.
    """
    tree_builder = repo.TreeBuilder()
    return str(tree_builder.write())
Ejemplo n.º 6
0
class GitRepositoryTestCase(unittest.TestCase):
    def setUp(self):
        self.repo_path = mkdtemp()
        init_repository(self.repo_path, False)
        self.repo = Repository(self.repo_path)

    def tearDown(self):
        try:
            rmtree(self.repo_path)
        except:
            pass

    def test_create_repo(self):
        repo_path = mkdtemp()
        try:
            GitRepository.create_repo(repo_path)
            for f in [
                    os.path.join('content', 'attachments', 'mercurial.png'),
                    os.path.join('content', 'post', 'example-post.rst'),
                    os.path.join('content', 'post', 'lorem-ipsum.rst'),
                    os.path.join('content', 'about.rst'),
                    os.path.join('static', 'screen.css'),
                    os.path.join('templates', 'base.html'),
                    os.path.join('templates', 'posts.html'),
                    os.path.join('templates', 'post_list.html'), 'config.yaml',
                    '.gitignore', '.git'
            ]:
                self.assertTrue(os.path.exists(os.path.join(repo_path, f)),
                                'Not found: %s' % f)
        finally:
            rmtree(repo_path)

    def test_get_changectx_rev_default(self):
        git_repo = GitRepository(self.repo_path)
        with codecs.open(os.path.join(self.repo_path, 'foo.rst'),
                         'w',
                         encoding='utf-8') as fp:
            fp.write('foo')
        sign = Signature('foo', '*****@*****.**')
        tree = self.repo.TreeBuilder().write()
        self.repo.index.add('foo.rst')
        self.repo.create_commit('refs/heads/master', sign, sign, 'foo', tree,
                                [])
        self.assertTrue(
            isinstance(git_repo.get_changectx(REVISION_DEFAULT),
                       ChangeCtxDefault),
            'changectx object is not an instance of '
            'ChangeCtxDefault')

    def test_get_changectx_rev_working_dir(self):
        git_repo = GitRepository(self.repo_path)
        with codecs.open(os.path.join(self.repo_path, 'foo.rst'),
                         'w',
                         encoding='utf-8') as fp:
            fp.write('foo')
        sign = Signature('foo', '*****@*****.**')
        tree = self.repo.TreeBuilder().write()
        self.repo.index.add('foo.rst')
        self.repo.create_commit('refs/heads/master', sign, sign, 'foo', tree,
                                [])
        self.assertTrue(
            isinstance(git_repo.get_changectx(REVISION_WORKING_DIR),
                       ChangeCtxWorkingDir),
            'changectx object is not an instance of ChangeCtxWorkingDir')
Ejemplo n.º 7
0
class PyGitEngine(GitContentDatabaseEngine):
    def __init__(self, config):
        super(PyGitEngine, self).__init__(config)
        self.repo = None

    def connect(self):
        """Create content directory"""
        if not isdir(self.content_path):
            init_repository(self.content_path, bare=True)
            self.repo = Repository(self.content_path)
            self.create_initial_commit()
        else:
            self.repo = Repository(self.content_path)

    @staticmethod
    def do_put(content_path, object_hashes, content, filename):
        """Perform put operation. This is used in the distributed wrapper"""
        content_hash = Repository(content_path).create_blob(content)
        result = object_hashes[filename] = str(content_hash)
        return result

    def put_attr(self, content, filename):
        """Return attributes for the do_put operation"""
        filename = self._inc_name(filename)
        return (self.content_path, self.object_hashes, content, filename)

    def put(self, content, filename="generic"):  # pylint: disable=method-hidden
        """Put content in the content database"""
        return self.do_put(*self.put_attr(content, filename))

    def get(self, content_hash):  # pylint: disable=method-hidden
        """Get content from the content database"""
        return_data = self.repo[content_hash].data
        return return_data

    def find_subhash(self, content_hash):
        """Find hash in git"""
        try:
            blob = self.repo.revparse_single(content_hash)
            return str(blob.id)
        except KeyError:
            return None

    def create_initial_commit(self):
        """Create the initial commit of the git repository"""
        empty_tree = self.repo.TreeBuilder().write()
        self.create_commit_object(self._initial_message, empty_tree)

    def create_commit_object(self, message, tree):
        """Create a commit object"""
        references = list(self.repo.references)

        master_ref = self.repo.lookup_reference(
            self._commit_ref) if len(references) > 0 else None

        parents = []
        if master_ref is not None:
            parents = [master_ref.peel().id]

        author = Signature(self._commit_name, self._commit_email)
        return self.repo.create_commit(self._commit_ref, author, author,
                                       message, tree, parents)

    def new_tree(self, parent):
        """Create new git tree"""
        return self.repo.TreeBuilder()

    def insert_blob(self, tree, basename, value):
        """Insert blob into tree"""
        tree.insert(basename, value, GIT_FILEMODE_BLOB)

    def insert_tree(self, tree, basename, value):
        """Insert tree into tree"""
        tree.insert(basename, value, GIT_FILEMODE_TREE)

    def write_tree(self, tree):
        """Write tree to git directory"""
        return tree.write()
Ejemplo n.º 8
0
class vgit_repo(metaclass=singleton):
    def __init__(self):
        self.repo = None

    def vgit_load_repo(self, path):
        if path is None:
            self.repo = None
            return
        try:
            self.repo = Repository(path)
        except ValueError:
            self.repo = None
        except Exception:
            self.repo = None

    def vgit_clone(self, path, url, log_cb):
        """Wraper method to clone a
        repository from a given `url' to a given `path'"""
        try:
            pygit2.clone_repository(url, path)
        except ValueError as err:
            log_cb(str(err))
            return False
        except Exception as err:
            log_cb(str(err))
            return False

        return True

    def vgit_init(self, path, log_cb, bare=False):
        """Wraper method to init a repository in given `path'"""
        try:
            pygit2.init_repository(path, bare)
        except ValueError as err:
            log_cb(str(err))
            return False
        except Exception as err:
            log_cb(str(err))
            return False

        return True

    def vgit_add_all(self, log_cb):
        """Wraper method to add all files to a commit."""
        try:
            if self.repo is None:
                log_cb(
                    "No .git in current directory. Use `init' to crete a new repository."
                )
                return False

            self.repo.index.add_all()
            self.repo.index.write()

        except ValueError as err:
            log_cb(str(err))
            return False
        except Exception as err:
            log_cb(str(err))
            return False

        return True

    def vgit_commit(self, user_name, email, branch, message, log_cb):
        """Wraper method for the commit -m "message" git command. `user_name' and
        `email' informations of the autor of the commit
        `user_name_author' and email_author: is the same idea of the
        commiter but for the author `branch' branch which the user
        want's to do the commit `message': message of the commit"""

        try:
            if self.repo is None:
                log_cb(
                    "No .git in current directory. Use `init' to crete a new repository."
                )
                return False

            reference = 'refs/heads/' + branch
            author = pygit2.Signature(user_name, email)
            # usando author e commiter como o mesmo ser
            self.repo.create_commit(reference, author, author, message,
                                    self.repo.TreeBuilder().write(),
                                    [self.repo.head.target])

        except ValueError as err:
            log_cb(str(err))
            return False
        except Exception as err:
            log_cb(str(err))
            return False

        return True

    def vgit_commits(self, log_cb):
        if self.repo is None:
            log_cb("No commits...")
        else:
            for commit in self.repo.walk(self.repo[self.repo.head.target].id,
                                         pygit2.GIT_SORT_TIME):
                log_cb('\n'.join([
                    'Commit: #{}'.format(commit.tree_id.hex),
                    'Author: {} <{}>'.format(commit.author.name,
                                             commit.author.email), 'Message: ',
                    commit.message, ''
                ]))

    def vgit_push(self, path, message, user_name_commiter, user_name_author,
                  email_commiter, email_author, branch, user_name_pusher,
                  user_passoword_pusher, log_cb):
        try:
            repo = Repository(path)
            index = repo.index
            reference = 'refs/heads/' + branch
            tree = index.write_tree()
            author = pygit2.Signature(user_name_author, email_author)
            commiter = pygit2.Signature(user_name_commiter, email_commiter)
            oid = repo.create_commit(reference, author, commiter, message,
                                     tree, [repo.head.target])
            credentials = pygit2.UserPass(user_name_pusher,
                                          user_passoword_pusher)
            remo = repo.remotes["origin"]
            remo.credentials = credentials
            aux = remo.url
            repo.remotes.set_push_url(user_name_pusher, aux)
            callbacks = pygit2.RemoteCallbacks(credentials=credentials)
            remo.push([reference], callbacks=callbacks)

        except ValueError as err:
            log_cb(str(err))
            return False
        except Exception as err:
            log_cb(str(err))
            return False

        return True
Ejemplo n.º 9
0
class GitHandler(object):
    def __init__(self, path, repo_path=None, update_working_copy=True):
        """
        Start a git handler in given repository.
        `update_working_copy`: wether also to update the working copy.
            By default, the git handler will only work on the git database.
            Updating the working copy can take a lot of time in
            large repositories.
        """
        self.path = path
        if repo_path is None:
            repo_path = self.path
        self.repo_path = repo_path
        self.update_working_copy = update_working_copy
        self.repo = Repository(self.repo_path)
        self.working_tree = self.get_last_tree()
        self.tree_modifier = TreeModifier(self.repo, self.working_tree)
        self.messages = []
        print("Started libgit2 git handler in ", self.path)

    def get_last_tree(self):
        if self.repo.head_is_unborn:
            tree_id = self.repo.TreeBuilder().write()
            return self.repo[tree_id]
        commit = self.repo[self.getCurrentCommit()]
        return commit.tree

    def insert_into_working_tree(self, blob_id, filename):
        self.tree_modifier.insert_blob(blob_id, filename)

    def remove_from_working_tree(self, filename):
        self.tree_modifier.remove_blob(filename)

    def write_file(self, filename, content):
        # TODO: combine writing many files
        assert isinstance(content, text_type)
        data = content.encode('utf-8')
        existing_entry = get_tree_entry(self.repo, self.working_tree, filename)
        if existing_entry:
            type = 'M'
            if existing_entry.id == git_hash(data):
                return
        else:
            type = 'A'
        blob_id = self.repo.create_blob(data)
        self.insert_into_working_tree(blob_id, filename)

        if not self.repo.is_bare and self.update_working_copy:
            real_filename = os.path.join(self.path, filename)
            mkdir_p(os.path.dirname(real_filename))
            with codecs.open(real_filename, 'w', encoding='utf-8') as outfile:
                outfile.write(content)

        self.messages.append('    {}  {}'.format(type, filename))

    def remove_file(self, filename):
        existing_entry = get_tree_entry(self.repo, self.working_tree, filename)
        if existing_entry:
            self.remove_from_working_tree(filename)

            if not self.repo.is_bare and self.update_working_copy:
                remove_file_with_empty_parents(self.path, filename)

            self.messages.append('    D  {}'.format(filename))

    def move_file(self, old_filename, new_filename):
        self.tree_modifier.move(old_filename, new_filename)

        if not self.repo.is_bare and self.update_working_copy:
            real_old_filename = os.path.join(self.path, old_filename)
            real_new_filename = os.path.join(self.path, new_filename)
            mkdir_p(os.path.dirname(real_new_filename))
            os.rename(real_old_filename, real_new_filename)
            remove_file_with_empty_parents(self.path, old_filename)

        self.messages.append('    R  {} -> {}'.format(old_filename,
                                                      new_filename))

    def commit(self):
        if self.tree_modifier.tree.oid != self.get_last_tree().oid:
            raise Exception("The repository was modified outside of this process. For safety reasons, we cannot commit!")
        self.working_tree = self.tree_modifier.apply()
        self.tree_modifier = TreeModifier(self.repo, self.working_tree)

        if self.repo.head_is_unborn:
            parents = []
        else:
            commit = self.repo[self.getCurrentCommit()]
            if commit.tree.id == self.working_tree.id:
                return
            parents = [commit.id]

        config = self.repo.config
        author = Signature(config['user.name'], config['user.email'])
        committer = Signature(config['user.name'], config['user.email'])
        tree_id = self.working_tree.id
        message = '\n'.join(self.messages)
        self.repo.create_commit('refs/heads/master',
                                author, committer, message,
                                tree_id,
                                parents)
        self.saveCurrentCommit()
        self.messages = []
        if not self.repo.is_bare and self.update_working_copy:
            self.repo.index.read_tree(self.working_tree)
            self.repo.index.write()

    def reset(self):
        self.working_tree = self.get_last_tree()
        self.tree_modifier = TreeModifier(self.repo, self.working_tree)
        self.messages = []

    def getCurrentCommit(self):
        return self.repo.head.target

    def saveCurrentCommit(self):
        with open(os.path.join(self.path, 'dbcommit'), 'w') as dbcommit_file:
            dbcommit_file.write(self.getCurrentCommit().hex+'\n')