Example #1
0
def init_repo(repo_path, clone_from=None, clone_refs=False,
              alternate_repo_paths=None, is_bare=True):
    """Initialise a new git repository or clone from existing."""
    assert is_valid_new_path(repo_path)
    init_repository(repo_path, is_bare)

    if clone_from:
        # The clone_from's objects and refs are in fact cloned into a
        # subordinate tree that's then set as an alternate for the real
        # repo. This lets git-receive-pack expose available commits as
        # extra haves without polluting refs in the real repo.
        sub_path = os.path.join(repo_path, 'turnip-subordinate')
        clone_repository(clone_from, sub_path, True)
        assert is_bare
        alt_path = os.path.join(repo_path, 'objects/info/alternates')
        with open(alt_path, 'w') as f:
            f.write('../turnip-subordinate/objects\n')

        if clone_refs:
            # With the objects all accessible via the subordinate, we
            # can just copy all refs from the origin. Unlike
            # pygit2.clone_repository, this won't set up a remote.
            # TODO: Filter out internal (eg. MP) refs.
            from_repo = Repository(clone_from)
            to_repo = Repository(repo_path)
            for ref in from_repo.listall_references():
                to_repo.create_reference(
                    ref, from_repo.lookup_reference(ref).target)

    if alternate_repo_paths:
        write_alternates(repo_path, alternate_repo_paths)
    ensure_config(repo_path)  # set repository configuration defaults
    return repo_path
Example #2
0
def sync_handler(fork_from: str, from_sha: str, repo_name: str,
                 ticket_id: int, pr_url: str):
    output_path = '{}.txt'.format(pr_url.split('/', 3)[3].rsplit('/', 2)[0])
    output_path = os.path.join(WORK_DIR, output_path.replace('/', '_'))
    work_tree = os.path.join(WORK_DIR, fork_from)
    parent_path = os.path.dirname(work_tree)
    if not os.path.exists(parent_path):
        os.makedirs(parent_path)
    if not os.path.exists(work_tree):
        repo = clone_repository(
            '{0}{1}.git'.format(GITHUB_URL, fork_from), work_tree)
    else:
        repo = Repository(work_tree)

    remote_name = repo_name.split('/')[0]
    update_remote(work_tree, repo, repo_name, remote_name)

    if remote_name == 'origin':
        commit = repo.revparse_single(from_sha)
        repo.checkout_tree(commit, strategy=GIT_CHECKOUT_FORCE)
    else:
        ref_name = 'refs/pull/{0}/head'.format(ticket_id)
        try:
            repo.create_reference(ref_name, from_sha)
        except ValueError:
            pass
        ref = repo.lookup_reference(ref_name)
        repo.checkout(ref, strategy=GIT_CHECKOUT_FORCE)
    cwd = os.getcwd()
    os.chdir(work_tree)
    subprocess.call(
        '{} . --output-file={}'.format(FLAKE8_EXECUTABLE, output_path),
        shell=True)
    os.chdir(cwd)
    return output_path
Example #3
0
class Git(object):
    r"""
    Interact with a git repository.
    """

    def __init__(self, gitdir):
        r"""
        Take a path to the git repository. Other methods interact with
        this git repository.
        """
        self.repo = Repository(gitdir)

    def branches(self):
        r"""
        Return the list of a branch name and its last commit id.
        """
        return self._refs('heads')

    def tags(self):
        r"""
        Return the list of a tag name and its last commit id.
        """
        return self._refs('tags')

    def _refs(self, type):
        refs = {}
        pattern = re.compile(r'refs/%s/(.*)$' % type)
        for ref in self.repo.listall_references():
            m = pattern.match(ref)
            if m:
                reference = self.repo.lookup_reference(ref)
                refs[m.group(1)] = reference.hex
        return refs

    def create_branch(self, name, target):
        r"""
        Create new branch.
        """
        if not is_valid_value(name):
            raise InvalidParamException("name is required")
        if not is_valid_hex(target):
            raise InvalidParamException("target is required")

        target = sha_hex2bin(target)
        try:
            self.repo.create_reference('refs/heads/%s' % name, target)
        except Exception, e:
            raise InvalidParamException(str(e))

        return True
Example #4
0
class GitRepo:
    def __init__(self, repo_path):
        self.repo = Repository(repo_path)

    def checkout_by(self, commit_id):
        ref = 'refs/tags/t-%s' % commit_id

        if self.repo.references.get(ref) is None:
            self.repo.create_reference(ref, commit_id)

        self.repo.checkout(ref)
        self.repo.references[ref].delete()

    def master(self):
        branch = self.repo.lookup_branch('master')
        self.repo.checkout(branch)

    def get_all_commit_id(self):
        self.master()
        return self.repo.walk(self.repo.head.target,
                              GIT_SORT_TOPOLOGICAL | GIT_SORT_REVERSE)
Example #5
0
    def _fast_forward(self, local_path, merge_target, branch):
        # fast-forward all the branches.
        # pygit2 repo
        repo = Repository(discover_repository(local_path))

        # convert merge_target from hex into oid.
        fetch_head = repo.revparse_single(merge_target)

        # try to resolve a common anscestor between fetched and local
        try:
            head = repo.revparse_single(branch)
        except KeyError:
            # Doesn't exist.  Create and done.
            repo.create_reference(branch, fetch_head.oid)
            return True, 'Created new branch: %s' % branch

        if head.oid == fetch_head.oid:
            return True, 'Source and target are identical.'

        # raises KeyError if no merge bases found.
        common_oid = repo.merge_base(head.oid, fetch_head.oid)

        # Three different outcomes between the remaining cases.
        if common_oid.hex not in (head.oid.hex, fetch_head.oid.hex):
            # common ancestor is beyond both of these, not going to
            # attempt a merge here and will assume this:
            return False, 'Branch will diverge.'
        elif common_oid.hex == fetch_head.oid.hex:
            # Remote is also the common ancestor, so nothing to do.
            return True, 'No new changes found.'

        # This case remains: common_oid.hex == head.oid.hex, meaning
        # this local repository is the ancestor of further changes
        # fetched from the remote - remote newer, so fast-forward.
        ref = repo.lookup_reference(branch)
        ref.delete()

        repo.create_reference(branch, fetch_head.oid)

        return True, 'Fast-forwarded branch: %s' % branch
Example #6
0
class DictRepository(object):
    """The :class:`DictRepository <DictRepository>` object.

    :param repo_or_path:
        The path to a repository, or an existing pygit2.Repository object.
        If it is a path that does not exist, a new bare git repository will
        be initialized there.  If it is a path that does exist, then the
        directory will be used as a bare git repository.
    :type repo_or_path: string or pygit2.Repository
    """

    def __init__(self, repo_or_path=None):

        self._default_author = get_default_author()
        if isinstance(repo_or_path, Repository):
            self._repo = repo_or_path
        elif os.path.isdir(repo_or_path):
            self._repo = Repository(repo_or_path)
        else:
            self._repo = init_repository(repo_or_path, True)  # bare repo

    def _key_to_ref(self, key):
        return "refs/%s/HEAD" % key

    def get_commit_oid_for_key(self, key):
        return self._repo[self._repo.lookup_reference(self._key_to_ref(key)).oid].oid

    def get_raw_dict_for_commit_oid(self, commit_oid):
        return json.loads(self._repo[self._repo[commit_oid].tree[DATA].oid].data)

    def get_parent_oids_for_commit_oid(self, commit_oid):
        return [parent.oid for parent in self._repo[commit_oid].parents]

    def raw_commit(self, key, raw_dict, author, committer, message, parents):
        """Commit a dict to this :class:`DictRepository <DictRepository>`.
        It is recommended that you use the :class:`GitDict <GitDict>` commit
        method instead.

        :param raw_dict: the data to commit.
        :type raw_dict: dict
        :param author:
            The author of the commit.  If None, will be replaced with default.
        :type author: pygit2.Signature
        :param committer:
            The committer of this commit. If None, will be replaced with author.
        :type committer: pygit2.Signature
        :param message: The commit message.
        :type message: string
        :param parents:
            A list of 20-byte object IDs of parent commits.  An empty list
            means this is the first commit.

        :return: The oid of the new commit.
        :rtype: 20 bytes
        """
        if not isinstance(raw_dict, dict):
            raise ValueError("%s is not a dict" % raw_dict)

        author = author or self._default_author.signature()
        committer = committer or author

        blob_id = self._repo.write(GIT_OBJ_BLOB, json.dumps(raw_dict))

        # TreeBuilder doesn't support inserting into trees, so we roll our own
        tree_id = self._repo.write(GIT_OBJ_TREE, "100644 %s\x00%s" % (DATA, blob_id))

        return self._repo.create_commit(self._key_to_ref(key), author, committer, message, tree_id, parents)

    def create(self, key, dict={}, autocommit=False, message="first commit", author=None, committer=None):
        """Create a new :class:`GitDict <GitDict>`

        :param key: The key of the new :class:`GitDict <GitDict>`
        :type key: :class:`GitDict <GitDict>`
        :param dict: (optional) The value of the dict.  Defaults to empty.
        :type dict: dict
        :param autocommit:
            (optional) Whether the :class:`GitDict <GitDict>` should
            automatically commit. Defaults to false.
        :type autocommit: boolean
        :param message:
            (optional) Message for first commit.  Defaults to "first commit".
        :type message: string
        :param author:
            (optional) The signature for the author of the first commit.
            Defaults to global author.
        :type author: pygit2.Signature
        :param committer:
            (optional) The signature for the committer of the first commit.
            Defaults to author.
        :type author: pygit2.Signature

        :returns: the GitDict
        :rtype: :class:`GitDict <GitDict>`
        """
        self.raw_commit(key, dict, author, committer, message, [])
        return self.get(key, autocommit=autocommit)

    def has(self, key):
        """Determine whether there is an entry for key in this repository.

        :param key: The key to check
        :type key: string

        :returns: whether there is an entry
        :rtype: boolean
        """
        try:
            self._repo.lookup_reference(self._key_to_ref(key))
            return True
        except KeyError:
            return False

    def get(self, key, autocommit=False):
        """Obtain the :class:`GitDict <GitDict>` for a key.

        :param key: The key to look up.
        :type key: string
        :param autocommit:
            (optional) Whether the :class:`GitDict <GitDict>` should
            automatically commit. Defaults to false.
        :type autocommit: boolean

        :returns: the GitDict
        :rtype: :class:`GitDict <GitDict>`
        :raises: KeyError if there is no entry for key
        """
        return GitDict(self, key, autocommit=autocommit)

    def fast_forward(self, from_dict, to_dict):
        """Fast forward a :class:`GitDict <GitDict>`.

        :param from_dict: the :class:`GitDict <GitDict>` to fast forward.
        :type from_dict: :class:`GitDict <GitDict>`
        :param to_dict: the :class:`GitDict <GitDict>`to fast forward to.
        :type to_dict: :class:`GitDict <GitDict>`
        """
        from_ref = self._key_to_ref(from_dict.key)
        self._repo.lookup_reference(from_ref).delete()
        self._repo.create_reference(from_ref, self.get_commit_oid_for_key(to_dict.key))

    def clone(self, original, key):
        """Clone a :class:`GitDict <GitDict>`.

        :param original: the :class:`GitDict <GitDict>` to clone
        :type original: :class:`GitDict <GitDict>`
        :param key: where to clone to
        :type key: string
        :raises: ValueError if to_key already exists.
        """
        try:
            self._repo.create_reference(self._key_to_ref(key), self.get_commit_oid_for_key(original.key))
            return self.get(key, autocommit=original.autocommit)
        except GitError:
            raise ValueError("Cannot clone to %s, there is already a dict there." % key)
Example #7
0
 def _checkout_commit(self, repo: pygit2.Repository, commit):
     repo.create_reference(DataExtractor.WORKING_TAG_REFNAME, commit.id)
     repo.checkout(DataExtractor.WORKING_TAG_REFNAME)
     repo.lookup_reference(DataExtractor.WORKING_TAG_REFNAME).delete()
Example #8
0
File: doc.py Project: obmarg/resumr
class Document(object):
    '''
    Class representing a document, interacts with the git
    database
    '''

    def __init__( self, name, create=False, rootPath=None ):
        '''
        Constructor

        Args:
            name        The name of the document
            create      If true, will create a document
            rootPath    The rootPath to use (if not supplied, uses default)
        Exceptions:
            RepoNotFound if repository isn't found
        '''
        if not rootPath:
            rootPath = DEFAULT_ROOT_PATH
        targetDir = os.path.join( rootPath, name + '.git' )
        if create:
            # Create a bare repository
            self.repo = init_repository( targetDir, True )
            self._CreateMasterBranch()
        else:
            try:
                self.repo = Repository( targetDir )
            except KeyError:
                raise RepoNotFound()

    def _CreateMasterBranch( self ):
        '''
        Creates the master branch on the repo w/ default file.
        For now this is just a file named layout
        '''
        commitId = CommitBlob(
                self.repo, '', SECTION_INDEX_FILENAME, 'Initial commit'
                )
        self.repo.create_reference( MASTER_REF, commitId )

    @staticmethod
    def _IsSectionRef( refName ):
        '''
        Checks if a refererence name refers to a section

        Args:
            refName:    The reference name
        Returns:
            A boolean
        '''
        return refName.startswith( SECTION_REF_PREFIX )

    @staticmethod
    def _RefNameToSectionName( refName ):
        '''
        Converts a reference name to a section name

        Args:
            ref:    The reference name
        '''
        return refName[ len(SECTION_REF_PREFIX) : ]

    def _SectionRefs( self ):
        '''
        Gets an iterator over the section refs
        '''
        return (
                (
                    self._RefNameToSectionName( ref ),
                    self.repo.lookup_reference( ref ),
                    )
                for ref in self.repo.listall_references()
                if self._IsSectionRef( ref )
                )

    def Sections( self ):
        '''
        Gets an iterator over all the sections
        '''
        return (
                Section( name, self.repo[ref.oid], self.repo )
                for name, ref in self._SectionRefs()
                )

    def CurrentSections( self ):
        '''
        Gets the current sections with their positions

        Returns:
            A list of tuples ( position, section )
        '''
        return enumerate( self._CurrentSections() )

    def _CurrentSections( self ):
        '''
        Internal method to get the current sections
        in order, without position numbers

        Returns:
            An iterator over the sections
        '''
        index = SectionIndex(self.repo)
        for s in index.CurrentSections():
            yield self.FindSection(s.name)

    def FindSection( self, name ):
        '''
        Finds a section by name

        Args:
            name    The name of the section to find
        Returns:
            The section if found
        Exceptions:
            SectionNotFound if section not found
        '''
        try:
            ref = self.repo.lookup_reference(
                    SECTION_REF_PREFIX + name
                    )
        except KeyError:
            raise SectionNotFound()
        return Section( name, self.repo[ref.oid], self.repo )

    def AddSection( self, name, content='' ):
        '''
        Creates a new section

        Args:
            name        The name of the section
            content     The optional initial content of the
                        section

        Returns:
            The new Section object
        '''
        # TODO: Should probably make
        #       sure no such section exists
        commitId = CommitBlob(
                self.repo,
                content,
                name,
                'Created section ' + name
                )
        ref = self.repo.create_reference(
                SECTION_REF_PREFIX + name,
                commitId
                )
        index = SectionIndex(self.repo)
        index.AddSection( name )
        index.Save( self.repo )
        return Section( name, self.repo[ ref.oid ], self.repo )

    def RemoveSection( self, name ):
        '''
        Removes a section.
        This function does not actually delete the data associated
        with a section, it just removes it from the index.

        Args:
            name    The name of the section to remove
        '''
        index = SectionIndex( self.repo )
        index.RemoveSection( name )
        index.Save( self.repo )