Blame progit/fork.py

Pierre-Yves Chibon fac0b1
#-*- coding: utf-8 -*-
Pierre-Yves Chibon fac0b1
Pierre-Yves Chibon fac0b1
"""
Pierre-Yves Chibon fac0b1
 (c) 2014 - Copyright Red Hat Inc
Pierre-Yves Chibon fac0b1
Pierre-Yves Chibon fac0b1
 Authors:
Pierre-Yves Chibon fac0b1
   Pierre-Yves Chibon <pingou@pingoured.fr></pingou@pingoured.fr>
Pierre-Yves Chibon fac0b1
Pierre-Yves Chibon fac0b1
"""
Pierre-Yves Chibon fac0b1
Pierre-Yves Chibon fac0b1
import flask
Pierre-Yves Chibon fac0b1
import os
Pierre-Yves Chibon fac0b1
from math import ceil
Pierre-Yves Chibon fac0b1
Pierre-Yves Chibon fac0b1
import pygit2
Pierre-Yves Chibon fac0b1
from sqlalchemy.exc import SQLAlchemyError
Pierre-Yves Chibon fac0b1
from pygments import highlight
Pierre-Yves Chibon fac0b1
from pygments.lexers import guess_lexer
Pierre-Yves Chibon fac0b1
from pygments.lexers.text import DiffLexer
Pierre-Yves Chibon fac0b1
from pygments.formatters import HtmlFormatter
Pierre-Yves Chibon fac0b1
Pierre-Yves Chibon fac0b1
Pierre-Yves Chibon fac0b1
from progit import APP, SESSION, LOG
Pierre-Yves Chibon fac0b1
Pierre-Yves Chibon fac0b1
Pierre-Yves Chibon fac0b1
### Application
Pierre-Yves Chibon fac0b1
@APP.route('/fork/<repo>')</repo>
Pierre-Yves Chibon fac0b1
def fork_project(repo):
Pierre-Yves Chibon fac0b1
    """ Fork the project specified into the user's namespace
Pierre-Yves Chibon fac0b1
    """
Pierre-Yves Chibon fac0b1
    reponame = os.path.join(APP.config['GIT_FOLDER'], repo)
Pierre-Yves Chibon fac0b1
    if not os.path.exists(reponame):
Pierre-Yves Chibon fac0b1
        flask.abort(404)
Pierre-Yves Chibon fac0b1
Pierre-Yves Chibon cbf031
    forkreponame = os.path.join(
Pierre-Yves Chibon fac0b1
        APP.config['FORK_FOLDER'],
Pierre-Yves Chibon fac0b1
        flask.g.fas_user.username,
Pierre-Yves Chibon fac0b1
        repo)
Pierre-Yves Chibon fac0b1
Pierre-Yves Chibon fac0b1
    if os.path.exists(forkreponame):
Pierre-Yves Chibon cbf031
        flask.flash('Repo "%s/%s" already exists' % (
Pierre-Yves Chibon cbf031
            flask.g.fas_user.username, repo))
Pierre-Yves Chibon fac0b1
    else:
Pierre-Yves Chibon fac0b1
        pygit2.clone_repository(reponame, forkreponame)
Pierre-Yves Chibon cbf031
        flask.flash('Repo "%s" cloned to "%s/%s"' % (
Pierre-Yves Chibon cbf031
            repo, flask.g.fas_user.username, repo))
Pierre-Yves Chibon fac0b1
Pierre-Yves Chibon fac0b1
    return flask.redirect(
Pierre-Yves Chibon fac0b1
        flask.url_for('view_user', username=flask.g.fas_user.username)
Pierre-Yves Chibon fac0b1
    )
Pierre-Yves Chibon fac0b1
Pierre-Yves Chibon fac0b1
Pierre-Yves Chibon fac0b1
@APP.route('/fork/<username>/<repo>')</repo></username>
Pierre-Yves Chibon fac0b1
def view_fork_repo(username, repo):
Pierre-Yves Chibon fac0b1
    """ Front page of a specific repo.
Pierre-Yves Chibon fac0b1
    """
Pierre-Yves Chibon fac0b1
    reponame = os.path.join(APP.config['FORK_FOLDER'], username, repo)
Pierre-Yves Chibon fac0b1
    if not os.path.exists(reponame):
Pierre-Yves Chibon fac0b1
        flask.abort(404)
Pierre-Yves Chibon fac0b1
    repo_obj = pygit2.Repository(reponame)
Pierre-Yves Chibon fac0b1
Pierre-Yves Chibon fac0b1
    cnt = 0
Pierre-Yves Chibon fac0b1
    last_commits = []
Pierre-Yves Chibon fac0b1
    for commit in repo_obj.walk(repo_obj.head.target, pygit2.GIT_SORT_TIME):
Pierre-Yves Chibon fac0b1
        last_commits.append(commit)
Pierre-Yves Chibon fac0b1
        cnt += 1
Pierre-Yves Chibon fac0b1
        if cnt == 10:
Pierre-Yves Chibon fac0b1
            break
Pierre-Yves Chibon fac0b1
Pierre-Yves Chibon fac0b1
    return flask.render_template(
Pierre-Yves Chibon fac0b1
        'repo_info.html',
Pierre-Yves Chibon fac0b1
        repo=repo,
Pierre-Yves Chibon fac0b1
        username=username,
Pierre-Yves Chibon fac0b1
        branches=sorted(repo_obj.listall_branches()),
Pierre-Yves Chibon fac0b1
        branchname='master',
Pierre-Yves Chibon fac0b1
        last_commits=last_commits,
Pierre-Yves Chibon fac0b1
        tree=sorted(last_commits[0].tree, key=lambda x: x.filemode),
Pierre-Yves Chibon fac0b1
    )
Pierre-Yves Chibon fac0b1
Pierre-Yves Chibon fac0b1
Pierre-Yves Chibon fac0b1
@APP.route('/fork/<username>/<repo>/branch/<branchname>')</branchname></repo></username>
Pierre-Yves Chibon fac0b1
def view_fork_repo_branch(username, repo, branchname):
Pierre-Yves Chibon fac0b1
    """ Displays the information about a specific branch.
Pierre-Yves Chibon fac0b1
    """
Pierre-Yves Chibon fac0b1
    reponame = os.path.join(APP.config['FORK_FOLDER'], username, repo)
Pierre-Yves Chibon fac0b1
    if not os.path.exists(reponame):
Pierre-Yves Chibon fac0b1
        flask.abort(404)
Pierre-Yves Chibon fac0b1
    repo_obj = pygit2.Repository(reponame)
Pierre-Yves Chibon fac0b1
Pierre-Yves Chibon fac0b1
    if not branchname in repo_obj.listall_branches():
Pierre-Yves Chibon fac0b1
        flask.abort(404)
Pierre-Yves Chibon fac0b1
Pierre-Yves Chibon fac0b1
    branch = repo_obj.lookup_branch(branchname)
Pierre-Yves Chibon fac0b1
Pierre-Yves Chibon fac0b1
    cnt = 0
Pierre-Yves Chibon fac0b1
    last_commits = []
Pierre-Yves Chibon fac0b1
    for commit in repo_obj.walk(branch.get_object().hex, pygit2.GIT_SORT_TIME):
Pierre-Yves Chibon fac0b1
        last_commits.append(commit)
Pierre-Yves Chibon fac0b1
        cnt += 1
Pierre-Yves Chibon fac0b1
        if cnt == 10:
Pierre-Yves Chibon fac0b1
            break
Pierre-Yves Chibon fac0b1
Pierre-Yves Chibon fac0b1
    return flask.render_template(
Pierre-Yves Chibon fac0b1
        'repo_info.html',
Pierre-Yves Chibon fac0b1
        repo=repo,
Pierre-Yves Chibon fac0b1
        username=username,
Pierre-Yves Chibon fac0b1
        branches=sorted(repo_obj.listall_branches()),
Pierre-Yves Chibon fac0b1
        branchname=branchname,
Pierre-Yves Chibon fac0b1
        last_commits=last_commits,
Pierre-Yves Chibon fac0b1
        tree=sorted(last_commits[0].tree, key=lambda x: x.filemode),
Pierre-Yves Chibon fac0b1
    )
Pierre-Yves Chibon fac0b1
Pierre-Yves Chibon fac0b1
Pierre-Yves Chibon fac0b1
@APP.route('/fork/<username>/<repo>/log')</repo></username>
Pierre-Yves Chibon fac0b1
@APP.route('/fork/<username>/<repo>/log/<branchname>')</branchname></repo></username>
Pierre-Yves Chibon fac0b1
def view_fork_log(username, repo, branchname=None):
Pierre-Yves Chibon fac0b1
    """ Displays the logs of the specified repo.
Pierre-Yves Chibon fac0b1
    """
Pierre-Yves Chibon fac0b1
    reponame = os.path.join(APP.config['FORK_FOLDER'], username, repo)
Pierre-Yves Chibon fac0b1
    if not os.path.exists(reponame):
Pierre-Yves Chibon fac0b1
        flask.abort(404)
Pierre-Yves Chibon fac0b1
    repo_obj = pygit2.Repository(reponame)
Pierre-Yves Chibon fac0b1
Pierre-Yves Chibon fac0b1
    if branchname and not branchname in repo_obj.listall_branches():
Pierre-Yves Chibon fac0b1
        flask.abort(404)
Pierre-Yves Chibon fac0b1
Pierre-Yves Chibon fac0b1
    if branchname:
Pierre-Yves Chibon fac0b1
        branch = repo_obj.lookup_branch(branchname)
Pierre-Yves Chibon fac0b1
    else:
Pierre-Yves Chibon fac0b1
        branch = repo_obj.lookup_branch('master')
Pierre-Yves Chibon fac0b1
Pierre-Yves Chibon fac0b1
    try:
Pierre-Yves Chibon fac0b1
        page = int(flask.request.args.get('page', 1))
Pierre-Yves Chibon fac0b1
    except ValueError:
Pierre-Yves Chibon fac0b1
        page = 1
Pierre-Yves Chibon fac0b1
Pierre-Yves Chibon fac0b1
    limit = APP.config['ITEM_PER_PAGE']
Pierre-Yves Chibon fac0b1
    start = limit * (page - 1)
Pierre-Yves Chibon fac0b1
    end = limit * page
Pierre-Yves Chibon fac0b1
Pierre-Yves Chibon fac0b1
    n_commits = 0
Pierre-Yves Chibon fac0b1
    last_commits = []
Pierre-Yves Chibon fac0b1
    for commit in repo_obj.walk(
Pierre-Yves Chibon fac0b1
            branch.get_object().hex, pygit2.GIT_SORT_TIME):
Pierre-Yves Chibon fac0b1
        if n_commits >= start and n_commits <= end:
Pierre-Yves Chibon fac0b1
            last_commits.append(commit)
Pierre-Yves Chibon fac0b1
        n_commits += 1
Pierre-Yves Chibon fac0b1
Pierre-Yves Chibon fac0b1
    total_page = int(ceil(n_commits / float(limit)))
Pierre-Yves Chibon fac0b1
Pierre-Yves Chibon fac0b1
    return flask.render_template(
Pierre-Yves Chibon fac0b1
        'repo_info.html',
Pierre-Yves Chibon fac0b1
        origin='view_fork_log',
Pierre-Yves Chibon fac0b1
        repo=repo,
Pierre-Yves Chibon fac0b1
        username=username,
Pierre-Yves Chibon fac0b1
        branches=sorted(repo_obj.listall_branches()),
Pierre-Yves Chibon fac0b1
        branchname=branchname,
Pierre-Yves Chibon fac0b1
        last_commits=last_commits,
Pierre-Yves Chibon fac0b1
        page=page,
Pierre-Yves Chibon fac0b1
        total_page=total_page,
Pierre-Yves Chibon fac0b1
    )
Pierre-Yves Chibon fac0b1
Pierre-Yves Chibon fac0b1
Pierre-Yves Chibon fac0b1
@APP.route('/fork/<username>/<repo>/blob/<identifier>/<path:filename>')</path:filename></identifier></repo></username>
Pierre-Yves Chibon fac0b1
@APP.route('/fork/<username>/<repo>/blob/<identifier>/<path:filename>')</path:filename></identifier></repo></username>
Pierre-Yves Chibon fac0b1
def view_fork_file(username, repo, identifier, filename):
Pierre-Yves Chibon fac0b1
    """ Displays the content of a file or a tree for the specified repo.
Pierre-Yves Chibon fac0b1
    """
Pierre-Yves Chibon fac0b1
    reponame = os.path.join(APP.config['FORK_FOLDER'], username, repo)
Pierre-Yves Chibon fac0b1
    if not os.path.exists(reponame):
Pierre-Yves Chibon fac0b1
        flask.abort(404)
Pierre-Yves Chibon fac0b1
    repo_obj = pygit2.Repository(reponame)
Pierre-Yves Chibon fac0b1
Pierre-Yves Chibon fac0b1
    if identifier in repo_obj.listall_branches():
Pierre-Yves Chibon fac0b1
        branchname = identifier
Pierre-Yves Chibon fac0b1
        branch = repo_obj.lookup_branch(identifier)
Pierre-Yves Chibon fac0b1
        commit = branch.get_object()
Pierre-Yves Chibon fac0b1
    else:
Pierre-Yves Chibon fac0b1
        try:
Pierre-Yves Chibon fac0b1
            commit = repo_obj.get(identifier)
Pierre-Yves Chibon fac0b1
            branchname = identifier
Pierre-Yves Chibon fac0b1
        except ValueError:
Pierre-Yves Chibon fac0b1
            # If it's not a commit id then it's part of the filename
Pierre-Yves Chibon fac0b1
            commit = repo_obj[repo_obj.head.target]
Pierre-Yves Chibon fac0b1
            branchname = 'master'
Pierre-Yves Chibon fac0b1
Pierre-Yves Chibon fac0b1
    def __get_file_in_tree(tree, filepath):
Pierre-Yves Chibon fac0b1
        ''' Retrieve the entry corresponding to the provided filename in a
Pierre-Yves Chibon fac0b1
        given tree.
Pierre-Yves Chibon fac0b1
        '''
Pierre-Yves Chibon fac0b1
        filename = filepath[0]
Pierre-Yves Chibon fac0b1
        if isinstance(tree, pygit2.Blob):
Pierre-Yves Chibon fac0b1
            return
Pierre-Yves Chibon fac0b1
        for el in tree:
Pierre-Yves Chibon fac0b1
            if el.name == filename:
Pierre-Yves Chibon fac0b1
                if len(filepath) == 1:
Pierre-Yves Chibon fac0b1
                    return repo_obj[el.oid]
Pierre-Yves Chibon fac0b1
                else:
Pierre-Yves Chibon fac0b1
                    return __get_file_in_tree(repo_obj[el.oid], filepath[1:])
Pierre-Yves Chibon fac0b1
Pierre-Yves Chibon fac0b1
    content = __get_file_in_tree(commit.tree, filename.split('/'))
Pierre-Yves Chibon fac0b1
    if not content:
Pierre-Yves Chibon fac0b1
        flask.abort(404, 'File not found')
Pierre-Yves Chibon fac0b1
Pierre-Yves Chibon fac0b1
    content = repo_obj[content.oid]
Pierre-Yves Chibon fac0b1
    if isinstance(content, pygit2.Blob):
Pierre-Yves Chibon fac0b1
        content = highlight(
Pierre-Yves Chibon fac0b1
            content.data,
Pierre-Yves Chibon fac0b1
            guess_lexer(content.data),
Pierre-Yves Chibon fac0b1
            HtmlFormatter(
Pierre-Yves Chibon fac0b1
                noclasses=True,
Pierre-Yves Chibon fac0b1
                style="tango",)
Pierre-Yves Chibon fac0b1
        )
Pierre-Yves Chibon fac0b1
        output_type = 'file'
Pierre-Yves Chibon fac0b1
    else:
Pierre-Yves Chibon fac0b1
        content = sorted(content, key=lambda x: x.filemode)
Pierre-Yves Chibon fac0b1
        output_type = 'tree'
Pierre-Yves Chibon fac0b1
Pierre-Yves Chibon fac0b1
    return flask.render_template(
Pierre-Yves Chibon fac0b1
        'file.html',
Pierre-Yves Chibon fac0b1
        repo=repo,
Pierre-Yves Chibon fac0b1
        username=username,
Pierre-Yves Chibon fac0b1
        branchname=branchname,
Pierre-Yves Chibon fac0b1
        filename=filename,
Pierre-Yves Chibon fac0b1
        content=content,
Pierre-Yves Chibon fac0b1
        output_type=output_type,
Pierre-Yves Chibon fac0b1
    )
Pierre-Yves Chibon fac0b1
Pierre-Yves Chibon fac0b1
Pierre-Yves Chibon fac0b1
@APP.route('/fork/<username>/<repo>/<commitid>')</commitid></repo></username>
Pierre-Yves Chibon fac0b1
def view_fork_commit(username, repo, commitid):
Pierre-Yves Chibon fac0b1
    """ Render a commit in a repo
Pierre-Yves Chibon fac0b1
    """
Pierre-Yves Chibon fac0b1
    reponame = os.path.join(APP.config['FORK_FOLDER'], username, repo)
Pierre-Yves Chibon fac0b1
    if not os.path.exists(reponame):
Pierre-Yves Chibon fac0b1
        flask.abort(404)
Pierre-Yves Chibon fac0b1
    repo_obj = pygit2.Repository(reponame)
Pierre-Yves Chibon fac0b1
Pierre-Yves Chibon fac0b1
    try:
Pierre-Yves Chibon fac0b1
        commit = repo_obj.get(commitid)
Pierre-Yves Chibon fac0b1
    except ValueError:
Pierre-Yves Chibon fac0b1
        flask.abort(404)
Pierre-Yves Chibon fac0b1
Pierre-Yves Chibon fac0b1
    if commit.parents:
Pierre-Yves Chibon fac0b1
        diff = commit.tree.diff_to_tree()
Pierre-Yves Chibon fac0b1
Pierre-Yves Chibon fac0b1
        parent = repo_obj.revparse_single('%s^' % commitid)
Pierre-Yves Chibon fac0b1
        diff = repo_obj.diff(parent, commit)
Pierre-Yves Chibon fac0b1
    else:
Pierre-Yves Chibon fac0b1
        # First commit in the repo
Pierre-Yves Chibon fac0b1
        diff = commit.tree.diff_to_tree(swap=True)
Pierre-Yves Chibon fac0b1
Pierre-Yves Chibon fac0b1
    html_diff = highlight(
Pierre-Yves Chibon fac0b1
        diff.patch,
Pierre-Yves Chibon fac0b1
        DiffLexer(),
Pierre-Yves Chibon fac0b1
        HtmlFormatter(
Pierre-Yves Chibon fac0b1
            noclasses=True,
Pierre-Yves Chibon fac0b1
            style="tango",)
Pierre-Yves Chibon fac0b1
    )
Pierre-Yves Chibon fac0b1
Pierre-Yves Chibon fac0b1
    return flask.render_template(
Pierre-Yves Chibon fac0b1
        'commit.html',
Pierre-Yves Chibon fac0b1
        repo=repo,
Pierre-Yves Chibon fac0b1
        username=username,
Pierre-Yves Chibon fac0b1
        commitid=commitid,
Pierre-Yves Chibon fac0b1
        commit=commit,
Pierre-Yves Chibon fac0b1
        diff=diff,
Pierre-Yves Chibon fac0b1
        html_diff=html_diff,
Pierre-Yves Chibon fac0b1
    )
Pierre-Yves Chibon fac0b1
Pierre-Yves Chibon fac0b1
Pierre-Yves Chibon fac0b1
@APP.route('/fork/<username>/<repo>/tree/')</repo></username>
Pierre-Yves Chibon fac0b1
@APP.route('/fork/<username>/<repo>/tree/<identifier>')</identifier></repo></username>
Pierre-Yves Chibon fac0b1
def view_fork_tree(username, repo, identifier=None):
Pierre-Yves Chibon fac0b1
    """ Render the tree of the repo
Pierre-Yves Chibon fac0b1
    """
Pierre-Yves Chibon fac0b1
    reponame = os.path.join(APP.config['FORK_FOLDER'], username, repo)
Pierre-Yves Chibon fac0b1
    if not os.path.exists(reponame):
Pierre-Yves Chibon fac0b1
        flask.abort(404)
Pierre-Yves Chibon fac0b1
    repo_obj = pygit2.Repository(reponame)
Pierre-Yves Chibon fac0b1
Pierre-Yves Chibon fac0b1
    if identifier in repo_obj.listall_branches():
Pierre-Yves Chibon fac0b1
        branchname = identifier
Pierre-Yves Chibon fac0b1
        branch = repo_obj.lookup_branch(identifier)
Pierre-Yves Chibon fac0b1
        commit = branch.get_object()
Pierre-Yves Chibon fac0b1
    else:
Pierre-Yves Chibon fac0b1
        try:
Pierre-Yves Chibon fac0b1
            commit = repo_obj.get(identifier)
Pierre-Yves Chibon fac0b1
            branchname = identifier
Pierre-Yves Chibon fac0b1
        except (ValueError, TypeError):
Pierre-Yves Chibon fac0b1
            # If it's not a commit id then it's part of the filename
Pierre-Yves Chibon fac0b1
            commit = repo_obj[repo_obj.head.target]
Pierre-Yves Chibon fac0b1
            branchname = 'master'
Pierre-Yves Chibon fac0b1
Pierre-Yves Chibon fac0b1
    content = sorted(commit.tree, key=lambda x: x.filemode)
Pierre-Yves Chibon fac0b1
    output_type = 'tree'
Pierre-Yves Chibon fac0b1
Pierre-Yves Chibon fac0b1
    return flask.render_template(
Pierre-Yves Chibon fac0b1
        'file.html',
Pierre-Yves Chibon fac0b1
        repo=repo,
Pierre-Yves Chibon fac0b1
        username=username,
Pierre-Yves Chibon fac0b1
        branchname=branchname,
Pierre-Yves Chibon fac0b1
        filename='',
Pierre-Yves Chibon fac0b1
        content=content,
Pierre-Yves Chibon fac0b1
        output_type=output_type,
Pierre-Yves Chibon fac0b1
    )