Exemplo n.º 1
0
class Repo(object):
  def __init__(self,path,username,password):
    self.repo = Repository(path)
    self.username = username
    self.password = password
    self.path=path

  def commit(self,file,refspec,email,author_username,message):
    self.repo.index.add(file)
    index = self.repo.index
    index.write()
    tree = self.repo.index.write_tree()
    author = Signature(author_username,email)
    self.repo.create_commit(refspec,
        author,
        author,message,tree, [self.repo.head.get_object().hex] )

  def commit_fallback(self,message):
    return subprocess.check_output(["git","commit","-m",message],cwd=self.path)

  def push(self,refspec):
    credentials = UserPass(self.username,self.password)
    remoteCall = RemoteCallbacks(credentials)
    remo = self.repo.remotes["origin"]
    remo.push(refspec,remoteCall)
Exemplo n.º 2
0
def get_new_articles():
    blog = PelicanBlog()
    content_dir = blog.get_content_directory()
    repo = Repository(os.path.abspath(os.path.dirname(__file__)))
    diff = repo.revparse_single("HEAD").tree.diff_to_tree()
    existing_articles = set(os.path.relpath(obj.old_file_path, content_dir)
                            for obj in diff
                            if obj.old_file_path.startswith(content_dir))
    all_articles = set(blog.get_posts())
    new_articles = {art for art in all_articles - existing_articles
                    if blog.get_post_lang(art) in (TWITTER_LANGUAGE, "")}
    new_titles = []
    repo.index.read()
    for newart in new_articles:
        title = blog.get_post_title(newart)
        yield Article(title, blog.get_post_url(newart),
                      blog.get_post_authors(newart))
        new_titles.append(title)
        repo.index.add(os.path.join(content_dir, newart))
    blogger = Signature(repo.config["user.name"], repo.config["user.email"])
    repo.create_commit("HEAD", blogger, blogger,
                       "[BLOG] %s" % ", ".join(new_titles),
                       repo.index.write_tree(), [repo.head.peel().oid])
    repo.index.write()
    # TODO(v.markovtsev): implement git push using pygit2
    subprocess.call(("git", "push", "origin", repo.head.shorthand))
Exemplo n.º 3
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')
Exemplo n.º 4
0
def commit_new_tickets_to_git(queue, repo_path):
    global last_push_ts
    #repo
    repo = Repository(os.path.join(repo_path, '.git'))
    index = repo.index
    author = Signature('yourname', 'youremail')
    while True:
        #write tickets into file
        ticket = queue.get(block=True)
        #filename format is yyyy-mm-dd
        d = datetime.utcnow().date()
        filename = '%s.txt' % d.strftime('%Y-%m-%d')
        f = open(os.path.join(repo_path, filename), 'ab')
        f.write('%s\n' % ticket.toJson())
        f.close()
        #commit
        index.add(filename)
        index.write()
        oid = index.write_tree()
        repo.create_commit('HEAD', author, author, ticket.toJson(), oid, [repo.head.oid])
        #push
        d_ts = datetime.utcnow()
        if last_push_ts is None or d_ts > (last_push_ts + timedelta(seconds = 60)):
            push_to_github(repo_path)
            last_push_ts = datetime.utcnow()
Exemplo n.º 5
0
    def createcommit(self):
        """Prepare a git repository with one existing commit.

        Create a directory, initialize a git Repository, add
        and commit a file.

        Returns:
            A list containing the directory and file
        """
        self.addfile()
        # Create commit
        repo = Repository(self.dir.name)
        index = repo.index
        index.read()
        tree = index.write_tree()
        message = "First commit of temporary test repo"
        repo.create_commit('HEAD',
                           self.author, self.comitter, message,
                           tree,
                           [])
Exemplo n.º 6
0
def merge(dir, reference, msg):
    repo = Repository(dir)
    reference = "refs/heads/" + reference
    print reference
    other_branch_ref = repo.lookup_reference(reference)
    other_branch_tip = other_branch_ref.target
    # repo.merge(other_branch_tip)
    print('merge complete!')
    user = repo.default_signature
    tree = repo.index.write_tree()
    message = msg
    new_commit = repo.create_commit('HEAD', user, user, message, tree,
                                    [repo.head.target, other_branch_tip])
    print(new_commit)
Exemplo n.º 7
0
def shift(amount, repo_path):
    repo = Repository(repo_path)
    head = repo.lookup_reference('HEAD').resolve()
    adder = partial(add, amount=amount)
    changelog = dict()
    reference = REF_FMT.format(time=time(), pid=getpid())
    for commit in repo.walk(head.oid, GIT_SORT_REVERSE | GIT_SORT_TOPOLOGICAL):
        newmsg, nsubs = ISSUE_RE.subn(adder, commit.message)
        if nsubs != 0 or any(pnt.oid in changelog for pnt in commit.parents):
            parents = [changelog.get(c.oid, c.oid) for c in commit.parents]
            new_oid = repo.create_commit(reference, commit.author,
                    commit.committer, newmsg, commit.tree.oid, parents)
            changelog[commit.oid] = new_oid
    return changelog, reference
Exemplo n.º 8
0
def commit(
    repo: pygit2.Repository,
    message: str,
    pathspecs: typing.Optional[typing.List[typing.Union[str, pathlib.Path]]] = None,
) -> None:
    if pathspecs is None:
        pathspecs = []

    pathspecs = [
        (
            str(
                pathspec.relative_to(repo.workdir)
                if pathspec.is_absolute()
                else pathspec
            )
            if isinstance(pathspec, pathlib.Path)
            else pathspec
        )
        for pathspec in pathspecs
    ]

    repo.index.add_all(pathspecs=pathspecs)
    repo.index.write()
    tree = repo.index.write_tree()

    try:
        parent, ref = repo.resolve_refish(refish=repo.head.name)
    except pygit2.GitError:
        parents = []
        ref_name = 'refs/heads/master'
    else:
        parents = [parent.oid]
        ref_name = ref.name

    repo.create_commit(
        ref_name, repo.default_signature, repo.default_signature, message, tree, parents
    )
Exemplo n.º 9
0
def commitAll(repo: Repository, message: Optional[str] = None) -> None:
    """
    Creates commit with all changes in the working tree.
    There is a lack of eny error handling.
    
    Arguments:
        repo {Repository} -- repository object
    
    Keyword Arguments:
        message {Optional[str]} -- in None passed the message will be autogenerated: `Updated: datetime.now().strftime("%Y-%m-%d %H:%M:%S")` (default: {None})
    
    Returns:
        None -- No result returned.
    """

    if message is None:
        message = "Updated: " + datetime.now().strftime("%Y-%m-%d %H:%M:%S")

    # checking if this is the root commit. Parent commit should be HEAD otherwise.
    if repo.head_is_unborn:
        parent = []
    else:
        parent = [repo.head.target]

    # todo: investigate this
    # Make sure it was written to disk before moving on.
    # test_fp.flush()
    # os.fsync(test_fp.fileno())
    # test_fp.close()
    repo.index.add_all()

    user = repo.default_signature
    tree = repo.index.write_tree()
    repo.create_commit('HEAD', user, user, message, tree, parent)
    # Apparently the index needs to be written after a write tree to clean it up.
    # https://github.com/libgit2/pygit2/issues/370
    repo.index.write()
Exemplo n.º 10
0
def commit_to_working_copy_tag(repo: pygit2.Repository) -> pygit2.Oid:
    repo.index.read()
    repo.index.add_all(
    )  # TODO: add a binding for update_all to pygit2 and use it.
    tree = repo.index.write_tree()
    signature = repo.default_signature
    message = 'whats up with me'
    commit_oid = repo.create_commit(None, signature, signature, message, tree,
                                    [repo.head.get_object().hex])
    try:
        tag = get_working_copy_tag(repo)
        tag.set_target(commit_oid)
    except KeyError:
        repo.create_tag(WORKING_COPY_TAG_NAME, commit_oid,
                        pygit2.GIT_OBJ_COMMIT, signature, message)

    return commit_oid
Exemplo n.º 11
0
def copy_template(repo: Repository,
                  name: str,
                  commit_message: str,
                  parents: List[str] = None) -> str:
    template_path = os.path.join(os.path.dirname(__file__), 'data', name)
    # Use distuils implementation instead of shutil to allow for copying into
    # a destination with existing files. See: https://stackoverflow.com/a/31039095/724251
    copy_tree(template_path, repo.workdir)

    # Stage the template changes
    repo.index.add_all()
    repo.index.write()
    tree = repo.index.write_tree()
    # Construct a commit with the staged changes.
    return repo.create_commit(None, repo.default_signature,
                              repo.default_signature, commit_message, tree,
                              parents or [])
Exemplo n.º 12
0
def advance_master(
    repo: git.Repository,
    parents: List[git.Oid],
    tree: git.Oid,
    when: Optional[int] = None,
    msg: Optional[str] = None,
) -> git.Oid:
    if when is None:
        when = int(time())
    if msg is None:
        msg = ""

    return repo.create_commit(
        "refs/heads/master",
        git.Signature(name="untrustix", email="*****@*****.**", time=when),
        git.Signature(name="untrustix", email="*****@*****.**", time=when),
        msg,
        tree,
        parents,
    )
Exemplo n.º 13
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
Exemplo n.º 14
0
class GitStorage(Storage):
    """ Git file storage backend. """

    def __init__(self, path):
        """
            Initialize repository.

            :param path: Absolute path to the existing Git repository.
            :type path: str
        """

        super(GitStorage, self).__init__()

        self.repo = Repository(path)
        self.index = self.repo.index
        self.index.read()

    @classmethod
    def create_storage(cls, path):
        """
            Create repository, and return GitStorage object on it

            :param path: Absolute path to the Git repository to create.
            :type path: str
            :returns: GitStorage
        """

        init_repository(path, False)

        return cls(path)

    def commit(self, user, message):
        """
            Save previous changes in a new commit.

            :param user: The commit author/committer.
            :type user: django.contrib.auth.models.User
            :param message: The commit message.
            :type message: unicode
            :returns: pygit2.Commit
        """

        # Refresh index before committing
        index = self.repo.index
        index.read()

        # Check the status of the repository
        status = self.repo.status()

        for filename, flags in status.items():
            # the file was deleted
            if flags in (GIT_STATUS_INDEX_DELETED, GIT_STATUS_WT_DELETED):
                # remove it from the tree
                del index[filename]

            # or the file was modified/added
            elif flags in (GIT_STATUS_INDEX_MODIFIED, GIT_STATUS_INDEX_NEW,
                           GIT_STATUS_WT_MODIFIED, GIT_STATUS_WT_NEW):
                # add it to the tree
                index.add(filename)

        treeid = index.write_tree()

        # Now make the commit

        author = Signature(u'{0} {1}'.format(
            user.first_name,
            user.last_name).encode('utf-8'),
            user.email.encode('utf-8')
        )
        committer = author

        try:
            parents = [self.repo.head.oid]

        except GitError:
            parents = []

        commit = self.repo.create_commit(
            'refs/heads/master',
            author, committer, message,
            treeid,
            parents
        )

        # Write changes to disk
        index.write()
        # and refresh index.
        self.index.read()

        # Return commit object
        return self.repo[commit]

    def log(self, name=None, limit=10):
        """
            Get history of the repository, or of a file if name is not None.

            :param name: File name within the repository.
            :type name: unicode or None
            :param limit: Maximal number of commits to get (default: 10), use a negative number to get all.
            :type limit: int
            :returns: list of pygit2.Commit
        """

        commits = []

        if not name:
            # Look for `limit` commits
            for commit in self.repo.walk(self.repo.head.oid, GIT_SORT_TIME):
                commits.append(commit)

                limit = limit - 1

                if limit == 0:
                    break

        else:
            # For each commits
            for commit in self.repo.walk(self.repo.head.oid, GIT_SORT_TIME):
                # Check the presence of the file in the tree

                if commit.parents:
                    # If the commit has parents, check if the file is present
                    # in the diff

                    diff = commit.tree.diff(commit.parents[0].tree)

                    for patch in diff:
                        # If the filename is the patch's filename...
                        if name.encode('utf-8') == patch.new_file_path:
                            # ... then we can add the commit to the list
                            # and leave the loop

                            commits.append(commit)

                            limit = limit - 1
                            break

                else:
                    # But if the commit has no parents (root commit)
                    # Simply check in its tree

                    try:
                        commit.tree[name]

                        # no error raised, it means the entry exists, so add the
                        # commit to the list
                        commits.append(commit)

                        limit = limit - 1

                    # If the file is not in the tree, then it raises a KeyError,
                    # so, just ignore it.
                    except KeyError:
                        pass

                # If the limit is reached, leave the loop
                if limit == 0:
                    break

        return commits

    def diffs(self, name=None, limit=10):
        """
            Get diffs between commits.

            Return the following dict :

                {"diffs": [
                    {
                        "msg": unicode(<commit message>),
                        "date": datetime.fromtimestamp(<commit date>),
                        "author": unicode(<author name>),
                        "sha": unicode(<commit SHA>),
                        "parent_sha": unicode(<parent commit SHA>), # optional
                    },
                    # ...
                ]}

            :param name: File name within the repository.
            :type name: unicode or None
            :param limit: Maximal number of diffs to get (default: 10), use a negative number to get all.
            :type limit: int
            :returns: dict
        """

        commits = self.log(name=name, limit=limit)

        diffs = {'diffs': []}

        # For each commit
        for commit in commits:
            # Create a JSON object containing informations about the commit
            diff = {
                'msg': commit.message,
                'date': datetime.datetime.fromtimestamp(commit.commit_time),
                'author': commit.author.name,
                'sha': commit.hex,
            }

            if commit.parents:
                diff['parent_sha'] = commit.parents[0].hex

            # The SHA and parent SHA will be used to get the diff via AJAX.

            diffs['diffs'].append(diff)

        return diffs

    def diff(self, asha, bsha, name=None):
        """
            Get diff between two commits.

            :param asha: SHA of commit A.
            :type asha: unicode
            :param bsha: SHA of commit B.
            :type bsha: unicode
            :param name: File name within the repository.
            :type name: unicode or None
            :returns: unicode
        """

        c1 = self.repo[asha]
        c2 = self.repo[bsha]

        d = c1.tree.diff(c2.tree)

        if name:
            diff = u''

            # For each patch in the diff
            for patch in d:
                # Check if the patch is our file
                if name.encode('utf-8') == patch.new_file_path:
                    # Format the patch
                    for hunk in patch.hunks:
                        p = u'\n'.join(hunk.lines)

                        # And add the diff to the final diff
                        diff = u'{0}{1}'.format(diff, p)

            return diff

        # For a global diff, just return the full patch
        else:
            return d.patch

    def search(self, pattern, exclude=None):
        """
            Search pattern in GIT repository.

            :param pattern: Pattern to search.
            :type pattern: unicode
            :param exclude: Exclude some files from the search results
            :type exclude: regex
            :returns: list of tuple containing the filename and the list of matched lines.
        """

        entries = []

        self.index.read()

        # For each files in the index
        for ientry in self.index:
            # If the filename match the exclude_file regex, then ignore it
            if exclude and re.match(exclude, ientry.path.decode('utf-8')):
                continue

            # Get the associated blob
            blob = self.repo[ientry.oid]

            # Create entry
            entry = (ientry.path.decode('utf-8'), [])

            # Add matched lines to the entry
            for line in blob.data.decode('utf-8').splitlines():
                if pattern in line:
                    entry[1].append(line)

            # If the entry has no matched lines, then ignore
            if entry[1]:
                entries.append(entry)

        return entries

    def is_dir(self, name):
        """
            Check if name refers to a directory.

            :param name: File name within the repository.
            :type name: unicode
            :returns: True, False
        """

        # Check if the path exists, if not returns default value.
        if not self.exists(name):
            return False

        # Get the TreeEntry associated to name
        tentry = self.repo.head.tree[name]

        # Convert it to its pygit2 representation
        obj = tentry.to_object()

        # If it's a Tree, then we can return True
        if isinstance(obj, Tree):
            return True

        # The instance is a Blob, so it's a file, return False
        else:
            return False

    def mimetype(self, name):
        """
            Get the mimetype of a file.

            :param name: File name within the repository.
            :type name: unicode
            :returns: str
        """

        # If the file is a directory
        if self.is_dir(name):
            return 'inode/directory'

        # Or doesn't exist
        elif not self.exists(name):
            return 'unknown'

        # The file exists, check its mimetype
        else:
            import urllib
            import mimetypes

            url = urllib.pathname2url(name.encode('utf-8'))

            return mimetypes.guess_type(url)[0] or 'unknown'

    def walk(self):
        """
            Walk through the repository.
        """

        self.index.read()

        for entry in self.index:
            yield entry

    # Storage API

    def accessed_time(self, name):
        """
            Get last accessed time of a file.

            :param name: File name within the repository.
            :type name: unicode
            :returns: datetime
            :raises: IOError
        """

        if not self.exists(name):
            raise IOError(u"{0}: Not found in repository".format(name))

        abspath = os.path.join(self.repo.workdir, name)
        stats = os.stat(abspath)

        return datetime.datetime.fromtimestamp(stats.st_atime)

    def created_time(self, name):
        """
            Get creation time of a file.

            :param name: File name within the repository.
            :type name: unicode
            :returns: datetime
            :raises: IOError
        """

        if not self.exists(name):
            raise IOError(u"{0}: Not found in repository".format(name))

        abspath = os.path.join(self.repo.workdir, name)
        stats = os.stat(abspath)

        return datetime.datetime.fromtimestamp(stats.st_ctime)

    def modified_time(self, name):
        """
            Get last modified time of a file.

            :param name: File name within the repository.
            :type name: unicode
            :returns: datetime
            :raises: IOError
        """

        if not self.exists(name):
            raise IOError(u"{0}: Not found in repository".format(name))

        abspath = os.path.join(self.repo.workdir, name)
        stats = os.stat(abspath)

        return datetime.datetime.fromtimestamp(stats.st_mtime)

    def size(self, name):
        """
            Get file's size.

            :param name: File name within the repository.
            :type name: unicode
            :returns: int
            :raises: IOError
        """

        if not self.exists(name):
            raise IOError(u"{0}: Not found in repository".format(name))

        e = self.index[name]
        blob = self.repo[e.oid]

        return blob.size

    def exists(self, path):
        """
            Check if ``path`` exists in the Git repository.

            :param path: Path within the repository of the file to check.
            :type param: unicode
            :returns: True if the file exists, False if the name is available for a new file.
        """

        # If the head is orphaned (does not point to any commit), returns False
        # because there is nothing in the repository.
        if self.repo.head_is_orphaned:
            return False

        # Try getting the path via the tree
        try:
            entry = self.repo.head.tree[path]

            return True

        # If it raises a KeyError, then the path doesn't exist
        except KeyError:
            return False

    def listdir(self, path=None):
        """
            Lists the contents of the specified path.

            :param path: Path of the directory to list (or None to list the root).
            :type path: unicode or None
            :returns: a 2-tuple of lists; the first item being directories, the second item being files.
        """

        abspath = os.path.join(self.repo.workdir, path) if path else self.repo.workdir

        dirs = []
        files = []

        for e in os.listdir(abspath):
            entry_fullpath = os.path.join(abspath, e)

            if os.path.isdir(entry_fullpath):
                if e != '.git':
                    dirs.append(e.decode('utf-8'))

            else:
                files.append(e.decode('utf-8'))

        return (dirs, files)

    def open(self, name, mode='rb'):
        """
            Opens the file given by name.

            :param name: Name of the file to open.
            :type name: unicode
            :param mode: Flags for openning the file (see builtin ``open`` function).
            :type mode: str
            :returns: GitFile
        """

        abspath = os.path.join(self.repo.workdir, name)
        dirname = os.path.dirname(abspath)

        if 'w' in mode and not os.path.exists(dirname):
            os.makedirs(dirname)

        return GitFile(open(abspath, mode))

    def path(self, name):
        """
            Return the absolute path of the file ``name`` within the repository.

            :param name: Name of the file within the repository.
            :type name: unicode
            :returns: str
            :raises: IOError
        """

        if not self.exists(name):
            raise IOError(u"{0}: Not found in repository".format(name))

        e = self.index[name]

        return os.path.join(self.repo.workdir, e.path).decode('utf-8')

    def save(self, name, content):
        """
            Saves a new file using the storage system, preferably with the name
            specified. If there already exists a file with this name, the
            storage system may modify the filename as necessary to get a unique
            name. The actual name of the stored file will be returned.

            :param name: Name of the new file within the repository.
            :type name: unicode
            :param content: Content to save.
            :type content: django.core.files.File
            :returns: str
        """

        new_name = self.get_available_name(name)
        abspath = os.path.join(self.repo.workdir, new_name)

        dirname = os.path.dirname(abspath)

        if not os.path.exists(dirname):
            os.makedirs(dirname)

        with open(abspath, 'wb') as f:
            for chunk in content.chunks():
                f.write(chunk)

    def delete(self, name):
        """
            Deletes the file referenced by name.

            :param name: Name of the file within the repository to delete
            :type name: unicode
            :raises: IOError
        """

        if not self.exists(name):
            raise IOError(u"{0}: Not found in repository".format(name))

        abspath = os.path.join(self.repo.workdir, name)
        os.remove(abspath)
Exemplo n.º 15
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()
Exemplo n.º 16
0
        pass_path = credentials_mapping[glob]["target"]


if not args.wip_force and not is_idle_enough("xprintidle-ng"):
    sys.exit(0)
diff_size = get_diff_size(repo)
if diff_size == 0:
    log_info("no changes to commit")
    sys.exit(0)
if diff_size > lines_changed or args.wip_force:
    branch = f"{repo.references['HEAD'].resolve().split('/')[-1]}: " if args.wip_add_branch_name else ""
    wip_message = f"{branch}WIP {datetime.now().strftime('%a %d/%m/%Y %H:%M:%S')}"
    index = repo.index
    index.read()
    index.add_all()
    index.write()
    # user = repo.default_signature
    name = list(config.get_multivar('user.name'))[0]
    email = list(config.get_multivar('user.email'))[0]
    author = committer = Signature(name, email)
    parents = [repo.references['HEAD'].resolve().target]
    tree = index.write_tree()
    wip_commit = repo.create_commit("HEAD", author, committer, wip_message, tree, parents)
    if args.wip_push:
        if is_main_branch_active(repo) and is_main_branch_protected():
            log_info("main branch is untouchable")
            sys.exit(1)
        else:
            remote = resolve_remote(repo, args.remote)
            remote.push(specs=["HEAD"], callbacks=build_auth_callbacks(repo, pass_path))
Exemplo n.º 17
0
class GitRepo:
    """A class that manages a git repository.

    This class enables versiong via git for a repository.
    You can stage and commit files and checkout different commits of the repository.
    """

    path = ''
    pathspec = []
    repo = None
    callback = None
    author_name = 'QuitStore'
    author_email = '*****@*****.**'
    gcProcess = None

    def __init__(self, path, origin=None, gc=False):
        """Initialize a new repository from an existing directory.

        Args:
            path: A string containing the path to the repository.
            origin: The remote URL where to clone and fetch from and push to
        """
        logger = logging.getLogger('quit.core.GitRepo')
        logger.debug('GitRepo, init, Create an instance of GitStore')
        self.path = path
        self.gc = gc

        if not exists(path):
            try:
                makedirs(path)
            except OSError as e:
                raise Exception('Can\'t create path in filesystem:', path, e)

        try:
            self.repo = Repository(path)
        except KeyError:
            pass
        except AttributeError:
            pass

        if origin:
            self.callback = QuitRemoteCallbacks()

        if self.repo:
            if self.repo.is_bare:
                raise QuitGitRepoError('Bare repositories not supported, yet')

            if origin:
                # set remote
                self.addRemote('origin', origin)
        else:
            if origin:
                # clone
                self.repo = self.cloneRepository(origin, path, self.callback)
            else:
                self.repo = init_repository(path=path, bare=False)

    def cloneRepository(self, origin, path, callback):
        try:
            repo = clone_repository(url=origin,
                                    path=path,
                                    bare=False,
                                    callbacks=callback)
            return repo
        except Exception as e:
            raise QuitGitRepoError(
                "Could not clone from: {} origin. {}".format(origin, e))

    def addall(self):
        """Add all (newly created|changed) files to index."""
        self.repo.index.read()
        self.repo.index.add_all(self.pathspec)
        self.repo.index.write()

    def addfile(self, filename):
        """Add a file to the index.

        Args:
            filename: A string containing the path to the file.
        """
        index = self.repo.index
        index.read()

        try:
            index.add(filename)
            index.write()
        except Exception as e:
            logger.info(
                "GitRepo, addfile, Could not add file  {}.".format(filename))
            logger.debug(e)

    def addRemote(self, name, url):
        """Add a remote.

        Args:
            name: A string containing the name of the remote.
            url: A string containing the url to the remote.
        """
        try:
            self.repo.remotes.create(name, url)
            logger.info("Successfully added remote: {} - {}".format(name, url))
        except Exception as e:
            logger.info("Could not add remote: {} - {}".format(name, url))
            logger.debug(e)

        try:
            self.repo.remotes.set_push_url(name, url)
            self.repo.remotes.set_url(name, url)
        except Exception as e:
            logger.info("Could not set push/fetch urls: {} - {}".format(
                name, url))
            logger.debug(e)

    def checkout(self, commitid):
        """Checkout a commit by a commit id.

        Args:
            commitid: A string cotaining a commitid.
        """
        try:
            commit = self.repo.revparse_single(commitid)
            self.repo.set_head(commit.oid)
            self.repo.reset(commit.oid, GIT_RESET_HARD)
            logger.info("Checked out commit: {}".format(commitid))
        except Exception as e:
            logger.info("Could not check out commit: {}".format(commitid))
            logger.debug(e)

    def commit(self, message=None):
        """Commit staged files.

        Args:
            message: A string for the commit message.
        Raises:
            Exception: If no files in staging area.
        """
        if self.isstagingareaclean():
            # nothing to commit
            return

        index = self.repo.index
        index.read()
        tree = index.write_tree()

        try:
            author = Signature(self.author_name, self.author_email)
            comitter = Signature(self.author_name, self.author_email)

            if len(self.repo.listall_reference_objects()) == 0:
                # Initial Commit
                if message is None:
                    message = 'Initial Commit from QuitStore'
                self.repo.create_commit('HEAD', author, comitter, message,
                                        tree, [])
            else:
                if message is None:
                    message = 'New Commit from QuitStore'
                self.repo.create_commit('HEAD', author, comitter, message,
                                        tree,
                                        [self.repo.head.get_object().hex])
            logger.info('Updates commited')
        except Exception as e:
            logger.info('Nothing to commit')
            logger.debug(e)

        if self.gc:
            self.garbagecollection()

    def commitexists(self, commitid):
        """Check if a commit id is part of the repository history.

        Args:
            commitid: String of a Git commit id.
        Returns:
            True, if commitid is part of commit log
            False, else.
        """
        if commitid in self.getids():
            return True
        else:
            return False

    def garbagecollection(self):
        """Start garbage collection.

        Args:
            commitid: A string cotaining a commitid.
        """
        try:
            # Check if the garbage collection process is still running
            if self.gcProcess is None or self.gcProcess.poll() is not None:
                # Start garbage collection with "--auto" option,
                # which imidietly terminates, if it is not necessary
                self.gcProcess = Popen(["git", "gc", "--auto", "--quiet"],
                                       cwd=self.path)
                logger.debug('Spawn garbage collection')
        except Exception as e:
            logger.debug('Git garbage collection failed to spawn')
            logger.debug(e)

    def getpath(self):
        """Return the path of the git repository.

        Returns:
            A string containing the path to the directory of git repo
        """
        return self.path

    def getcommits(self):
        """Return meta data about exitsting commits.

        Returns:
            A list containing dictionaries with commit meta data
        """
        commits = []
        if len(self.repo.listall_reference_objects()) > 0:
            for commit in self.repo.walk(self.repo.head.target,
                                         GIT_SORT_REVERSE):
                commits.append({
                    'id':
                    str(commit.oid),
                    'message':
                    str(commit.message),
                    'commit_date':
                    datetime.fromtimestamp(
                        commit.commit_time).strftime('%Y-%m-%dT%H:%M:%SZ'),
                    'author_name':
                    commit.author.name,
                    'author_email':
                    commit.author.email,
                    'parents': [c.hex for c in commit.parents],
                })
        return commits

    def getids(self):
        """Return meta data about exitsting commits.

        Returns:
            A list containing dictionaries with commit meta data
        """
        ids = []
        if len(self.repo.listall_reference_objects()) > 0:
            for commit in self.repo.walk(self.repo.head.target,
                                         GIT_SORT_REVERSE):
                ids.append(str(commit.oid))
        return ids

    def isgarbagecollectionon(self):
        """Return if gc is activated or not.

        Returns:
            True, if activated
            False, if not
        """
        return self.gc

    def isstagingareaclean(self):
        """Check if staging area is clean.

        Returns:
            True, if staginarea is clean
            False, else.
        """
        status = self.repo.status()

        for filepath, flags in status.items():
            if flags != GIT_STATUS_CURRENT:
                return False

        return True

    def pull(self, remote='origin', branch='master'):
        """Pull if possible.

        Return:
            True: If successful.
            False: If merge not possible or no updates from remote.
        """
        try:
            self.repo.remotes[remote].fetch()
        except Exception as e:
            logger.info("Can not pull:  Remote {} not found.".format(remote))
            logger.debug(e)

        ref = 'refs/remotes/' + remote + '/' + branch
        remoteid = self.repo.lookup_reference(ref).target
        analysis, _ = self.repo.merge_analysis(remoteid)

        if analysis & GIT_MERGE_ANALYSIS_UP_TO_DATE:
            # Already up-to-date
            pass
        elif analysis & GIT_MERGE_ANALYSIS_FASTFORWARD:
            # fastforward
            self.repo.checkout_tree(self.repo.get(remoteid))
            master_ref = self.repo.lookup_reference('refs/heads/master')
            master_ref.set_target(remoteid)
            self.repo.head.set_target(remoteid)
        elif analysis & GIT_MERGE_ANALYSIS_NORMAL:
            self.repo.merge(remoteid)
            tree = self.repo.index.write_tree()
            msg = 'Merge from ' + remote + ' ' + branch
            author = Signature(self.author_name, self.author_email)
            comitter = Signature(self.author_name, self.author_email)
            self.repo.create_commit('HEAD', author, comitter, msg, tree,
                                    [self.repo.head.target, remoteid])
            self.repo.state_cleanup()
        else:
            logger.debug('Can not pull. Unknown merge analysis result')

    def push(self, remote='origin', branch='master'):
        """Push if possible.

        Return:
            True: If successful.
            False: If diverged or nothing to push.
        """
        ref = ['refs/heads/' + branch]

        try:
            remo = self.repo.remotes[remote]
        except Exception as e:
            logger.info(
                "Can not push. Remote: {} does not exist.".format(remote))
            logger.debug(e)
            return

        try:
            remo.push(ref, callbacks=self.callback)
        except Exception as e:
            logger.info("Can not push to {} with ref {}".format(
                remote, str(ref)))
            logger.debug(e)

    def getRemotes(self):
        remotes = {}

        try:
            for remote in self.repo.remotes:
                remotes[remote.name] = [remote.url, remote.push_url]
        except Exception as e:
            logger.info('No remotes found.')
            logger.debug(e)
            return {}

        return remotes
Exemplo n.º 18
0
class GitBareBackend(object):

    nb_transactions = 0

    def __init__(self, path):
        self.path = abspath(path) + '/'
        # Open database
        self.path_data = '%s/database/' % self.path
        if not lfs.is_folder(self.path_data):
            error = '"%s" should be a folder, but it is not' % path
            raise ValueError, error
        # Open repository
        self.repo = Repository(self.path_data)
        # Read index
        try:
            tree = self.repo.head.peel(GIT_OBJ_TREE)
            self.repo.index.read_tree(tree.id)
        except:
            pass
        # Check git commiter
        try:
            _, _ = self.username, self.useremail
        except:
            print '========================================='
            print 'ERROR: Please configure GIT commiter via'
            print ' $ git config --global user.name'
            print ' $ git config --global user.email'
            print '========================================='
            raise

    @classmethod
    def init_backend(cls, path, init=False, soft=False):
        init_repository('{0}/database'.format(path), bare=True)

    #######################################################################
    # Internal utility functions
    #######################################################################
    def _call(self, command):
        """Interface to cal git.git for functions not yet implemented using
        libgit2.
        """
        popen = Popen(command, stdout=PIPE, stderr=PIPE, cwd=self.path_data)
        stdoutdata, stderrdata = popen.communicate()
        if popen.returncode != 0:
            raise EnvironmentError, (popen.returncode, stderrdata)
        return stdoutdata

    @lazy
    def username(self):
        cmd = ['git', 'config', '--get', 'user.name']
        try:
            username = self._call(cmd).rstrip()
        except EnvironmentError:
            raise ValueError(
                "Please configure 'git config --global user.name'")
        return username

    @lazy
    def useremail(self):
        cmd = ['git', 'config', '--get', 'user.email']
        try:
            useremail = self._call(cmd).rstrip()
        except EnvironmentError:
            raise ValueError(
                "Please configure 'git config --global user.email'")
        return useremail

    def _resolve_reference(self, reference):
        """This method returns the SHA the given reference points to. For now
        only HEAD is supported.

        FIXME This is quick & dirty. TODO Implement references in pygit2 and
        use them here.
        """
        # Case 1: SHA
        if len(reference) == 40:
            return reference

        # Case 2: reference
        reference = self.repo.lookup_reference(reference)
        try:
            reference = reference.resolve()
        except KeyError:
            return None

        return reference.target

    def normalize_key(self, path, __root=None):
        # Performance is critical so assume the path is already relative to
        # the repository.
        key = __root.resolve(path)
        if key and key[0] == '.git':
            err = "bad '{0}' path, access to the '.git' folder is denied"
            raise ValueError(err.format(path))
        return '/'.join(key)

    def handler_exists(self, key):
        tree = self.repo.head.peel(GIT_OBJ_TREE)
        try:
            tree[key]
        except:
            return False
        return True

    def get_handler_names(self, key):
        try:
            tree = self.repo.head.peel(GIT_OBJ_TREE)
            if key:
                tree_entry = tree[key]
                if tree_entry.type == 'blob':
                    raise ValueError
                tree = self.repo[tree_entry.id]
        except:
            yield None
        else:
            for item in tree:
                yield item.name

    def get_handler_data(self, key):
        tree = self.repo.head.peel(GIT_OBJ_TREE)
        tree_entry = tree[key]
        blob = self.repo[tree_entry.id]
        return blob.data

    def get_handler_mimetype(self, key):
        data = self.get_handler_data(key)
        return magic_from_buffer(data)

    def handler_is_file(self, key):
        return not self.handler_is_folder(key)

    def handler_is_folder(self, key):
        repository = self.repo
        if key == '':
            return True
        else:
            tree = repository.head.peel(GIT_OBJ_TREE)
            tree_entry = tree[key]
        return tree_entry.type == 'tree'

    def get_handler_mtime(self, key):
        # FIXME
        return datetime.utcnow().replace(tzinfo=fixed_offset(0))

    def traverse_resources(self):
        tree = self.repo.head.peel(GIT_OBJ_TREE)
        yield self.get_resource('/')
        for name in self.get_names(tree):
            if name[-9:] == '.metadata' and name != '.metadata':
                yield self.get_resource('/' + name[:-9])

    def get_names(self, tree, path=''):
        for entry in tree:
            base_path = '{0}/{1}'.format(path, entry.name)
            yield base_path
            if entry.filemode == GIT_FILEMODE_TREE:
                sub_tree = self.repo.get(entry.hex)
                for x in self.get_names(sub_tree, base_path):
                    yield x

    def do_transaction(self, commit_message, data, added, changed, removed,
                       handlers):
        self.nb_transactions += 1
        # Get informations
        git_author, git_date, git_msg, docs_to_index, docs_to_unindex = data
        git_msg = commit_message or git_msg or 'no comment'
        # List of Changed
        added_and_changed = list(added) + list(changed)
        # Build the tree from index
        index = self.repo.index
        for key in added_and_changed:
            handler = handlers.get(key)
            blob_id = self.repo.create_blob(handler.to_str())
            entry = IndexEntry(key, blob_id, GIT_FILEMODE_BLOB_EXECUTABLE)
            index.add(entry)
        for key in removed:
            index.remove(key)
        git_tree = index.write_tree()
        # Commit
        self.git_commit(git_msg, git_author, git_date, tree=git_tree)

    def git_commit(self, message, author=None, date=None, tree=None):
        """Equivalent to 'git commit', we must give the message and we can
        also give the author and date.
        """
        # Tree
        if tree is None:
            #tree = self.index.write_tree()
            raise ValueError('Please give me a tree')

        # Parent
        parent = self._resolve_reference('HEAD')
        parents = [parent] if parent else []

        # Committer
        when_time = time.time()
        when_offset = -(time.altzone if time.daylight else time.timezone)
        when_offset = when_offset / 60

        name = self.username
        email = self.useremail
        committer = Signature(name, email, when_time, when_offset)

        # Author
        if author is None:
            author = (name, email)

        if date:
            if date.tzinfo:
                from pytz import utc
                when_time = date.astimezone(utc)  # To UTC
                when_time = when_time.timetuple()  # As struct_time
                when_time = timegm(when_time)  # To unix time
                when_offset = date.utcoffset().seconds / 60
            else:
                err = "Worktree.git_commit doesn't support naive datatime yet"
                raise NotImplementedError, err

        author = Signature(author[0], author[1], when_time, when_offset)

        # Create the commit
        return self.repo.create_commit('HEAD', author, committer, message,
                                       tree, parents)

    def abort_transaction(self):
        # TODO: Remove created blobs
        pass
Exemplo n.º 19
0
    index.add(device_name)

# Prepare to commit
author = Signature(git_parameters['author_name'],
                   git_parameters['author_email'])
committer = Signature(git_parameters['committer_name'],
                      git_parameters['committer_email'])
# reference = 'refs/HEAD'
reference = 'refs/heads/master'

# Build the message
year = datetime.now().strftime('%Y')
month = datetime.now().strftime('%m')
day = datetime.now().strftime('%d')
hour = datetime.now().strftime('%H')
message = 'Commit repository changes at ' + year + month + day + hour

# Save the new index of repository (git add)
index.write()

tree_oid = index.write_tree()
# tree_oid = repo.TreeBuilder().write()
if not git_init:
    # Get previous commit (parent)
    parent_commit = repo.revparse_single('HEAD')
    commit_oid = repo.create_commit(reference, author, committer, message,
                                    tree_oid, [parent_commit.oid])
else:
    commit_oid = repo.create_commit(reference, author, committer, message,
                                    tree_oid, [])
Exemplo n.º 20
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)
Exemplo n.º 21
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')
Exemplo n.º 22
0
class GitBlack:
    def __init__(self):
        self.repo = Repository(".")
        self.patchers = {}

    def get_blamed_deltas(self, patch):
        filename = patch.delta.old_file.path
        self.patchers[filename] = Patcher(self.repo, filename)
        hb = HunkBlamer(self.repo, patch)
        return hb.blames()

    def group_blame_deltas(self, blames):
        for delta_blame in blames:
            commits = tuple(sorted(delta_blame.commits))
            self.grouped_deltas.setdefault(commits,
                                           []).append(delta_blame.delta)

        self.progress += 1
        now = time.monotonic()
        if now - self.last_log > 0.04:
            sys.stdout.write("Reading file {}/{} \r".format(
                self.progress, self.total))
            sys.stdout.flush()
            self.last_log = now

    def commit_changes(self):
        start = time.monotonic()
        self.grouped_deltas = {}

        for path, status in self.repo.status().items():
            if status & index_statuses:
                raise GitIndexNotEmpty

        patches = []
        self._file_modes = {}
        diff = self.repo.diff(context_lines=0,
                              flags=GIT_DIFF_IGNORE_SUBMODULES)
        for patch in diff:
            if patch.delta.status != GIT_DELTA_MODIFIED:
                continue
            self._file_modes[
                patch.delta.old_file.path] = patch.delta.old_file.mode
            patches.append(patch)

        self.progress = 0
        self.last_log = 0
        self.total = len(patches)

        executor = ThreadPoolExecutor(max_workers=8)
        tasks = set()
        for patch in patches:
            tasks.add(executor.submit(self.get_blamed_deltas, patch))
            if len(tasks) > 8:
                done, not_done = wait(tasks, return_when=FIRST_COMPLETED)
                for task in done:
                    self.group_blame_deltas(task.result())
                tasks -= set(done)

        for task in tasks:
            self.group_blame_deltas(task.result())

        secs = time.monotonic() - start
        sys.stdout.write("Reading file {}/{} ({:.2f} secs).\n".format(
            self.progress, self.total, secs))

        start = time.monotonic()
        self.total = len(self.grouped_deltas)
        self.progress = 0
        self.last_log = 0

        for commits, deltas in self.grouped_deltas.items():
            blobs = self._create_blobs(deltas)
            self._commit(commits, blobs)

        secs = time.monotonic() - start
        print("Making commit {}/{} ({:.2f} secs).".format(
            self.progress, self.total, secs))

    def _create_blobs(self, deltas):
        filenames = set()
        for delta in deltas:
            self.patchers[delta.filename].apply(delta)
            filenames.add(delta.filename)

        blobs = {}
        for filename in filenames:
            blob_id = self.repo.create_blob(self.patchers[filename].content())
            blobs[filename] = blob_id

        return blobs

    def _commit(self, original_commits, blobs):
        for filename, blob_id in blobs.items():
            file_mode = self._file_modes[filename]
            index_entry = IndexEntry(filename, blob_id, file_mode)
            self.repo.index.add(index_entry)

        commits = [self.repo.get(h) for h in original_commits]

        main_commit = commits[0]
        if len(commits) > 1:
            # most recent commit
            main_commit = sorted(commits, key=commit_datetime)[-1]

        commit_message = main_commit.message
        commit_message += "\n\nautomatic commit by git-black, original commits:\n"
        commit_message += "\n".join(
            ["  {}".format(c) for c in original_commits])

        committer = Signature(
            name=self.repo.config["user.name"],
            email=self.repo.config["user.email"],
        )

        self.repo.index.write()
        tree = self.repo.index.write_tree()
        head = self.repo.head.peel()
        self.repo.create_commit("HEAD", main_commit.author, committer,
                                commit_message, tree, [head.id])
        self.progress += 1
        now = time.monotonic()
        if now - self.last_log > 0.04:
            sys.stdout.write("Making commit {}/{} \r".format(
                self.progress, self.total))
            sys.stdout.flush()
            self.last_log = now
Exemplo n.º 23
0
from pygit2 import Repository, Signature
import os
from datetime import datetime, date
from subprocess import call

d = datetime.utcnow().date()
path = '/home/xubin/workspace/tickets_history/'
name = '%s.txt' % d.strftime('%Y-%m-%d')
abspath = os.path.join(path, name)
f = open(abspath, 'ab')
f.write('test\n');
f.close()

repo = Repository(os.path.join(path, '.git'))
index = repo.index
index.add(name)
index.write()
oid = index.write_tree()

author = Signature('icefreedom', '*****@*****.**')

cm  = repo.create_commit('HEAD', author, author, 'test', oid, [repo.head.oid])

username = '******'
passwd = '87650872ice'
remote_repo = 'https://%s:%[email protected]/icefreedom/tickets_history.git' % (username, passwd)
call(['cd "' + path + '" && git push ' + remote_repo] , shell=True)

Exemplo n.º 24
0
            open(x64_binary_path,
                 'rb').read()).hexdigest()] = 'DSpellCheck.dll'
        str_after = json.dumps(validate_data['DSpellCheck'])
        validate_text = validate_text.replace(str_before[1:-1],
                                              str_after[1:-1])
        with open(validate_path, "w", encoding='utf-8') as file:
            file.write(validate_text)

        print('Creating commit in npp-plugins-x64 repository...')
        repo = Repository(plugins_x64_path)
        index = repo.index
        index.add_all()
        index.write()
        config = Config.get_global_config()
        author = Signature(config['user.name'], config['user.email'])
        commiter = author
        tree = index.write_tree()
        repo.create_commit('refs/heads/master', author, commiter,
                           'Update DSpellCheck to {}'.format(ver), tree,
                           [repo.head.get_object().hex])
    else:
        print('%NPP_PLUGINS_X64_PATH% is not set up, nothing to update')

successString = 'Success!'
try:
    from colorama import init, Fore, Style
    init()
    successString = Fore.GREEN + Style.BRIGHT + successString
except:
    pass
print(successString)
Exemplo n.º 25
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')